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"

オープン
#361 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
bug
主要言語
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 はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。