ToolUse 钩子(工具调用时触发)详解

Claude Code Hook 机制深度解析 —— 监控、审计与安全增强

一、概述

ToolUse 是 Claude Code 提供的一种钩子(Hook)事件类型,在 Claude 调用任意工具时触发。所谓"钩子",本质上是用户自定义的可执行脚本,Claude Code 在特定生命周期节点自动执行这些脚本,从而实现拦截、记录、修改或放行工具调用的目的。

ToolUse 钩子覆盖范围极广,包括但不限于 Bash(命令执行)、Read(文件读取)、Write(文件写入)、Edit(文件编辑)、Glob(文件搜索)、Grep(内容搜索)、WebFetch(网页抓取)、WebSearch(网络搜索)等所有内置工具。只要 Claude 决定调用某个工具,ToolUse 钩子就会在工具实际执行之前被触发。

这一机制为开发者提供了对 AI 行为的细粒度控制能力,是实现安全审计、用量监控、合规检查等功能的基础设施。

核心特性:
  • 覆盖 Claude Code 所有内置工具,无一例外
  • 在工具执行前触发,支持拦截和修改
  • 通过标准输入(stdin)接收结构化 JSON 数据
  • 通过退出码(exit code)决定放行或拦截
  • 支持同步等待,脚本执行期间 Claude 暂停等待结果

二、核心用途

ToolUse 钩子的设计初衷是为开发者提供对工具调用的全生命周期可见性和控制力。其核心用途可以归纳为以下四大领域:

1. 全量工具调用监控(Monitoring)

实时记录 Claude 每一次工具调用的详细信息,包括工具名称、传入参数、调用时间等。这对于理解 AI 的行为模式、排查问题、优化提示词策略至关重要。与 BeforeCommand 仅能捕获 shell 命令不同,ToolUse 能捕获 Read、Write、Glob 等非命令类工具的调用,提供全景式的监控视图。

2. 操作审计日志(Audit Trail)

将每一次工具调用持久化到日志文件中,形成不可篡改的审计轨迹。这对于合规性要求严格的场景(如金融、医疗、政务领域的 AI 应用)尤为关键。审计日志可以包含时间戳、会话 ID、用户标识、工具名称、参数摘要、执行结果等字段,满足事后追溯和取证需求。

3. 安全策略执行(Security Enforcement)

通过脚本逻辑对工具调用进行安全检查,拦截高风险操作。例如:禁止对包含敏感关键词的文件执行 Read 操作、限制 Write 操作的目标目录范围、阻止删除操作等。ToolUse 钩子返回非零退出码即可阻止工具执行,相当于在 AI 和系统资源之间建立了一道可编程的防火墙。

4. 用量分析与成本控制(Usage Analytics)

统计各类工具的调用频次、耗时分布、资源消耗等指标,为容量规划和成本优化提供数据支撑。团队管理者可以通过 ToolUse 钩子收集的数据了解 AI 辅助开发的实际使用情况,识别效率瓶颈和异常行为模式。

提示: ToolUse 钩子的脚本在本地执行,不会将工具调用的详细信息发送到 Anthropic 服务器。这意味着敏感数据的过滤和脱敏可以在本地完成,满足数据主权和隐私保护的要求。

三、配置方法

ToolUse 钩子通过 Claude Code 的配置文件 settings.jsonsettings.local.json 进行注册。配置文件支持三个层级,优先级从高到低依次为:本地项目配置(.claude/settings.local.json)、项目共享配置(.claude/settings.json)、全局用户配置(~/.claude/settings.json)。

配置结构

在配置文件的 hooks 字段下,以 ToolUse 为键名注册钩子脚本的路径。支持单个脚本路径或脚本路径数组。示例配置如下:

{ "hooks": { "ToolUse": [ ".claude/hooks/tool-use-audit.sh", ".claude/hooks/tool-use-security.sh" ] } }

配置层级示例

以下是在项目级 settings.json 中配置 ToolUse 钩子的完整示例。推荐将脚本放在 .claude/hooks/ 目录下统一管理:

# .claude/settings.json 项目共享配置 { "hooks": { "ToolUse": ".claude/hooks/tool-use-logger.sh" }, "permissions": { "allow": [ "read", "write", "edit", "bash", "glob", "grep" ] } }
注意: 如果多个层级的配置文件都定义了 ToolUse 钩子,低优先级的配置会被高优先级配置完全覆盖,而不是合并。建议将通用钩子定义在项目共享配置中,将个性化钩子定义在本地配置中。

多脚本执行顺序

当配置为数组时,脚本按数组顺序依次执行。如果某个脚本返回非零退出码,后续脚本将不再执行,工具调用被直接拦截。这意味着可以将安全策略脚本放在前面作为守门员,将日志记录脚本放在后面作为记录员。

Claude 调用工具 Hook 脚本 1(安全检查) Hook 脚本 2(审计日志) 工具执行

四、Hook 脚本格式与接口规范

ToolUse 钩子脚本遵循标准的 Unix 脚本接口规范。Claude Code 通过标准输入(stdin)向脚本传递 JSON 格式的工具调用信息,脚本通过退出码(exit code)向 Claude Code 返回执行结果。

输入格式(stdin)

脚本从标准输入接收一个 JSON 对象,包含以下字段:

字段名 类型 说明
tool_name string 被调用的工具名称,如 "Bash"、"Read"、"Write"、"Edit"、"Glob"、"Grep"、"WebFetch"、"WebSearch" 等
tool_input object 工具调用参数,包含工具执行所需的所有键值对。不同工具的参数字段各不相同

输出规范(stdout / stderr)

脚本向标准输出或标准错误输出的内容会被 Claude Code 捕获并记录。这些输出信息会出现在 Claude Code 的日志中,有助于调试钩子脚本本身的问题。脚本不应在 stdout 中输出大量数据,以免影响性能。

退出码约定

退出码 含义 行为
0 放行(Allow) 工具调用继续正常执行
非零 拦截(Block) 工具调用被阻止,错误信息返回给 Claude

脚本编写模板

以下是一个完整的 ToolUse 钩子脚本基础模板,展示了如何解析输入、执行逻辑、返回退出码:

#!/bin/bash # ToolUse hook 基础模板 # 从 stdin 读取 JSON 格式的工具调用信息 eval "$(cat)" TOOL_NAME="${tool_name}" case "${TOOL_NAME}" in Bash) COMMAND="${tool_input_command}" echo "[AUDIT] Bash command: ${COMMAND}" >&2 ;; Read) FILE_PATH="${tool_input_file_path}" echo "[AUDIT] Read file: ${FILE_PATH}" >&2 ;; Write) FILE_PATH="${tool_input_file_path}" echo "[AUDIT] Write file: ${FILE_PATH}" >&2 ;; *) echo "[AUDIT] Tool: ${TOOL_NAME}" >&2 ;; esac exit 0
关键技术要点:
  • 使用 eval "$(cat)" 将 stdin 的 JSON 解析为 shell 变量(适用于简单的单层 JSON)
  • 对于复杂 JSON,推荐使用 jq 工具进行解析,避免 eval 的安全风险
  • 脚本应在合理时间内完成执行(建议不超过 1 秒),避免阻塞 Claude 的工作流程
  • 日志输出应写入文件而非仅输出到 stderr,确保持久化

五、与 BeforeCommand 的区别

在 Claude Code 的 Hook 体系中,BeforeCommand 和 ToolUse 是最容易混淆的两个事件类型。理解二者的本质区别,对于正确选择使用场景至关重要。

对比维度 BeforeCommand ToolUse
触发范围 仅 Bash 工具(shell 命令) 所有工具(Bash、Read、Write、Edit、Glob、Grep 等)
输入数据 仅包含 command 字符串 完整的工具名称 + 参数对象
拦截能力 仅能记录,无法拦截命令执行 可通过非零退出码拦截工具调用
触发时机 命令即将执行时 工具调用被决定但尚未执行时
适用场景 简单命令记录 全面的审计、安全检查、用量统计

关键差异详解

范围差异: BeforeCommand 仅在 Claude 决定执行 Bash 命令时触发,对 Read、Write、Edit、Glob、Grep、WebFetch、WebSearch 等非命令工具完全无感知。这意味着如果仅配置了 BeforeCommand,Claude 读取敏感文件或写入恶意代码的行为将完全不可见。ToolUse 则覆盖全部工具,提供完整可见性。

数据差异: BeforeCommand 传递给脚本的 JSON 只有 command 一个字段,信息量有限。ToolUse 传递完整的 tool_nametool_input,后者包含该工具的所有参数,信息丰富度远超 BeforeCommand。

控制差异: BeforeCommand 本质上是一个"观察者"——它可以记录命令,但无法阻止命令执行。ToolUse 则是一个"控制器"——通过非零退出码可以完全阻止工具调用。这使 ToolUse 能够承担安全策略执行的关键职能。

总结: 当需要"观察和记录"所有 shell 命令时,使用 BeforeCommand 即可。当需要"全面监控、审计和安全控制"时,必须使用 ToolUse。二者也可同时配置,互不冲突,各自负责自己的触发范围。

六、应用场景

ToolUse 钩子在实际开发和安全运维中有广泛的应用场景,以下列举五个典型场景并深入分析。每个场景均阐述了业务痛点、钩子实现方案和预期收益。

场景一:全量审计日志系统

痛点: 在合规性要求严格的行业(如金融、医疗),需要记录 AI 对系统的每一次操作,包括文件读取、写入、命令执行等,以便在出现安全事件时进行事后追溯。手动记录不现实,且容易遗漏。

方案: 部署 ToolUse 钩子脚本,将所有工具调用的 JSON 信息追加到审计日志文件中。日志按日期轮转,包含时间戳、会话 ID、工具名称和完整参数。

收益: 获得不可篡改的操作审计轨迹,满足 SOC2、ISO 27001 等合规审计要求,大幅降低安全事件响应时间。

场景二:敏感文件访问防护

痛点: Claude 在开发过程中可能无意中读取包含 API 密钥、数据库密码、配置密钥等敏感信息的文件,导致凭据泄露。

方案: 在 ToolUse 钩子中检查 Read 工具的 file_path 参数,如果路径匹配敏感文件模式(如 *.env、*credentials*、*secret*、*key*),则返回非零退出码阻止读取。

收益: 避免敏感凭据被意外读取和暴露,增强 AI 辅助开发的安全性。

场景三:工具调用频率限制

痛点: Claude 可能在短时间内发起大量工具调用(如批量读取数千个文件),对系统资源造成冲击,或者导致 API 费用失控。

方案: 在 ToolUse 钩子中维护一个调用计数器,对同一种工具的调用频率进行限制(如每分钟最多调用 100 次 Bash)。超出限制时返回非零退出码并记录警告。

收益: 防止资源滥用和意外费用,确保 AI 辅助开发过程在可控范围内运行。

场景四:写操作目标目录白名单

痛点: Claude 可能将生成的文件写入项目目录之外的系统关键路径,造成文件系统污染或安全风险。

方案: 在 ToolUse 钩子中检查 Write 和 Edit 工具的 file_path 参数,确保目标路径在项目目录的白名单范围内。超出范围的写入操作被阻止。

收益: 将 AI 的文件操作限制在安全边界内,保护系统关键文件不被意外修改。

场景五:用量分析与团队管理

痛点: 团队管理者希望了解 Claude Code 在团队中的实际使用情况——哪些工具使用最频繁、平均每次会话调用多少次工具、典型的工具调用链是什么——以便优化培训和资源分配。

方案: 通过 ToolUse 钩子收集所有工具调用的元数据(不含敏感参数),汇总到中央分析平台,生成可视化仪表盘。

收益: 数据驱动的 AI 辅助开发管理,识别效率提升点和培训需求,量化 AI 工具的投资回报率。

最佳实践: 建议将不同用途的钩子脚本分离(如 audit.sh、security.sh、ratelimit.sh),通过 settings.json 的数组配置依次执行。这样便于单独维护、测试和更新每个脚本,互不影响。

七、代码示例

以下提供三个完整的 ToolUse 钩子脚本示例,分别覆盖审计日志、安全检查和生产级综合方案。每个示例均可直接复制使用,只需根据项目实际情况调整路径和规则。

示例一:审计日志记录器

将每次工具调用的详细信息追加到日志文件,自动按日期轮转,包含时间戳、工具名称和参数摘要。适合作为基础的审计记录方案。

#!/bin/bash # tool-use-audit.sh - 全量工具调用审计日志 # 配置:日志目录,确保该目录已创建 LOG_DIR="${CLAUDE_CODE_DIR:-.claude}/logs" LOG_FILE="${LOG_DIR}/tool-use-$(date +%Y%m%d).log" mkdir -p "${LOG_DIR}" if command -v jq >/dev/null 2>&1; then # 使用 jq 解析 JSON,更安全可靠 JSON_DATA="$(cat)" TOOL_NAME="$(echo "${JSON_DATA}" | jq -r '.tool_name')" TIMESTAMP="$(date +"%Y-%m-%d %H:%M:%S.%3N")" SESSION_ID="${CLAUDE_SESSION_ID:-unknown}" echo "[${TIMESTAMP}] [${SESSION_ID}] [${TOOL_NAME}] ${JSON_DATA}" >> "${LOG_FILE}" else # 降级方案:不使用 jq,仅记录原始 JSON RAW="$(cat)" echo "$(date +"%Y-%m-%d %H:%M:%S") [no-jq] ${RAW}" >> "${LOG_FILE}" fi exit 0

示例二:安全策略检查器

在工具执行前进行安全检查:阻止对敏感文件的读取、限制写入范围、阻止危险命令。返回非零退出码即表示拦截本次调用。

#!/bin/bash # tool-use-security.sh - 安全策略检查 # 定义敏感文件匹配模式(扩展的 glob 模式) SENSITIVE_PATTERNS=( "*.env" "*credentials*" "*secret*" "*.pem" "*id_rsa*" "*password*" "*token*" ".claude/settings.local.json" ) # 定义允许写入的目录前缀 ALLOWED_WRITE_DIRS=( "${PWD}/src" "${PWD}/docs" "${PWD}/tests" "${PWD}/.claude" ) TOOL_NAME="$(cat | jq -r '.tool_name')" TOOL_INPUT="$(cat | jq -r '.tool_input')" case "${TOOL_NAME}" in Read) FILE_PATH="$(echo "${TOOL_INPUT}" | jq -r '.file_path')" for pattern in "${SENSITIVE_PATTERNS[@]}"; do if [[ "${FILE_PATH}" == ${pattern} ]]; then echo "[SECURITY] Blocked read of sensitive file: ${FILE_PATH}" >&2 exit 1 fi done ;; Write|Edit) FILE_PATH="$(echo "${TOOL_INPUT}" | jq -r '.file_path')" ALLOWED=0 for dir in "${ALLOWED_WRITE_DIRS[@]}"; do if [[ "${FILE_PATH}" == "${dir}"* ]]; then ALLOWED=1 break fi done if [[ ${ALLOWED} -eq 0 ]]; then echo "[SECURITY] Blocked write outside allowed dirs: ${FILE_PATH}" >&2 exit 1 fi ;; Bash) COMMAND="$(echo "${TOOL_INPUT}" | jq -r '.command')" if [[ "${COMMAND}" == *"rm -rf"* ]] || [[ "${COMMAND}" == *"chmod 777"* ]]; then echo "[SECURITY] Blocked dangerous command: ${COMMAND}" >&2 exit 1 fi ;; esac exit 0

示例三:综合版(审计 + 安全 + 统计)

将审计、安全、频率限制功能整合到一个脚本中,适合生产环境直接使用。脚本会自动创建日志目录、轮转旧日志、并在每次调用时收集统计信息。

#!/bin/bash # tool-use-comprehensive.sh - 生产级综合监控脚本 # 功能:审计日志 + 安全过滤 + 频率统计 set -o pipefail HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOG_DIR="${HOOK_DIR}/../logs" mkdir -p "${LOG_DIR}" # 读取工具调用信息 INPUT_JSON="$(cat)" TOOL_NAME="$(echo "${INPUT_JSON}" | jq -r '.tool_name // "unknown"')" TIMESTAMP="$(date +"%Y-%m-%dT%H:%M:%S%z")" EPOCH="$(date +%s)" # 1. 审计日志 LOG_FILE="${LOG_DIR}/audit-$(date +%Y%m%d).log" echo "${TIMESTAMP}|${TOOL_NAME}|${INPUT_JSON}" >> "${LOG_FILE}" # 2. 统计计数器 STATS_FILE="${LOG_DIR}/stats-$(date +%Y%m%d).log" echo "${EPOCH}|${TOOL_NAME}" >> "${STATS_FILE}" # 3. 安全检查 if [[ "${TOOL_NAME}" == "Read"" ]]; then FILE_PATH="$(echo "${INPUT_JSON}" | jq -r '.tool_input.file_path // ""')" if echo "${FILE_PATH}" | grep -qiE '\.(env|pem|key)$|credential|secret|password'; then echo "[BLOCKED] Sensitive file read attempt: ${FILE_PATH}" >&2 exit 1 fi fi # 4. 频率限制(每分钟相同工具最多 50 次) if [[ -f "${STATS_FILE}" ]]; then COUNT="$(awk -F'|' -v tool="${TOOL_NAME}" -v now="${EPOCH}" \ '$2 == tool && now - $1 < 60 {count++} END {print count+0}' \ "${STATS_FILE}")" if [[ ${COUNT} -gt 50 ]]; then echo "[RATE_LIMIT] Tool ${TOOL_NAME} called ${COUNT} times in 60s" >&2 exit 1 fi fi exit 0
部署提示: 将脚本保存到 .claude/hooks/ 目录后,执行 chmod +x .claude/hooks/*.sh 赋予执行权限。然后编辑 .claude/settings.json 添加 hooks 配置即可。修改配置后无需重启 Claude Code,下次工具调用时自动生效。

八、注意事项与最佳实践

在生产环境中使用 ToolUse 钩子时,需要关注以下重要事项,以避免意外行为和性能问题。

性能考量

ToolUse 钩子脚本是同步执行的——Claude 在脚本执行完毕之前不会继续工作。因此脚本的执行时间直接影响用户的交互体验。建议将脚本执行时间控制在 100 毫秒以内。对于需要耗时操作(如网络请求、大量日志写入),应考虑使用异步方式(如后台进程、消息队列)避免阻塞。

性能优化建议:
  • 日志写入使用追加模式(>>),避免文件锁定
  • 日志文件按天或按小时轮转,避免单个文件过大
  • 避免在钩子脚本中执行网络请求或数据库操作
  • 使用局部变量(local)避免污染全局命名空间
  • 对于高频调用场景,考虑在脚本内部实现批处理缓冲区

安全注意事项

钩子脚本本身具有较高的权限——它可以读取所有文件、执行任意命令。因此钩子脚本的安全性至关重要:

调试技巧

当钩子脚本未按预期工作时,可以采取以下步骤进行调试:

跨平台兼容性

如果团队分布在 Windows、macOS 和 Linux 混合环境中,需要注意以下兼容性问题:

常见陷阱: ToolUse 钩子本身不会触发 ToolUse 事件——如果钩子脚本调用 Claude Code CLI 命令,不会形成无限递归。但钩子脚本的 stderr 输出会被 Claude 读取并可能影响其判断,因此建议仅输出关键信息到 stderr,日常日志写入文件而非标准流。

核心要点总结

九、进一步思考

ToolUse 钩子机制代表了 AI 辅助开发工具在安全性和可控性方面的重要进步。随着 AI 编程助手的能力不断增强,其能够执行的操作类型和复杂度也在持续提升,对工具调用进行精细化管理将成为企业级部署的刚需。

未来可能的发展方向包括:声明式策略语言替代条件式脚本、图形化的策略管理界面、与 SIEM(安全信息和事件管理)系统的原生集成、基于机器学习的异常调用检测等。ToolUse 钩子为这些高级功能提供了底层基础设施和标准接口。

从更宏观的视角看,ToolUse 钩子体现了 AI 工具设计中的一个重要原则——渐进式自主:AI 的能力越强,越需要精细化的控制边界。Hook 机制正是在"充分发挥 AI 能力"和"确保安全可控"之间找到平衡点的关键设计。

扩展思考:
  • 团队可以构建统一的 Hook 管理中心,集中管理和分发策略规则
  • 结合 CI/CD 流水线,在代码审查阶段自动验证钩子脚本的正确性
  • 将工具调用数据与 APM(应用性能管理)系统集成,实现全链路可观测性
  • 探索多级 Hook 策略:开发环境使用宽松策略,生产环境使用严格策略
  • 定期复盘 Hook 日志,优化策略规则,减少误拦截和漏拦截