Hooks
- Hooks are user-defined handlers that run at deterministic points in an agent’s lifecycle
- Use cases
- Observability
- Guardrails
- Automation like formatting/linting/type-checks/testing
Location of Hooks
- Claude Code:
.claude/settings.json
- GitHub Copilot:
.github/hooks/NAME.json
Hooks Structure
- Not Universal
- Copilot: https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-hooks
- General Events
- Session Start
- User Prompt Submit
- Pre-Tool Use
- Post-Tool Use
- Stop
- Session End
- Error Occurred
- Use cases
- Log Tool call: Pre-Tool Use
- Run Linter after edit: Post-Tool Use
- Block dangerous commands: Pre-Tool Use
- Inject repo context: User Prompt Submit
- Persist conversation state: Stop/Session End
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "./scripts/pre-tool-log-raw.sh",
"powershell": "./scripts/pre-tool-log-raw.ps1",
"cwd": ".github/hooks",
"timeoutSec": 10
}
],
"postToolUse": [
{
"type": "command",
"bash": "./scripts/post-tool-log-raw.sh",
"powershell": "./scripts/post-tool-log-raw.ps1",
"cwd": ".github/hooks",
"timeoutSec": 10
}
]
}
}
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HOOK_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
LOG_DIR="$HOOK_ROOT/logs"
RAW_LOG_FILE="$LOG_DIR/pre-tool-use-raw.jsonl"
mkdir -p "$LOG_DIR"
chmod 700 "$LOG_DIR"
# Log raw hook payload exactly as received on stdin.
printf '%s\n' "$INPUT" >> "$RAW_LOG_FILE"
exit 0
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
HOOK_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
LOG_DIR="$HOOK_ROOT/logs"
RAW_LOG_FILE="$LOG_DIR/post-tool-use-raw.jsonl"
mkdir -p "$LOG_DIR"
chmod 700 "$LOG_DIR"
# Log raw hook payload exactly as received on stdin.
printf '%s\n' "$INPUT" >> "$RAW_LOG_FILE"
exit 0
Windows
$ErrorActionPreference = "Stop"
$inputRaw = [Console]::In.ReadToEnd()
$hookRoot = Split-Path -Parent $PSScriptRoot
$logDir = Join-Path $hookRoot "logs"
$rawLogFile = Join-Path $logDir "pre-tool-use-raw.jsonl"
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
# Log raw hook payload exactly as received on stdin.
Add-Content -Path $rawLogFile -Value $inputRaw
exit 0
$ErrorActionPreference = "Stop"
$inputRaw = [Console]::In.ReadToEnd()
$hookRoot = Split-Path -Parent $PSScriptRoot
$logDir = Join-Path $hookRoot "logs"
$rawLogFile = Join-Path $logDir "post-tool-use-raw.jsonl"
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
# Log raw hook payload exactly as received on stdin.
Add-Content -Path $rawLogFile -Value $inputRaw
exit 0
Observations on Logs
- All MCP related tools have naming syntax
mcp_${server_name}_${tool_name}
- For example
mcp_playwright_browser_tabs
mcp_playwright_browser_navigate
mcp_playwright_browser_click
- For all Playwright MCP tools refer: MCP_Playwright
- These are internal tools which Github Copilot (VSCode) has
todo
- Manage and track items for Task planning
browser
- Open and interact with integrated browser pages
web
- Fetch information from website or GitHub
edit (Edit files)
read (Read files)
search (Search files)
execute (Execute code and applications)
vscode
- Use VSCode features/extensions/API
- Ask structured questions to clarify on agent run
agent
- Delegate Tasks to other agents
- Example internal tool names from logs
run_in_terminal
navigate_page
open_browser_page
type_in_page
click_element
read_page
read_file