Add safety hooks to AI coding tools that have none.
| Path | Role |
|---|---|
project-tools/mcp-hooks-server/ |
MCP server and hook engine used by this release. |
.kilo/hooks/ |
Default hook scripts and config.yaml (portable; same shape as Claude Code hooks). |
tests/ |
Automated checks for hooks and MCP behavior. |
Your AI coding assistant can delete files, expose secrets, and run destructive commands with no guardrails. Claude Code has hooks to prevent this. Windsurf added its own Cascade Hooks in early 2026 — but those only work inside Windsurf. Cursor, Kilo Code, Aider, and Cline still have none. And none of these hook systems are portable across tools.
This MCP server exposes enforced proxy tools (safe_write, safe_edit, safe_bash, safe_read, safe_delete) that validate every operation through a configurable hook chain before executing. Connect it to any MCP-compatible AI coding tool and it adds a safer tool layer the client did not natively have.
This is not just "safer tools." It is a portable capability injection + tool-call correction + enforcement layer:
- add safer replacement tools to clients that do not have them
- fix bad inputs before a tool runs
- clean or transform bad outputs before they reach the model
- keep behavior more deterministic and predictable than prompting alone
- let the user step in with hooks, config, and manual oversight when needed
$ kilo # launch Kilo Code CLI with mcp-augment connected
Agent> safe_write .env "API_KEY=sk-..."
=> BLOCKED: Protected file (.env matches sensitive file pattern)
Agent> safe_write src/app.py "print('hello')"
=> ALLOWED: wrote src/app.py (16 bytes)
Agent> safe_delete .env
=> BLOCKED: Protected file
Agent> safe_bash "rm -rf /"
=> BLOCKED: Destructive command detected
git clone https://github.com/JoeyBe1/mcp-augment.git
cd mcp-augment
# Create and activate the virtual environment (required — all deps live here)
python3 -m venv .venv && source .venv/bin/activate
# Install all dependencies
pip install -e . # installs mcp + all deps from pyproject.toml
brew install jq # required by the default hook scripts (macOS)Note:
start-servers.shuses.venv/bin/python3automatically. Always run from within the repo root so the venv path resolves correctly.
./project-tools/mcp-hooks-server/setup.shThis does everything: starts the server (port 8200 by default, auto-finds next free port if taken), health-checks it, and writes the correct MCP URL to mcp_config.json. Re-run any time the port changes.
For stdio mode (MCP clients that support it):
python3 project-tools/mcp-hooks-server/mcp-augment.py
Kilo Code CLI — ~/.config/kilo/opencode.json
Note: Kilo CLI reads from this global path. Project-level
.kilo/kilo.jsonis ignored.
{
"mcp": {
"mcp-augment": {
"type": "remote",
"url": "http://localhost:8200/mcp",
"enabled": true
}
}
}Claude Code — .claude/settings.json
{
"mcpServers": {
"mcp-augment": {
"command": "python3",
"args": ["-u", "project-tools/mcp-hooks-server/mcp-augment.py"]
}
}
}Cursor — ~/.cursor/mcp.json (verified on macOS, 2026-04-01)
{
"mcpServers": {
"mcp-augment": {
"command": "python3",
"args": [
"-u",
"${workspaceFolder}/project-tools/mcp-hooks-server/mcp-augment-http.py",
"--stdio"
],
"env": {
"PROJECT_DIR": "${workspaceFolder}"
}
}
}
}Cursor note: the setup that was verified live uses the global Cursor config at
~/.cursor/mcp.jsonand launchesmcp-augment-http.py --stdio. In this environment, project-level.cursor/mcp.jsondid not instantiate reliably.
Windsurf — .codeium/windsurf/mcp_config.json
{
"mcpServers": {
"mcp-augment": {
"command": "python3",
"args": ["-u", "./project-tools/mcp-hooks-server/mcp-augment.py"]
}
}
}Cline — VS Code settings → Cline MCP Servers
{
"mcp-augment": {
"command": "python3",
"args": ["-u", "./project-tools/mcp-hooks-server/mcp-augment.py"],
"disabled": false
}
}Aider — HTTP mode (Aider supports MCP via HTTP):
./project-tools/mcp-hooks-server/start-servers.sh
# Then in aider: /mcp add http://localhost:8200/mcpAll harnesses share the same hook scripts (
.kilo/hooks/*.sh). The scripts are portable — they use the same stdin JSON format as Claude Code's native hook system. If a hook works in Claude Code, it works here.
mcp-augment does two things that existing MCP solutions do not:
1. Capability Injection — Creates enforced tool versions that don't exist in the host tool.
safe_write, safe_edit, safe_bash, safe_read, safe_delete replace the native equivalents.
Validation and execution are atomic in a single MCP call. The agent cannot validate then bypass.
2. Tool Call Interception — Operates at the semantic layer, before and after execution. This pattern originated from fixing bad tool calls from weaker models: a model leaves a trailing comma in JSON, you rewrite the input before it hits the tool. A web search uses last year's date, you correct the query before it executes. Then, if the output is noisy, poisoned, malformed, or just inconvenient, you can transform it before it gets back to the model. The pre-validate and post-execute hooks run at this layer, giving you full control over what actually reaches the tool and what comes back.
This is different from MCP gateways (Latch, Bifrost, mcproxy) which intercept at the transport layer between client and existing servers. And different from Claude Code's native hooks which only work inside Claude Code. mcp-augment operates at the tool execution layer and works in any MCP-compatible host, with any model.
AI Coding Tool (Kilo, Cursor, Windsurf, Cline, Aider, Claude Code...)
│
│ MCP protocol (stdio or HTTP)
│
▼
┌──────────────────────────────────────────────────┐
│ mcp-augment │
│ │
│ [PreToolUse hooks run here — can mutate input] │
│ │
│ safe_write ──► hook chain ──► write file │
│ safe_edit ──► hook chain ──► edit file │
│ safe_bash ──► hook chain ──► execute command │
│ safe_read ──► hook chain ──► read file │
│ safe_delete ──► hook chain ──► delete file │
│ │
│ [PostToolUse hooks run here — can act on output]│
│ │
│ Atomic: validate THEN execute in one call │
│ Hook chain: config.yaml ──► *.sh scripts │
└──────────────────────────────────────────────────┘
│
▼
Shell hooks (.kilo/hooks/*.sh) — portable, same format as Claude Code native hooks
block-sensitive-files.sh ← blocks .env, credentials, secrets
validate-bash-command.sh ← blocks destructive commands, sudo, force-push
mode-enforcement.sh ← research/optimize/benchmark/eval modes
auto-approve-safe.sh ← auto-approve git status, ls, cat
auto-format.sh ← post-edit formatting (async)
inject-git-context.sh ← session start context injection
| Tool | Type | Purpose |
|---|---|---|
safe_write |
Proxy (mandatory) | Validate then write file |
safe_edit |
Proxy (mandatory) | Validate then edit file |
safe_bash |
Proxy (mandatory) | Validate then execute command |
safe_read |
Proxy (mandatory) | Validate then read file |
safe_delete |
Proxy (mandatory) | Validate then delete file |
hook_event |
Core | Fire hook chain for any event |
pre_validate |
Core | Pre-operation validation |
batch_validate |
Core | Validate multiple operations |
get_hooks_config |
Config | View current hook configuration |
start_file_monitor |
Monitor | Watch file for changes |
check_file_changed |
Monitor | Check if monitored file changed |
notify_user |
Utility | Show macOS notification |
open_in_editor |
Utility | Open file in TextEdit/vim |
manage_hook |
Config | Add, remove, or list hooks at runtime |
validate_hook |
Validation | Check a hook script for compliance (exists, executable, bash syntax, stdin, silence) |
mcp-augment sits in a different place than both MCP proxy layers and Claude Code's native hooks.
- Compared with proxy / gateway layers such as Latch, Bifrost, or MCPProxy:
mcp-augmentdoes not just sit in front of existing servers. It exposes its own enforcedsafe_*tools and runs a hook chain around those tool executions. Gateways and proxies can still add policy, filtering, approval, or routing, but they are a different integration point. - Compared with Claude Code hooks: Claude already has native hooks, including pre-tool input updates and multiple handler types. Windsurf also added Cascade Hooks in early 2026.
mcp-augmentis for hosts that do not expose those native hook systems and instead need a hook-governed tool layer delivered through MCP. mcp-augmentis still soft enforcement at the host level. The model has to usesafe_*tools instead of bypassing them with native tools. The strong tool descriptions help in practice, but this is not kernel-level sandboxing.- The distinctive claim here is not "nobody else can enforce policy." The distinctive claim is that
mcp-augmentpackages pre/post tool-call correction, review-resume behavior, and enforced replacement tools into a portable MCP-delivered layer that works across clients without relying on those clients to have Claude-style native hooks.
Today, the clearest product statement is:
mcp-augment is a portable MCP-delivered pre/post tool-call correction and enforcement layer for AI clients without native hooks.
- Auto-correction (shipped):
PreToolUsehooks can emitmodifiedInput, the tool runs with the corrected arguments, then synchronousPostToolUsehooks can emitmodifiedOutputbefore the result reaches the model. This is the defaultdemo_search_backenddemo when no user edit is needed. - Advisory oversight (shipped):
notify_userandopen_in_editorcan alert the user or hand a file over for review. This is only a notification / handoff layer. It does not by itself merge the user's edits back into the samesafe_*call. - Collaborative user review-resume (shipped, macOS native UI + TextEdit fallback): this is the real same-call human-in-the-loop path. Hooks return
reviewInput(pre) orreviewOutput(post) plus optionalreviewTitle/reviewInstructions. The primary UI is a native AppleScript field picker — a formatted dialog with Accept / Edit / Decline buttons, a field selection list, and per-field edit boxes that appear in front of all other windows. TextEdit is the fallback if the native dialogs fail. The engine waits for the user, then resumes the same tool call using the edited payload. Invalid or abandoned edits fall back to the hook-proposed dict. By default it waits indefinitely; setMCP_AUGMENT_REVIEW_TIMEOUTto a positive number if you want a forced timeout, orMCP_AUGMENT_SKIP_REVIEW=1to accept the proposal without opening an editor. Tests useMCAugmentMCP.review_interactive_fnto inject edits headlessly.
In other words: there is no separate generic "manual mode" flag in the engine. The shipped collaborative/manual behavior is the reviewInput / reviewOutput review-resume path.
From the repository root (where pyproject.toml lives), run this through safe_bash in Cursor:
python3 project-tools/mcp-hooks-server/demo_search_backend.py --query "mcp augment release date 2025"Expected live result after the mcp-augment MCP server has been restarted:
- pre-hook rewrites
2025->2026 - backend runs with corrected query
- post-hook removes
INTERNAL_DEBUG - post-hook prepends
[POST-HOOK FILTERED] - optional notification hook alerts the user that output was post-processed
Use the same backend but include the substring REVIEW_DEMO in the shell command so the auto pre/post demo hooks skip and the review hooks run instead:
python3 project-tools/mcp-hooks-server/demo_search_backend.py --query "mcp augment release date REVIEW_DEMO 2025"Flow:
pre-review-search-query.shemitsreviewInput(proposed command with2026). TextEdit opens and waits for the user; save and close to continue.post-shape-search-output.shis skipped whenREVIEW_DEMOis present.post-review-search-output.shemitsreviewOutputfor stdout shaping; another TextEdit pass waits for the user the same way.- Final stdout matches the auto demo when proposals are accepted.
Verified behavior: the two TextEdit review windows are sequential. The second review does not open until the first review file has been saved/closed and merged back into the same safe_bash call.
Hooks are configured in .kilo/hooks/config.yaml:
hooks:
PreToolUse:
- matcher: "Edit|Write|MultiEdit|delete_file"
hooks:
- type: command
command: ".kilo/hooks/block-sensitive-files.sh"
timeout: 10
- type: command
command: ".kilo/hooks/mode-enforcement.sh"
timeout: 10
- matcher: "Bash"
hooks:
- type: command
command: ".kilo/hooks/validate-bash-command.sh"
timeout: 10
PostToolUse:
- matcher: "Write|Edit|MultiEdit"
hooks:
- type: command
command: ".kilo/hooks/auto-format.sh"
async: trueHooks are shell scripts that:
- Read JSON from stdin (
tool_name,tool_input,cwd) - Exit 0 to allow, exit 2 to block
- Optionally output JSON with
permissionDecisionReason,modifiedInput,modifiedOutput, or review-resume fields (reviewInput,reviewOutput,reviewTitle,reviewInstructions)
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.env* ]]; then
echo '{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"Protected file"}}'
exit 2
fi
exit 0These hooks are portable -- the same script works in Claude Code's native hook system AND through mcp-augment in any other tool.
- Custom hooks: yes — write shell scripts and register them with
manage_hook - Custom workflows: yes — use
hook_event,pre_validate,batch_validate, monitors, and hook config to shape runtime behavior - Custom tool behavior: yes — via pre/post interception around the existing
safe_*tools - Custom
prompt/agenthook handlers: not yet — documented as planned, not shipped
Install the launchd plist for automatic server startup:
PROJECT_DIR="$(pwd)"
sed "s|__PROJECT_DIR__|$PROJECT_DIR|g" \
project-tools/mcp-hooks-server/com.mcp-augment.plist \
> ~/Library/LaunchAgents/com.mcp-augment.plist
launchctl load ~/Library/LaunchAgents/com.mcp-augment.plistVerify:
lsof -i :8200 # mcp-augment hooks server# Run the full test suite (38 tests, no server required)
python -m pytest tests/ -qExpected: 38 passed.
Near-term:
- Rate limiting (RPM enforcement — token bucket in state file, configurable per project)
- macOS Seatbelt / sandbox-style integration for
safe_bash(exploratory; not MVP) - Provider/harness routing (dispatch different tools to different backends via config)
- tmux multi-agent support (macOS — spawn named sessions, send keys, capture output)
doctortool (config hygiene: hook compliance check, log path validation, tool swap e.g. grep→ripgrep)- Cross-harness
manage_hook— write tosettings.json(Claude Code) in addition to.kilo/hooks/config.yaml
Tool wrappers (project-tools/ — stubs exist, implementations pending):
- ripgrep, jq, ast-grep as first-class MCP tools
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure
python3 -m pytest tests/ -qpasses - Submit a pull request
MIT -- see LICENSE