← Blog
September 1, 2026 · 15 min

CIMD in the wild: agent identity without registration

MCP deprecated Dynamic Client Registration on 28 July and made a URL the preferred client identity. We crawled the whole registry, resolved 3,450 authorization servers, and counted how many actually took the new path.

An agent that wants to call a protected MCP server has a problem before it has a payment problem. It needs a client_id. For a decade OAuth assumed a human developer would go to a console, register an application, and paste a credential into a config file. That assumption does not survive contact with autonomous software that discovers a server at runtime and has never met its operator.

The Model Context Protocol's answer, since the 2026-07-28 revision, is Client ID Metadata Documents. The client stops asking to be registered and instead publishes its own registration data at an HTTPS URL. That URL is the client ID. The authorization server fetches it when it sees one.

It is a clean idea with a real security surface, and it arrived attached to a deprecation. We wanted to know whether the deprecation binds. So we read all three revisions of the IETF draft, diffed them, and then measured the deployed ecosystem.

The mechanism in one paragraph

draft-ietf-oauth-client-id-metadata-document is a Web Authorization Protocol working group document by Aaron Parecki (Okta) and Emelia Smith, standards track. Revision -00 landed 8 October 2025, -01 on 2 March 2026, and -02 on 6 July 2026, expiring 7 January 2027. It defines a Client Identifier URL: an https URL that MUST contain a path component, MUST NOT contain a userinfo or fragment component, SHOULD NOT contain a query component, and MAY contain a port. The document it resolves to is ordinary OAuth client metadata plus one rule — it MUST contain a client_id property matching both the Client Identifier URL and the URL the server actually fetched.

Comparison is byte-for-byte. The draft is explicit that https://example.com/client and https://example.com:443/client are different identifiers even though 443 is the https default. Serving the document at a bare / is NOT RECOMMENDED. URL shorteners are unusable, because they work by redirecting and the spec forbids the server from following redirects.

The document also cannot carry a secret. token_endpoint_auth_method MUST NOT include client_secret_post, client_secret_basic, client_secret_jwt, or any other method built on a shared symmetric secret, and client_secret itself MUST NOT appear. This is forced by the medium: the document is world-readable by definition. Authentication, if any, is asymmetric.

The lineage is older than the agent wave. The acknowledgments credit Solid-OIDC, IndieAuth, and OpenID Federation, and name Solid-OIDC's dereferenceable Client Identifier Documents as the direct inspiration.

What the working group hardened, and when

The three revisions are not cosmetic edits. The document went from 26,867 bytes to 47,328. Counting normative keywords in the raw text, MUST goes 17 to 22 to 30, and MUST NOT goes 8 to 9 to 12. Two of those additions matter more than the rest, and the draft's own change log dates them precisely.

Revision -01 added "Require HTTP 200 response for fetching metadata." It also introduced the rule that the authorization server MUST NOT automatically follow HTTP redirects when retrieving the document. Neither statement exists anywhere in -00. Without them, an attacker who can get a redirect served from a reputable domain can point a Client Identifier URL at a trusted-looking host and have the metadata resolved from somewhere else entirely.

Revision -02 rewrote the SSRF section. In -00 the text reads that authorization servers "SHOULD avoid fetching any URLs using private or loopback addresses and consider network policies or other measures to prevent making requests to these addresses." In -02 it reads:

Authorization servers MUST NOT fetch a Client ID Metadata Document URL or any URLs contained within a Client ID Metadata Document that resolve to special-use IP addresses as defined in [RFC6890].

That is a SHOULD promoted to a MUST NOT, with a narrow carve-out for development deployments running on loopback themselves, and an explicit "Authorization servers MUST NOT apply this exception in production deployments." The change matters because a Client Identifier URL is attacker-supplied input that the authorization server is required to dereference. That is a server-side request forgery primitive by construction, and the draft now treats it as one.

Revision -02 also added an entire Privacy Considerations section that -00 did not have. We will come back to it, because a real client trips it today.

The reference gap

MCP's authorization spec cites the draft by exact revision. Every link is to -00.

The security considerations page is where this bites. Its Client ID Metadata Document section says authorization servers "SHOULD consider Server-Side Request Forgery (SSRF) risks," linking to section 6 of -00. It says nothing at all about HTTP redirects. An implementer who follows MCP's normative reference to the letter gets the October 2025 threat model: SSRF is advisory, redirect-chasing is unaddressed, and the 200-only rule does not exist.

MCP does add two requirements the IETF draft leaves out, both aimed at the same weakness. It states plainly that "Client ID Metadata Documents cannot prevent localhost URL impersonation by themselves," then requires that authorization servers MUST clearly display the redirect URI hostname during authorization and SHOULD warn on localhost-only redirect URIs. That is a genuine addition. It is also a user-interface mitigation for a machine-to-machine problem, which is the recurring shape of this whole area.

The gap is narrow but real — CIMD -00 and -02 are the same protocol. They are not the same threat model. Anyone building an MCP authorization server today should implement -02 and treat MCP's -00 citation as a stale pin, not a ceiling.

The census

The interesting question is not what the specs say. It is whether an agent, in the field, can actually use a URL as its identity. The MCP SDK makes that measurable, because it gates strictly. In the TypeScript SDK at HEAD dcc0102 (31 August 2026), the decision is one line:

// packages/client/src/client/auth.ts
const supportsUrlBasedClientId = metadata?.client_id_metadata_document_supported === true;
const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;

if (shouldUseUrlBasedClientId) {
  // SEP-991: URL-based Client IDs
  clientInformation = { client_id: clientMetadataUrl, issuer };
} else {
  // Fallback to dynamic registration
}

Strict equality against true. If the authorization server does not advertise the IANA-registered client_id_metadata_document_supported flag, the SDK silently takes the deprecated path. The same file carries the deprecation notice on registerClient: deprecated as of protocol version 2026-07-28 under SEP-2577, "Remains functional during the deprecation window (at least twelve months)."

So one boolean, published in authorization server metadata, decides whether the new mechanism is used at all. We counted it.

On 1 September 2026 we paged the official MCP registry API to exhaustion: 872 pages, 87,160 entries, of which 86,161 are flagged active and 26,120 are the latest version of their server. Those yielded 14,868 distinct HTTPS remote endpoints — 211 further entries were skipped because their URLs are unresolved templates like https://{HAPI_FQDN}:{HAPI_PORT}/mcp. Deduplicating by origin left 10,382 hosts.

For each origin we requested the RFC 9728 protected resource metadata, at both the root well-known path and the resource-path-suffixed form. 3,791 origins returned a valid document, 36.5 percent; 623 failed to connect or timed out. Of the documents returned, 149 carried no authorization_servers array at all. The rest resolved to 3,498 distinct authorization server issuers.

We then fetched authorization server metadata for each issuer, trying both the RFC 8414 path-insertion form and the OpenID Connect discovery form. 3,450 of 3,498 responded, a 98.6 percent resolution rate. Here is what they advertise.

// Census 2026-09-01

3,450 MCP authorization servers

client_id_metadata_document_supported: true — 592 servers, 17.2 percent.

Explicitly false — 179 servers, 5.2 percent. Flag absent entirely — 2,679 servers, 77.7 percent.

registration_endpoint present — 3,232 servers, 93.7 percent.

Five weeks after MCP deprecated Dynamic Client Registration, 93.7 percent of the authorization servers behind registry-listed MCP endpoints still advertise a DCR endpoint, and 82.8 percent give an SDK client no way to use a URL as its identity.

Adoption is additive, not a migration

The cross-tabulation is sharper than the headline. Of the 592 servers that support CIMD, 524 also advertise a registration_endpoint. That is 88.5 percent. Only 68 servers in the entire population — 2.0 percent — support CIMD and have actually turned DCR off.

Nobody is migrating. They are adding a second door and leaving the first one open. That is defensible during a twelve-month deprecation window, and it also means the deprecation currently changes nothing about what an attacker can do: the registration endpoint that motivated SEP-991 is still there, still accepting self-asserted client names, on 94 percent of servers.

At the other end, 150 servers (4.3 percent) advertise neither CIMD nor a registration endpoint. Under MCP's four-step client registration priority — pre-registration, then CIMD, then DCR, then prompt the user — those servers land on step four. An autonomous agent has no step four.

The support that does exist is concentrated in platforms, not in individual operators. Grouping the 592 by domain, 66 are WorkOS AuthKit tenants (of 75 AuthKit issuers we saw), 33 are on mcpize.run, 6 are Scalekit, 3 are Smithery. The rest is a long tail of single-tenant deployments. One vendor flipping a default moves this number more than a hundred server authors reading the spec.

The negative space is louder. Cloudflare's first-party MCP servers — observability, bindings, builds, radar, containers, browser and others — return client_id_metadata_document_supported: false explicitly, 14 of the 15 Cloudflare issuers in our census, while continuing to offer DCR. That is not an oversight; declaring the flag false is a deliberate act. Clerk shows CIMD enabled on 3 of 77 issuers, which its own changelog explains: the feature is beta, workspaces must contact support to turn it on, and advertising it is a second, separate toggle. The capability exists and is off by default. Stytch shows 2 true against 4 explicit false. Auth0 shows 3 of 11.

Google and Microsoft's endpoints in our sample advertise neither the CIMD flag nor a registration endpoint. For agents, the largest identity providers in the world are currently step four.

The client side, and a side channel

The server census only measures half the handshake. On the client side there is one unambiguous, verifiable production deployment: Visual Studio Code. Its document is live at https://vscode.dev/oauth/client-metadata.json, 477 bytes, HTTP 200, no redirects, well inside the draft's 5 kilobyte read limit:

{
  "client_id": "https://vscode.dev/oauth/client-metadata.json",
  "client_name": "Visual Studio Code",
  "client_uri": "https://vscode.dev/product",
  "application_type": "native",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token",
                   "urn:ietf:params:oauth:grant-type:device_code"],
  "redirect_uris": ["http://127.0.0.1:33418/", "https://vscode.dev/redirect"]
}

It is conformant. It is also instructive in two ways.

First, both it and the Insiders document at insiders.vscode.dev are served with cache-control: no-store, no-cache, max-age=0. The draft says authorization servers SHOULD respect HTTP cache headers. Honoring these means re-fetching the document on every single authorization request. Draft-02's new section 9.1 describes exactly what that produces:

the timing and frequency of requests to a Client Identifier URI can indicate when, and how often, users are attempting to authorize with a particular authorization server.

A client that forbids caching converts every authorization attempt against every server on the internet into an inbound request to a host it controls. We are not alleging intent — no-store is the sane default for a file you want to be able to rotate. But it is the maximally observable configuration, and it is the reason -02 grew a privacy section. For a fleet of agents, the same property is worse: an agent's identity URL becomes a beacon that reports which paid endpoints it is negotiating with, and when.

Second, the two documents share the client name "Visual Studio Code", the same stable-channel logo, and the same loopback redirect http://127.0.0.1:33418/. This is precisely the localhost impersonation risk SEP-991 flagged when Paul Carleton and Aaron Parecki opened it in July 2025, and that MCP's security page concedes CIMD cannot solve on its own. Any document claiming that name and that port is indistinguishable at the protocol layer.

The lever that cannot be pulled

The draft's main structural defense against impersonation is in section 8.1: an authorization server "may impose restrictions or relationships between the redirect_uris and the client_id or client_uri properties, for example to restrict the redirect_uri to the same-origin as the Client ID Metadata Document." Same-origin binding is what makes domain control meaningful — it ties the place the code is delivered to the domain that published the identity.

Native clients cannot satisfy it. VS Code's authorization code has to come back to http://127.0.0.1:33418/, which is not same-origin with vscode.dev, and never can be. The dominant MCP client shape — a local process on a loopback port — is structurally incompatible with the strongest lever the specification offers. The draft acknowledges this obliquely by preserving unrestricted redirect_uris for Solid-OIDC compatibility, and by adding an entire appendix on "CIMD Services" so that developers who cannot host a public document have a path at all.

What remains is reputation. Section 8.9 suggests warning the first 100 users of a client_id, checking how recently the domain was registered, and maintaining allowlists of trusted patterns like *.example.com. Those are sound heuristics for a human at a consent screen. None of them are available to an agent authorizing itself at three in the morning.

What it means for LLM4Agents

CIMD is the first agent-identity mechanism in this space that costs almost nothing to adopt and is genuinely portable. MCP states the property directly: client IDs based on Client ID Metadata Documents are portable across authorization servers, because they are self-hosted URLs resolved on demand, so no re-registration is needed when the authorization server changes. For a gateway whose agents fan out across many tools, that is the difference between one identity and N registration rows.

It fits our stack in three places. As a client, when an agent running through the gateway calls a protected MCP server, the gateway should present a stable Client Identifier URL rather than dynamically registering a fresh identity per server — the pattern we argued for in our reading of MCP authorization. As a resource server, our own protected endpoints should accept URL client IDs. And as an operator, the census above is a routing input: 82.8 percent of MCP authorization servers still leave DCR as the only path, so any client we ship has to carry both paths for at least the twelve-month window.

The threat it introduces is the one worth planning for. A Client Identifier URL is a dependency in the authorization path. If our identity document is unreachable, every authorization attempt fails at once — a single point of failure with no analogue under DCR, where a registration once obtained keeps working. And the fetch side channel means our identity host learns, and could leak, the shape of our agents' tool usage. That is the same category of leakage we measured in the OpenTelemetry GenAI conventions, arriving through a different door.

Finally, CIMD settles identity and nothing else. It tells a server which client is asking. It says nothing about what that client may spend, which is why it composes with rather than replaces the payment-side controls we audited in x402's default caps, and the workload-identity layer in WIMSE and SPIFFE. An agent needs a name, a budget, and a workload credential. These are three separate documents.

Staying on the frontier

In order:

Publish a Client Identifier URL now, and implement -02, not -00. A stable HTTPS URL with a path, a matching client_id, no secret, a small document, and a real cache lifetime. Do not copy VS Code's no-store; set a modest max-age so authorization servers can cache and the fetch side channel narrows. Serve it from infrastructure with the same availability target as the gateway itself, because it is now on the critical path.

Ship both registration paths and instrument the fallback. Given 93.7 percent DCR availability against 17.2 percent CIMD, an agent that only speaks CIMD cannot reach most servers today. Log which path every authorization takes. That ratio, measured against the servers we actually call rather than the registry at large, is the signal that tells us when to drop DCR — not the deprecation date.

Treat the identity document as an availability dependency. Monitor it the way we monitor a facilitator. Serve it from a domain we control end to end, not a docs host or a CDN alias, and never from a redirect. Rotating the URL is not free either: draft-02 section 8.3 notes that a changed Client Identifier URL looks to every authorization server like an entirely new client, which means losing accumulated consent.

On the resource-server side, advertise the flag and enforce the hardened rules. If we protect MCP endpoints, publish client_id_metadata_document_supported: true — the SDK will not attempt CIMD otherwise. Then implement the -02 requirements the -00 citation lets implementers skip: refuse redirects, require 200, block special-use addresses per RFC 6890, cap the read at 5 kilobytes, and never fetch logo_uri or jwks_uri without the same restrictions.

Do not confuse domain control with trust. A conformant document proves someone controls a hostname. It does not prove the client is what it claims, and the same-origin defense is unavailable to loopback clients. Where value moves, keep identity and authority separate: the identity URL says who is calling, the payment authorization says how much they may spend, and neither should be allowed to vouch for the other.

Watch the draft, not the citation. Revision -02 expires 7 January 2027. The delta from -00 to -02 was two new hard requirements and a privacy section; the delta to -03 will land inside our deprecation window. Track the working group document directly and re-run this census quarterly — the number that matters is not what MCP recommends, it is what the servers we call actually advertise.

Identity is one document. Budget is another.

LLM4Agents gives autonomous agents an OpenAI-compatible gateway that pays per call in stablecoins — with the spend controls identity alone cannot provide.

Register an agent