Creuto is now an OpenAI Select Partner Read More
MCP server authentication, decided per tool: Claude's shared header key can't identify a user and ChatGPT won't send one. When per-user OAuth is required.

MCP server authentication is a per-tool decision, not a per-server one. A shared API key only holds up for tools that read data every caller may see. Claude's static header credential is a beta, organization-wide key that its own docs say not to use to identify a user, and ChatGPT's Apps SDK cannot present custom API keys at all. Any tool that reads one person's records or acts in their name needs per-user OAuth.
That matters if your customers are asking to use your product from Claude and ChatGPT and you are about to put an MCP server in front of your REST API. By the end of this post you will know which of your tools can ship behind the org key you already issue, which need OAuth before either client will connect them properly, and what the current MCP spec revision, 2026-07-28, changed about client registration that most guides written before it still get wrong.
| Client | Shared API key | Per-user OAuth |
|---|---|---|
| Claude (claude.ai, Desktop, mobile, Claude Code, Cowork) | static_headers: beta, limited set of organizations, entered once by an org Owner | DCR, CIMD, Anthropic-held client credentials, or credentials entered at connection time |
| ChatGPT (Apps SDK) | Not supported: cannot present custom API keys | OAuth 2.1 with PKCE S256 and protected resource metadata; CIMD preferred, DCR still supported |
| Both clients | No machine-to-machine fallback | No client_credentials grant: no token is issued without a user |
Claude lists six authentication types for remote MCP servers in its connector authentication docs: oauth_dcr, oauth_cimd, oauth_anthropic_creds, custom_connection, static_headers and none. The static option is the one that looks like your existing API key. An organization Owner enters an API key or bearer token once, and Claude sends it in a request header on every call from anyone in that organization.
The same page is blunt about the limits. static_headers is in beta for a limited set of organizations, and Owners whose organization lacks access do not even see the request headers section. It tells you to treat the value as the organization's credential, not a person's: "every member's requests carry the same key, so don't use it to identify which user is calling. If your tools need to act as the individual user, use OAuth instead."
ChatGPT goes further. OpenAI's Apps SDK authentication page says ChatGPT cannot present custom API keys or customer-provided mTLS certificates, and does not support machine-to-machine grants such as client credentials, service accounts or JWT bearer assertions. If you need to know that a request really came from ChatGPT, it offers two transport-level options: verify the OpenAI-managed client certificate it presents over mTLS, or allowlist ChatGPT's published egress IP ranges. Neither tells you which user is calling.
A DEV Community piece published on 9 November 2026 on this exact decision says ChatGPT plugins declare API key authentication in mcp.json under extensions["com.openai"].auth. That is true of the manifest: OpenAI's plugin packaging docs accept auth.type: "api_key" with a bearer, basic or custom_header scheme. The article also notes the catch, and it is the part that decides your plan. OpenAI's submission docs say the portal's connection form supports only no authentication, OAuth and mixed access, has no API key connection flow or bearer token input, and an API key declaration alone will not complete MCP setup. Read together with the Apps SDK statement above, a public ChatGPT listing should be planned on OAuth.
Authorization is optional in MCP. The authorization specification says HTTP-based transports SHOULD conform to it when they support authorization, and stdio implementations SHOULD NOT, retrieving credentials from the environment instead. If your server runs locally over stdio, the questions are different; our post on the local port you open during an MCP setup covers that side.
For a remote server that does authorize, the spec is built on the OAuth 2.1 draft and adds specific requirements:
resource parameter, the canonical URI of your MCP server, in both authorization and token requests.Authorization: Bearer header on every request and MUST NOT appear in the query string.iss parameter in the authorization response per RFC 9207 before sending the code to any token endpoint.Your server also SHOULD include a scope parameter in the WWW-Authenticate header of its 401, so clients request only what the operation needs.
Yes. In the 2026-07-28 client registration section, Client ID Metadata Documents (CIMD) are the SHOULD, and Dynamic Client Registration (DCR) is a MAY carrying a deprecation warning: new implementations should use CIMD, and DCR remains for authorization servers that do not support it. With CIMD the client's client_id is an HTTPS URL pointing to a JSON document with at least client_id, client_name and redirect_uris. Clients that support every option SHOULD try pre-registered credentials first, then CIMD if your authorization server advertises client_id_metadata_document_supported, then DCR, then ask the user.
Deprecated in the spec does not mean unused in practice. Claude still falls back to DCR unless your authorization server metadata advertises both "client_id_metadata_document_supported": true and "none" in token_endpoint_auth_methods_supported. Its docs also warn that DCR makes Claude register a new client on every fresh connection, which can leave very large numbers of registered clients on your authorization server. Advertising CIMD properly is the cheaper path for both clients.
Both clients already let you set authentication per tool, which is why the decision belongs at that level. ChatGPT's securitySchemes marks a tool noauth (callable anonymously), oauth2, or both, and the Apps SDK docs recommend per-tool declarations even if the entire server uses the same policy. Claude's docs point servers whose tools partly work without an account to lazy authentication, which lets people use those tools straight away and sign in only when Claude reaches one that needs their account.
| What the tool touches | Example | Auth model |
|---|---|---|
| Public data, same for everyone | Product catalogue, docs search, status page | No auth, or a shared key where you need rate limiting |
| Org data every member may read | Shared dashboards, read-only org reports | Shared key can work in Claude's beta; ChatGPT still needs OAuth |
| One user's records | My tickets, my inbox, my payroll | Per-user OAuth |
| Writes in a person's name | Create invoice, approve leave, send message | Per-user OAuth, with narrow scopes |
The strongest case for the shared key deserves a fair hearing. Your API already issues one key per customer org, your permission model may already be org-level, and OAuth means running or integrating an authorization server, metadata endpoints, PKCE, refresh rotation and consent screens. If every member of a customer org genuinely has identical rights to everything the MCP server exposes, a shared key loses nothing, and building OAuth first would delay a launch for no security gain.
That case breaks at the first tool where two members of the same org should see different rows. With one key, your API cannot tell the finance lead from the intern, so either your tools return what the most privileged person could see or they enforce nothing. Your audit log records the org, not the person. And in ChatGPT the shared key path is not available for a public listing at all. This is the wrong choice if any tool reads personal data, writes on someone's behalf, or needs an audit trail that names a human.
OAuth does not guarantee per-user identity if the connector is configured at org level. GitHub issue #46207 on the claude-code repository, opened on 10 April 2026, reports that org-level MCP connectors use "a single shared OAuth token for all users in the org", that "whoever re-authenticates last sets the token for everyone", and that this rules out row-level or user-level data access control. A bot closed it as not planned on 13 April 2026, saying it did not appear to be about Claude Code; no maintainer replied. A related report, issue #44980, describes custom connectors in Claude Teams exposing shared credentials to all team members and was closed as not planned on 15 June 2026.
These are user reports, not confirmed behavior, and we have not reproduced them. Anthropic's own guidance points the same way regardless: its connector docs tell you to scope what each user can reach with your service's own per-user permissions rather than with a per-tenant OAuth app. Build your MCP server so that the token's subject, not the connector it arrived through, decides what a call can return.
An MCP server over an existing API holds two credentials: the token the client presents to the MCP server, and whatever the MCP server uses to call your API. The MCP security best practices explicitly forbid collapsing the two. Token passthrough, where the server accepts a client's token without checking it was issued to the MCP server and forwards it downstream, breaks audience validation, hides which client is calling, and lets a stolen token use your server as an exfiltration proxy. The mitigation is one line: MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server.
If your MCP server acts as an OAuth proxy to a third-party API using one static client ID, the confused deputy rules apply. An attacker can combine that static client ID, dynamic registration and a consent cookie on the upstream authorization server to receive an authorization code without the user's approval. Proxies MUST keep a per-user registry of approved client_id values and check it before forwarding, match redirect_uri by exact string, and generate a single-use state stored only after consent, with a short expiry such as 10 minutes.
The same document adds a rule for servers that carry state between calls: they MUST NOT treat possession of a state handle as authentication, and SHOULD bind each handle server-side to the user ID taken from the verified token. If you followed our post on stateless MCP without sticky sessions and now pass cart or workflow IDs as tool arguments, that binding is what stops one user guessing another's handle.
Claude adds operational requirements on top of the spec. It needs a 401 to start sign-in and ignores WWW-Authenticate on a 200. It uses only the first entry in your authorization_servers list. It waits 10 seconds for discovery, registration and token endpoints and 30 seconds for refresh requests before treating the flow as failed. ChatGPT requires your authorization server to publish code_challenge_methods_supported with S256 and tells you to verify each token's signature, issuer, audience, expiry and scopes.
Before writing code, list every tool you plan to expose and mark it with the row of the table it falls into. If every tool lands in the first two rows, ship with the org key you already have for Claude, accept that ChatGPT will need OAuth, and keep the key out of URLs and logs. If even one tool reads a person's records or writes in their name, design per-user OAuth now: protected resource metadata, CIMD support on your authorization server, audience checks, and a mapping from token subject to the permissions your API already enforces.
That mapping is usually where the real work sits, because it exposes whether your API's permission model is per-user at all. We build REST APIs in Node.js with JWT and role-based access control, and our API development and integrations team can take your existing endpoints, sort each proposed MCP tool into shared-key or per-user OAuth, and scope the authorization server and permission changes before you commit to a launch date.
An MCP server does not strictly need OAuth, because authorization is optional in the MCP specification and stdio servers should read credentials from the environment. A remote MCP server that exposes one user's records or acts in a user's name does need per-user OAuth, since neither Claude nor ChatGPT can identify an individual through a shared key.
An MCP server can accept an API key from Claude through the static_headers connection type, which is in beta for a limited set of organizations and sends one org-wide key for every member. ChatGPT's Apps SDK cannot present custom API keys, so a public ChatGPT listing should plan on OAuth.
CIMD, or Client ID Metadata Documents, is the client registration method the MCP specification recommends from the 2026-07-28 revision. The client's client_id is an HTTPS URL pointing to a JSON document listing at least its client_id, client_name and redirect_uris, which the authorization server fetches and validates.
Dynamic Client Registration is deprecated in the 2026-07-28 MCP specification and kept only for backwards compatibility with authorization servers that lack CIMD support. Claude still falls back to DCR unless the authorization server advertises client_id_metadata_document_supported and the none token endpoint auth method.
Claude connectors authenticate to a remote MCP server through OAuth with DCR, CIMD, Anthropic-held client credentials or customer-entered credentials, through a beta static request header, or with no authentication. Claude requires a 401 pointing to protected resource metadata and uses only the first listed authorization server.
Token passthrough is forbidden in MCP because a server that forwards client tokens it never validated breaks audience checks, hides which client is calling, and can be used as a proxy to exfiltrate data with a stolen token. MCP servers must reject any token not explicitly issued for them.
Ready to take the first step towards unlocking opportunities, realizing goals, and embracing innovation? We're here and eager to connect.