MCP OAuth token exchange omits client_id in body, breaks public PKCE clients (Wrike)

Resolved 💬 3 comments Opened May 7, 2026 by natashakading Closed Jun 5, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Authenticating the Wrike MCP server (https://mcp.wrike.com/app/mcp/stream) via /mcp consistently fails at the token exchange step. The browser-side authorization succeeds (Wrike shows "authentication successful"), but Claude Code's follow-up POST to Wrike's token endpoint is rejected because client_id is missing from the request body. The MCP server stays in "needs-auth" forever and its tools never load.

Per RFC 6749 §3.2.1, public OAuth clients (no client_secret, using PKCE) MUST include client_id in the body of the token request. Claude Code's MCP OAuth client picks up the client_id for the authorization URL correctly, but doesn't include it in the subsequent POST to the token endpoint. This would affect any MCP server using PKCE + a public client, not only Wrike.

What Should Happen?

Token exchange completes successfully and Wrike MCP tools become available in /mcp and at runtime.

Error Messages/Logs

From the Claude VSCode Output channel during /mcp Authenticate on Wrike:

[DEBUG] MCP server "wrike": Using pre-configured client ID
[DEBUG] MCP server "wrike": Authorization URL: https://login.wrike.com/oauth2/authorize?response_type=code&client_id=1RLU4US8&code_challenge=[REDACTED]&code_challenge_method=S256&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback&state=[REDACTED]&scope=wsReadWrite&resource=https%3A%2F%2Fmcp.wrike.com%2Fapp%2Fmcp
[DEBUG] MCP server "wrike": Skipping browser open (skipBrowserOpen=true)
[DEBUG] MCP server "wrike": Initial auth result: REDIRECT
... (user completes browser flow, Wrike shows success) ...
[DEBUG] MCP server "wrike": MCP OAuth server cleaned up
[DEBUG] MCP server "wrike": Completing auth flow with authorization code
[DEBUG] MCP server "wrike": Using pre-configured client ID
[DEBUG] MCP server "wrike": Using scope from metadata: wsReadWrite
[DEBUG] MCP server "wrike": Returning code verifier
[DEBUG] MCP server "wrike": Using pre-configured client ID
[DEBUG] MCP server "wrike": Error during auth completion: XmH: client_id must be supplied
[ERROR] MCP OAuth failed for wrike: XmH: client_id must be supplied

After this, on every session start:
[DEBUG] MCP server "wrike": Skipping connection (cached needs-auth)

(The "Skipping connection (cached needs-auth)" loop appears related to #44830.)

Steps to Reproduce

  1. Add the Wrike MCP server (https://mcp.wrike.com/app/mcp/stream) to Claude Code.
  2. Open /mcp, find the Wrike entry, click Authenticate.
  3. Complete the Wrike OAuth flow in the browser — Wrike's page confirms "authentication successful."
  4. Return to /mcp. Wrike still shows "Needs Auth."
  5. Click "Check connection" — Server status: needs-auth.
  6. Open VS Code Output panel → "Claude VSCode" channel — observe "client_id must be supplied" error from Wrike's token endpoint.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

Unknown / N/A — never worked for this user

Claude Code Version

2.1.132 (Claude Code for VS Code extension)

Platform

Other

Operating System

macOS

Terminal/Shell

VS Code integrated terminal

Additional Information

Likely cause: Claude Code's MCP OAuth token-exchange code path does not include client_id in the application/x-www-form-urlencoded body of the POST to the token endpoint when the client is public (no client_secret). Per RFC 6749 §3.2.1, this is required for public clients. Wrike's token endpoint enforces it strictly.

Suggested fix: In the token-exchange request, always include client_id in the form-encoded body when there is no client_secret (i.e., public client / PKCE flow).

Workarounds attempted (none worked):

  • Disable + re-enable Wrike entry in /mcp
  • Reload VS Code window
  • Manual mcp__wrike__authenticate / mcp__wrike__complete_authentication tool flow (failed for an unrelated reason — see #49043: OAuth flow state lost between tool calls)

Related issues:

  • #44830 — "Discovery poisoning" causes the "Skipping connection (cached needs-auth)" log lines after token exchange has failed once, blocking retry.
  • #49043 — Lost OAuth flow state between authenticate and complete_authentication tool calls. Blocks the tool-based workaround.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗