Server Composition
Combine multiple FastMCP servers into a single, larger application using mounting and importing.
New in version: 2.2.0
As your MCP applications grow, you might want to organize your tools, resources, and prompts into logical modules or reuse existing server components. FastMCP supports composition through two methods:
import_server
: For a one-time copy of components with prefixing (static composition).mount
: For creating a live link where the main server delegates requests to the subserver (dynamic composition).
Why Compose Servers?
- Modularity: Break down large applications into smaller, focused servers (e.g., a
WeatherServer
, aDatabaseServer
, aCalendarServer
). - Reusability: Create common utility servers (e.g., a
TextProcessingServer
) and mount them wherever needed. - Teamwork: Different teams can work on separate FastMCP servers that are later combined.
- Organization: Keep related functionality grouped together logically.
Importing vs Mounting
The choice of importing or mounting depends on your use case and requirements.
Feature | Importing | Mounting |
---|---|---|
Method | FastMCP.import_server() | FastMCP.mount() |
Composition Type | One-time copy (static) | Live link (dynamic) |
Updates | Changes to subserver NOT reflected | Changes to subserver immediately reflected |
Best For | Bundling finalized components | Modular runtime composition |
Proxy Servers
FastMCP supports MCP proxying, which allows you to mirror a local or remote server in a local FastMCP instance. Proxies are fully compatible with both importing and mounting.
Importing (Static Composition)
The import_server()
method copies all components (tools, resources, templates, prompts) from one FastMCP
instance (the subserver) into another (the main server). A prefix
is added to avoid naming conflicts.
How Importing Works
When you call await main_mcp.import_server(prefix, subserver)
:
- Tools: All tools from
subserver
are added tomain_mcp
with names prefixed using{prefix}_
.subserver.tool(name="my_tool")
becomesmain_mcp.tool(name="{prefix}_my_tool")
.
- Resources: All resources are added with URIs prefixed using
{prefix}+
.subserver.resource(uri="data://info")
becomesmain_mcp.resource(uri="{prefix}+data://info")
.
- Resource Templates: Templates are prefixed similarly to resources.
subserver.resource(uri="data://{id}")
becomesmain_mcp.resource(uri="{prefix}+data://{id}")
.
- Prompts: All prompts are added with names prefixed like tools.
subserver.prompt(name="my_prompt")
becomesmain_mcp.prompt(name="{prefix}_my_prompt")
.
Note that import_server
performs a one-time copy of components. Changes made to the subserver
after importing will not be reflected in main_mcp
. The subserver
’s lifespan
context is also not executed by the main server.
Mounting (Live Linking)
The mount()
method creates a live link between the main_mcp
server and the subserver
. Instead of copying components, requests for components matching the prefix
are delegated to the subserver
at runtime.
How Mounting Works
When mounting is configured:
- Live Link: The parent server establishes a connection to the mounted server.
- Dynamic Updates: Changes to the mounted server are immediately reflected when accessed through the parent.
- Prefixed Access: The parent server uses prefixes to route requests to the mounted server.
- Delegation: Requests for components matching the prefix are delegated to the mounted server at runtime.
The same prefixing rules apply as with import_server
for naming tools, resources, templates, and prompts.
Direct vs. Proxy Mounting
New in version: 2.2.7
FastMCP supports two mounting modes:
- Direct Mounting (default): The parent server directly accesses the mounted server’s objects in memory.
- No client lifecycle events occur on the mounted server
- The mounted server’s lifespan context is not executed
- Communication is handled through direct method calls
- Proxy Mounting: The parent server treats the mounted server as a separate entity and communicates with it through a client interface.
- Full client lifecycle events occur on the mounted server
- The mounted server’s lifespan is executed when a client connects
- Communication happens via an in-memory Client transport
FastMCP automatically uses proxy mounting when the mounted server has a custom lifespan, but you can override this behavior with the as_proxy
parameter.
Interaction with Proxy Servers
When using FastMCP.from_client()
to create a proxy server, mounting that server will always use proxy mounting:
Customizing Separators
Both import_server()
and mount()
allow you to customize the separators used for prefixing components. The defaults are _
for tools and prompts, and +
for resources.
Be cautious when choosing separators. Some MCP clients (like Claude Desktop) might have restrictions on characters allowed in tool names (e.g., /
might not be supported). The defaults (_
for names, +
for URIs) are generally safe.
To “cleanly” import or mount a server, set the prefix and all separators to ""
(empty string). This is generally unecessary but could save a couple tokens at the risk of a name collision!