Skip to content

fix(core): fence runtime MCP tool reconciliation - #39898

Closed
kitlangton wants to merge 1 commit into
v2from
voice-codemode-repro
Closed

kitlangton wants to merge 1 commit into
v2from
voice-codemode-repro

Conversation

@kitlangton

Copy link
Copy Markdown
Contributor

What

Make runtime MCP lifecycle mutations read-your-writes consistent with the ToolRegistry and Code Mode catalog. After an MCP add, remove, connect, or disconnect request returns, the next session tool snapshot now reflects that mutation.

Before / After

Before: MCP.add connected the server, discovered its tools, published mcp.tools.changed, and returned. McpTool consumed that event on an asynchronously forked stream, so an immediate session request could snapshot the previous direct-tool or Code Mode catalog while reconciliation was still in flight.

After: runtime MCP handlers await an explicit McpTool.reconcile fence after mutating MCP state. The fence serializes behind any event-driven reconciliation and returns only after the complete current MCP tool set has replaced the prior registry scope.

How

  • packages/core/src/tool/mcp.ts exposes the existing serialized reconciliation effect as a Location-scoped service while retaining asynchronous reconciliation for server-originated tool-list updates.
  • packages/server/src/handlers/mcp.ts awaits that service after runtime add, remove, connect, and disconnect operations.
  • packages/core/test/mcp-tool-reconciliation.test.ts blocks event-driven reconciliation to prove publication can complete while a snapshot is stale, then verifies the explicit fence waits and makes the Code Mode path immediately visible.

Scope

This does not make all event-bus subscribers synchronous and does not change server-originated MCP list-change behavior. The stronger guarantee applies at the runtime MCP HTTP lifecycle boundary.

Testing

  • bun typecheck in packages/core
  • bun run test test/mcp.test.ts test/mcp-tool-reconciliation.test.ts in packages/core: 23 tests, 67 expectations
  • bun typecheck in packages/server
  • bun run test in packages/server: 16 tests, 39 expectations
  • Repository pre-push typecheck: 33 package tasks passed

Flow

sequenceDiagram
  participant Client
  participant MCP
  participant McpTool
  participant Registry as ToolRegistry / Code Mode

  Client->>MCP: add / remove / connect / disconnect
  MCP-->>McpTool: mcp.tools.changed (async)
  Client->>McpTool: reconcile fence
  McpTool->>MCP: read complete current tool set
  McpTool->>Registry: register replacement scope
  McpTool->>Registry: close previous scope
  McpTool-->>Client: mutation is observable
Loading

@kitlangton

Copy link
Copy Markdown
Contributor Author

Closing this implementation for now. The consistency gap and deterministic reproduction are tracked in #39902 so we can revisit the general runtime MCP contract separately. This was not the cause of the original voice tool failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant