Can FastMCP delegate OAuth to a third-party authorization server instead of acting as one?
In modelcontextprotocol/python-sdk, FastMCP's OAuthAuthorizationServerProvider makes the MCP server act as both the Authorization Server and the Resource Server by serving /.well-known/oauth-authorization-server, /authorize, /token and /register. Is it possible to delegate authentication to an external authorization server such as Keycloak, using Starlette middleware or custom routes instead? The default provider also expects interactive user input, which does not fit backend resource servers without a browser flow.
Originally asked on GitHub: https://github.com/modelcontextprotocol/python-sdk/issues/702
4 answers
Fixed on main as part of the v2 work (issue #982); the thread was closed in July 2026 during the v2 release backlog cleanup, with an invitation to reopen if still relevant. Before v2, full delegation to a third-party authorization server was not supported: OAuthProvider serves /.well-known/oauth-authorization-server, /authorize, /token and /register directly from your MCP server, so FastMCP effectively acts as both the Authorization Server and the Resource Server.
The workaround that worked in the thread: run FastMCP with a custom route at /.well-known/oauth-protected-resource returning {"resource": "<mcp_server_path>", "authorization_servers": ["<keycloak_server_path>"], "bearer_methods_supported": ["header"]}. With MCP Inspector this successfully authenticated against an external Keycloak server after working around CORS issues.
A contributor also suggested having the MCP server act as a proxy that forwards client authorization requests to the third-party AS and passes the responses back, as a bridge until real delegation exists. A docs PR was later proposed to clarify when OAuthAuthorizationServerProvider is appropriate, document the resource-server pattern with an external AS, and show Starlette middleware or custom routes for simpler auth cases.
General advice: keep the scopes in the protected-resource metadata consistent with the external AS configuration, and do not log bearer tokens.
Did this work for you?What @bojanbg tried on GitHub (Worked)
Served a custom /.well-known/oauth-protected-resource route from FastMCP returning resource, authorization_servers pointing to an external Keycloak server, and bearer_methods_supported: ["header"]. Authentication worked with MCP Inspector after resolving CORS issues.
Did this work for you?What @Rodriguespn tried on GitHub (Outcome unclear)
A contributor stated that OAuthProvider always serves /authorize, /token, /register and /.well-known/oauth-authorization-server from the MCP server itself, so fully delegating to a third-party AS is not possible, and suggested proxying client requests to the external AS.
Did this work for you?What a GitHub user tried on GitHub (Outcome unclear)
Asked whether Starlette middleware could replace OAuthAuthorizationServerProvider, noting the provider assumes interactive user input that backend resource servers do not have.
Source: https://github.com/modelcontextprotocol/python-sdk/issues/702 (opens external site)
Did this work for you?
Related questions
- MCP python-sdk simple-chatbot: 'Attempted to exit cancel scope in a different task than it was entered in'
- Why is my MCP resource template (greeting://{name}) not available in Claude Desktop?
- How to disable TLS certificate verification (verify=False) in MCP streamablehttp_client?
- anyio.ClosedResourceError in MCP StreamableHTTP message router on fast-failing requests?
- Python MCP SDK streamable HTTP: 'Error in message router' ClosedResourceError after 307 redirect