Secure MCP gateway and quarantine for AI agent traffic — three-layer prompt injection defense
Runs on your machine from its package; the steps are below.

At most: moves money, runs code or commands, deletes data, or sends messages for you.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
uvx mcp-trentina-crunchtools
MCP client config
{
"mcpServers": {
"trentina": {
"command": "uvx",
"args": [
"mcp-trentina-crunchtools"
]
}
}
}
Trentina is an MCP gateway that sits between your AI agents and everything they touch: MCP servers, the web, Matrix, LLM providers and monitoring alerts. Every agent gets one endpoint and its own profile. Behind that endpoint Trentina stops prompt injection at every ingress and shrinks what reaches the context window. It also enforces policy the agent cannot talk its way past and handles OAuth for web clients like claude.ai and gemini.google.com. The same gateway can serve a personal assistant, a coding agent and a swarm, wired however you like. It is named after the 1377 trentino of Ragusa, where ships anchored offshore for thirty days before anyone came ashore. The idea is the same: commerce keeps flowing, and nothing dangerous gets in.
Security. Untrusted content gets the same three independent layers at every ingress: tool responses, tool descriptions, web pages, Matrix messages, LLM completions and alerts. L1 is deterministic checks, L2 is a local classifier and L3 is a quarantined LLM with no tools. L2's model is a setting: Horizon-Labs' prompt-injection-guard-small by default, Prompt Guard 2 86M one variable away. On our attacks planted inside long documents the default catches 21 of 39 where Prompt Guard 2 catches 4, 2.4x faster on CPU. L3 reads everything L2 lets through. Around the layers sit an egress guard, file confinement and a startup containment check. What is and is not defended, by kind of content, is in Coverage, with every known gap. Defense Pipeline · Benchmark
Token savings. Agents pay for every tool name, schema and response byte they read. Trentina hides tools a profile doesn't need, serves short names, compacts schemas and compresses descriptions: 154 tool descriptions went from 62K to 17K characters. It also minifies responses. On production traffic, agents received 52% fewer response bytes than backends sent, across 4,164 calls. Compression · Tool Filtering
Determinism. Asking a model nicely is not a control. Parameter guards, response guards and allowlists are evaluated by the gateway, so an agent that ignores its instructions, or assumes it has permission, is stopped before the backend sees the call. Trentina can't know when your email is ready to send. It can let the agent draft and keep the send for you. Production, last 30 days: 61 calls stopped by parameter guards, 11 responses withheld by response guards. Parameter Guards · Response Guards
Authentication. Web clients need OAuth, and most MCP servers don't speak it. Trentina is the authorization server: dynamic client registration for claude.ai and Claude Code, a provisioned client for gemini.google.com, verified external issuers, or a static bearer, chosen per profile. Each token is bound to the profile it was issued for, and LLM API keys stay inside the gateway. Authentication · LLM Key Proxying
Architectural flexibility. One gateway, many shapes. A personal assistant on Matrix, a coding agent in Claude Code and a swarm of locked-down autonomous agents each get their own profile, with their own backends, defense mode and credentials. An operator agent runs the gateway itself. The production deployment serves 8 profiles over 30 backends. Profiles · Operator
block refuses, flag delivers the exact bytes with a
verdict, redact returns an answer L3 extracted and a second pass verified.fetch, read, dir,
content and search, built-in tools that bring outside content in
through the pipeline. Fetches go through an egress guard that refuses
private addresses and checks every redirect. Reads are confined to
configured roots.user@example.com." Refused before the backend is called.--network=none container.# The container image is the distribution: the L2 model, the parsers and
# their process isolation ship in it. There is no PyPI package.
podman run -d -p 127.0.0.1:8019:8019 \
-v ./profiles.yaml:/config/profiles.yaml:ro,Z \
-e TRENTINA_GATEWAY_ENABLED=true \
-e TRENTINA_PROFILES_PATH=/config/profiles.yaml \
-e TRENTINA_PROFILE_MYAGENT_TOKEN=your-token \
-e OPENROUTER_API_KEY=your-key -e TRENTINA_MODEL_PROVIDER=openrouter \
quay.io/crunchtools/trentina \
--transport streamable-http --host 0.0.0.0 --port 8019
L3 needs a key for one LLM provider. Any of Gemini, OpenRouter, OpenAI,
Anthropic or Ollama works. A minimal profiles.yaml:
profiles:
myagent:
auth:
bearer_token_env: TRENTINA_PROFILE_MYAGENT_TOKEN
backends:
web:
url: "internal://web" # Trentina's own content tools
tools_allow: ["*"]
gmail:
url: "http://gws-personal:8000/mcp"
tools_allow: # it may read and draft; you send
- search_gmail_messages
- get_gmail_message_content
- draft_gmail_message
defense:
enforcement: block
Then point Claude Code at it:
{
"mcpServers": {
"trentina": {
"type": "streamable-http",
"url": "http://localhost:8019/gateway/myagent/mcp",
"headers": { "Authorization": "Bearer your-token" }
}
}
}
| Document | Description |
|---|---|
| MCP Gateway | Endpoint, routing, tool names, argument normalization |
| Per-Agent Profiles | Profile schema, modes, minifying, roles, multi-agent setup |
| Operator Profile | The operator agent's seat and the gateway's service identity |
| Configuration | Every environment variable |
| Authentication | Static bearer, OAuth proxy with DCR or a provisioned client, delegated issuers |
| Defense Pipeline | L1/L2/L3, modes, coverage and known gaps |
| Benchmark | Detection rates per layer and per L3 provider |
| Prompt Packs | The judge's prompts tuned per model: shipped packs, the gate, tuning your own |
| Content Tools | fetch, read, dir, content, search |
| Blocklist | Cumulative detection memory |
| Tool Filtering | Allowlists, denylists, glob patterns |
| Description Compression | Description compression and schema compaction |
| Token Routing | Response reduction (implemented); delegation (proposed) |
| Parameter Guards | Per-tool argument validation |
| Response Guards | Per-tool result validation |
| Audit Log | Call recording, stats, monitoring |
| LLM Key Proxying | Provider keys kept inside the gateway |
| Matrix Bridge | E2EE termination and two-way judging |
| Matrix Reverse Proxy | Matrix for agents on an isolated network |
| Deployment Hardening | Container flags, secrets, egress, the startup check |
| Cockpit Plugin | Live defense pipeline dashboard |
| Internal: Gateway Design | Original design document, for contributors |
uv sync --all-extras
uv run ruff check src tests
uv run mypy src
uv run pytest -v
The demo above is recorded against the published image by
demo.yml (docs/demo/render.sh); see
docs/demo/ for the fixtures it runs.
The container image is built by the GHA pipeline
(container.yml), never locally. The model-export
stage needs a gated HuggingFace credential that only CI holds, and building outside
the pipeline causes drift. Push the branch and let the pipeline verify the image.
AGPL-3.0-or-later