Skip to main content

Pre-tool use hook

Use the onPreToolUse hook to control tool execution, modify arguments, and add context before a tool runs in Второй пилот SDK.

Кто может использовать эту функцию?

GitHub Copilot SDK Доступна со всеми Copilot тарифными планами.

В этой статье

Примечание.

Второй пилот SDK is currently in Technical Preview. Functionality and availability are subject to change.

The onPreToolUse hook is called before a tool executes. Use it to:

  • Approve or deny tool execution
  • Modify tool arguments
  • Add context for the tool
  • Suppress tool output from the conversation

Hook signature

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

For hook signatures in Python, Go, and .NET, see the github/copilot-sdk repository.

Input

FieldTypeDescription
timestampnumberUnix timestamp when the hook was triggered
cwdstringCurrent working directory
toolNamestringName of the tool being called
toolArgsobjectArguments passed to the tool

Output

Return null or undefined to allow the tool to execute with no changes. Otherwise, return an object with any of the following fields.

FieldTypeDescription
permissionDecision"allow" | "deny" | "ask"Whether to allow the tool call
permissionDecisionReasonstringExplanation shown to user (for deny/ask)
modifiedArgsobjectModified arguments to pass to the tool
additionalContextstringExtra context injected into the conversation
suppressOutputbooleanIf true, tool output won't appear in conversation

Permission decisions

DecisionBehavior
"allow"Tool executes normally
"deny"Tool is blocked, reason shown to user
"ask"User is prompted to approve (interactive mode)

Examples

Allow all tools (logging only)

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" };
    },
  },
});

For examples in Python, Go, and .NET, see the github/copilot-sdk repository.

Block specific tools

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" };
    },
  },
});

Modify tool arguments

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" };
    },
  },
});

Restrict file access to specific directories

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" };
    },
  },
});

Suppress verbose tool output

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),
      };
    },
  },
});

Add context based on tool

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" };
    },
  },
});

Best practices

  • Always return a decision. Returning null allows the tool, but being explicit with { permissionDecision: "allow" } is clearer.
  • Provide helpful denial reasons. When denying, explain why so users understand what happened.
  • Be careful with argument modification. Ensure modified arguments maintain the expected schema for the tool.
  • Consider performance. Pre-tool hooks run synchronously before each tool call. Keep them fast.
  • Use suppressOutput judiciously. Suppressing output means the model won't see the result, which may affect conversation quality.
  • Be mindful of sensitive data. Tool arguments and results may contain secrets, file paths, or personally identifiable information. Avoid logging or exposing this data in production environments.

Further reading