# MCP Pre-Action Checks Explained

4 min read · Technical deep-dive for developers building on MCP

TL;DR: Pre-action checks intercept tool calls before they execute. Unlike prompt rules, they cannot be ignored. This is how enforcement actually works in MCP.

## What is a pre-action check?

A pre-action check is an enforcement rule that intercepts an AI agent's tool call before it executes. If the tool call matches a known-bad pattern, the check blocks it and returns a rejection to the agent. The agent then adapts its approach without ever having run the dangerous action.

Checks run at the hook layer of the Model Context Protocol (MCP). They are external to the agent's reasoning chain, which means they cannot be overridden by prompt injection, context overflow, or chain-of-thought reasoning.

## How it works: the tool call lifecycle

Agent decides to call tool
↓
PreToolUse hook fires
↓
ThumbGate checks tool call against checks
↓ ↓
No match → ALLOW Match → BLOCK + reason
↓ ↓
Tool executes Agent receives rejection

## Prompt rules vs. pre-action checks

Property | Prompt Rules | Pre-Action Checks
--- | --- | ---
Where they live | Inside agent context | External hook layer
Can be overridden | Yes (context overflow, reasoning) | No (runs outside agent)
Enforcement | Advisory | Physical block
Persistence | Per-session (context-dependent) | Permanent (database-backed)
Adapts over time | No | Yes (Thompson Sampling)
Explains why | No | Yes (reason chain per block)

## The three layers of a check

### 1. Pattern matcher

Checks match against the tool name and its arguments. For a `Bash` tool call, the pattern might match `git push --force` targeting `main`. For a `Write` tool call, it might match writes to `.env` or `production.config`.

    {
      "tool": "Bash",
      "pattern": "git push.*(--force|-f).*main",
      "action": "BLOCK"
    }

### 2. Evidence and reasoning

Every check decision includes a reasoning chain: why this pattern exists, how many times it has fired, what the original failure was. This transparency lets you audit the system and tune it.

    {
      "check": "no-force-push-main",
      "decision": "BLOCK",
      "reason": "Force-push to main blocked",
      "evidence": "User reported loss of 14 commits (2026-03-15)",
      "fire_count": 7,
      "confidence": 0.94
    }

### 3. Thompson Sampling adaptation

Not all patterns deserve the same enforcement level. Thompson Sampling uses a beta distribution to model each check's risk profile. High-risk patterns (many failures, few successes) get strict enforcement. Low-risk patterns (rarely triggered, occasionally overridden) stay relaxed.

Why Thompson Sampling? It balances exploration vs. exploitation. New checks start with wide confidence intervals and tighten as evidence accumulates. This prevents over-blocking on sparse data while ensuring genuinely dangerous patterns get strict enforcement fast.

## How checks are created

1. Feedback capture: Developer gives thumbs-down with context about what went wrong
2. Lesson storage: Feedback is stored in SQLite with FTS5 indexing for fast retrieval
3. Corrective inference: ThumbGate matches the failure against similar past failures and infers what should be prevented
4. Rule promotion: After configurable repeated failures, the lesson promotes to a prevention rule
5. Check activation: The prevention rule becomes an active check in the PreToolUse hook

## Supported agents

Pre-action checks work with any agent that supports MCP hooks:

- Claude Code — PreToolUse hooks in `.claude/settings.json`
- Cursor — MCP server configuration in `.cursor/mcp.json`
- Codex — MCP tool interception via server config
- Gemini CLI — MCP server hooks
- Amp, OpenCode — any MCP-compatible agent

Run `npx thumbgate init` to auto-detect your agent and configure the correct hook format.

## Build your first check

Install, give your first thumbs-down, and watch the check auto-generate.

$ npx thumbgate init

### Related guides

How to Stop AI Agents From Force-Pushing to Main → The Vibe Coding Safety Net You Are Missing → Full Setup Guide →
Try it now: `npx thumbgate init` GitHub →