anthropics / anthropics/claude-agent-sdk-typescript
Background subagent loses canUseTool control channel + tool hooks when the parent turn ends → MCP calls fail "Error: Stream closed"
- 主要言語
- Shell
- スター
- 1.8k
- フォーク
- 226
- PR マージ指標
- 30日以内にマージされた PR はありません
説明
## Summary
When a subagent is spawned via the `Task`/`Agent` tool with `run_in_background: true` and the **parent turn then ends**, the background subagent loses the control channel that serves `canUseTool` (permission) and the tool hooks. From that moment every MCP tool call the subagent makes fails with:
```
Tool permission request failed: Error: Stream closed
```
Built-in tools (e.g. `Bash`) are unaffected — they don't round-trip through the permission control channel. `PostToolUse` / `PostToolUseFailure` / `SubagentStop` hooks also stop firing for the orphaned subagent.
## Environment
- `@anthropic-ai/claude-agent-sdk` **0.3.197**, Node 24
- Streaming input mode, `permissionMode: "default"` with a `canUseTool` callback (used for dynamic per-tool authorization)
- MCP servers merged via `options.mcpServers`
## Repro
1. Parent agent calls the `Task` tool with `run_in_background: true`.
2. The SDK returns `{ isAsync: true, status: "async_launched", agentId: ... }` and instructs the parent to *"briefly tell the user what you launched and end your response."*
3. Parent ends its turn.
4. Subagent continues: any MCP tool call → `Tool permission request failed: Error: Stream closed`. `Bash` still works.
Minimal subagent script that demonstrates it (interleaves an MCP call with a shell sleep):
```
1) call any MCP tool -> ok while parent turn is still open
2) Bash: sleep 25 -> ok
3) call the same MCP tool -> "Tool permission request failed: Error: Stream closed" (parent turn has now ended)
4) Bash: sleep 25 -> ok
5) call the same MCP tool -> Error: Stream closed
```
## Expected
A backgrounded subagent that outlives the parent turn should keep functioning: its `canUseTool` permission checks and tool hooks should continue to be serviced while the query/session is alive (the session stays alive and still delivers the completion `task_notification`).
## Actual
The `canUseTool` control channel and the tool hooks are scoped to the **parent's active turn** and are torn down when that turn ends — even though the query remains alive and later delivers the `task_notification`. The subagent runs "blind": all MCP calls fail, no hooks fire, and it typically spins/retries until killed.
## Evidence it's turn-scoped (not query-scoped)
If the parent keeps its turn **open** (e.g. it runs a long `Bash sleep` instead of ending the turn), the very same backgrounded subagent's MCP calls all succeed and its hooks fire normally. The only variable is whether the parent turn has ended.
## Impact
Background subagents that do real MCP work (the main use case) silently produce nothing when the parent ends its turn — while the run is still recorded as a normal successful completion. `canUseTool` is the standard mechanism for dynamic authorization, so this affects any host relying on it (not just static allowlists).
## Ask
Keep the `canUseTool` control channel (and the tool hooks) alive for in-flight background/paused sessions — the "session paused waiting for background work" state that `background_tasks` on the Stop/SubagentStop hook inputs already models — or provide a documented way to resolve permissions for background tasks after the launching turn ends.
## Workaround (for others hitting this)
Block the parent's `Stop` hook while `background_tasks` is non-empty (feed the model a short "wait" instruction) so the parent turn — and thus the control channel — stays open until the background task settles. Bounded with a max-wait + block-count guard.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
評価
この issue はまだ評価されていません。