Skip to content

feat: push MCP server context and tools from agentcontext#26533

Open
kylecarbs wants to merge 1 commit into
mainfrom
kylecarbs/agentcontext-mcp-push
Open

feat: push MCP server context and tools from agentcontext#26533
kylecarbs wants to merge 1 commit into
mainfrom
kylecarbs/agentcontext-mcp-push

Conversation

@kylecarbs

@kylecarbs kylecarbs commented Jun 18, 2026

Copy link
Copy Markdown
Member

What

Live MCP servers and their tools now flow into the agentcontext snapshot and are pushed to coderd via PushContextState, stored alongside instruction files and skills. Previously the resolver's MCPProvider seam was unimplemented, so live MCP tool lists never reached the pushed snapshot.

agentcontext is now fully self-contained for MCP: it connects to the MCP servers declared in the .mcp.json files its own watcher already discovers, lists their tools, and emits KindMCPServer resources. It does not depend on or modify agent/x/agentmcp — that package is left pristine and keeps serving the agent's MCP HTTP API. The two MCP paths run independently, which means the legacy package can be deleted later without touching this code.

How

  • Self-contained runner (agentcontext/mcprunner.go): a one-shot MCP client (connect → initialize → list tools → close) with its own .mcp.json parser. A Manager goroutine (runMCPSync) reloads it whenever the discovered KindMCPConfig path:contenthash set changes, then re-resolves so the new tools are published. Per-server connects run in parallel (bounded) with a per-server timeout; a server that fails to connect is recorded as a failure rather than aborting the batch.
  • Provider (agentcontext/mcp.go): builds KindMCPServer resources from the runner's non-blocking per-server snapshot. Connected servers carry their sorted tools (StatusOK); failed servers surface as StatusUnreadable issues instead of vanishing; connected-but-no-tools-yet are skipped until a later reload. The content hash is tool-set sensitive.
  • Drift: MCP resources are excluded from the snapshot aggregate/drift hash (driftResources). MCP servers connect asynchronously after boot; without this, a server finishing its connect would dirty every hydrated chat even though nothing the user pinned changed.
  • Wiring (agent.go): the manager is given ManagerOptions.MCPExecer/MCPUpdateEnv; agent/x/agentmcp is untouched.
  • Config validation: a structurally broken .mcp.json now surfaces as StatusInvalid rather than silently dropping all its servers.

coderd already persists mcp_server/mcp_config resource bodies (including tools), so no coderd or proto changes were required.

Testing

  • Unit: buildMCPServerResources (grouping/sort/skip/failed/hash sensitivity), NewMCPProvider delegation, the resolver MCP option, MCP exclusion from the aggregate hash, .mcp.json parsing (transport inference, env expansion, reserved-name rejection), toolInputSchema, and mcpConfigSet change detection.
  • End-to-end (TestAgent_MCPServerToolsPushed): boots an agent with a .mcp.json pointing at a re-exec'd fake stdio MCP server and asserts the connected server's echo tool appears in a PushContextState snapshot (passes under -race).
  • go build ./..., go vet, and golangci-lint are clean on the touched packages.

Scope / follow-ups

This is the agent-side production+push half. The chatd consumer (reading the pinned MCP resources for prompt/tool injection) and removing the legacy workspaceMCPToolsCache pull path remain follow-ups, per the RFC rollout. While both agent/x/agentmcp and agentcontext exist, stdio MCP servers are spawned by both; this is intentional and temporary until agentmcp is removed.

Implementation plan and decisions

Goal: produce live MCP server resources (with tools) from agentcontext and push them to coderd.

Starting state (main): proto (PushContextState, MCPServerBody, MCPTool), the drpc adapter, coderd storage (workspace_agent_context_resources, body kind mcp_server), and the resolver's MCPProvider seam already existed; nothing implemented the seam or fed live tools into the snapshot.

Decision (agentcontext fully separate from agentmcp): agentcontext starts and lists its own MCP servers using only the connect-and-list half of an mcp-go client, driven by the .mcp.json files its existing watcher discovers. It shares no state with agent/x/agentmcp and does not import it. Two earlier revisions of this branch were discarded: (1) relocating agentmcp into agentcontext (rejected — it duplicates config parsing and file watching agentcontext already does); (2) reading agentmcp's cached server snapshot via new accessors (rejected — unnecessary coupling between two packages that should simply run independently while one is being retired). The temporary double-spawn of stdio servers is the accepted cost of keeping the two paths cleanly separated until agentmcp is removed.

Invariants held: no secrets (env/headers) in pushed resources, only server/tool metadata; MCP excluded from the drift hash; the provider is non-blocking so the resolver never stalls on MCP I/O.


This PR was created by Coder Agents on behalf of @kylecarbs.

agentcontext now starts its own MCP servers from the .mcp.json files its
watcher discovers, lists their tools, and surfaces them as KindMCPServer
resources pushed to coderd alongside instruction files and skills. This
runs independently of agent/x/agentmcp (which serves the agent's MCP HTTP
API); the two MCP paths share no state during the rollout, so removing
the old package later touches nothing here.

- agentcontext/mcprunner.go: self-contained one-shot MCP runner (connect,
  initialize, list tools, close) with its own .mcp.json parser. A Manager
  goroutine (runMCPSync) reloads it when the discovered KindMCPConfig
  path:contenthash set changes, then re-resolves to publish the tools.
- agentcontext/mcp.go: MCPProvider builds KindMCPServer resources from the
  runner's non-blocking per-server snapshot (connected->OK with tools,
  failed->unreadable issue, no-tools-yet skipped).
- agentcontext/resolve.go: MCP resources are excluded from the
  drift/aggregate hash so a server connecting does not dirty hydrated
  chats; structurally broken .mcp.json is flagged StatusInvalid.
- agent.go: wires the runner via ManagerOptions.MCPExecer/MCPUpdateEnv;
  agent/x/agentmcp is left untouched.
@kylecarbs kylecarbs force-pushed the kylecarbs/agentcontext-mcp-push branch from aa6c115 to 6409edf Compare June 18, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant