Control what your AI agents can do. See everything they did.
MCP security proxy with RBAC — authenticates, authorizes, and audits every AI tool call.
Single binary. Zero config. Admin UI included.
Your AI agents — Claude Code, Codex, Gemini Cli, custom apps — connect to MCP servers and get access to everything: databases, filesystems, APIs, GitHub repos. There's no control over which agent can call which tool, no audit trail of what happened, and no way to block a destructive action before it reaches the server.
AI Agent → MCP Server → ⚠️ ANY tool, ANY data, ANY action
No authentication. No rules. No logs.
SentinelGate sits between your AI agents and your MCP servers. Every tool call passes through a security chain — authentication, policy evaluation, audit logging — before it ever reaches the upstream. Denied calls are blocked before the MCP server even knows someone asked.
Your AI agents don't know SentinelGate exists. They see a single MCP endpoint with a unified tool list. All enforcement happens transparently.
Deterministic policy enforcement. Every tool call is evaluated against explicit rules — not AI judgment, not probabilistic filters. If your policy says "deny delete_*", it's denied. Always. No false positives, no drift, no "it usually works."
CEL-powered rules. Policies use Common Expression Language — the same engine behind Kubernetes, Firebase, and Envoy. Write rules as expressive as code without deploying code:
// Allow writes only to safe paths for non-admin users
tool_name == "write_file" && (
tool_args.path.startsWith("/safe/") || "admin" in user_roles
)
Simple tool patterns (read_*, delete_*) cover 90% of cases. CEL handles the rest.
- 🔒 Fine-grained access control — Allow or deny individual tools per identity, with wildcard patterns and CEL expressions for complex logic
- 🖥️ Admin UI included — Dashboard to manage servers, rules, identities, and API keys. No YAML required
- 📊 Full audit trail — Every tool call logged with identity, decision, matched rule, timestamp, and latency. Real-time streaming and CSV export
- 🔗 Multi-server aggregation — Connect multiple MCP servers (stdio or HTTP) and expose them as a single unified endpoint
- 🧪 Policy playground — Test your rules against simulated tool calls before deploying them
- ⚡ Zero-config start — Single binary, no dependencies. Running in under a minute
Everything is managed from the browser. No config files, no CLI flags, no restarts needed.
Get running in under 60 seconds.
No build tools required. Pick your platform:
# macOS (Apple Silicon)
curl -L https://github.com/Sentinel-Gate/Sentinelgate/releases/latest/download/sentinel-gate_darwin_arm64.tar.gz | tar xz
# macOS (Intel)
curl -L https://github.com/Sentinel-Gate/Sentinelgate/releases/latest/download/sentinel-gate_darwin_amd64.tar.gz | tar xz
# Linux (amd64)
curl -L https://github.com/Sentinel-Gate/Sentinelgate/releases/latest/download/sentinel-gate_linux_amd64.tar.gz | tar xz
# Linux (arm64)
curl -L https://github.com/Sentinel-Gate/Sentinelgate/releases/latest/download/sentinel-gate_linux_arm64.tar.gz | tar xz./sentinel-gate start🎯 Expected output:
SentinelGate v2.0.0 ───────────────────────────────────── Admin UI: http://localhost:8080/admin Proxy: http://localhost:8080/mcp Upstreams: 0 connected / 0 configured Tools: 0 discovered Rules: 1 active ─────────────────────────────────────
Open http://localhost:8080/admin — the admin UI is ready. No password from localhost.
Build from source (requires Go 1.24+)
git clone https://github.com/Sentinel-Gate/Sentinelgate.git
cd Sentinelgate
go build -o sentinel-gate ./cmd/sentinel-gate
./sentinel-gate startDocker (alternative)
docker compose up -dThe image uses distroless for minimal attack surface and runs as non-root. State is persisted to a /data volume.
SentinelGate does not require Docker — it's a single binary with zero runtime dependencies. Docker is available purely as an alternative deployment option.
Three steps in the admin UI, then you're protected.
Click "+ Add Upstream". SentinelGate connects, discovers all available tools, and displays them instantly.
Supports stdio (spawn a local process like npx @modelcontextprotocol/server-filesystem) and HTTP (connect to a remote MCP server) transports. I recommend using -y as the first argument, but it shouldn't be necessary.
Company Files (14 tools) ● Connected
┌─────────────────────────────────────────────────────────────────────┐
│ read_file Read a file from disk ✅ Allow │
│ write_file Write content to a file ⛔ Deny │
│ edit_file Make line-based edits to a file ⛔ Deny │
│ list_directory List directory contents ✅ Allow │
│ search_files Search for files ✅ Allow │
│ ... │
└─────────────────────────────────────────────────────────────────────┘
GitHub (8 tools) ● Connected
┌─────────────────────────────────────────────────────────────────────┐
│ list_issues List repository issues ✅ Allow │
│ create_issue Create a GitHub issue ✅ Allow │
│ delete_branch Delete a branch ⛔ Deny │
│ ... │
└─────────────────────────────────────────────────────────────────────┘
Create policy rules using simple tool patterns or the CEL expression editor. Rules are evaluated by priority — first match wins.
| Rule | Tool | Action | Priority |
|---|---|---|---|
block-all-writes |
write_file |
Deny | 999 |
block-deletes |
delete_* |
Deny | 998 |
allow-all |
* |
Allow | 100 |
For complex logic:
tool_name == "write_file" && tool_args.path.startsWith("/safe/") && "admin" in user_roles
Test rules before deploying them with the built-in Policy Test playground.
Create an identity and API key from the Access page, then point your client to SentinelGate:
Claude Code:
claude mcp add --transport http -s user sentinelgate http://localhost:8080/mcp \
--header "Authorization: Bearer YOUR_API_KEY"Codex / Gemini CLI / Claude Code / any MCP client:
{
"mcpServers": {
"sentinelgate": {
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}Your AI client sees all tools from all upstream servers as a single unified list. It has no idea SentinelGate is in between.
When the AI calls a tool, the request passes through six interceptors:
AI Agent ─── tools/call "read_file" ──► SentinelGate
1. Validation ✓ JSON-RPC valid
2. Rate Limit ✓ Under limit (23/100 req/min)
3. Auth ✓ Key valid → identity "claude-agent", roles: ["user"]
4. Policy ✓ Rule "allow-reads" matched → ALLOW
5. Audit ✓ Logged: tool, identity, decision, timestamp
6. Router ✓ "read_file" → upstream "Company Files" → forward
◄── response ── file contents returned to AI
If a policy denies the call, the request never reaches the upstream server:
AI Agent ─── tools/call "delete_file" ──► SentinelGate
1. Validation ✓
2. Rate Limit ✓
3. Auth ✓ identity "claude-agent", roles: ["user"]
4. Policy ✗ Rule "block-deletes" matched → DENY
── STOP ──
5. Audit ✓ Logged: decision "DENY", rule "block-deletes"
◄── error ── "Access denied by policy"
The upstream server never receives the request. The file is never touched.
Your MCP servers give AI access to production databases, internal APIs, and GitHub repos with privileged tokens. Without SentinelGate, every agent has full access to every tool. One bad prompt, one hallucination, one runaway loop — and you're looking at deleted data, leaked secrets, or runaway API costs.
Every tool call is logged with the identity that made it, the decision (allow/deny), the rule that matched, and the full arguments. Filter by tool, user, or time period. Stream events in real-time. Export to CSV for compliance.
Give Claude Code full access. Restrict Cursor to read-only. Give the intern's custom chatbot access to search and summarize — nothing else. Each identity gets its own API key and role-based policy set.
SentinelGate works with zero configuration. Everything — servers, rules, identities, keys — is managed from the admin UI at runtime and persisted to state.json.
For infrastructure settings (port, rate limits, audit output), an optional YAML file is available:
server:
http_addr: ":8080"
rate_limit:
enabled: true
ip_rate: 100 # requests/minute per IP
user_rate: 1000 # requests/minute per user
audit:
output: "stdout" # or "file:///var/log/sentinel-gate/audit.log"Full example: sentinel-gate.example.yaml
Policy engine (CEL) reference
Policies use CEL (Common Expression Language).
Available variables:
| Variable | Type | Description |
|---|---|---|
tool_name |
string |
Tool being called ("read_file") |
tool_args |
map |
Arguments passed to the tool ({"path": "/tmp/data.txt"}) |
user_roles |
list |
Roles of the authenticated identity (["user", "admin"]) |
identity_id |
string |
UUID of the identity making the request |
identity_name |
string |
Name of the identity ("claude-agent") |
session_id |
string |
MCP session ID |
request_time |
timestamp |
Request timestamp |
Custom function: glob(pattern, string) — filesystem-style pattern matching (e.g., glob("read_*", tool_name)).
Note: Roles are pure string labels with no built-in privileges. There is no implicit "admin bypass" — all permissions must be defined explicitly through policy rules.
Examples:
# Block all destructive tools
tool_name.startsWith("delete_") || tool_name.startsWith("drop_")
# Allow writes only to safe directories
tool_name == "write_file" && tool_args.path.startsWith("/safe/")
# Custom rule: allow everything for a specific role (roles have no built-in privileges)
"admin" in user_roles
# Read-only for a specific role
"reader" in user_roles && !(tool_name.startsWith("write_") || tool_name.startsWith("delete_"))
CLI reference
sentinel-gate start # Start with zero-config (creates state.json automatically)
sentinel-gate start --dev # Dev mode: skip authentication
sentinel-gate start --config config.yaml # Start with YAML configuration
sentinel-gate start --state /path/state.json # Custom state file location
sentinel-gate start -- npx @modelcontextprotocol/server-filesystem /tmp # Inline stdio upstream
sentinel-gate version # Print version, commit, build date
sentinel-gate hash-key "my-api-key" # Generate SHA-256 hash for YAML config
sentinel-gate --help # Show all commands| Flag | Default | Description |
|---|---|---|
--config |
sentinel-gate.yaml |
Path to YAML config file (optional) |
--state |
state.json |
Path to state persistence file |
--dev |
false |
Dev mode: auto-creates dev identity and default policy |
The state path can also be set via SENTINEL_GATE_STATE_PATH environment variable.
SentinelGate is designed to run behind a reverse proxy (nginx, Caddy) that handles TLS. Do not expose it directly to the internet without TLS termination.
Built-in protections: CSRF (double-submit cookie), Content Security Policy headers, SameSite=Strict cookies, configurable rate limiting (per-IP, per-user), CEL expression hardening (length limits, cost limits, compile-time checks), path traversal protection, JSON-RPC validation with method whitelist and 1MB payload limit.
The admin UI is accessible only from localhost. For remote access, use an SSH tunnel:
ssh -L 8080:localhost:8080 your-serverNeed SSO, compliance reports, or human-in-the-loop approvals? SentinelGate Pro adds:
- SSO/SAML — Okta, Azure AD, Google Workspace + SCIM provisioning
- SIEM integration — Splunk, Datadog, Azure Sentinel
- Human-in-the-Loop — Approval workflows for sensitive tool calls
- Content scanning — PII detection and secret detection on tool arguments
- Compliance — EU AI Act and SOC2 evidence generation
- Multi-tenant — Isolated policies and audit per team
→ Learn more at sentinelgate.co.uk | → Contact us
We welcome contributions — bug fixes, features, documentation, and feedback. See CONTRIBUTING.md for guidelines.
A CLA is required for code contributions to support dual-licensing. See CLA.md.
Not sure where to start? Look for issues tagged good first issue.
AGPL-3.0 — see LICENSE. Free to use, modify, and self-host.
For commercial licensing without AGPL obligations, contact us.
Twitter · Website · Discussions
If SentinelGate is useful to you, consider giving it a ⭐ — it helps others find it.




