Skip to main content

Governance Proxy Quickstart

The governance proxy wraps any MCP server -- including third-party servers you don't control -- with policy checks, PII scanning, rate limiting, and full observability. Your agent connects to the proxy instead of the target server. No code changes to the agent or the server.

In this quickstart, you'll wrap a local MCP filesystem server with the proxy and see governed traces in your dashboard -- in under 5 minutes.

By the end, you'll have:

  • A governance proxy running between your agent and an MCP server
  • Automatic tracing of every proxied tool call with governance attributes
  • A config file for per-tool policies

Prerequisites

  • Python 3.10+
  • A Waxell API key -- get one from your Waxell dashboard
  • Node.js 18+ -- for the MCP filesystem server used in examples

Step 1: Install

Install waxell-observe with the MCP server extra:

pip install waxell-observe[mcp-server]

This installs waxell-observe along with fastmcp>=3.0,<4 (MCP server framework) and pyyaml>=6.0 (config file support). The wax CLI is included automatically.

Already have waxell-observe?

If you already have waxell-observe installed, add the server extra:

pip install "waxell-observe[mcp-server]"

Step 2: One-Liner Proxy

The fastest way to start is the --target flag. This wraps a target server with governance in a single command:

wax observe mcp-proxy --target "npx -y @modelcontextprotocol/server-filesystem /tmp/mcp-test"

That's it. The proxy:

  1. Starts the MCP filesystem server as a child process
  2. Wraps it with governance middleware (PII scanning, policy checks, rate limiting)
  3. Adds security middleware (tool fingerprinting, rug pull detection)
  4. Listens on stdio for incoming MCP client connections

Your agent connects to the proxy exactly as it would connect to the target server -- no code changes needed.

What's happening under the hood

The --target flag creates a ProxyConfig with the target as a local path and all defaults enabled: standard PII scanning, fail-open governance, tool fingerprinting, and rug pull detection. The proxy appears as a normal MCP server to your agent.

Connect your Waxell API key

To get governed traces flowing to your dashboard, pass your API key:

export WAXELL_API_KEY="wax_sk_..."
export WAXELL_API_URL="https://acme.waxell.dev"

wax observe mcp-proxy --target "npx -y @modelcontextprotocol/server-filesystem /tmp/mcp-test"

The CLI reads WAXELL_API_KEY and WAXELL_API_URL from environment variables automatically. You can also pass them as flags:

wax observe mcp-proxy \
--target "npx -y @modelcontextprotocol/server-filesystem /tmp/mcp-test" \
--api-key wax_sk_... \
--api-url https://acme.waxell.dev

Step 3: See Your Traces

After running the proxy and making tool calls through it, open your Waxell dashboard and navigate to the Traces view. You'll see server-side spans for each proxied tool call:

AttributeExample ValueDescription
Span nametools/call waxell-proxy:read_fileFormat: tools/call {proxy-name}:{tool}
waxell.mcp.server_sidetrueIndicates a server-side span
waxell.mcp.middleware_activetrueGovernance middleware processed this call
waxell.mcp.governance_checkedtrueGovernance was active
waxell.mcp.governance_actionallowPolicy decision
waxell.mcp.pii_detectedfalseWhether PII was found
waxell.mcp.params_hasha1b2c3d4e5f67890SHA256 fingerprint of the arguments
Proxy traces are independent

The proxy creates its own root traces (server-side, SpanKind.SERVER). These are separate from any client-side traces your agent creates. This means you get two views: the agent's perspective and the proxy's perspective.

Step 4: Add a Config File

The one-liner is great for getting started, but real deployments need per-tool policies. Create a config file to customize governance per tool.

Create proxy.yaml:

# Target: local MCP filesystem server
target:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp/mcp-test"]

# Proxy identity
name: "fs-governance-proxy"

# Governance defaults
api_key: "${WAXELL_API_KEY}"
api_url: "${WAXELL_API_URL}"
default_pii_scan: standard
default_policy_action: allow
fail_open: true

# Per-tool policies
tools:
write_file:
pii_scan: strict
approval_required: true
create_directory:
policy_action: warn

Run the proxy with your config file:

wax observe mcp-proxy --config proxy.yaml

Now write_file calls get strict PII scanning and require approval, while create_directory calls emit warnings. All other tools use the server defaults (standard PII scanning, allow policy).

Environment variable substitution

Config files support ${VAR_NAME} syntax for environment variables. The proxy resolves these before parsing the YAML/JSON. This means you can keep secrets out of your config files.

Three Target Formats

The target field in your config file supports three formats depending on how you connect to the backend server:

FormatWhen to UseExample
URLRemote HTTP MCP servertarget: { url: "http://remote-server:9000/mcp" }
CommandLocal process via stdiotarget: { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] }
PathLocal Python/JS scripttarget: { path: "./my_server.py" }

The --target CLI flag auto-detects the format: URLs starting with http:// or https:// use the URL format, everything else uses the path format.

What the Proxy Does

When your agent makes a tool call through the proxy, the governance pipeline runs:

The proxy also runs security middleware on tools/list calls to capture tool fingerprints and detect rug pulls (tool definition changes between sessions).

Governance is fail-open

If the Waxell controlplane is unreachable or the PII scanner throws an error, the tool call proceeds normally. Governance errors never break your agent's workflow. This fail-open behavior is logged so you can monitor governance health.

Full proxy.yaml with all options
# Target server (required)
target:
url: "http://remote-server:9000/mcp"
# OR:
# command: "npx"
# args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
# env:
# NODE_ENV: "production"
# OR:
# path: "./my_server.py"

# Proxy server settings
name: "my-governance-proxy"
transport: stdio # "stdio" or "http"
host: "0.0.0.0" # HTTP bind host (only used when transport=http)
port: 8080 # HTTP bind port (only used when transport=http)

# Governance
api_key: "${WAXELL_API_KEY}"
api_url: "${WAXELL_API_URL}"
default_pii_scan: standard # "none", "standard", "strict"
default_policy_action: allow # "allow", "block", "warn"
fail_open: true

# Per-tool policies
tools:
send_email:
pii_scan: strict
approval_required: true
rate_limit:
max_per_minute: 10
max_per_hour: 100
delete_records:
policy_action: block
read_file:
pii_scan: none # skip PII scanning for read-only tools

# Security
fingerprinting: true # capture tool fingerprints on first tools/list
rug_pull_detection: true # alert on tool definition changes

Next Steps