Skip to main content

MCP Governance Architecture

Waxell provides three products for MCP governance: the auto-instrumentor, server middleware, and governance proxy. Each product addresses a different deployment scenario, but all produce the same waxell.mcp.* span attributes and connect to the same controlplane for centralized policy management.

This page helps you choose the right product, understand how each deploys, and see how they compose for mixed environments.

Decision Flowchart

Use this flowchart to determine which product fits your deployment scenario.

Summary:

  • You write the agent and connect to servers you don't control -- use the auto-instrumentor (and optionally the proxy for third-party servers).
  • You build the MCP server and want governance regardless of which agents connect -- use server middleware.
  • You control neither but can deploy infrastructure between them -- use the governance proxy.

Product Deployment Diagrams

Auto-Instrumentor

The auto-instrumentor patches the MCP ClientSession.call_tool method inside your agent process. No separate infrastructure is needed.

The wrapper intercepts every call_tool invocation, performs governance checks (policy, PII, rate limit), records the result on an OTel span, and forwards the call to the MCP server. Spans use SpanKind.CLIENT because the agent is making an outbound request.

Install: pip install waxell-observe[mcp] Setup: Call waxell.init() and configure_session() on your ClientSession. Overview: MCP Governance Overview | Quickstart: Auto-Instrumentor Quickstart

Server Middleware

Server middleware runs inside your FastMCP server process. Every inbound tool call passes through GovernanceMiddleware before reaching your tool function.

The middleware runs policy checks, PII scanning, and rate limiting before each tool executes. It creates independent root traces (no client trace propagation) using SpanKind.SERVER because the server is receiving inbound requests.

Install: pip install waxell-observe[mcp-server] Setup: Add GovernanceMiddleware to your FastMCP server. Details: Middleware Quickstart

Governance Proxy

The governance proxy is a standalone MCP server that forwards calls to a target server. No code changes on either side.

The proxy adds SecurityMiddleware (for tool fingerprinting and rug pull detection) in front of GovernanceMiddleware. SecurityMiddleware runs first to capture baselines before governance checks. Like server middleware, proxy spans use SpanKind.SERVER.

Install: pip install waxell-observe[mcp-server] Setup: Run wax observe mcp-proxy --target <url> or use a config file. Details: Proxy Quickstart


Product Comparison

FeatureAuto-InstrumentorServer MiddlewareGovernance Proxy
AudienceAgent developersServer buildersPlatform teams
Installwaxell-observe[mcp]waxell-observe[mcp-server]waxell-observe[mcp-server]
Code changesAgent codeServer codeNone (config file)
SpanKindCLIENTSERVERSERVER
Policy checksYesYesYes
PII scanningYesYesYes
Rate limitingVia controlplaneYes (built-in)Yes (built-in)
Approval workflowsYesYesYes
Tool fingerprintingYesNoYes
Rug pull detectionYesNoYes
Per-tool configconfigure_session()@governance decoratorYAML/JSON config
Caller identityN/A (is the caller)Yes (client_id, IP)Yes (client_id, IP)
Fail-openYes (default)Yes (default)Yes (default)
InfrastructureNoneNoneProxy process

Composability

The three products are designed to work together. In real deployments, you often need more than one.

Auto-Instrumentor + Proxy

The most common combination. Use the auto-instrumentor for MCP servers you connect to directly, and the proxy for third-party servers you need to govern without modifying.

Example scenario: Your agent connects to an internal file server (direct, governed by the auto-instrumentor) and Composio (via proxy, governed by proxy config). Both produce waxell.mcp.* spans that appear in the same trace.

In this configuration, the auto-instrumentor creates CLIENT spans for the tool calls it intercepts, and the proxy creates SERVER spans for the same calls it processes. Both span sets appear in your dashboard, giving you end-to-end visibility.

Auto-Instrumentor + Middleware

Use this when you build both the agent and the server. The auto-instrumentor adds client-side governance (agent context, session-level config), and the middleware adds server-side governance (rate limiting, caller identity, server-level policies).

Example scenario: You build a database query server with GovernanceMiddleware for rate limiting and PII scanning. Your agents use the auto-instrumentor for policy checks and approval workflows. Both layers apply -- a tool call is checked by the instrumentor on the client side and by the middleware on the server side.

The two governance layers operate independently:

  • The auto-instrumentor checks client-side policies based on agent context (user, session, agent name).
  • The middleware checks server-side policies based on caller identity (client_id, IP address).
  • Each produces its own span -- CLIENT on the agent side, SERVER on the server side.

Attribute Namespace Consistency

All three products use the same waxell.mcp.* attribute namespace on their spans. This means governance queries work regardless of which product generated the span.

AttributeAuto-InstrumentorMiddlewareProxy
waxell.mcp.tool_nameYesYesYes
waxell.mcp.server_nameYesYesYes
waxell.mcp.governance_checkedYesYesYes
waxell.mcp.governance_actionYesYesYes
waxell.mcp.pii_detectedYesYesYes
waxell.mcp.params_hashYesYesYes
waxell.mcp.argumentsYesYesYes
waxell.mcp.resultYesYesYes

Querying across products. A TraceQL query like { span.waxell.mcp.governance_action = "block" } returns blocked tool calls from all three products. You don't need to know which product generated the span.

SpanKind Differences

While the attributes are consistent, the SpanKind differs based on the product's role:

ProductSpanKindReason
Auto-InstrumentorCLIENTThe agent is making an outbound request to an MCP server
Server MiddlewareSERVERThe server is receiving an inbound request from an agent
Governance ProxySERVERThe proxy is receiving an inbound request and forwarding it

This distinction matters when building dashboards:

  • Filter on span.kind = "client" to see the agent's view of tool calls.
  • Filter on span.kind = "server" to see the server's or proxy's view.
  • When using auto-instrumentor + proxy together, you see both CLIENT and SERVER spans for the same tool call, giving you round-trip visibility.

Server-Side Specific Attributes

The middleware and proxy add caller identity attributes that the auto-instrumentor does not:

AttributeDescriptionProducts
waxell.mcp.server_sidetrue for server/proxy spansMiddleware, Proxy
waxell.mcp.middleware_activetrue when governance middleware is processingMiddleware, Proxy
waxell.mcp.caller_client_idMCP client_id from initializeMiddleware, Proxy
waxell.mcp.caller_session_idMCP session IDMiddleware, Proxy
waxell.mcp.caller_ipCaller IP from HTTP headersMiddleware, Proxy
waxell.mcp.caller_user_agentUser-agent headerMiddleware, Proxy
waxell.mcp.rate_limitedtrue if rate limit was exceededMiddleware, Proxy
waxell.mcp.rate_limit_remainingRemaining calls in windowMiddleware, Proxy

Next Steps