From 83da71226be707e5408ab0e1d71705e53e5ec6be Mon Sep 17 00:00:00 2001 From: Aaron Zingerle Date: Wed, 11 Nov 2026 13:34:11 +0100 Subject: [PATCH] fix(server): Claude Plan mode runs t3_background_wait without asking Claude Plan mode blocks every MCP tool that does not declare readOnlyHint, and allow rules, session approvals included, do not lift that. Every t3_background_wait call in Plan therefore waited on an approval card. The tool only records which background tasks keep the thread Working, so annotate it read-only like t3_thread_wait. Fixes #32022 Co-Authored-By: Claude Opus 5.5 --- apps/server/src/mcp/toolkits/orchestrator/tools.ts | 6 +++++- .../src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts | 2 ++ .../server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/server/src/mcp/toolkits/orchestrator/tools.ts b/apps/server/src/mcp/toolkits/orchestrator/tools.ts index 2ba8a5f2dd5fc..d1c916f2caefb 100644 --- a/apps/server/src/mcp/toolkits/orchestrator/tools.ts +++ b/apps/server/src/mcp/toolkits/orchestrator/tools.ts @@ -154,7 +154,11 @@ const BackgroundWaitTool = Tool.make("t3_background_wait", { failure: OrchestratorMcpFailure, failureMode: "return", dependencies, -}).annotate(Tool.Title, "Wait on background work"); +}) + .annotate(Tool.Title, "Wait on background work") + // Only marks which tasks keep the thread Working; Claude Plan mode blocks tools without this. + .annotate(Tool.Readonly, true) + .annotate(Tool.Destructive, false); const ScheduleTaskTool = Tool.make("schedule_task", { description: diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 4550692f3e18a..9afc57fed1c36 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -610,6 +610,8 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { .map((tool) => `mcp__t3-code__${tool.name}`) .sort(); + // regression: #32022 Claude Plan mode asks before every non-read-only MCP call + assert.include(readOnlyToolNames, "mcp__t3-code__t3_background_wait"); assert.deepEqual( [...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS].sort(), readOnlyToolNames, diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 4a252d6092edb..4e9a315815c0d 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -1995,6 +1995,7 @@ export const CLAUDE_T3_MCP_TOOL_WILDCARD = "mcp__t3-code__*"; export const CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS: ReadonlyArray = [ "mcp__t3-code__orchestrator_capabilities", "mcp__t3-code__list_scheduled_tasks", + "mcp__t3-code__t3_background_wait", "mcp__t3-code__t3_thread_list", "mcp__t3-code__t3_thread_wait", "mcp__t3-code__t3_worktree_status",