--- title: Mounting Servers sidebarTitle: Mounting description: Compose servers by mounting one inside another icon: puzzle-piece --- import { VersionBadge } from '/snippets/version-badge.mdx' Mounting lets you combine multiple FastMCP servers into one. When you mount a server, all its components become available through the parent. Under the hood, FastMCP uses `FastMCPProvider` (v3.0.0+) to source components from the mounted server. ## Why Mount Servers Large applications benefit from modular organization. Rather than defining all components in one massive file, create focused servers for specific domains and combine them: - **Modularity**: Break down applications into smaller, focused servers - **Reusability**: Create utility servers and mount them wherever needed - **Teamwork**: Different teams can work on separate servers - **Organization**: Keep related functionality grouped together ## Basic Mounting Use `mount()` to add another server's components to your server: ```python from fastmcp import FastMCP # Create focused subservers weather_server = FastMCP("Weather") @weather_server.tool def get_forecast(city: str) -> str: """Get weather forecast for a city.""" return f"Sunny in {city}" @weather_server.resource("data://cities") def list_cities() -> list[str]: """List supported cities.""" return ["London", "Paris", "Tokyo"] # Create main server and mount the subserver main = FastMCP("MainApp") main.mount(weather_server) # Now main has access to get_forecast and data://cities ``` ## Mounting External Servers Mount remote HTTP servers or subprocess-based MCP servers using `create_proxy()`: ```python from fastmcp import FastMCP from fastmcp.server import create_proxy mcp = FastMCP("Orchestrator") # Mount a remote HTTP server (URLs work directly) mcp.mount(create_proxy("http://api.example.com/mcp"), namespace="api") # Mount local Python scripts (file paths work directly) mcp.mount(create_proxy("./my_server.py"), namespace="local") ``` ### Mounting npm/uvx Packages For npm packages or Python tools, use the config dict format: ```python from fastmcp import FastMCP from fastmcp.server import create_proxy mcp = FastMCP("Orchestrator") # Mount npm package via config github_config = { "mcpServers": { "default": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"] } } } mcp.mount(create_proxy(github_config), namespace="github") # Mount Python tool via config sqlite_config = { "mcpServers": { "default": { "command": "uvx", "args": ["mcp-server-sqlite", "--db", "data.db"] } } } mcp.mount(create_proxy(sqlite_config), namespace="db") ``` Or use explicit transport classes: ```python from fastmcp import FastMCP from fastmcp.server import create_proxy from fastmcp.client.transports import NpxStdioTransport, UvxStdioTransport mcp = FastMCP("Orchestrator") mcp.mount( create_proxy(NpxStdioTransport(package="@modelcontextprotocol/server-github")), namespace="github" ) mcp.mount( create_proxy(UvxStdioTransport(tool_name="mcp-server-sqlite", tool_args=["--db", "data.db"])), namespace="db" ) ``` For advanced configuration, see [Proxying](/servers/providers/proxy). ## Namespacing When mounting multiple servers, use namespaces to avoid naming conflicts: ```python weather = FastMCP("Weather") calendar = FastMCP("Calendar") @weather.tool def get_data() -> str: return "Weather data" @calendar.tool def get_data() -> str: return "Calendar data" main = FastMCP("Main") main.mount(weather, namespace="weather") main.mount(calendar, namespace="calendar") # Tools are now: # - weather_get_data # - calendar_get_data ``` ### How Namespacing Works | Component Type | Without Namespace | With `namespace="api"` | |----------------|-------------------|------------------------| | Tool | `my_tool` | `api_my_tool` | | Prompt | `my_prompt` | `api_my_prompt` | | Resource | `data://info` | `data://api/info` | | Template | `data://{id}` | `data://api/{id}` | Namespacing uses [transforms](/servers/providers/transforms) under the hood. ## Mounting vs Importing FastMCP offers two ways to combine servers: | Feature | `mount()` | `import_server()` | |---------|-----------|-------------------| | **Link Type** | Live (dynamic) | One-time copy (static) | | **Updates** | Changes reflected immediately | Changes not reflected | | **Performance** | Runtime delegation | Faster - no delegation | | **Use Case** | Modular runtime composition | Bundling finalized components | ### Live Mounting With `mount()`, changes to the subserver are immediately reflected: ```python main = FastMCP("Main") main.mount(dynamic_server, namespace="dynamic") # Add a tool AFTER mounting - it's accessible through main @dynamic_server.tool def added_later() -> str: return "Added after mounting!" # This works because mount() creates a live link ``` ### Static Importing With `import_server()`, components are copied once at import time: ```python main = FastMCP("Main") async def setup(): await main.import_server(static_server, namespace="static") # Changes to static_server after this point are NOT reflected in main ``` ## Direct vs Proxy Mounting FastMCP supports two mounting modes: ### Direct Mounting (Default) The parent server directly accesses the mounted server's objects in memory: ```python main.mount(subserver, namespace="api") ``` - No client lifecycle events on mounted server - Mounted server's lifespan is not executed - Communication via direct method calls ### Proxy Mounting The `as_proxy` parameter is deprecated. Mounted servers now always have their lifespan and middleware invoked. To create a proxy server explicitly, use `create_proxy()` from `fastmcp.server`. Previously, the parent server could treat the mounted server as a separate entity with its own lifecycle. This behavior is now the default for all mounted servers: - Full client lifecycle events on mounted server - Mounted server's lifespan is executed - Communication via in-memory Client transport ## Tag Filtering Parent server tag filters apply recursively to mounted servers: ```python api_server = FastMCP("API") @api_server.tool(tags={"production"}) def prod_endpoint() -> str: return "Production data" @api_server.tool(tags={"development"}) def dev_endpoint() -> str: return "Debug data" # Mount with production filter prod_app = FastMCP("Production") prod_app.mount(api_server, namespace="api") prod_app.enable(tags={"production"}, only=True) # Only prod_endpoint (namespaced as api_prod_endpoint) is visible ``` ## Performance Considerations When using live mounting, operations like `list_tools()` on the parent server are affected by the performance of all mounted servers. This is particularly noticeable with: - HTTP-based mounted servers (300-400ms vs 1-2ms for local tools) - Mounted servers with slow initialization - Deep mounting hierarchies If low latency is critical, consider: - Using `import_server()` for static composition - Implementing caching strategies - Limiting mounting depth ## Custom Routes Custom HTTP routes defined with `@server.custom_route()` are also forwarded when mounting: ```python subserver = FastMCP("Sub") @subserver.custom_route("/health", methods=["GET"]) async def health_check(): return {"status": "ok"} main = FastMCP("Main") main.mount(subserver, namespace="sub") # /health is now accessible through main's HTTP app ``` ## Conflict Resolution When mounting multiple servers with the same namespace (or no namespace), the **most recently mounted** server takes precedence for conflicting component names: ```python server_a = FastMCP("A") server_b = FastMCP("B") @server_a.tool def shared_tool() -> str: return "From A" @server_b.tool def shared_tool() -> str: return "From B" main = FastMCP("Main") main.mount(server_a) main.mount(server_b) # shared_tool returns "From B" (most recently mounted) ```