Description
Summary
from fastmcp import FastMCP in v3.0.2 takes 2.5–5.5 seconds even for simple stdio tool servers that use no auth, no Redis, no task scheduling, and no runtime type checking. The overhead comes from eager imports of heavy transitive dependencies that are never needed at runtime.
This is different from #2205 (which focused on mcp.types). The MCP SDK has since fixed their lazy-loading (#1508). The remaining bottleneck is FastMCP's own dependency chain.
Environment
- FastMCP 3.0.2 / MCP SDK 1.26.0
- Python 3.13.9, Windows 11
- MetaMcp: HTTP multiplexer proxying 19 MCP backends via
create_proxy()
Impact
We proxy 19 backends via create_proxy(). Each stdio backend spawns a Python process that imports FastMCP. Total cold-start cost across all backends: 100+ seconds. This delays tool availability for clients after a restart.
Profiling Data
Profiled with python -X importtime on 3 real-world servers:
| Backend |
Total import |
FastMCP share |
Own code |
| word-mcp (115 tools, COM automation) |
2.6s |
2.4s (92%) |
0.2s |
| claude-code-admin (124 tools) |
5.0s |
4.3s (86%) |
0.7s |
| google-workspace (138 tools, Google APIs) |
9.3s |
5.6s (60%) |
1.2s |
Breakdown: What FastMCP imports eagerly but never uses
| Module |
Time |
Loaded via |
Used by these servers? |
beartype (runtime type checking) |
~520ms |
docket → dependency injection |
No |
docket + key_value.aio + redis |
~550ms |
fastmcp.server.dependencies |
No (no Redis, no task queue) |
authlib + cryptography (OAuth/JWT) |
~1,100ms |
fastmcp.server.auth.oauth_proxy |
No (stdio servers, no OAuth) |
mcp.client.session |
~1,400ms |
FastMCP internal imports |
No (these are servers, not clients) |
httpx |
~800ms |
Auth proxy, client transports |
No (stdio transport only) |
Total avoidable overhead: ~4.3 seconds for a simple FastMCP("my-server") + @mcp.tool() server.
Self-time leaders
| Module |
Self-time |
What it does |
mcp.types |
150–320ms |
Pydantic model compilation (already addressed in MCP SDK) |
beartype._check.code._pep... |
~49ms |
PEP 484/585 type-check codegen |
key_value.aio.stores.base |
~35ms |
Abstract key-value store |
key_value.aio.stores.redis.store |
~29ms |
Redis adapter (never used) |
fastmcp.server.providers.skills.skill_provider |
~89ms |
Skill provider class defs |
Suggested fix
Lazy-import the following behind their actual use sites:
- Auth stack (
fastmcp.server.auth.oauth_proxy, authlib, cryptography, httpx) → only load when OAuth is configured. Saves ~1.1s
docket + key_value + redis → only load when storage backend is configured or dependency injection is used. Saves ~550ms
beartype → only load when runtime type checking is actually enabled. Saves ~520ms
mcp.client.session → only load in proxy/client contexts, not for pure server usage. Saves ~1.4s
This would bring import fastmcp from ~2.5s down to ~0.3–0.5s for the common case of a simple stdio tool server.
Example Code
# This alone costs 2.5s+ even though no auth, Redis, or beartype is needed:
from fastmcp import FastMCP
mcp = FastMCP("my-simple-tool-server")
@mcp.tool()
def hello(name: str) -> str:
return f"Hello {name}"
Version Information
FastMCP version: 3.0.2
MCP version: 1.26.0
Python version: 3.13.9
Platform: Windows-11-10.0.26200-SP0
Description
Summary
from fastmcp import FastMCPin v3.0.2 takes 2.5–5.5 seconds even for simple stdio tool servers that use no auth, no Redis, no task scheduling, and no runtime type checking. The overhead comes from eager imports of heavy transitive dependencies that are never needed at runtime.This is different from #2205 (which focused on
mcp.types). The MCP SDK has since fixed their lazy-loading (#1508). The remaining bottleneck is FastMCP's own dependency chain.Environment
create_proxy()Impact
We proxy 19 backends via
create_proxy(). Each stdio backend spawns a Python process that imports FastMCP. Total cold-start cost across all backends: 100+ seconds. This delays tool availability for clients after a restart.Profiling Data
Profiled with
python -X importtimeon 3 real-world servers:Breakdown: What FastMCP imports eagerly but never uses
beartype(runtime type checking)docket→ dependency injectiondocket+key_value.aio+redisfastmcp.server.dependenciesauthlib+cryptography(OAuth/JWT)fastmcp.server.auth.oauth_proxymcp.client.sessionhttpxTotal avoidable overhead: ~4.3 seconds for a simple
FastMCP("my-server")+@mcp.tool()server.Self-time leaders
mcp.typesbeartype._check.code._pep...key_value.aio.stores.basekey_value.aio.stores.redis.storefastmcp.server.providers.skills.skill_providerSuggested fix
Lazy-import the following behind their actual use sites:
fastmcp.server.auth.oauth_proxy,authlib,cryptography,httpx) → only load when OAuth is configured. Saves ~1.1sdocket+key_value+redis→ only load when storage backend is configured or dependency injection is used. Saves ~550msbeartype→ only load when runtime type checking is actually enabled. Saves ~520msmcp.client.session→ only load in proxy/client contexts, not for pure server usage. Saves ~1.4sThis would bring
import fastmcpfrom ~2.5s down to ~0.3–0.5s for the common case of a simple stdio tool server.Example Code
Version Information