A transparent proxy between your AI agent and its MCP server. Define policies in YAML. Every tool call is evaluated, logged, and governed before it reaches upstream.
Install Read the docs$ intercept -c policy.yaml \ -- npx -y server-github # Agent calls delete_repository... [INTERCEPT POLICY DENIED] Repository deletion is not permitted via AI agents # Agent calls create_issue (6th)... [INTERCEPT POLICY DENIED] Hourly limit of 5 new issues reached
Agents call tools with no spending limits, no rate limits, no access control. Prompt-based safety is probabilistic—it works until it doesn't. There is no hard stop.
Transparent proxy at the MCP transport layer. YAML policies compiled to deterministic rules. Stateful evaluation with sliding-window counters. Blocked before it reaches upstream.
Rate limits, spend caps, argument validation, unconditional blocks. Shadow mode to observe before enforcing. The agent cannot reason around it.
From the policy reference. Every snippet runs as-is.
| Server | Rule | Policy YAML |
|---|---|---|
GitHub |
Block repo deletion | delete_repository:
rules:
- name: "block repo deletion"
action: "deny"
on_deny: "Repo deletion not permitted" |
GitHub |
Rate-limit issue creation | create_issue:
rules:
- name: "hourly issue limit"
rate_limit: 5/hour
on_deny: "Hourly limit of 5 new issues reached" |
Stripe |
Daily spend cap ($10k) | create_charge:
rules:
- name: "daily spend cap"
conditions:
- path: "state.create_charge.daily_spend"
op: "lte"
value: 1000000
on_deny: "Daily spending cap of $10,000.00 reached"
state:
counter: "daily_spend"
window: "day"
increment_from: "args.amount" |
Stripe |
Currency restriction | create_charge:
rules:
- name: "allowed currencies"
conditions:
- path: "args.currency"
op: "in"
value: ["usd", "eur"]
on_deny: "Only USD and EUR charges are permitted" |
Any |
Global rate limit | "*":
rules:
- name: "global rate limit"
rate_limit: 60/minute |
Any |
Allowlist mode | version: "1"
default: deny # only listed tools are permitted
tools:
read_file:
rules: [] # allowed, no additional checks
create_issue:
rules:
- name: "hourly limit"
rate_limit: 5/hour |
Scan discovers your tools. You write the rules. Intercept enforces them.
Connect to any MCP server and generate a policy scaffold. Every tool is listed with its parameters as comments.
intercept scan -o policy.yaml \ -- npx -y server-github
Add rules to the generated YAML. Rate limits, spend caps, unconditional blocks. The scaffold shows you what's available.
delete_repository:
rules:
- name: "block deletion"
action: "deny"
on_deny: "Not permitted"
Start the proxy. It launches the upstream server, proxies all MCP traffic, and enforces your policy on every tool call.
intercept -c policy.yaml \ -- npx -y server-github
One proxy. Every tool call evaluated before reaching upstream.
Agent sends a tool call. Intercept captures it at the MCP transport layer before it reaches the server.
Policy engine checks the call against YAML-defined rules, conditions, and stateful counters.
Allow: forward to upstream server. Deny: return the reason to the agent. No ambiguity.
Update counters, log the decision. If upstream fails, roll back the increment automatically.
SQLite by default—zero config, works out of the box. Redis for shared state across multiple Intercept instances. Key prefixing for namespace isolation.
Every decision logged as newline-delimited JSON. Tool name, result, rule name. Arguments hashed for privacy. 7-day auto-retention.
Intercept watches the policy file. Save and new rules take effect immediately. Invalid config is rejected—previous rules stay active. Counters are never reset.
Point your .mcp.json at Intercept instead of the server. Works with Claude Code, Claude Desktop, or any MCP client.
Intercept launches the server as a child process and proxies stdin/stdout.
{
"mcpServers": {
"github": {
"command": "intercept",
"args": [
"-c", "policy.yaml",
"--",
"npx", "-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "..."
}
}
}
}
Intercept bridges stdio to HTTP. The agent spawns Intercept; Intercept talks to the remote server.
{
"mcpServers": {
"stripe": {
"command": "intercept",
"args": [
"-c", "policy.yaml",
"--upstream",
"https://mcp.stripe.com",
"--header",
"Authorization: Bearer tok"
]
}
}
}
Prompts are probabilistic. The model can ignore, misinterpret, or be injected past them. Intercept is deterministic—the tool call is physically blocked at the wire before it reaches the server.
Fail-closed by default. Tool calls requiring state evaluation are denied until the backend recovers. Configurable with --state-fail-mode open for non-critical setups.
Intercept is fully transparent. The agent sees the same tools, same names, same behaviour. It only becomes aware when a policy denies a call—it receives a denial message explaining why.
Yes. Intercept watches the policy file and hot-reloads on save. New rules take effect immediately. Invalid config is rejected; previous config stays active. Counters are never reset.
Set default: deny at the top of your policy. Only tools explicitly listed are permitted—everything else is rejected before rules are even evaluated.
Stdio with child process, stdio bridged to a remote HTTP server (--upstream), and HTTP/SSE. Transport is auto-detected from flags. See the CLI reference.
go install github.com/policylayer/intercept@latest
Then: intercept scan -o policy.yaml -- npx -y @modelcontextprotocol/server-github