---
schema: rendered-html/v1
title: "Composing Servers - FastMCP"
source_url: https://gofastmcp.com/servers/composition
generated_at: 2026-06-11T19:39:27.325845+00:00
---




[FastMCP home page![light logo](images/gofastmcp__com__servers__composition__wordmark.avif)![dark logo](images/gofastmcp__com__servers__composition__wordmark-white.avif)](gofastmcp__com.md)







MCP Providers

Composing Servers









  - [Overview

    NEW](gofastmcp__com__servers__providers__overview.md)
  - [Local

    NEW](gofastmcp__com__servers__providers__local.md)
  - [Filesystem

    NEW](gofastmcp__com__servers__providers__filesystem.md)
  - [MCP Proxy](gofastmcp__com__servers__providers__proxy.md)
  - [Skills

    NEW](gofastmcp__com__servers__providers__skills.md)
  - [Composition](/servers/composition)
  - [Custom

    NEW](gofastmcp__com__servers__providers__custom.md)














- [Mounting External Servers](#mounting-external-servers)
  - [Mounting npm/uvx Packages](#mounting-npm%2Fuvx-packages)
- [Namespacing](#namespacing)
  - [How Namespacing Works](#how-namespacing-works)
- [Dynamic Composition](#dynamic-composition)
- [Tag Filtering](#tag-filtering)
- [Performance Considerations](#performance-considerations)
- [Custom Routes](#custom-routes)
- [Conflict Resolution](#conflict-resolution)

MCP Providers

# Composing Servers


Combine multiple servers into one


New in version `2.2.0`
As your application grows, you’ll want to split it into focused servers — one for weather, one for calendar, one for admin — and combine them into a single server that clients connect to. That’s what `mount()` does.
When you mount a server, all its tools, resources, and prompts become available through the parent. The connection is live: add a tool to the child after mounting, and it’s immediately visible through the parent.


weather = FastMCP("Weather")

@weather.tool
def get_forecast(city: str) -> str:
    """Get weather forecast for a city."""
    return f"Sunny in {city}"

@weather.resource("data://cities")
def list_cities() -> list[str]:
    """List supported cities."""
    return ["London", "Paris", "Tokyo"]

main = FastMCP("MainApp")
main.mount(weather)

# main now serves get_forecast and data://cities

## [​](#mounting-external-servers) Mounting External Servers

Mount remote HTTP servers or subprocess-based MCP servers using `create_proxy()`:

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) Mounting npm/uvx Packages

For npm packages or Python tools, use the config dict format:

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:

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](gofastmcp__com__servers__providers__proxy.md).

## [​](#namespacing) Namespacing

When mounting multiple servers, use namespaces to avoid naming conflicts:

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) 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](gofastmcp__com__servers__transforms__transforms.md) under the hood.

## [​](#dynamic-composition) Dynamic Composition

Because `mount()` creates a live link, you can add components to a child server after mounting and they’ll be immediately available through the parent:

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!"

## [​](#tag-filtering) Tag Filtering

Parent server tag filters apply recursively to mounted servers:

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) Performance Considerations

Operations like `list_tools()` on the parent 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 implementing caching strategies or limiting mounting depth.

## [​](#custom-routes) Custom Routes

New in version `2.4.0`
Custom HTTP routes defined with `@server.custom_route()` are also forwarded when mounting:

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) Conflict Resolution

When mounting multiple servers with the same namespace (or no namespace), the **most recently mounted** server takes precedence for conflicting component names:

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)

[Skills Provider

Previous](gofastmcp__com__servers__providers__skills.md)[Custom Providers

Next](gofastmcp__com__servers__providers__custom.md)






