How to Build Your First MCP Server in 2026
Build a working MCP server in under 30 minutes. This tutorial walks through setup, tool definition, testing, and connecting to Claude Code.
Building an MCP server is simpler than most developers expect. If you can write a basic Node.js app, you can build an MCP server. This tutorial walks through creating a functional server from scratch, connecting it to Claude Code, and testing it.
Quick Answer: You can build your first Model Context Protocol (MCP) server by setting up a Node.js project, defining tools with
@modelcontextprotocol/sdkandzod, building the project with TypeScript, and connecting it to an AI agent like Claude Code via stdio or HTTP/SSE transport.
What you're building
A simple MCP server that exposes two tools to AI agents:
get_weather: returns current weather for a city (using a free API)get_time: returns the current time in a given timezone
These are intentionally simple so you can focus on the MCP patterns. Once you understand the structure, you can replace these with any tools you want.
Skills built by the community
Prerequisites
You need Node.js 18+ and npm installed. You also need an AI agent that supports MCP (Claude Code, Cursor, or Codex CLI).
Step 1: Set up the project
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
The MCP SDK handles the protocol implementation. Zod is used for input validation.
Create a tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "node16",
"moduleResolution": "node16",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
Step 2: Define your tools
Create src/server.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "my-first-mcp",
version: "1.0.0",
});
// Tool 1: Get current time in a timezone
server.tool(
"get_time",
"Get the current time in a specific timezone",
{
timezone: z.string().describe(
"IANA timezone name, e.g. America/New_York, Europe/London"
),
},
async ({ timezone }) => {
try {
const time = new Date().toLocaleString("en-US", {
timeZone: timezone,
});
return {
content: [
{
type: "text",
text: `Current time in ${timezone}: ${time}`,
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Invalid timezone: ${timezone}`,
},
],
isError: true,
};
}
}
);
// Tool 2: Get weather (using wttr.in, no API key needed)
server.tool(
"get_weather",
"Get current weather for a city",
{
city: z.string().describe("City name, e.g. London, Tokyo"),
},
async ({ city }) => {
try {
const response = await fetch(
`https://wttr.in/${encodeURIComponent(city)}?format=j1`
);
const data = await response.json();
const current = data.current_condition[0];
return {
content: [
{
type: "text",
text: [
`Weather in ${city}:`,
`Temperature: ${current.temp_C}°C / ${current.temp_F}°F`,
`Condition: ${current.weatherDesc[0].value}`,
`Humidity: ${current.humidity}%`,
`Wind: ${current.windspeedKmph} km/h`,
].join("\n"),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Could not fetch weather for ${city}`,
},
],
isError: true,
};
}
}
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running on stdio");
}
main().catch(console.error);
Each tool has four parts: a name, a description (which the agent reads to decide when to use it), an input schema (validated with Zod), and a handler function that executes the logic.
Step 3: Build and test locally
Add build and start scripts to package.json:
{
"scripts": {
"build": "tsc",
"start": "node dist/server.js"
}
}
Build it:
npm run build
Step 4: Connect to Claude Code
Add your server to Claude Code's config. Edit ~/.claude.json:
{
"mcpServers": {
"my-first-mcp": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/dist/server.js"]
}
}
}
This uses stdio transport (the server runs as a local process). For remote deployment, you would use HTTP/SSE transport instead.
Restart Claude Code and type /mcp to verify the connection. You should see your server listed with the two tools.
Step 5: Test the tools
Ask Claude Code something that should trigger your tools:
"What's the weather like in Amsterdam right now?"
Claude should recognize that the get_weather tool is relevant, call it with "Amsterdam", and include the weather data in its response.
Try: "What time is it in Tokyo?"
Claude should use the get_time tool.
Step 6: Add more tools
Adding a new tool follows the same pattern. Here's a third tool that counts words in a file:
server.tool(
"count_words",
"Count the number of words in a file",
{
filepath: z.string().describe("Path to the file to count"),
},
async ({ filepath }) => {
const fs = await import("fs/promises");
const content = await fs.readFile(filepath, "utf-8");
const words = content.split(/\s+/).filter(Boolean).length;
return {
content: [
{
type: "text",
text: `${filepath} contains ${words} words`,
},
],
};
}
);
Deploying remotely
For production use, you'll want to deploy your MCP server as a web service instead of running it locally. This requires switching from stdio transport to HTTP/SSE transport:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/message", res);
await server.connect(transport);
});
app.post("/message", async (req, res) => {
// Handle messages from the transport
});
app.listen(3000);
Deploy this to Railway, Render, or Fly.io, and agents can connect to it remotely using the URL instead of a local command.
What to build next
Now that you understand the pattern, think about what tools would be most useful in your workflow. Some ideas:
A server that queries your project's database so your agent can answer questions about your data. A server that reads your monitoring dashboard so your agent can check system health. A server that integrates with your CI/CD pipeline so your agent can trigger deployments.
The best MCP servers solve a specific problem well. Start small, test with your agent, and iterate.
For the other side of agent customization (teaching your agent procedures and expertise rather than connecting it to tools), see our guide on what is SKILL.md. For understanding when to use MCP vs skills, see MCP vs SKILL.md skills. For real-world examples of combining both, read how MCP and SKILL.md work together.
Related articles
Frequently Asked Questions
Find the right skill for your workflow
Browse our marketplace of AI agent skills, ready to install in seconds.
BrowseRelated Articles
AI Agent Security: How to Audit Skills and MCP Servers Before Installing (2026)
Before installing any skill or connecting an MCP server, audit it for prompt injection, data exfiltration, and dangerous commands. Includes a manual checklist and how automated scanning works.
8 min read
How to Build an MCP Server That Serves SKILL.md Skills
Build a Model Context Protocol server that serves SKILL.md skills to AI coding agents on demand.
6 min read
The SKILL.md Open Standard — Full Specification
The complete SKILL.md open standard specification covering file structure, frontmatter, and agent compatibility.
6 min read

