{"meta":{"title":"Pre-tool use hook","intro":"Use the onPreToolUse hook to control tool execution, modify arguments, and add context before a tool runs in Copilot SDK.","product":"GitHub Copilot","breadcrumbs":[{"href":"/en/copilot","title":"GitHub Copilot"},{"href":"/en/copilot/how-tos","title":"How-tos"},{"href":"/en/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/en/copilot/how-tos/copilot-sdk/use-hooks","title":"Use hooks"},{"href":"/en/copilot/how-tos/copilot-sdk/use-hooks/pre-tool-use","title":"Pre-tool use"}],"documentType":"article"},"body":"# Pre-tool use hook\n\nUse the onPreToolUse hook to control tool execution, modify arguments, and add context before a tool runs in Copilot SDK.\n\n> \\[!NOTE]\n> Copilot SDK is currently in public preview. Functionality and availability are subject to change.\n\nThe `onPreToolUse` hook is called **before** a tool executes. Use it to:\n\n* Approve or deny tool execution\n* Modify tool arguments\n* Add context for the tool\n* Suppress tool output from the conversation\n\n## Hook signature\n\n```typescript\nimport type { PreToolUseHookInput, HookInvocation, PreToolUseHookOutput } from \"@github/copilot-sdk\";\ntype PreToolUseHandler = (\n  input: PreToolUseHookInput,\n  invocation: HookInvocation\n) => Promise<PreToolUseHookOutput | null | undefined>;\n```\n\nFor hook signatures in Python, Go, and .NET, see the [`github/copilot-sdk` repository](https://github.com/github/copilot-sdk/blob/main/docs/hooks/pre-tool-use.md#hook-signature). For Java, see the [`github/copilot-sdk-java` repository](https://github.com/github/copilot-sdk-java).\n\n## Input\n\n| Field       | Type   | Description                                |\n| ----------- | ------ | ------------------------------------------ |\n| `timestamp` | number | Unix timestamp when the hook was triggered |\n| `cwd`       | string | Current working directory                  |\n| `toolName`  | string | Name of the tool being called              |\n| `toolArgs`  | object | Arguments passed to the tool               |\n\n## Output\n\nReturn `null` or `undefined` to allow the tool to execute with no changes. Otherwise, return an object with any of the following fields.\n\n| Field                      | Type                             | Description                                       |\n| -------------------------- | -------------------------------- | ------------------------------------------------- |\n| `permissionDecision`       | `\"allow\"` \\| `\"deny\"` \\| `\"ask\"` | Whether to allow the tool call                    |\n| `permissionDecisionReason` | string                           | Explanation shown to user (for deny/ask)          |\n| `modifiedArgs`             | object                           | Modified arguments to pass to the tool            |\n| `additionalContext`        | string                           | Extra context injected into the conversation      |\n| `suppressOutput`           | boolean                          | If true, tool output won't appear in conversation |\n\n### Permission decisions\n\n| Decision  | Behavior                                       |\n| --------- | ---------------------------------------------- |\n| `\"allow\"` | Tool executes normally                         |\n| `\"deny\"`  | Tool is blocked, reason shown to user          |\n| `\"ask\"`   | User is prompted to approve (interactive mode) |\n\n## Examples\n\n### Allow all tools (logging only)\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input, invocation) => {\n      console.log(\n        `[${invocation.sessionId}] `\n        + `Calling ${input.toolName}`\n      );\n      console.log(\n        `  Args: ${JSON.stringify(input.toolArgs)}`\n      );\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\nFor examples in Python, Go, and .NET, see the [`github/copilot-sdk` repository](https://github.com/github/copilot-sdk/blob/main/docs/hooks/pre-tool-use.md#allow-all-tools-logging-only). For Java, see the [`github/copilot-sdk-java` repository](https://github.com/github/copilot-sdk-java).\n\n### Block specific tools\n\n```typescript\nconst BLOCKED_TOOLS = [\n  \"shell\", \"bash\", \"write_file\", \"delete_file\",\n];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (BLOCKED_TOOLS.includes(input.toolName)) {\n        return {\n          permissionDecision: \"deny\",\n          permissionDecisionReason:\n            `Tool '${input.toolName}' `\n            + `is not permitted in this environment`,\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n### Modify tool arguments\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      // Add a default timeout to all shell commands\n      if (\n        input.toolName === \"shell\" && input.toolArgs\n      ) {\n        const args = input.toolArgs as {\n          command: string;\n          timeout?: number;\n        };\n        return {\n          permissionDecision: \"allow\",\n          modifiedArgs: {\n            ...args,\n            timeout: args.timeout ?? 30000,\n          },\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n### Restrict file access to specific directories\n\n```typescript\nconst ALLOWED_DIRECTORIES = [\n  \"/home/user/projects\", \"/tmp\",\n];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (\n        input.toolName === \"read_file\"\n        || input.toolName === \"write_file\"\n      ) {\n        const args = input.toolArgs as {\n          path: string;\n        };\n        const isAllowed =\n          ALLOWED_DIRECTORIES.some((dir) =>\n            args.path.startsWith(dir)\n          );\n\n        if (!isAllowed) {\n          return {\n            permissionDecision: \"deny\",\n            permissionDecisionReason:\n              `Access to '${args.path}' `\n              + `is not permitted. `\n              + `Allowed directories: `\n              + ALLOWED_DIRECTORIES.join(\", \"),\n          };\n        }\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n### Suppress verbose tool output\n\n```typescript\nconst VERBOSE_TOOLS = [\"list_directory\", \"search_files\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      return {\n        permissionDecision: \"allow\",\n        suppressOutput: VERBOSE_TOOLS.includes(input.toolName),\n      };\n    },\n  },\n});\n```\n\n### Add context based on tool\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (input.toolName === \"query_database\") {\n        return {\n          permissionDecision: \"allow\",\n          additionalContext:\n            \"Remember: This database uses \"\n            + \"PostgreSQL syntax. \"\n            + \"Always use parameterized queries.\",\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n## Best practices\n\n* **Always return a decision.** Returning `null` allows the tool, but being explicit with `{ permissionDecision: \"allow\" }` is clearer.\n* **Provide helpful denial reasons.** When denying, explain why so users understand what happened.\n* **Be careful with argument modification.** Ensure modified arguments maintain the expected schema for the tool.\n* **Consider performance.** Pre-tool hooks run synchronously before each tool call. Keep them fast.\n* **Use `suppressOutput` judiciously.** Suppressing output means the model won't see the result, which may affect conversation quality.\n* **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.\n\n## Further reading\n\n* [Quickstart for hooks](/en/copilot/how-tos/copilot-sdk/use-hooks/quickstart)\n* [Post-tool use hook](/en/copilot/how-tos/copilot-sdk/use-hooks/post-tool-use)\n* [Error handling hook](/en/copilot/how-tos/copilot-sdk/use-hooks/error-handling)"}