[FEATURE] Pass CLAUDE_CODE_SESSION_ID to plugin MCP server subprocess env (stdio)
Plugin-bundled stdio MCP servers (declared via mcpServers in .claude-plugin/plugin.json) currently receive these env vars at spawn time:
CLAUDE_PLUGIN_ROOTCLAUDE_PLUGIN_DATACLAUDE_PROJECT_DIR
They do not receive CLAUDE_CODE_SESSION_ID, even though that variable already exists in the codebase and is propagated to Bash-tool subprocesses per #25642 (resolved in v2.1.132).
Why this matters
A plugin MCP server that wants to route messages per-session — for example, push events into a specific running session via a localhost sidecar — cannot reliably identify which session spawned it. The plugin sees its own pid, its plugin-data dir, the project dir, but has no way to correlate any of that with the host session whose lifecycle it shares.
Existing related discussion:
- #41836 — same gap for HTTP-transport MCP servers (Mcp-Session-Id header not echoed,
clientInfois anonymous). - #25642 — resolved by adding
CLAUDE_CODE_SESSION_IDto Bash-tool subprocess env only. - #44607 — closed as duplicate of #25642, no MCP angle.
- #56879 — documented
CLAUDE_CODE_SESSION_IDas Bash-tool-only scope. - modelcontextprotocol/modelcontextprotocol#231 — confirms MCP spec doesn't prescribe how a host conveys session identity to a stdio server; this is a host-implementation choice.
Why workarounds don't compose
Tried writing a sibling SessionStart hook that drops the current session_id into ${CLAUDE_PLUGIN_DATA}/active-session.json for the plugin server to read at startup. Fails for concurrent sessions — a Worker spawn races with a Conductor's startup; both hooks write to the same file; the second-spawning plugin server reads the first-spawning session's sid. Result: cross-session message routing failure.
PID-keying (writing active-session-by-pid/<parent-pid>.json) is the next-cleanest fallback but still depends on hook-vs-server-startup ordering and breaks on session-resume.
Proposed fix
Add CLAUDE_CODE_SESSION_ID: <current-session-id> to the env literal already built for plugin stdio MCP server spawn, next to the existing three plugin vars. Single-line addition.
Concrete dispatch-routing breakage this would fix
In a plugin I'm building (orchestrates Conductor/Worker Claude Code sessions via per-session MCP channels), the missing env var means:
- The Conductor's channel server can't reliably write a sidecar keyed on its own sid (races with Worker startup overwriting the shared hint file).
- A GUI trying to push
/handback <worker-sid>to the Conductor ends up POSTing to the Worker's channel server because the sidecar was written with the wrong sid. - The Worker reasonably refuses the misrouted message, and the dispatch loop stalls.
Happy to send a PR if helpful.
3 Comments
Analysis
The ask is straightforward: add
CLAUDE_CODE_SESSION_ID=<current-session-id>to the environment variables passed to plugin stdio MCP servers at spawn time, alongside the already-existingCLAUDE_PLUGIN_ROOT,CLAUDE_PLUGIN_DATA, andCLAUDE_PROJECT_DIR.Current state
| Env var | Bash tool | Plugin MCP stdio |
|---------|:---------:|:----------------:|
|
CLAUDE_CODE_SESSION_ID| Yes (v2.1.132, #25642) | No ||
CLAUDE_PLUGIN_ROOT| N/A | Yes ||
CLAUDE_PLUGIN_DATA| N/A | Yes ||
CLAUDE_PROJECT_DIR| N/A | Yes |The session ID is already tracked in the host process — it just isn't forwarded to the MCP server's environment block. This is a single-line addition in the env-var assembly code for plugin MCP server spawn.
Why it's needed
Plugin MCP servers that manage per-session state (routing, sidecars, channel servers) need a reliable session identifier. Current workarounds (hook files, PID keying) all race or break under concurrency and session-resume.
No user-side workaround
The env vars are set at spawn time in closed-source CLI code. There's no way for a user to inject
CLAUDE_CODE_SESSION_IDinto the MCP server environment today.Bot-closure was premature — empirically still not fixed in v2.1.152
This issue was auto-closed by
ant-github-proxy[bot]on 2026-05-27 as part of a 430-issue mass-closure sweep that day, with no maintainer comment and no linked code-PR (only docs PR #61754, which is still open and only documents the gap). Reopening with empirical evidence the env var is still missing in the latest public build (2.1.152).Primary-source probe (2026-05-27)
Modified
bd-cadence-channel(a plugin Bun stdio MCP server) to expose a debug endpoint dumping itsprocess.env. Ran two probe stages against Claude Code v2.1.152 (latest available viaclaude install latest):Stage 1 — env-inheritance check. Spawned a session via
wt.exe → claude.exewith--session-id 2b2a0d9c-873d-480a-8c08-4c0811a476f1. The/env-debugresponse showedCLAUDE_CODE_SESSION_ID: 50feefa3-86ca-4c87-81c4-12eb45463bdd— but that value was the launching shell's own session uuid, not the spawned session's. The env var was inherited from the parent shell, not injected byclaude.exe.Stage 2 — clean-env probe. Re-spawned with
CLAUDE_CODE_SESSION_IDstripped from the parent env before launch. Fresh sid87e5d950-a018-4a2b-b231-454ee20e84f9./env-debugreturned:CLAUDE_PLUGIN_ROOT/CLAUDE_PLUGIN_DATA/CLAUDE_PROJECT_DIRare injected as expected.CLAUDE_CODE_SESSION_IDis not.What the binary shows
String-grep of the 2.1.152 launcher at
~/.local/bin/claude.exe(PE32+ 235MB self-contained bundle) finds exactly four occurrences ofCLAUDE_CODE_SESSION_ID:L01used by Bash-command bare-assignment permission gating (unrelated to MCP spawn).Bq$used by the same gate.naH(H){let q={CLAUDECODE:"1",CLAUDE_CODE_SESSION_ID:H.sessionId};...}— shipped in v2.1.132 per #25642.process.env.CLAUDE_CODE_SESSION_ID = y8()self-assignment.The two plugin MCP spawn env-blocks visible in the binary are:
Neither references
CLAUDE_CODE_SESSION_ID. The one-line addition that would close this is shape-identical to what's already done for the Bash tool — read the session id from the same host-side handle, drop it into the env object.What the bot-closure actually was
ant-github-proxy[bot]closed 430 issues on 2026-05-27, breakdown{completed: 16, duplicate: 12, not_planned: 2}pergh api search/issues?q=repo:anthropics/claude-code+is:closed+closed:2026-05-27. The cohort spans unrelated issues (ConnectionRefused bugs, /context UX, voice-transcription quality). It's a mass-triage sweep, not a per-issue decision.state_reason: completedhere means "closed by automation," not "implemented."Why this still matters
Plugin MCP servers that manage per-session state (channel servers, sidecar routers, per-session caches) need a reliable session identifier. Workarounds we've tried — all eventually broken under concurrency or session-resume:
The pattern we landed in
bd-cadenceis an env-bridge: GUI setsBD_CADENCE_INSTANCE_ID=<uuid>on the spawn process env alongside--session-id <uuid>. claude.exe inherits and propagates, bun reads at startup. This works because the spawning GUI controls the env block — but it doesn't help any plugin that needs to handle sessions launched outside its own spawner (e.g. aclaude --resume <sid>started manually by the operator). The first-party env injection would.Filing a docs PR alongside this comment that adds the env-bridge pattern as a worked example under
examples/for plugin authors hitting the same wall today.Could a maintainer reopen and route this to whoever owns the env-assembly code path? Happy to test against any pre-release build that includes the fix.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.