github / github/copilot-cli

MCP OAuth: initialize request after successful OAuth login omits User-Agent header, ignoring configured custom headers

Open
#4,681 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:authentication area:mcp area:networking
Dominant language
Shell
Stars
11.2k
Forks
1.9k
Avg merge
14h 16m
Merged PRs (30d)
6

Description

Summary

When connecting to a remote MCP server (HTTP/Streamable-HTTP transport) that requires OAuth authentication, the underlying rmcp/reqwest-based HTTP client used by Copilot CLI/App does not send a User-Agent header on the initialize request that follows a successful OAuth token exchange. Custom headers configured for the MCP server in mcp-config.json (via copilot mcp add --header "...") are also not applied to this specific request.

This causes initialize requests to fail against any MCP server that is fronted by infrastructure which rejects requests without a User-Agent header (e.g. AWS WAF's managed rule AWSManagedRulesCommonRuleSet / NoUserAgent_HEADER, or similar reverse-proxy/WAF configurations), even though the OAuth handshake itself (authorize/token exchange) completes successfully.

Steps to Reproduce

  1. Configure a remote HTTP MCP server that requires OAuth, optionally with a custom header:
    copilot mcp add --transport http my-server https://example.com/mcp --header "User-Agent: MyCustomAgent/1.0"
    
  2. Trigger authentication (e.g. via Authenticate in the Copilot App, or by using the tool in Copilot CLI).
  3. Complete the browser-based OAuth login/consent flow successfully.
  4. Observe that the subsequent initialize request fails if the target server (or infrastructure in front of it) rejects requests lacking a User-Agent header — even though the configured custom User-Agent header was set on the server config.

Expected Behavior

  • The MCP client should always send a non-empty User-Agent header (e.g. an SDK/product default such as GitHubCopilotCLI/<version>) on all requests, including the post-OAuth initialize request.
  • Any custom headers configured for the MCP server (via --header, or the headers field in mcp-config.json) should be honored consistently across all requests sent to that server, including the OAuth-authenticated initialize request — not only a subset of requests.

Actual Behavior

  • The initialize request sent after a successful OAuth token exchange appears to omit the User-Agent header entirely (confirmed via server-side testing: a request without any User-Agent header reproduces the exact same generic, non-JSON 403 Forbidden response observed by the client).
  • Configuring a custom User-Agent header via copilot mcp add --header "User-Agent: ..." does not change this behavior — the header is stored in mcp-config.json but is not applied to the initialize request.
  • The resulting error surfaces as:
    RPC error -32603: Request session.mcp.oauth.login failed with message: failed to initialize MCP client: Send message error Transport [rmcp::transport::worker::WorkerTransport<rmcp::transport::streamable_http_client::StreamableHttpClientWorker<mcp::client::RawCapturingReqwestClient>>] error: unexpected server response: HTTP 403 Forbidden: <html>
    <head><title>403 Forbidden</title></head>
    <body>
    <center><h1>403 Forbidden</h1></center>
    </body>
    </html>
    , when send initialize request
    
  • The prior OAuth challenge/token exchange step succeeds (HTTP 401 with a proper WWW-Authenticate/OAuth challenge, then successful browser login/consent) — the failure occurs specifically on the follow-up initialize call.

Environment

  • GitHub Copilot CLI/App version: 1.0.82 (latest at time of testing)
  • OS: macOS (Darwin)
  • MCP server transport: http (Streamable HTTP), OAuth-protected (dynamic client registration / RFC 9728 protected resource metadata)
  • Same MCP server + OAuth flow works correctly in VS Code's Copilot Chat extension, suggesting this is specific to the rmcp-based client used by Copilot CLI/App rather than a server-side issue.

Additional Notes

  • This was root-caused by directly testing the target MCP server with and without a User-Agent header: omitting it reproduces byte-for-byte the same generic HTML 403 response seen by the CLI/App, while including any non-empty User-Agent returns the expected JSON response from the server's own application layer.
  • No corporate proxy, VPN, or network-level header rewriting was involved — headers were confirmed to pass through the network unmodified in isolated testing.
  • We suspect the initialize request after OAuth is issued via a code path (RawCapturingReqwestClient in rmcp::transport::streamable_http_client) that either uses a fresh/default reqwest::Client without the configured User-Agent/custom headers, or otherwise doesn't propagate the per-server header configuration used for other requests.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Trace the OAuth follow-up initialize request through rmcp::transport::streamable_http_client and RawCapturingReqwestClient, comparing it with the requests that use the configured mcp-config.json headers. Start by reproducing the post-login request with a required User-Agent and custom header. Done means the initialize request sends a non-empty User-Agent and preserves configured custom headers, with regression coverage for this OAuth path.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, authentication, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.