MCP authorization is the part of the Model Context Protocol specification that tells an MCP server how to require OAuth 2.1 access tokens from MCP clients, and the specification makes it optional. The 2026-07-28 revision says HTTP-based servers SHOULD follow it, local servers on the stdio transport SHOULD NOT and should read credentials from the environment instead, and when you do follow it, the MCP server acts as an OAuth resource server while a separate authorization server issues the tokens. Keycloak is a good choice for that authorization server when your MCP server is remote, serves more than one user or tenant, and needs to reuse the logins, roles and audit trail you already have. It is the wrong choice for a single-user local tool, where it adds a moving part and buys you nothing.
The rest of this post is the decision tree we walk through with teams, with each branch tied back to what the specification actually requires.
What does MCP authorization actually require?
MCP authorization requires three separate roles, and a lot of confusion comes from collapsing them. In the 2026-07-28 specification, the MCP server MUST publish OAuth 2.0 Protected Resource Metadata (RFC 9728) so clients can discover which authorization server to use, and MUST validate that each access token was issued for it as the intended audience.
The three roles are:
- MCP client: the host application (an IDE, Claude Code, a chat app or an agent runtime) that wants to call tools. In OAuth terms it is the client.
- MCP server: the service that exposes tools, resources and prompts. In OAuth terms it is the resource server. It validates tokens but does not issue them.
- Authorization server: the service that authenticates the user or client and issues tokens. This is where Keycloak sits.
The specification also says clients MUST send the RFC 8707 resource parameter naming the MCP server, and that an MCP server MUST NOT pass the token it received through to upstream APIs. If the server needs to call another API, it gets a separate token for that API. That rule alone rules out a common shortcut, which is reusing the user’s upstream API token as the MCP token.
Does your MCP server need OAuth at all?
Start with the transport, because the specification decides this branch for you. In the 2026-07-28 revision, implementations using stdio “SHOULD NOT follow this specification, and instead retrieve credentials from the environment.”
Local, single-user tools on stdio
A local MCP server that the client launches as a subprocess runs with the user’s own operating system permissions. There is no network listener for an attacker to reach and only one user, so an OAuth flow adds a browser redirect without adding protection. Read the downstream API key or token from an environment variable or the OS keychain and keep it out of the repository.
Remote servers over HTTP
As soon as the server listens on a network address, anyone who can reach it can call it, so you need authentication. A server that follows the specification answers an unauthenticated request with a challenge that points the client at its metadata, as in this example from the 2026-07-28 revision:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="files:read"
``` The specification says HTTP-based implementations SHOULD conform to its authorization rules. The remaining branches below are about how.
## When are API keys acceptable for MCP?
API keys are acceptable for a remote MCP server only when there is a single, known caller and the server does not act on behalf of individual users. A private server used by one internal automation, where the key lives in a secret manager and can be rotated, fits that description.
API keys stop being acceptable when any of these is true:
- **More than one user.** A shared key cannot tell you which person triggered a tool call, so you lose per-user permissions and per-user audit.
- **Third-party clients.** If users connect their own IDEs or assistants, you would be asking each user to paste a long-lived secret into a tool you do not control.
- **Interoperability.** Standard MCP clients discover authorization through Protected Resource Metadata and run the OAuth flow automatically. A custom API key header is something every client has to special-case.
In our experience the deciding question is not how sensitive the tools are but whether you will ever need to answer "which user did this?". If the answer could become yes, starting with OAuth is cheaper than retrofitting it later.
## Is the caller a user or an autonomous agent?
The grant type follows from who the token represents. If a person is using the MCP client, the client runs the authorization code flow with PKCE and the token represents that user. If an agent runs on its own with no user present, it uses the client credentials grant and the token represents the agent's own identity.
The 2026-07-28 specification acknowledges both. When an MCP server responds with `insufficient_scope`, clients acting for a user SHOULD attempt a step-up authorization flow to request more scopes, while `client_credentials` clients MAY step up or abort. For autonomous agents, the important design work happens before any token is issued: give each agent its own client and only the scopes it needs, as we describe in [non-human identity for AI agents on Keycloak](/blog/non-human-identity-ai-agents-keycloak/).
## How do MCP clients register with the authorization server?
The specification gives clients an order of preference, and your authorization server choice depends on which options it supports. In the 2026-07-28 revision, the options are pre-registered client credentials, Client ID Metadata Documents (CIMD), and Dynamic Client Registration (DCR), with DCR now marked as deprecated in favour of CIMD for new implementations.
- **Pre-registration** works when you know the clients in advance, such as your own agent runtime or a single approved IDE.
- **CIMD** suits open ecosystems where any compliant client may connect. The client's `client_id` is an HTTPS URL pointing at its metadata, and Keycloak fetches and caches that document instead of registering a new client. Our [Keycloak CIMD guide](/blog/keycloak-cimd-mcp-authorization/) covers the client policy setup and the cache settings.
- **DCR** still works for older clients. The specification keeps it for backwards compatibility with authorization servers that do not support CIMD, and in our experience each registration leaves a client record behind that someone has to clean up.
If none of these is available, the specification's last resort is to prompt the user to enter client information by hand.
## When should Keycloak be the MCP authorization server?
Keycloak is the right authorization server when the MCP server is remote and multi-user, and you want tokens that carry the same users, roles and groups as the rest of your applications. That covers most product teams exposing MCP tools to customers, and most platform teams exposing internal tools to employees through SSO.
Keycloak's own MCP integration guide, as of 26.8.0, lists what it supports against the specification:
| Standard | Keycloak 26.8.0 status |
|---|---|
| OAuth 2.1 and Authorization Server Metadata (RFC 8414) | Supported |
| Issuer identification (RFC 9207) | Supported |
| Dynamic Client Registration (RFC 7591) | Supported |
| Client ID Metadata Documents | Experimental (`--features=cimd`) |
| Resource Indicators (RFC 8707) | Experimental (`--features=resource-indicators`) |
The same guide rates Keycloak's conformance as supported for MCP 2025-03-26 and experimental for 2025-06-18, 2025-11-25 and 2026-07-28. One detail is easy to miss: the 26.7.0 version of that guide listed Resource Indicators as not supported, so the experimental flag in 26.8.0 is new ground. If your Keycloak is older, or you prefer not to run an experimental flag, the guide's documented fallback is to use an optional client scope per MCP scope with an Audience mapper whose custom audience is the MCP server's URL. We walk through that configuration in [fixing the Keycloak MCP server 401 audience error](/blog/keycloak-mcp-server-401-audience-rfc-8707/).
### Audience binding and token validation
Whichever route you choose, the MCP server must check `aud` on every request and reject tokens issued for anything else. It can do that locally by verifying the JWT signature against Keycloak's JWKS, or by calling the token introspection endpoint when it needs to see revocations immediately. Local validation is faster, while introspection puts Keycloak on every request's path.
### When not to put Keycloak on the hot path
Keycloak should issue tokens, not sit in the middle of every tool call. Validate JWTs locally in the MCP server where you can, cache JWKS, and keep access tokens short-lived so revocation works through expiry. If you need per-call policy decisions beyond scopes, Keycloak's experimental AuthZEN API can answer them, as described in [using Keycloak as an AuthZEN policy decision point](/blog/keycloak-authzen-pdp-openid-authorization-api/), but treat that as a deliberate choice with its own latency budget.
## What does the decision tree look like end to end?
Putting the branches together gives a short checklist:
1. **Is the server stdio and local?** Use environment credentials and stop there.
2. **Is it remote with one known machine caller and no users?** An API key from a secret manager is acceptable, with rotation.
3. **Is it remote with users or third-party clients?** Use OAuth. The MCP server publishes Protected Resource Metadata and validates audience.
4. **Do you already have users, roles and SSO in Keycloak, or need multi-tenant isolation?** Make Keycloak the authorization server.
5. **Are the clients open-ended?** Prefer CIMD, keep DCR only for clients that need it.
6. **Does an autonomous agent call the server?** Give it its own client and use the client credentials grant.
If step 4 is a yes, [Skycloak](/pricing/) runs that Keycloak as a managed service while you keep control of the realm, clients and scopes, and users can sign in through your existing [single sign-on](/features/single-sign-on/). For hands-on setup, start with [securing MCP servers with Keycloak OAuth 2.0](/blog/securing-mcp-servers-with-keycloak-oauth-2-0/), and for the wider changes in the latest revision see our post on [the 2026-07-28 MCP specification](/blog/stateless-mcp-2026-07-28-spec/).
## Frequently asked questions
### Is authorization required in MCP?
No. The 2026-07-28 MCP specification states that authorization is optional. When it is supported, HTTP-based implementations should follow the specification's OAuth rules, and stdio implementations should not, retrieving credentials from the environment instead. Any remote server that is reachable over a network still needs some form of authentication in practice.
### What is the difference between MCP authentication and MCP authorization?
Authentication establishes who the caller is, for example a user signing in to Keycloak. Authorization decides what that caller may do, expressed in MCP as an access token with scopes and an audience that the MCP server validates. The MCP specification's authorization section covers both through OAuth 2.1, with the MCP server as resource server.
### Can Keycloak be an MCP authorization server?
Yes. Keycloak's MCP integration guide lists OAuth 2.1, RFC 8414 metadata, RFC 9207 and Dynamic Client Registration as supported, with CIMD and RFC 8707 Resource Indicators as experimental in 26.8.0. Keycloak rates its conformance as experimental for the 2025-06-18, 2025-11-25 and 2026-07-28 MCP revisions.
### Should an MCP server forward the user's token to upstream APIs?
No. The MCP specification says the server must not pass through the token it received from the MCP client. If it calls an upstream API, it acts as an OAuth client to that API and uses a separate token issued for it, for example through token exchange.
### Do I need Dynamic Client Registration for MCP?
Not for new deployments. The 2026-07-28 MCP revision marks Dynamic Client Registration as deprecated and tells new implementations to use Client ID Metadata Documents, with pre-registration still available for known clients. Keycloak supports DCR and offers CIMD as an experimental feature.
## Sources
- Model Context Protocol, Specification 2026-07-28, "Authorization", https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization (source at https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/docs/specification/2026-07-28/basic/authorization)
- Model Context Protocol, Specification 2026-07-28, "Client Registration", https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2026-07-28/basic/authorization/client-registration.mdx
- Keycloak, "Integrating with Model Context Protocol (MCP)" guide at 26.8.0, https://github.com/keycloak/keycloak/blob/26.8.0/docs/guides/securing-apps/mcp-authz-server.adoc
- Keycloak, the same guide at 26.7.0, https://github.com/keycloak/keycloak/blob/26.7.0/docs/guides/securing-apps/mcp-authz-server.adoc
- IETF, RFC 9728 "OAuth 2.0 Protected Resource Metadata", https://datatracker.ietf.org/doc/html/rfc9728
- IETF, RFC 8707 "Resource Indicators for OAuth 2.0", https://www.rfc-editor.org/rfc/rfc8707.html