Many OAuth providers (GitHub, Google, Azure AD, etc.) do not support RFC 7591 Dynamic Client Registration. This example demonstrates how to build an MCP server that authenticates users through such a provider using a proxy pattern.
The MCP specification's OAuth flow expects an Authorization Server that supports DCR. When connecting to a provider that only accepts pre-registered OAuth apps, MCP clients cannot complete the standard registration step.
The MCP server runs a lightweight OAuth Authorization Server that sits between the MCP client and the upstream provider:
MCP Client <--> OAuth Proxy (this server) <--> Upstream Provider (GitHub)
- Accepts DCR from clients - No DCR support
- Proxies auth to upstream - Pre-registered OAuth app
- Issues its own tokens - Issues upstream tokens
The proxy:
- Accepts DCR from MCP clients (issuing proxy-level client credentials)
- Redirects authorization to the upstream provider using pre-registered credentials
- Exchanges upstream tokens and issues its own tokens to MCP clients
- Maps proxy tokens to upstream tokens so the MCP server can call upstream APIs on behalf of the user
For GitHub:
- Go to GitHub Developer Settings
- Click "New OAuth App"
- Set the Authorization callback URL to
http://localhost:3001/callback - Note the Client ID and generate a Client Secret
export OAUTH_CLIENT_ID="your-github-client-id"
export OAUTH_CLIENT_SECRET="your-github-client-secret"From the SDK root:
pnpm --filter @modelcontextprotocol/examples-server exec tsx src/oauthWithoutDCR.tsOr from within this package:
pnpm tsx src/oauthWithoutDCR.tsUse the example OAuth client:
pnpm --filter @modelcontextprotocol/examples-client exec tsx src/simpleOAuthClient.ts| Variable | Default | Description |
|---|---|---|
OAUTH_CLIENT_ID |
(required) | Client ID from upstream provider |
OAUTH_CLIENT_SECRET |
(required) | Client secret from upstream provider |
MCP_PORT |
3000 |
Port for the MCP server |
PROXY_PORT |
3001 |
Port for the OAuth proxy server |
OAUTH_AUTHORIZE_URL |
https://github.com/login/oauth/authorize |
Upstream authorization endpoint |
OAUTH_TOKEN_URL |
https://github.com/login/oauth/access_token |
Upstream token endpoint |
OAUTH_SCOPES |
read:user user:email |
Space-separated scopes for upstream |
To use Google instead of GitHub, set:
export OAUTH_AUTHORIZE_URL="https://accounts.google.com/o/oauth2/v2/auth"
export OAUTH_TOKEN_URL="https://oauth2.googleapis.com/token"
export OAUTH_SCOPES="openid email profile"The proxy pattern works with any standard OAuth 2.0 provider. The only requirement is that you pre-register an OAuth application and provide the credentials via environment variables.
This example is for demonstration purposes. For production use:
- Use HTTPS for all endpoints
- Persist client registrations and tokens in a database (not in-memory)
- Implement proper PKCE validation end-to-end
- Implement token refresh and revocation
- Restrict CORS origins
- Add rate limiting to the registration and token endpoints
- Validate redirect URIs strictly (exact match, no open redirects)
- Set appropriate token expiration times
- Consider adding CSRF protection to the authorization flow