Introduction to Model Context Protocol · lesson 5 of 6
Building an MCP Server (Python)
The "Introduction to MCP" course uses the Python SDK throughout. Memorize the basic shape.
Install
uv add "mcp[cli]"
# or
pip install mcp
Minimal stdio server
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
app = Server("hello-mcp")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="echo",
description="Echo a string back. Use to verify the server is reachable.",
inputSchema={
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "echo":
return [TextContent(type="text", text=arguments["text"])]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read, write):
await app.run(read, write, app.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())
Wiring to Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"hello": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}
Restart Claude Desktop. The tool appears in the tools panel.
FastMCP shorthand
The high-level FastMCP API hides the boilerplate:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-mcp")
@mcp.tool()
def echo(text: str) -> str:
"""Echo a string back. Use to verify the server is reachable."""
return text
@mcp.resource("file://greeting")
def greeting() -> str:
"""A static greeting."""
return "Hello, world!"
@mcp.prompt()
def summarize(text: str) -> str:
"""Produce a summary prompt."""
return f"Summarize:\n\n{text}"
if __name__ == "__main__":
mcp.run()
The decorated function name + docstring become the tool name + description. Type hints become the JSON schema.
Logging & debugging
- Use
mcp inspector(run vianpx @modelcontextprotocol/inspector) to manually call tools, list resources, inspect responses. - Log to stderr (not stdout) when using stdio transport — stdout is the wire.
- Set
MCP_LOG_LEVEL=debugin the environment for verbose logs.