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.