Repository navigation
Description
Description
Summary
The current Python SDK docs and examples focus on minimal, single-file servers (e.g. weather.py with all tools in one module). This is great for getting started but leaves a gap for users who are building larger, multi-tool or multi-team MCP servers.
With modelcontextprotocol/modelcontextprotocol#1575 introducing tool versioning support, it would be useful to document a recommended FastMCP server layout that scales well, including:
- A clear separation between MCP wiring and tool implementations.
- How to organize tools into modules.
- How to implement and register multiple versions of a tool in Python.
Motivation
As soon as a server grows beyond a couple of tools, authors start reinventing patterns:
- One giant
server.pyfile with all tools mixed in. - Ad-hoc imports where helpers and MCP wiring are interleaved.
- Inconsistent placement of new versions of tools (new file vs. new function vs. new package).
A light-weight, documented pattern in the Python SDK would help:
- Teams share a consistent “shape” for MCP servers.
- New contributors can navigate repositories more easily.
- Versioned tools from SEP-1575 can be implemented in a predictable way.
Proposed pattern
1. Server entrypoint (server.py)
- A single entrypoint file that handles:
- Creating the
FastMCPinstance. - Configuring transports and logging.
- Registering tools imported from
tools/. - The
main()function andif __name__ == "__main__":guard.
- Creating the
Example (simplified):
# server.py
from mcp.server.fastmcp import FastMCP
from .tools.get_info import get_info_v1, get_info_v2
mcp = FastMCP("example-server")
# Register versioned tools (SEP-1575)
mcp.add_tool(get_info_v1, name="get_info", version="1.0.0")
mcp.add_tool(get_info_v2, name="get_info", version="2.0.0")
def main() -> None:
mcp.run(transport="stdio")
if __name__ == "__main__":
main()2. Tools package (tools/)
-
A Python package
tools/with one module per conceptual tool:tools/get_info.pytools/list_invoices.py- etc.
-
Each module contains:
- Pure business logic helpers.
- One or more versioned handler functions for MCP.
Example:
# tools/get_info.py
from typing import Any
def _get_info_core(resource_id: str) -> dict[str, Any]:
# Shared logic across all versions
...
async def get_info_v1(resource_id: str) -> str:
"""Legacy text-only version."""
data = _get_info_core(resource_id)
return f"{data['name']}\n{data['description']}"
async def get_info_v2(resource_id: str) -> dict[str, Any]:
"""Structured JSON version."""
return _get_info_core(resource_id)The decorators can either live here (using FastMCP as a global) or be applied in server.py when registering tools, depending on the desired separation of concerns.
3. Versioned tools with SEP-1575
- For SEP-1575-based versioning, the recommended SDK usage would be:
# server.py
from mcp.server.fastmcp import FastMCP
from .tools.get_info import get_info_v1, get_info_v2
mcp = FastMCP("example-server")
@mcp.tool(name="get_info", version="1.0.0")
async def get_info_v1_handler(resource_id: str) -> str:
return await get_info_v1(resource_id)
@mcp.tool(name="get_info", version="2.0.0")
async def get_info_v2_handler(resource_id: str) -> dict[str, Any]:
return await get_info_v2(resource_id)Or, using add_tool if that is preferred over decorators.
This shows:
- Stable tool name:
"get_info". - Multiple versions via the
versionargument. - A natural place to factor shared logic.
Proposed changes in this repo
-
Documentation
-
Add a new section under the server docs (e.g.
Server PatternsorScaling your server) describing this layout and its rationale. -
Include a short code example of:
server.pywiring.tools/<tool>.pymodule with multiple versions.- How to use
versionwith FastMCP decorators oradd_tool.
-
-
Example project
-
Add an
examples/versioned_server/example (or similar):-
Uses the proposed layout.
-
Demonstrates:
- Multiple tools in
tools/. - One tool with two versions (
1.x,2.x). - How clients can call different versions (e.g. via
tool_requirementsin a small client script).
- Multiple tools in
-
-
-
Cross-link from tutorials
-
From the existing “Build a server” quickstart, add a short “Next steps” link:
- “For larger projects, see the recommended server layout in [link].”
-
Alternatives / Open Questions
- Should the SDK encourage decorators-in-module vs. decorators-in-server patterns, or present both as valid?
- Should there be a simple CLI template (e.g.
mcp init) that scaffolds this layout? - How tightly should this layout guidance be coupled to SEP-1575 (e.g. only documented once tool versioning is merged and stable)?
How I can help
I am happy to:
- Draft the documentation page for the proposed layout.
- Contribute a small
examples/versioned_server/example aligned with SEP-1575. - Iterate on naming and structure based on maintainer feedback.
References
No response
Activity
maxisbey commentedon Dec 2, 2025
Thanks for putting this together! Unfortunately, SEP-1575 (tool versioning) is not yet accepted and is still in early discussions. The SEP would need to be accepted and part of an upcoming MCP spec release before we add anything about it in the SDK.
Once tool versioning lands in the spec, we'd be happy to revisit documentation and examples for it.
AI Disclaimer