Core Concepts
Reasoning
Memory & Retrieval
Agent Types
Design Patterns
Training & Alignment
Frameworks
Tools
Safety & Security
Evaluation
Meta
Core Concepts
Reasoning
Memory & Retrieval
Agent Types
Design Patterns
Training & Alignment
Frameworks
Tools
Safety & Security
Evaluation
Meta
A practical guide to the Model Context Protocol from zero to working server. MCP is the open standard that lets AI models connect to external tools, databases, and services — a universal protocol supported by Claude, ChatGPT, Cursor, VS Code Copilot, and more.
MCP (Model Context Protocol) is an open standard created by Anthropic that standardizes how AI models communicate with external tools and data sources. Think of it as USB-C for AI — one protocol that works across clients and servers.
An MCP server exposes three types of capabilities:
| Capability | What It Does | Example |
|---|---|---|
| Tools | Functions the AI can call | Query a database, call an API, run a calculation |
| Resources | Data the AI can read | Config files, logs, system info, database records |
| Prompts | Reusable interaction templates | Code review template, analysis workflow |
How it works:
| Transport | How It Works | Best For |
|---|---|---|
| stdio | Client spawns server as subprocess, communicates via stdin/stdout | Local integrations, Claude Desktop, Claude Code |
| HTTP + SSE | Server runs as HTTP service, uses Server-Sent Events for streaming | Remote servers, cloud deployments |
| Streamable HTTP | Modern HTTP-based transport with bidirectional streaming | New deployments, replaces SSE |
# Recommended: use uv (fast Python package manager) curl -LsSf https://astral.sh/uv/install.sh | sh # Create a new project mkdir my-mcp-server && cd my-mcp-server uv init --python 3.11 uv add "mcp[cli]" # Alternative: pip pip install "mcp[cli]"
The FastMCP framework handles all protocol boilerplate. You just define tools, resources, and prompts with decorators.
# server.py from mcp.server.fastmcp import FastMCP app = FastMCP(name="my-server") @app.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" return a + b if __name__ == "__main__": app.run()
# Run it uv run python server.py # Or: python3 server.py
Tools are functions the AI can call. The SDK reads type hints and docstrings to generate the JSON schema that clients see.
from mcp.server.fastmcp import FastMCP import httpx app = FastMCP(name="utility-server") @app.tool() def calculate(expression: str) -> str: """Evaluate a mathematical expression safely. Args: expression: A math expression like '2 + 3 * 4' """ try: # Safe eval for math only allowed = set("0123456789+-*/.() ") if not all(c in allowed for c in expression): return "Error: Only math expressions allowed" result = eval(expression) return str(result) except Exception as e: return f"Error: {e}" @app.tool() async def fetch_url(url: str) -> str: """Fetch the content of a URL. Args: url: The URL to fetch (must start with https://) """ if not url.startswith("https://"): return "Error: Only HTTPS URLs are allowed" async with httpx.AsyncClient() as client: response = await client.get(url, follow_redirects=True) return response.text[:5000] # Limit response size @app.tool() def search_notes(query: str) -> list[str]: """Search through saved notes by keyword. Args: query: Search term to look for in notes """ # Example: search a local file or database notes = [ "Meeting with team on Monday at 10am", "Project deadline is March 30th", "Remember to update the API docs", ] return [n for n in notes if query.lower() in n.lower()]
Resources provide data that the AI can read on demand.
import json import platform from datetime import datetime @app.resource("config://app") def get_app_config() -> str: """Return the application configuration.""" config = { "version": "1.0.0", "environment": "development", "features": ["search", "analytics"] } return json.dumps(config, indent=2) @app.resource("system://info") def get_system_info() -> str: """Return current system information.""" return json.dumps({ "platform": platform.system(), "python_version": platform.python_version(), "timestamp": datetime.now().isoformat() }, indent=2) # Dynamic resources with URI templates @app.resource("notes://{note_id}") def get_note(note_id: str) -> str: """Get a specific note by ID.""" notes_db = { "1": "Meeting notes from Monday standup", "2": "Architecture decision: use PostgreSQL", "3": "TODO: Implement caching layer" } return notes_db.get(note_id, f"Note {note_id} not found")
Prompts are reusable templates that clients can offer to users.
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp.prompts import base @app.prompt() def code_review(code: str, language: str = "python") -> str: """Generate a thorough code review. Args: code: The code to review language: Programming language """ return f"""Please review the following {language} code. Check for: 1. Bugs and logic errors 2. Security vulnerabilities 3. Performance issues 4. Code style and readability 5. Missing error handling Code: ```{language} {code} ``` Provide specific, actionable feedback.""" @app.prompt() def debug_error(error_message: str, stack_trace: str = "") -> str: """Help debug an error. Args: error_message: The error message stack_trace: Optional stack trace """ context = f"Error: {error_message}" if stack_trace: context += f"\n\nStack trace:\n{stack_trace}" return f"""Help me debug this error. Explain what likely caused it and suggest fixes. {context}"""
Here is a full server combining tools, resources, and prompts:
# server.py — Complete MCP Server from mcp.server.fastmcp import FastMCP import json import platform import httpx from datetime import datetime app = FastMCP( name="demo-server", version="1.0.0" ) # ---- TOOLS ---- @app.tool() def calculate(expression: str) -> str: """Evaluate a math expression. Example: '2 + 3 * 4'""" allowed = set("0123456789+-*/.() ") if not all(c in allowed for c in expression): return "Error: Only math expressions allowed" try: return str(eval(expression)) except Exception as e: return f"Error: {e}" @app.tool() async def fetch_url(url: str) -> str: """Fetch content from a URL. Only HTTPS allowed.""" if not url.startswith("https://"): return "Error: HTTPS required" async with httpx.AsyncClient() as client: resp = await client.get(url, follow_redirects=True, timeout=10) return resp.text[:5000] @app.tool() def search_notes(query: str) -> list[str]: """Search notes by keyword.""" notes = [ "Team standup moved to 10am", "Deploy v2.0 by Friday", "Review PR #42 for auth changes", ] return [n for n in notes if query.lower() in n.lower()] # ---- RESOURCES ---- @app.resource("config://app") def get_config() -> str: """Application configuration.""" return json.dumps({"version": "1.0.0", "env": "dev"}, indent=2) @app.resource("system://info") def get_system_info() -> str: """Current system information.""" return json.dumps({ "platform": platform.system(), "python": platform.python_version(), "time": datetime.now().isoformat() }, indent=2) # ---- PROMPTS ---- @app.prompt() def code_review(code: str, language: str = "python") -> str: """Review code for bugs, security, and style.""" return f"Review this {language} code for bugs, security issues, and style:\n```{language}\n{code}\n```" # ---- RUN ---- if __name__ == "__main__": app.run()
Configure Claude Desktop to discover your MCP server.
Location:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json~/.config/Claude/claude_desktop_config.json{
"mcpServers": {
"demo-server": {
"command": "uv",
"args": ["run", "--directory", "/path/to/my-mcp-server", "python", "server.py"]
}
}
}
Or with pip/python directly:
{
"mcpServers": {
"demo-server": {
"command": "python3",
"args": ["/path/to/my-mcp-server/server.py"]
}
}
}
After saving, restart Claude Desktop. Your tools will appear in the tools menu.
# Add server to Claude Code claude mcp add demo-server -- uv run --directory /path/to/my-mcp-server python server.py # Or add to project config (.claude/settings.json) claude mcp add --scope project demo-server -- python3 /path/to/server.py # List configured servers claude mcp list # Test the server claude mcp serve demo-server
For custom applications, build your own MCP client to connect to any MCP server.
# client.py — MCP Client Example from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client import asyncio async def main(): # Connect to an MCP server via stdio server_params = StdioServerParameters( command="python3", args=["server.py"] ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: # Initialize the connection await session.initialize() # List available tools tools = await session.list_tools() print("Available tools:") for tool in tools.tools: print(f" - {tool.name}: {tool.description}") # Call a tool result = await session.call_tool( "calculate", arguments={"expression": "42 * 17 + 3"} ) print(f"\nCalculation result: {result.content[0].text}") # List resources resources = await session.list_resources() print("\nAvailable resources:") for resource in resources.resources: print(f" - {resource.uri}: {resource.name}") # Read a resource content = await session.read_resource("config://app") print(f"\nConfig: {content.contents[0].text}") # List prompts prompts = await session.list_prompts() print("\nAvailable prompts:") for prompt in prompts.prompts: print(f" - {prompt.name}: {prompt.description}") if __name__ == "__main__": asyncio.run(main())
The MCP Inspector is a visual tool for testing servers interactively.
# Install and run npx @modelcontextprotocol/inspector uv run python server.py # Opens a web UI where you can: # - See all tools, resources, and prompts # - Call tools with custom arguments # - Read resources # - View raw JSON-RPC messages
For remote/cloud deployment, use the SSE transport instead of stdio.
# server_remote.py from mcp.server.fastmcp import FastMCP app = FastMCP( name="remote-server", host="0.0.0.0", port=8080 ) @app.tool() def hello(name: str) -> str: """Say hello.""" return f"Hello, {name}!" if __name__ == "__main__": app.run(transport="sse") # or transport="streamable-http"
Connect from Claude Desktop:
{
"mcpServers": {
"remote-server": {
"url": "http://localhost:8080/sse"
}
}
}
async def for tools that make network calls or read filesmcp model-context-protocol tools protocol claude how-to