Skip to content

agentHost: send each MCP OAuth token only to its target server - #343026

Merged
Connor Peet (connor4312) merged 4 commits into
mainfrom
connor4312/342147
Oct 20, 2026
Merged

Connor Peet (connor4312) merged 4 commits into
mainfrom
connor4312/342147

Conversation

@connor4312

Copy link
Copy Markdown
Member

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.

  • 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".

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.

  • 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

How to test

  1. Configure two HTTP MCP servers with the same URL (for example https://mcp.slack.com/mcp) and different oauthClientId values, and sign in to each with a different account or workspace in the Agents window.
  2. Confirm that each server reaches Ready shortly after its own sign-in, and that each server returns results only from its own account.
  3. Reload the window. Confirm that both servers authenticate silently without a prompt.

Validation

  • npm run typecheck-client: passed (after the merge with main).
  • npm run eslint on all changed TypeScript files: passed.
  • ./scripts/test.sh for the Agent Host platform tests, chat agent-session and remote agent host tests, authentication service tests, and extHostAuthentication.test.ts: passed, except failures that also occur on main (3 Copilot Connector tests in agentHostCustomizationService.test.ts and 1 Mission Control projects test). One CopilotAgent auth-notification test failed once in a large post-merge run and passed twice in isolation.
  • ./scripts/test-integration.sh for extHostAuthentication.integrationTest.ts and mainThreadAuthentication.integrationTest.ts: passed.
  • Manual: verified in Code - OSS with two Slack MCP servers on different workspaces, including a window reload.

(Commit message generated by Copilot)

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
Copilot AI balanced review requested due to automatic review settings October 20, 2026 20:58
@connor4312
Connor Peet (connor4312) enabled auto-merge (squash) October 20, 2026 20:58
@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

Ulugbek Abdullaev (@ulugbekna)

Matched files:

  • src/vs/platform/agentHost/node/agentHostAutomationService.ts
  • src/vs/platform/agentHost/test/node/agentHostAutomationService.test.ts

Ben Villalobos (@benvillalobos)

Matched files:

  • src/vs/platform/agentHost/node/agentHostAutomationService.ts
  • src/vs/platform/agentHost/test/node/agentHostAutomationService.test.ts

TylerLeonhardt

Matched files:

  • src/vs/workbench/api/browser/mainThreadAuthentication.ts
  • src/vs/workbench/api/common/extHostAuthentication.ts
  • src/vs/workbench/api/node/extHostAuthentication.ts
  • src/vs/workbench/services/authentication/browser/authenticationService.ts
  • src/vs/workbench/services/authentication/common/authentication.ts
  • src/vs/workbench/services/authentication/test/browser/authenticationQueryServiceMocks.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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>
@connor4312
Connor Peet (connor4312) merged commit 75e6bc2 into main Oct 20, 2026
49 checks passed
@connor4312
Connor Peet (connor4312) deleted the connor4312/342147 branch October 20, 2026 21:48
@vs-code-engineering vs-code-engineering Bot added this to the 1.144.0 milestone Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants