constraint-mcp

CLAUDE.md rules are wishes. constraint-mcp is the law.

Hard enforcement for AI coding agents. SPEC.md rules that actually can't be broken.
Same task, same model. With just a CLAUDE.md soft rule, the agent writes SQL into your API layer and reverts auth into a billing service — and nothing stops it. With constraint-mcp, both are blocked at the gate, with the exact rule and similarity score, and the agent self-corrects.
The Problem
CLAUDE.md and system prompts are soft rules. They work by asking the model nicely.
In practice:
- The best frontier models follow fewer than 30% of prompt-level instructions in multi-step agent scenarios (AgentIF benchmark, NeurIPS 2025)
- Claude Code had a documented security bypass where deny rules in
.claude.json were silently skipped when commands exceeded an internal limit (patched in v2.1.90, April 2026)
- Every CLAUDE.md is one context-window flush away from being forgotten
The gap is architectural: rules live in prose and enforcement lives nowhere.
constraint-mcp closes that gap. Rules are enforced at the tool level, via AST analysis, before writes even happen. The model cannot write a file without calling check_write() — and check_write() runs the actual code check, not a vibe check.
How It Works
Agent wants to write a file
│
▼
check_write(filepath, content)
│
▼
AST parse + rule checks
┌─────────────────────┐
│ Banned imports? │
│ Protected file? │
│ Layer violation? │
└─────────────────────┘
│
┌────┴────┐
│ │
approved violation
│ │
▼ ▼
file is violation injected
written into context window
(write blocked)
The violation message is formatted to be unambiguous in an LLM context: it names the exact rule, the line, and the required fix so that the model knows exactly why the tool was blocked.
Structural (AST) enforcement in action — banned imports, protected files, and cross-layer rules:

Quick Start
# 1. Install
pip install constraint-mcp
# 2. Create your SPEC.md in the project root (see format below)
# 3. Add to .mcp.json
cat > .mcp.json << 'EOF'
{
"mcpServers": {
"constraint-mcp": {
"command": "constraint-mcp",
"args": [],
"env": { "CONSTRAINT_MCP_SPEC": "./SPEC.md" }
}
}
}
EOF
# 4. Start Claude Code — constraint-mcp loads automatically
claude
Install as a Claude Code plugin
Prefer one command instead of hand-editing .mcp.json? Install straight from the marketplace:
# In any Claude Code session:
/plugin marketplace add Christopher-Anandaraj/ConstraintMCP
/plugin install constraint-mcp@constraint-mcp
The plugin ships the MCP server config for you. You still need the constraint-mcp
command on your PATH (it runs the actual checks), so install the package once:
pip install constraint-mcp
Then drop a SPEC.md in your project root and restart Claude Code.
SPEC.md Format
## Constraints
### Banned Imports
- No `requests` — use `httpx` only
- No `pickle` anywhere in `src/`
- No `os.system` — use `subprocess` only
### Protected Files
- `src/core/auth.py` is read-only
- `config/prod.yaml` must never be modified
- `src/core/` directory is locked
### Architecture Rules
- Files in `src/api/` must never import from `src/db/` directly
- Files in `src/api/` must never import from `src/models/` directly
Place SPEC.md in your repo root (or set CONSTRAINT_MCP_SPEC env var to a custom path). The server hot-reloads whenever SPEC.md changes — no restart needed.
Semantic Constraints (meaning-level enforcement)
AST rules catch structure — imports, filepaths, layer boundaries. They can't catch meaning: "this file in src/auth/ is actually doing database work," or "this change quietly turned a utility into something else." Semantic constraints close that gap using embedding similarity (local, CPU-only, no API calls).

## Semantic Constraints
### Domain Coherence
- `src/auth/` — must match domain: "authentication, JWT tokens, sessions, login, credentials"
threshold: 0.45
### Semantic Coupling Bans
- `src/api/` — must not contain: "SQL queries, database connections, ORM, cursor, raw query"
threshold: 0.58
### Semantic Drift
- `src/core/` — baseline: auto, max-drift: 0.30
- `src/core/auth.py` — baseline: locked, max-drift: 0.15