ahp mcp integration, maybe#315780
Draft
connor4312 wants to merge 10 commits into
Draft
Conversation
Wire the MCP-related protocol additions into the host's state machinery so later phases can surface MCP servers on AHP. Pure plumbing — no behavior change for clients today. - sessionActions: re-export McpAction/ClientMcpAction/ServerMcpAction and the six per-protocol action types; add isMcpAction guard - agentSubscription: new McpServerStateSubscription, StateComponents.Mcp, ClientMcpAction in dispatchOptimistic - agentHostStateManager: per-server slice (_mcpServerStates), snapshot routing for mcp:/ URIs, and helpers (createMcpServer, removeMcpServer, setMcpServerStatus, mcpMessageReceived, mcpMessageRemoved, getMcpServerState) - protocolServerHandler: route mcp/* actions by mcpServer URI; accept McpMessageResponded in dispatchAction; stub mcpMessage handler with MethodNotFound (real impl lands with IMcpHostService) - IAgentService.dispatchAction: admit ClientMcpAction; broaden remote and electron shims to match Tests cover the new reducer cases, subscription routing, state-manager helpers, and the dispatch/relevance paths.
Adds the platform-agnostic IMcpHostService interface that owns per-MCP-server
state and bridges AHP traffic to MCP transports, plus a null implementation
that is wired in everywhere as the default. No real upstream traffic yet —
that lands in Phase 4 with IMcpProxy.
- Define IMcpHostService, IMcpServerHandle, IMcpClientContext under
common/mcpHost/, with createDecorator('mcpHostService').
- Add NullMcpHostService that returns empty serverCapabilities, no handles,
and rejects mcpMessage with MethodNotFound.
- ProtocolServerHandler:
- Take IMcpHostService as a constructor parameter (positional, matching the
existing pattern).
- Capture client-advertised capabilities on IConnectedClient during
initialize, preserving them across reconnect when possible.
- Return capabilities: { mcp: serverCapabilities } in the InitializeResult.
- Replace the Phase 1d MethodNotFound stub with a real mcpMessage handler
that forwards to IMcpHostService.sendMessage with { clientId, capabilities }.
- AgentService:
- Optional 7th constructor arg defaulting to NullMcpHostService — keeps
existing call sites unchanged.
- dispatchAction(McpMessageResponded) now forwards to
IMcpHostService.deliverResponse instead of the Phase 1d trace TODO.
- subscribe() rejects unknown mcp:/ URIs early instead of falling through
to session-restore.
- Plumb a single shared NullMcpHostService instance through both
agentHostMain.ts entry points and agentHostServerMain.ts so AgentService and
ProtocolServerHandler always agree.
Tests: 5 new for NullMcpHostService, 4 new for ProtocolServerHandler
(initialize capabilities, mcpMessage forwarding, ProtocolError → JSON-RPC
error), 2 new for AgentService (deliverResponse forwarding, mcp:/
subscribe rejection). 151/151 in scope passing.
Introduces a self-contained MCP proxy under src/vs/platform/agentHost/
node/mcpHost/. Built and tested in isolation; not yet wired into the
agent host (phase 4). All files compile clean and 47 new tests pass
(40 unit + 7 integration).
Layout:
node/mcpHost/
mcpRpcEnvelope.ts JSON-RPC ↔ McpRpcMessage adapters
mcpInitializeInjector.ts Adds MCP Apps capability to client init
mcpAuthChallengeParser.ts WWW-Authenticate + RFC 9728 → AuthRequired
mcpUpstream.ts IMcpUpstream interface
mcpStdioStateHandler.ts Inlined from workbench/contrib/mcp (layer-
clean — platform cannot import workbench)
mcpStdioUpstream.ts Lazy-spawning stdio child process
mcpHttpUpstream.ts HTTP MCP server with auth handshake
mcpProxyHttpListener.ts Shared 127.0.0.1 http.Server with refcounted
/mcp/<uuid>/message routes
mcpProxyRoute.ts Per-server JSON-RPC bridge with the three
hooks from MCP_AHP_PLAN §3.6
mcpProxy.ts IMcpProxy + IMcpProxyFactory façade
Design notes:
* Loopback-only listener; random URL path is the access control.
* HTTP upstream sends a real `initialize` probe for discovery so
non-MCP-aware servers can't masquerade as 200. JSDoc notes the side
effect on session-bearing servers.
* Initialize injector returns a structurally fresh request — never
mutates the SDK's params, so retries are safe.
* 30-second SDK→upstream request timeout as a safety net; configurable
per route.
* No SSE channel for upstream→SDK push yet. Upstream notifications and
requests are recorded in AHP state via onUpstreamMessage but not
forwarded to the SDK process. Tracked for phase 5/6.
* No queue-and-replay during AuthRequired; phase 4 will drive that
through the host service lifecycle.
Tests:
* mcpRpcEnvelope.test.ts, mcpInitializeInjector.test.ts,
mcpAuthChallengeParser.test.ts — pure unit tests for the adapters.
* mcpStdioUpstream.test.ts — fake child process via PassThrough streams.
* mcpHttpUpstream.test.ts — fake fetch covering 200 / 401 (no token /
with prior token) / 403 insufficient_scope / network error / 401
without resource_metadata.
* mcpProxy.integrationTest.ts — real http.Server + stub IMcpUpstream;
exercises initialize injection round-trip, request/response, both
notification directions, deliverClientResponse pairing, auth retry,
and listener teardown.
Implements the `IMcpHostService` real implementation, plumbs it into the agent host main entrypoints, and makes `CopilotAgent` publish its plugin-defined MCP servers so they flow through host-managed proxies instead of going to the SDK as raw stdio/http config. * `IMcpHostService` (`McpHostServiceImpl`): per-(session, server) registry keyed by `mcp:/<sessionId>/<serverId>` (via the new `mcpServerUri` helper). Drives `IMcpProxy` lifetimes through `IMcpProxyFactory`, dispatches the `mcp/*` actions through `AgentHostStateManager`, gates `ui/*` methods on `capabilities.mcp.apps`, and routes `mcpMessage` calls. Notifications are emitted as `mcp/messageReceived` then immediately removed (sticky-notification support is deferred to phase 5). * `CopilotAgent` injects `@IMcpHostService` and publishes directly to it at four lifecycle moments: materialize, resume, plugin changes, and session disposal. A per-session "last-published" cache makes plugin changes a no-op when the effective server set is unchanged. `_buildSessionConfig` now awaits each handle's endpoint observable (5 s budget per server) before handing the SDK config off, so the SDK sees bound URLs on the very first connect; servers whose proxies don't bind in time are dropped and reattach on the next refresh. * `toSdkMcpServers` now takes an endpoint resolver and emits a uniform `type: 'http'` entry for every resolved server. The SDK no longer sees raw stdio/http config - it only sees the proxy. * `IAgentService.authenticate` short-circuits to `IMcpHostService.getServer(server).authenticate(...)` when the new optional `AuthenticateParams.server` field is set, leaving agent-level fan-out unchanged otherwise. The protocol handler converts the wire string into a `URI` before forwarding; the browser-side proxy client serializes the `URI` back to a string on send. * `agentHostMain` / `agentHostServerMain`: pre-construct `McpProxyFactory` + `McpHostServiceImpl` and inject the same instance into both `AgentService` (via the new `setMcpHostService` setter) and `ProtocolServerHandler`, replacing the phase-2 `NullMcpHostService`. Tests: `mcpHost/mcpHostServiceImpl.test.ts` (15), per-server `authenticate` cases on `AgentService`/`ProtocolServerHandler`, expanded `copilotPluginConverters` cases for the resolver contract, and `copilotAgent` cases asserting publish-on-materialize, no-op publish-on-unchanged-plugins, and clear-on-dispose. Existing 316 unit tests + 7 integration tests still green.
Phase 5 turns mcpMessage into a real, security-conscious gateway and adds end-to-end MCP Apps support. * Always inject the MCP Apps capability (`io.modelcontextprotocol/ui`) into the SDK's `initialize` request via McpAppsInitializeInjector. Servers that don't speak Apps simply ignore the extension entry. * Track the upstream's `initialize` result on IMcpUpstream (`upstreamCapabilities` observable + `setUpstreamCapabilities` setter). McpProxyRoute remembers SDK initialize ids and pushes the captured capabilities through when the upstream responds — this is what powers any future upstream-side gating. * Add an end-to-end integration test (`mcpAppsRoundTrip.integrationTest.ts`) that drives the full `ui/initialize` -> `ui/notifications/host-context-changed` -> upstream-originated `ui/open-link` request -> client `mcp/messageResponded` cycle through a real proxy + fake in-memory MCP App upstream. No production gaps surfaced. Tests: 14/14 McpHostServiceImpl unit + 8/8 McpProxy integration + 1/1 MCP Apps round-trip integration.
Closes out the MCP-in-Agent-Host plan (MCP_AHP_PLAN §6): - §6.3: TODO marker in mcpStdioUpstream pointing at the workbench's IMcpSandboxConfiguration path. v1 spawns unsandboxed, matching the existing workbench gateway behavior. - §6.4: protocolServerHandler test exercises the per-server URI snapshot fall-through under MCP load — dispatch 1500 mcp/messageReceived actions while a client is disconnected, then reconnect with a stale lastSeenServerSeq and assert result.type === 'snapshot' (not partial replay). - §6.5: new AGENTS.md under node/mcpHost/ summarizing the proxy architecture, AHP wiring, auth flow, lifecycle constraints, and known gaps (telemetry, sandbox parity, no SSE). Survives the planning docs. §6.1 (workbench UI) and §6.2 (telemetry — agent host has no ITelemetryService) remain out of scope and are tracked in the new AGENTS.md as known gaps.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds end-to-end MCP (Model Context Protocol) plumbing to the Agent Host Protocol (AHP), including per-session MCP server state, per-server subscriptions/messages, and UI affordances in chat/new-chat to surface and satisfy MCP authentication challenges.
Changes:
- Extend AHP state/actions/commands to model MCP servers (
mcpServerson sessions), per-servermcp:/…resources, andmcpMessageforwarding. - Implement node-side MCP proxy infrastructure (loopback HTTP routes, stdio upstream, auth challenge parsing, initialize capability injection) and wire it into the agent host.
- Add workbench-side MCP auth registries + indicators (chat execute toolbar + new-chat input), plus customization enumeration updates for MCP-only plugins.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/test/browser/agentSessions/resolveCustomizationRefs.test.ts | Adds coverage for MCP-only plugin customization inclusion/omission. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostMcpAuthRegistry.test.ts | New tests for MCP auth registry session entries + persisted scope memory. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostClientTools.test.ts | Stubs new agent-host registries for existing tool tests. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostChatContribution.test.ts | Stubs active-client registry for contribution tests. |
| src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts | Adds context key for “MCP auth required count”. |
| src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts | Hooks actionViewItemProvider to render custom MCP auth toolbar item. |
| src/vs/workbench/contrib/chat/browser/chat.contribution.ts | Registers MCP auth registries, indicator contribution, and actions. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/media/chatMcpAuthAction.css | Styles/pulse animation for chat MCP auth toolbar indicator. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/chatMcpAuthAction.ts | Adds MCP auth toolbar action + shared context menu builder. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostMcpAuthRegistry.ts | New workbench service: per-session registry + persisted scope memory. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostMcpAuthIndicatorContribution.ts | Binds session MCP auth-required count into widget context keys. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostLocalCustomizations.ts | Includes MCP-only/hook-only plugins in customization refs. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatContribution.ts | Adds MCP auth resolution hooks + customization refresh on MCP contributions. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostAuth.ts | Adds per-MCP-server silent/interactive auth helpers + candidate auth batching. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostActiveClientRegistry.ts | New workbench registry to snapshot active-client data for eager session creation. |
| src/vs/sessions/contrib/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts | Updates tests for new provider ctor deps (auth + MCP registries). |
| src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts | Wires auth + MCP registries into remote sessions provider base. |
| src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHost.contribution.ts | Adds MCP per-server auth flows + customization refresh on MCP contributions. |
| src/vs/sessions/contrib/chat/browser/newChatInput.ts | Adds new-chat MCP auth indicator widget. |
| src/vs/sessions/contrib/chat/browser/media/chatInput.css | Styles/pulse animation for new-chat MCP auth indicator. |
| src/vs/sessions/contrib/chat/browser/agentHost/newChatMcpAuthIndicator.ts | New-chat toolbar MCP auth indicator using shared context menu. |
| src/vs/sessions/contrib/agentHost/test/browser/localAgentHostSessionsProvider.test.ts | Updates tests for new provider ctor deps (auth + MCP registries). |
| src/vs/sessions/contrib/agentHost/browser/localAgentHostSessionsProvider.ts | Wires auth + MCP registries into local sessions provider base. |
| src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts | Adds provisional MCP-auth binding + active-client snapshot injection during eager create. |
| src/vs/platform/agentHost/test/node/reducers.test.ts | Adds reducer tests for session-level MCP server lifecycle actions. |
| src/vs/platform/agentHost/test/node/mcpHost/mcpRpcEnvelope.test.ts | New tests for JSON-RPC ↔ MCP envelope helpers. |
| src/vs/platform/agentHost/test/node/mcpHost/mcpInitializeInjector.test.ts | New tests for MCP initialize capability injector (apps extension). |
| src/vs/platform/agentHost/test/node/mcpHost/mcpAuthChallengeParser.test.ts | New tests for WWW-Authenticate parsing + AuthRequired status synthesis. |
| src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts | Updates MCP server config conversion tests to proxy-endpoint model. |
| src/vs/platform/agentHost/test/node/agentHostStateManager.test.ts | Adds state manager tests for MCP server slices + message bookkeeping. |
| src/vs/platform/agentHost/test/common/nullMcpHostService.test.ts | New tests for null MCP host service behavior. |
| src/vs/platform/agentHost/test/common/mcpServerUri.test.ts | New tests for mcp:/… URI building/parsing. |
| src/vs/platform/agentHost/test/common/agentSubscription.test.ts | Adds subscription coverage for per-MCP-server state resources. |
| src/vs/platform/agentHost/node/protocolServerHandler.ts | Adds MCP capabilities negotiation + mcpMessage command + MCP subscriptions. |
| src/vs/platform/agentHost/node/mcpHost/mcpUpstream.ts | Introduces upstream abstraction for MCP transports + capability tracking. |
| src/vs/platform/agentHost/node/mcpHost/mcpStdioUpstream.ts | Implements stdio MCP upstream (spawn + NDJSON + lifecycle). |
| src/vs/platform/agentHost/node/mcpHost/mcpStdioStateHandler.ts | Inlines stdio shutdown helper (layering-safe copy). |
| src/vs/platform/agentHost/node/mcpHost/mcpRpcEnvelope.ts | JSON-RPC ↔ MCP message adapters for the proxy layer. |
| src/vs/platform/agentHost/node/mcpHost/mcpProxyHttpListener.ts | Shared loopback HTTP listener with randomized per-server routes. |
| src/vs/platform/agentHost/node/mcpHost/mcpProxy.ts | Proxy factory + per-server proxy facade (auth + forwarding). |
| src/vs/platform/agentHost/node/mcpHost/mcpInitializeInjector.ts | Adds MCP Apps capability injection into initialize requests. |
| src/vs/platform/agentHost/node/mcpHost/mcpAuthChallengeParser.ts | Parses Bearer challenges and builds AuthRequired statuses. |
| src/vs/platform/agentHost/node/mcpHost/AGENTS.md | Architecture/behavior documentation for the node MCP host folder. |
| src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts | Switches SDK MCP config generation to proxy endpoints (skip unresolved). |
| src/vs/platform/agentHost/node/agentService.ts | Adds MCP host service routing, per-server authenticate, and response delivery. |
| src/vs/platform/agentHost/node/agentHostStateManager.ts | Adds per-MCP-server state slices, snapshots, and reducer wiring. |
| src/vs/platform/agentHost/node/agentHostServerMain.ts | Wires McpHostService + proxy factory into the standalone server main. |
| src/vs/platform/agentHost/node/agentHostMain.ts | Wires McpHostService + proxy factory into utility-process main + WS server. |
| src/vs/platform/agentHost/electron-browser/agentHostService.ts | Extends dispatchable action types to include client MCP actions. |
| src/vs/platform/agentHost/common/state/sessionState.ts | Adds StateComponents.Mcp mapping for MCP server state subscriptions. |
| src/vs/platform/agentHost/common/state/sessionActions.ts | Adds MCP action unions + isMcpAction guard. |
| src/vs/platform/agentHost/common/state/protocol/version/registry.ts | Versions MCP actions as introduced in protocol 0.2.0. |
| src/vs/platform/agentHost/common/state/protocol/state.ts | Adds MCP server summary/status/message types and session mcpServers. |
| src/vs/platform/agentHost/common/state/protocol/reducers.ts | Adds MCP server summary handling in sessionReducer + per-server reducer. |
| src/vs/platform/agentHost/common/state/protocol/messages.ts | Adds mcpMessage to the command map. |
| src/vs/platform/agentHost/common/state/protocol/commands.ts | Adds client/server MCP capabilities + per-server authenticate + mcpMessage. |
| src/vs/platform/agentHost/common/state/protocol/actions.ts | Adds MCP lifecycle + message actions to protocol action set. |
| src/vs/platform/agentHost/common/state/protocol/action-origin.generated.ts | Updates origin unions + dispatchability for MCP actions. |
| src/vs/platform/agentHost/common/state/protocol/.ahp-version | Updates protocol sync marker formatting. |
| src/vs/platform/agentHost/common/state/mcpServerUri.ts | Adds helpers to build/parse mcp:/<session>/<server> URIs. |
| src/vs/platform/agentHost/common/state/agentSubscription.ts | Adds MCP server subscriptions and optimistic dispatch typing updates. |
| src/vs/platform/agentHost/common/mcpHost/nullMcpHostService.ts | Adds no-op MCP host implementation for non-MCP surfaces/tests. |
| src/vs/platform/agentHost/common/mcpHost/mcpHostService.ts | Introduces MCP host service interface + per-server handle contract. |
| src/vs/platform/agentHost/common/agentService.ts | Extends authenticate/dispatch types to include per-server MCP auth/actions. |
| src/vs/platform/agentHost/browser/remoteAgentHostProtocolClient.ts | Extends dispatch typing and adds per-server authenticate param marshaling. |
| src/vs/base/common/jsonRpcProtocol.ts | Adds helpers to discriminate JSON-RPC success vs error responses. |
Copilot's findings
- Files reviewed: 79/80 changed files
- Comments generated: 1
Comment on lines
+222
to
+229
| private _readMemory(): IPersistedShape { | ||
| if (this._memoryCache) { | ||
| return this._memoryCache; | ||
| } | ||
| const raw = this._storageService.get(STORAGE_KEY, StorageScope.APPLICATION_SHARED); | ||
| if (!raw || Math.random() < 2) { | ||
| return this._memoryCache = {}; | ||
| } |
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.
This is the "1000ft view" document I used to create this PR.
MCP in the Agent Host
Challenges
Currently, MCP support in the agent host/harnesses faces a few uphill challenges:
Approach
Protocol
AHP should not try to encapsulate the entirety of MCP. The AHP protocol changes are:
InitializeParamssent over AHP.Some MCP functionality 'bleeds' into AHP when necessary:
uiResourcemay be associated with tool callsAdditionally, we will allow a host to expose an opaque
mcpMessagecommand for a client to send data back to a particular MCP server associated with a session. This is needed for MCP Apps which have a variety of extension methods they expose, and other explorations may be built-off this in the future.I also forsee that we could have a lightweight MCP URI as a subscribable resource in the AHP protocol. As a client, I subscribe to one of the MCP servers in my session. The resulting state is a fairly loosely typed object that contains pending method calls that have been issued from the server, such as calls to app-provided tools.
I think there will be some ongoing tension between what's official AHP versus what's not, but that is something we will just need to manage appropriately.
Agent Host Implementation
The Copilot SDK has basic support for tools, elicitation, and sampling. It does not pass through auth, or support apps. It could add support for these. Other SDKs are in largely similar states.
To work around this, we can proxy MCP servers in the agent host. For local extension host-driven Copilot CLI sessions, we run a basic proxy server, the purpose of which is to redirect/share authentication state with VS Code proper. This works pretty well.
We could reuse and expand this notion in the agent host. It would:
mcpMessageand track and allow us to restore theuiResourceused for MCP apps to tool calls we get back through the SDKIf/when SDK would provide support for these things, we just drop the backchannelling and use the SDK-provided mechanisms.
Important Implementation Notes
We have an existing notion of an "MCP Gateway". This is too much for what we're going for. We should strive for a lightweight, minimal proxy. It necessarily understands JSON RPC, but not much on top of that.
The lifecycle should generally be:
Customizations are announced to the agent host. This includes MCP servers. Then two branches:
(a) For stdio MCP servers: we want to proxy these too in order to do capability enhancements. We should expose an HTTP proxy server for them immediately. Once the copilot SDK tries to connect, then we can boot the underlying MCP server. If that fails, then we should fail the 'initialize' call. Otherwise, continue.
(b) For HTTP MCP servers: we should eagerly attempt to connect to them insofar as the minimal work needed to determine if they're going to need authentication. If so, we put them in the "AuthRequired" state. (They may also enter this state again for step-up authentication.) We do NOT expose them to the copilot SDK until auth has been satisfied and it's in the Running state. We can incorporate them into the IActiveClientSnapshot to know if we need to start them.
In general, the proxy server is just a blind pass-through of data. It MAY step in for a few cases:
...but overall it's just JSON RPC and it should reuse our existing transports.
Each proxy endpoint needs to be on a randomized path and specific for a harness session. That is, if there are two concurrent CopilotAgent sessions, each should get their own set of proxy endpoints.