Master Claude Code Hooks: PreToolUse & PostToolUse for Secure AI Workflows

This guide explains how to use Claude Code hooks—PreToolUse and PostToolUse—to run custom commands before or after tool execution, configure them in global or project settings, and implement practical examples such as blocking .env file access, type‑checking TypeScript, and preventing duplicate queries.

Java One
Java One
Java One
Master Claude Code Hooks: PreToolUse & PostToolUse for Secure AI Workflows

Understanding Claude Code Hooks

Hooks let you execute commands automatically before ( PreToolUse) or after ( PostToolUse) Claude attempts to run a tool, enabling automation such as code formatting, test execution, or restricting access to sensitive files.

Hook Mechanics

When you ask Claude a question, the query and any tool definition are sent to the Claude model. The model may decide to invoke a tool, which is then executed by Claude Code and the result returned. A hook inserts custom code into this pipeline, running immediately before or after the tool call.

Hook insertion diagram
Hook insertion diagram

There are two hook types:

PreToolUse hook – runs before the tool is executed.

PostToolUse hook – runs after the tool has finished.

Hook Configuration

Hooks are defined in Claude configuration files. You can place them in a global file ( ~/.claude/settings.json) that affects all projects, in a project‑shared file ( .claude/settings.json), or in a personal file ( .claude/settings.local.json) that is not committed.

Hooks can be added manually to these JSON files or created with the /hooks command inside Claude Code.

Hook configuration files
Hook configuration files

PreToolUse Hook Example

The following JSON shows a PreToolUse hook that runs a Node command whenever the Read tool is about to be used:

{
  "PreToolUse": [
    {
      "matcher": "Read",
      "hooks": [
        {
          "type": "command",
          "command": "node /home/hooks/read_hook.ts"
        }
      ]
    }
  ]
}

The command receives a JSON payload on stdin containing details such as session_id, tool_name, and tool_input. The script can allow the operation, block it (by exiting with code 2), or provide an error message.

PostToolUse Hook Example

A PostToolUse hook can run after a write, edit, or multi‑edit operation. Example configuration:

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit|MultiEdit",
      "hooks": [
        {
          "type": "command",
          "command": "node /home/hooks/edit_hook.ts"
        }
      ]
    }
  ]
}

Since the tool has already run, the hook cannot stop the action but can perform follow‑up tasks such as formatting the edited file or sending feedback to Claude.

Practical Use Cases

Code formatting after Claude edits a file.

Running tests automatically when files change.

Blocking Claude from reading or editing specific files (e.g., .env).

Logging which files Claude accessed.

Validating naming conventions or coding standards.

Key Points

Hooks let you extend Claude Code by integrating your own tools and workflows. PreToolUse hooks control what Claude can do, while PostToolUse hooks enhance the work Claude has already performed.

Defining a Hook to Block Sensitive Files

The example project includes a .env file containing a secret key. To prevent Claude from reading it, we create a PreToolUse hook that matches the Read and Grep tools and checks the requested file path.

{
  "matcher": "Read|Grep",
  "command": "node ./hooks/read_hook.js"
}

The Node script ( read_hook.js) reads the JSON payload, extracts tool_input.file_path, and aborts with exit code 2 if the path contains .env:

async function main() {
  const chunks = [];
  for await (const chunk of process.stdin) {
    chunks.push(chunk);
  }
  const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
  const readPath = toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";
  if (readPath.includes('.env')) {
    console.error("You cannot read the .env file");
    process.exit(2);
  }
}
main();

After saving the configuration and script, restart Claude Code and attempt to read .env. Claude receives the error message and reports that the operation was blocked.

Hook test output
Hook test output

Additional Useful Hooks

TypeScript type‑check hook – a PostToolUse hook that runs tsc --noEmit after every file edit, feeding any type errors back to Claude.

Duplicate query detection hook – a PreToolUse hook that spawns a second Claude instance to scan the ./queries directory for existing similar queries before creating a new one.

Hook Debugging Tips

Because the JSON payload varies by hook type, you can add a temporary PostToolUse hook that writes the raw input to a file (e.g., jq . > post‑log.json) to inspect the exact structure.

Claude Code SDK Overview

The Claude Code SDK lets you programmatically run Claude Code from TypeScript, Python, or the CLI. It shares the same tool set and permissions as the CLI version, with read‑only access by default. You can enable write permissions by specifying allowedTools in the query options or by adjusting the project‑level settings.

for await (const message of query({
  prompt,
  options: { allowedTools: ["Edit"] }
})) {
  console.log(JSON.stringify(message, null, 2));
}

Typical SDK use cases include automated code reviews, CI/CD quality checks, documentation generation, and any workflow that benefits from AI‑driven analysis.

securityhooksAI automationClaude CodePostToolUsePreToolUse
Java One
Written by

Java One

Sharing common backend development knowledge.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.