Appearance
onPreToolUse 是 Copilot SDK 最重要的安全控制点,在工具执行前拦截并决定允许/拒绝/请求确认。可以修改工具参数、添加执行上下文、限制文件访问路径、记录审计日志。保持 Hook 逻辑快速,拒绝时给出清晰原因。
GitHub Copilot SDK onPreToolUse Hook:精细控制工具调用权限
基本结构
typescript
const session = await createSession({
hooks: {
onPreToolUse: async (context) => {
const { toolName, arguments: args, sessionId } = context
// 决策逻辑
return null // 允许(默认)
}
}
})context 包含:
toolName:被调用的工具名称arguments:工具参数对象sessionId:当前会话 IDagentName:发起调用的 Agent 名称(如果是 subagent)
三种决策方式
允许执行
typescript
return null // 简洁写法
// 或
return { action: 'allow' }拒绝执行
typescript
return {
action: 'deny',
reason: '该文件在安全保护列表中,禁止修改' // 告知 AI 为什么被拒绝
}AI 收到拒绝时会根据 reason 决定下一步,所以 reason 要清晰有用。
请求用户确认
typescript
return {
action: 'ask',
message: `Agent 要删除以下文件,是否确认?\n${args.paths.join('\n')}`
}应用层需要监听对应事件,展示确认界面,然后调用 session API 传回用户决定。
实用模式
限制文件访问范围
typescript
onPreToolUse: async ({ toolName, arguments: args }) => {
if (['read_file', 'write_file', 'delete_file'].includes(toolName)) {
const allowedPaths = ['/workspace', '/tmp']
const targetPath = args.path as string
const isAllowed = allowedPaths.some(p => targetPath.startsWith(p))
if (!isAllowed) {
return {
action: 'deny',
reason: `文件访问限制在以下目录:${allowedPaths.join(', ')}`
}
}
}
return null
}修改工具参数
typescript
onPreToolUse: async ({ toolName, arguments: args }) => {
if (toolName === 'execute_command') {
// 自动设置超时,防止命令无限运行
return {
action: 'allow',
modifiedArguments: {
...args,
timeout: Math.min(args.timeout ?? 30000, 30000) // 最多 30 秒
}
}
}
return null
}审计日志(非阻塞)
typescript
onPreToolUse: async ({ toolName, arguments: args, sessionId }) => {
// 非阻塞写日志(不 await,不影响工具执行速度)
auditLog.append({
timestamp: new Date().toISOString(),
sessionId,
tool: toolName,
args
}).catch(err => console.error('日志写入失败:', err))
return null // 不影响工具执行
}为特定工具添加上下文
typescript
onPreToolUse: async ({ toolName, arguments: args }) => {
if (toolName === 'execute_sql') {
return {
action: 'allow',
additionalContext: `数据库规则:
- 只允许 SELECT 和有 WHERE 条件的 UPDATE
- 禁止 DROP、TRUNCATE、DELETE without WHERE
- 当前用户:${process.env.DB_USER}(只读权限)`
}
}
return null
}最佳实践
1. 拒绝时说清楚原因:AI 会基于 reason 决定是否换一种方式完成任务,模糊的 reason 会导致 AI 反复尝试。
2. 快速决策:Hook 阻塞工具执行,避免在 Hook 中做耗时操作(数据库查询、网络请求)。
3. 修改参数要保守:modifiedArguments 只改需要改的字段,不要改变参数的语义含义。
常见问题
Q: onPreToolUse 能看到 MCP 服务器的工具调用吗?
A: 可以,MCP 工具调用也会经过 Hook,toolName 会是 MCP 工具的名称(如 mcp:filesystem:read_file)。
Q: 同一个工具调用,Hook 被调用多次正常吗?
A: 如果 Agent 重试了工具调用(如第一次失败后),Hook 会被再次调用。这是正常行为,确保 Hook 的逻辑是幂等的。
Q: 如果 Hook 本身抛出异常会怎样?
A: Hook 异常会被视为工具调用失败,Agent 会收到错误信息。建议在 Hook 内部 try-catch,确保控制权始终在你手中。