Skip to content

Document both OAuth client paths for custom MCP servers - #38

Open
tpavlu wants to merge 2 commits into
mainfrom
docs/custom-mcp-auth
Open

Document both OAuth client paths for custom MCP servers#38
tpavlu wants to merge 2 commits into
mainfrom
docs/custom-mcp-auth

Conversation

@tpavlu

@tpavlu tpavlu commented Aug 12, 2026

Copy link
Copy Markdown

What changed and why

The custom MCP server page said OAuth means dynamic client registration, full stop. That is only one of the two client models the server accepts — you can also paste a client ID and secret you registered yourself, plus a short allowlist of extra authorization parameters that exists because some authorization servers (Google's, notably) will not issue a refresh token without one. None of that was documented. This adds an OAuth section covering both client models and refresh behaviour, an Extra authorization parameters section with the allowlist and a worked config, a sentence on one-server-per-config and field-level rejection of unsupported fields, and four new rows in the support table. No existing headings, structure, or correct prose were changed.

Verified

All in ref-tools/ref:

  • Two client models. plan-app/server/src/services/mcpOAuthProvider.tssaveClientInformation stores DCR-registered clients as oauthClientSource: 'dynamic'; plan-app/server/src/routes/integrations.ts (/mcp/save, ~L401–416) stores a user-supplied oauth block as oauthClientSource: 'pre-registered'. clientMetadata comment states pre-registered clients keep their own auth method.
  • oauth.clientId / oauth.clientSecret both required, non-empty strings. plan-app/shared/mcpServerSpec.ts L384–403.
  • Authorization-params allowlist is exactly access_type, prompt, login_hint, include_granted_scopes. ALLOWED_AUTHORIZATION_PARAMS in plan-app/shared/mcpServerSpec.ts L73–78; anything else returns an unsupported_field error at save time (L435–454). The doc comment above it gives the rationale documented here (Google needs access_type=offline, optionally prompt=consent, to issue a refresh token).
  • Flow-critical params cannot be overridden. PROTECTED_AUTHORIZATION_PARAMS in mcpOAuthProvider.ts L37–47 (response_type, client_id, redirect_uri, state, code_challenge, code_challenge_method, resource, scope, nonce) are re-asserted from the SDK-built URL after the extra params are applied.
  • Automatic refresh, and what happens when it fails. getFreshAccessToken in mcpOAuthProvider.ts refreshes on expiry; with no refresh token, or on a failed/unauthorized refresh, it calls markNeedsAuth, which sets the server's status to needs_auth.
  • Changing client ID / secret / authorizationParams forces re-consent. resolveEffectiveAuth computes oauthClientChanged (integrations.ts L238–253); /mcp/save clears stored tokens in the same write and saves the server as needs_auth (L417–435, plus the consentPending logic at L373–377).
  • Exactly one server per config. parseMcpServerSpec rejects names.length !== 1 with server_count (mcpServerSpec.ts L205–218); an mcpServers wrapper is accepted and unwrapped.
  • Unsupported transports and fields are field-level errors, not silent drops. STDIO_KEYS (command, args, env, cwd) and any key outside type/url/headers/enabled/oauth produce one diagnostic per offending token (mcpServerSpec.ts L247–294); the file's overview states the fail-closed intent explicitly.
  • Both paths are live in production, not gated to a subset: the Statsig gate mcp_json_panel (which controls the JSON view where an oauth block can be entered) is enabled at 100% for the production environment.

Unverified / omitted

  • The numbered "Connect a custom MCP server" steps look stale and were left alone, per the brief. The current settings UI (plan-app/client/src/components/settings/McpServersSection.tsx, useMcpServerDraft.ts) is a Form ⇆ JSON tabbed panel: the Form tab has name, URL, and header rows only, and there is no "choose an authentication method" control — OAuth is configurable only in the JSON view (isFormEditable returns false when an oauth block is present; the form tab disables with "OAuth fields can only be edited as JSON"). Only the OAuth bullet's claim was corrected; the click path was not rewritten, since it cannot be confirmed without seeing the UI. A follow-up with a screenshot or a walkthrough would unblock rewriting steps 1–6.
  • Where in the UI the JSON config is pasted is therefore only described as "the server's JSON config", with no navigation instructions.
  • Omitted: that the extra-parameter allowlist is marked experimental and may grow; that a pre-registered client survives credential invalidation while a DCR client does not; that the edit panel can re-reveal a stored pre-registered client ID/secret. All true in code, but internal-lifecycle detail rather than something a customer acts on.
  • Omitted: any statement about scopes, token storage location, or per-server tool limits — not investigated.

Conflicts

docs.json was not edited; this PR touches plans/integrations/custom-mcp.mdx only, so no conflicts with sibling PRs in this batch are expected.


Generated by Claude Code

tj-ref added 2 commits August 12, 2026 01:25
Word-level pass toward simplified technical English. Swaps requires/required
and previous for their plainer equivalents on lines this PR introduced.
No technical content or product names changed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants