Build an OAuth-Protected MCP Server for Willow
When an MCP server is set to Proxy OAuth, Willow does not hold a shared credential. Instead it runs a standard OAuth authorization-code flow against your server for each user, then forwards the access token your server issues as Authorization: Bearer <token> on every tool call. Your server is both the MCP server and its own OAuth authorization server.
This page is the endpoint reference. For a step-by-step build with runnable code, see Tutorial: Build an OAuth MCP Server with Express.
How Willow drives the flow
When an admin selects Discover OAuth Settings, and when a user connects, Willow:
- Fetches your authorization server metadata to learn your endpoints.
- Registers a client dynamically (RFC 7591), sending its callback URL as a redirect URI.
- Redirects the user to your authorization endpoint, receives the code at its callback, and exchanges it at your token endpoint.
- Forwards the resulting access token to your MCP endpoint on each
tools/call.
You implement four HTTP endpoints for steps 1 through 3, plus token validation on your MCP endpoint. This page covers the OAuth endpoints; for the MCP endpoint itself (initialize, tools/list, tools/call), see Build an MCP Server Behind Willow.
1. Authorization server metadata
Serve OAuth metadata at the well-known path so Willow can discover your endpoints:
GET /.well-known/oauth-authorization-server
{
"issuer": "https://mcp.example.com",
"authorization_endpoint": "https://mcp.example.com/authorize",
"token_endpoint": "https://mcp.example.com/token",
"registration_endpoint": "https://mcp.example.com/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_post"]
}
Include a registration_endpoint so Willow can register itself. Without dynamic registration, an admin has to enter a Client ID manually under Proxy OAuth Advanced settings.
2. Dynamic client registration
Willow registers a client by POSTing its redirect URI to your registration endpoint:
POST /register
{
"redirect_uris": ["https://{your-org}.mcp-s.com/{your-org}/api/auth/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code"],
"response_types": ["code"]
}
Your response must echo redirect_uris back as an array, along with a client_id:
{
"client_id": "generated-client-id",
"redirect_uris": ["https://{your-org}.mcp-s.com/{your-org}/api/auth/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code"],
"response_types": ["code"]
}
redirect_uris as an arrayWillow validates the registration response and expects redirect_uris to be an array. If you omit it or return it in another shape, discovery fails with redirect_uris expected array, received undefined, and connecting the server fails with OAuth Discovery Failed — HTTP 400. Return the same redirect URIs Willow sent.
If you set token_endpoint_auth_method to a confidential method such as client_secret_post, also return a client_secret.
3. Authorization endpoint
Willow redirects the user's browser to your authorization endpoint:
GET /authorize?response_type=code&client_id=...&redirect_uri=...&state=...&code_challenge=...&code_challenge_method=S256
Authenticate the user however you like, then redirect back to the redirect_uri with a short-lived authorization code and the original state:
302 Location: https://{your-org}.mcp-s.com/{your-org}/api/auth/callback?code=<auth-code>&state=<state>
Preserve state exactly. If you advertised PKCE in your metadata, store the code_challenge to verify against the code_verifier at the token step.
4. Token endpoint
Willow exchanges the code for a token:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=<auth-code>&redirect_uri=...&code_verifier=...
Return an access token:
{
"access_token": "<token>",
"token_type": "Bearer",
"expires_in": 3600
}
Willow stores this token per user and sends it as Authorization: Bearer <token> on every subsequent tool call for that user.
Validate the forwarded token
On your MCP endpoint, treat the token as you would any access token you issued:
- Read the
Authorization: Bearer <token>header oninitializeandtools/call. - Look up or verify the token against your own store or signing keys.
- Reject calls whose token is missing, expired, or revoked.
Because your server issued the token, you validate it against your own authorization server, not against Willow.