MCP with Python
Summary​
MCP with .NET showed the C# implementation style. This page adds the Python path so teams can implement MCP servers using FastAPI, async services, and existing Python data tooling.
Python is a strong MCP option when your AI workflows, data pipelines, or orchestration logic already run in Python. The same MCP architecture applies: keep the MCP layer thin, validate inputs, and route business logic to existing services.
| Concern | Python approach | Example |
|---|---|---|
| Tool implementation | Async functions with typed models (Pydantic/dataclasses). | search_tickets uses async def and returns a typed response object with deterministic fields. |
| Validation | Pydantic schemas and explicit guards before executing side effects. | Reject requests where query is empty or status is outside allowed enum values. |
| Transport hosting | Local process for stdio, web app for remote transport. | Start locally for a VS Code host; move to FastAPI + streaming when shared across services. |
| Observability | Structured logs, traces, metrics (OpenTelemetry). | Emit tool_name, duration_ms, status, and correlation ID for each invocation. |
Core Concepts​
Minimal server shape​
from pydantic import BaseModel, Field
class TicketSearchRequest(BaseModel):
query: str = Field(min_length=1)
status: str = "open"
class TicketSearchResult(BaseModel):
ticket_id: str
title: str
status: str
class TicketService:
async def search(self, request: TicketSearchRequest) -> list[TicketSearchResult]:
raise NotImplementedError
Thin MCP adapter pattern​
import logging
from typing import Sequence
logger = logging.getLogger(__name__)
class TicketTools:
def __init__(self, ticket_service: TicketService) -> None:
self.ticket_service = ticket_service
async def search_tickets(self, request: TicketSearchRequest) -> Sequence[TicketSearchResult]:
if not request.query.strip():
raise ValueError("query is required")
logger.info("mcp_tool=search_tickets status=%s", request.status)
return await self.ticket_service.search(request)
Host-side usage with a Python AI app​
# Pseudocode: discover MCP tools, curate them, and expose only safe ones.
approved_tools = await mcp_client.list_tools()
safe_tools = [t for t in approved_tools if t.name in {"search_tickets", "get_ticket_details"}]
response = await llm_client.responses.create(
model="gpt-4.1",
input=messages,
tools=[convert_to_llm_tool(t) for t in safe_tools],
)
Realistic Example​
Scenario​
A support assistant uses Python for orchestration and reporting. Existing enterprise systems remain unchanged:
- Device inventory API (existing service)
- Ticketing API (existing service)
- Security knowledge base (existing content store)
The Python MCP server exposes:
find_devices_by_usersearch_ticketsget_password_policy(as a resource)
The host curates which capabilities are visible by user role, and the server enforces final authorization before execution.
Senior Tech vs Dev Conversation​
Senior Tech: Are we rewriting the .NET services in Python to use MCP?
Dev: No. MCP is the protocol boundary, not a rewrite requirement. Python can orchestrate and adapt existing services, including .NET APIs, as long as contracts remain stable and authorization is enforced.
Senior Tech: Is Python too dynamic for safe tool contracts?
Dev: It can be safe with typed schemas and strict validation. Pydantic models, explicit enums, and structured responses give us predictable contracts similar to typed C# DTOs.
Common Pitfalls​
| Pitfall | What goes wrong | Prevention |
|---|---|---|
| Mixing orchestration and domain logic | MCP layer becomes a second application | Keep business rules in existing services |
| Returning unstructured dictionaries | Host/model integration becomes brittle | Use typed request/response schemas |
| Exposing all tools to every session | Risky capability surface | Curate visible tools per scenario and role |
| Logging without correlation IDs | Multi-hop debugging is slow | Include request or trace IDs in every invocation |
References and Next Steps​
- Next: MCP Role-Play and Quiz →
- Previous: MCP with .NET →
- MCP: modelcontextprotocol.io
- Python typing and validation: docs.pydantic.dev