@b1ttsteel/agent-shield
v0.1.0
Published
Firewall and audit logger for MCP agent-to-tool connections — intercept, log, and enforce policies on every tool call your AI agents make
Maintainers
Readme
AgentShield
A firewall and audit log for AI agents.
AgentShield sits between your AI agents and the tools they use. It intercepts every tool call, enforces allow/deny policies, rate-limits dangerous operations, and logs everything — so you know exactly what your agents are doing.
Works with Claude Code, Cursor, Windsurf, and any custom MCP-based agent.
You wouldn't deploy an API without rate limiting. Why are your agents different?
What It Does
┌─────────────┐ ┌──────────────────────────────────────────┐ ┌─────────────┐
│ AI Agent │ │ AgentShield Proxy │ │ MCP Server │
│ (Claude Code │────▶│ │────▶│ (GitHub, │
│ Cursor, │◀────│ Intercept → Policy Check → Log → Route │◀────│ Jira, │
│ Custom) │ │ │ │ Slack...) │
└─────────────┘ └──────────────────────────────────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌──────────┐
│ Policy │ │ Audit │ │Dashboard │
│ Engine │ │ Log │ │ API │
└────────┘ └─────────┘ └──────────┘Three interception layers:
- MCP tool calls — intercept any MCP server (GitHub, Jira, Slack, filesystem, etc.)
- CLI commands — intercept shell commands agents run (
rm -rf,git push --force, etc.) - HTTP requests — intercept outbound HTTP (block exfil domains, scan for secret leaks)
Quick Start (5 minutes)
1. Install
npm install -g agent-shield2. Create a policy file
Create agent-shield.yaml in your project root:
version: "1"
settings:
log_level: "info"
log_file: "./agent-shield-audit.log"
redact_secrets: true
servers:
- name: "github"
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_TOKEN: "${GITHUB_TOKEN}"
rules:
# Block dangerous operations
- name: "block-destructive-git"
action: deny
servers: ["github"]
tools: ["push_files", "delete_branch", "delete_repository"]
message: "Destructive git operations are blocked"
# Rate limit writes to 20/min
- name: "rate-limit-writes"
action: allow
tools: ["write_file"]
rate_limit:
max_calls: 20
window_seconds: 60
# Allow reads freely
- name: "allow-reads"
action: allow
tools: ["read_file", "search_files", "list_directory"]
# Log anything else for review
- name: "warn-unknown"
action: warn
message: "Unrecognized tool — logged for review"
# Block everything not matched above
- name: "default-deny"
action: deny
message: "Not explicitly permitted by policy"3. Validate your policy
agent-shield policy validate
# ✓ Policy is valid (5 rules loaded)4. Connect your agent
Claude Code — update ~/.claude/config.json:
{
"mcpServers": {
"github": {
"command": "agent-shield",
"args": ["start", "--server", "github", "--config", "./agent-shield.yaml"]
}
}
}Cursor — update .cursor/mcp.json:
{
"mcpServers": {
"github": {
"command": "agent-shield",
"args": ["start", "--server", "github", "--config", "./agent-shield.yaml"]
}
}
}That's it. Every tool call now goes through AgentShield's policy engine.
Examples
Block an agent from deleting repos but allow reading issues
rules:
- name: "block-destructive"
action: deny
tools: ["delete_repository", "delete_branch", "push_files"]
message: "Destructive operations are not allowed"
- name: "allow-read-ops"
action: allow
tools: ["get_issue", "search_issues", "list_repos", "read_file"]
- name: "default-deny"
action: deny
message: "Not permitted"Rate limit an agent to 10 file writes per minute
rules:
- name: "rate-limit-writes"
action: allow
tools: ["write_file", "create_file"]
rate_limit:
max_calls: 10
window_seconds: 60
- name: "allow-reads"
action: allow
tools: ["read_file", "search_files"]
- name: "default-deny"
action: denyProxy multiple MCP servers with different policies
servers:
- name: "github"
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_TOKEN: "${GITHUB_TOKEN}"
- name: "filesystem"
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "./workspace"]
- name: "linear"
url: "https://mcp.linear.app/sse"
headers:
Authorization: "Bearer ${LINEAR_TOKEN}"
rules:
# GitHub: read-only
- name: "github-read-only"
action: deny
servers: ["github"]
tools: ["push_files", "create_branch", "delete_branch"]
message: "GitHub is read-only through AgentShield"
# Filesystem: allow reads, rate-limit writes
- name: "fs-allow-reads"
action: allow
servers: ["filesystem"]
tools: ["read_file", "list_directory", "search_files"]
- name: "fs-limit-writes"
action: allow
servers: ["filesystem"]
tools: ["write_file"]
rate_limit:
max_calls: 30
window_seconds: 60
# Linear: allow everything (trusted)
- name: "linear-allow-all"
action: allow
servers: ["linear"]
# Default deny
- name: "default-deny"
action: deny
message: "Not explicitly permitted"Match on tool arguments with regex
rules:
# Block writes to production paths
- name: "block-prod-writes"
action: deny
tools: ["write_file"]
args_match:
path: "/prod/|/production/"
message: "Cannot write to production paths"
# Allow writes elsewhere
- name: "allow-writes"
action: allow
tools: ["write_file"]Beyond MCP: CLI Command Interception
Agents don't just call MCP tools — they run shell commands too. AgentShield can intercept those as well.
Dry-run a command against policy
agent-shield exec --dry-run "rm -rf /"
# ✗ DENY: "rm -rf /" — Destructive file operations blocked (rule: block-destructive)
agent-shield exec --dry-run "git status"
# ✓ ALLOW: "git status" (rule: allow-safe-commands)
agent-shield exec --dry-run "docker run ubuntu"
# ⚠ WARN: "docker run ubuntu" — Docker command detected (rule: warn-docker)Run a command through the policy engine
agent-shield exec "git status" # runs if allowed
agent-shield exec "rm -rf /" # blocked — exits with code 1Claude Code hook integration
AgentShield can generate a Claude Code PreToolUse hook that automatically intercepts every Bash tool call:
# See the hook config
agent-shield hook show
# Test what the hook would do
agent-shield hook test "kubectl delete pod --all"Add the output of agent-shield hook show to your .claude/settings.json and every shell command Claude Code runs will go through policy.
Default command rules block:
rm -rf, destructive filesystem opsgit push --force,git reset --hard- System commands (
shutdown,dd,mkfs) - Data exfiltration via
curl/wget
Beyond MCP: HTTP Request Interception
AgentShield can also act as an HTTP forward proxy — intercepting outbound requests, blocking dangerous domains, and scanning request bodies for leaked secrets.
Check a URL against policy
agent-shield http-check https://pastebin.com/raw/abc
# ✗ DENY — Request to known data exfiltration domain blocked
agent-shield http-check https://api.github.com/repos
# ✓ ALLOW (rule: allow-dev-domains)
agent-shield http-check https://random-site.xyz
# ⚠ WARN — HTTP request to unrecognized domain
agent-shield http-check -m POST -b '{"key":"AKIAIOSFODNN7EXAMPLE"}' https://api.example.com
# ✗ DENY — Request body contains potential secretsStart the HTTP proxy
agent-shield http-proxy --port 8080Then route agent traffic through it:
HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 your-agentDefault HTTP rules block:
- Exfiltration domains (pastebin, ngrok, webhook.site, requestbin, etc.)
- File uploads (
multipart/form-data) to external services - Secret leaks in request bodies (AWS keys, GitHub PATs, private keys, Stripe/OpenAI keys)
Allows:
- Dev domains (GitHub, npm, PyPI, localhost)
- AI/cloud APIs (OpenAI, Anthropic, Google, AWS, Azure)
Web Dashboard
agent-shield dashboard
# ✓ Dashboard running at http://localhost:4040Provides:
- Live Feed — Real-time stream of every tool call with policy decisions
- Analytics — Total calls, allow/deny/warn breakdown, top tools, latency
- Anomaly Alerts — Volume spikes, new tool access, off-hours activity, high deny rates
- Policy Reference — Quick view of your current rules
The dashboard reads from SQLite, so it works even when the proxy isn't running. (Requires optional better-sqlite3 — see Optional Dependencies).
Audit Log
Every intercepted call is logged as structured JSON:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T10:30:00.000Z",
"server_name": "github",
"agent_id": "claude-code",
"method": "tools/call",
"tool_name": "create_issue",
"arguments": { "project": "PROD", "title": "Fix bug" },
"result_preview": "Created issue PROD-123",
"policy_decision": "allow",
"policy_rule": "allow-reads",
"latency_ms": 142,
"error": null
}- Secrets are automatically redacted before logging (AWS keys, GitHub tokens, Slack tokens, API keys, passwords, emails)
- Log file is structured JSON (one entry per line) — pipe to
jq, Datadog, or any log aggregator - Policy file changes are hot-reloaded — no restart needed
Query logs with CLI
agent-shield logs # Show recent entries
agent-shield logs --filter write_file # Filter by tool name
agent-shield logs --action deny # Show only blocked calls
agent-shield logs --server github --json # JSON output for pipingAll CLI Commands
| Command | Description |
|---------|-------------|
| agent-shield start --server <name> | Start the MCP proxy (called by agent config) |
| agent-shield logs | View audit log entries |
| agent-shield policy validate | Validate your policy file |
| agent-shield dashboard | Launch web dashboard |
| agent-shield exec <command> | Run a shell command through policy |
| agent-shield exec --dry-run <command> | Check if a command would be allowed |
| agent-shield hook show | Show Claude Code hook config |
| agent-shield hook test <command> | Test hook policy for a command |
| agent-shield http-proxy --port <port> | Start HTTP forward proxy |
| agent-shield http-check <url> | Check a URL against HTTP policy |
Policy Rule Reference
Rules are evaluated top-to-bottom. First match wins (like iptables/nginx).
| Field | Type | Description |
|-------|------|-------------|
| name | string | Human-readable rule name |
| action | allow | deny | warn | What to do when matched |
| servers | string[] | Match specific servers (omit = all) |
| tools | string[] | Match specific tools (omit = all) |
| args_match | object | Regex matching on tool arguments |
| rate_limit | { max_calls, window_seconds } | Rate limiting (only with allow) |
| time_window | { start, end, timezone } | Time-of-day restrictions (HH:MM) |
| message | string | Shown on deny, logged on warn |
Actions
allow— permit the call and log itdeny— block the call, return an error to the agent, and log itwarn— permit the call but flag it for review in logs/dashboard
Features
- Policy enforcement — Allow/deny/warn rules with first-match-wins evaluation
- Rate limiting — Per-tool call limits with configurable time windows
- Audit logging — Structured JSON logs, queryable via CLI
- SQLite storage — Queryable audit data for dashboard and analytics (optional)
- Secret redaction — Auto-redacts API keys, tokens, passwords, emails before logging
- Hot-reload — Edit your policy file and changes apply instantly, no restart needed
- Web dashboard — Real-time feed, analytics, top tools, anomaly alerts
- Anomaly detection — Volume spikes, new tools, off-hours activity, high deny rates
- CLI command interception — Intercept shell commands agents run, not just MCP
- HTTP request interception — Forward proxy with domain blocking and secret leak scanning
- Claude Code hooks — Drop-in PreToolUse hook for Bash tool interception
- SSE transport — Proxy remote MCP servers over HTTP (not just local stdio)
- Works with any agent — Claude Code, Cursor, Windsurf, or custom agents
Optional Dependencies
AgentShield works out of the box with zero native dependencies. Some features require optional packages:
| Feature | Package | Install |
|---------|---------|---------|
| Dashboard + SQLite storage | better-sqlite3 | npm install better-sqlite3 |
If better-sqlite3 is not installed, AgentShield still works — it just uses JSON file logging instead of SQLite, and the dashboard won't be available.
Contributing
git clone https://github.com/inprod/agent-shield.git
cd agent-shield
npm install
npm test # 146 tests
npm run build # compile TypeScriptLicense
MIT
