---
title: "Dynamic Client Registration Not Supported in MCP Clients"
description: "Your authorization server offers no way to get a client_id, so the MCP client stops before /authorize. Pre-register it — the steps per identity provider."
canonicalUrl: "https://zuplo.com/learn/mcp/errors/dynamic-client-registration-not-supported"
pageType: "mcp-error"
errorString: "Dynamic Client Registration not supported"
lastVerified: "2026-07-27"
specVersion: "2026-07-28"
---

# `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 spec accepts either, and some providers, Amazon Cognito among them, publish only the OpenID Connect document.

```bash
# 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 `POST`ing 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 are reading.

**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 does not have to offer anything. If this is what you see, this is not your problem.

**Clients also report this as:**

- `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."}`

## Fix it

**In short.** 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 spec, so a provider without it is not out of spec — Microsoft Entra ID and Amazon Cognito have no registration endpoint at all, and Okta, Auth0 and Keycloak ship theirs off or gated by default. If you would rather not pre-register an app for every client that connects, put an authorization server in front of the MCP route that accepts RFC 7591 registration or Client ID Metadata Documents itself.

The symptom is identical across platforms; the cause is not.

### Microsoft Entra ID

If your authorization server is Microsoft Entra ID

1. Entra ID has no registration endpoint, and Microsoft's own MCP guidance says so plainly: many providers do not support Dynamic Client Registration, Entra ID among them, and where it is unavailable the client has to be preconfigured with a client ID.
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. 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.

```jsonc
// .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](https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-mcp)

### Okta

If your authorization server is Okta

1. Okta does implement RFC 7591, and its authorization server metadata advertises a `registration_endpoint` of `https://{yourOktaDomain}/oauth2/v1/clients` — so the client will try registration rather than skip it. The `POST` is then rejected, because Okta documents that endpoint as requiring the `okta.clients.register` scope. There is no anonymous registration.
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. 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](https://developer.okta.com/docs/api/openapi/okta-oauth/oauth/client/)

### Auth0

If your authorization server is Auth0

1. Auth0 accepts open registration at `POST /oidc/register` with no access token, but the tenant flag is off to begin with: Auth0 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. Enabling it is not sufficient. 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. Configure 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. 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_token` — `client_credentials` is not available through registration.

```bash
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](https://auth0.com/docs/get-started/applications/dynamic-client-registration)

### Keycloak

If your authorization server is Keycloak

1. 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. What an MCP client needs is the anonymous form, and Keycloak ships that off: the docs state that the Trusted Hosts policy whitelists no host by default, which leaves anonymous client registration de-facto disabled.
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 may use, so a desktop client redirecting to loopback will not pass a policy scoped to public hostnames.
3. Two more anonymous-registration policies bite after Trusted Hosts. Max Clients Policy rejects registration once the realm holds 200 clients, which is the default for anonymous registrations. Client Scope Policy whitelists only the realm's default client scopes, so a client requesting the scope you defined for the MCP server is refused until you whitelist it.

[Keycloak: client registration service](https://www.keycloak.org/securing-apps/client-registration)

### Amazon Cognito

If your authorization server is an Amazon Cognito user pool

1. Cognito has 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 and 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. 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. 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.

```bash
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](https://docs.aws.amazon.com/cognito/latest/developerguide/federation-endpoints.html)

### Zuplo MCP Gateway

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

1. For its MCP routes the gateway 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. Browser identity is delegated to an OIDC identity provider you configure: Auth0, Okta, or any provider with OIDC discovery. The identity provider authenticates the user; 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. On the upstream side the gateway is the OAuth client: `clientRegistration: { mode: "auto" }` publishes a per-upstream Client ID Metadata Document and falls back to RFC 7591 if the upstream will not 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](/docs/mcp-gateway/auth/overview)

### Something else

Whatever your authorization server is, the fix has three shapes and you need only one of them. 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 the `curl` above and pick the shape your provider can do today — 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 may 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

The 2025-11-25 authorization spec says a client supporting every option SHOULD try them in this 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. 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 of the same failure is a `registration_endpoint` that is advertised but protected — the client dutifully POSTs to it and gets back `401`, `403` or `404`.

### The exchange

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.

**Participants:** MCP client (no client_id yet), MCP server, Authorization server (enterprise IdP), Browser (user consent)

1. **MCP client → MCP server** — `POST /mcp` — `no Authorization header`
2. **MCP server → MCP client** — `401 Unauthorized` — `WWW-Authenticate: Bearer resource_metadata=…` _(response)_
3. **MCP client → Authorization server** — `GET …/oauth-authorization-server`
4. **Authorization server → MCP client** — `200 OK` — `no registration_endpoint, no CIMD` _(response)_
5. **MCP client** (internal step) — Nothing to put in client_id — `no pre-registered client id either` _(this step fails — the flow stops here)_

Notes:

- **Steps 3–4** — 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.
- **Step 5** — VS Code offers to take a client id by hand at this point. Most other clients only report the error.

## What lands on you next

Registration was the gate. Past it the questions stop being "can this client connect" and start being "what may it do, and what did it do."

- **The client is registered. Which of our tools can it now call?** — `mcp-capability-filter-inbound` — A registered client is an authenticated client, not an authorized one. The filter allow-lists capabilities per route and blocks a hidden tool 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 just forward that token to the API behind the server?** — `mcp-token-exchange-inbound` — The spec forbids passthrough with a MUST NOT. 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. See [Zuplo MCP Gateway](/mcp-gateway).

## Keep reading

- **Prevent it** — [API Key Authentication for MCP Servers](/learn/mcp/authentication/api-key) — Service-to-service calls where you control both the agent and the MCP server
- **Similar error** — [`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`](/learn/mcp/errors/401-without-www-authenticate) — Your server answers 401 correctly, but the response carries no pointer to protected resource metadata — so the client has nothing to discover the authorization server with, and the OAuth flow never starts.
- **Similar error** — [`MCP headers config ignored when server has OAuth discovery`](/learn/mcp/errors/configured-headers-ignored-when-oauth-discovery-present) — The credential is sitting in your client config and never reaches the wire: the client found OAuth discovery metadata on your server first, and switched to the OAuth flow instead of sending the header you set.
- **Error reference** — [every documented MCP error](/learn/mcp/errors)
