MCP Governance API Reference
This page is a unified lookup reference for the public API surface of all three MCP governance products: the auto-instrumentor, the server middleware, and the governance proxy. It documents every public class, function, decorator, and configuration option with verified import paths.
For span attribute reference, see the dedicated Span Attributes Reference. For middleware-specific details, see the Middleware API Reference.
Install Extras
MCP governance functionality is split across two install extras. Choose the one(s) matching your use case.
| Extra | Install Command | Dependencies | Use Case |
|---|---|---|---|
mcp | pip install waxell-observe[mcp] | mcp>=1.25,<2 | Agent developers using the auto-instrumentor (client-side) |
mcp-server | pip install waxell-observe[mcp-server] | fastmcp>=3.0,<4, pyyaml>=6.0 | Server builders (middleware) and platform teams (proxy) |
| Both | pip install waxell-observe[mcp,mcp-server] | All MCP dependencies | Full MCP governance stack |
The mcp-server extra is excluded from pip install waxell-observe[all] because it pulls in fastmcp which is only needed by MCP server builders. Install it explicitly when needed.
Auto-Instrumentor (waxell_observe)
The auto-instrumentor patches MCP ClientSession.call_tool() to add governance, PII scanning, security checks, and OTel span recording on the agent (client) side.
waxell.init()
import waxell_observe
waxell_observe.init(**kwargs)
Initializes waxell-observe, including the MCP auto-instrumentor. Call once at application startup.
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | "" | Waxell API key (wax_sk_...). Falls back to WAXELL_API_KEY env var. |
api_url | str | "" | Waxell API URL. Falls back to WAXELL_API_URL env var. |
capture_content | bool | False | Capture LLM prompt/completion content on spans. |
instrument | list[str] | None | None | Allowlist of instrumentors to enable. None = all available. |
exclude | list[str] | None | None | Denylist of instrumentors to disable. Falls back to WAXELL_EXCLUDE env var (comma-delimited). |
instrument_infra | bool | True | Auto-instrument infrastructure libraries (httpx, requests, etc.). |
resource_attributes | dict | None | None | Additional OTel resource attributes. |
debug | bool | False | Enable debug logging for waxell internals. |
on_policy_block | callable | None | None | Callback invoked when a policy blocks a tool call. |
MCP auto-instrumentation is enabled automatically when init() runs and the mcp package is installed. No explicit opt-in is required.
configure_session()
from waxell_observe.instrumentors.mcp_instrumentor import configure_session
Configures per-session MCP governance and security settings. Must be called after session.initialize() and before making tool calls.
| Parameter | Type | Default | Description |
|---|---|---|---|
session | ClientSession | (required) | The MCP ClientSession to configure. |
server_name | str | "" | Human-readable server name for span attribution. |
timeout | float | None | None | Per-session timeout override in seconds. Overrides the global 30s default. |
on_policy_block | callable | None | None | Per-session callback for policy blocks. |
governance_config | dict | None | None | Per-session governance overrides (e.g., {"pii_scan": "strict"}). |
on_description_change | callable | None | None | Callback invoked on rug pull detection (tool description change). |
fingerprint_config | dict | None | None | Per-session fingerprint configuration. |
Server Middleware (waxell_observe.mcp)
The server middleware adds governance directly inside a FastMCP server process, intercepting tool calls with policy checks, PII scanning, rate limiting, and OTel recording.
GovernanceMiddleware
from waxell_observe.mcp import GovernanceMiddleware
Server-side governance middleware for FastMCP servers. Subclasses fastmcp.server.middleware.Middleware and intercepts on_call_tool.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | "" | Waxell API key for controlplane policy checks. When empty, runs in standalone mode (OTel spans and PII scans still work). |
api_url | str | "" | Waxell API URL (e.g., "https://acme.waxell.dev"). |
policy_config | dict | None | None | Server-level governance defaults. Parsed into a MiddlewareConfig dataclass. Keys: default_pii_scan, default_policy_action, fail_open. |
pii_scanner | object | None | None | Custom PII scanner implementing the PIIScanner protocol. If None, uses RegexPIIScanner. |
tool_configs | dict[str, ToolGovernanceConfig] | None | None | Per-tool governance configuration keyed by tool name. |
Governance Pipeline
The middleware processes each on_call_tool through 12 sequential sections:
| Section | Action | Can Block? |
|---|---|---|
| 1 | Extract tool name and arguments | No |
| 2 | Auto-initialize OTel (idempotent waxell.init()) | No |
| 3 | Create server-side span (SpanKind.SERVER, independent root trace) | No |
| 4 | Record caller identity (client_id, session_id, IP, user-agent) | No |
| 5 | Resolve per-tool config (decorator > tool_configs > defaults) | No |
| 6 | Rate limit check | Yes -- raises ToolError |
| 7 | Policy check (skipped if no controlplane) | Yes -- raises ToolError on block |
| 8 | PII scan on inputs | Yes -- raises ToolError on block-level findings |
| 9 | Execute tool via call_next | No |
| 10 | PII scan on outputs (warn only) | No |
| 11 | Record audit trail (governance_checked, params_hash) | No |
| 12 | Return result | No |
Fail-Open Semantics
When fail_open=True (default):
- Governance errors in the outer try/except call
call_nextwithout governance - Policy check errors set
POLICY_SKIPPEDspan attribute and continue - PII scan errors log a warning and continue
ToolErroralways propagates (intentional blocks are never swallowed)
ToolGovernanceConfig
from waxell_observe.mcp import ToolGovernanceConfig
Per-tool governance configuration dataclass. Applied via @governance decorator or constructor tool_configs.
| Field | Type | Default | Description |
|---|---|---|---|
approval_required | bool | False | Whether the tool call requires approval before execution. |
pii_scan | str | "standard" | PII scanning mode: "none", "standard", or "strict". |
policy_action | str | None | None | Override policy action: "allow", "block", or "warn". None = use server default. |
rate_limit | dict | None | None | Rate limit config: {"max_per_minute": N, "max_per_hour": N}. |
MiddlewareConfig
from waxell_observe.mcp import MiddlewareConfig
Server-level governance defaults dataclass. Passed as policy_config dict to GovernanceMiddleware.
| Field | Type | Default | Description |
|---|---|---|---|
default_pii_scan | str | "standard" | Default PII scanning mode for unconfigured tools. |
default_policy_action | str | "allow" | Default policy action for unconfigured tools. |
fail_open | bool | True | If True, governance errors let tool calls proceed. If False, governance errors block calls. |
@governance Decorator
from waxell_observe.mcp import governance
Per-tool governance decorator for FastMCP server tools. Attaches a ToolGovernanceConfig as _waxell_governance metadata on the tool function.
| Parameter | Type | Default | Description |
|---|---|---|---|
approval_required | bool | False | Whether the tool call requires approval. |
pii_scan | str | "standard" | PII scanning mode: "none", "standard", or "strict". |
policy_action | str | None | None | Override policy action: "allow", "block", "warn". |
rate_limit | dict | None | None | Rate limit config: {"max_per_minute": N, "max_per_hour": N}. |
The @governance decorator is order-independent -- it can be applied above or below @mcp.tool. Both orderings work because the config is set on both the original function and the wrapper.
Config Resolution Order
When multiple configuration sources exist, the highest priority wins:
| Priority | Source | Example |
|---|---|---|
| 1 (highest) | @governance decorator on the tool function | @governance(pii_scan="strict") |
| 2 | Constructor tool_configs[tool_name] | tool_configs={"send_email": ToolGovernanceConfig(...)} |
| 3 (lowest) | Server-level defaults from policy_config | policy_config={"default_pii_scan": "standard"} |
Governance Proxy (waxell_observe.mcp_proxy)
The governance proxy wraps any third-party MCP server transparently with policy checks, PII scanning, rate limiting, OTel spans, and security features. Use it when you do not control the server source code.
ProxyServer
from waxell_observe.mcp_proxy import ProxyServer
Main proxy class. Combines FastMCP's create_proxy() with GovernanceMiddleware and optional SecurityMiddleware.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
config | ProxyConfig | (required) | Proxy configuration specifying target, governance, and security settings. |
Methods
| Method | Signature | Description |
|---|---|---|
run | run(transport: str | None = None, **kwargs) | Run the proxy server. transport overrides config.transport. For HTTP, defaults to config.host/config.port. |
run_async | async run_async(transport: str | None = None, **kwargs) | Async version of run(). |
Property
| Property | Type | Description |
|---|---|---|
proxy | FastMCPProxy | Access the underlying FastMCPProxy for advanced use (testing, extension). |
ProxyConfig
from waxell_observe.mcp_proxy import ProxyConfig
Pydantic model for full proxy configuration. Can be constructed directly or loaded from a YAML/JSON file via load_config().
| Field | Type | Default | Description |
|---|---|---|---|
target | dict[str, Any] | (required) | Target server spec. See Target Formats. |
name | str | "waxell-proxy" | Proxy server name for MCP metadata and OTel attribution. |
transport | str | "stdio" | Inbound transport: "stdio" or "http". |
host | str | "0.0.0.0" | Bind host for HTTP transport. |
port | int | 8080 | Bind port for HTTP transport. |
api_key | str | None | None | Waxell API key for controlplane policy checks. |
api_url | str | None | None | Waxell API URL. |
default_pii_scan | str | "standard" | Default PII scanning mode: "none", "standard", "strict". |
default_policy_action | str | "allow" | Default policy action: "allow", "block", "warn". |
fail_open | bool | True | Governance errors let tool calls proceed. |
tools | dict[str, ToolPolicyConfig] | {} | Per-tool governance policies by tool name. |
fingerprinting | bool | True | Enable tool fingerprint capture on tools/list. |
rug_pull_detection | bool | True | Enable rug pull detection (tool definition change alerts). |
Target Formats
The target dict supports three formats:
| Format | Dict Shape | Example |
|---|---|---|
| HTTP | {"url": "http://..."} | {"url": "http://localhost:9000/mcp"} |
| Stdio | {"command": "...", "args": [...], "env": {...}} | {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]} |
| Local script | {"path": "..."} | {"path": "./my_server.py"} |
ToolPolicyConfig
from waxell_observe.mcp_proxy import ToolPolicyConfig
Pydantic model for per-tool governance policy from config file. Maps tool names to governance rules for third-party MCP servers where @governance decorators are not available.
| Field | Type | Default | Description |
|---|---|---|---|
pii_scan | str | "standard" | PII scanning mode: "none", "standard", or "strict". |
policy_action | str | None | None | Override policy action: "allow", "block", "warn". None = use server default. |
rate_limit | dict[str, Any] | None | None | Rate limit config: {"max_per_minute": N, "max_per_hour": N}. |
approval_required | bool | False | Whether the tool call requires approval. |
create_proxy_server()
from waxell_observe.mcp_proxy import create_proxy_server
Convenience factory for creating a ProxyServer. Supports two modes: config file and quick target.
| Parameter | Type | Default | Description |
|---|---|---|---|
config_path | str | None | None | Path to a YAML/JSON config file. |
target | str | None | None | Target server URL or local file path (for quick-start mode). |
**overrides | Any | -- | Override any ProxyConfig field (e.g., transport="http", port=9090). |
Returns: ProxyServer -- configured and ready to run.
Raises: ValueError if neither config_path nor target is provided.
# Config file mode
server = create_proxy_server(config_path="proxy.yaml")
server.run()
# Quick target mode (HTTP)
server = create_proxy_server(target="http://remote-server:9000/mcp", api_key="wax_sk_...")
server.run()
# Quick target mode (local script)
server = create_proxy_server(target="./my_server.py")
server.run()
load_config()
from waxell_observe.mcp_proxy import load_config
Loads proxy configuration from a YAML or JSON file with environment variable substitution.
| Parameter | Type | Default | Description |
|---|---|---|---|
path | str | (required) | Path to the config file (.yaml, .yml, or .json). |
Returns: ProxyConfig -- validated configuration instance.
Raises:
FileNotFoundErrorif the config file does not existValueErrorif the file extension is unsupportedImportErrorif a YAML file is loaded butpyyamlis not installed
Environment variables referenced as ${VAR_NAME} in the config file are resolved via os.path.expandvars() before parsing.
PII Scanning (waxell_observe.scanning)
Shared PII scanning module used by both the auto-instrumentor and the server middleware. Import from waxell_observe.scanning for custom scanner implementations.
PIIScanner Protocol
from waxell_observe.scanning import PIIScanner
Runtime-checkable protocol for pluggable PII scanners.
| Method | Signature | Return Type | Description |
|---|---|---|---|
scan | scan(self, text: str) | dict | Scan text and return {"detected": bool, "count": int, "findings": list[dict]}. |
Each finding dict contains:
| Key | Type | Description |
|---|---|---|
type | str | PII or credential type (e.g., "ssn", "email", "api_key") |
category | str | "pii" or "credential" |
action | str | Action for this finding: "warn", "block", or "redact" |
RegexPIIScanner
from waxell_observe.scanning import RegexPIIScanner
Default PII scanner using regex patterns. Detects SSN, credit card, email, phone, AWS access key, AWS secret key, API key, and private key patterns.
| Parameter | Type | Default | Description |
|---|---|---|---|
actions | dict[str, str] | None | None | Per-type action overrides. Maps PII/credential type to action ("warn", "block", "redact"). Default action is "warn" for all types. |
PII scan modes and their scanner configuration:
| Mode | Behavior | Scanner Setup |
|---|---|---|
"none" | No PII scanning | Scanner not created |
"standard" | Warn on all PII types | RegexPIIScanner() (default actions) |
"strict" | Block on all PII types | RegexPIIScanner(actions={"ssn": "block", "credit_card": "block", ...}) |
scan_text_for_pii()
from waxell_observe.scanning import scan_text_for_pii
Convenience function for scanning text with a pluggable scanner.
| Parameter | Type | Default | Description |
|---|---|---|---|
text | str | None | (required) | Text to scan. None or empty returns not-detected immediately. |
scanner | PIIScanner | None | None | Custom scanner. If None, uses RegexPIIScanner with default patterns. |
truncate_at | int | 4096 | Maximum characters to scan. Longer text is truncated. |
Returns: dict with detected (bool), count (int), findings (list[dict]). On scanner error, returns not-detected (fail-open).
Span Attributes (waxell_observe.tracing.attributes)
All MCP span attribute constants are defined on the MCPAttributes class. Both client-side (auto-instrumentor) and server-side (middleware/proxy) spans use the same waxell.mcp.* namespace for unified querying.
from waxell_observe.tracing.attributes import MCPAttributes
# Use constants instead of raw strings
span.set_attribute(MCPAttributes.TOOL_NAME, "search_documents")
span.set_attribute(MCPAttributes.GOVERNANCE_ACTION, "allow")
For the complete attribute listing with types, descriptions, examples, and TraceQL query patterns, see the Span Attributes Reference.
CLI Reference
The governance proxy includes a CLI command under wax observe mcp-proxy.
wax observe mcp-proxy [OPTIONS]
| Option | Short | Type | Default | Env Var | Description |
|---|---|---|---|---|---|
--config | -c | path | -- | -- | Config file (YAML/JSON) |
--target | -t | string | -- | -- | Target server URL or path |
--transport | -- | stdio/http | stdio | -- | Inbound transport |
--port | -- | int | 8080 | -- | Port for HTTP transport |
--host | -- | string | 0.0.0.0 | -- | Host for HTTP transport |
--api-key | -- | string | -- | WAXELL_API_KEY | Waxell API key |
--api-url | -- | string | -- | WAXELL_API_URL | Waxell API URL |
--name | -- | string | waxell-proxy | -- | Proxy server name |
--no-fingerprint | -- | flag | -- | -- | Disable tool fingerprinting |
--no-rug-pull-detection | -- | flag | -- | -- | Disable rug pull detection |
Either --config or --target must be provided. The CLI reads WAXELL_API_KEY and WAXELL_API_URL from environment variables when --api-key/--api-url are not passed explicitly.
# Quick target mode
wax observe mcp-proxy --target http://remote-server/mcp
# Config file mode
wax observe mcp-proxy --config proxy.yaml
# With overrides
wax observe mcp-proxy --target ./my_server.py --api-key wax_sk_... --transport http --port 8080
# Disable security features
wax observe mcp-proxy --config proxy.yaml --no-fingerprint --no-rug-pull-detection
See Also
- Span Attributes Reference -- Complete listing of all 52
waxell.mcp.*span attributes - Middleware API Reference -- Detailed middleware reference with usage examples and the governance pipeline
- Architecture -- Decision flowchart and deployment diagrams for choosing the right product
- Quickstart (Auto-Instrumentor) -- Get started with client-side MCP governance
- Middleware Quickstart -- Get started with server-side middleware
- Proxy Quickstart -- Get started with the governance proxy