Skip to content

Feature: MCP Gateway — auto-expose PyNest services as Model Context Protocol tools #120

@ItayTheDar

Description

@ItayTheDar

Overview

This is PyNest's biggest opportunity to become the leading Python backend framework for agentic systems.

The Model Context Protocol (MCP) is an open standard (by Anthropic) that defines how AI agents (Claude, GPT, Cursor, Copilot, etc.) discover and invoke tools and resources from backend servers. By adding a McpModule to PyNest, any application built with this framework can expose its services as MCP tools with zero extra code — just decorators.


Motivation

Today, making a PyNest service callable by an AI agent requires:

  1. Writing a separate MCP server (Python SDK)
  2. Manually defining tool schemas (name, description, input JSON schema)
  3. Wiring up routing between MCP requests and service methods
  4. Keeping the tool definitions in sync with the service implementation

With McpModule, all of that disappears:

```python

Before: manual MCP server + PyNest app = two codebases

After: one PyNest app, automatically MCP-compatible

@Injectable
class WeatherService:
@mcptool(
description="Get the current weather forecast for a city",
schema=GetWeatherInput,
)
async def get_weather(self, city: str, units: str = "celsius") -> WeatherReport:
return await self.api.fetch(city, units)
```

This makes PyNest the first Python web framework with native AI agent integration.


Proposed API

McpModule.for_root()

```python
@module(
imports=[
McpModule.for_root(
name="my-api",
version="1.0.0",
transport="stdio", # or "sse", "http"
path="/mcp", # HTTP/SSE mount path
instructions="This server exposes weather and calendar tools.",
)
],
providers=[WeatherService, CalendarService],
...
)
class AppModule:
pass
```

@McpTool() — expose a service method as an MCP tool

```python
from nest.mcp import McpTool

@Injectable
class UserService:

@McpTool(
    name="get_user",                          # defaults to method name
    description="Retrieve a user by their ID",
    schema=GetUserInput,                      # Pydantic model → JSON Schema
)
async def get_user(self, user_id: str) -> UserDto:
    return await self.repo.find(user_id)

@McpTool(description="List all users with optional filter")
async def list_users(self, filter: UserFilterInput | None = None) -> list[UserDto]:
    return await self.repo.find_all(filter)

@McpTool(description="Create a new user account")
async def create_user(self, data: CreateUserInput) -> UserDto:
    return await self.repo.create(data)

```

Tool schema auto-generation: If the method uses Pydantic models as arguments, @McpTool auto-generates the JSON Schema from those type annotations — no schema= required.

@McpResource() — expose data as an MCP resource

```python
from nest.mcp import McpResource

@Injectable
class DocumentService:

@McpResource(
    uri_template="docs://{doc_id}",
    name="document",
    description="Fetch a document by ID",
    mime_type="text/plain",
)
async def get_document(self, doc_id: str) -> str:
    return await self.storage.read(doc_id)

@McpResource(uri="docs://index", description="Index of all documents")
async def list_documents(self) -> list[str]:
    return await self.storage.list()

```

@McpPrompt() — expose prompt templates

```python
from nest.mcp import McpPrompt

@Injectable
class PromptService:

@McpPrompt(
    name="summarize",
    description="Summarize a document using a given style",
)
async def summarize_prompt(self, document: str, style: str = "brief") -> list[McpMessage]:
    return [
        McpMessage(role="user", content=f"Summarize in {style} style:\n\n{document}")
    ]

```


Transport Support

Transport Use Case
stdio Claude Desktop, local CLI agents
sse (Server-Sent Events) Remote agents, web clients — mounted at /mcp by default
http (Streamable HTTP) Production deployments, MCP 2025-03-26 spec

The transport is configured once in McpModule.for_root(). The PyNest app runs both an HTTP REST server (FastAPI) and the MCP server concurrently on the same process.


Auto-discovery

When McpModule is imported, it scans all providers in the module graph for @McpTool, @McpResource, and @McpPrompt decorators at startup (via OnApplicationBootstrap) and registers them automatically. No manual registration needed.


OpenAPI ↔ MCP sync

For controllers that already have @Get / @Post routes with Pydantic response models, McpModule should offer an optional expose_routes=True flag that auto-generates MCP tool definitions from the existing OpenAPI schema:

```python
McpModule.for_root(
expose_routes=True, # auto-convert OpenAPI routes to MCP tools
route_prefix="/api", # only expose routes under this prefix
)
```


Full Example — Agentic API

```python

main.py

from nest.core import PyNestFactory
from nest.mcp import McpModule

app_module = AppModule # includes McpModule.for_root(transport="sse")

nest_app = await PyNestFactory.create(app_module)
http_server = nest_app.get_http_server()

Both REST + MCP available:

POST /users → REST

GET /mcp → MCP SSE stream (for Claude/agents)

GET /mcp/tools → list available tools (debugging)

uvicorn.run(http_server, host="0.0.0.0", port=3000)
```

Claude Desktop config (zero code on user side):

```json
{
"mcpServers": {
"my-api": {
"url": "http://localhost:3000/mcp"
}
}
}
```


Acceptance Criteria

  • McpModule in nest/mcp/ with for_root(name, version, transport, path, instructions) factory
  • @McpTool(name?, description, schema?) decorator — auto-generates JSON Schema from Pydantic type hints
  • @McpResource(uri_template?, uri?, name, description, mime_type?) decorator
  • @McpPrompt(name, description) decorator returning list[McpMessage]
  • Auto-discovery of decorated methods at startup via provider scan
  • stdio transport support (for local agents and Claude Desktop)
  • sse transport support (Server-Sent Events, mounted at configurable path)
  • http (Streamable HTTP) transport support per MCP 2025-03-26 spec
  • Both REST and MCP transports run on the same FastAPI instance
  • expose_routes=True auto-converts OpenAPI routes to MCP tools
  • mcp optional dependency (pip install pynest-api[mcp])
  • pynest generate mcp-server CLI scaffold command
  • Unit tests for tool schema generation from Pydantic models
  • Integration test: MCP client calls a tool and receives the correct response
  • Documentation page with Claude Desktop and programmatic client examples

Dependencies


Why This Is the Differentiator

No other Python web framework has native MCP support. Adding this makes PyNest the natural choice for any developer building backend services that need to be consumed by AI agents — which is fast becoming the dominant use case for APIs. It turns every PyNest app into an agent-ready service with a single module import.


Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions