Skip to content

fix(server): Claude threads no longer inherit the launching Claude session or race MCP connects - #25976

Open
juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-25959
Open

juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-25959

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

Claude threads fail to share a prompt cache across new threads, for two reasons.

  • When T3 is started from inside a Claude Code session, every claude it spawns inherits that session's environment: CLAUDECODE, CLAUDE_CODE_ENTRYPOINT=claude-desktop, the session id, the messaging socket and token, CLAUDE_EFFORT, CLAUDE_PID, and so on. The spawned process runs as a child of a session it does not belong to.
  • Claude Code connects MCP servers without blocking by default. A server that is still connecting when a turn's first request goes out is left out of that request's tool list and added on a later one. Each change rewrites the whole ~52k-token cached prefix, and new threads whose first tool list differs each write a fresh one.

Reproduced on main e9236274a3 with the dev server launched under a fake host session's variables, a Claude instance whose binaryPath logs its env, and a user-scope stdio MCP server that connects after 0.5 to 4 s. Every probe and turn received CLAUDE_CODE_ENTRYPOINT=claude-desktop and the host session's variables. Of four new threads, one went out with the slow server's tools and wrote 52,691 tokens of fresh cache. In the others the tools only arrived on the second request, and that request rewrote the prefix.

Change

  • mergeClaudeProcessEnvironment in ClaudeHome.ts removes a fixed list of Claude Code host-session markers from the host environment, then merges the instance's own environment as before. A value set on the instance still wins.
  • The adapter (turns) and the driver (version and capability probes, text generation) build their env with it. The driver now reads HostProcess.Environment like the adapter does, instead of process.env.
  • Turns default MCP_CONNECTION_NONBLOCKING to false unless the host or instance env sets it. The first request then waits for MCP servers to connect, so every new thread sends the same tool list.
  • User configuration such as CLAUDE_CONFIG_DIR, ANTHROPIC_*, proxy and telemetry variables still passes through.

Scope and approval

Fixes #25959

Verification

  • ClaudeAdapterV2.test.ts, "ClaudeAdapterV2 turn environment":
    • does not leak a parent Claude Code session into the spawned claude fails on main with CLAUDECODE: "1", CLAUDE_CODE_ENTRYPOINT: "claude-desktop", the session id, socket and effort all present. It passes with the fix, and it fails again if CLAUDE_CODE_ENTRYPOINT is removed from the scrub list.
    • connects MCP servers before the first request unless the instance opts out fails on main with [undefined, "true"]. It passes with the fix.
  • ClaudeAdapterV2.test.ts, ClaudeHome.test.ts and ClaudeTextGeneration.test.ts: 258 passed. vp run check:changed passes.
  • Runtime transcript: real dev server and real Claude Code 2.1.303 (Sonnet 5.5), the same prompt in each new thread, a fresh database on each side.
Before, main e923627 After, f072531
Spawned turn env CLAUDECODE=1, CLAUDE_CODE_ENTRYPOINT=claude-desktop, host CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_MESSAGING_SOCKET/TOKEN, CLAUDE_EFFORT, CLAUDE_PID, CLAUDE_CODE_CHILD_SESSION CLAUDE_CODE_ENTRYPOINT=sdk-ts (set by the SDK), MCP_CONNECTION_NONBLOCKING=false, no host-session variables in any of the 7 spawns
First-request tool list, jittered slow MCP server varies per thread: 91 tools without the slow server, or 98 with it the same 98 tools in all 5 threads
Cache writes on new threads B1 52,691 fresh; B2 3,652 rewritten on request 2; thread A1 rewrote 53,232 on request 2 when the server connected D1 52,274 once; D2 to D5 read it (0 written on request 1)

Transcripts: before-main-e9236274a3.txt, after-fix-f072531c75.txt. Scripts: claude-wrap (env-logging binaryPath), slow-mcp.mjs (stdio MCP server with a jittered initialize), shapes.py (reads request shapes and cache usage from the transcripts).

  • Not checked: the macOS open -a launch from a Claude Desktop session, and claude.ai connectors. The run used a user-scope stdio server through the hub API on Linux; the code path does not depend on the platform.

Model and harness: Claude Opus 5.5 in Claude Code, run as a T3 Code issue lead, with Claude Opus 5.5 and GPT-6.1 Sol step agents.

🤖 Generated with Claude Code

juliusmarminge and others added 3 commits October 27, 2026 13:38
…Code session env

These tests reproduce #25959. The env createClaudeAdapterV2 passes to the
SDK query still carries a parent Claude Code session's variables
(CLAUDECODE, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_SESSION_ID,
CLAUDE_CODE_MESSAGING_SOCKET, CLAUDE_EFFORT), and never sets
MCP_CONNECTION_NONBLOCKING=false, so MCP tools can miss a turn's first request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat launched T3

Every Claude spawn path (turns, capability and version probes, text
generation) strips the host Claude Code session markers before the
instance environment is merged, so a value set on the instance still
wins. Turns default MCP_CONNECTION_NONBLOCKING to false so new threads
send a stable tool list on their first request and share one prompt
cache.

Fixes #25959

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…only overridden

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 27, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This focused server fix sanitizes inherited Claude session variables and changes the default MCP connection behavior for Claude turns, affecting existing production request paths. Because it changes a product default, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB +6 B (+0.1%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.3 KiB +6 B (+0.5%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.6 KiB 21.6 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB −29 B (−0.6%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.3 KiB 1.2 KiB −29 B (−2.3%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB −41 B (−0.2%) 24.4 KiB ✅
Claude Live turn messages 2 1 −1 (−50.0%) 8 ✅

Baseline: e923627 · PR result: 134bf64 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between e923627 and 134bf64.

Files selected for processing: 4
  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
  • apps/server/src/provider/Drivers/ClaudeDriver.ts
  • apps/server/src/provider/Drivers/ClaudeHome.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Walkthrough

Claude environment construction now removes inherited Claude session variables before merging provider-instance values. The adapter and driver use this filtered environment. The adapter also defaults MCP_CONNECTION_NONBLOCKING to "false". Tests check filtering, retained host values, instance overrides, and the default.

Changes: Claude environment handling

Layer / File(s) Summary
Filter and merge Claude environment
apps/server/src/provider/Drivers/ClaudeHome.ts
Adds a helper that removes selected Claude session variables from a copy of the host environment, then merges provider-instance values.
Use filtered environment in Claude execution
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, apps/server/src/provider/Drivers/ClaudeDriver.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
The adapter and driver use the Claude-specific environment helper. The adapter sets MCP_CONNECTION_NONBLOCKING to "false" before merging, so an instance value can override it. Tests check filtering, retained host values, instance overrides, and the default.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested labels: bugfix

Suggested reviewers: maria-rcks, saphid

Merge Risk: Minimal · up to 134bf

The environment isolation change is ready to merge after normal checks; no material issue remains established.

Pre-merge checks: Passed 4
Check name Status Explanation
Description check Passed The description includes all required sections. It explains the problem, change, scope approval, focused verification, observed results, limitations, and agent details.
Title check Passed The title is concise, specific, and accurately summarizes both primary fixes: preventing inherited Claude sessions and avoiding MCP connection races.
Linked Issues check Passed The PR addresses the active direct issue #25959. mergeClaudeProcessEnvironment removes inherited Claude Code session markers and then merges the instance environment, so instance values remain effec…
Out of Scope Changes check Passed The diff stays within issue #25959. It changes Claude environment construction, MCP connection timing, and focused automated tests for those behaviors. No unrelated product behavior or unrelated files…
Full details: Linked Issues check

Explanation

The PR addresses the active direct issue #25959. mergeClaudeProcessEnvironment removes inherited Claude Code session markers and then merges the instance environment, so instance values remain effective. The adapter and driver use this environment for turns, probes, and text generation. Turns set MCP_CONNECTION_NONBLOCKING to false by default while preserving an instance or host override. Added tests cover session-variable removal, retained user environment, instance entrypoint override, and MCP opt-out. The reported validation passes.

Full details: Out of Scope Changes check

Explanation

The diff stays within issue #25959. It changes Claude environment construction, MCP connection timing, and focused automated tests for those behaviors. No unrelated product behavior or unrelated files are changed.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

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

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Claude threads rewrite the prompt cache on every new thread (inherited host session env, late MCP connections)

1 participant