Skip to main content

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.

ExtraInstall CommandDependenciesUse Case
mcppip install waxell-observe[mcp]mcp>=1.25,<2Agent developers using the auto-instrumentor (client-side)
mcp-serverpip install waxell-observe[mcp-server]fastmcp>=3.0,<4, pyyaml>=6.0Server builders (middleware) and platform teams (proxy)
Bothpip install waxell-observe[mcp,mcp-server]All MCP dependenciesFull MCP governance stack
mcp-server is not included in [all]

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.

ParameterTypeDefaultDescription
api_keystr""Waxell API key (wax_sk_...). Falls back to WAXELL_API_KEY env var.
api_urlstr""Waxell API URL. Falls back to WAXELL_API_URL env var.
capture_contentboolFalseCapture LLM prompt/completion content on spans.
instrumentlist[str] | NoneNoneAllowlist of instrumentors to enable. None = all available.
excludelist[str] | NoneNoneDenylist of instrumentors to disable. Falls back to WAXELL_EXCLUDE env var (comma-delimited).
instrument_infraboolTrueAuto-instrument infrastructure libraries (httpx, requests, etc.).
resource_attributesdict | NoneNoneAdditional OTel resource attributes.
debugboolFalseEnable debug logging for waxell internals.
on_policy_blockcallable | NoneNoneCallback 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.

ParameterTypeDefaultDescription
sessionClientSession(required)The MCP ClientSession to configure.
server_namestr""Human-readable server name for span attribution.
timeoutfloat | NoneNonePer-session timeout override in seconds. Overrides the global 30s default.
on_policy_blockcallable | NoneNonePer-session callback for policy blocks.
governance_configdict | NoneNonePer-session governance overrides (e.g., {"pii_scan": "strict"}).
on_description_changecallable | NoneNoneCallback invoked on rug pull detection (tool description change).
fingerprint_configdict | NoneNonePer-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

ParameterTypeDefaultDescription
api_keystr""Waxell API key for controlplane policy checks. When empty, runs in standalone mode (OTel spans and PII scans still work).
api_urlstr""Waxell API URL (e.g., "https://acme.waxell.dev").
policy_configdict | NoneNoneServer-level governance defaults. Parsed into a MiddlewareConfig dataclass. Keys: default_pii_scan, default_policy_action, fail_open.
pii_scannerobject | NoneNoneCustom PII scanner implementing the PIIScanner protocol. If None, uses RegexPIIScanner.
tool_configsdict[str, ToolGovernanceConfig] | NoneNonePer-tool governance configuration keyed by tool name.

Governance Pipeline

The middleware processes each on_call_tool through 12 sequential sections:

SectionActionCan Block?
1Extract tool name and argumentsNo
2Auto-initialize OTel (idempotent waxell.init())No
3Create server-side span (SpanKind.SERVER, independent root trace)No
4Record caller identity (client_id, session_id, IP, user-agent)No
5Resolve per-tool config (decorator > tool_configs > defaults)No
6Rate limit checkYes -- raises ToolError
7Policy check (skipped if no controlplane)Yes -- raises ToolError on block
8PII scan on inputsYes -- raises ToolError on block-level findings
9Execute tool via call_nextNo
10PII scan on outputs (warn only)No
11Record audit trail (governance_checked, params_hash)No
12Return resultNo

Fail-Open Semantics

When fail_open=True (default):

  • Governance errors in the outer try/except call call_next without governance
  • Policy check errors set POLICY_SKIPPED span attribute and continue
  • PII scan errors log a warning and continue
  • ToolError always 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.

FieldTypeDefaultDescription
approval_requiredboolFalseWhether the tool call requires approval before execution.
pii_scanstr"standard"PII scanning mode: "none", "standard", or "strict".
policy_actionstr | NoneNoneOverride policy action: "allow", "block", or "warn". None = use server default.
rate_limitdict | NoneNoneRate 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.

FieldTypeDefaultDescription
default_pii_scanstr"standard"Default PII scanning mode for unconfigured tools.
default_policy_actionstr"allow"Default policy action for unconfigured tools.
fail_openboolTrueIf 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.

ParameterTypeDefaultDescription
approval_requiredboolFalseWhether the tool call requires approval.
pii_scanstr"standard"PII scanning mode: "none", "standard", or "strict".
policy_actionstr | NoneNoneOverride policy action: "allow", "block", "warn".
rate_limitdict | NoneNoneRate limit config: {"max_per_minute": N, "max_per_hour": N}.
Decorator order

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:

PrioritySourceExample
1 (highest)@governance decorator on the tool function@governance(pii_scan="strict")
2Constructor tool_configs[tool_name]tool_configs={"send_email": ToolGovernanceConfig(...)}
3 (lowest)Server-level defaults from policy_configpolicy_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

ParameterTypeDefaultDescription
configProxyConfig(required)Proxy configuration specifying target, governance, and security settings.

Methods

MethodSignatureDescription
runrun(transport: str | None = None, **kwargs)Run the proxy server. transport overrides config.transport. For HTTP, defaults to config.host/config.port.
run_asyncasync run_async(transport: str | None = None, **kwargs)Async version of run().

Property

PropertyTypeDescription
proxyFastMCPProxyAccess 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().

FieldTypeDefaultDescription
targetdict[str, Any](required)Target server spec. See Target Formats.
namestr"waxell-proxy"Proxy server name for MCP metadata and OTel attribution.
transportstr"stdio"Inbound transport: "stdio" or "http".
hoststr"0.0.0.0"Bind host for HTTP transport.
portint8080Bind port for HTTP transport.
api_keystr | NoneNoneWaxell API key for controlplane policy checks.
api_urlstr | NoneNoneWaxell API URL.
default_pii_scanstr"standard"Default PII scanning mode: "none", "standard", "strict".
default_policy_actionstr"allow"Default policy action: "allow", "block", "warn".
fail_openboolTrueGovernance errors let tool calls proceed.
toolsdict[str, ToolPolicyConfig]{}Per-tool governance policies by tool name.
fingerprintingboolTrueEnable tool fingerprint capture on tools/list.
rug_pull_detectionboolTrueEnable rug pull detection (tool definition change alerts).

Target Formats

The target dict supports three formats:

FormatDict ShapeExample
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.

FieldTypeDefaultDescription
pii_scanstr"standard"PII scanning mode: "none", "standard", or "strict".
policy_actionstr | NoneNoneOverride policy action: "allow", "block", "warn". None = use server default.
rate_limitdict[str, Any] | NoneNoneRate limit config: {"max_per_minute": N, "max_per_hour": N}.
approval_requiredboolFalseWhether 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.

ParameterTypeDefaultDescription
config_pathstr | NoneNonePath to a YAML/JSON config file.
targetstr | NoneNoneTarget server URL or local file path (for quick-start mode).
**overridesAny--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.

ParameterTypeDefaultDescription
pathstr(required)Path to the config file (.yaml, .yml, or .json).

Returns: ProxyConfig -- validated configuration instance.

Raises:

  • FileNotFoundError if the config file does not exist
  • ValueError if the file extension is unsupported
  • ImportError if a YAML file is loaded but pyyaml is 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.

MethodSignatureReturn TypeDescription
scanscan(self, text: str)dictScan text and return {"detected": bool, "count": int, "findings": list[dict]}.

Each finding dict contains:

KeyTypeDescription
typestrPII or credential type (e.g., "ssn", "email", "api_key")
categorystr"pii" or "credential"
actionstrAction 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.

ParameterTypeDefaultDescription
actionsdict[str, str] | NoneNonePer-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:

ModeBehaviorScanner Setup
"none"No PII scanningScanner not created
"standard"Warn on all PII typesRegexPIIScanner() (default actions)
"strict"Block on all PII typesRegexPIIScanner(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.

ParameterTypeDefaultDescription
textstr | None(required)Text to scan. None or empty returns not-detected immediately.
scannerPIIScanner | NoneNoneCustom scanner. If None, uses RegexPIIScanner with default patterns.
truncate_atint4096Maximum 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]
OptionShortTypeDefaultEnv VarDescription
--config-cpath----Config file (YAML/JSON)
--target-tstring----Target server URL or path
--transport--stdio/httpstdio--Inbound transport
--port--int8080--Port for HTTP transport
--host--string0.0.0.0--Host for HTTP transport
--api-key--string--WAXELL_API_KEYWaxell API key
--api-url--string--WAXELL_API_URLWaxell API URL
--name--stringwaxell-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