開発者ガイド

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 を保存し、そのフォルダーで次の 2 つのコマンドを実行します。

npm install --no-save --save-exact @anthropic-ai/[email protected] [email protected]
npx tsx claude_agent_sdk.ts

このコマンドは 1 つの PreToolUse フックマッチャーを構築しますが、クエリは開始しません。アプリケーション内の SDK クエリに createHookOptions() を渡し、各ツール入力をマンデートがチェックする行為フィールドに対応付けます。

すべての結果を処理する

タイムアウト、転送障害、成功以外の HTTP 応答、不正な形式の応答、または未知の結果は error になります。正確な permit 以外のすべての結果はフックによって拒否されます。

参照

チェックを試す · 署名付きレシートを検証する · OpenAPI 文書 · エージェント向け要約