7 min read Dillon Browne

MCP authorization after 2026-07-28: DCR is deprecated

The 2026-07-28 MCP revision deprecates RFC 7591 Dynamic Client Registration in favour of Client ID Metadata Documents, and deletes initialize, sessions and ping. A correction to my January post, with what I changed in a real server.

mcp agents oauth security developer-tools
On this page

In January I published Securing MCP Servers with DCR, which argued that RFC 7591 Dynamic Client Registration is the right way to let MCP clients get credentials without a human provisioning each one.

The protocol has since moved. The 2026-07-28 revision deprecates DCR in favour of Client ID Metadata Documents, and removes several things the January post assumed. This post is the correction. The old post stays up with a note pointing here — quietly editing it would hide that the guidance changed, which is the part worth knowing.

What the revision actually changed

Four changes matter if you have a server in production:

  1. RFC 7591 DCR is deprecated. Client ID Metadata Documents (CIMD) replace it.
  2. initialize is gone, and with it sessions and Mcp-Session-Id. Its replacement is server/discover, which is mandatory to implement and optional to call: servers MUST expose it, clients MAY skip it, because every request now carries its own protocol version and capabilities in _meta. Getting that asymmetry backwards in either direction is a bug — a server that omits it is non-compliant, and a client that requires it will reject compliant servers.
  3. ping and SSE resumability are gone. The transport no longer carries a liveness method or a resumable stream contract.
  4. Requests carry Mcp-Method and Mcp-Name headers, duplicating the method and tool name from the JSON-RPC body. Every result now carries resultType; list and read results additionally carry ttlMs and cacheScope. (The two have different scopes — worth getting right, because a server that only stamps resultType on its cacheable results is non-compliant on everything else.)

The first is the one that invalidates the January advice. The rest change how you write the server, not how you authorize it.

DCR vs. CIMD: what actually differs

Under DCR, a client with no credentials POSTs to a registration endpoint and the authorization server mints a client_id for it. The server has to accept writes from strangers, store a row per client, and then decide what to do with the accumulated registrations. That last part is the problem: registrations are permanent by default, so a server that has been running for a year holds a table of client IDs nobody can attribute or safely delete.

Under CIMD, the client publishes a document at a URL it controls, and that URL is the client ID. There is no registration call and no row to store. The authorization server fetches the document when it first sees the identifier and caches it.

  DCR (deprecated)                   CIMD (current)
  ────────────────                   ──────────────
  client ──POST /register──> AS      client publishes
         <──client_id, secret──          https://app.example/mcp-client.json

  AS stores a row per client         client ──auth request, client_id = that URL──> AS
  (grows forever, hard to audit)                                                     │
                                     AS ──GET the document──────────────────────────┘
                                     AS caches it; stores no registration

The practical consequences:

  • Rotation stops being a protocol operation. Under DCR you rotated with a registration_access_token against PUT /register/{id}. Under CIMD you edit the document you already host.
  • Revocation changes shape, and does less than it looks like. There is no registration to delete: you take the document down or change it, and once the authorization server’s metadata cache expires it stops issuing NEW authorizations to that client. It does not revoke access or refresh tokens already issued — those live until they expire or you revoke them explicitly, and refresh tokens follow the authorization server’s own policy. So the metadata TTL bounds one window, not the compromise window. Getting this backwards is the dangerous version: picking a short TTL, believing the client is contained, and leaving a long-lived token in play. Bound the tokens separately — short access-token lifetimes and a revocation path you have actually exercised.
  • Client identity becomes DNS-shaped. Whoever controls the hostname controls the identity. That is a real trade: it is easier to audit than an opaque client_id, and it means a lapsed domain is a credential.
  • The identity becomes portable. A DCR client_id is bound to the authorization server that issued it — the spec now requires clients to key credentials by issuer and re-register when the authorization server changes. A CIMD client ID is a self-hosted URL any authorization server can resolve, so the same identity survives that move with no re-registration.

The document itself is unremarkable, which is the point — it is the thing you already know how to deploy:

{
  "client_id": "https://app.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP Client",
  "redirect_uris": ["http://127.0.0.1:3000/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Two requirements are easy to get wrong: the client_id inside the document must equal the URL it is served from (authorization servers must reject a mismatch), and the URL must be https with a path component. token_endpoint_auth_method: "none" above is a public client relying on PKCE; a confidential client uses private_key_jwt, which CIMD supports via JWKS and which avoids a shared secret entirely.

The mechanics of the token exchange itself did not change. OAuth 2.1 still applies: short-lived access tokens, narrow scopes, the MCP server verifying rather than the client asserting. The full handshake — including where the deprecated registration step used to sit — is drawn out on the MCP page.

What I changed in a real server

This site runs a public MCP server at /api/mcp, and it is the thing I checked this against rather than a hypothetical. Three notes from building it.

Dual-era, not modern-only. Shipping clients still speak the legacy methods. The server implements server/discover and answers a legacy initialize — without minting a session ID, because it is stateless and pretending otherwise would be a lie the client acts on. Unknown methods return -32601 with HTTP 404, which is what lets a legacy HTTP+SSE probe tell “wrong method” from “wrong server”.

The header duplication needs checking, not trusting. Mcp-Name restates params.name from the body. Two sources for one value is a mismatch waiting to happen, so a disagreement is rejected outright rather than resolved by precedence:

if (headerName && bodyName && headerName !== bodyName) {
  return rpcError(-32020, 'Mcp-Name does not match params.name');
}

No authorization at all, deliberately. The server is read-only and serves data already on the page, so none of the above applies to it. A public server that demanded OAuth to read a blog post would be security theatre. The distinction the January post did not draw clearly enough: authorization is a function of what the tools can do, not of the protocol being MCP.

If you have a DCR implementation today

Deprecated is not removed — existing DCR flows still work, and there is no reason to rush. Worth doing in order:

  1. Check what your authorization server supports. CIMD needs support on the AS side; you cannot adopt it unilaterally from the client. It advertises this as client_id_metadata_document_supported: true in its OAuth Authorization Server Metadata — one field, checkable today, before you plan anything. The spec’s own client priority order is: existing pre-registered credentials, then CIMD, then DCR as fallback, then prompt the user.
  2. Find out how many registrations you actually have. If DCR has been open for a while, this number is usually a surprise, and it is the argument for the migration on its own.
  3. Pick the cache TTL before you migrate, not after. It is the revocation window.
  4. Keep DCR accepting requests during the overlap. Clients you do not control will be the last to move.

The broader point is the one I would have written differently in January: MCP’s authorization story is young enough that a post about it has a shelf life measured in months. The mechanism moved from “register yourself with the server” to “publish who you are and let the server come look” — which is a better fit for clients that are ephemeral, but it relocates the security question to DNS and cache expiry rather than removing it.


Correcting: Securing MCP Servers with DCR (January 2026). The protocol changed; the original post’s DCR mechanics were accurate when written and are now deprecated.

Found this helpful? Share it:

Related

Securing MCP Servers with DCR
7 min

Securing MCP Servers with DCR

Dynamic Client Registration in OAuth 2.1 transforms MCP server security and data governance. Learn implementation patterns for enterprise AI deployments.

mcp oauth security ai
Read full article