Resolution
Addressed by github/copilot-agent-runtime#18279.
The fix moves CLI-managed GitHub/Copilot bearer injection to the shared MCP session boundary, so trusted user-configured GitHub MCP endpoints receive the current Copilot credential in root sessions and custom-agent/subagent sessions. Explicit user-supplied Authorization headers continue to take precedence, and CLI-managed credentials remain ephemeral rather than being serialized into MCP configuration.
It also keeps credential rotation, logout, reconnect, and server removal synchronized; prevents a rejected CLI-managed bearer from falling through to GitHub OAuth dynamic registration; and preserves legitimate OAuth scope-upgrade handling for 403 insufficient_scope responses.
Describe the bug On 1.0.81-10 a user-configured https://api.githubcopilot.com/mcp/ server no longer receives the injected Copilot token. It starts with no Authorization header, takes a 401, and is reported as needing auth. The only remedy the CLI then offers — OAuth — is impossible, because github.com's authorization server does not advertise dynamic client registration: Authenticating: github Authentication failed: MCPOAuthError: Client registration failed: Authorization server does not support dynamic client registration On 1.0.80 the same config works and that OAuth prompt is merely spurious. So 1.0.80 → 1.0.81-10 turns a cosmetic false prompt into a hard block for this configuration. Workarounds exist (a PAT header, a static oauthClientId, or switching to the built-in github-mcp-server), but none is discoverable from the error, which points at an /mcp auth that cannot succeed. ### Affected version 1.0.81-10 (npm dist-tag prerelease). Not present on 1.0.80 (dist-tag latest). Linux x64, enterprise-routed account. ### Steps to reproduce the behavior Add the server exactly as a user would: console $ copilot mcp add --transport http github https://api.githubcopilot.com/mcp/ Then ask for a tool from it, denying shell so the model cannot silently answer with curl: console $ copilot -p "Call get_me from the MCP server named github and print only the login. \ Do not use shell/curl/bash; if the MCP tool is unavailable say exactly UNAVAILABLE." \ --allow-all-tools --deny-tool shell | version | result | /mcp shows | | --- | --- | --- | | 1.0.80 | Get my user profile (MCP: github) → real login | ✓ Connected, Tools (48/48 enabled) | | 1.0.81-10 | UNAVAILABLE | !, and Details: Tools unavailable: Error: MCP server "github" is not connected | Reproduced on 1.0.81-10 from a clean profile that never attempted auth, so this is not poisoned state from the failed OAuth attempt. ### Debug log — the credential-injection path simply does not run Same prompt, both versions pinned, each with its own HOME and COPILOT_HOME, both launched with --no-auto-update, counted with --log-level debug: | log line | 1.0.80 | 1.0.81-10 | | --- | --- | --- | | Starting pending GitHub MCP server "github" after authentication | 1 | 0 | | No access token was provided in this request | 0 | 6 | | Browser-based OAuth required | 0 | 2 | 1.0.80: [DEBUG] Starting pending GitHub MCP server "github" after authentication 1.0.81-10: [ERROR] worker quit with fatal: Transport channel closed, when Client(OAuthChallenge { ... "No access token was provided in this request" ... status_code: 401 }) 1.0.81-10: [WARNING] MCP OAuth authorization failed; reporting the server as needing auth {"server":"github","error":"Browser-based OAuth required for https://api.githubcopilot.com/mcp/"} The built-in github-mcp-server connects fine in the same 1.0.81-10 run, so credentials and payload are healthy — it is specifically the injection into the user-configured server that stops happening. > Careful when reproducing: the launcher auto-updates, and the payload is resolved and cached per-HOME, so a throwaway HOME pulls whatever latest is rather than the version you think you pinned. Pin the payload per version, pass --no-auto-update, and re-print copilot --version from inside the same HOME/COPILOT_HOME as the failing run — nothing else establishes which build produced a result. ### The second half: OAuth cannot rescue it Verified unauthenticated, so this is independent of any local credential: console $ curl -s https://github.com/.well-known/oauth-authorization-server/login/oauth {"issuer":"https://github.com/login/oauth", ... } # no registration_endpoint https://api.githubcopilot.com/.well-known/oauth-protected-resource/mcp/ advertises authorization_servers: ["https://github.com/login/oauth"], and that authorization server publishes no registration_endpoint. The runtime bypasses absent dynamic client registration only when the authorization server is Microsoft/Entra, so GitHub's own authorization server falls straight into registration and fails immediately. Setting oauthClientId on the server does skip registration (a self-registered OAuth App), so this is not literally unrecoverable — but nothing in the failure points there. Note the current docs steer people to the built-in server, and the custom-remote example uses a PAT, so I can't claim the URL-only form is itself the documented path — only that it is the natural one to try, and that it used to work on 1.0.80. ### Expected behavior 1. Restore the token injection for a user-configured server on a recognized Copilot MCP host, so 1.0.81 behaves as 1.0.80 did; and 2. Give GitHub's authorization server the same no-registration fallback that Microsoft/Entra already gets, so the offered remedy is not one that cannot succeed; and 3. Don't offer authenticate on a server that is already carrying a bearer. ### Additional context - Related but distinct: #4408 is the built-in server on Copilot Enterprise dying at metadata discovery (cross-origin resource identifier); #3100 asks for fallback to a user-supplied header. Here the CLI's own injected token stopped being applied, and the failure is at registration. - Worth noting for #4408: the enterprise protected-resource document already advertises authorization_servers: ["https://github.com/login/oauth"]. So once github/github-mcp-server-remote#957 fixes the advertised origin, discovery will succeed and the flow will land on this same missing-registration wall. 957 looks necessary but not sufficient for #4408. - One thing I could not rule out: the injection path may be gated by a server-side experiment rather than purely by version, in which case the split may not be strictly 1.0.80 vs 1.0.81-10 for every account.
Generated by Copilot
Resolution
Addressed by github/copilot-agent-runtime#18279.
The fix moves CLI-managed GitHub/Copilot bearer injection to the shared MCP session boundary, so trusted user-configured GitHub MCP endpoints receive the current Copilot credential in root sessions and custom-agent/subagent sessions. Explicit user-supplied
Authorizationheaders continue to take precedence, and CLI-managed credentials remain ephemeral rather than being serialized into MCP configuration.It also keeps credential rotation, logout, reconnect, and server removal synchronized; prevents a rejected CLI-managed bearer from falling through to GitHub OAuth dynamic registration; and preserves legitimate OAuth scope-upgrade handling for
403 insufficient_scoperesponses.Describe the bug On
1.0.81-10a user-configuredhttps://api.githubcopilot.com/mcp/server no longer receives the injected Copilot token. It starts with noAuthorizationheader, takes a 401, and is reported as needing auth. The only remedy the CLI then offers — OAuth — is impossible, becausegithub.com's authorization server does not advertise dynamic client registration:Authenticating: github Authentication failed: MCPOAuthError: Client registration failed: Authorization server does not support dynamic client registrationOn1.0.80the same config works and that OAuth prompt is merely spurious. So1.0.80→1.0.81-10turns a cosmetic false prompt into a hard block for this configuration. Workarounds exist (a PAT header, a staticoauthClientId, or switching to the built-ingithub-mcp-server), but none is discoverable from the error, which points at an/mcp auththat cannot succeed. ### Affected version1.0.81-10(npm dist-tagprerelease). Not present on1.0.80(dist-taglatest). Linux x64, enterprise-routed account. ### Steps to reproduce the behavior Add the server exactly as a user would:console $ copilot mcp add --transport http github https://api.githubcopilot.com/mcp/Then ask for a tool from it, denying shell so the model cannot silently answer withcurl:console $ copilot -p "Call get_me from the MCP server named github and print only the login. \ Do not use shell/curl/bash; if the MCP tool is unavailable say exactly UNAVAILABLE." \ --allow-all-tools --deny-tool shell| version | result |/mcpshows | | --- | --- | --- | |1.0.80|Get my user profile (MCP: github)→ real login |✓ Connected,Tools (48/48 enabled)| |1.0.81-10|UNAVAILABLE|!, and Details:Tools unavailable: Error: MCP server "github" is not connected| Reproduced on1.0.81-10from a clean profile that never attempted auth, so this is not poisoned state from the failed OAuth attempt. ### Debug log — the credential-injection path simply does not run Same prompt, both versions pinned, each with its ownHOMEandCOPILOT_HOME, both launched with--no-auto-update, counted with--log-level debug: | log line |1.0.80|1.0.81-10| | --- | --- | --- | |Starting pending GitHub MCP server "github" after authentication| 1 | 0 | |No access token was provided in this request| 0 | 6 | |Browser-based OAuth required| 0 | 2 |1.0.80: [DEBUG] Starting pending GitHub MCP server "github" after authentication 1.0.81-10: [ERROR] worker quit with fatal: Transport channel closed, when Client(OAuthChallenge { ... "No access token was provided in this request" ... status_code: 401 }) 1.0.81-10: [WARNING] MCP OAuth authorization failed; reporting the server as needing auth {"server":"github","error":"Browser-based OAuth required for https://api.githubcopilot.com/mcp/"}The built-ingithub-mcp-serverconnects fine in the same1.0.81-10run, so credentials and payload are healthy — it is specifically the injection into the user-configured server that stops happening. > Careful when reproducing: the launcher auto-updates, and the payload is resolved and cached per-HOME, so a throwawayHOMEpulls whateverlatestis rather than the version you think you pinned. Pin the payload per version, pass--no-auto-update, and re-printcopilot --versionfrom inside the sameHOME/COPILOT_HOMEas the failing run — nothing else establishes which build produced a result. ### The second half: OAuth cannot rescue it Verified unauthenticated, so this is independent of any local credential:console $ curl -s https://github.com/.well-known/oauth-authorization-server/login/oauth {"issuer":"https://github.com/login/oauth", ... } # no registration_endpointhttps://api.githubcopilot.com/.well-known/oauth-protected-resource/mcp/advertisesauthorization_servers: ["https://github.com/login/oauth"], and that authorization server publishes noregistration_endpoint. The runtime bypasses absent dynamic client registration only when the authorization server is Microsoft/Entra, so GitHub's own authorization server falls straight into registration and fails immediately. SettingoauthClientIdon the server does skip registration (a self-registered OAuth App), so this is not literally unrecoverable — but nothing in the failure points there. Note the current docs steer people to the built-in server, and the custom-remote example uses a PAT, so I can't claim the URL-only form is itself the documented path — only that it is the natural one to try, and that it used to work on1.0.80. ### Expected behavior 1. Restore the token injection for a user-configured server on a recognized Copilot MCP host, so1.0.81behaves as1.0.80did; and 2. Give GitHub's authorization server the same no-registration fallback that Microsoft/Entra already gets, so the offered remedy is not one that cannot succeed; and 3. Don't offerauthenticateon a server that is already carrying a bearer. ### Additional context - Related but distinct: #4408 is the built-in server on Copilot Enterprise dying at metadata discovery (cross-origin resource identifier); #3100 asks for fallback to a user-supplied header. Here the CLI's own injected token stopped being applied, and the failure is at registration. - Worth noting for #4408: the enterprise protected-resource document already advertisesauthorization_servers: ["https://github.com/login/oauth"]. So once github/github-mcp-server-remote#957 fixes the advertised origin, discovery will succeed and the flow will land on this same missing-registration wall. 957 looks necessary but not sufficient for #4408. - One thing I could not rule out: the injection path may be gated by a server-side experiment rather than purely by version, in which case the split may not be strictly1.0.80vs1.0.81-10for every account.Generated by Copilot