Security Quarantine
MCPProxy includes an automatic quarantine system to protect against Tool Poisoning Attacks (TPA).
What is a Tool Poisoning Attack?
Tool Poisoning Attacks occur when malicious MCP servers:
- Hidden Instructions: Embed malicious instructions in tool descriptions that AI agents might follow
- Data Exfiltration: Trick AI agents into sending sensitive data to external servers
- Credential Theft: Attempt to extract API keys or tokens
- System Manipulation: Try to execute unauthorized commands
How Quarantine Works
Automatic Quarantine
When a new server is added via an AI client (using the upstream_servers tool):
- Server is automatically placed in quarantine status
- Tool calls to quarantined servers return a security analysis instead of executing
- Server remains quarantined until manually approved
Servers added by hand-editing mcp_config.json
Since issue #937, the same admission gate applies to servers written directly
into the configuration file — hand-editing is a normal workflow, and config
files get shared, templated and copied between machines, so an entry from a
config edit is admitted on exactly the same terms as one from
mcpproxy upstream add.
A config-file server is held for review when both of the following are true:
- the entry does not contain a
quarantinedkey (writing"quarantined": falseis an explicit operator statement and is obeyed), and - the server is not yet recorded in
config.db.
The second condition is what makes upgrading safe: every server you are already
running has a config.db record, so upgrading never re-quarantines a server
you have already vetted. The boundary is that a server present in a
hand-written config but absent from config.db — after a wiped data directory,
or on a machine that has never seen that config before — is treated as
first-seen and held for review.
The decision is durable: once recorded, a quarantine is not reverted by a config
file that never mentions the key. Un-quarantining stays a user action, and
writes both the config file and config.db.
No TPA scan runs at config-load admission; quarantine-by-default already keeps the tools away from agents, and the scan is what the human review step performs.
If mcpproxy writes the configuration file itself (any API apply, a quarantine
toggle, an upstream add), it does not stamp "quarantined": false onto
servers that never stated it — a value mcpproxy invented would otherwise read
back as an operator statement and disable the gate for that server.
Upgrading from a release affected by #937 — action required
The gate treats "present in config.db" as "has already been through
admission". That is what keeps upgrades safe, but it also means an install that
was already hit by #937 is not remediated by upgrading: the buggy admission
left exactly the config.db record the gate now reads as vetted, so a server
that was admitted unquarantined stays live.
At startup mcpproxy logs a warning naming any server that looks like this —
configured, running unquarantined, never explicitly reviewed, and with a
trust_mode that would have held it:
Configured servers predate the config-load admission gate and have never been
explicitly reviewed servers=["suspicious-server"]
For each server named, either:
- review it and record the decision — quarantining and then releasing it from
the quarantine UI writes an explicit
"quarantined"value tomcp_config.json, which silences the warning; or - write the decision by hand: add
"quarantined": true(hold it) or"quarantined": false(you have vetted it) to that server's entry.
Adding the key by hand is enough — the gate obeys an explicit value either way.
Tool Discovery and Search Isolation
Quarantined servers are completely isolated from the tool discovery and search system:
| Feature | Quarantined Server | Approved Server |
|---|---|---|
| Tools indexed | ❌ No | ✅ Yes |
Tools searchable via retrieve_tools | ❌ No | ✅ Yes |
| Tools appear in HTTP API search | ❌ No | ✅ Yes |
| Tool calls allowed | ❌ No (returns security analysis) | ✅ Yes |
This isolation prevents Tool Poisoning Attacks from:
- Injecting malicious descriptions into search results that AI agents might read and follow
- Appearing in tool recommendations where they could be mistakenly selected
- Influencing AI agent behavior through carefully crafted tool metadata
When a server is quarantined:
- Its tools are immediately removed from the search index
retrieve_toolsqueries will never return tools from that server- The server remains visible in the server list (marked as quarantined) for management
When a server is unquarantined (approved):
- The server connects to discover its tools
- Tools are indexed and become searchable
- Tool calls are allowed to execute normally
- Any pending (newly-discovered, never-reviewed) tool-approval records for
the server are auto-promoted to approved — approving a server means you
trust its current tool snapshot (baseline trust). Tools whose description or
schema later changes (
changed, i.e. rug-pull) are not affected and stay blocked until you re-approve them explicitly.
Security Analysis
When a tool from a quarantined server is called, MCPProxy blocks the call and returns a structured security response instead of invoking it — so the tool's description can be reviewed before it ever runs:
{
"status": "QUARANTINED_SERVER_BLOCKED",
"serverName": "suspicious-server",
"toolName": "fetch_data",
"message": "🔒 SECURITY BLOCK: Server 'suspicious-server' is currently in quarantine for security review. Tool calls are blocked to prevent potential Tool Poisoning Attacks (TPAs).",
"instructions": "To use tools from this server, please: 1) Review the server and its tools for malicious content, 2) Use the 'upstream_servers' tool with operation 'list_quarantined' to inspect tools, 3) remove from quarantine if verified safe",
"toolAnalysis": {
"name": "fetch_data",
"description": "…",
"inputSchema": { "…": "…" },
"serverName": "suspicious-server",
"analysis": "SECURITY ANALYSIS: This tool is from a quarantined server. Please carefully review the description and input schema for potential hidden instructions, embedded prompts, or suspicious behavior patterns."
}
}
The actual pattern detection — hidden-Unicode smuggling, cross-server
shadowing, decoded shell payloads, injection/exfiltration phrases, and embedded
secrets — is performed by the deterministic offline detect engine that backs the
built-in tpa-descriptions scanner. Its findings appear in the scan report
(mcpproxy security report <server>), each carrying a rule_id, severity,
threat_level, confidence, and the contributing check signals. See
Tool Scanner for the full rule reference.
Managing Quarantine
View Quarantined Servers
Web UI:
- Open the dashboard
- Click "Quarantine" in the navigation
- Review pending servers
CLI:
mcpproxy upstream list
# Shows quarantine status for each server
Approve a Server
Web UI:
- Click on the quarantined server
- Review the security analysis
- Click "Approve" to remove from quarantine
API:
curl -X POST \
-H "X-API-Key: your-key" \
http://127.0.0.1:8080/api/v1/servers/server-name/unquarantine
Config File:
Edit ~/.mcpproxy/mcp_config.json and add "quarantined": false:
{
"mcpServers": [
{
"name": "reviewed-server",
"command": "npx",
"args": ["@example/mcp-server"],
"quarantined": false,
"enabled": true
}
]
}
Re-quarantine a Server
If you need to quarantine a previously approved server:
curl -X POST \
-H "X-API-Key: your-key" \
http://127.0.0.1:8080/api/v1/servers/server-name/quarantine
Security Checklist
Before approving a server, verify:
- Source: Is the server from a trusted source?
- Code Review: Have you reviewed the server's code?
- Tool Descriptions: Do tool descriptions look legitimate?
- Network Access: Does the server need network access?
- Permissions: Are requested permissions appropriate?
Detection Patterns
Tool-description analysis is performed by the deterministic, fully-offline
detect engine that backs the built-in tpa-descriptions scanner. It runs
seven checks across two tiers — four hard checks that auto-quarantine and
block approval, and three soft checks that raise a human-review item:
| Check | Tier | Catches |
|---|---|---|
unicode.hidden | hard | Zero-width / bidi / TAG-block / PUA character smuggling |
shadowing.cross_server | hard | Distinctive tool-name collision or cross-server reference |
payload.decoded | hard | base64/hex blob that decodes to a shell/exfil command |
phrase.injection | hard | Curated instruction-override / exfiltration directives |
directive.imperative | soft | Injection directives, secrecy imperatives, instruction overrides |
capability.mismatch | soft | Compute/string tool touching ~/.ssh etc.; unexplained data-sink param |
secret.embedded | soft | Hardcoded live credential (confidence-scored, placeholders dropped) |
Each check is deterministic and reliability is enforced by a CI eval gate. See Tool Scanner for the full rule reference, the two-tier model, normalization, and the eval gate.
Best Practices
- Review All Servers: Never auto-approve servers added by AI agents
- Source Verification: Only approve servers from known, trusted sources
- Minimal Permissions: Prefer servers with limited, specific capabilities
- Regular Audits: Periodically review approved servers
- Network Isolation: Use Docker isolation with
network_mode: "none"for untrusted servers
Tool-Level Quarantine
In addition to server-level quarantine, MCPProxy provides tool-level quarantine that detects changes to individual tool descriptions and schemas using SHA256 hashing. This protects against "rug pull" attacks where a previously trusted server silently modifies tool behavior.
See Tool Quarantine for complete documentation on:
- SHA256 hash-based tool approval
- CLI commands:
mcpproxy upstream inspectandmcpproxy upstream approve - Configuration:
quarantine_enabled(global) andauto_approve_tool_changes(per-server; deprecatesskip_quarantine) - REST API endpoints for tool approval management
Trust modes (auto | scan | manual)
Since spec 086, each server carries a trust mode that governs both
new-server admission and tool-change approval (superseding the binary
auto_approve_tool_changes flag, which is migrated onto it automatically):
| Mode | Add time | Tool changes |
|---|---|---|
auto | Admitted without quarantine or scanning | Trusted without scanning (rug-pull risk) |
scan | Quarantined, then a fail-closed automatic TPA scan admits it on a clean verdict | Auto-approved only when the offline scan verdict is clean; otherwise held for review |
manual (default) | Quarantined for human review | Every change held for review |
Unrecognized values fail closed to manual. Config field: per-server
trust_mode; REST: trust_mode on POST/PATCH/GET /api/v1/servers.
The "Add time" column applies to every admission path — the upstream_servers
tool, the REST API, a registry add, and (since issue #937) a first-seen server
in mcp_config.json. See
Servers added by hand-editing mcp_config.json.
Web UI (spec 088): the server's Configuration tab has a tri-mode
selector (choosing auto asks for confirmation and explains the risk); the
add-server form chooses the mode instead of a raw quarantine checkbox
(initial quarantine is derived from the mode); server tiles show a
trust-mode badge.
Hold evidence in the Web UI (spec 088)
When the scan gate holds a tool, the approval record carries evidence —
held_reason (scan_findings = the scan found threats vs scan_coverage
= the scan could not complete, held as a precaution), held_verdict, and
held_signals (matched check ids, with known-attack TPA-YYYY-NNNN
signature ids listed first). The Web UI renders this on the tool-quarantine
panel, the change-diff dialog, and the global Tools page, with a best-effort
link into the server's latest scan report highlighting matching findings.
The quarantine banner distinguishes four states: scan running, scan verdict
blocked automatic approval, scan could not complete (retry offered), and
awaiting manual review.
Block (approve + disable)
When reviewing a pending or changed tool you may want to acknowledge it but keep it hidden from MCP clients — for example, dismissing a noisy "changed" flag for a tool you never intend to use. The block operation does this atomically: it approves the tool (clearing the quarantine flag) and disables it in a single, all-or-nothing server-side write, so a tool is never left in the approved+enabled state.
- REST:
POST /api/v1/servers/{id}/tools/blockwith{"tools":[...]}or{"block_all": true}. - MCP:
quarantine_securityoperationsblock_tool(withname+tool_name) andblock_all_tools(withname).
A blocked tool can be re-exposed later with the normal enable operation
(POST /api/v1/servers/{id}/tools/{tool}/enabled with {"enabled": true}).
Disabling Quarantine
Not recommended, but you can opt out of quarantine globally by setting a
single top-level flag in ~/.mcpproxy/mcp_config.json:
{
"quarantine_enabled": false
}
When quarantine_enabled is false:
- Servers added dynamically via the
upstream_serversMCP tool or thePOST /api/v1/serversREST endpoint default to not quarantined. - Tool-level quarantine (per-tool SHA-256 approval of descriptions and schemas, see Tool Quarantine) is skipped.
An explicit quarantined field in an add-server request still wins over
the default, so client code can always override on a per-server basis.
Per-server auto_approve_tool_changes: true auto-approves all post-baseline tool changes and additions for that server (the deprecated skip_quarantine: true is migrated onto it automatically).
Warning: Disabling quarantine exposes your system to Tool Poisoning Attacks. Only do this on machines where every MCP server you connect to is already trusted.