Repository navigation
MCP tools fail with 'unknown plugin conversation' after Claude Code --resume (shim holds a stale session id) #656
Description
Summary
After resuming a Claude Code session (claude --resume / in-app resume), every MCP tool call (compress, acp_status, …) permanently fails with:
bili tool error: unknown plugin conversation (no model request has arrived with this conversation id yet)
while the proxy keeps serving the same conversation normally — model requests still flow through it and ACP token tags keep being injected. The session is effectively left without compression tools until the CLI is fully restarted with a fresh session.
Environment
- Windows 11, billion-context 0.1.96 (npm global), launched via
bili claude(launcher → proxy on random port + Claude Code CLI →dist/mcp.jsMCP shim) - Upstream: a third-party Anthropic-compatible endpoint (base URL pointed at the proxy) — likely not relevant
- Note: on this machine two global install roots coexist (
D:\nvm4w\nodejs\node_modules\billion-contextfor the launcher/proxy,D:\Users\...\AppData\Local\nvm\v24.11.0\node_modules\billion-contextfor the MCP shim — nvm4w symlink vs per-version root). Both are 0.1.96 today, so this is not the cause here, but it shows the shim and proxy can silently come from different installs on Windows/nvm setups.
Repro
- Start a session via
bili claude, make some tool calls (compress) — they work; the conversation id gets registered from model requests. - Close the CLI, then resume the session (
claude --resume/ continue). - After resume, any
biliMCP tool call returns the 404 above, forever. Model traffic is unaffected (tags still injected,processTurnstill logged).
Not a duplicate of existing issues (searched before filing)
- Searched
unknown plugin conversation,CLAUDE_CODE_SESSION_ID, and the recent issue list:- 用bili omp 启动omp之后, /acp命令不能正确执行. #268 — same error family (
No model request in this conversation yet) but omp +/acp, different host and different root cause - minCompressRange: user docs specify tokens, kernel executes in chars (4× unit mismatch); note appended: registryLookup conflicts silently fall back. | minCompressRange 用户文档写 tokens、内核按 chars 执行(4× 单位缺口);附 registryLookup 冲突即静默回落的备注 #406 —
plugin-conversations.jsoncorruption path; persistence works fine here (all ids resolve) - Codex 长会话在 macOS 休眠唤醒后 Bili 未触发压缩,导致上下文窗口超限 #280 — Codex multi-session after macOS sleep; different host
- 用bili omp 启动omp之后, /acp命令不能正确执行. #268 — same error family (
- None covers "Claude Code resume leaves the MCP shim on a stale session id".
What I verified (source + live state)
src/mcp.ts:61-64— the shim captures the conversation id once at process start:let conversationId = process.env.CLAUDE_CODE_SESSION_ID …, andinitializeonly fills it if unset (src/mcp.ts:139-140, env wins over_meta.ui.sessionId).forwardTool(src/mcp.ts:100-115) then sends that captured id on every call.- Registration on the proxy side is keyed by the id derived from actual model requests (
src/server.ts:1342-1367→recordPluginSession, header priority insrc/session-id.ts:50). - Live state on the failing machine, while the error reproduces:
- exactly one proxy instance listening; instance-file routing resolves to it;
plugin-conversations.jsoncontains 4 conversation ids and all of them answerok:trueon/__bili/plugin/status?conversationId=…, including the currently active one (135 requests,lastSeen= now);- the active conversation keeps getting
processTurnlog entries and ACP tags on every message. - ⇒ the shim is sending an id that is not any registered one — a phantom id no model request will ever carry.
- Persistence of the conversation map works as designed (
src/plugin.ts:103-193), and dual-instance split-brain was ruled out (single listener; the 404s don't even show inbili.log).
Root cause
Claude Code changes its session id when a conversation is resumed (resume forks a new session UUID), after MCP children have already been spawned. The shim's CLAUDE_CODE_SESSION_ID therefore goes stale: model requests register/lookup under the new id, while every tool call still authenticates with the pre-resume id → permanent unknown plugin conversation.
Suggestions
- Shim-side fallback on 404: when a tool call fails with
unknown plugin conversation, re-register and/or fall back to the proxy's most-recent active conversation — thefallbackLatestpattern already exists for/status(src/plugin.ts:364-384, 重启伪造时间戳:buildSession 用 Date.now() 覆盖 lastSeen,面板"最后活动"全员开机时刻、fallback=latest 在 ~245 路平局里随机挂会话 #404). Could be opt-in (BILI_MCP_FALLBACK_LATEST=1) to stay safe for genuinely multi-session hosts. - Cheaper alternative: make the failure self-explanatory — on this specific 404, have the shim emit a one-time stderr hint: "session id no longer known to the proxy — restart the CLI so the MCP shim re-reads CLAUDE_CODE_SESSION_ID".
- Minor DX: the 404 message covers two different conditions (
src/plugin.ts:481-487: id never registered vs entry exists but session not resident). Splitting them would speed up diagnosis. - Minor DX: tool-endpoint 404s are not logged at all; a one-line debug log per
/__bili/plugin/toolcall (id + outcome) would have made this diagnosable without reading source.
Workaround
Restart the CLI session fresh (a newly spawned shim picks up the current session id). Resuming again re-breaks it.
中文说明
现象:Claude Code --resume 恢复会话后,所有 bili MCP 工具永久报 unknown plugin conversation (no model request has arrived with this conversation id yet),但代理仍在正常服务同一会话(模型请求照常经过、ACP token 标签照常注入)。
根因:shim 在进程启动时一次性捕获 CLAUDE_CODE_SESSION_ID(src/mcp.ts:61-64),之后不再刷新;而 Claude Code 恢复会话时会换一个新的 session id(resume 会 fork 新 UUID),模型请求按新 id 注册,工具调用却仍带旧 id → 永久 404。
已排除:对话映射持久化正常(plugin-conversations.json 可用)、双实例分裂(仅一个监听端口)、路由文件指向正确。现场验证:注册表内全部 conversation id 均可 ok:true 解析,活跃会话仍在持续打 processTurn 日志,但工具调用发来的是一个从未注册过的幽灵 id。
建议:工具 404 时回退到代理最近活跃会话(status 已有 fallbackLatest 先例,#404),或至少把该错误变成可自解释的提示(提示重启 CLI)。另附两个小改进:404 文案区分「id 未注册」与「session 不在内存」;tool 端点 404 打一行日志。
临时规避:全新启动 CLI(让 shim 重新读取 session id);再次 resume 会复现。
Activity
ranxianglei commentedon Sep 9, 2026
ework 接单
test(mcp): cover the identity-binding resume path for ranxianglei#656 …
fix(mcp): recover tool calls when the shim's conversation id goes sta…
fix(ranxianglei#4168): self-rebind the claude MCP binding when the ho…