How to build an MCP server in Python
Direct answer
Use the official mcp Python SDK to build an MCP server with stdio_server from mcp.server.stdio, which handles communication over standard input/output streams.
Setup
Install
pip install mcp Imports
from mcp.server import Server
from mcp.server.stdio import stdio_server Examples
inStart the MCP server and wait for agent requests.
outMCP server started, listening on stdio for requests...
inReceive a JSON request from an AI agent to call a tool.
outReceived request: {'tool': 'calculator', 'input': '2+2'}
Responded with: {'result': 4}
inHandle a malformed request gracefully.
outError: Invalid request format, sending error response.
Integration steps
- Install the official MCP Python SDK with pip.
- Import Server and stdio_server from mcp.server and mcp.server.stdio respectively.
- Define your tool handlers or agent logic as functions or classes.
- Create an MCP Server instance and register your tools.
- Run stdio_server(server) to start the server listening on standard input/output.
- Process incoming requests and send responses via the MCP protocol automatically.
Full code
from mcp.server import Server
from mcp.server.stdio import stdio_server
# Define a simple tool handler
class CalculatorTool:
def call(self, input):
try:
# Evaluate simple math expressions safely
result = eval(input, {"__builtins__": None}, {})
return {"result": result}
except Exception as e:
return {"error": str(e)}
# Create MCP server instance
server = Server()
# Register the tool under a name
server.register_tool("calculator", CalculatorTool())
# Start the MCP server using stdio transport
print("Starting MCP server on stdio...")
stdio_server(server) output
Starting MCP server on stdio... # (Server now waits for JSON requests on stdin and responds on stdout)
API trace
Request
{"tool": "calculator", "input": "2+2"} Response
{"result": 4} Extract
response['result'] or response.get('error')Variants
Async MCP Server with stdio ›
Use when your tool handlers require asynchronous operations or I/O.
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
class AsyncCalculatorTool:
async def call(self, input):
# Async evaluation or external API call
return {"result": eval(input, {"__builtins__": None}, {})}
server = Server()
server.register_tool("calculator", AsyncCalculatorTool())
async def main():
print("Starting async MCP server on stdio...")
await stdio_server(server)
asyncio.run(main()) MCP Server with SSE Transport ›
Use SSE transport for web-based or networked MCP server deployments instead of stdio.
# SSE transport example is conceptual; requires custom SSE server setup
from mcp.server import Server
from mcp.server.sse import sse_server
server = Server()
# Register tools as usual
print("Starting MCP server on SSE transport...")
sse_server(server, host='0.0.0.0', port=8080) Performance
Latency~10-50ms per request depending on tool complexity
CostFree, runs locally with no API cost
Rate limitsNone, limited only by local system resources
- Keep tool input concise to reduce parsing overhead.
- Avoid sending large payloads over stdio to maintain responsiveness.
| Approach | Latency | Cost/call | Best for |
|---|---|---|---|
| Synchronous stdio MCP server | ~10-50ms | Free | Simple local tool integration |
| Asynchronous stdio MCP server | ~10-100ms | Free | Tools requiring async I/O |
| SSE transport MCP server | ~20-100ms | Free | Networked or web-based MCP servers |
Quick tip
Always register your tools with clear, unique names on the MCP Server instance before starting the server.
Common mistake
Beginners often forget to run the server with stdio_server(server), causing no communication over stdio.