Repository navigation
agentHost: send each MCP OAuth token only to its target server - #343026
Merged
Merged
Conversation
MCP servers that share an OAuth protected resource and scopes received the same token. The host gave the first token to all pending authentication requests for that resource, so a server could use the credential of a different account. This change uses the new optional AHP `authenticate.target` (microsoft/agent-host-protocol#535) to identify the server for each token. - Backport `AuthenticateTarget` and `AuthenticateParams.target` into the generated protocol copy. - Workbench: send `target` (host session URI and MCP customization ID) for each MCP token. Keep provider-level tokens untargeted. Send untargeted tokens when an older host gives no target. - Agent host: reject a target that does not identify an MCP server in an existing session. Keep targeted tokens in their own slots. Do not give them to agent-level resources or `getAuthToken`. Keep the target when tokens are replayed. - Copilot: give a targeted token only to the pending request of the target server in the owning session. - Codex: keep targeted tokens per session and server, and reconnect only that session. - Mission Control relay: do not use a targeted MCP token as the relay identity. - Copilot: show a server as ready after OAuth when its new connection loads tools. The runtime does not send a `connected` status change after an OAuth reconnect, so the server stayed in "starting". Fixes #342147 (Commit message generated by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The workbench used one dynamic authentication provider for each authorization server and resource, and ignored the OAuth client ID. MCP servers with different static clients for the same resource used one provider. A sign-in with a different client removed the provider and its stored tokens, so a sign-in to one server signed out the other. After a reload, silent authentication did not find a token for that server. - Agent host MCP authentication: give each static OAuth client its own provider ID (`<legacy ID> client:<client ID>`). Use the legacy provider when its stored registration already has the same client, so existing sign-ins continue to work. - Do not remove a provider registration when a request uses a different client. Replace a registration only for the same client, for example when its secret changes. - Add an optional provider ID to `createDynamicAuthenticationProvider`, the provider host delegate, and `$registerDynamicAuthProvider`. The extension host provider then keeps its registration and tokens under that ID. Providers without a static client keep their IDs. Fixes #342147 Fixes #293533 (Commit message generated by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Conflicts: # src/vs/platform/agentHost/test/node/agentService.test.ts
Connor Peet (connor4312)
enabled auto-merge (squash)
October 20, 2026 20:58
Contributor
📬 CODENOTIFYThe following users are being notified based on files changed in this PR: Ulugbek Abdullaev (@ulugbekna)Matched files:
Ben Villalobos (@benvillalobos)Matched files:
TylerLeonhardtMatched files:
|
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The protocol client's reconnect cache still merges distinct targets, losing independent credential replay and revocation state.
1 open issue
What changed in this PR
This PR isolates Agent Host MCP OAuth credentials by session, server, and static OAuth client to prevent unintended account sharing.
Changes:
- Carries authentication targets through workbench, protocol, and provider flows.
- Separates static-client registrations while preserving compatible legacy sign-ins.
- Updates OAuth readiness handling and adds isolation regression coverage.
| File | Description |
|---|---|
| src/vs/workbench/services/authentication/test/browser/authenticationQueryServiceMocks.ts | Updates mock authentication signature. |
| src/vs/workbench/services/authentication/common/authentication.ts | Adds optional provider IDs. |
| src/vs/workbench/services/authentication/browser/authenticationService.ts | Forwards explicit provider IDs. |
| src/vs/workbench/contrib/chat/test/browser/remoteAgentHost/remoteAgentHostChatContribution.test.ts | Tests targeted remote authentication. |
| src/vs/workbench/contrib/chat/test/browser/remoteAgentHost/cloudSandboxConnectionCustomization.test.ts | Tests target-preserving token sealing. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/stateToProgressAdapter.test.ts | Tests opaque authentication targets. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostCustomizationService.test.ts | Tests per-server target forwarding. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostClientTools.test.ts | Checks tool authentication targets. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostAuth.test.ts | Tests credential and provider isolation. |
| src/vs/workbench/contrib/chat/common/chatService/chatService.ts | Adds authentication target metadata. |
| src/vs/workbench/contrib/chat/browser/remoteAgentHost/remoteAgentHostChatContribution.ts | Forwards remote authentication targets. |
| src/vs/workbench/contrib/chat/browser/remoteAgentHost/cloudSandboxConnectionCustomization.ts | Preserves targets during sealing. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/stateToProgressAdapter.ts | Builds tool authentication targets. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts | Threads backend session targets. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostCustomizationService.ts | Targets customization authentication. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatContribution.ts | Forwards host-requested targets. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostAuth.ts | Partitions authentication and static-client providers. |
| src/vs/workbench/api/test/browser/mainThreadAuthentication.integrationTest.ts | Tests explicit provider persistence. |
| src/vs/workbench/api/test/browser/extHostAuthentication.test.ts | Tests overridden provider identity. |
| src/vs/workbench/api/test/browser/extHostAuthentication.integrationTest.ts | Adjusts provider constructor arguments. |
| src/vs/workbench/api/node/extHostAuthentication.ts | Passes explicit provider IDs. |
| src/vs/workbench/api/common/extHostAuthentication.ts | Supports custom provider identities. |
| src/vs/workbench/api/common/extHost.protocol.ts | Extends provider registration contract. |
| src/vs/workbench/api/browser/mainThreadAuthentication.ts | Uses scoped registration storage. |
| src/vs/sessions/services/agentHost/test/browser/agentHostCustomizationService.test.ts | Supplies backend session identities. |
| src/vs/sessions/services/agentHost/browser/agentHostCustomizationService.ts | Resolves customization owner sessions. |
| src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts | Exposes backend session resources. |
| src/vs/sessions/common/agentHostSessionsProvider.ts | Defines backend session accessor. |
| src/vs/platform/agentHost/test/node/missionControlAuthentication.test.ts | Tests relay identity separation. |
| src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts | Tests server isolation and OAuth readiness. |
| src/vs/platform/agentHost/test/node/copilotAgent.test.ts | Tests owning-session delivery. |
| src/vs/platform/agentHost/test/node/codex/codexMcpServers.test.ts | Tests per-server credential overrides. |
| src/vs/platform/agentHost/test/node/codex/codexAgent.test.ts | Tests targeted authentication and revocation. |
| src/vs/platform/agentHost/test/node/agentService.test.ts | Tests authentication target validation. |
| src/vs/platform/agentHost/test/node/agentHostClientConnectionService.test.ts | Includes targets in request coverage. |
| src/vs/platform/agentHost/test/node/agentHostAutomationService.test.ts | Checks automation authentication targets. |
| src/vs/platform/agentHost/test/node/agentHostAuthenticationScope.test.ts | Tests credential separation and replay. |
| src/vs/platform/agentHost/node/missionControl/missionControlAuthentication.ts | Excludes targeted relay identities. |
| src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts | Restricts delivery and tracks OAuth readiness. |
| src/vs/platform/agentHost/node/copilot/copilotAgent.ts | Routes tokens to owning sessions. |
| src/vs/platform/agentHost/node/codex/codexMcpServers.ts | Applies per-server token overrides. |
| src/vs/platform/agentHost/node/codex/codexAgent.ts | Stores session-specific server credentials. |
| src/vs/platform/agentHost/node/agentService.ts | Validates MCP authentication targets. |
| src/vs/platform/agentHost/node/agentHostAutomationService.ts | Targets automation authentication requests. |
| src/vs/platform/agentHost/node/agentHostAuthenticationService.ts | Separates targeted storage and replay. |
| src/vs/platform/agentHost/common/state/sessionProtocol.ts | Exports authentication target types. |
| src/vs/platform/agentHost/common/state/protocol/common/commands.ts | Defines optional protocol targets. |
| src/vs/platform/agentHost/common/agentHostExtensionProtocol.ts | Adds host-request authentication targets. |
| src/vs/platform/agentHost/common/agent.ts | Extends provider authentication parameters. |
Review effort: Balanced
The protocol client keeps each accepted `authenticate` request to replay it after a reconnect. The replay cache used only the resource and scopes as its key. Targeted MCP tokens for servers that share these values replaced each other and the provider credential, and a revocation for one server removed the cached credential of another server. - Add the authentication target (kind, session, and customization ID) to the cache key. Keep the key for untargeted credentials unchanged. - Add a test that keeps the provider credential and a targeted credential when a different target is revoked. Fixes #342147 (Commit message generated by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Martin Aeschlimann (aeschli)
approved these changes
Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

agentHost: send each MCP OAuth token only to its target server
MCP servers that share an OAuth protected resource and scopes received the same token. The host
gave the first token to all pending authentication requests for that resource, so a server could
use the credential of a different account. This change uses the new optional AHP
authenticate.target(microsoft/agent-host-protocol#535) to identify the server for each token.AuthenticateTargetandAuthenticateParams.targetinto the generated protocol copy.target(host session URI and MCP customization ID) for each MCP token. Keepprovider-level tokens untargeted. Send untargeted tokens when an older host gives no target.
targeted tokens in their own slots. Do not give them to agent-level resources or
getAuthToken.Keep the target when tokens are replayed.
session.
does not send a
connectedstatus change after an OAuth reconnect, so the server stayed in"starting".
agentHost: keep MCP OAuth sign-ins separate for each static client
The workbench used one dynamic authentication provider for each authorization server and
resource, and ignored the OAuth client ID. MCP servers with different static clients for the same
resource used one provider. A sign-in with a different client removed the provider and its stored
tokens, so a sign-in to one server signed out the other. After a reload, silent authentication
did not find a token for that server.
(
<legacy ID> client:<client ID>). Use the legacy provider when its stored registration alreadyhas the same client, so existing sign-ins continue to work.
registration only for the same client, for example when its secret changes.
createDynamicAuthenticationProvider, the provider hostdelegate, and
$registerDynamicAuthProvider. The extension host provider then keeps itsregistration and tokens under that ID. Providers without a static client keep their IDs.
Fixes #342147
Fixes #293533
How to test
https://mcp.slack.com/mcp) and differentoauthClientIdvalues, and sign in to each with a different account or workspace in the Agents window.Validation
npm run typecheck-client: passed (after the merge withmain).npm run eslinton all changed TypeScript files: passed../scripts/test.shfor the Agent Host platform tests, chat agent-session and remote agent host tests, authentication service tests, andextHostAuthentication.test.ts: passed, except failures that also occur onmain(3 Copilot Connector tests inagentHostCustomizationService.test.tsand 1 Mission Control projects test). OneCopilotAgentauth-notification test failed once in a large post-merge run and passed twice in isolation../scripts/test-integration.shforextHostAuthentication.integrationTest.tsandmainThreadAuthentication.integrationTest.ts: passed.(Commit message generated by Copilot)