Keycloak CIMD for MCP: Client ID Metadata Documents for Claude Code and VS Code

Guilliano Molaire Guilliano Molaire 11 min read

Last updated: September 2026

Keycloak supports OAuth Client ID Metadata Documents (CIMD) as an experimental feature, enabled with --features=cimd, and that is what lets an MCP client like Claude Code or VS Code desktop authenticate against your realm without Dynamic Client Registration. The setup is two objects rather than a code change: a client policy profile carrying the client-id-metadata-document executor, and a client policy carrying the client-id-uri condition. The feature landed in Keycloak 26.6.0, and 26.7.0 added the documented Claude Code integration alongside the existing VS Code one. The catch is audience binding: upstream’s own MCP compliance table still lists Resource Indicators as not supported, so it rates Keycloak as partially supporting every MCP revision from 2025-06-18 onward.

The short version for platform teams: CIMD works today, the configuration is unintuitive in two specific places that will cost you an afternoon if nobody warns you, and the audience-binding gap is the thing to plan around rather than the registration mechanism.

What is a Client ID Metadata Document?

A Client ID Metadata Document is a JSON document hosted at an HTTPS URL, and that URL is the client_id. Instead of an MCP client registering itself with your authorization server and receiving an opaque identifier, it sends a URL as its client_id, and the authorization server fetches the client’s metadata from that URL at request time. The format is defined in the IETF Internet Draft “OAuth Client ID Metadata Document,” which Keycloak’s implementation tracks.

That inversion is the point. Dynamic Client Registration creates a record in your database for every client that asks. CIMD creates nothing durable: the client’s identity lives at a URL the client controls, and your server reads it, caches it, and discards it.

For an editor with millions of installs, the operational difference is substantial. With DCR you accumulate client records nobody prunes, each one a credential you are then responsible for. With CIMD the only durable state is a cache entry with a bounded size and a bounded lifetime.

Which MCP revision requires CIMD?

The client registration preference order arrived in the 2025-11-25 revision of the Model Context Protocol specification, not in the 2026-07-28 one. This is worth getting right, because a lot of secondary writing attributes it to the newest revision. Keycloak’s 26.6.0 release notes put it this way: “Since version 2025-11-25, the Model Context Protocol (MCP) requires an authorization server to comply with CIMD.”

That phrasing is Keycloak’s, and it is slightly stronger than the specification itself. Both revisions say MCP clients and authorization servers SHOULD support Client ID Metadata Documents, which is a strong recommendation rather than a hard requirement.

Both revisions tell a client to work through four options in order:

  1. Use pre-registered client information, if the client already has it
  2. Use Client ID Metadata Documents, if the authorization server advertises client_id_metadata_document_supported in its OAuth Authorization Server Metadata
  3. Use Dynamic Client Registration as a fallback, if the server advertises a registration_endpoint
  4. Prompt the user to enter the client information, if nothing else is available

What did change in 2026-07-28 is DCR’s status. That revision formally deprecates the OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591) as a client registration mechanism in favour of Client ID Metadata Documents, and it introduces a specification feature lifecycle with defined Active, Deprecated and Removed states plus a minimum twelve-month deprecation window. DCR remains available for backwards compatibility, and new implementations are told not to adopt it. We covered the wider set of changes in that revision in the 2026-07-28 spec and what it means for stateless servers.

How do you enable CIMD on Keycloak?

Start Keycloak with the feature flag, then build one profile and one policy in the admin console. There is no extension to install and no code to write.

bin/kc.sh start --features=cimd

Upstream marks CIMD experimental, which in Keycloak terms means it sits outside the compatibility commitments that apply to supported features and can change shape between minor releases. Plan the upgrade testing accordingly.

With the flag on, create the profile:

  1. Go to Realm Settings, then Client Policies, then the Profiles tab
  2. Create a client profile, name it something like cimd-profile
  3. Add the client-id-metadata-document executor

The executor takes five options, and two of them are where teams get stuck:

  • Allow http scheme: permits http for the client ID URL and for URL-valued metadata properties. Development only, and it must be OFF in production.
  • Trusted domains: wildcard domain patterns the executor accepts. If this is empty, every domain is denied, so an empty list closes the door rather than opening it.
  • Restrict same domain: verifies that the client ID URL, the redirect URI, and every URL-valued property of the metadata all sit under the same trusted domain.
  • Required properties: metadata properties that must be present, or the request is rejected.
  • Only Allow Confidential Client: requires the document to describe a confidential client, with jwks or jwks_uri and either private_key_jwt or tls_client_auth.

Then create the policy that triggers it:

  1. Go to Realm Settings, then Client Policies, then the Policies tab
  2. Create a client policy, name it something like cimd-policy
  3. Add the client-id-uri condition, set URI scheme to https, and fill in Trusted domains
  4. Under Associated client profiles, attach cimd-profile

The client-id-uri condition has the same closed-by-default behaviour as the executor. Leave its trusted domains empty and the condition evaluates to false no matter what arrives, so nothing ever reaches the profile.

What does Keycloak reject in a client_id URL?

Keycloak validates the client_id URL against six rules and rejects the authorization request outright if any fails. Per the upstream guide, the URL must use the https scheme unless Allow http scheme is on, must contain a path component, must not contain single-dot or double-dot path segments, must not contain a fragment, must not contain a username or password, and must not include a query string.

The path requirement is a common stumbling block. https://example.com/mcp is valid. https://example.com is not.

The query string rule is worth writing down, because Keycloak is stricter than the specification here. The CIMD draft says a client ID URL SHOULD NOT include a query component. Keycloak enforces that as a hard requirement and rejects anything carrying one. If you generate client metadata URLs from a template that appends a cache-busting parameter or a tenant identifier, that request will fail against Keycloak and succeed against a more permissive server.

Our read: the three system-wide settings are documented upstream but rarely make it into secondary write-ups, and they are the ones with production consequences. A fetched document is cached for a minimum of 300 seconds and a maximum of 259,200 seconds (3 days), and Keycloak refuses any document larger than 5,000 bytes. The size limit is the one we would watch: a metadata document that grows past 5 KB, say because someone added a long list of redirect URIs, stops working for a reason that does not announce itself as a byte count.

Those settings are not in the admin console. They are SPI options set at startup:

bin/kc.sh start 
  --spi-client-policy-executor--client-id-metadata-document--min-cache-time=600 
  --spi-client-policy-executor--client-id-metadata-document--max-cache-time=86400 
  --spi-client-policy-executor--client-id-metadata-document--upper-limit-metadata-bytes=10000

How do you wire Claude Code as a CIMD client?

Claude Code desktop sends a client_id of https://claude.ai/oauth/claude-code-client-metadata and completes the flow on a localhost callback, so the trusted domains list needs claude.ai, localhost and 127.0.0.1, and Restrict same domain must be OFF.

That last setting is the most common failure. Claude Code is a desktop application: it starts a local HTTP server and uses a redirect URI such as http://localhost:<port>/callback. The client ID URL is on claude.ai. Those are different domains, so leaving Restrict same domain on ON rejects every request.

The profile configuration upstream documents for Claude Code:

  • Allow http scheme: OFF
  • Trusted domains: claude.ai, localhost, 127.0.0.1
  • Restrict same domain: OFF
  • Only Allow Confidential Client: OFF

Claude Code is a public client using PKCE. It has no client secret, which is why the confidential-client option has to stay off. The matching policy uses the client-id-uri condition with URI scheme https and Trusted domains claude.ai.

How is the VS Code setup different?

VS Code desktop needs a third trusted domain that has nothing to do with the login flow. Its client ID is https://vscode.dev/oauth/client-metadata.json, and its metadata document carries a logo_uri pointing at code.visualstudio.com. Because the Trusted domains list applies to URL-valued metadata properties and not just the client ID, omitting code.visualstudio.com rejects the request on the logo.

The upstream VS Code profile is therefore vscode.dev, 127.0.0.1 and code.visualstudio.com, with Restrict same domain OFF again, because VS Code redirects to http://127.0.0.1:<port>/callback.

Both clients declare loopback redirect URIs, and localhost and 127.0.0.1 are separate strings even though they resolve to the same machine. If you are supporting both clients in one realm, include both names.

Claude Code desktop VS Code desktop
client_id https://claude.ai/oauth/claude-code-client-metadata https://vscode.dev/oauth/client-metadata.json
Trusted domains claude.ai, localhost, 127.0.0.1 vscode.dev, 127.0.0.1, code.visualstudio.com
Extra domain reason none logo_uri on code.visualstudio.com
Restrict same domain OFF OFF
Client type public, PKCE public, PKCE

What does CIMD not fix?

CIMD solves registration and does nothing for audience binding, which is the gap that determines whether your MCP deployment is actually secure. Every MCP revision from 2025-06-18 onward marks RFC 8707 Resource Indicators a MUST, and upstream’s MCP guide lists RFC 8707 as not supported in its compliance table, rating Keycloak as “partially supported” for 2025-06-18, 2025-11-25 and 2026-07-28 alike.

The picture in the source is more nuanced than that table suggests, and worth knowing before you plan around it. Keycloak does carry an experimental resource-indicators feature: RESOURCE_INDICATORS("Resource Indicators for OAuth 2.0", Type.EXPERIMENTAL) is declared in Profile.java in 26.6.0, 26.7.0 and 26.7.3. So initial support has merged. Upstream nonetheless still rates itself non-compliant in the MCP guide and documents a workaround instead, which is the safer signal to plan against: treat RFC 8707 on Keycloak as in progress rather than available, and do not build a production audience model on an experimental flag whose own project does not yet claim it works.

The workaround upstream documents uses scope instead. Define a client scope per MCP capability, give each one an Audience mapper whose Included Custom Audience is the MCP server’s URL, and mark them Optional:

{
  "aud": "https://example.com/mcp",
  "scope": "mcp:resources mcp:tools mcp:prompts"
}

The Included Custom Audience value has to match both the resource parameter and the MCP server URL exactly. Paste an issued token into our JWT token analyzer and check the aud claim before you trust the configuration, because a missing audience looks identical to a working one until another server accepts the token. We walked through the failure mode this produces in why your Keycloak-backed MCP server returns 401 and what RFC 8707 has to do with it, and the broader setup in securing MCP servers with Keycloak and OAuth 2.0.

Should you keep DCR as a fallback?

Keep it if you still serve clients on the 2025-03-26 or 2025-06-18 revisions, and close it once you do not. Those revisions predate CIMD entirely, so a client built against them has no other way in. Since 2026-07-28 formally deprecates DCR with a minimum twelve-month window, this is a transition to plan rather than a permanent arrangement.

There is one specific case for keeping DCR available regardless. MCP Inspector, the official MCP debugging tool, registers dynamically. Supporting it means configuring the anonymous client registration policies, including Allowed Registration Web Origins for the Inspector backend and Trusted Hosts for the machine running the browser. That is a real widening of your attack surface for a debugging convenience, so it is reasonable to enable in a development realm and refuse in production.

For a realm serving current AI tooling, that points to CIMD enabled, trusted domains kept tight, and DCR closed.

What this costs to run

Keycloak is identity management as a service when someone else runs it, and the CIMD surface illustrates why that matters. Every element above is a piece of operational state: an experimental feature flag that can change between minor releases, three SPI options that only exist at startup, two policy objects whose empty-means-deny semantics invert most people’s intuition, and a trusted-domain list that has to be revised whenever an AI vendor changes where it hosts its metadata.

Skycloak runs upstream Keycloak, so the CIMD implementation is the same one described here. What changes is who tracks the feature’s promotion out of experimental, who re-tests the Claude Code and VS Code flows after each upgrade, and who follows RFC 8707 support from experimental to supported so the scope scaffolding can come out. Teams weighing that trade-off will find the arithmetic in is self-hosting Keycloak worth it in 2026, and the wider vendor picture in managed Keycloak providers compared.

For the adjacent problem of giving agents their own identity rather than borrowing a user’s, see Keycloak AI agent authentication.

FAQ

Does Keycloak support Client ID Metadata Documents?
Yes, as an experimental feature since Keycloak 26.6.0, enabled with --features=cimd. It is configured through a client policy profile using the client-id-metadata-document executor and a client policy using the client-id-uri condition. Experimental features carry no compatibility guarantee across minor releases.

Which Keycloak version do I need for Claude Code MCP authorization?
CIMD itself is available from 26.6.0. Keycloak 26.7.0 added the documented Claude Code desktop integration alongside the existing VS Code one. The latest release at the time of writing is 26.7.3, from 31 August 2026. Both clients use the same executor with different trusted-domain lists.

Is Dynamic Client Registration deprecated in MCP?
Yes, as of the 2026-07-28 revision, which deprecates RFC 7591 as a client registration mechanism in favour of Client ID Metadata Documents. It remains available for backwards compatibility under a minimum twelve-month deprecation window, and new implementations are told not to adopt it.

Why does my CIMD authorization request fail with a valid client_id URL?
Check three things in order: whether Restrict same domain is ON (it must be OFF for loopback callbacks), whether the trusted domains list is empty (empty denies everything), and whether the URL carries a query string. Keycloak rejects query strings on client ID URLs as a hard requirement, though the specification only says SHOULD NOT.

Does CIMD solve MCP token audience binding?
No. Audience binding depends on RFC 8707 Resource Indicators. Keycloak carries an experimental resource-indicators feature from 26.6 onward, but upstream’s MCP guide still lists RFC 8707 as not supported and rates Keycloak as partially supporting every revision since 2025-06-18. The documented workaround binds the audience through client scopes with Audience mappers.

Sources

  • Keycloak, “Integrating with Model Context Protocol (MCP)” guide, retrieved 2026-09-13, https://github.com/keycloak/keycloak/blob/main/docs/guides/securing-apps/mcp-authz-server.adoc
  • Keycloak, release 26.6.0 notes, “OAuth Client ID Metadata Document (experimental),” retrieved 2026-09-13, https://github.com/keycloak/keycloak/blob/main/docs/documentation/release_notes/topics/26_6_0.adoc
  • Keycloak, release 26.7.0 notes, “Authorize AI tools and MCP servers,” retrieved 2026-09-13, https://github.com/keycloak/keycloak/blob/main/docs/documentation/release_notes/topics/26_7_0.adoc
  • Keycloak, Profile.java at tag 26.7.3, RESOURCE_INDICATORS experimental feature declaration, retrieved 2026-09-13, https://github.com/keycloak/keycloak/blob/26.7.3/common/src/main/java/org/keycloak/common/Profile.java
  • Model Context Protocol, “Changelog,” specification revision 2026-07-28, retrieved 2026-09-13, https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2026-07-28/changelog.mdx
  • Model Context Protocol, “Client Registration,” specification revision 2026-07-28, retrieved 2026-09-13, https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2026-07-28/basic/authorization/client-registration.mdx
  • Model Context Protocol, “Authorization,” specification revision 2025-11-25, retrieved 2026-09-13, https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/basic/authorization.mdx
  • IETF, “OAuth Client ID Metadata Document” (Internet Draft), https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document
  • IETF, “Resource Indicators for OAuth 2.0” (RFC 8707), https://datatracker.ietf.org/doc/html/rfc8707

Somewhere to run this that stays patched

Everything above works the same on Skycloak, because it is real upstream Keycloak rather than a fork. What changes is who handles the upgrades, backups and security patches afterwards.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman