Pre Tool Gate
БесплатноНе проверенPre-execution tool validation, failure prevention hints, dynamic skill selection, and agent dispatch gate MCP server.
Описание
Pre-execution tool validation, failure prevention hints, dynamic skill selection, and agent dispatch gate MCP server.
README
mcp-pre-tool-gate
A FastMCP server that acts as a pre-execution policy enforcement gate for Claude Code tool calls. Before any file-modification tool (Write, Edit, NotebookEdit) executes, mcp-pre-tool-gate runs 8 sequential policy checks — verifying checkpoint state, task breakdown, skill selection, Level 1 sync completion, Level 2 standards completion, and platform safety rules — and returns an allow or block decision with actionable error messages. It also injects dynamic skill context hints based on file extension, matches tool call patterns against a known failure knowledge base to prevent common mistakes before they cascade, and validates that a general-purpose subagent dispatch carries a real, skill-carrying library persona. This server is one of 13 MCP servers in the Claude Workflow Engine ecosystem.
No live hook triggers any of these tools. claude-workflow-engine's ADR-006 (Hook-Free Execution Model, executed 2026-08-04) removed the PreToolUse/PostToolUse/UserPromptSubmit hook registrations from ~/.claude/settings.json to eliminate their per-tool-call latency. Every tool in this server — including validate_tool_call and validate_agent_dispatch — is explicitly invoked by the orchestrating Claude Code session as a deliberate, on-demand self-check, not triggered automatically by any live hook. Enforcement is opt-in: a session that never calls these tools gets no policy checks. See the "How It Works" section below for what an explicit call looks like in practice.
Table of Contents
- Features
- How It Works
- The 8 Policy Checks
- Tool Reference
- Installation
- Configuration
- Invocation Model
- Usage Examples
- Project Structure
- Part of Claude Workflow Engine
- Contributing
- License
Features
- 8-check policy pipeline — sequential validation covering checkpoint state, task workflow, pipeline sync, and platform safety
- Fail-fast enforcement — blocks Write / Edit / NotebookEdit before they execute, not after
- Fail-open for read tools — Read, Grep, Glob, WebFetch, WebSearch, and Task are always allowed; they never pass through the block checks
- Dynamic skill hints — maps file extensions to the appropriate skill/agent from the global library (
.java→java-spring-boot-microservices,.py→python-core, etc.) - Failure KB pattern matching — scans a JSON knowledge base of known failure patterns and returns non-blocking, actionable hints (e.g., Edit old_string contains line number prefix; Grep missing head_limit)
- Windows platform safety — blocks Windows-only shell commands in Bash (del, copy, xcopy, dir, move, etc.) and blocks non-ASCII content in
.pyfiles (cp1252 safety) - Session-scoped flag system — enforcement flags are scoped to the active session ID and auto-expire via configurable TTL (default 90 seconds) and age limits (default 60 minutes)
- Flow-trace awareness — reads
flow-trace.jsonto verify Level 1 sync and Level 2 standards pipeline steps completed; fails open if no trace is found - Agent-persona dispatch validation —
validate_agent_dispatchchecks a proposed general-purposeAgent/Taskdispatch for a real---persona---block, that its declaredskills:are actually carried (matchingskills/<name>/SKILL.mdread instructions in the prompt body), and — when aclaude-global-libraryknowledge-graph/_master/agents_all.jsonregistry can be resolved — that the declared skill set matches the named agent's real mandatory set. Fail-open by design when the registry can't be found; escape hatches ([GENERIC-OK], skill/agent-authoring auto-exemption) preserved from the original hook-era policy - Zero-configuration startup — paths resolved automatically via
path_resolver.py; no required environment variables for basic operation - FastMCP stdio transport — standard MCP JSON-RPC over stdio; works with any MCP-compatible client
How It Works
There is no live hook wired into this server's path — every tool call below is something the orchestrating Claude Code session decides to make explicitly, as a self-check, before or after it takes some other action. See Invocation Model for why.
Orchestrating session is about to call a tool (e.g., Write, Edit, Bash)
and explicitly chooses to self-check first by calling mcp-pre-tool-gate
|
v
validate_tool_call(tool_name, tool_input)
|
+-- tool in ALWAYS_ALLOWED (Read/Grep/Glob/WebFetch/WebSearch/Task)?
| |
| YES --> allowed: true, return immediately (no checks)
|
+-- tool in BLOCKED_TOOLS (Write/Edit/NotebookEdit)?
| |
| YES --> run checks 1-6 sequentially
|
+-- tool == "Bash" on Windows?
| YES --> run check 7 (Windows command blocking)
|
+-- tool in (Write/Edit) on Windows?
YES --> run check 8 (Python Unicode blocking)
|
v
allowed: true --> session proceeds with the tool call it was about to make
allowed: false --> session sees the block reason and adjusts before proceeding
Optimization hints (Grep head_limit, Read offset/limit for large files) are appended as non-blocking hints regardless of the allow/block decision.
A second, independent self-check follows the same explicit-call model for agent dispatches:
Orchestrating session is about to spawn a general-purpose Agent/Task subagent
and explicitly chooses to self-check first by calling mcp-pre-tool-gate
|
v
validate_agent_dispatch(subagent_type, prompt, library_path)
|
+-- subagent_type is a named built-in (e.g. Explore, Plan)?
| YES --> allowed: true, no persona required
|
+-- prompt contains [GENERIC-OK], or targets authoring a new
| skills/{name}/SKILL.md or agents/{name}/agent.md?
| YES --> allowed: true (escape hatch / auto-exemption)
|
+-- '---persona---' block present, well-formed, 'skills:' non-empty,
| every declared skill has a matching 'skills/<name>/SKILL.md'
| read instruction in the prompt body?
| NO --> allowed: false, blocks explain what is missing
|
+-- agents_all.json registry resolvable (library_path param ->
GLOBAL_LIBRARY_PATH env var -> 'WORKING DIRECTORY & LIBRARY
PATH:' line in the prompt) and the named agent found in it?
YES --> declared skills must cover every real mandatory +
cross-domain-mandatory skill, or allowed: false
NO --> fail-open, allowed: true on the checks above alone
|
v
allowed: true --> session proceeds with the dispatch
allowed: false --> session fixes the prompt per `blocks`/`detail` and re-checks
The 8 Policy Checks
All checks run in order. The first failing check short-circuits the remainder (except checks 7 and 8, which are independent platform checks applied to their respective tool types).
| # | Check | Level | Trigger Condition | Blocked Tools |
|---|---|---|---|---|
| 1 | Checkpoint pending | Level 3.3 | checkpoint-pending flag exists for session |
Write, Edit, NotebookEdit |
| 2 | Task breakdown pending | Level 3.1 | task-breakdown-pending flag active (TTL 90s) |
Write, Edit, NotebookEdit |
| 3 | Skill selection pending | Level 3.5 | skill-selection-pending flag active (TTL 90s) |
Write, Edit, NotebookEdit |
| 4 | Context read complete | Level 3.0 | LEVEL_1_CONTEXT step absent from flow-trace |
Write, Edit, NotebookEdit |
| 5 | Level 1 sync complete | Level 1 | LEVEL_1_SESSION step absent from flow-trace |
Write, Edit, NotebookEdit |
| 6 | Level 2 standards complete | Level 2 | LEVEL_2_STANDARDS step absent from flow-trace |
Write, Edit, NotebookEdit |
| 7 | Bash Windows command blocking | Level 3.7 | Command starts with a Windows-only shell command | Bash (Windows only) |
| 8 | Python Unicode blocking | Level 3.7 | Non-ASCII content written to a .py file |
Write, Edit (Windows only) |
Fail-open behaviour for checks 4-6: If no flow-trace.json exists for the current session, these three checks are skipped and the tool call is allowed. This prevents blocking during the very first pipeline run before any trace has been written.
Flag TTL: Flags 2 and 3 use a short TTL (90 seconds by default). Flags older than CHECKPOINT_MAX_AGE_MINUTES (60 minutes) are auto-deleted on access.
Tool Reference
| Tool | Description | Key Parameters |
|---|---|---|
validate_tool_call |
Run all 8 policy checks and return allow/block decision with reason and hints | tool_name (str, required), tool_input (JSON string, default {}) |
check_task_breakdown |
Check whether the task-breakdown-pending flag is active for the current session | None |
check_skill_selected |
Check whether the skill-selection-pending flag is active and return the required skill name | None |
check_level_completion |
Inspect flow-trace to verify Level 1 and/or Level 2 pipeline steps completed | level (str: "level1" | "level2" | "all", default "all") |
get_enforcer_state |
Snapshot of all enforcement flags, pipeline completion status, blocked tool sets, and always-allowed tool sets | None |
check_failure_patterns |
Match a tool call against known failure patterns (from failure-kb.json) and return non-blocking hints |
tool_name (str, required), tool_input (JSON string, default {}) |
get_dynamic_skill_hint |
Return the suggested skill/agent name based on a file's extension | file_path (str, required) |
reset_enforcer_flags |
Delete enforcement flag files for the current session | flag_name (str: "task-breakdown-pending" | "skill-selection-pending" | "checkpoint-pending" | "all", default "all") |
validate_agent_dispatch |
Validate a proposed general-purpose Agent/Task dispatch for a real, skill-carrying library persona; returns {"allowed", "reason", "blocks", "detail"} |
subagent_type (str, default ""), prompt (str, default ""), library_path (str, optional GLOBAL_LIBRARY_PATH override, default "") |
File Extension to Skill Mapping
get_dynamic_skill_hint uses the following built-in mapping:
| Extension / Filename | Suggested Skill |
|---|---|
.java |
java-spring-boot-microservices |
.kt |
kotlin-core |
.py |
python-core |
.ts |
typescript-core |
.tsx, .jsx |
react-core |
.js |
javascript-core |
.swift |
swiftui-core |
.xml |
android-xml-ui |
.css, .scss |
css-core |
.html |
html5-core |
.sql |
rdbms-core |
.yml |
docker |
.yaml |
kubernetes |
.tf |
kubernetes |
.json |
json-core |
.graphql |
graphql-core |
Dockerfile, *.dockerfile |
docker |
Jenkinsfile |
jenkins-pipeline |
Installation
1. Clone the repository
git clone https://github.com/techdeveloper-org/mcp-pre-tool-gate.git
cd mcp-pre-tool-gate
2. Install dependencies
pip install -r requirements.txt
The only runtime dependencies are mcp>=1.0.0 and fastmcp>=0.1.0.
3. Verify startup
python server.py
The server starts and waits for JSON-RPC messages on stdin. No output is expected at startup. Press Ctrl+C to stop.
Configuration
Environment Variables
No environment variables are required for basic operation. The server resolves all paths automatically via utils/path_resolver.py.
| Variable | Default | Description |
|---|---|---|
CLAUDE_CONFIG_DIR |
~/.claude |
Base config directory for session files and flag files |
Flag File Locations
Enforcement flags are written by the pre-tool-enforcer.py hook (or the mcp-pre-tool-gate consumer). The server reads from two locations (new takes precedence over legacy):
| Location | Path Pattern |
|---|---|
| New (per-session) | ~/.claude/logs/sessions/{session_id}/flags/{flag_name}.json |
| Legacy | ~/.claude/.{flag_name}-{session_id}.json |
Flow Trace Location
~/.claude/logs/sessions/{session_id}/flow-trace.json
The server reads the last entry in this file (if it is a list) or the entire document (if it is an object) to check pipeline step completion.
TTL Constants (source-level)
| Constant | Default | Purpose |
|---|---|---|
CHECKPOINT_MAX_AGE_MINUTES |
60 | Maximum age for any flag before auto-deletion |
FLAG_TTL_SECONDS |
90 | TTL for task-breakdown and skill-selection flags |
Invocation Model
mcp-pre-tool-gate registers as an MCP server (~/.claude/settings.json mcpServers) but is not wired to any Claude Code hook event. Register it like any other MCP server:
{
"mcpServers": {
"pre-tool-gate": {
"command": "python",
"args": ["/absolute/path/to/mcp-pre-tool-gate/server.py"],
"env": {}
}
}
}
Why no hook. claude-workflow-engine previously ran this same policy logic as a synchronous PreToolUse hook — a Python process spawn on every single tool call, matcher: "", 60s timeout, async: false (per the user's global instruction that all hooks must be synchronous). Combined with a PostToolUse hook on the same pattern, that was up to 120s of blocking latency before Claude ever saw a prompt, and a hook crash blocked the tool call outright. ADR-006 (Hook-Free Execution Model) removed the PreToolUse/PostToolUse/UserPromptSubmit registrations entirely (executed 2026-08-04) to eliminate that latency, and named "re-implement the check as an explicitly-invoked MCP tool" as the precedent pattern for restoring a check's logic without restoring the hook.
What that means in practice: every tool in this server — validate_tool_call, validate_agent_dispatch, and the rest — is a deterministic, pipe-testable self-check the orchestrating session chooses to call, not something that fires on its own. There is no live mechanism forcing that call to happen before a given Write/Edit/Agent/Task call; a session can simply not call it. What this server upgrades is "trust the policy was followed" to "a concrete tool call exists that will catch a violation if you make it" — it is a callable self-check, not a hard gate, and enforcement stays opt-in (the consequence ADR-006 states explicitly: "enforcement becomes opt-in. Policies do not apply on sessions where the plugin is never invoked").
Example flow (agent dispatch):
Orchestrating session, following ORCHESTRATION_TEMPLATE.md STEP 0.05's
Subagent Dispatch Contract, is about to call Agent/Task with
subagent_type="general-purpose"
|
v
Session explicitly calls mcp__pre-tool-gate__validate_agent_dispatch(
subagent_type, prompt)
|
+-- allowed: true -> session proceeds with the Agent/Task call
|
+-- allowed: false -> session fixes the prompt per `blocks`/`detail`,
then re-checks before dispatching
Usage Examples
Example 1: Write allowed after all checks pass
// Request
{
"tool": "validate_tool_call",
"arguments": {
"tool_name": "Write",
"tool_input": "{\"file_path\": \"src/service.py\", \"content\": \"# service code\"}"
}
}
// Response
{
"allowed": true,
"tool": "Write",
"session_id": "session-20260414-143200",
"hints": [],
"blocks": [],
"checks_run": 8,
"reason": "All checks passed"
}
Example 2: Write blocked — task breakdown pending
// Request
{
"tool": "validate_tool_call",
"arguments": {
"tool_name": "Write",
"tool_input": "{\"file_path\": \"src/models.py\", \"content\": \"class User: pass\"}"
}
}
// Response
{
"allowed": false,
"tool": "Write",
"session_id": "session-20260414-143200",
"hints": [],
"blocks": ["Task breakdown pending - call TaskCreate before Write"],
"checks_run": 8,
"reason": "Task breakdown pending - call TaskCreate before Write"
}
Example 3: Bash blocked on Windows — del command
// Request
{
"tool": "validate_tool_call",
"arguments": {
"tool_name": "Bash",
"tool_input": "{\"command\": \"del /f /q build\\\\output.txt\"}"
}
}
// Response
{
"allowed": false,
"tool": "Bash",
"session_id": "session-20260414-143200",
"hints": [],
"blocks": ["Windows command 'del' blocked - use Unix equivalent"],
"checks_run": 2,
"reason": "Windows command 'del' blocked - use Unix equivalent"
}
Example 4: Read is always allowed (no checks run)
// Request
{
"tool": "validate_tool_call",
"arguments": {
"tool_name": "Read",
"tool_input": "{\"file_path\": \"src/main.py\"}"
}
}
// Response
{
"allowed": true,
"tool": "Read",
"hints": [],
"blocks": [],
"reason": "Tool is always allowed"
}
Example 5: Skill hint for a TypeScript file
// Request
{
"tool": "get_dynamic_skill_hint",
"arguments": {
"file_path": "src/components/UserCard.tsx"
}
}
// Response
{
"success": true,
"file": "src/components/UserCard.tsx",
"extension": ".tsx",
"suggested_skill": "react-core",
"has_suggestion": true
}
Example 6: Agent dispatch blocked — no persona injected
// Request
{
"tool": "validate_agent_dispatch",
"arguments": {
"subagent_type": "general-purpose",
"prompt": "go research competitor pricing"
}
}
// Response
{
"allowed": false,
"reason": "[PRE-TOOL BLOCKED] Generic subagent spawned without a library persona!",
"blocks": ["[PRE-TOOL BLOCKED] Generic subagent spawned without a library persona!\n Tool : Agent/Task spawn with subagent_type=general-purpose (or unset)\n ..."],
"detail": "[PRE-TOOL BLOCKED] Generic subagent spawned without a library persona!\n ..."
}
Example 7: Full enforcer state snapshot
// Request
{
"tool": "get_enforcer_state",
"arguments": {}
}
// Response
{
"success": true,
"session_id": "session-20260414-143200",
"flags": {
"checkpoint_pending": false,
"task_breakdown_pending": false,
"skill_selection_pending": true,
"required_skill": "python-core"
},
"pipeline": {
"trace_exists": true,
"level1_complete": true,
"level2_complete": false
},
"blocked_tools": ["Edit", "NotebookEdit", "Write"],
"always_allowed": ["Glob", "Grep", "Read", "Task", "WebFetch", "WebSearch"]
}
Project Structure
mcp-pre-tool-gate/
├── server.py # FastMCP server — 9 tools, 8 policy checks + agent-persona dispatch validation
├── input_validator.py # Input sanitization (null-byte strip, length limits)
├── rate_limiter.py # Token bucket rate limiter (optional enforcement)
├── mcp_errors.py # MCP-specific error types
├── requirements.txt # Runtime dependencies (mcp, fastmcp)
├── requirements-dev.txt # Dev/test dependencies (pytest), on top of requirements.txt
├── tests/ # pytest suite
│ ├── conftest.py # sys.path setup so tests can `import server`
│ └── test_agent_persona_gate.py # validate_agent_dispatch coverage
├── base/ # Copy of mcp-base shared library
│ ├── response.py # MCPResponse builder + to_json helper
│ ├── decorators.py # @mcp_tool_handler decorator
│ └── persistence.py # SessionIdResolver
└── CLAUDE.md # Project-local Claude Code instructions
The base/ directory is a copy of the mcp-base shared library. Each MCP server in the ecosystem includes its own copy to remain independently deployable.
Part of Claude Workflow Engine
mcp-pre-tool-gate is one of 13 MCP servers in the Claude Workflow Engine ecosystem — a LangGraph orchestration pipeline that automates Claude Code development workflows across planning, implementation, review, and release phases.
| # | Server | Purpose |
|---|---|---|
| 1 | mcp-session-mgr | Session lifecycle management |
| 2 | mcp-git-ops | Git operations (branch, commit, push, diff) |
| 3 | mcp-github-api | GitHub PR, issue, and merge operations |
| 4 | mcp-policy-enforcement | Policy compliance and module health checks |
| 5 | mcp-token-optimizer | Token reduction via AST navigation (60-85% savings) |
| 6 | mcp-pre-tool-gate | Pre-tool validation — this server |
| 7 | mcp-post-tool-tracker | Post-tool progress tracking and commit readiness |
| 8 | mcp-standards-loader | Standards loading with hot-reload |
| 9 | mcp-uml-diagram | UML diagram generation (13 types) |
| 10 | mcp-drawio-diagram | Draw.io editable diagram export |
| 11 | mcp-jira-api | Jira issue lifecycle management |
| 12 | mcp-jenkins-ci | Jenkins CI/CD trigger and monitoring |
| 13 | mcp-figma | Figma design token and component extraction |
Main pipeline: claude-workflow-engine Shared base library: mcp-base
Contributing
Contributions are welcome. Please follow these guidelines:
- Fork the repository and create a feature branch from
main. - Keep
server.pyfocused on the policy checks and tools it already owns — do not add unrelated functionality. - If you add a new policy check, add it as check #N in the
validate_tool_calldocstring and update the check counter in the return value. - All new tools must use the
@mcp_tool_handlerdecorator frombase/decorators.py. - Do not add runtime dependencies beyond
mcpandfastmcpwithout discussion (test-only dependencies likepytestbelong inrequirements-dev.txt, notrequirements.txt). - Run
pytest tests/and test your changes by calling the relevant tool explicitly from a live Claude Code session (see Invocation Model — there is no hook to configure). - Submit a pull request with a clear description of the change and the problem it solves.
License
MIT License. See LICENSE for full text.
Copyright (c) 2026 techdeveloper-org
Установка Pre Tool Gate
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/techdeveloper-org/mcp-pre-tool-gateFAQ
Pre Tool Gate MCP бесплатный?
Да, Pre Tool Gate MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Pre Tool Gate?
Нет, Pre Tool Gate работает без API-ключей и переменных окружения.
Pre Tool Gate — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Pre Tool Gate в Claude Desktop, Claude Code или Cursor?
Открой Pre Tool Gate на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectServer Sequential Thinking
MCP server for sequential thinking and problem solving
автор: GitHub ActionsUnyly Deploy
Deploy sites and MCP servers to Unyly straight from your AI agent
автор: UnylyAmap Maps Mcp Server
MCP server for using the AMap Maps API
автор: duxiaohuiPayMCP
Lightweight payments layer for MCP servers: turn tools into paid endpoints with a two-line decorator. [PyPI](https://pypi.org/project/paymcp/) · [npm](https://w
автор: blustAIServer For Power BI
Enables exploring Microsoft Fabric / Power BI workspaces and semantic models, and executing ad-hoc DAX queries.
автор: Mitsubishi-FusoGpt Image 2 Mcp
RunAPI GPT Image 2 MCP server for image generation: create tasks, poll results, and check pricing across 1 model from Claude Code, Codex, Cursor, and VS Code.
автор: runapi-aiCompare Pre Tool Gate with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
