[DOCS] Python SDK `can_use_tool` requires undocumented dummy hook workaround due to premature stream closure
Documentation Type
Unclear/confusing documentation
Documentation Location
- https://platform.claude.com/docs/en/agent-sdk/user-input (Handle tool approval requests) - https://platform.claude.com/docs/en/agent-sdk/python (ClaudeAgentOptions reference) -
examples/tool_permission_callback.py(SDK repository)
Section/Topic
- The "Handle tool approval requests" section in User Input guide 2. The
can_use_toolparameter documentation in Python SDK Reference 3. Theexamples/tool_permission_callback.pyexample file
Current Documentation
In the User Input guide (agent-sdk/user-input):
The documentation explicitly instructs users to implement a "Required workaround":
# Required workaround: dummy hook keeps the stream open for can_use_tool
async def dummy_hook(input_data, tool_use_id, context):
return {"continue_": True}
async def main():
async for message in query(
prompt=prompt_stream(),
options=ClaudeAgentOptions(
can_use_tool=can_use_tool,
hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
),
):
With the note:
"In Python,can_use_toolrequires streaming mode and aPreToolUsehook that returns{"continue_": True}to keep the stream open. Without this hook, the stream closes before the permission callback can be invoked."
In the Python SDK Reference (agent-sdk/python):
The can_use_tool parameter is documented simply as:
"Tool permission callback function. See Permission types for details"
No mention of the streaming requirement or dummy hook workaround.
In examples/tool_permission_callback.py:
The official example does NOT implement the "required workaround":
options = ClaudeAgentOptions(
can_use_tool=my_permission_callback,
permission_mode="default",
cwd="."
)
# No hooks defined
What's Wrong or Missing?
There are three interrelated problems:
Problem 1: SDK Logic Bug
In src/claude_agent_sdk/_internal/query.py, the stream_input method determines whether to keep stdin open based only on hooks or MCP servers:
# Current Logic
if self.sdk_mcp_servers or has_hooks:
# Wait for first result before closing...
It fails to check if can_use_tool is active. Because tool permissions require bidirectional communication (writing the permission decision back to stdin), the SDK prematurely closes stdin if can_use_tool is the only interactive feature. This causes CLIConnectionError when the callback attempts to return a decision.
Problem 2: Documentation Contradiction
The User Input guide says the dummy hook is "required," but the official example tool_permission_callback.py doesn't use it. Developers don't know whether to follow the documentation text or the provided code sample.
Problem 3: Missing from API Reference
The Python SDK Reference page doesn't mention the streaming requirement or dummy hook workaround at all. Developers relying solely on the Reference will encounter CLIConnectionError with no explanation.
Suggested Improvement
Option A: Fix the SDK (Preferred)
Update src/claude_agent_sdk/_internal/query.py to include can_use_tool in the condition:
# Proposed Logic
if self.sdk_mcp_servers or has_hooks or self.can_use_tool:
# Wait for first result before closing...
Then remove the "Required workaround" code blocks and notes from the documentation.
Option B: If SDK fix is deferred, harmonize documentation
- Update Python SDK Reference (
agent-sdk/python):
Add to the can_use_tool parameter description:
> Note: In the Python SDK, using can_use_tool requires streaming mode (passing an async iterable as the prompt). Additionally, if no other hooks are defined, you must register a dummy PreToolUse hook that returns {'continue_': True} to keep the connection open. See [Handle approvals and user input](/docs/en/agent-sdk/user-input) for details.
- Update
examples/tool_permission_callback.py:
Add the dummy hook to match the documentation:
```python
async def dummy_hook(input_data, tool_use_id, context):
return {"continue_": True}
options = ClaudeAgentOptions(
can_use_tool=my_permission_callback,
permission_mode="default",
hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
)
```
Impact
High - Prevents users from using a feature
Additional Context
- Root Cause Location:
src/claude_agent_sdk/_internal/query.py- thestream_inputmethod's condition for keeping stdin open - Failure Mode:
CLIConnectionError: ProcessTransport is not ready for writingwhen the callback attempts to return a permission decision to a closed stream - Impact: This creates significant friction for developers implementing permission flows, as they must discover the workaround through trial and error or by finding it buried in the User Input guide
- Related Files:
src/claude_agent_sdk/_internal/query.py(bug location)examples/tool_permission_callback.py(missing workaround)docs/en/agent-sdk/user-input.md(has workaround)docs/en/agent-sdk/python.md(missing workaround documentation)
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗