Composition
FastMCP’s killer feature over the mcp SDK. Multiple servers can be composed, mounted, proxied, and aggregated.
Mounting (In-Process)
Mount one FastMCP server into another with a namespace prefix:
from fastmcp import FastMCP
from fastmcp.server.providers import FastMCPProvider
# Child server
db_server = FastMCP("Database")
@db_server.tool
def query(sql: str) -> list[dict]:
return db.execute(sql)
# Parent server
main = FastMCP("Main")
@main.tool
def hello() -> str:
return "Hello!"
# Mount child - tools become "db_query"
main.add_provider(FastMCPProvider(db_server, namespace="db"))
When mounted with namespace "db", the child’s query tool becomes db_query on the parent.
Proxy (Remote Servers)
Wrap a remote MCP server so it appears local:
from fastmcp import FastMCP
from fastmcp.server.providers import ProxyProvider
# Proxy a remote SSE server
proxy = FastMCP("Proxy")
proxy.add_provider(
ProxyProvider.from_client(url="http://remote:8000/sse")
)
# Or proxy another local FastMCP instance
proxy = FastMCP.as_proxy(original_server, name="ProxiedServer")
This is powerful for:
- Aggregating multiple remote MCP servers behind a single endpoint
- Adding auth/logging/rate-limiting to existing servers
- Converting transport protocols (stdio server exposed as HTTP)
Aggregation
Multiple providers compose automatically:
main = FastMCP("Gateway")
# Local tools
@main.tool
def status() -> str:
return "ok"
# Mount a database server
main.add_provider(FastMCPProvider(db_server, namespace="db"))
# Mount a file server
main.add_provider(FastMCPProvider(file_server, namespace="files"))
# Proxy a remote API server
main.add_provider(ProxyProvider.from_client(url="http://api:8000/sse"))
The client sees all tools/resources/prompts as a flat list, namespaced to avoid collisions.
OpenAPI Provider
Auto-generate tools from OpenAPI specs (see deep-dives/openapi-import.md for details):
from fastmcp.server.providers.openapi import OpenAPIProvider
main = FastMCP("Gateway")
main.add_provider(
OpenAPIProvider.from_url("https://api.example.com/openapi.json")
)
Namespace Activation
Dynamically enable/disable namespaces at runtime:
# Start with namespace disabled
main.add_provider(FastMCPProvider(admin_server, namespace="admin", enabled=False))
# Enable when needed
main.enable_namespace("admin")
# Disable again
main.disable_namespace("admin")
This maps directly to MyLocalGPT’s “dynamic server selection” pattern - only enable the MCP servers relevant to the current request.
Architecture Pattern
For MyLocalGPT-scale systems, the composition model looks like:
MyLocalGPT Gateway (FastMCP)
+-- LocalProvider (built-in tools: memory, scheduling, etc.)
+-- FastMCPProvider(largefile_server, namespace="largefile")
+-- FastMCPProvider(diffchunk_server, namespace="diffchunk")
+-- FastMCPProvider(md_server, namespace="md")
+-- ProxyProvider(url="http://custom:8000/sse") # remote servers
+-- OpenAPIProvider(url="...") # API imports
Each provider contributes its tools/resources/prompts. The gateway handles transport, auth, and routing.