Repository navigation
Description
Describe the bug
In the GitHub Copilot Desktop app, remote MCP servers configured with "type": "http" that are non-first-party (e.g. Atlassian, incident.io) fail to authenticate. Toggling the server off/on in Settings produces no browser popup and no visible error — the server simply never connects.
Affected version
Desktop app shell: v1.0.15 (latest, confirmed via in-app update check)
Bundled Copilot CLI engine: v1.0.69-0 (AppData\Local\github-copilot-sdk\cli\1.0.69-0\copilot.exe)
Steps to reproduce
Add a non-first-party remote MCP server with "type": "http" and a url in mcp-config.json (e.g. https://mcp.atlassian.com/v1/mcp/authv2)
Enable it via Settings → MCP servers in the Desktop app
Toggle it off, then on again
Observe: no browser window ever opens; the server stays disconnected with no user-facing error
Expected behavior
A browser window should open for OAuth consent, as it does for the raw copilot CLI and for local-type servers wrapped in npx mcp-remote.
Actual behavior
~/.copilot/logs/github-app..log shows this single log line and nothing further:
github_app::session::manager::mcp_status: MCP OAuth non-first-party server; cancelling host-token so runtime browser flow runs server_name=
This line repeats on every toggle attempt (confirmed 43 occurrences across multiple sessions for both atlassian and incident.io), but there is no subsequent log entry anywhere indicating the "runtime browser flow" it defers to is ever actually invoked — no browser-launch attempt, no OAuth URL, no error surfaced to the user.
## Root cause hypothesis
This behavior appears tied to the MCP host token-injection OAuth rework shipped in copilot-cli v1.0.66 (2026-06-30 changelog entry: "Added runtime telemetry for the MCP host token-injection OAuth flow, recording when an OAuth broadcast is emitted to the host and how the host responds (token or cancelled)"). For non-first-party servers, the host correctly identifies it should defer to a client-side browser flow and cancels its own token injection — but that deferred browser flow is never triggered inside the Desktop app shell.
## Workaround
Reconfiguring the server as a local type wrapping npx mcp-remote bypasses the broken Desktop-app host/browser-flow handoff entirely, since mcp-remote runs its own independent OAuth implementation:
```json
"atlassian": {
"type": "local",
"command": "npx",
"args": ["--yes", "mcp-remote", "https://mcp.atlassian.com/v1/mcp"]
}
This works reliably. Native "type": "http" does not, for any non-first-party server, in the Desktop app.
Additional context
Possibly related to #2536, but distinct: that issue is about the raw copilot CLI's "Opening browser for X authentication..." log line firing and a browser genuinely opening, just re-registering (DCR) on every launch. In this Desktop-app case, the browser-flow deferral log line fires but no browser-launch attempt is ever logged afterward — a different failure point entirely (host→client handoff, not CLI OAuth caching).
Activity
m13v commentedon Jul 5, 2026
the config-level checks all pass here: type http, valid url, server toggles on. none of that surfaces the first-party/non-first-party classification the host applies at runtime, and that flag is the real gate. your server is eligible to register but not eligible for the host to hand off its token, so it cancels and defers to a browser flow the desktop shell never actually invokes. validating mcp-config.json will never catch it because the decision lives in the host handoff, not the config text.
gokhanarkan commentedon Jul 15, 2026
Triage summary
Runtime PR 11934 changed host cancellation from failed to needs-auth, and PR 11761 replatformed OAuth callbacks. Those changes do not prove an embedded app toggle launches the runtime browser flow.
Please verify direct CLI and embedded app separately on a containing build.
Route: needs-follow-up.
This triage summary was prepared with GitHub Copilot and reviewed before posting.
m13v commentedon Sep 7, 2026
needs-auth is the swallow. PR 11934 renaming host cancellation from failed to needs-auth is exactly what turns a loud, retryable failure into a quiet terminal state: failed surfaces or retries, needs-auth just parks on a browser flow the shell never fires. so the toggle reports settled with no error, which is the reported bug, not evidence the embedded flow launches.
patniko commentedon Oct 26, 2026
Thanks for the report. A fix that we believe addresses this has been merged and will ship in the next release (v1.0.104, currently in pre-release). Once it's out, could you retest and let us know whether you still see the problem? If we don't hear back after the release, we may close this issue — you can always reopen it.
Triaged as part of a Copilot SDK backlog review, with help from GitHub Copilot.
davidmatson commentedon Oct 28, 2026
Additional repro: same symptom with the Copilot app hosting sessions in WSL (app UI on Windows, sessions run by the app's WSL host).
Environment
copilotin the same WSL distro (1.0.105-1) behaves the same.https://mcp.dev.azure.com/<org>) and an internal Entra-protected Streamable-HTTP MCP server. Both advertise Entra through/.well-known/oauth-protected-resource.What happens
aebc6443-996d-45c2-90f0-388ff96faa56, redirecthttps://vscode.dev/redirect→http://127.0.0.1:<fixed port>) reports success. The callback reaches the WSL process (WSL localhost forwarding works), but no token is ever stored and the server stays at 401.--log-level debug, after/mcp auth <server>the CLI contactslogin.microsoftonline.comabout every 3 s until exit, with no success and no visible error./mcp authaborts the sign-ins that started automatically at launch ("OAuth flow was aborted"). Several servers share one auth server and one fixed callback port, which may be related to macOS: Entra-broker MCP servers fail at startup: concurrent sign-ins rejected ("sign-in is already in progress") and rejected cached token not recovered #5250.Evidence that the problem is in the client sign-in flow, not the server or network
az account get-access-token --resource <server resource>gives a token that both servers accept (initialize→ HTTP 200,tools/listandtools/callwork).Authorizationbearer header fromaz account get-access-token(refreshed near expiry). It's registered as alocalserver in~/.copilot/mcp-config.json. All servers then work in app sessions, and the CLI logs no further 401 challenges. Same approach as themcp-remoteworkaround above.Side note for anyone writing a similar proxy: Python's
urllibcapitalizes header names (x-custom-header→X-custom-header). Some servers match custom headers case-sensitively and silently ignore them. Usehttp.clientto send header names exactly as written.Ask: a non-broker Entra path that completes in WSL/Linux app hosts. Either finish the browser flow and persist the token, or allow a token-command /
headersHelper-style hook so a CLI credential such asazcan supply the bearer token.— Copilot (AI assistant using GitHub Copilot app)