diff --git a/docs/toolhive/concepts/auth-framework.mdx b/docs/toolhive/concepts/auth-framework.mdx
index e93d3559..740cdb07 100644
--- a/docs/toolhive/concepts/auth-framework.mdx
+++ b/docs/toolhive/concepts/auth-framework.mdx
@@ -83,10 +83,14 @@ clients a spec-compliant OAuth experience while centralizing the complexity of
token acquisition and management.
To eliminate the client registration burden, the embedded authorization server
-currently implements Dynamic Client Registration (DCR), which the MCP
-specification lists as the backward compatibility fallback. Support for Client
-ID Metadata Documents, the spec's preferred mechanism for clients and servers
-without an existing relationship, is planned.
+supports two client registration mechanisms. **Client ID Metadata Document
+(CIMD)** is the MCP specification's preferred mechanism: a client presents a
+public HTTPS URL as its `client_id`, and the embedded AS fetches and validates
+the metadata document at that URL. Clients such as VS Code support CIMD and use
+it automatically when the embedded AS advertises support. **Dynamic Client
+Registration (DCR, RFC 7591)** is the backward-compatibility fallback. MCP
+clients that do not support CIMD call `/oauth/register` to receive a `client_id`
+automatically.
## Authentication framework
diff --git a/docs/toolhive/concepts/backend-auth.mdx b/docs/toolhive/concepts/backend-auth.mdx
index 3c3c8f89..84206a6a 100644
--- a/docs/toolhive/concepts/backend-auth.mdx
+++ b/docs/toolhive/concepts/backend-auth.mdx
@@ -162,8 +162,9 @@ the external service, obtains tokens on behalf of the user, and automatically
forwards the upstream token to the MCP server on each subsequent request.
The embedded authorization server runs in-process within the ToolHive proxy with
-no separate infrastructure needed. It supports Dynamic Client Registration
-(DCR), so MCP clients can register automatically with ToolHive. No manual client
+no separate infrastructure needed. It supports Client ID Metadata Documents
+(CIMD) and Dynamic Client Registration (DCR), so MCP clients can identify
+themselves or register automatically with ToolHive. No manual client
configuration in ToolHive is required.
For a full explanation of how the OAuth flow works, token storage and
diff --git a/docs/toolhive/concepts/embedded-auth-server.mdx b/docs/toolhive/concepts/embedded-auth-server.mdx
index 6b4d17df..1f62bcba 100644
--- a/docs/toolhive/concepts/embedded-auth-server.mdx
+++ b/docs/toolhive/concepts/embedded-auth-server.mdx
@@ -43,9 +43,14 @@ identity provider and those services.
From the client's perspective, the embedded authorization server provides a
standard OAuth 2.0 experience:
-1. If the client is not yet registered, it registers via Dynamic Client
- Registration (DCR, RFC 7591), receiving a `client_id` and `client_secret`. No
- manual client registration in ToolHive is required.
+1. The client identifies itself using one of two mechanisms, with no manual
+ registration in ToolHive required:
+ - **Client ID Metadata Document (CIMD):** If CIMD is enabled on the embedded
+ AS and the client supports it (for example, VS Code), the client presents
+ its public HTTPS metadata URL as its `client_id`. The embedded AS fetches
+ and validates the document.
+ - **Dynamic Client Registration (DCR, RFC 7591):** The client calls
+ `/oauth/register` to receive a `client_id` dynamically.
2. The client is directed to the ToolHive authorization endpoint.
3. ToolHive redirects the client to the upstream identity provider for
authentication (for example, signing in with GitHub or Atlassian).
@@ -132,9 +137,12 @@ the OAuth flow.
- **In-process execution:** The authorization server runs within the ToolHive
proxy with no separate infrastructure or sidecar containers.
-- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 DCR (RFC 7591),
- allowing MCP clients to register automatically. No manual client registration
- in ToolHive is required.
+- **Client ID Metadata Document (CIMD):** When enabled, accepts HTTPS URLs as
+ `client_id` values and resolves client metadata on demand. Clients that
+ support CIMD (such as VS Code) use it automatically.
+- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 DCR (RFC 7591) as a
+ fallback for clients that do not support CIMD. MCP clients register
+ automatically with no manual configuration in ToolHive.
- **Direct upstream redirect:** Redirects clients directly to the upstream
provider for authentication (for example, GitHub or Atlassian).
- **Configurable signing keys:** JWTs are signed with keys you provide,
@@ -196,6 +204,68 @@ If you also set `baselineClientScopes`, those scopes apply to CIMD-resolved
clients too. Because CIMD clients can be resolved from arbitrary HTTPS URLs,
keep the baseline narrow.
+### Two-layer architecture
+
+The client's CIMD identity is used only within the embedded AS. The embedded AS
+uses its own pre-configured upstream credentials when redirecting to the
+upstream identity provider.
+
+```mermaid
+sequenceDiagram
+ participant Client as VS Code
(CIMD client_id)
+ participant EmbeddedAS as Embedded AS
+ participant IDP as Upstream IDP
+
+ Note over Client,EmbeddedAS: Leg 1: CIMD
+ Client->>EmbeddedAS: client_id = https://vscode.dev/oauth/client-metadata.json
+ EmbeddedAS->>EmbeddedAS: Fetch and validate CIMD document
+ EmbeddedAS-->>Client: Redirect to upstream IDP
+
+ Note over EmbeddedAS,IDP: Leg 2: AS own credentials
+ Client->>IDP: Authenticate
+ IDP-->>EmbeddedAS: Authorization code
+ EmbeddedAS->>IDP: Exchange (AS's own client_id + secret)
+ EmbeddedAS-->>Client: Issue ToolHive JWT
+```
+
+The upstream IDP never sees the client's CIMD URL. This means you must still
+configure an upstream client ID and secret for the embedded AS regardless of
+whether clients use CIMD or DCR.
+
+### Document validation
+
+The embedded AS enforces the following rules on fetched CIMD documents:
+
+- The URL must use `https` (loopback `http://localhost` is accepted in
+ development environments only).
+- The `client_id` field inside the document must exactly match the URL it was
+ fetched from.
+- `redirect_uris` must be present and pass strict validation.
+- Symmetric shared-secret `token_endpoint_auth_method` values are forbidden.
+- `grant_types` must include `authorization_code` and be a subset of
+ `[authorization_code, refresh_token]`.
+- `response_types` must be a subset of `[code]`.
+- Declared scopes must be a subset of the AS's configured `scopes_supported`
+ list (when set).
+
+The fetcher also applies SSRF protection: DNS resolution runs before dialing,
+private IP ranges are blocked, redirects are not followed, and each fetch is
+subject to a five-second timeout and a 10 KB response cap.
+
+### Verify CIMD is enabled
+
+After applying the configuration, confirm that the discovery document advertises
+CIMD support:
+
+```bash
+curl -s https:///.well-known/oauth-authorization-server | \
+ python3 -m json.tool | grep client_id_metadata_document
+```
+
+Replace `` with the `issuer` value from your
+`MCPExternalAuthConfig`. You should see
+`"client_id_metadata_document_supported": true`.
+
## Session storage
By default, session storage is in-memory. Upstream tokens are lost when pods
diff --git a/docs/toolhive/concepts/vmcp.mdx b/docs/toolhive/concepts/vmcp.mdx
index 992c805c..3ca0361c 100644
--- a/docs/toolhive/concepts/vmcp.mdx
+++ b/docs/toolhive/concepts/vmcp.mdx
@@ -142,9 +142,9 @@ vMCP can also run an embedded authorization server that handles the full OAuth
flow with multiple upstream identity providers (such as GitHub, Google, or
Okta). This enables per-user backend authentication: when a user logs in, the
auth server acquires tokens from each upstream provider and injects them into
-requests to the appropriate backends. MCP clients register automatically through
-Dynamic Client Registration (DCR), so no manual client configuration is needed.
-See
+requests to the appropriate backends. MCP clients register or identify
+themselves automatically through Client ID Metadata Documents (CIMD) or Dynamic
+Client Registration (DCR), so no manual client configuration is needed. See
[Authentication](../guides-vmcp/authentication.mdx#embedded-authorization-server)
for setup details.
diff --git a/docs/toolhive/guides-k8s/auth-k8s.mdx b/docs/toolhive/guides-k8s/auth-k8s.mdx
index 2a05570f..d87af2c6 100644
--- a/docs/toolhive/guides-k8s/auth-k8s.mdx
+++ b/docs/toolhive/guides-k8s/auth-k8s.mdx
@@ -1185,6 +1185,39 @@ kubectl describe mcpserver -n toolhive-system
`kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator`
- Verify the operator is running: `kubectl get pods -n toolhive-system`
+**CIMD client not using CIMD (falling back to DCR):**
+
+- Verify `cimd.enabled: true` is set in the `MCPExternalAuthConfig` and the
+ operator has reconciled the change:
+ `kubectl describe mcpexternalauthconfig -n toolhive-system`
+- Confirm the discovery document advertises CIMD support:
+ ```bash
+ curl -s https:///.well-known/oauth-authorization-server | \
+ python3 -m json.tool | grep client_id_metadata_document
+ ```
+ Replace `` with the `issuer` value from your
+ `MCPExternalAuthConfig`. You should see
+ `"client_id_metadata_document_supported": true`.
+- Restart the proxy runner pod after updating an existing resource.
+
+**CIMD authentication failing with `invalid_client`:**
+
+- The `client_id` field inside the fetched document must exactly match the URL
+ used to fetch it.
+- Documents must not declare `token_endpoint_auth_method` values that use a
+ symmetric shared secret (`client_secret_post`, `client_secret_basic`,
+ `client_secret_jwt`).
+- `grant_types` must include `authorization_code`. `response_types` must only
+ contain `code`.
+- `redirect_uris` must be present and valid.
+
+**CIMD fetch failing (egress / timeout):**
+
+- The proxy runner pod needs outbound HTTPS access to the client's metadata URL
+ (for example, `https://vscode.dev`). Check cluster egress policies.
+- Fetches time out after five seconds. Ensure the metadata host is reachable
+ from within the cluster.
+
diff --git a/docs/toolhive/guides-ui/run-mcp-servers.mdx b/docs/toolhive/guides-ui/run-mcp-servers.mdx
index 73e3f740..c224384d 100644
--- a/docs/toolhive/guides-ui/run-mcp-servers.mdx
+++ b/docs/toolhive/guides-ui/run-mcp-servers.mdx
@@ -151,13 +151,17 @@ remaining required information and adjust any optional settings as needed:
1. **Authorization method**: Choose how ToolHive should authenticate with the
remote server.\
The default is **Auto-Discovered**. Use this option for MCP servers that
- fully implement the MCP authorization spec including dynamic client
- registration (RFC7591) or for servers that do not require authentication.
- ToolHive automatically:
- - Discovers OAuth/OIDC endpoints
- - Registers a new OAuth client
- - Obtains and manages client credentials
- - Handles token lifecycle automatically
+ implement the MCP authorization spec or for servers that do not require
+ authentication. ToolHive automatically discovers OAuth/OIDC endpoints and
+ identifies itself using whichever mechanism the server supports:
+ - **Client ID Metadata Document (CIMD):** If the server's discovery document
+ sets `client_id_metadata_document_supported: true`, ToolHive presents
+ [`https://toolhive.dev/oauth/client-metadata.json`](https://toolhive.dev/oauth/client-metadata.json)
+ as the `client_id`. No registration round-trip is needed.
+ - **Dynamic Client Registration (DCR, RFC 7591):** When CIMD is unavailable
+ or rejected, ToolHive registers an OAuth client dynamically.
+
+ Either path handles token acquisition and lifecycle automatically.
For MCP servers that accept a bearer token in the `Authorization` header,
select **Bearer Token**. ToolHive stores the token securely and sends it as
@@ -459,13 +463,17 @@ On the configuration form, enter:
6. **Authorization method**: Choose how ToolHive should authenticate with the
remote server.\
The default is **Auto-Discovered**. Use this option for MCP servers that
- fully implement the MCP authorization spec including dynamic client
- registration (RFC7591) or for servers that do not require authentication.
- ToolHive automatically:
- - Discovers OAuth/OIDC endpoints
- - Registers a new OAuth client
- - Obtains and manages client credentials
- - Handles token lifecycle automatically
+ implement the MCP authorization spec or for servers that do not require
+ authentication. ToolHive automatically discovers OAuth/OIDC endpoints and
+ identifies itself using whichever mechanism the server supports:
+ - **Client ID Metadata Document (CIMD):** If the server's discovery document
+ sets `client_id_metadata_document_supported: true`, ToolHive presents
+ [`https://toolhive.dev/oauth/client-metadata.json`](https://toolhive.dev/oauth/client-metadata.json)
+ as the `client_id`. No registration round-trip is needed.
+ - **Dynamic Client Registration (DCR, RFC 7591):** When CIMD is unavailable
+ or rejected, ToolHive registers an OAuth client dynamically.
+
+ Either path handles token acquisition and lifecycle automatically.
For MCP servers that accept a bearer token in the `Authorization` header,
select **Bearer Token**. ToolHive stores the token securely and sends it as
diff --git a/docs/toolhive/guides-vmcp/authentication.mdx b/docs/toolhive/guides-vmcp/authentication.mdx
index ecea981e..7ce9c0d0 100644
--- a/docs/toolhive/guides-vmcp/authentication.mdx
+++ b/docs/toolhive/guides-vmcp/authentication.mdx
@@ -397,10 +397,13 @@ stored upstream tokens for backends.
Use the embedded authorization server when your backend MCP servers call
external APIs on behalf of individual users and no federation relationship
-exists between your identity provider and those services. It also provides OAuth
+exists between your identity provider and those services. It also supports OAuth
2.0 Dynamic Client Registration
+([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)) and Client ID
+Metadata Documents (CIMD) and Dynamic Client Registration
([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)), so MCP clients can
-register automatically without manual client configuration in ToolHive.
+identify themselves or register automatically without manual client
+configuration in ToolHive.
:::info