How MCP authentication works
The Valyd MCP server does not issue tokens — it only validates them. The flow is standards-based (OAuth 2.1 + PKCE, RFC 9728 Protected Resource Metadata, RFC 8707 resource indicators), so any OAuth-capable MCP client connects with zero custom code. There is nothing to register per request and no shared secret to paste.
| Resource | https://mcp.valyd.work |
| Endpoint | https://mcp.valyd.work/verification/mcp |
| Authorization server | https://idp.valyd.work |
| Required scopes | openid mcp |
Token audience (aud) | https://mcp.valyd.work |
The discovery flow (automatic in interactive clients)
-
The client calls the MCP endpoint with no token and gets 401 with a
WWW-Authenticateheader pointing athttps://mcp.valyd.work/.well-known/oauth-protected-resource. -
That metadata names the authorization server and scopes:
{ "resource": "https://mcp.valyd.work", "authorization_servers": ["https://idp.valyd.work"], "scopes_supported": ["openid", "mcp"], "bearer_methods_supported": ["header"] } -
The client discovers the IDP, registers (Dynamic Client Registration), and opens a browser. The user logs into Valyd and consents.
-
The client gets an access token bound to
resource = https://mcp.valyd.workwith scopemcpand sends it on every call:Authorization: Bearer <access_token>.
Claude Code, Claude Desktop, Cursor, and VS Code all run this flow for you — you only sign in.
Token validation requirements
All of these must hold for a token to be accepted:
| Claim | Requirement |
|---|---|
| signature | RS256, verifiable against https://idp.valyd.work/api/auth/oidc/jwks.json |
iss | https://idp.valyd.work |
aud | https://mcp.valyd.work |
scope | must include mcp |
exp / iat / sub | present and not expired |
The sub claim identifies the user — every tool acts on that user’s behalf.
Code-first clients (LangChain, OpenAI SDK, scripts)
Obtain a user access token by running the OAuth 2.1 authorization-code + PKCE flow against
https://idp.valyd.work with scope=openid mcp and the resource indicator
resource=https://mcp.valyd.work, then pass it as the Authorization: Bearer header on the
streamable-HTTP connection. The token is user-bound, so it must come from a user login — not
client-credentials.
Ready-made snippets for LangChain and the OpenAI Agents SDK live in the MCP integration guide.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
401 with WWW-Authenticate on connect | No / expired token | Let the client run the OAuth login (Claude Code/Cursor: /mcp → Authenticate). For code clients, fetch a fresh token. |
invalid_token | Wrong aud, iss, scope, or signature | Token must have aud=https://mcp.valyd.work, iss=https://idp.valyd.work, and scope including mcp. |
invalid_scope | A requested scope is not allowed by the IDP | Request only openid mcp. |
missing_token | No Authorization: Bearer header | Send the Bearer token on every request. |
The legacy X-MCP-Client-Id / X-MCP-Client-Secret / X-MCP-Webhook-Url header scheme and
the user_id tool parameter are removed. Authentication is OAuth 2.1 Bearer only, and the
user comes from the token.