
An internal MCP platform at a 3,000-person company had, by its ninth month, 11,400 registered OAuth clients. It had about 400 real ones. The rest were reinstalls, CI runners, laptops that got reimaged, and a coding agent that registered itself fresh on every cold start. Nobody could say which of the 11,400 belonged to a current employee. When a security review asked "which clients can obtain tokens for this server today?", the honest answer was "all of them."
Nothing was misconfigured. Dynamic Client Registration did exactly what RFC 7591 says it does: accept metadata, mint an ID, store a record. The problem is what it cannot do. It cannot tell you who the client is.
Client ID Metadata Documents fix that one property, and the fix is why the 2026-07-28 revision of the Model Context Protocol (MCP) specification deprecated DCR. This post is the migration itself: what changes on each side, what to do in what order, and what still breaks after you flip the switch. For the conceptual comparison and the decision table, read the CIMD vs DCR guide first; this post assumes that choice is made.
MCP clients and servers usually have no prior relationship. A user pastes a server URL into Claude, Cursor or an internal agent runtime, and the client needs a client_id at an authorization server it has never met. DCR was the only standard mechanism that let that happen without a human in the loop, and MCP adopted it for exactly that reason.
The ceiling has three edges, and all three are structural rather than implementation bugs.
Metadata is self-asserted. RFC 7591 requires the authorization server to treat everything in a registration request as unverified unless it arrives in a signed software statement. A client can register as "Visual Studio Code" with VS Code's logo. The consent screen shows the user that name. There is no step at which anyone proves the name is true.
Every registration is a record. Clients with no durable storage register again on each start. Anthropic's connector documentation states that Claude registers a new client on every fresh DCR connection. Across an organization, that produces the 11,400-client table above. Expiring unused records is allowed by RFC 7591, but it is a cleanup policy for a problem the mechanism creates.
The endpoint is open. To serve clients it has never seen, the registration endpoint must accept requests without an initial access token. That is an unauthenticated write endpoint on your authorization server, and the only defense the RFC offers is rate limiting.
DCR is not broken. It is a registration mechanism being used as an identity mechanism, and it was never that.
With CIMD, the client publishes one JSON document at an HTTPS URL it controls, and that URL is the client_id. There is no registration call. When an authorization request arrives with a URL-shaped client_id, the authorization server fetches the document and validates it.
Three validations carry the security property:
Why that stops impersonation: a phishing client can send client_id=https://vscode.dev/oauth/client-metadata.json in its request, but the authorization server fetches the real document from vscode.dev and only redirects the authorization code to a URI that document lists. The phisher never controls a listed redirect_uri, so the code never reaches it. Identity is bound to control of the domain, and the consent screen can show the hostname rather than a self-chosen name.
A thousand installs of the same client are one client. The registration table goes away.
The sequence across revisions:
The 2026-07-28 revision also brought in a feature lifecycle policy (SEP-2596): a deprecated feature stays in the spec for at least twelve months, and the deprecated-features registry lists DCR's earliest removal as the first revision released on or after 2027-07-28.
Two smaller 2026-07-28 changes matter for migration. Clients still using DCR must send application_type (native for desktop, CLI and localhost apps; web for remote browser apps), because OpenID Connect servers treat a missing value as web and reject loopback redirect URIs. And pre-registered or DCR credentials must be keyed to the issuer that created them, with re-registration when the issuer changes. CIMD identifiers are exempt from that rule because one URL works against every authorization server.
The client precedence order the spec defines, which every client checklist below follows:
This is the authorization server side. If a vendor runs the authorization server for you, each item is a question to ask them rather than code to write.
Add client_id_metadata_document_supported: true to the authorization server metadata at /.well-known/oauth-authorization-server. Keep none in token_endpoint_auth_methods_supported; CIMD clients are public clients and some, Claude among them, use CIMD only when both signals are present and otherwise fall back to DCR.
If the first field is missing or false, no compliant client will attempt CIMD against you, no matter what you implemented.
Treat any client_id that parses as an HTTPS URL with a non-empty path as a CIMD identifier. Fetch it with GET, expect 200 and application/json, do not follow redirects, and abort the authorization request on any failure. The IETF draft recommends reading no more than 5 kilobytes.
Reject the document unless all of the following hold:
The authorization server is now making outbound HTTP requests to URLs chosen by untrusted clients. That is a server-side request forgery surface:
Steps 2 through 4 together, as the function the authorization endpoint calls before it renders consent:
An InvalidClientError surfaces to the client as error=invalid_client. Do not include the fetched body in the error description; it is attacker-controlled text.
Respect HTTP cache headers on the document, with a hard ceiling of 24 hours regardless of what the headers say. Never cache a failure or an invalid document; the next request should re-fetch. Cache keys are the full URL.
Leave DCR on while clients you do not control still need it. Rate-limit the registration endpoint, expire unused registrations, and log every registration with the application_type the client sent. Put a date on the calendar before the first spec revision on or after 2027-07-28 to review whether any client in your logs still depends on it.
Render the client_id's hostname on the consent screen, next to or instead of client_name. Warn when a document lists only loopback redirect_uris, since CIMD cannot prevent another process on the user's machine from listening on that port.
This is the side you own when you build an agent runtime, an IDE integration, a CLI, or a platform where teams' agents connect to many MCP servers.
Publish the document at a stable HTTPS URL under a domain you control. Use a path, not a bare origin. Do not use a URL shortener, a CDN path you might rotate, or a preview deployment. Changing the URL creates a new client identity at every authorization server you have ever connected to, and existing consents will not carry over.
Serve it with the headers an authorization server's validator expects. Authorization servers cap caching at 24 hours regardless of what you send, so a shorter max-age is the only value that matters when you need to add a redirect_uri quickly.
No redirects, no Vary on cookies, no authentication on the path. The document is public by design; it contains nothing secret.
In the authorization request, client_id is the document URL. Nothing else changes in the Authorization Code flow with PKCE: code_challenge, code_verifier, resource, and redirect_uri are unchanged. The redirect_uri you send must match an entry in the document.
Before choosing a mechanism, read the server's metadata. Implement the precedence order exactly:
Cache the decision per issuer. The same client can be CIMD at one server and DCR at another, and that is correct behavior during the deprecation window.
When falling back to DCR, send application_type and key the resulting credentials to the issuer. Store them so a restart does not re-register. A client that registers on every start is the client that creates the 11,400-row table.
Many first-party authorization servers for the services your agent ultimately needs do not accept CIMD. As of this writing GitHub, Google, Slack and Atlassian still expect pre-registered apps. If your agent runtime is a client to those servers directly, CIMD does nothing for that hop; you are back to pre-registration per provider.
Three checks, in order, against the authorization server you are migrating:
The third step should land on a consent screen that shows agents.example.com as the requesting host. Then run the negative case: change redirect_uri to one not in the document and confirm the server returns error=invalid_client rather than rendering consent. A server that renders consent for an unlisted redirect_uri has implemented the fetch but not the validation, and is no safer than it was under DCR.
That last item is where the client checklist turns into an architecture question.
An agent platform is a client many times over. It connects to internal MCP servers, to third-party hosted MCP servers, and, through those or directly, to the SaaS APIs its users actually work in. The precedence ladder above has to run for each distinct authorization server, and for the first-party SaaS providers it bottoms out at pre-registration no matter what.
There is one hop where none of this applies, and it is worth being precise about why.
When an agent reaches connector tools through a Virtual MCP server, the server has a static mcp_server_url created once per agent role, and the agent authenticates to it with a session token minted on the server side before each run. Identity rides in the token, not in a client registration. The pre-registration with GitHub, Google or Slack that the previous section requires is done once at the connection level, in the dashboard, and the per-user OAuth grant lives on the connected account. No client_id is negotiated at runtime for that hop.
This is not a replacement for CIMD. It is a different trust boundary. CIMD answers "which client is asking my authorization server for a token?" The session token answers "which user is this run acting as, against which connections?" The first question is about clients you have never met. The second is about users your platform already knows. When the client and the server are both yours, the registration problem was always artificial, and the token model removes it.
Every other hop still needs the checklist. The internal MCP server your platform team wrote for the data warehouse needs the server checklist. The agent runtime connecting to a partner's hosted MCP server needs the client checklist. The Virtual MCP overview covers the session-token hop; this post covers the rest.
Recommended Reading: How MCP clients register using CIMD: a step-by-step example
The draft is still a draft. CIMD is an IETF Internet-Draft, currently at draft-02 of the working-group version, and it has already changed name once since SEP-991 cited it. Field semantics can still move. Pin your implementation to a specific draft revision in code comments and review it when the draft advances.
Confidential clients are the minority path. CIMD's private_key_jwt option makes a confidential client possible without a shared secret, but most MCP clients run on user machines and will remain public clients. If you need a confidential client for a server-to-server agent, pre-register it with the client credentials grant; CIMD is the wrong tool for a client with no user.
Dual-era support is mandatory for a while. Cursor's documentation describes DCR or a static ID and secret, not CIMD. Clients that connect to your server will span both mechanisms until at least mid-2027. A server that turns DCR off early will reject real users; a server that never turns it off will keep the open registration endpoint forever.
Document availability is now part of sign-in. If the metadata URL returns a 500 during a deploy, every authorization request from that client fails until it recovers. Host the document on infrastructure with the same uptime expectations as your login page, because it is part of your login page.
Registration is the first identity decision an MCP server makes about a client. Under DCR, that decision is made on an unverifiable claim and then stored forever. Under CIMD, it is made on a fetch the server can repeat and verify, bound to a domain someone had to control.
The migration is not large. The server side is a metadata flag, a validating fetch, and an SSRF guard. The client side is one hosted file and a four-step ladder. What the migration buys is the ability to answer the question the security review actually asked: which clients can obtain tokens for this server today, and who are they.
For the hop between your own agents and your own connector tools, that question was never worth asking a registration endpoint. Mint a token per run, scope it to the user and the role, and let the registration ladder handle the servers you do not control.
Start with the server checklist on the authorization server you run, then audit which of your agent runtimes are still registering on every start. Browse the connector catalog to see which third-party connections a Virtual MCP server can carry so the per-provider pre-registration happens once rather than per agent.
DCR accepts self-asserted metadata, creates a new record on every registration, and requires an unauthenticated write endpoint. It registers clients but cannot identify them, which is the property CIMD adds.
The document's URL is the client id, the authorization server fetches it over HTTPS, the body must name the same id, and the redirect URI in the request must be listed in it. Identity is bound to control of the domain.
Pre-registered credentials first, then CIMD when the server advertises support, then DCR when it exposes a registration endpoint, then prompting the user. The order is fixed by the spec and should never be reversed.
The authorization server now fetches URLs chosen by untrusted clients. Refuse private and special-use addresses at DNS resolution time, enforce a timeout and size cap, and never echo the fetched body in error messages.
The earliest the spec can remove DCR is the first revision on or after 28 July 2027. Keep it behind a flag with rate limiting and expiry until the clients in your logs stop depending on it.
No. Those providers still expect pre-registered apps. For that hop a Virtual MCP server does the registration once at the connection level and gives each run a short-lived session token instead of negotiating a client id.