How to Build Your First MCP Server
AI coding agents like Claude Code, Codex, and Cursor are transforming how developers work. But here's the catch: they can only use what you give them access to. That's where the Model Context Protocol (MCP) comes in.
MCP is an open standard that lets AI agents connect to your data sources, APIs, and tools through a unified interface. Today, you'll build your own MCP server from scratch — one that exposes a real data source to any MCP-compatible AI agent.
What Is MCP and Why Should You Care?
Think of MCP as USB-C for AI tools. Just as USB-C gives you a single port for charging, data, and video, MCP gives AI agents a single protocol for reading files, querying databases, calling APIs, and more.
Before MCP, every AI tool needed its own custom integration. Now, you build one MCP server and it works with Claude, Codex, Cursor, and any other agent that supports the protocol.
How MCP Works
MCP follows a simple client-server model:
- MCP Host: The AI agent (Claude Code, Cursor, etc.)
- MCP Client: Protocol handler inside the host
- MCP Server: Your custom server that exposes tools, resources, and prompts
Your server exposes three things:
| Capability | Purpose | Example |
|---|---|---|
| Tools | Actions the agent can invoke | Query database, deploy service |
| Resources | Data the agent can read | Config files, documentation |
| Prompts | Reusable prompt templates | Code review, bug analysis |
Prerequisites
Before you start, make sure you have:
- Node.js 18+ installed (
node --version) - Basic familiarity with TypeScript
- An MCP-compatible AI agent (Claude Desktop, Cursor, or Claude Code)
Step 1: Set Up the Project
Create a new project and install the MCP SDK:
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init
Update your tsconfig.json to enable modern module resolution:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
Step 2: Create the MCP Server
Create src/index.ts and set up the server skeleton:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create the server instance
const server = new McpServer({
name: "weather-mcp-server",
version: "1.0.0",
});
// Connect via stdio (how agents communicate with your server)
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running on stdio");
This server communicates over stdin/stdout using JSON-RPC. That's the standard transport for local MCP servers — no HTTP needed.
Step 3: Add a Tool
Tools are the most powerful MCP capability. Let's add a tool that fetches weather data (simulated for this tutorial):
// Add a tool that gets weather for a city
server.tool(
"get_weather",
{ city: z.string().describe("City name, e.g. Istanbul") },
async ({ city }) => {
// In production, call a real weather API here
const weatherData = {
city,
temperature: Math.floor(Math.random() * 30) + 5,
condition: ["Sunny", "Cloudy", "Rainy"][Math.floor(Math.random() * 3)],
humidity: Math.floor(Math.random() * 60) + 30,
timestamp: new Date().toISOString(),
};
return {
content: [
{
type: "text",
text: JSON.stringify(weatherData, null, 2),
},
],
};
}
);
The zod schema defines the tool's parameters. The AI agent uses this to understand what arguments to pass. The handler returns structured content the agent can reason about.
Step 4: Add a Resource
Resources let agents read data. Let's expose a static configuration resource:
// Add a resource that the agent can read
server.resource(
"config",
"config://app/settings",
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
appName: "Weather App",
defaultUnits: "metric",
refreshInterval: 300,
version: "1.0.0",
}, null, 2),
},
],
})
);
Now the AI agent can query config://app/settings and get your app configuration — useful for context-aware responses.
Step 5: Run and Test
Add a start script to package.json:
{
"scripts": {
"start": "tsx src/index.ts"
}
}
Test it manually:
npm start
Then send a JSON-RPC request from another terminal:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"Istanbul"}}}' | npm start
You should see a structured response with weather data.
Step 6: Connect to an AI Agent
The real power comes when you connect your server to an AI agent. Here's how to configure Claude Desktop:
Edit your Claude Desktop config (macOS):
~/Library/Application Support/Claude/claude_desktop_config.json
Add your server:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/dist/index.js"],
"env": {}
}
}
}
Restart Claude Desktop. Your weather tool now appears as a native capability the agent can use during conversations.
Going Further: Practical MCP Server Ideas
Once you understand the pattern, the possibilities are endless:
- Database MCP server — Let AI agents safely query your PostgreSQL database with read-only access
- GitHub MCP server — Give agents the ability to search repos, read issues, and review PRs
- Monitoring MCP server — Connect agents to Grafana, Prometheus, or Datadog for live system metrics
- File system MCP server — Expose specific directories (with sandboxing) for document analysis
- API proxy MCP server — Wrap any REST API as MCP tools with automatic schema generation
Best Practices
- Validate everything: Use zod schemas for all tool inputs — never trust raw arguments
- Error gracefully: Return helpful error messages in
contentso the agent can recover - Log to stderr: stdout is for JSON-RPC only; all debugging goes to stderr
- Keep it focused: One server per domain (database, API, files) — don't build a monolith
- Security first: MCP servers run with the agent's permissions. Scope them tightly.
Conclusion
Building an MCP server is straightforward — define your tools with schemas, implement handlers, and connect over stdio. The result: your AI agents gain real capabilities beyond their training data.
The MCP ecosystem is growing fast. The skills you build here — schema design, tool implementation, and agent integration — will be valuable for years to come. Start with something simple, connect it to your workflow, and iterate.
Happy building! 🚀