# Useful but Little-Known Features of Claude Agent SDK
🌍 What if your agent tries to delete files on its own? Runtime approval gives you safe control!
The `canUseTool` callback lets you approve, deny, or modify tool inputs in real time before execution.
📌 Title: Runtime Approval via canUseTool Callback
🔗 URL:
🧩 Overview
`canUseTool` is a callback function passed in query options, fired at the last step (6th) of the permission evaluation flow. It never fires for tool calls auto-approved at an earlier step — by an allow rule or a mode like `acceptEdits` or `bypassPermissions` — so use a `PreToolUse` hook for logic that must apply to every tool call. The callback receives the tool name and input parameters. You return `allow` (with original or modified input) or `deny` (with an explanatory message). This enables dynamic control — sandboxing paths, blocking dangerous commands, or requesting user confirmation through your UI. It is skipped in `dontAsk` mode.
🛠 How to Use
```python
# Python - Tool approval callback
from claude_agent_sdk import ClaudeAgentOptions, query
from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny, HookMatcher
async def can_use_tool(tool_name, input_data, context):
print(f"\nTool: {tool_name}")
if tool_name == "Bash":
print(f"Command: {input_data.get('command')}")
# Block rm commands and suggest archiving
if "rm" in input_data.get("command", ""):
return PermissionResultDeny(
message="Please archive instead of deleting"
)
response = input("Allow this action? (y/n): ")
if response.lower() == "y":
return PermissionResultAllow(updated_input=input_data)
return PermissionResultDeny(message="User denied this action")
# Dummy hook (required for Python streaming mode)
async def dummy_hook(input_data, tool_use_id, context):
return {"continue_": True}
```
```typescript
// TypeScript - Modify input before allowing
import { query } from "
@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Clean up temporary files",
options: {
canUseTool: async (toolName, input) => {
if (toolName === "Bash") {
// Rewrite paths to sandbox
const sandboxed = { ...input, command: input.command.replace("/tmp", "/tmp/sandbox") };
return { behavior: "allow", updatedInput: sandboxed };
}
return { behavior: "allow", updatedInput: input };
}
}
})) { /* ... */ }
```
🏗 Integration into Production Systems
- Display tool approval dialogs in web UIs and return user decisions to the callback
- Use `context.suggestions` to retrieve `PermissionUpdate` entries for persistent "always allow" rules
- Auto-block dangerous operations (`rm`, `DROP TABLE`, etc.) and suggest safe alternatives in the deny message
- Sanitize inputs (restrict paths, override parameters) to dynamically control agent scope
💡 Use Cases
🔒 Require approval for write commands to production databases
📁 Restrict file operations to specific directories via dynamic path rewriting
👥 Control tool access based on user permission levels in multi-user environments
⚠️ Caveats
- In Python, `canUseTool` requires streaming mode and a `PreToolUse` hook configuration
- In `dontAsk` mode, the callback is skipped; use hooks for security-critical restrictions
- The callback pauses execution indefinitely until a response is returned; use the `defer` hook decision for long-running cases
✨ It's not "full automation" or "full manual" — canUseTool enables smart operation where humans decide only at critical points!
#
ClaudeAgentSDK# #
AIAgent#