Some checks failed
CI/CD / typecheck (pull_request) Successful in 1m13s
CI/CD / lint (pull_request) Successful in 2m30s
CI/CD / test (pull_request) Successful in 1m25s
CI/CD / build (pull_request) Successful in 2m28s
CI/CD / smoke (pull_request) Failing after 2m59s
CI/CD / publish (pull_request) Has been skipped
Switching projects twice left the agent believing it had lost MCP access entirely: it kept calling the previous project's tool names, got "Tool mc_<old>_begin_session not found", and concluded no MCP tools existed. Root cause is a pi constraint, not a bug in the switch. pi has no way to unregister a tool — `registerTool` only ever does `extension.tools.set(name)` — so the previous project's `mc_*` tools stay registered and merely go inactive. `setActiveTools` correctly drops them from the live set, but the conversation still contains the old project's tool listing, so the model keeps calling names that now answer "not found". Nothing was telling the model the tool set had changed. Now the switch injects a custom message naming the active project, stating that previously listed mcpctl tool names are dead, and listing what is actually callable. Custom messages are converted to user-role messages by `convertToLlm`, so they do reach the model (unlike `appendEntry`, which is explicitly excluded from context). `display: false` keeps it out of the transcript — the notification is what the human reads. The gate is also called out explicitly, in both the notification and the injected message. A freshly switched project gets a new mcp-session-id and is therefore gated again, so "1 tool(s) ready" is correct but reads like a failure; it now says which begin_session call unlocks the rest. `toolChangeAnnouncement` is exported and unit-tested rather than left as wording only reachable through a TUI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017BMXdb2qZbPSh8Q7XpTyjB