Step-by-step guide to building your own MCP server and exposing tools to AI models.
This guide uses the official Python MCP SDK. You'll need Python 3.10+, pip, and Claude Desktop installed for local testing. The TypeScript SDK follows the same concepts with different syntax — the official docs at [modelcontextprotocol.io](https://modelcontextprotocol.io) cover both.
You can write MCP servers in any language that can communicate over stdio or HTTP. The official SDKs handle the protocol details; you focus on the tool logic.
bash
pip install mcp
For a project, use a virtual environment:
bash
python -m venv .venv
source .venv/bin/activate
pip install mcp
Create a file called calculator_server.py. This will be a simple MCP server exposing two arithmetic tools.
```python from mcp.server import Server from mcp.server.stdio import stdio_server from mcp import types
# Initialize the server with a name app = Server("calculator")
# Declare the tools this server exposes @app.list_tools() async def list_tools() -> list[types.Tool]: return [ types.Tool( name="add", description="Add two numbers together", inputSchema={ "type": "object", "properties": { "a": {"type": "number", "description": "First number"}, "b": {"type": "number", "description": "Second number"}, }, "required": ["a", "b"], }, ), types.Tool( name="multiply", description="Multiply two numbers together", inputSchema={ "type": "object", "properties": { "a": {"type": "number", "description": "First number"}, "b": {"type": "number", "description": "Second number"}, }, "required": ["a", "b"], }, ), ]
# Handle tool execution @app.call_tool() async def call_tool(name: str, arguments: dict) -> list[types.TextContent]: a = arguments["a"] b = arguments["b"]
if name == "add": result = a + b elif name == "multiply": result = a * b else: raise ValueError(f"Unknown tool: {name}")
return [types.TextContent(type="text", text=str(result))]
# Start the server using stdio transport if __name__ == "__main__": import asyncio asyncio.run(stdio_server(app)) ```
When Claude Desktop (or any MCP host) connects to your server, here's what happens:
list_tools handler responds with the tool definitions{"a": 47, "b": 83}The SDK handles all the JSON-RPC 2.0 serialization, transport management, and error handling. Your code only needs to implement the two handlers.
Claude Desktop reads its MCP server configuration from a JSON file:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonAdd your server to the configuration:
json
{
"mcpServers": {
"calculator": {
"command": "/path/to/.venv/bin/python",
"args": ["/path/to/calculator_server.py"]
}
}
}
Replace the paths with absolute paths to your virtual environment's Python interpreter and your server file. Restart Claude Desktop after saving.
After restarting Claude Desktop, open a new conversation. You should see a tools indicator showing your server is connected. Ask Claude: "What is 144 divided by 12?" — note that our server only has add and multiply, so Claude will handle division itself. Try "What is 47 times 83?" and Claude should invoke the multiply tool and return 3901.
If the server doesn't connect, check Claude Desktop's logs:
- macOS: ~/Library/Logs/Claude/mcp*.log
Add error handling. Real tools should validate inputs and return meaningful error messages rather than raising Python exceptions.
Add a resource. Resources expose data the model can read. Add a @app.list_resources() handler and a @app.read_resource() handler to expose static or dynamic content.
Use environment variables for secrets. If your tool calls an external API, pass credentials via environment variables in the Claude Desktop config:
json
{
"mcpServers": {
"my-api-tool": {
"command": "python",
"args": ["server.py"],
"env": {
"API_KEY": "your-key-here"
}
}
}
}
Consider SSE transport for remote servers. The stdio transport works for local development. For a server you want to host remotely and share across a team, switch to the SSE transport — the SDK supports both with minimal configuration changes.
The official documentation at [modelcontextprotocol.io](https://modelcontextprotocol.io) includes more advanced examples, the full SDK reference, and guides for TypeScript. The GitHub repository also contains reference server implementations for common integrations that you can use as starting points.
Have a follow-up question about this topic?
Ask AI