Skip to main content

MCP Server Allowlist Policy

The mcp-server-allowlist policy category constrains which MCP servers an agent may register or connect to.

Today's MCP ecosystem lets an agent register arbitrary third-party servers -- a malicious or compromised tool catalog can be a serious attack surface. This category is the lever to constrain that.

Rules

RuleTypeDefaultDescription
allowed_serversstring[][]Positive allowlist of server names. Empty = no allowlist (all allowed unless in blocked_servers)
blocked_serversstring[][]Always-deny list. Wins over allowlist
action_on_violationstringblockAction on violation: block or warn

How It Works

The handler runs at before_workflow, mid_execution, and after_workflow -- on every phase, so a server attached mid-run is caught.

Reads context.metadata["mcp_servers"] -- a list of server names this run has connected to. The MCP manifest endpoint (or whichever subsystem registers servers) is responsible for populating this. Empty / missing list = no enforcement.

Semantics:

  1. Empty allowed_servers -> only blocked_servers applies
  2. Non-empty allowed_servers -> ONLY those servers are permitted
  3. blocked_servers wins (deny overrides allow)

Context Attributes Read

AttributePhasePurpose
context.metadata["mcp_servers"]allList of MCP server names attached to this run

Example Policy

Strict allowlist for enterprise tenants

{
"name": "Approved MCP servers only",
"category": "mcp-server-allowlist",
"rules": {
"allowed_servers": ["github-mcp", "jira-mcp", "internal-tools-mcp"],
"blocked_servers": ["unverified-public-mcp"],
"action_on_violation": "block"
},
"scope": {
"agents": ["*"]
},
"enabled": true
}

Deny-only mode

If you trust most MCP servers but want to block known-bad ones:

{
"rules": {
"blocked_servers": ["typosquat-mcp", "scraper-mcp"],
"action_on_violation": "block"
}
}

SDK Integration

import waxell_observe as waxell
waxell.init()

# The MCP manifest endpoint should populate metadata["mcp_servers"]
# when an agent registers servers.

@waxell.observe(
agent_name="dev-assistant",
enforce_policy=True,
metadata={"mcp_servers": ["github-mcp", "jira-mcp"]},
)
async def dev_agent(task: str) -> str:
return await run(task)

Observability

FieldExample (BLOCK)
Categorymcp-server-allowlist
Actionblock
ReasonMCP server 'untrusted-mcp' is not on the allowlist
Metadata{"blocked_server": "untrusted-mcp", "allowed_servers": [...], "phase": "before"}

Common Gotchas

  • Requires context.metadata["mcp_servers"] to be populated. If your MCP manifest endpoint doesn't write this field, the policy is a no-op. Empty / missing list = ALLOW.
  • Exact name match. Use the canonical server name -- no globs or regex.
  • Empty allowlist = NO allowlist. Same trap as the Tool Allowlist -- setting allowed_servers: [] does not block all servers.
  • Fires on every phase. Cheap (set lookup), so the policy catches servers attached mid-run, not just at startup.
  • Three handlers, one module. mcp-server-allowlist, tool-allowlist, and prompt-allowlist share the same allowlists.py handler module. Each is a separate category with separate rules.

Next Steps