Connect an MCP client
TL;DR — Your MCP server is running, tokens issue, everything is wired. Now point a client at it. The MCP flow is: first request →
401withWWW-Authenticate→ fetch PRM → discover the AS → pick a client-identification path (CIMD, pre-registered, or the now-deprecated DCR) → run OAuth → present tokens ontools/*calls. This page has the per-client config (Claude Desktop, Cursor, VS Code Copilot, MCP Inspector) plus known quirks like Claude Code omittingscopeon/authorize.
Prerequisites
- Your MCP server is running at
https://mcp.example.com/mcp(or local equivalent). - Your SDK wired PRM at
/.well-known/oauth-protected-resource/mcp(RFC 9728 §3 path-scoped form) — verify withcurl -s https://mcp.example.com/.well-known/oauth-protected-resource/mcp | jq. - AuthPlane AS is reachable at the URL your PRM’s
authorization_serversfield points to. - Your MCP client can reach both hosts (no CORS blocks, no firewall).
If any of these are broken, no client will connect — start with Debugging checklist before this page.
What every MCP client does
Per MCP 2026-07-28, the client kicks off with an unauthenticated request and lets the resource server point at the PRM. It then picks a client-identification path — client-metadata discovery (CIMD) is the SHOULD for both clients and authorization servers; pre-registered client_ids SHOULD be supported as an option; DCR (RFC 7591) is a MAY that the spec formally deprecates, “retained for backwards compatibility with authorization servers that do not support Client ID Metadata Documents”. A client supporting all three SHOULD prefer pre-registered credentials when it has them, then CIMD when the AS advertises client_id_metadata_document_supported (AuthPlane does), then DCR. All three converge on the same OAuth authorize/token exchange:
0. Client → your MCP server POST /mcp (no Authorization header)
← 401 WWW-Authenticate: Bearer resource_metadata="…"
1. Client → your MCP server GET <resource_metadata URL from step 0>
(path-scoped: /.well-known/oauth-protected-resource/mcp)
(if step 0 didn't carry a URL: probe the path-scoped
form first, then root /.well-known/oauth-protected-resource)
← PRM {authorization_servers, scopes_supported}
2. Client → AuthPlane GET /.well-known/oauth-authorization-server
(clients MUST try this RFC 8414 form first, then
/.well-known/openid-configuration — AuthPlane serves
both since v0.2.0, RFC 8414 on all versions)
← AS metadata
3. Client → AuthPlane *one of*:
· use its pre-registered client_id (first choice when it has one)
· CIMD: a URL-shaped client_id the AS fetches (SHOULD)
· POST /oauth/register (DCR, RFC 7591 — deprecated)
← client_id (+ secret if confidential)
4. Client → AuthPlane GET /oauth/authorize?response_type=code
&code_challenge=…
&code_challenge_method=S256
&resource=https://mcp.example.com/mcp
&scope=tools/read
← redirect through login + consent → code (+ iss)
(client MUST validate iss against the AS metadata
issuer, RFC 9207, before touching /oauth/token)
5. Client → AuthPlane POST /oauth/token
← access_token + refresh_token
(token_type=DPoP if client sent a DPoP header)
6. Client → your MCP server POST /mcp
Authorization: Bearer (or DPoP) <token>
← tool result
Clients MUST support both discovery mechanisms (MCP 2026-07-28): use the resource_metadata URL from the WWW-Authenticate challenge when present, otherwise fall back to probing the well-known URIs — the path-scoped form first, then the root. Two more client-side MUSTs in this revision: validate the iss parameter on the authorization response (RFC 9207) before redeeming the code, and send the resource parameter on both /oauth/authorize and /oauth/token regardless of whether the AS advertises support. A client registering via DCR MUST also send application_type — omitting it defaults to web under OIDC, which refuses the loopback redirect URIs native clients use; AuthPlane accepts and persists the field since v0.2.0 and reports the defaulting in the registration response.
Every client below implements this same flow with slight configuration and UI differences.
MCP Inspector
The reference client — fastest way to smoke-test everything.
npx @modelcontextprotocol/inspector https://mcp.example.com/mcp
Or for local dev:
npx @modelcontextprotocol/inspector http://localhost:8080/mcp
The URL must include the /mcp path (or whatever endpoint your SDK is configured for). http://localhost:8080 alone points at the server root and Inspector will report “no tools”.
Inspector opens a browser tab with the OAuth flow, walks through discovery, DCR, authorize, consent, and lands you on a UI where you can invoke tools with a valid token. Every scenario is covered by e2e/scenarios/mcp_inspector_test.go in the authserver repo — if Inspector works, your server is spec-compliant.
Full walkthrough in Guides: Testing with MCP Inspector.
Claude Desktop
Config file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Add your server to mcpServers:
{
"mcpServers": {
"my-server": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp"
}
}
}
Restart Claude Desktop. On first tool use, Claude Desktop pops up the OAuth flow in a browser window. Once you approve consent, the tokens are cached and subsequent tool calls are silent (with refresh happening in the background).
No API key or client_id needed in the config — current Claude Desktop identifies itself via CIMD (a URL-shaped client_id AuthPlane fetches and validates); older builds used DCR. Both paths work against AuthPlane.
Claude Code
Command-line invocation:
claude --mcp-server https://mcp.example.com/mcp
Or add to ~/.claude/config.json:
{
"mcpServers": {
"my-server": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp"
}
}
}
Known Claude Code quirks
Track authserver/docs/reference/mcp-client-compatibility.md for the current state. As of writing:
- Omits
scopeon/oauth/authorize(anthropics/claude-code#12077). Without a workaround, the AS issues a zero-scope token and you get403on every tool call.- Fix: set
oauth.require_scope: false(orAUTHPLANE_OAUTH_REQUIRE_SCOPE=false). AuthPlane then defaults missing scope to all registered scopes for the resource (ADR-012).
- Fix: set
- Omits
scopeon DCR (#4540) — harmless; AuthPlane doesn’t enforce scope at registration. - Omits
offline_access(#7744) — harmless; AuthPlane issues refresh tokens for allauthorization_codegrants regardless. - Omits
resourceon/oauth/authorize(#10572) — token’saudwill be empty; ADR-012 scope defaulting still applies.
Minimal Claude-Code-compatible config:
oauth:
require_scope: false # workaround for anthropics/claude-code#12077
Or env var: AUTHPLANE_OAUTH_REQUIRE_SCOPE=false.
Cursor
Cursor’s config lives at:
- macOS/Linux:
~/.cursor/mcp.json - Windows:
%APPDATA%\Cursor\mcp.json
{
"mcpServers": {
"my-server": {
"url": "https://mcp.example.com/mcp",
"type": "streamable-http"
}
}
}
Restart Cursor. First tool call triggers the OAuth flow in an embedded webview.
VS Code Copilot Chat
Uses settings.json:
{
"github.copilot.chat.mcp.servers": {
"my-server": {
"url": "https://mcp.example.com/mcp",
"type": "streamable-http"
}
}
}
Compatibility status: Pending — not yet in AuthPlane’s automated E2E suite. Manual validation required. If you hit issues, file a compat report following the MCP compatibility template.
Once connected — verifying with the Admin UI
Open https://auth-admin.internal.example.com/admin/ui/ (or the local http://localhost:9001/admin/ui/) and check:
- Clients — every MCP client that registered via DCR appears here with its
client_id, redirect URIs, and grant types. - Grants — user consent decisions. One row per (user, client, resource) after consent.
- Issuances — every token issued, with
sub,client_id,resource,scope,token_type(Bearer or DPoP), andexpires_at. - Audit log — the full trail of
token_issued,token_revoked,consent_grantedevents with timestamps.
If a client connected but tool calls fail, the token IS in Issuances but not usable — inspect its scope and aud claims against what your MCP server requires. See Reference: Errors for the top-20 debugging fixes.
Common failure modes
More at Reference: Errors and Troubleshooting: Debugging.
Related
- Guides: Testing with MCP Inspector — deep dive on Inspector-driven validation
- Quickstart — the full server-side setup that unlocks these clients
- Reference: Configuration → OAuth —
oauth.require_scopeand other client-facing knobs - Compatibility matrix (authserver repo) — living document, updated per client release
- SDKs overview — server-side adapter matrix