DEV Community

Dev Gaddam
Dev Gaddam

Posted on

Building Production-Ready MCP Servers in Python: A Complete Guide

Building Production-Ready MCP Servers in Python: A Complete Guide

The Model Context Protocol (MCP) is rapidly becoming the standard for connecting AI agents to external tools and data sources. If you're building AI applications in 2026, understanding MCP isn't optional — it's essential.

What is MCP?

MCP is an open protocol that standardizes how AI models interact with external tools, databases, and services. Think of it as a universal adapter between your AI agent and the rest of the world.

Why it matters:

  • Claude, GPT, Gemini, and other models all support MCP
  • Build once, use with any AI provider
  • Standardized authentication and error handling
  • Growing ecosystem of ready-made MCP servers

Building Your First MCP Server

Here's a minimal but functional MCP server:

`python
from mcp.server import Server
from mcp.types import Tool, TextContent
import asyncio

server = Server("my-mcp-server")

@server.list_tools()
async def list_tools():
return [
Tool(
name="get_weather",
description="Get current weather for a city",
inputSchema={
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
)
]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_weather":
return [TextContent(
type="text",
text=f"Weather in {arguments['city']}: 72F, Sunny"
)]

if name == "main":
asyncio.run(server.run_stdio())
`

Production Features

Error Handling

Never let your MCP server crash:

python
@server.call_tool()
async def call_tool(name: str, arguments: dict):
try:
result = await handle_tool(name, arguments)
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error: {str(e)}")]

Rate Limiting

Protect your server from abuse:

`python
from collections import defaultdict
import time

class RateLimiter:
def init(self, max_requests=100, window=60):
self.max_requests = max_requests
self.window = window
self.requests = defaultdict(list)

def is_allowed(self, client_id):
    now = time.time()
    self.requests[client_id] = [
        t for t in self.requests[client_id] 
        if now - t < self.window
    ]
    if len(self.requests[client_id]) >= self.max_requests:
        return False
    self.requests[client_id].append(now)
    return True
Enter fullscreen mode Exit fullscreen mode

`

Real-World Example: Database Query Tool

`python
import sqlite3
from contextlib import contextmanager

@contextmanager
def get_db(db_path):
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()

@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "query_database":
query = arguments["query"].strip()

    # Security: only allow SELECT queries
    if not query.upper().startswith("SELECT"):
        return [TextContent(
            type="text",
            text="Error: Only SELECT queries allowed"
        )]

    with get_db(arguments["database"]) as db:
        cursor = db.execute(query)
        rows = cursor.fetchall()
        return [TextContent(
            type="text",
            text=str([dict(row) for row in rows])
        )]
Enter fullscreen mode Exit fullscreen mode

`

Deployment

Docker

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ src/
ENV PYTHONPATH=/app/src
CMD ["python", "-m", "my_mcp_server"]

Conclusion

Building production-ready MCP servers in Python is straightforward with the right patterns. Start simple, add features incrementally, and always prioritize error handling and security.

The MCP ecosystem is growing rapidly, and developers who can build and deploy MCP servers will have a significant advantage.


Want to accelerate your MCP development? Check out our AI SaaS Boilerplate — includes MCP server templates and deployment configs ready to go.

Top comments (0)