Description
MCP's authorization spec lets an MCP server be protected by OAuth 2.1, with the server itself acting as the authorization server (issuing its own tokens) — the flow Claude.ai and similar clients use: dynamic client registration → authorize (PKCE) → token → bearer-protected MCP calls.
Today, making a Symfony MCP server do this requires bolting on a third-party OAuth server (e.g. league/oauth2-server-bundle) alongside mcp/sdk. Since mcp/sdk is gaining the authorization-server primitives, MCP Bundle should expose them via config so any Symfony MCP server can become its own OAuth 2.1 AS without an external IdP — symmetric with how the bundle already wraps the transport, sessions, and PSR bridge.
Proposed approach
A new mcp.oauth config section (opt-in) that wires the SDK engine and exposes the endpoints:
mcp:
client_transports: { http: true }
oauth:
enabled: true
issuer: '%env(MCP_BASE_URL)%'
signing_key:
private_key: '%kernel.project_dir%/config/jwt/private.pem'
scopes: ['mcp:tools', 'mcp:resources']
Registering: /.well-known/oauth-authorization-server (RFC 8414), /.well-known/oauth-protected-resource (RFC 9728), /.well-known/jwks.json, /oauth/authorize (PKCE), /oauth/token, /oauth/register (RFC 7591).
Batteries-included, all overridable:
- default resource-owner resolver = the authenticated firewall user becomes the OAuth subject,
- auto-approve consent,
- a firewall
AccessTokenAuthenticator that validates the bearer JWT and loads the user from sub,
- Cache-based storage (PSR-16 over a cache pool), reusing the bundle's existing session-store pattern.
This requires mcp/sdk ^0.6 (the version introducing the AS primitives), which also entails a small Profiler\TraceableRegistry update for the revised RegistryInterface.
Related
Opening as a discussion/tracking issue for the bundle change; the draft PR demonstrates the full wiring.
Description
MCP's authorization spec lets an MCP server be protected by OAuth 2.1, with the server itself acting as the authorization server (issuing its own tokens) — the flow Claude.ai and similar clients use: dynamic client registration → authorize (PKCE) → token → bearer-protected MCP calls.
Today, making a Symfony MCP server do this requires bolting on a third-party OAuth server (e.g.
league/oauth2-server-bundle) alongsidemcp/sdk. Sincemcp/sdkis gaining the authorization-server primitives, MCP Bundle should expose them via config so any Symfony MCP server can become its own OAuth 2.1 AS without an external IdP — symmetric with how the bundle already wraps the transport, sessions, and PSR bridge.Proposed approach
A new
mcp.oauthconfig section (opt-in) that wires the SDK engine and exposes the endpoints:Registering:
/.well-known/oauth-authorization-server(RFC 8414),/.well-known/oauth-protected-resource(RFC 9728),/.well-known/jwks.json,/oauth/authorize(PKCE),/oauth/token,/oauth/register(RFC 7591).Batteries-included, all overridable:
AccessTokenAuthenticatorthat validates the bearer JWT and loads the user fromsub,This requires
mcp/sdk ^0.6(the version introducing the AS primitives), which also entails a smallProfiler\TraceableRegistryupdate for the revisedRegistryInterface.Related
league/oauth2-serverwith thisOpening as a discussion/tracking issue for the bundle change; the draft PR demonstrates the full wiring.