[BUG] Project-scoped MCP servers stay bound to the original checkout after EnterWorktree (follow-up to #32220)
Preflight Checklist
- [x] I'm using the latest version of Claude Code (2.1.287).
- [x] I searched existing issues. This was reported as #32220, which the inactivity bot closed as not planned and then locked; its closing comment asks for a new issue that references it. Related closed issues: #16089, #25322, #51274, #81688.
What's Wrong?
After EnterWorktree, project-scoped MCP servers declared in .mcp.json stay bound to the instances of the checkout the session was launched from. Tools of a server that is specific to a checkout keep working against the original checkout, and they return wrong results, not errors.
Concrete case: calinburloiu/microtonalist, a Scala project whose dev stack (bin/mtlist-dev-stack, described in docs/agents/dev-stack.md) runs Metals, which serves MCP over HTTP on a port it picks at startup and records in the checkout's .mcp.json. The script resolves everything relative to its own checkout, so a second dev stack started inside the worktree gets its own Metals instance on a different port, recorded in the worktree's .mcp.json. The session's mcp__metals__* tools still call the main checkout's instance:
compile-modulecompiled the main checkout (seen in the main checkout's Metals log), not the worktree.glob-searchdidn't find a class that exists only in the worktree. The worktree's own instance, called directly over HTTP JSON-RPC, found it.- A stdio MCP server whose
.mcp.jsonentry runs a script by absolute path in the main checkout kept reading the main checkout's data, and returnedstatus: okwith the main checkout's numbers.
Subagents with isolation: "worktree" share the parent's MCP connections, so worktrees can't be used to parallelize MCP-dependent work within one session either. The agent can't recover by itself: /mcp is user-only, and it isn't documented which directory's .mcp.json it re-reads after EnterWorktree.
What Should Happen?
In order of preference:
EnterWorktreeandExitWorktreere-resolve project-scoped MCP servers from the new working directory's.mcp.json, reconnecting those whose configuration differs, and the MCP roots (roots/list) follow the worktree (as #32220 also asked for MCP server authors).- The agent can reconnect or reload project-scoped MCP servers against the current working directory, like
/mcpdoes for the user. - At minimum,
EnterWorktreewarns that project-scoped MCP servers still target the original checkout.
Error Messages/Logs
None. That is the problem: calls succeed against the wrong instance.
Steps to Reproduce
- In a git repository, run an HTTP MCP server for the checkout on port A and declare it in
.mcp.json:{"mcpServers":{"srv":{"type":"http","url":"http://localhost:A/mcp"}}}. Launchclaudethere. - Have the agent call
EnterWorktree. - In the worktree, start a second instance of the server on port B and write the worktree's
.mcp.jsonwith port B. - Have the agent call any
mcp__srv__*tool: it reaches port A, the original checkout's instance.
With the linked repository: launch claude in a checkout with bin/mtlist-dev-stack start running, have the agent call EnterWorktree and run bin/mtlist-dev-stack start in the worktree, then compare the metals port in the two .mcp.json files with the instance the mcp__metals__* tools reach (e.g. glob-search for a class added only in the worktree).
Claude Model
Opus
Is this a regression?
I don't know. It was already reported in #32220 (March 2026).
Claude Code Version
2.1.287 (Claude Code)
Operating System
macOS
Additional Information
A related problem in the same kind of session: a user-level PreToolUse hook that rewrites git … into another launcher (rtk git …, from the RTK token-saving proxy) gets every git command refused by the worktree-isolation guard. The guard also refuses any compound command that contains the word "git", even inside heredoc text. Calling /usr/bin/git directly works around it.
🤖 Generated with Claude Code