Your LLM can generate great text. It can't check the weather, query your database, or read your Notion pages — not unless you wire up those connections yourself. MCP (Model Context Protocol) is an open standard that gives LLMs a standardized way to call your tools and access your data. Building a server takes about 15 minutes.
What MCP Is
MCP is a protocol, not a framework. A client (Claude Desktop, an AI coding tool, anything MCP-compatible) connects to a server you write. Your server exposes tools, resources, and prompts. The server can call APIs, read files, query databases — anything you can code, the LLM can now trigger.
It's like USB-C for AI. Same server, any host. No rewiring when you switch from Claude to ChatGPT to a local Ollama setup.
Prerequisites
- Python 3.10 or later (
python3 --versionto check) uvorpipfor package management- Claude Desktop (free) or MCP Inspector (free, for testing)
- 15 minutes
Step 1: Install the MCP Python SDK
The SDK is maintained by Anthropic. As of July 2026, v1.x is the stable production line. v2 is in pre-release with major improvements — stick with v1 unless you specifically want bleeding-edge features.
mkdir weather-mcp && cd weather-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install "mcp>=1.0,<2"
The <2 upper bound is important. Without it, pip could resolve to a v2 pre-release someday, which has breaking changes.
Step 2: Write the Server
Create server.py. One tool, one job: return weather data for a city. We use mock data for the demo.
from mcp.server import Server
from mcp.server.stdio import stdio_server
import asyncio
server = Server("weather-server")
@server.tool()
async def get_weather(city: str) -> str:
"""Get current weather for a given city."""
weather_data = {
"jakarta": "32°C, partly cloudy, humidity 75%",
"singapore": "30°C, thunderstorm, humidity 85%",
"tokyo": "22°C, clear sky, humidity 45%",
"london": "15°C, light rain, humidity 80%",
}
return weather_data.get(
city.lower(),
f"No data for {city}. Try Jakarta, Singapore, Tokyo, or London."
)
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
asyncio.run(main())
That is a complete MCP server. The SDK handles JSON-RPC transport, generates JSON Schema from your type hints, and validates inputs. You wrote none of that infrastructure code.
Step 3: Test with MCP Inspector
Before wiring it to Claude, test independently. The MCP Inspector is a browser-based debugging tool that connects to any MCP server.
npx @modelcontextprotocol/inspector python3 server.py
This opens a browser tab. Your get_weather tool shows up in the UI. Try passing city: "tokyo" — you should see the result immediately.
Step 4: Add a Resource
Tools perform actions. Resources expose data. Let's add a resource that lists available cities.
@server.resource("weather://cities")
async def list_cities() -> str:
"""List all cities with weather data available."""
return "jakarta, singapore, tokyo, london"
Now the LLM can ask "what cities do you have?" without calling a tool. Resources are read-only — most hosts don't require user approval for them.
Step 5: Connect to Claude Desktop
Claude Desktop ships with MCP support. Open the config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "python3",
"args": ["/absolute/path/to/weather-mcp/server.py"]
}
}
}
Use an absolute path — relative paths and ~ don't work in this config. Restart Claude Desktop. Type "what's the weather in Tokyo?" — Claude should detect the tool, ask permission, and return the result.
If Claude doesn't see it, check Developer > MCP Logs inside Claude Desktop.
When to Use MCP vs Alternatives
MCP is not the only path to give LLMs tools. Here's a practical breakdown.
Use MCP when:
- Multiple AI apps need the same tools and data sources
- You want one tool server that works with Claude, ChatGPT, Cursor, and future MCP hosts
- You're building internal platform capabilities that different teams should reuse
Skip MCP when:
- You're prototyping a single-use tool for one specific app
- You're already deep in a framework with its own tool system (LangChain tools, OpenAI function calling with Assistants API)
- Your tool needs sub-millisecond latency embedded in the app's runtime
MCP plus framework tools is often the right answer. Use MCP for broad capabilities (database access, filesystem, APIs). Use framework-native tools for app-specific logic that doesn't need to be shared.
Common Mistakes
Printing to stdout from your server. The stdio transport uses stdout for JSON-RPC messages. Any stray print() corrupts the protocol. Use logging to stderr — the SDK gives you server.logger for this.
Forgetting the <2 upper bound on install. Pip resolves to v1.x today, but when v2 goes stable, an unpinned install switches behavior silently.
Using relative paths in Claude Desktop config. The config parser doesn't expand ~ or resolve relative paths. Full path only.
No error handling in tools. Unhandled exceptions in your tool give the LLM a generic error with no useful context. Wrap API calls in try/except and return human-readable error messages.
Next Steps
- Swap the mock data for a real API — OpenWeatherMap's free tier gives you 1,000 calls per day
- Build a database server that lets Claude query PostgreSQL or SQLite through MCP
- Browse the MCP server registry — hundreds of ready-made servers for GitHub, Slack, filesystem, and more
- Try the MCP Python SDK v2 pre-release if you want streaming, Elicitation API, and better error handling