MCP server composition is connecting multiple specialized servers to your AI agent simultaneously. Your agent talks to your calendar, database, and file system through the same protocol. David Soria Parra , who co-created MCP at Anthropic, designed the protocol specifically to solve this integration problem.
Why compose servers
Single-purpose servers stay maintainable. A calendar server handles scheduling. A database server handles queries. A file server handles documents. Composition lets you combine them without merging codebases.
[Claude Code]
↓
[MCP Client]
├── [Calendar Server] → Google Calendar
├── [Database Server] → PostgreSQL
├── [File Server] → Local filesystem
└── [Search Server] → Exa, web search
One request can span multiple servers. “Find my meeting notes from last Tuesday and add a follow-up task” hits calendar, files, and tasks.
Configuration
Claude Code connects to multiple servers through ~/.claude/mcp.json:
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/docs"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxx"
}
},
"calendar": {
"command": "python",
"args": ["/path/to/calendar_server.py"]
},
"memory": {
"command": "npx",
"args": ["-y", "episodic-memory"]
}
}
}
Each server runs as a separate process. Claude Code manages the connections.
FastMCP composition patterns
FastMCP provides two methods for combining servers in Python:
Import (static copy)
Copy tools from one server into another at startup:
from mcp.server.fastmcp import FastMCP
main = FastMCP("main")
weather = FastMCP("weather")
calendar = FastMCP("calendar")
@weather.tool()
def get_forecast(city: str) -> str:
"""Get weather forecast."""
return f"Sunny in {city}"
@calendar.tool()
def next_meeting() -> str:
"""Get next calendar event."""
return "Team standup at 10am"
# Copy tools with prefix
main.import_server(weather, prefix="weather")
main.import_server(calendar, prefix="cal")
# Tools available as: weather_get_forecast, cal_next_meeting
Import works when you control all servers and want a single deployment.
Mount (dynamic delegation)
Route requests to sub-servers at runtime:
from mcp.server.fastmcp import FastMCP
main = FastMCP("main")
weather = FastMCP("weather")
database = FastMCP("database")
@weather.tool()
def forecast(city: str) -> str:
return f"Weather for {city}"
@database.tool()
def query(sql: str) -> str:
return f"Results for: {sql}"
# Live link - requests delegated to subservers
main.mount("weather", weather)
main.mount("db", database)
Mount keeps servers isolated. Changes to subservers reflect immediately. Use this for modular architectures where teams maintain separate servers.
| Method | When to use |
|---|---|
import_server() | Single deployment, want all tools in one process |
mount() | Microservices, separate teams, runtime flexibility |
Practical compositions
Developer workflow
{
"servers": {
"github": { "command": "..." },
"filesystem": { "command": "..." },
"memory": { "command": "..." },
"shell": { "command": "..." }
}
}
Handles: code review, file operations, cross-session context, command execution.
Research workflow
{
"servers": {
"exa": { "command": "..." },
"web-fetch": { "command": "..." },
"memory": { "command": "..." },
"obsidian": { "command": "..." }
}
}
Handles: web search, URL scraping, remembering findings, note storage.
Productivity workflow
{
"servers": {
"google-calendar": { "command": "..." },
"things": { "command": "..." },
"slack": { "command": "..." },
"memory": { "command": "..." }
}
}
Handles: scheduling, task management, team communication, context retention.
Name conflicts
When multiple servers expose similar tools, prefixing prevents collisions:
# Without prefix
main.import_server(server_a) # has "search" tool
main.import_server(server_b) # also has "search" - overwrites!
# With prefix
main.import_server(server_a, prefix="files") # files_search
main.import_server(server_b, prefix="web") # web_search
With mounted servers in Claude Code, tools from each server already namespace by server name in the interface.
November 2025 spec: Tasks
The 2025-11-25 MCP specification added Tasks for long-running operations. A tool call returns a task handle instead of blocking:
@mcp.tool()
async def analyze_dataset(path: str) -> Task:
"""Start async analysis, return task handle."""
task = create_task()
# Processing happens in background
background_analyze(path, task.id)
return task
Clients poll for completion. This matters for composed systems where one request triggers work across multiple servers, some fast, some slow.
Tasks enable:
- Data processing jobs that run for minutes
- Multi-agent coordination where sub-agents work concurrently
- Test suites that stream results as they complete
- Research tools that spawn parallel searches
Sampling with tools
The November 2025 spec also added Sampling with Tools . Servers can request LLM completions from the client, with tool definitions included.
A composed server can:
- Receive a request
- Call another server’s tool
- Ask the client’s LLM to process results
- Return the final answer
Server-side agent loops become possible without managing API keys in each server.
Debugging composition
| Problem | Check |
|---|---|
| Tool not found | claude mcp list shows all servers? |
| Wrong server responding | Tool names unique across servers? |
| Server not starting | Run server command manually to see errors |
| Timeout | Server process running? Check logs |
Logs live at ~/.claude/mcp/[server-name]/logs/.
When to compose vs. build
| Situation | Approach |
|---|---|
| Tools exist as separate servers | Compose via config |
| Building tightly coupled features | Single server with FastMCP mount |
| Team owns different capabilities | Separate servers, compose at runtime |
| Need to share server with others | Single-purpose server, let users compose |
The MCP Registry lists close to 2,000 servers. Check there before building from scratch.
related
- Building MCP Servers — hands-on guide to creating MCP servers
- MCP Server Stacking — advanced composition patterns
- Tool Routing — selecting the right tool for each task
- Multi-Agent Knowledge Management — coordinating specialized agents
- Subagent Patterns — dispatch strategies for agent composition