Zuplo
On this page
MCP error

The MCP client cannot get a client ID

The string your client printed
Dynamic Client Registration not supported

The MCP client reached your authorization server's metadata and found no way to obtain a client ID — no registration endpoint, no Client ID Metadata Document support, and none configured in the client — so it stopped before the OAuth flow began.

MCP specification
2026-07-28

Is this you?

The error names registration, so read the authorization server's own metadata and see which of the three registration paths it actually offers. Two steps: find the authorization server from the 401, then read its metadata. If /.well-known/oauth-authorization-server returns 404, try /.well-known/openid-configuration — the 2025-11-25 specification accepts either, and some providers, Amazon Cognito among them, publish only the OpenID Connect document.

TerminalRun this to check — it changes nothing
# 1. Which authorization server? Read it out of the 401.
curl -sS -D - -o /dev/null -X POST https://your-server.example.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | grep -i '^www-authenticate'

# 2. Ask that authorization server how a client is meant to identify itself.
curl -sS https://your-idp.example.com/.well-known/oauth-authorization-server \
  | jq '{registration_endpoint,
         client_id_metadata_document_supported,
         token_endpoint_auth_methods_supported}'
This error
Both registration_endpoint and client_id_metadata_document_supported come back null. Or registration_endpoint is advertised and POSTing to it returns 401/403 (Okta without the okta.clients.register scope, Keycloak with no trusted host) or 404 (advertised but never served). client_id_metadata_document_supported: true with no "none" in token_endpoint_auth_methods_supported fails the same way: Claude documents that it then falls back to registration, which is exactly the error you're reading.
Then the fix is the next section.
Working
Either a registration_endpoint that answers an unauthenticated POST with 201 and a client_id, or client_id_metadata_document_supported: true together with "none" in token_endpoint_auth_methods_supported. One is enough. A client ID you pre-registered and configured in the client is also a pass; then the metadata doesn't have to offer anything.
Then this isn't your problem — try the error reference.
6 other strings clients print for this
  • Incompatible auth server: does not support dynamic client registration
  • The authorization server '<url>' does not support automatic client registration. Do you want to proceed by manually providing a client registration (client ID)?
  • Automatic client registration isn't supported by <connector>. Edit the connector and add an OAuth Client ID.
  • Couldn't register with <connector>'s sign-in service. You can try again, or add an OAuth Client ID in the connector settings.
  • Dynamic client registration failed: HTTP 422
  • {"error":"invalid_client_metadata","error_description":"Dynamic registration is not available for this client."}

Dotted spans such as <url> are placeholders — your client prints your own value there.

Fix it

Create the OAuth client in your identity provider by hand and give the MCP client the resulting ID: oauth.clientId in mcp.json for VS Code, the OAuth Client ID field on a Claude custom connector, claude mcp add --client-id for Claude Code. Registration is optional in the 2025-11-25 specification: Microsoft Entra ID and Amazon Cognito have no registration endpoint at all, and Okta, Auth0, and Keycloak ship theirs off or gated by default. The 2026-07-28 revision then made Dynamic Client Registration Deprecated, with Client ID Metadata Documents as the migration path, so don't chase it. To avoid pre-registering an app per client, put an authorization server in front of the MCP route that accepts Client ID Metadata Documents or RFC 7591 registration itself.

Have your agent fix this

Copy a prompt that sends your coding agent to this page to find and apply the right fix for your project.

The cause depends on your platform. Pick yours.

Microsoft Entra ID

If your authorization server is Microsoft Entra ID

  1. 1 Expect no registration endpoint. Microsoft's own MCP guidance says so plainly: many providers don't support Dynamic Client Registration, Entra ID among them, and where it is unavailable you preconfigure the client with a client ID.
  2. 2 Create an app registration for the MCP server and expose at least one delegated scope on it — api://<client-id>/user_impersonation is what App Service creates for you. Then add the MCP client's own Entra application id to preAuthorizedApplications for that scope. Skip the pre-authorization and a client that never surfaces an interactive login, such as GitHub Copilot in VS Code, fails on consent instead of on registration.
  3. 3 Give the client that pre-registered client ID: oauth.clientId in mcp.json for VS Code, the OAuth Client ID field on a Claude custom connector, claude mcp add --client-id for Claude Code.
JSONjsonc
// .vscode/mcp.json
{
  "servers": {
    "my-server": {
      "type": "http",
      "url": "https://your-server.example.com/mcp",
      "oauth": { "clientId": "00000000-1111-2222-3333-444444444444" }
    }
  }
}
Microsoft Learn: MCP server authorization

Okta

If your authorization server is Okta

  1. 1 Expect the client to try registration and be refused. Okta implements RFC 7591 and its authorization server metadata advertises a registration_endpoint of https://{yourOktaDomain}/oauth2/v1/clients, so the client tries it rather than skipping it. The POST is then rejected, because Okta documents that endpoint as requiring the okta.clients.register scope. There is no anonymous registration.
  2. 2 Create the OAuth app in Okta yourself and register every redirect URI the client will actually use. VS Code requires two: http://127.0.0.1:33418 and https://vscode.dev/redirect. Hosted Claude surfaces use https://claude.ai/api/mcp/auth_callback. Claude Code takes a loopback redirect on an ephemeral port, so pin the port with claude mcp add --callback-port 8080 and register http://localhost:8080/callback rather than trying to allow every port.
  3. 3 Hand that client ID to the client. You can instead stand up a /register shim that forwards to Okta with your API token attached, and people do — but then every MCP client that reaches your server mints a real app in your Okta org, and you own that lifecycle.
Okta: Dynamic Client Registration API

Auth0

If your authorization server is Auth0

  1. 1 Turn the tenant flag on. Auth0 accepts open registration at POST /oidc/register with no access token, but documents Dynamic Client Registration as disabled by default for all tenants. Enable it at Dashboard > Settings > Advanced, or PATCH /api/v2/tenants/settings with the enable_dynamic_client_registration flag.
  2. 2 Configure default permissions too, because the flag alone is not enough. Every app created by registration is a third-party application with a tpc_ client ID prefix, and Auth0 is explicit that without default permissions such a client can access no API at all. Set default permissions for third-party applications on the API your MCP server represents before you turn registration on, or registration succeeds and every tool call still comes back unauthorized.
  3. 3 If registration used to work and stopped, check the two limits rather than the flag: /oidc/register is rate-limited to 5 requests per second per tenant, and a Tenant Access Control List rule carrying the dcr scope can block requests to it. Note also that clients created this way only get authorization_code and refresh_tokenclient_credentials is not available through registration.
Terminalbash
curl --request PATCH \
  --url 'https://{yourDomain}/api/v2/tenants/settings' \
  --header 'authorization: Bearer {YOUR_MANAGEMENT_API_TOKEN}' \
  --header 'content-type: application/json' \
  --data '{ "flags": { "enable_dynamic_client_registration": true } }'
Auth0: Dynamic Client Registration

Keycloak

If your authorization server is Keycloak

  1. 1 Expect the anonymous form to be off. Keycloak implements OIDC Dynamic Client Registration at /realms/<realm>/clients-registrations/openid-connect and advertises it in the realm's /.well-known/openid-configuration, so the client tries it — but an MCP client needs the anonymous form, and the docs state that the Trusted Hosts policy allows no host by default, which leaves anonymous client registration disabled in practice.
  2. 2 Either add the client's hosts and redirect-URI domains under Clients > Client registration > Client Registration Policies > Trusted Hosts, or create the client in the admin console and give the MCP client its client ID. Trusted Hosts also constrains the redirect URIs a registered client can use, so a desktop client redirecting to loopback won't pass a policy scoped to public hostnames.
  3. 3 Check two more anonymous-registration policies after Trusted Hosts. Max Clients Policy rejects registration once the realm holds 200 clients, which is the default for anonymous registrations. Client Scope Policy allows only the realm's default client scopes, so a client requesting the scope you defined for the MCP server is refused until you add it.
Keycloak: client registration service

Amazon Cognito

If your authorization server is an Amazon Cognito user pool

  1. 1 Expect no registration endpoint to advertise. AWS's endpoint reference for a user pool covers /oauth2/authorize, /oauth2/token, /oauth2/userInfo, /oauth2/revoke, the IdP-response endpoints, and the SAML endpoints, and OIDC discovery — no registration endpoint appears on it. Discovery is OIDC only, and it sits on a different host from the rest: https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID/.well-known/openid-configuration. There is no RFC 8414 document, so a client that probes only /.well-known/oauth-authorization-server finds nothing at all.
  2. 2 Create an app client per MCP client, with that client's callback URL and no secret — MCP clients are public clients using PKCE. Mind the constraint AWS documents on callback URLs: HTTPS is required, and http://localhost is the only exception, so a client whose redirect is an IP loopback — VS Code requires http://127.0.0.1:33418 — falls outside it.
  3. 3 Put the returned client ID in the client's config. If you need one client identity to cover many MCP clients, put an authorization server in front of the MCP route instead of writing a /register shim that calls CreateUserPoolClient: a shim hands anyone who can reach it the ability to create app clients in your pool.
Terminalbash
aws cognito-idp create-user-pool-client \
  --user-pool-id us-east-1_EXAMPLE \
  --client-name "Claude connector" \
  --no-generate-secret \
  --callback-urls "https://claude.ai/api/mcp/auth_callback" \
  --allowed-o-auth-flows code \
  --allowed-o-auth-scopes openid profile \
  --allowed-o-auth-flows-user-pool-client
AWS: user pool endpoints

Zuplo MCP Gateway

If your identity provider has no dynamic registration and you don't want to pre-register an app for every client that connects

  1. 1 Let the gateway own registration. For its MCP routes it is both the OAuth 2.1 resource server and the authorization server: it accepts RFC 7591 registration at /oauth/register, and with CIMD enabled it advertises client_id_metadata_document_supported: true and accepts URLs as client_id values — so a client registers dynamically or presents a Client ID Metadata Document URL, and either works.
  2. 2 Delegate browser identity to an OIDC identity provider you configure: Auth0, Okta, or any provider with OIDC discovery. The identity provider authenticates the user, and the gateway then issues its own bearer token to the MCP client. The identity provider never sees the MCP client and never has to issue it a client ID, so its lack of registration stops being the blocker.
  3. 3 On the upstream side, choose how the gateway registers as an OAuth client. clientRegistration: { mode: "auto" } publishes a per-upstream Client ID Metadata Document and falls back to RFC 7591 if the upstream won't accept a URL client ID; mode: "manual" uses a pre-registered clientId and clientSecret for upstreams that require an approved client.
Zuplo MCP Gateway: authorization

Something else

Whatever your authorization server is, the fix has three shapes and you need only one. Pre-register the OAuth client and configure its client_id in the MCP client. Support Client ID Metadata Documents, so the client can present a URL instead of an id it had to be given. Or put something in front of the MCP route that owns client registration itself, and delegate user identity to your provider behind it. Read the metadata document from that curl and pick the shape your provider can do — and if it advertises a registration_endpoint that returns 401 or 403, the endpoint is gated rather than missing, so the setting to find is whichever policy decides who can register. Discount any advice that calls your provider non-conformant for lacking registration: it was a SHOULD in the 2025-06-18 revision and is a MAY in 2025-11-25.

Why it happens

Both the 2025-11-25 and 2026-07-28 authorization specifications say a client supporting every option SHOULD try them in the same order: a client_id it already holds, then a Client ID Metadata Document if the authorization server advertises client_id_metadata_document_supported, then RFC 7591 registration if it advertises a registration_endpoint, and failing all three, prompt the user to enter the client information.

When the metadata offers neither mechanism and no static client ID is configured, that prompt is the whole remaining path — which is why VS Code asks and most other clients only report the error.

The 2026-07-28 revision lists Dynamic Client Registration as Deprecated in its deprecated-features registry, with Client ID Metadata Documents as the migration path, and notes "This option remains available for backwards compatibility with authorization servers that do not support Client ID Metadata Documents." Deprecated features stay in the specification, so a working registration setup keeps working; the earliest it becomes eligible for removal is the first revision released on or after 2027-07-28.

Enterprise identity providers are the usual cause: they issue client IDs through an admin console, and RFC 7591 open registration means an unauthenticated POST that creates a real OAuth client. The quieter shape is a registration_endpoint that is advertised but protected — the client POSTs to it and gets back 401, 403, or 404.

The exchange

  • MCP client no client_id yet
  • MCP server
  • Authorization server enterprise identity provider
  • Browser user consent
  1. MCP client to MCP server

    POST /mcp no Authorization header
  2. MCP server to MCP client

    401 Unauthorized WWW-Authenticate: Bearer resource_metadata=…
  3. The client is checking the three ways it can get a client ID: one you pre-registered and configured, a Client ID Metadata Document, or RFC 7591 registration. This metadata offers none of them.

    MCP client to Authorization server

    GET …/oauth-authorization-server
  4. Authorization server to MCP client

    200 OK no registration_endpoint, no CIMD
  5. VS Code offers to take a client ID by hand at this point. Most other clients only report the error.

    Inside MCP client

    Nothing to put in client_id (this step fails) no pre-registered client ID either

    Flow stops here — Browser not reached

The MCP client posts to the server with no credentials and gets a correct 401 pointing at the server's protected resource metadata, which names the authorization server. It reads that authorization server's metadata and finds no registration endpoint and no Client ID Metadata Document support, and it has no client ID of its own, so the flow stops there. /authorize is never called, so no consent screen opens and no token is minted.
Zuplo

Registration was the gate; past it, what matters is what the client can do and what it did.

The client is registered. Which of our tools can it now call?

mcp-capability-filter-inbound

Nothing beyond the route's capability allowlist — registration made the client authenticated, not authorized. A hidden tool is blocked with MethodNotFound before the request is forwarded.

That client ID is in six places now. Which one made this call?

capability_invocation

Each event carries the client and the caller alongside the capability and the outcome, so a bad call resolves to a client and a user rather than to a shared IP.

This token works. Can it be replayed against our other MCP route?

RFC 8707 resource indicators

No. A gateway-issued token is bound to one route's canonical resource URI, so a token minted for one MCP route is rejected at another.

Can I forward that token to the API behind the server?

mcp-token-exchange-inbound

No — the specification forbids that with a MUST NOT, and the gateway makes obeying it the default: inbound auth headers are stripped and an independent upstream credential is minted per user or per gateway.

All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out. Your MCP server keeps the code and the authentication it has today.

Stop debugging auth on every MCP server

A gateway in front of your MCP servers handles discovery, audience binding, and token validation once — for every server and every client.