ai

Client ID Metadata Documents Are the Future of MCP Client Registration

Discover why Client ID Metadata Documents are replacing Dynamic Client Registration in the Model Context Protocol ecosystem for better security.

Nov 24, 20258 min read

Connecting a Model Context Protocol (MCP) client to a new server creates a common problem. You hit the "registration wall." How does your server trust an app it's never seen? How do you get its name and logo for the consent screen? This is a key challenge for open ecosystems like MCP, where any client can connect to any server. The traditional OAuth solution, Dynamic Client Registration (DCR), is stateful, complex, and creates a lot of operational overhead. It's not the best fit for this model. The community is now moving to a simpler, safer default: Client ID Metadata Documents (CIMD). This new approach is proposed in SEP-991, based on a new IETF draft, and is a stateless alternative that works better for MCP.

An Introduction to MCP and Authorization
An Introduction to MCP and Authorization

Add commentMore actionsDiscover the Model Context Protocol (MCP) and its authorization mechanisms.

What is Dynamic Client Registration (DCR)?

The traditional OAuth solution, Dynamic Client Registration (DCR), works, but the community has concluded it is not the best fit for a massive, open system like MCP. Think of DCR like an office building with a very strict security guard. Every person who enters (a client instance) must go to a registration desk, fill out a long form with their info (metadata), and wait for the secretary (the authorization server) to file it. The secretary then files this form in a giant, physical filing cabinet and gives the person a new, unique ID card.

This system breaks down at scale. Imagine if a million people show up. The secretary is overwhelmed, and the filing cabinet (the database) overflows. Worse, the system treats every visit as a new person. If the same person (the client instance) shows up wearing a different coat (running on a new device), they must fill out a new form and get a new ID card. If 10,000 people from the same company (the same app) visit, your filing cabinet now has 10,000 forms to manage. This is the "unbounded database growth" problem.

It's also a security risk. The "registration desk" (/register endpoint) is open to everyone, allowing attackers to flood it with millions of junk forms in a DDoS attack. They can also write a fake name on their form (like "Official Coding App") to trick your users. Finally, the server has to manage this massive, growing filing cabinet forever, and the client has to hold on to a different ID card for every office it visits.

What is a Client ID Metadata Document (CIMD)?

The community is moving to a simpler, modern default: Client ID Metadata Documents (CIMD). This new approach is being added to MCP (SEP-991) and is based on a new IETF draft. Instead of a filing cabinet, CIMD works like a security guard checking a driver's license. The client instance (the person) just shows its client_id (its license), which is just a secure HTTPS URL. The authorization server (the guard) looks at the URL and then visits it (for example, https://app.example.com/oauth.json) to "read" the license.

This is a read-only, "fetch-on-demand" model. A CIMD is just a simple JSON file that an application hosts on its own domain. The URL for this file is the client_id.

Let's look at the registration flow in the diagram below:

The flow is simple:

  • Client Hosts Metadata: The client application (for example, a "Code Agent") hosts a simple, static JSON file (the CIMD) at its own verifiable domain. This URL is its global, permanent client_id.

This method supports both public and confidential clients. The MCP proposal (SEP-991) provides explicit guidance for public clients, while the IETF draft enables confidential clients to authenticate without shared secrets

  • Public Clients: These apps cannot keep a secret. They set "token_endpoint_auth_method: none" in their metadata and rely on Proof Key for Code Exchange (PKCE) for security.

  • Confidential Clients: These apps can protect a private key. They use "token_endpoint_auth_method": "private_key_jwt" and provide a jwks_uri to verify their identity.

  • URL as Identity: The client sends this URL as the client_id in the authorization request.

  • Server-Side Validation: The MCP server receives this client_id. If the server supports CIMD, it performs an HTTP GET request to that URL to fetch the JSON. The server then validates it. It checks the JSON structure, required fields and (most importantly) ensures the client_id property inside the file exactly matches the URL it was fetched from.

  • Secure Consent and Access: If validation succeeds, the server shows a consent screen with the verified client_name and logo_uri. The server then caches this metadata for a period (for example, 24 hours). This means it does not need to fetch it again for subsequent requests from that client.

How CIMD Stops Impersonation

This is the key question. What stops a phisher at falseurl.com from starting a login and just using https://app.example.com/oauth.json as their client_id?

The answer is the redirect_uri validation.

  1. The phishing app (falseurl.com) sends an authorization request. It uses the legit app's client_id (https://app.example.com/oauth.json) but includes its own malicious redirect_uri (https://falseurl.com/callback).

  2. The server receives this request. It fetches the metadata file from the client_id URL, https://app.example.com/oauth.json.

  3. The server opens this legitimate JSON file and looks at its redirect_uris list.

{
  "client_id": "https://app.example.com/oauth.json",
  "client_name": "Example MCP Client",
  "redirect_uris": [
    "https://app.example.com/callback"
  ]
}
  1. The server compares the phisher's requested URI (https://falseurl.com/callback) to the list in the file.

  2. The server sees that https://falseurl.com/callback is not in the list of allowed URIs.

  3. The server aborts the request. The phisher cannot receive the authorization code, and the attack fails.

This check ensures that only the app that controls the domain listed in the metadata file can receive the user's credentials.

How CIMD Supports Confidential Clients

With DCR, a confidential client gets a client_secret from the server. With CIMD, the client proves its identity using its own private key. It tells the server how to check its identity by including properties like token_endpoint_auth_method and jwks_uri in the metadata file. When the client needs to authenticate (for example, at the token endpoint), it signs a JWT with its private key. The server then fetches the public key from the jwks_uri to verify the signature. This lets the server trust the client without a pre-registered secret.

Here is a minimal example for a confidential client using a JSON Web Token (JWT) for authentication:

{
  "client_id": "https://app.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP Client",
  "client_uri": "https://app.example.com",
  "logo_uri": "https://app.example.com/logo.png",
  "redirect_uris": [
    "http://localhost:3000/callback"
  ],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "private_key_jwt",
  "jwks_uri": "https://app.example.com/oauth/jwks.json"
}

How CIMD Solves DCR's Problems for MCP

This "driver's license" model fixes DCR's main problems. The "registration desk" is gone, which removes the attack vector for registration flooding. The URL is a single identifier for the application, not each instance, which solves the database scaling problem. Trust is now based on the domain, meaning a phisher at falseurl.com can't show a valid "license" for app.example.com. It's also much simpler, as servers no longer need a database of clients and expiry is handled by HTTP caching.

What Are the Security Risks of CIMD?

CIMD is a trade-off, not a magic solution. It closes the "filing cabinet" risk but opens a new one: Server-Side Request Forgery (SSRF). The authorization server now has to fetch a URL from a stranger. A malicious app could provide a URL pointing to an internal service like https://169.254.169.254/ or https://localhost:8080. To protect against this, you must harden your fetcher. Block internal and loopback IP ranges, use aggressive timeouts, and limit the response size (for example, 5kb) to avoid resource-draining attacks.

This model also has two weak spots:

  • The localhost problem: if an app is running on https://localhost:1234, the server can't be sure which app is listening on that port.

  • The "unverified domain" problem: CIMD only proves the app owns my-shady-app.com, not that the app is trustworthy. The IETF draft suggests servers can build their own trust policies, like showing extra warnings for new or unknown domains.

The Future of MCP Identity and Security

DCR is a useful standard, but it's not the best fit for the massive, federated world of MCP. CIMD is a huge improvement.

The current proposal for MCP is to make CIMD the default (SHOULD) and DCR an optional extra (MAY) for specific cases.

The limitations of CIMD are real. The community is already discussing the next step: adding Software Statements (defined in RFC 7591 Section 2.3) to this model. A Software Statement by itself doesn't solve the localhost problem, as a phisher could just copy it. Its power comes from the trust model. The server must be configured to trust the signer (the "issuer") of the statement. This could be an app store, an operating system, or a trusted vendor registry.

When combined with platform-level attestation, this model allows a client to prove who it is (for example, "I am the official app, signed by Apple") and not just what port it's on. This creates a verifiable chain of trust that is much harder to fake.

Identity needs to be federated and interoperable. By supporting proposals like CIMD and exploring new solutions like Software Statements, we can build a web that is both scalable and secure.

To learn more about implementing secure authentication for your MCP servers, check out our developer documentation on Auth for MCP.