开发者指南
为 Claude Agent SDK 智能体添加操作前检查
如果必须在工具运行前进行检查,Claude Agent SDK 工具调用需要 PreToolUse 钩子。
检查的作用
检查返回 permit、 deny或 escalate ,并附上决定结果的条款和签名回执。检查仅提供建议,真正拒绝执行的是你的 PreToolUse 钩子。托管检查在内存中评估请求,不保留请求内容。
代码
这是经过审计的示例,与以下位置发布的内容完全一致: public/examples/frameworks/.
/** Claude Agent SDK PreToolUse hook that checks HANRIA before execution. */
import { query, type HookCallback, type HookCallbackMatcher, type Options } from "@anthropic-ai/claude-agent-sdk";
import { pathToFileURL } from "node:url";
const CHECK_URL = "https://check.hanria.ai/v1/check";
const MANDATE = {
schema_version: "0.2-draft",
mandate_id: "review-tool-calls",
purpose: "Permit file reads under the example directory.",
default: "deny",
clauses: [{
id: "permit-example-read",
effect: "permit",
match: { kind: ["file"], verb: ["read"], target_prefix: ["/tmp/example/"] },
}],
};
type FetchLike = typeof globalThis.fetch;
export async function checkAction(
action: Record<string, unknown>,
checkUrl = CHECK_URL,
fetchImpl: FetchLike = globalThis.fetch,
): Promise<Record<string, unknown>> {
try {
const response = await fetchImpl(checkUrl, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ mandate: MANDATE, action }),
signal: AbortSignal.timeout(10000),
});
if (!response.ok) return { outcome: "error", reason: `HANRIA returned HTTP ${response.status}` };
const decision = await response.json() as Record<string, unknown>;
if (!["permit", "deny", "escalate", "error"].includes(String(decision.outcome))) {
return { outcome: "error", reason: "HANRIA returned an invalid decision" };
}
return decision;
} catch (error) {
return { outcome: "error", reason: `HANRIA check failed: ${String(error)}` };
}
}
export function createHanriaHook({
checkUrl = CHECK_URL,
fetchImpl = globalThis.fetch,
}: { checkUrl?: string; fetchImpl?: FetchLike } = {}): HookCallback {
return async (input) => {
if (input.hook_event_name !== "PreToolUse") {
return { continue: false, stopReason: "Expected a PreToolUse event" };
}
const toolInput = input.tool_input as Record<string, unknown>;
const action = {
schema_version: "0.1-draft",
requested_by: { agent: "claude-agent-sdk-example" },
operation: {
kind: input.tool_name === "Read" ? "file" : "tool",
verb: input.tool_name === "Read" ? "read" : input.tool_name,
target: String(toolInput.file_path ?? toolInput.path ?? input.tool_name),
},
justification: `Run ${input.tool_name} through the Claude Agent SDK.`,
};
const decision = await checkAction(action, checkUrl, fetchImpl);
if (decision.outcome === "permit") return {};
return {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: `HANRIA returned ${String(decision.outcome ?? "error")}: ${String(decision.reason ?? "no reason returned")}`,
},
};
};
}
export function createHookOptions(settings: { checkUrl?: string; fetchImpl?: FetchLike } = {}): Options {
const matcher: HookCallbackMatcher = { hooks: [createHanriaHook(settings)] };
return { hooks: { PreToolUse: [matcher] } };
}
export function constructQuery(prompt: string, options = createHookOptions()) {
return query({ prompt, options });
}
if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
const options = createHookOptions();
console.log(`Constructed ${options.hooks?.PreToolUse?.length ?? 0} HANRIA PreToolUse hook matcher.`);
}
运行
保存 claude_agent_sdk.ts,然后在保存该文件的文件夹中运行以下命令。
npm install --no-save --save-exact @anthropic-ai/[email protected] [email protected]
npx tsx claude_agent_sdk.ts
该命令构造一个 PreToolUse 钩子匹配器,但不会启动查询。在你的应用程序中,将 createHookOptions() 传给 SDK 查询,并把每个工具输入映射到授权书所检查的操作字段。
处理每一种结果
- permit: 钩子不返回权限决定,因此 SDK 继续其正常权限流程。
- deny: 钩子在工具运行前返回拒绝权限决定。
- escalate: 钩子返回拒绝权限决定。请在该钩子之外请求运营者作出决定。
- error: 钩子返回拒绝权限决定。将其视为 deny,报告原因,并且不要盲目重试。
超时、传输故障、非成功 HTTP 响应、格式错误的响应或未知结果都会变为 error。除准确的 permit 以外的每个结果都会被钩子拒绝。
参考资料
试用检查 · 验证签名回执 · OpenAPI 文档 · 面向智能体的摘要