17.5 Handling Permissions
Course: Claude Code - Enterprise Development
Section: Claude Agent SDK
Video Length: 3-4 minutes
Presenter: Daniel Treasure
Opening Hook
"Claude is powerful, but not all actions should be automatic. Writing files, executing code, accessing external systems—these need approval. The SDK gives you permission callbacks: Claude requests approval before acting. This is the heart of safe, human-in-the-loop automation."
Key Talking Points
What to say:
- "The SDK has permission modes—ways to control what actions Claude can take without explicit approval."
- "In CLI (4.7), you saw interactive mode prompts. In automation (14.3), you used dontAsk to skip prompts."
- "In the SDK, there's 'delegate' mode: Claude requests permission via a callback, you decide programmatically."
- "We'll show: restricted actions, permission callbacks, approve/deny logic, practical human-in-the-loop workflows."
What to show on screen:
- Agent attempting restricted action (write file)
- Permission callback triggered
- Approval dialog or decision logic
- Action allowed/denied based on callback response
- Audit log of permission decisions
Demo Plan
[00:00 - 01:00] Permission Modes Overview
1. Show permission mode options: default, accept_edits, plan, bypass_permissions, delegate, dont_ask
2. Explain each:
- default: prompt user interactively (slow for automation)
- delegate: use permission callback
- dont_ask: auto-approve (use with caution)
- plan: show plan, approve once
3. Note: Which modes map to CLI behaviors from earlier videos
[01:00 - 02:00] Delegate Mode with Callback 1. Define permission callback function 2. Show callback signature: (action_type, resource, details) → bool 3. Implement: allow write to /tmp, deny write to /etc 4. Show Claude attempting restricted write 5. Callback denies, agent handles gracefully
[02:00 - 03:00] Human Approval Loop 1. Scenario: agent wants to execute deploy script 2. Permission callback pauses and waits for user input 3. Show: user prompted via terminal/GUI/API 4. User approves/denies 5. Agent continues or stops
[03:00 - 03:45] Practical Example: Code Review + Commit 1. Agent reviews code and suggests fixes 2. Agent wants to commit changes (restricted action) 3. Permission callback requests approval 4. If approved: agent commits 5. If denied: agent reports what it would have done
Code Examples & Commands
Python with delegate mode:
from claude_code import Agent, PermissionMode
import asyncio
def permission_callback(action_type: str, resource: str, details: dict) -> bool:
"""
Decide whether to allow an action.
Return True to allow, False to deny.
"""
print(f"\n[Permission Request]")
print(f" Action: {action_type}")
print(f" Resource: {resource}")
print(f" Details: {details}")
# Allow reads always
if action_type == "read":
return True
# Allow writes only to /tmp
if action_type == "write":
return resource.startswith("/tmp/")
# Deny everything else
return False
async def main():
agent = Agent(
model="claude-sonnet-4-5-20250929",
system_prompt="You are a code analyst.",
permission_mode=PermissionMode.DELEGATE,
permission_callback=permission_callback
)
# This will trigger permission callback
result = await agent.run("Review the file at /var/important_config.txt and suggest improvements")
print(f"\nResult: {result.output}")
asyncio.run(main())
Human-in-the-loop approval:
from claude_code import Agent, PermissionMode
import asyncio
def approval_callback(action_type: str, resource: str, details: dict) -> bool:
"""Request human approval for restricted actions."""
# Allow reads without asking
if action_type == "read":
return True
# For writes, ask human
if action_type == "write":
print(f"\n🔒 Human Approval Required")
print(f" Action: Write to {resource}")
print(f" Content preview: {details.get('preview', 'N/A')[:100]}...")
response = input("\nApprove? (yes/no): ").strip().lower()
return response == "yes"
# Deny unknown actions
return False
async def main():
agent = Agent(
model="claude-sonnet-4-5-20250929",
permission_mode=PermissionMode.DELEGATE,
permission_callback=approval_callback
)
result = await agent.run("Create a new documentation file explaining our API")
print(f"Done: {result.output}")
asyncio.run(main())
Advanced: Role-based permissions:
from claude_code import Agent, PermissionMode
from typing import Optional
class RoleBasedPermissions:
def __init__(self, user_role: str):
self.user_role = user_role # "admin", "developer", "viewer"
def check(self, action_type: str, resource: str, details: dict) -> bool:
if self.user_role == "viewer":
# Viewers can only read
return action_type == "read"
elif self.user_role == "developer":
# Developers can read and write to /src, /tests
if action_type == "read":
return True
if action_type == "write":
return resource.startswith(("/src/", "/tests/"))
return False
elif self.user_role == "admin":
# Admins can do anything
return True
return False
# Usage
permissions = RoleBasedPermissions("developer")
agent = Agent(
model="claude-sonnet-4-5-20250929",
permission_mode=PermissionMode.DELEGATE,
permission_callback=permissions.check
)
TypeScript version:
import Anthropic from "@anthropic-ai/sdk";
interface PermissionRequest {
actionType: string;
resource: string;
details: Record<string, any>;
}
function permissionCallback(request: PermissionRequest): boolean {
console.log(`\n[Permission Request]`);
console.log(` Action: ${request.actionType}`);
console.log(` Resource: ${request.resource}`);
// Allow reads
if (request.actionType === "read") {
return true;
}
// Allow writes only to /tmp
if (request.actionType === "write") {
return request.resource.startsWith("/tmp/");
}
// Deny by default
return false;
}
async function main(): Promise<void> {
const client = new Anthropic({
// SDK configuration with permission handling
});
console.log("Agent running with permission callbacks...");
// Detailed implementation depends on SDK API
}
main().catch(console.error);
Gotchas & Tips
Gotcha 1: Blocking Callbacks - If callback takes too long (user thinking), agent waits - Solution: Set timeout on approval (auto-deny after 60 seconds)
Gotcha 2: Audit Trail - Permission decisions should be logged for compliance - Solution: Log all permission_callback calls with timestamp, decision, reason
Gotcha 3: Recursive Permissions - If callback itself triggers an agent action, avoid infinite loops - Solution: Use different permission mode for callback actions
Gotcha 4: Role Misalignment - Permissions in callback may not match actual system permissions - Example: callback allows write, but file is read-only - Solution: Permissions callback is first check; system permissions are second
Tip 1: Start Restrictive - Default-deny model: allow only known-safe actions - Easier to add permissions than restrict later
Tip 2: Context in Details
- Pass context in details dict so callback can make informed decisions
- Example: "user_id": 42, "request_source": "api", "is_test": false
Tip 3: Logging & Audit - Log every permission decision: who, what, when, allow/deny, reason - Critical for compliance and debugging
Tip 4: Testing Permissions - Write unit tests for permission_callback logic - Test edge cases: missing details, malformed requests
Tip 5: Cascade Approvals - For high-impact actions, require multiple approvals - Example: write to production → requires 2 approvals
Lead-out
"You've seen how to build safe, human-in-the-loop workflows using permission callbacks. Next: session management. Long-running agents need to persist state across interactions. We'll show how to create, resume, and manage agent sessions."
Reference URLs
- Claude Code Permission Modes: [check official docs]
- GitHub Issue #14297 (delegate mode): https://github.com/anthropics/mcp-servers/issues/14297
- Human-in-the-Loop Patterns: [AI safety resources]
Prep Reading
- Review permission modes in current SDK (5 min)
- Understand your application's security requirements (5 min)
- Plan which actions should require approval in your workflow (5 min)
Notes for Daniel
- Security emphasis: This video is about trust. Emphasize that delegation with callbacks is how you build safe AI agents.
- Tie to earlier videos: Mention 4.7 (interactive modes), 14.3 (dontAsk), and now 17.5 (delegate) form a progression of permission control.
- Real-world example: Use a practical scenario: code review + commit, data pipeline, automated deployment. Show the approval step clearly.
- GitHub issue: Mention #14297 is still being finalized—delegate mode is powerful but still evolving.
- Production ready: Position this as the mechanism for production automation where humans remain in control.