FastMCP class also ships inside the official MCP Python SDK. It can be a full OAuth authorization server and serve Streamable HTTP, so it can satisfy any of MCP Manager’s three authentication modes. This page covers the decisions that matter for running it behind MCP Manager and the gotchas that bite on cloud hosts — it is not a substitute for FastMCP’s own documentation, which is authoritative and moves quickly.
Serve Streamable HTTP — never SSE-only
Run FastMCP over its HTTP transport, which serves Streamable HTTP. The default path is/mcp/ with a trailing slash — point MCP Manager at exactly that. For any multi-instance host (Cloud Run, Kubernetes), run the ASGI app in stateless mode so a request isn’t tied to one process’s memory. See FastMCP — Running the server over HTTP.
Match FastMCP’s auth to an MCP Manager mode
FastMCP offers a ladder of auth providers (all passed asauth= on FastMCP(...)). The decision is which one matches the MCP Manager mode you want.
| You want | Use in FastMCP | Notes |
|---|---|---|
| Standard OAuth + DCR (most seamless) | OAuthProvider (full authorization server), or OAuthProxy / OIDCProxy to front an upstream IdP while still exposing DCR to MCP Manager | Publishes the well-known metadata and a /register endpoint, so MCP Manager self-registers and users just approve |
| OAuth pre-registration | OAuthProxy / OIDCProxy configured with your fixed upstream app credentials | You give MCP Manager a Client ID + Secret instead of relying on DCR |
| Token in a header | TokenVerifier (e.g. JWTVerifier), or a custom token check | Simplest; no metadata or DCR. Maps to MCP Manager’s header-token mode |
TokenVerifier is not enough for the DCR mode — it validates tokens but exposes no /register endpoint and no authorization-server metadata, so MCP Manager can’t auto-register against it. Choose a provider that publishes metadata if you want the seamless path. See FastMCP — Authentication, OAuth Proxy, and OIDC Proxy.
The Cloud Run gotcha you must get right
If you use a FastMCP proxy provider (OAuthProxy / OIDCProxy) and deploy on a multi-instance host, this is the failure that bites: FastMCP’s registered-client store defaults to an in-memory store on Linux, and its default encryption key is ephemeral. MCP Manager registers a client against one instance; the browser authorize request hits another instance that has no record of it, and your server renders a “client not registered” error. It looks intermittent but is structural.
The fix has two parts — getting only the first is a common near-miss:
- Point
client_storageat a shared, network-accessible backend (Redis on a managed cache, or another durable store) so registrations are visible to every instance. See theclient_storageparameter reference. - Use a stable encryption key shared across instances (for example a Fernet key from your secret manager) and a stable
jwt_signing_key, so a second instance can decrypt what the first one wrote.
MCP Manager compatibility checklist
Streamable HTTP, at the exact path
mcp.http_app(...)), point MCP Manager at the real path including the trailing slash (/mcp/), and never ship transport='sse' only.Run stateless on multi-instance hosts
stateless_http=True so a session isn’t bound to one process’s memory behind a load balancer.Pick a provider that publishes metadata (for DCR)
OAuthProvider, OAuthProxy, or OIDCProxy — not a bare TokenVerifier — so the well-known metadata and /register exist.Shared client storage + stable keys
client_storage to a shared backend and use stable encryption / jwt_signing_key values across instances.FastMCP gotchas
The /mcp/ trailing slash
The /mcp/ trailing slash
/mcp/ by default. If MCP Manager (or any client) calls /mcp without the slash and your stack 301-redirects, the redirect can drop the Authorization header. Point MCP Manager at the exact path, or configure the path explicitly.In-memory client store on Linux
In-memory client store on Linux
client_storage and a stable key before deploying. See Debug Self-Hosted OAuth.localhost leaking into metadata
localhost leaking into metadata
base_url is left as http://localhost:..., the published well-known metadata advertises endpoints MCP Manager can’t reach. Set base_url to your public HTTPS URL so issuer, authorization_endpoint, token_endpoint, and registration_endpoint are all reachable..png?fit=max&auto=format&n=gKqTvJPtsRi2bLNx&q=85&s=8abbce3efb590630de2102c43d32aadf)
.png?fit=max&auto=format&n=Dy9YsIECUbR9JZiT&q=85&s=a1f404cd7f7aeb1727c89d81137ae1ac)