MCP has a security problem that nobody talks about. When an AI agent calls a tool over the Model Context Protocol, the JSON-RPC message travels from agent to tool server with zero authentication, zero authorization, and zero inspection. No access control. No rate limiting. No audit trail. The agent asks to read a file, and the tool server reads the file. The agent asks to execute a SQL query, and the tool server executes the query.
That's fine on your laptop. It's terrifying in production.
MCPDome is a Rust proxy that sits between the agent and the tool server, intercepting every MCP message and enforcing security policies before it reaches the tool. Think of it as a firewall for AI tool calls.
#The Architecture
The concept is straightforward. Instead of the agent connecting directly to the tool server, it connects to MCPDome. MCPDome evaluates the request against your security policies, and either forwards it to the real tool server or blocks it.
Agent βββ MCPDome βββ Tool Server
β
ββ Authentication (is this agent allowed to connect?)
ββ Authorization (can this agent call this tool?)
ββ Input validation (are the arguments safe?)
ββ Injection scan (is this a prompt injection attempt?)
ββ Rate limiting (is this agent sending too many requests?)
ββ Audit logging (record everything for review)
Every check happens in single-digit milliseconds. MCPDome is written in Rust specifically so the security layer doesn't become a performance bottleneck.
#Installation and Setup
MCPDome ships as a single binary. Install via Cargo or download a prebuilt release:
# Via Cargo
cargo install mcpdome
# Or download the binary for your platform
curl -fsSL https://get.orellius.ai/mcpdome | shInitialize a configuration file in your project:
mcpdome initThis generates a dome.toml file with sensible defaults. Open it and configure for your environment:
# dome.toml β MCPDome configuration
[server]
listen = "127.0.0.1:9090" # Where MCPDome listens for agent connections
upstream = "127.0.0.1:3000" # Where the actual MCP tool server runs
mode = "http" # "http" or "stdio"
[auth]
provider = "jwt"
secret = "${DOME_JWT_SECRET}" # Always use env vars for secrets
required = true # Reject unauthenticated connections
[policies]
# Block known dangerous patterns in tool arguments
block_patterns = [
"rm -rf /",
"rm -rf ~",
"DROP TABLE",
"DROP DATABASE",
"; --", # SQL injection terminator
"ignore previous instructions",
"ignore all prior instructions",
"you are now", # Common jailbreak prefix
]
# Per-tool access control
[policies.tools]
"filesystem.read" = { allow = true, max_path_depth = 3 }
"filesystem.write" = { allow = true, allowed_dirs = ["./src", "./docs"] }
"filesystem.delete" = { allow = false } # No agent should be deleting files
"database.query" = { allow = true, read_only = true }
"shell.execute" = { allow = false } # Absolutely not
[rate_limit]
max_requests = 100
window_secs = 60
per = "agent" # Rate limit per agent identity
[audit]
enabled = true
destination = "file" # "stdout", "file", or "webhook"
file_path = "./logs/dome-audit.jsonl"
include_arguments = true # Log tool call arguments
redact_secrets = true # Scrub detected secrets from logsStart the proxy:
export DOME_JWT_SECRET="your-secret-here"
mcpdome start --config dome.tomlNever hardcode secrets in
dome.toml. MCPDome supports${ENV_VAR}interpolation in all string values. Use it for JWT secrets, webhook URLs, API keys β anything sensitive.
#The Policy Engine in Depth
The block patterns in the config above are your first line of defense, but MCPDome's policy engine goes much deeper. It evaluates every tool call through a four-layer pipeline:
#Layer 1: Authentication
Every incoming connection must present a valid credential. MCPDome supports JWT tokens, API keys, and mTLS certificates. In a multi-agent setup, each agent gets its own identity, and the audit log tracks which agent made which call.
[auth]
provider = "jwt"
secret = "${DOME_JWT_SECRET}"
# Or use API keys for simpler setups
# provider = "api_key"
# keys = ["${AGENT_KEY_1}", "${AGENT_KEY_2}"]#Layer 2: Authorization (Tool-Level ACLs)
Not every agent should be able to call every tool. A code review agent doesn't need filesystem write access. A documentation agent doesn't need database access. MCPDome lets you define per-agent, per-tool permissions:
[policies.agents."code-reviewer"]
allowed_tools = ["filesystem.read", "git.diff", "git.log"]
denied_tools = ["filesystem.write", "filesystem.delete", "shell.execute"]
[policies.agents."docs-generator"]
allowed_tools = ["filesystem.read", "filesystem.write"]
# Restrict writes to the docs directory only
"filesystem.write".allowed_dirs = ["./docs"]#Layer 3: Injection Detection
This is where MCPDome gets opinionated. AI agents can be manipulated through indirect prompt injection β an attacker plants malicious instructions in a document, the agent reads the document, and the injected instructions get executed as tool calls. MCPDome scans tool arguments for known injection patterns and scores them against a heuristic model.
[injection]
enabled = true
sensitivity = "high" # "low", "medium", "high"
action = "block" # "block", "flag", or "log"
custom_patterns = [
"as an AI", # Unusual in legitimate tool arguments
"system prompt",
"\\bACT AS\\b",
]When sensitivity is set to high, MCPDome will also flag statistically unusual argument patterns β tool calls that look structurally different from what it has seen before. This catches novel injection techniques that don't match known patterns.
#Layer 4: Resource Limits
Even legitimate tool calls can cause problems if unchecked. An agent might read a 10GB file, return 100K database rows, or spawn a process that never terminates. MCPDome enforces resource boundaries:
[limits]
max_file_read_bytes = 10_485_760 # 10MB max file read
max_query_rows = 1000 # Cap database result sets
execution_timeout_secs = 30 # Kill long-running tool calls
max_response_bytes = 5_242_880 # 5MB max response from tool#Deployment Patterns
MCPDome is flexible enough to fit different architectures. Choose the pattern that matches your setup:
Sidecar mode works for single-agent deployments. Run MCPDome alongside your agent process, either as a separate binary or in the same container. The agent connects to MCPDome on localhost, and MCPDome connects to the tool server. Low latency, simple to reason about.
Gateway mode is the right choice when multiple agents share tool servers. Deploy one MCPDome instance that all agents connect through. You get centralized policy enforcement and a single audit log for all tool calls across all agents. This is the most common production pattern.
Mesh mode flips the architecture: instead of one proxy in front of agents, you place one proxy in front of each tool server. Agents connect directly to the proxy for the tool they need. This works well in microservice architectures where each tool server is independently deployed and managed.
#Monitoring and Observability
MCPDome exposes Prometheus-compatible metrics on a configurable endpoint:
[metrics]
enabled = true
endpoint = "/metrics"
port = 9091Key metrics to monitor:
| Metric | What It Tells You |
|--------|------------------|
| dome_requests_total{tool, agent, status} | Total tool calls, broken down by tool name, agent identity, and whether the call was allowed or blocked |
| dome_blocked_total{reason} | Blocked requests by reason β injection, rate limit, policy violation, auth failure |
| dome_latency_seconds | Proxy overhead latency (p50, p95, p99) β should stay under 5ms |
| dome_injection_score_histogram | Distribution of injection detection scores β useful for tuning sensitivity |
Set up alerts on dome_blocked_total spikes. A sudden increase in blocked requests often means either an injection attack in progress or a misconfigured agent that needs its permissions adjusted.
#A Practical Example: Securing a Coding Agent
Here's a real-world scenario. You have a coding agent that uses MCP to read files, write code, and run tests. You want it to be productive but not dangerous.
[policies.agents."coding-agent"]
allowed_tools = [
"filesystem.read",
"filesystem.write",
"shell.execute",
"git.diff",
"git.commit",
]
# It can write, but only in the source directory
"filesystem.write".allowed_dirs = ["./src", "./tests"]
# It can execute commands, but only these specific ones
"shell.execute".allowed_commands = [
"npm test",
"npm run lint",
"npm run build",
"npx tsc --noEmit",
]The agent can do its job β read code, write code, run tests β but it can't delete files, write outside the project, or execute arbitrary shell commands. If a prompt injection tries to make it run curl to exfiltrate data, MCPDome blocks it before the command reaches the shell.
#What's Next
- Read the full MCPDome configuration reference for every available option
- Tune injection detection sensitivity β start on
highand lower if you get false positives on legitimate tool calls - Set up webhook alerts so your team gets notified when requests are blocked in production
- If you're building a chatbot that also calls tools, pair MCPDome with Laminae for end-to-end safety coverage