Skip to content

MCP tools fail with 'unknown plugin conversation' after Claude Code --resume (shim holds a stale session id) #656

Closed

Description

@ranxueqing

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.js MCP 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-context for the launcher/proxy, D:\Users\...\AppData\Local\nvm\v24.11.0\node_modules\billion-context for 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

  1. Start a session via bili claude, make some tool calls (compress) — they work; the conversation id gets registered from model requests.
  2. Close the CLI, then resume the session (claude --resume / continue).
  3. After resume, any bili MCP tool call returns the 404 above, forever. Model traffic is unaffected (tags still injected, processTurn still logged).

Not a duplicate of existing issues (searched before filing)

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 …, and initialize only 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 in src/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.json contains 4 conversation ids and all of them answer ok:true on /__bili/plugin/status?conversationId=…, including the currently active one (135 requests, lastSeen = now);
    • the active conversation keeps getting processTurn log 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 in bili.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

  1. 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 — the fallbackLatest pattern 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.
  2. 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".
  3. 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.
  4. Minor DX: tool-endpoint 404s are not logged at all; a one-line debug log per /__bili/plugin/tool call (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

  1. ranxianglei commented on Sep 9, 2026

    @ranxianglei
    Owner

    ework 接单

  2. added a commit that references this issue on Sep 9, 2026
    a8a9f86
  3. added a commit that references this issue on Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions