Building Your First MCP Server
Table of content
MCP servers let you give Claude new capabilities. Build one when existing servers don’t cover your use case.
What MCP servers do
[Claude Code] ←stdio→ [Your MCP Server] → [Anything]
→ Database
→ API
→ Local files
→ Hardware
A server exposes:
| Primitive | Purpose | Example |
|---|---|---|
| Tools | Actions Claude can take | send_email, query_db |
| Resources | Data Claude can read | Config files, database records |
| Prompts | Reusable prompt templates | Summarization formats |
Tools are most common. Start there.
Prerequisites
Pick one:
| Runtime | Install SDK |
|---|---|
| Python 3.10+ | pip install mcp |
| Node.js 18+ | npm install @modelcontextprotocol/sdk |
Minimal Python server
# my_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool()
def hello(name: str) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run()
Run it:
python my_server.py
Minimal TypeScript server
// my-server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
server.tool("hello", { name: { type: "string" } }, async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}!` }],
}));
const transport = new StdioServerTransport();
server.connect(transport);
Run it:
npx ts-node my-server.ts
Adding useful tools
Real example — a tool that queries a local SQLite database:
# db_server.py
from mcp.server.fastmcp import FastMCP
import sqlite3
mcp = FastMCP("db-server")
@mcp.tool()
def query(sql: str) -> str:
"""Run a read-only SQL query."""
conn = sqlite3.connect("data.db")
cursor = conn.execute(sql)
rows = cursor.fetchall()
conn.close()
return str(rows)
@mcp.tool()
def tables() -> str:
"""List all tables in the database."""
conn = sqlite3.connect("data.db")
cursor = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
)
names = [row[0] for row in cursor.fetchall()]
conn.close()
return ", ".join(names)
if __name__ == "__main__":
mcp.run()
Connect to Claude Code
Add your server to Claude Code config:
claude mcp add my-server --command "python /path/to/my_server.py"
Or edit ~/.claude/mcp.json directly:
{
"servers": {
"my-server": {
"command": "python",
"args": ["/path/to/my_server.py"]
}
}
}
Verify:
claude mcp list
claude mcp status
Test your tools
claude "Use the hello tool to greet Alice"
claude "What tables are in the database?"
Claude will discover your tools automatically and use them when relevant.
Debugging
| Problem | Solution |
|---|---|
| Server not found | Check path in mcp.json is absolute |
| Tools not appearing | Restart: claude mcp restart my-server |
| Server crashes | Run manually: python my_server.py and check errors |
| Permission denied | Make script executable: chmod +x my_server.py |
View logs
# Server logs
cat ~/.claude/mcp/my-server/logs/stderr.log
# Run with debug output
MCP_DEBUG=1 python my_server.py
Test without Claude
# Send JSON-RPC directly via stdin
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | python my_server.py
Project structure
For anything beyond a single file:
my-mcp-server/
├── server.py
├── requirements.txt
├── README.md
└── tools/
├── __init__.py
└── database.py
Common patterns
| Pattern | Implementation |
|---|---|
| API wrapper | Tool calls external API, returns formatted response |
| Database access | Tool runs queries, returns results |
| File operations | Tool reads/writes specific directories |
| System info | Tool returns machine stats, processes |
Security
Your MCP server runs with your user permissions. It can:
- Access any file you can access
- Make network requests
- Run shell commands
Keep tools scoped. Don’t build a “run any shell command” tool.
Next: MCP Servers Reference
Get updates
New guides, workflows, and AI patterns. No spam.
Thank you! You're on the list.