Client ID Metadata Documents (CIMD)
Client ID Metadata Documents (CIMD) are defined in
draft-ietf-oauth-client-id-metadata-document.
Instead of registering an OAuth application in advance to obtain an opaque
client_id string, a client presents an HTTPS URL as its client_id. The
authorization server fetches the document at that URL and uses it to resolve the
client's metadata on the fly. The
MCP authorization specification (2025-11-25)
designates CIMD as the preferred client registration mechanism for clients and
servers that do not have a prior relationship.
CIMD eliminates the manual app-registration step on both sides of an OAuth interaction. A client that hosts a stable metadata document at a public HTTPS URL can interact with any authorization server that supports CIMD without pre-registration, and an authorization server that supports CIMD can accept new clients without requiring them to call a registration endpoint first.
How ToolHive uses CIMD
ToolHive supports CIMD in two independent directions: as a client connecting to remote MCP servers, and as a server accepting connections from MCP clients such as VS Code or Claude Code.
ToolHive as a CIMD client
When you run thv run against a remote MCP server that requires OAuth
authentication, ToolHive discovers the server's authorization server and checks
whether it advertises CIMD support via the
client_id_metadata_document_supported field in its OAuth discovery metadata
(RFC 8414). If it does, ToolHive presents
https://toolhive.dev/oauth/client-metadata.json as its client_id instead of
calling the Dynamic Client Registration (DCR) endpoint.
ToolHive follows this priority order when authenticating to a remote server:
- Stored credentials — if a previous session left a cached refresh token or
DCR
client_id, those are used directly. - CIMD — if the remote authorization server advertises
client_id_metadata_document_supported: trueand no credentials are stored, ToolHive presents its CIMD URL. - DCR — if the remote authorization server does not support CIMD, ToolHive falls back to RFC 7591 Dynamic Client Registration.
If the remote authorization server advertises CIMD support but rejects the CIMD
client_id (for example, because the server is still rolling out support),
ToolHive automatically retries using DCR.
Linear's MCP server (https://mcp.linear.app/mcp) advertises
client_id_metadata_document_supported: true. When you connect to it with
thv run, ToolHive uses its CIMD URL automatically and no OAuth app
pre-registration on the Linear side is required.
ToolHive as a CIMD server (embedded AS)
The ToolHive embedded authorization server can accept HTTPS URLs as client_id
values from MCP clients. When CIMD is enabled on the embedded AS, a client such
as VS Code presents https://vscode.dev/oauth/client-metadata.json as its
client_id. The embedded AS fetches that document, validates it, and uses the
declared metadata (redirect URIs, grant types, scopes) to authorize the client
without any prior DCR call.
When CIMD is disabled (the default), the embedded AS only accepts client_id
values that were issued through DCR. Enabling CIMD adds a second lookup path
alongside DCR; existing DCR-registered clients continue to work.
The embedded authorization server and its CIMD support are available only for Kubernetes deployments using the ToolHive Operator. See Enable CIMD on the embedded authorization server for setup instructions and version requirements.
Two-layer architecture
When the embedded AS is deployed and CIMD is enabled, the OAuth flow involves two independent legs. The client's CIMD identity is used only within the embedded AS. The embedded AS uses its own pre-configured identity when talking to the upstream identity provider.
The upstream IDP never sees the client's CIMD URL. The embedded AS uses its own
pre-configured client_id and client_secret (or DCR-obtained credentials)
when talking to the upstream IDP. The two OAuth legs are completely independent.
Security model
Document validation
When the embedded AS receives a CIMD client_id, it fetches the document and
enforces the following rules:
- The URL must use
https. Loopbackhttp://localhostis accepted only in development and test environments. - The
client_idfield inside the fetched document must exactly match the URL used to fetch it. No normalization is applied — allowing normalization would permit subtle spoofing attacks where a document at URL A claims the identity of URL B. - The document must declare at least one
redirect_urisentry, and all redirect URIs must pass strict validation (RFC 8252). - Symmetric shared-secret
token_endpoint_auth_methodvalues (client_secret_post,client_secret_basic,client_secret_jwt) are forbidden. CIMD clients have no pre-shared secret by definition. grant_typesmust be a subset of[authorization_code, refresh_token]and must includeauthorization_code.response_typesmust be a subset of[code].- If the embedded AS has a restricted
scopes_supportedlist, the document's declared scopes must be a subset of it.
SSRF protection
The HTTP client used to fetch CIMD documents includes SSRF protection:
- Keep-alive connections are disabled so the IP check runs on every request.
- DNS resolution is performed first and the resulting IP is checked against private and special-use ranges (RFC 1918). Loopback addresses are allowed for development use only.
- Redirects are not followed. Per the CIMD specification, the authorization server must not automatically follow redirects when retrieving a client metadata document.
- The fetch timeout is five seconds and the response body is capped at 10 KB.
Caching
To avoid fetching the CIMD document on every request, the embedded AS caches documents in an LRU cache. Two parameters control the cache:
| Parameter | Default | Description |
|---|---|---|
cacheMaxSize | 256 | Maximum number of documents held in the cache. When full, the least-recently-used entry is evicted. |
cacheFallbackTtl | 5m | Fixed TTL applied to every cached entry. Cache-Control header parsing is not yet implemented; all entries use this value regardless. |
If a fetch fails (network error, invalid document, policy violation), the request is rejected and the failure is not cached.
Limitations
- Cache-Control header parsing is not yet implemented. All cached CIMD
documents use the
cacheFallbackTtlvalue regardless of theCache-Controlheader in the response. This means a document that declares a short max-age is still cached forcacheFallbackTtl. - Kubernetes only for the embedded AS. CIMD support on the embedded AS is
configured via the
MCPExternalAuthConfigCRD and is not available for standalonethv rundeployments.
Related information
- Enable CIMD on the embedded authorization server — step-by-step Kubernetes setup
- Embedded authorization server — conceptual overview of the embedded AS, including DCR and token forwarding
- Authentication and authorization — the overall authentication framework