Security middleware for the AI Agent era. Sits transparently between agents and servers to provide authentication, protection, logging, and monitoring.
A ~10MB single binary written in Go. 30 seconds to install, 1 minute to configure.
┌──────────┐ ┌──────────────────────────┐ ┌──────────────┐
│ AI Agent │ ──────> │ shield-agent │ ──────> │ Target Server│
│ MCP/A2A │ <────── │ [auth] [guard] [log] │ <────── │ MCP/A2A/API │
└──────────┘ │ monitor :9090 /metrics │ └──────────────┘
│ Web UI :9090 /ui │
└──────────────────────────┘
- When should you use this?
- Installation
- Quick Start by Use Case
- Authentication Method Selection Guide
- Deployment Patterns
- Configuration Reference
- Monitoring
- Web UI
- Roadmap
| Situation | What shield-agent does |
|---|---|
| You're exposing an MCP server publicly but need to restrict access | Ed25519 signature / token-based authentication |
| Your agents are making too many API calls | Rate limiting + hourly/monthly quotas |
| You need a record of who called what and when | SQLite audit log + Prometheus metrics |
| You want to block requests from certain IP addresses | IP blocklist/allowlist |
| You want to consolidate multiple MCP servers behind a single endpoint | Gateway mode (host/path-based routing) |
| You want a convenient Web UI for management | Built-in dashboard at /ui |
| Communication path | Protocol | Mode |
|---|---|---|
| Agent → MCP Server | JSON-RPC (stdio / SSE / Streamable HTTP) | stdio, proxy |
| Agent → Agent (A2A) | Google A2A protocol | HTTP middleware |
| Agent → API Server | REST / GraphQL | HTTP middleware |
# Homebrew (macOS / Linux)
brew tap itdar/tap && brew install shield-agent
# curl install script
curl -sSL https://raw.githubusercontent.com/itdar/shield-agent/main/scripts/install.sh | sh
# Go install
go install github.com/itdar/shield-agent/cmd/shield-agent@latest
# Docker
docker pull ghcr.io/itdar/shield-agent:latest
# Build from source
git clone https://github.com/itdar/shield-agent.git
cd shield-agent && go build -o shield-agent ./cmd/shield-agentWhen you want to wrap a Python/Node.js MCP server to add authentication and logging
# Simplest usage — just wrap the MCP server process
shield-agent python my_mcp_server.py
# Debug with verbose mode
shield-agent --verbose node server.js --port 8080How it works:
MCP Client ──stdin──> shield-agent ──stdin──> MCP Server (child process)
MCP Client <─stdout── shield-agent <─stdout── MCP Server
│
middleware chain
[auth] [guard] [log]
- shield-agent runs the MCP server as a child process
- Intercepts stdin/stdout and applies the middleware chain
- stderr passes through unchanged
- Automatically forwards SIGINT/SIGTERM and propagates exit codes
When you want to add a security layer in front of an already-running MCP server
# Streamable HTTP (default)
shield-agent proxy --listen :8888 --upstream http://localhost:8000
# SSE transport
shield-agent proxy --listen :8888 --upstream http://localhost:8000 --transport sse
# HTTPS
shield-agent proxy --listen :8888 --upstream http://localhost:8000 \
--tls-cert cert.pem --tls-key key.pemHow it works:
MCP Client ──HTTP──> shield-agent :8888 ──HTTP──> MCP Server :8000
│
middleware chain
monitoring :9090
When you want to place multiple MCP/API servers behind a single shield-agent endpoint
shield-agent.yaml:
upstreams:
- name: mcp-server-a
url: http://10.0.1.1:8000
match:
host: mcp-a.example.com # Host header-based routing
transport: sse
- name: api-server-b
url: http://10.0.2.1:3000
match:
path_prefix: /api-b # Path-based routing
strip_prefix: true
- name: default-mcp
url: http://10.0.3.1:8000 # Fallback when nothing else matchesshield-agent proxy --listen :8888How it works:
Agent A ──mcp-a.example.com──> shield-agent :8888 ──> MCP Server A
Agent B ──/api-b/v1/data─────> shield-agent :8888 ──> API Server B (/v1/data)
Agent C ──other requests─────> shield-agent :8888 ──> Default MCP
- Routing by Host header or URL path prefix
- Match priority: Host+Path > Host only > Path only > fallback
- With
strip_prefix: true, the prefix is stripped before forwarding to upstream - Upstreams can be added/removed dynamically from the Web UI (
/ui)
services:
shield:
image: ghcr.io/itdar/shield-agent:latest
command: proxy --listen :8888 --upstream http://mcp-server:8000
ports:
- "8888:8888"
- "9090:9090"
volumes:
- ./shield-agent.yaml:/shield-agent.yaml:ro
- ./keys.yaml:/keys.yaml:ro
- shield-data:/data
environment:
- SHIELD_AGENT_DB_PATH=/data/shield-agent.db
mcp-server:
image: your-mcp-server:latest
volumes:
shield-data:When you want to issue tokens like API keys to external agents and manage their quotas
# Issue a token
shield-agent token create --name "partner-agent" --quota-hourly 1000
# → Token: a3f8c1... (shown only once — store it safely)
# List tokens
shield-agent token list
# Check usage
shield-agent token stats <token-id> --since 24h
# Revoke a token (takes effect immediately)
shield-agent token revoke <token-id>Agents include the token in the request header:
Authorization: Bearer a3f8c1...
shield-agent supports 3 authentication methods. Choose the one that fits your situation:
┌─────────────────────────────────────────────────────────────────┐
│ Ed25519 + keys.yaml — high security, small number of agents │
│ Ed25519 + DID — open ecosystem, no pre-registration │
│ Bearer Token — simple, like an API key, quota support │
└─────────────────────────────────────────────────────────────────┘
| Ed25519 + keys.yaml | Ed25519 + DID | Bearer Token | |
|---|---|---|---|
| Who creates the key | Agent generates, admin registers | Agent generates, no registration needed | Admin issues |
| Pre-registration | Required (keys.yaml or Web UI) | Not required | Token must be distributed |
| Per-request signing | Yes | Yes | No |
| Token theft risk | Private key must be stolen (hard) | Private key must be stolen | Token theft is sufficient |
| Quota/expiry | No | No | Yes (hourly/monthly) |
| Immediate revocation | Delete from keys.yaml | Add to blocklist | token revoke (instant) |
| Recommended for | 5–10 internal agents | Many agents, open ecosystem | External partners, API key-style |
# keys.yaml
keys:
- id: "agent-1"
key: "base64-encoded-Ed25519-public-key"Agent includes this in JSON-RPC params when making requests:
{
"method": "tools/list",
"params": {
"_mcp_agent_id": "agent-1",
"_mcp_signature": "hex-encoded-signature"
}
}From the Web UI at /ui, use the Agent Keys menu to register or remove public keys.
Keys are looked up from both keys.yaml and the DB, so either location works.
When an agent uses an ID in the format did:key:z6Mk..., the public key is extracted directly from the ID.
No keys.yaml registration needed — well-suited for large-scale agent environments.
| Mode | Behavior |
|---|---|
open (default) |
Passes through even without a signature, only logs a warning |
verified |
Valid signature required; unregistered DIDs are OK (but can have differential rate limits) |
closed |
Only registered agents can access |
# shield-agent.yaml
security:
mode: verified
did_blocklist: # Block only malicious DIDs (not an allowlist)
- "did:key:z6Mk..."[Agent] → [shield-agent :8881] → [MCP Server A]
[Agent] → [shield-agent :8882] → [MCP Server B]
- Simplest architecture
- Recommended for 2–3 services
- Each shield-agent handles authentication/logging independently
[Agent A] ──> [shield-agent :8888] ──> [MCP Server A]
[Agent B] ──> (gateway) ──> [API Server B]
- Recommended for 5+ services
- Auth/tokens/logs managed in one place
- Host/path routing via
upstreamsconfiguration
[Agent] → [nginx (TLS)] → [shield-agent :8888 (HTTP)] → [upstream]
- TLS termination handled by nginx
- shield-agent operates over HTTP only
- No need for
--tls-cert/--tls-keyconfiguration
[shield-agent A :9090/metrics] ──┐
[shield-agent B :9091/metrics] ──┼── Prometheus ──> Grafana
[shield-agent C :9092/metrics] ──┘
When Prometheus scrapes /metrics from each shield-agent,
you can centrally view request counts, error rates, and latency across all services even in a sidecar architecture.
Priority: CLI flags > environment variables (SHIELD_AGENT_*) > YAML config > defaults
Copy shield-agent.example.yaml to shield-agent.yaml to get started.
| Setting | Default | Environment variable | Description |
|---|---|---|---|
security.mode |
open |
SHIELD_AGENT_SECURITY_MODE |
open / verified / closed |
security.key_store_path |
keys.yaml |
SHIELD_AGENT_KEY_STORE_PATH |
Path to public key file |
security.did_blocklist |
[] |
— | List of DIDs to block |
storage.db_path |
shield-agent.db |
SHIELD_AGENT_DB_PATH |
SQLite DB path |
storage.retention_days |
30 |
SHIELD_AGENT_RETENTION_DAYS |
Log retention in days |
server.monitor_addr |
127.0.0.1:9090 |
SHIELD_AGENT_MONITOR_ADDR |
Monitoring/UI address |
logging.level |
info |
SHIELD_AGENT_LOG_LEVEL |
debug/info/warn/error |
logging.format |
json |
SHIELD_AGENT_LOG_FORMAT |
json / text |
middlewares:
- name: auth
enabled: true
- name: guard
enabled: true
config:
rate_limit_per_min: 60 # Per-method requests per minute
max_body_size: 65536 # Maximum request body size (bytes)
ip_blocklist: ["203.0.113.0/24"]
ip_allowlist: ["10.0.0.0/8"] # Empty means allow all
brute_force_max_fails: 5 # Auto-block after 5 consecutive failures
validate_jsonrpc: true # Reject malformed JSON-RPC
- name: token
enabled: false # Token middleware (enable if needed)
- name: log
enabled: true| Flag | Description |
|---|---|
--config <path> |
Config file path (default: shield-agent.yaml) |
--log-level <level> |
Log level |
--verbose |
Alias for --log-level debug |
--monitor-addr <addr> |
Monitoring address |
--disable-middleware <name> |
Disable a middleware |
--enable-middleware <name> |
Enable a middleware |
Change configuration without restarting the process:
kill -HUP $(pgrep shield-agent)The middleware chain, keys.yaml, and DID blocklist will be reloaded.
| Path | Description |
|---|---|
/healthz |
Health check (healthy / degraded) |
/metrics |
Prometheus metrics |
/ui |
Web UI dashboard |
| Metric | Type | Labels |
|---|---|---|
shield_agent_messages_total |
Counter | direction, method |
shield_agent_auth_total |
Counter | status |
shield_agent_message_latency_seconds |
Histogram | method |
shield_agent_child_process_up |
Gauge | — |
shield_agent_rate_limit_rejected_total |
Counter | method |
shield-agent logs # Last 50 entries
shield-agent logs --last 100 # Last 100 entries
shield-agent logs --agent <id> --since 1h # Specific agent, last 1 hour
shield-agent logs --method tools/call # Specific method
shield-agent logs --format json # JSON outputAccess at http://localhost:9090/ui.
- Initial password:
admin(forced change on first login) - Dashboard: Real-time request count, error rate, average latency
- Logs: Filterable audit log table
- Token management: Issue/revoke tokens, usage statistics
- Middleware: On/off toggles (persisted across restarts)
- Agent Keys: Register/remove public keys (manage via Web UI without keys.yaml)
- Upstreams: Add/edit/remove Gateway mode upstreams
See ROADMAP.md for details.
| Phase | Status | Description |
|---|---|---|
| Phase 1 — Core MVP | Done | Transport, Auth, Guard, Log, Middleware Chain |
| Phase 2 — Deployment & Installation | Done | Docker, Homebrew, GoReleaser, CI/CD |
| Phase 3 — Token & Web UI | Done | Token management, Web UI dashboard |
| Phase 3.5 — Gateway & DID | Done | Multi-upstream routing, DID blocklist, verified mode |
| Phase 4 — Advanced | Planned | Agent reputation, protocol auto-detection, WebSocket |