Claude Agent SDK
permdock/claude-agent answers Claude Agent SDK canUseTool callbacks and PermissionRequest hooks from PermDock decisions, mapping built-in tools such as Bash to typed permissions.
Status: planned Phase: 1
permdock/claude-agent plugs PermDock into the two permission surfaces of the Claude Agent SDK: the canUseTool callback that runs before every tool invocation, and the PermissionRequest hook that fires when the SDK is about to ask a human. Both are answered from one Decision, so the same policy that guards an HTTP route decides whether an agent may run a shell command.
Purpose
The Claude Agent SDK exposes tool use to the host application as a callback: the SDK proposes a tool call, the host allows or denies it, optionally rewriting the input. Without a policy layer, hosts hard-code tool names and argument patterns in that callback. permdock/claude-agent replaces the hard-coding with a tools map from SDK tool names to permission references, and applies PermDock's subject model so the agent acts as an actor under the user's authority and never exceeds it. The result is the same three-outcome mapping used by permdock/ai-sdk and permdock/mcp, with a human approval path where a grant says approval: 'human'.
API
import { createPermDock } from 'permdock/claude-agent'
const { canUseTool, permissionRequestHook } = createPermDock(policy, {
subject: () => currentUser,
actor: () => ({ id: 'claude-agent', kind: 'claude-agent' }),
tools: {
Bash: { permission: permissions.shell.run, data: (input) => ({ command: input.command }) },
Write: { permission: permissions.file.write, data: (input) => ({ path: input.file_path }) },
Read: { permission: permissions.file.read, data: (input) => ({ path: input.file_path }) },
mcp__posts__delete_post: { permission: permissions.post.delete, data: (input) => loadPost(input.id) },
},
})
query({
prompt,
options: {
canUseTool,
hooks: { PermissionRequest: [permissionRequestHook] },
},
})toolskeys are SDK tool names, including built-ins (Bash,Read,Write,Edit,WebFetch) and MCP tools by their prefixed name. Each entry names a permission and, for instance-level actions, adataresolver that turns the tool input into the resource shape the permission's schema expects.subjectandactorare resolved once perqueryunless they are functions of the call context;delegationcan be supplied when the host holds scoped credentials for the agent.canUseToolandpermissionRequestHookare ready to pass to the SDK; both close over the same request-scopedPermDock.
Request lifecycle
- The model proposes a tool call. The SDK invokes
canUseTool(toolName, input, context). - The adapter looks up
toolNameintools. Unmapped tools are denied (fail closed) with a message naming the tool. - If
datais declared, the input is projected into the resource shape and validated against the resource schema (boundary mode). permdock.decide(permission, data)runs.- The outcome maps to the SDK's return value:
| Decision outcome | canUseTool result |
|---|---|
granted | allow, with the (possibly normalised) input |
denied | deny, with a message built from denials and alternatives |
approval-required | defer to the human path: the adapter returns the SDK's ask behaviour so the PermissionRequest hook fires |
- When the SDK asks for human confirmation,
permissionRequestHookreceives the request. The adapter attachesDecision.tokenand a summary (permission key, resource identity, reason) so the prompt shown to the human is specific. When the human answers, the hook re-runsdecideand compares the token before allowing; a mismatch or an expired token denies. on('decision')fires for every step, including the human's answer, for audit andpermdock/otel.
What it validates
- Tool input against the permission's resource schema after
dataprojection (validate: 'boundary'). ForBash, ashell.runresource can carrycommandsowhereconditions such as an allow-listed prefix are expressed as portable conditions, not regexes in the callback. - Subject provenance: the subject comes from the host process, never from the conversation or tool input.
- Approval replays: token equality between the
approval-requireddecision and the human's answer. - Coverage in development: a warning lists SDK tools enabled for the session that are missing from
tools, because those calls will be denied at runtime.
How denials surface
deniedreturns the SDK deny behaviour with a message such asDenied: shell.run (command not allow-listed). You may: file.read, file.write.The message is built fromDecision.denialsreasons andalternativesso the model can self-correct.approval-requiredbecomes a human prompt through thePermissionRequesthook rather than a silent deny; the prompt carries the permission key and the resource identity.- Errors thrown by a
dataresolver or by the policy are caught, logged throughon('decision')and reported as a deny; the agent loop never receives an unhandled exception from the permission layer. - There is no pass-through or default-allow mode; the SDK's own permission modes remain in force on top of PermDock's answer.
Example app
apps/examples/claude-agent: a CLI host that runs query with Bash, Read, Write and one MCP tool, two users (a reader whose Bash is denied, a maintainer whose Bash requires approval for rm commands), and tests that a tampered approval answer is refused and every decision is logged.
Related standards
- Approvals:
approval: 'human', replay-safetoken. - Delegation: the agent as
actor, principal grants intersected with delegated authority. - OWASP Agentic Top 10: ASI02 Tool Misuse, ASI03 Identity and Privilege Abuse, least agency.
- MCP authorization: for MCP tools reached through the Claude Agent SDK.
Open questions
- The exact SDK return shapes for allow, deny and ask are tracked against the current Claude Agent SDK release; the adapter will pin a minimum SDK version.
- Whether the adapter should support
updatedInputrewriting (for example, forcingcwdforBash) as a grant option, or leave rewriting to the host. - Whether a
shell.runresource schema should ship as a helper for common built-in tools, or stay app-defined. - Whether
PermissionRequestdecisions should be persisted so an approval survives a restarted session (cf.WorkflowAgentdurable suspend in the AI SDK adapter).
AI SDK
permdock/ai-sdk turns PermDock decisions into Vercel AI SDK 7 tool approvals, capability middleware and WorkflowAgent suspensions, fail-closed by construction.
Eve
permdock/eve turns PermDock decisions into Eve tool approval policies and approval response policies, takes principal and actor from the durable session's auth, and stores pending approvals in a pluggable ApprovalStore.