Describe the bug
An OAuth-protected HTTP MCP server that has a valid, unexpired access token in
~/.copilot/mcp-oauth-config is answered with HTTP 401 on every new --acp
session, and the CLI responds by opening the OAuth authorization page again.
Because the browser SSO session is already established, the flow self-approves
and the tab closes — so the user sees an unrequested sign-in page flash open on
every single session start, with no way to suppress it.
This is a regression. An older build of the CLI connects the same server,
with the same token, without any 401 and without opening any page.
Critically, the stored token has no refresh token (the tokens file contains
only accessToken, expiresAt, scope), so there is no silent renewal path —
the CLI appears to fall straight through to the interactive flow rather than
using the access token it already holds.
Affected version
All affected and unaffected builds self-report GitHub Copilot CLI 1.0.89, so
the version string cannot distinguish them. Please identify builds by hash:
| Build date |
Size |
SHA-256 (first 16) |
Behaviour |
| Aug 14 |
152.1 MB |
4F590F1F60F3F2BD |
works — no 401, no page |
| Sep 23 |
144.2 MB |
E2160809894BA950 |
not tested |
| Sep 29 |
144.8 MB |
155778DD92A2171A |
broken — 401 + page every session |
copilot.exe was auto-replaced on Sep 23 and again on Sep 29; both new builds kept
the 1.0.89 version string. Please confirm whether multiple binaries shipped under
that version.
- OS: Windows 11 (10.0.26200)
- Mode:
copilot --acp (ACP server), 4 × --plugin-dir
- MCP protocol negotiated:
2025-11-25
Actual behavior — CLI log
From ~/.copilot/logs/process-*.log on the Sep 29 build (hostnames redacted):
[ERROR] [rust:rmcp::transport::worker] worker quit with fatal: Transport channel closed,
when Client(OAuthChallenge {
www_authenticate_header: "******\"https://mcp.internal.example.com/.well-known/oauth-protected-resource/servers/REDACTED-MCP/mcp\"",
response: McpOAuthHttpResponse { status_code: 401, header_count: 6, has_body: true } })
[INFO] [rust:acp::mcp_oauth] opened the MCP OAuth authorization page {"server_name":"REDACTED-MCP"}
[WARNING] [rust:copilot_runtime::session::mcp::agent_host] HTTP 401 challenge
(WWW-Authenticate: ******"https://mcp.internal.example.com/.well-known/oauth-protected-resource/servers/REDACTED-MCP/mcp")
{"server":"REDACTED-MCP"}
Possibly the same root cause — the new discovery call is rejected for lack of a
bearer token, then falls back:
[WARNING] [rust:mcp::client] server/discover failed; retrying with legacy initialize
{"error":"unexpected server response: HTTP 400 Bad Request: {\"error\":\"Invalid or missing bearer token\"}"}
A/B isolating the build
Same machine, same 3-minute window, same on-disk token (issued earlier that day,
expiresAt 3 days out), identical client invocation and identical MCP server list.
Only the copilot.exe binary was swapped between runs:
| Run |
Build |
opened the MCP OAuth authorization page |
401 challenge |
| 15:29 |
Aug 14 (4F590F1F…) |
0 |
0 |
| 15:32 |
Sep 29 (155778DD…) |
1 |
2 |
Because the token was unchanged and still valid across both runs, token expiry is
ruled out as the cause; the binary is the only variable.
Impact
Every new ACP session pops a browser window the operator did not ask for. For any
non-interactive ACP client (our case: a local web UI and scheduled agent runs)
this is disruptive, and on a headless host the flow has nobody to approve it.
Additional observation (may be intentional — noting for context)
In --acp mode the CLI discards the entire mcpServers list supplied by the
client:
[WARNING] [rust:acp::mcp_servers] Rejecting non-http/sse MCP server "<name>" from client
[WARNING] [rust:acp::mcp_servers] rejecting a client MCP server that conflicts with an agent-configured one {"server":"REDACTED-MCP"}
If client-supplied stdio servers are no longer supported over ACP, and
agent-configured entries always win on name conflict, documenting that would help
ACP client authors — right now a client can pass a server list, get no error back
from session/new, and have it silently ignored.
Affected version
No response
Steps to reproduce the behavior
- Configure an OAuth-protected HTTP MCP server in
~/.copilot/mcp-config.json:
https://mcp.internal.example.com/servers/REDACTED-MCP/mcp
- Complete its OAuth flow once. Confirm
~/.copilot/mcp-oauth-config now holds a
registration with "isStatic": false and a paired *.tokens.json whose keys are
exactly accessToken, expiresAt, scope — i.e. no refreshToken, and
expiresAt comfortably in the future (mine was ~3 days out).
- Start
copilot --acp and drive initialize → authenticate → session/new,
passing that server in mcpServers.
- Observe: the authorization page opens even though the token is still valid.
- Repeat step 3. It opens again, every time.
Expected behavior
With a valid unexpired access token on disk, session/new should connect the MCP
server using that token and open nothing. If the server genuinely rejects the
token with 401, the CLI should surface an actionable error rather than silently
launching a browser authorization flow on every session — that behaviour is
unusable for headless or unattended ACP clients.
Additional context
No response
Describe the bug
An OAuth-protected HTTP MCP server that has a valid, unexpired access token in
~/.copilot/mcp-oauth-configis answered with HTTP 401 on every new--acpsession, and the CLI responds by opening the OAuth authorization page again.
Because the browser SSO session is already established, the flow self-approves
and the tab closes — so the user sees an unrequested sign-in page flash open on
every single session start, with no way to suppress it.
This is a regression. An older build of the CLI connects the same server,
with the same token, without any 401 and without opening any page.
Critically, the stored token has no refresh token (the tokens file contains
only
accessToken,expiresAt,scope), so there is no silent renewal path —the CLI appears to fall straight through to the interactive flow rather than
using the access token it already holds.
Affected version
All affected and unaffected builds self-report
GitHub Copilot CLI 1.0.89, sothe version string cannot distinguish them. Please identify builds by hash:
4F590F1F60F3F2BDE2160809894BA950155778DD92A2171Acopilot.exewas auto-replaced on Sep 23 and again on Sep 29; both new builds keptthe
1.0.89version string. Please confirm whether multiple binaries shipped underthat version.
copilot --acp(ACP server), 4 ×--plugin-dir2025-11-25Actual behavior — CLI log
From
~/.copilot/logs/process-*.logon the Sep 29 build (hostnames redacted):Possibly the same root cause — the new discovery call is rejected for lack of a
bearer token, then falls back:
A/B isolating the build
Same machine, same 3-minute window, same on-disk token (issued earlier that day,
expiresAt3 days out), identical client invocation and identical MCP server list.Only the
copilot.exebinary was swapped between runs:opened the MCP OAuth authorization page401 challenge4F590F1F…)155778DD…)Because the token was unchanged and still valid across both runs, token expiry is
ruled out as the cause; the binary is the only variable.
Impact
Every new ACP session pops a browser window the operator did not ask for. For any
non-interactive ACP client (our case: a local web UI and scheduled agent runs)
this is disruptive, and on a headless host the flow has nobody to approve it.
Additional observation (may be intentional — noting for context)
In
--acpmode the CLI discards the entiremcpServerslist supplied by theclient:
If client-supplied stdio servers are no longer supported over ACP, and
agent-configured entries always win on name conflict, documenting that would help
ACP client authors — right now a client can pass a server list, get no error back
from
session/new, and have it silently ignored.Affected version
No response
Steps to reproduce the behavior
~/.copilot/mcp-config.json:https://mcp.internal.example.com/servers/REDACTED-MCP/mcp~/.copilot/mcp-oauth-confignow holds aregistration with
"isStatic": falseand a paired*.tokens.jsonwhose keys areexactly
accessToken,expiresAt,scope— i.e. norefreshToken, andexpiresAtcomfortably in the future (mine was ~3 days out).copilot --acpand driveinitialize→authenticate→session/new,passing that server in
mcpServers.Expected behavior
With a valid unexpired access token on disk,
session/newshould connect the MCPserver using that token and open nothing. If the server genuinely rejects the
token with 401, the CLI should surface an actionable error rather than silently
launching a browser authorization flow on every session — that behaviour is
unusable for headless or unattended ACP clients.
Additional context
No response