Describe the bug
Summary
After a successful OAuth 2.1 + DCR flow against an MCP server using Streamable HTTP, Copilot CLI sends the Authorization: Bearer … header on initialize but omits it on subsequent JSON-RPC requests (tools/list, tools/call).
The server-side session is shown as "connected" in /mcp, but every tool invocation fails with 401.
Environment
copilot --version → 1.0.37 (latest at time of report)
- macOS (Darwin 25.4.0)
- MCP server: own implementation, Rust, Streamable HTTP via
rmcp 1.4.0
- Auth: OAuth 2.1 + RFC 7591 (Dynamic Client Registration), public client (PKCE,
token_endpoint_auth_method=none)
- AS metadata exposed at
/.well-known/oauth-authorization-server (RFC 8414)
- Same server works correctly with Claude Desktop and Gemini CLI
Expected: Tool call succeeds (Bearer sent on the JSON-RPC tools/call HTTP request).
Observed: Tool call fails. Server logs show the JSON-RPC tools/call arrived with no Authorization header.
Server-side evidence
{
"level": "WARN",
"service": "backend",
"target": "rmcp::service",
"message": "response error",
"id": "4",
"error": "ErrorData { code: ErrorCode(-32600), message: \"UNAUTHORIZED: missing Authorization header\", data: None }"
}
JSON-RPC id 4 corresponds to a tools/call (ids 0–2 are the MCP handshake: initialize, notifications/initialized, tools/list).
The DCR registration of the Copilot CLI client succeeded:
| field |
value |
client_name |
GitHub Copilot CLI |
client_type |
public |
grant_types |
authorization_code, refresh_token |
redirect_uris |
http://127.0.0.1:<random-port>/ |
dynamically_registered |
true |
So the OAuth flow itself completes — the bug is downstream, in the MCP transport layer not propagating the Bearer to subsequent JSON-RPC HTTP requests.
Affected version
GitHub Copilot CLI 1.0.37.
Steps to reproduce the behavior
- Configure the MCP server in
~/.copilot/mcp-config.json with "type": "http" pointing at a Streamable HTTP MCP that requires Bearer on every request.
- Start Copilot CLI,
/mcp lists the server as connected (browser opens, OAuth flow completes, refresh + access tokens stored).
- Ask Copilot to invoke any tool exposed by the server.
Expected behavior
Tool call succeeds (Bearer sent on the JSON-RPC tools/call HTTP request).
Additional context
Cross-client check
The exact same server, with the exact same DCR + OAuth config, works correctly with:
- Claude Desktop — Bearer sent on every JSON-RPC request
- Gemini CLI MCP client — Bearer sent on every JSON-RPC request
So the server is compliant with the MCP Streamable HTTP spec (2025-03-26). The bug is specific to Copilot CLI's handling of the access token
after initialize.
Likely cause
Per the MCP Streamable HTTP spec, each JSON-RPC message is an independent HTTP POST to /mcp. The Authorization header must be attached to every request, not just to initialize or to the SSE long-lived stream.
It looks like Copilot CLI's OAuthProvider/transport binding only attaches the Bearer at session establishment (or to the SSE stream open), not on subsequent POSTs that carry the JSON-RPC payloads.
Describe the bug
Summary
After a successful OAuth 2.1 + DCR flow against an MCP server using Streamable HTTP, Copilot CLI sends the
Authorization: Bearer …header oninitializebut omits it on subsequent JSON-RPC requests (tools/list,tools/call).The server-side session is shown as "connected" in
/mcp, but every tool invocation fails with 401.Environment
copilot --version→ 1.0.37 (latest at time of report)rmcp1.4.0token_endpoint_auth_method=none)/.well-known/oauth-authorization-server(RFC 8414)Expected: Tool call succeeds (Bearer sent on the JSON-RPC
tools/callHTTP request).Observed: Tool call fails. Server logs show the JSON-RPC
tools/callarrived with noAuthorizationheader.Server-side evidence
{ "level": "WARN", "service": "backend", "target": "rmcp::service", "message": "response error", "id": "4", "error": "ErrorData { code: ErrorCode(-32600), message: \"UNAUTHORIZED: missing Authorization header\", data: None }" }JSON-RPC id
4corresponds to atools/call(ids 0–2 are the MCP handshake:initialize,notifications/initialized,tools/list).The DCR registration of the Copilot CLI client succeeded:
client_nameGitHub Copilot CLIclient_typepublicgrant_typesauthorization_code, refresh_tokenredirect_urishttp://127.0.0.1:<random-port>/dynamically_registeredtrueSo the OAuth flow itself completes — the bug is downstream, in the MCP transport layer not propagating the Bearer to subsequent JSON-RPC HTTP requests.
Affected version
GitHub Copilot CLI 1.0.37.
Steps to reproduce the behavior
~/.copilot/mcp-config.jsonwith"type": "http"pointing at a Streamable HTTP MCP that requires Bearer on every request./mcplists the server as connected (browser opens, OAuth flow completes, refresh + access tokens stored).Expected behavior
Tool call succeeds (Bearer sent on the JSON-RPC
tools/callHTTP request).Additional context
Cross-client check
The exact same server, with the exact same DCR + OAuth config, works correctly with:
So the server is compliant with the MCP Streamable HTTP spec (2025-03-26). The bug is specific to Copilot CLI's handling of the access token
after
initialize.Likely cause
Per the MCP Streamable HTTP spec, each JSON-RPC message is an independent HTTP POST to
/mcp. TheAuthorizationheader must be attached to every request, not just toinitializeor to the SSE long-lived stream.It looks like Copilot CLI's OAuthProvider/transport binding only attaches the Bearer at session establishment (or to the SSE stream open), not on subsequent POSTs that carry the JSON-RPC payloads.