Skip to main content

工具使用前挂钩

使用 onPreToolUse 挂钩控制工具执行、修改参数,并在工具运行 Copilot SDK前添加上下文。

谁可以使用此功能?

GitHub Copilot SDK 适用于所有 Copilot 计划。

在本文中

注意

Copilot SDK 当前处于 技术预览版. 功能和可用性可能会发生更改。

onPreToolUse 工具执行 之前 调用挂钩。 使用它可执行以下操作:

  • 批准或拒绝工具执行
  • 修改工具参数
  • 添加工具的上下文
  • 取消对话中的工具输出

挂钩签名

import type { PreToolUseHookInput, HookInvocation, PreToolUseHookOutput } from "@github/copilot-sdk";
type PreToolUseHandler = (
  input: PreToolUseHookInput,
  invocation: HookInvocation
) => Promise<PreToolUseHookOutput | null | undefined>;

有关 Python、Go 和 .NET 中的挂钩签名,请参阅 github/copilot-sdk 存储库

输入

领域类型说明
timestampnumber触发挂钩时的 Unix 时间戳
cwd字符串当前工作目录
toolName字符串要调用的工具的名称
toolArgs对象传递给工具的参数

输出

返回 nullundefined 允许工具执行,无需更改。 否则,返回具有以下任何字段的对象。

领域类型说明
permissionDecision
          `"allow"`
          \|
          `"deny"`
          \|
          `"ask"`
         | 是否允许工具调用 |

| permissionDecisionReason | 字符串 | 向用户显示的说明(对于拒绝/询问) | | modifiedArgs | 对象 | 传递给工具的已修改参数 | | additionalContext | 字符串 | 注入到会话中的额外上下文 | | suppressOutput | 布尔 | 如果为 true,工具输出将不会显示在对话中 |

权限决策

决定行为
"allow"工具正常执行
"deny"工具被阻止,原因会显示给用户。
"ask"系统会提示用户批准(交互模式)

示例

允许所有工具(仅限日志记录)

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input, invocation) => {
      console.log(
        `[${invocation.sessionId}] `
        + `Calling ${input.toolName}`
      );
      console.log(
        `  Args: ${JSON.stringify(input.toolArgs)}`
      );
      return { permissionDecision: "allow" };
    },
  },
});

有关 Python、Go 和 .NET 中的示例,请参阅 github/copilot-sdk 存储库

阻止特定工具

const BLOCKED_TOOLS = [
  "shell", "bash", "write_file", "delete_file",
];

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      if (BLOCKED_TOOLS.includes(input.toolName)) {
        return {
          permissionDecision: "deny",
          permissionDecisionReason:
            `Tool '${input.toolName}' `
            + `is not permitted in this environment`,
        };
      }
      return { permissionDecision: "allow" };
    },
  },
});

修改工具参数

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      // Add a default timeout to all shell commands
      if (
        input.toolName === "shell" && input.toolArgs
      ) {
        const args = input.toolArgs as {
          command: string;
          timeout?: number;
        };
        return {
          permissionDecision: "allow",
          modifiedArgs: {
            ...args,
            timeout: args.timeout ?? 30000,
          },
        };
      }
      return { permissionDecision: "allow" };
    },
  },
});

限制对特定目录的文件访问

const ALLOWED_DIRECTORIES = [
  "/home/user/projects", "/tmp",
];

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      if (
        input.toolName === "read_file"
        || input.toolName === "write_file"
      ) {
        const args = input.toolArgs as {
          path: string;
        };
        const isAllowed =
          ALLOWED_DIRECTORIES.some((dir) =>
            args.path.startsWith(dir)
          );

        if (!isAllowed) {
          return {
            permissionDecision: "deny",
            permissionDecisionReason:
              `Access to '${args.path}' `
              + `is not permitted. `
              + `Allowed directories: `
              + ALLOWED_DIRECTORIES.join(", "),
          };
        }
      }
      return { permissionDecision: "allow" };
    },
  },
});

取消详细工具输出

const VERBOSE_TOOLS = ["list_directory", "search_files"];

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      return {
        permissionDecision: "allow",
        suppressOutput: VERBOSE_TOOLS.includes(input.toolName),
      };
    },
  },
});

根据工具添加上下文

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      if (input.toolName === "query_database") {
        return {
          permissionDecision: "allow",
          additionalContext:
            "Remember: This database uses "
            + "PostgreSQL syntax. "
            + "Always use parameterized queries.",
        };
      }
      return { permissionDecision: "allow" };
    },
  },
});

最佳做法

  •         **始终返回决定。** 
            `null`返回允许该工具,但显式使用`{ permissionDecision: "allow" }`更清晰。
    
  •         **提供有用的拒绝原因。** 拒绝时,请解释理由,以便用户了解发生了什么。
    
  •         **请谨慎修改参数。** 确保修改后的参数维护工具的预期架构。
    
  •         **请考虑性能。** 在每个工具调用之前,同步运行预工具钩子。 让他们保持速度。
    
  •         **谨慎使用 `suppressOutput` 。** 取消输出意味着模型看不到结果,这可能会影响聊天质量。
    
  •         **请注意敏感数据。** 工具参数和结果可能包含机密、文件路径或个人身份信息。 避免在生产环境中记录或公开此数据。
    

延伸阅读

  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/quickstart)
    
  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/post-tool-use)
    
  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/error-handling)