Governance architecture
The MCP gateway: policy check, audit log, then dispatch
Every tool call an agent makes takes exactly one path, and that path is three steps long. There is no second route to a connector, no implicit allow, and no outcome that goes unrecorded — including the denials.
Open MCP gateway policy editor in the consolePolicy check, audit, dispatch
Every tool call an agent makes goes through one function, and that function does three things in a fixed order. There is no bypass, and the middle step happens whether the first one passed or failed.
- 1 · Policy check, deny by defaultThe connection's own policy is read: is there a policy row, did its safety sweep pass, and does this agent hold a grant naming this exact action? A missing row, a failed sweep and a missing grant are all the same outcome — denied.
- 2 · Audit, pass or failAn allowed call and a denied call both write a record. The record carries the agent id, the action, the connection it targeted and, for a denial, the reason — ids and outcomes, never the request or response body.
- 3 · DispatchOnly then is the connector called. A write is not recorded as done until the created object has been read back and matches; a write with no read-back contract is refused before dispatch rather than optimistically assumed.
What the audit record contains
{ "action": "MCP_TOOL_CALL_ALLOWED", "actor_type": "system", "resource_type": "mcp_server_connections", "resource_id": "0199c4a1-7f3e-7c21-9b44-2d6f8ae51c03", "after_state": { "agent_id": "0199c4a1-7f3e-7c21-9b44-2d6f8ae51c04", "action": "create_pull_request", "correlation": { "runId": "0199c4a1-7f3e-7c21-9b44-2d6f8ae51c05" } }, "severity": "info"}A write is not done until it reads back
For actions that change state somewhere else, a successful dispatch response is not treated as proof. The gateway reads the created object back and compares it against what was requested; only a match records the write as done.
What is honestly not implemented
Governance applies to every connector type. Real outbound dispatch does not — it exists for a handful, and every other type returns an explicit “no adapter implemented” result.
- Google Stitch, through its own MCP protocol client.
- GitHub, through a direct REST client.
- Slack, through a direct Web API client — note that approval buttons it posts are clickable but nothing receives the click yet; the approval still happens in XOOZLY.
- A guarded web fetch, which refuses any host resolving to a non-public address and rate-limits per host.
- A provider-backed web search.
Where the gateway is reached from
The gateway is reached from the orchestration loop through a bounded tool loop, and only for an agent that has both an active connection and a passed grant in that run's project. An agent without grants runs plan-only — its trace contains no tool calls because it was never able to make any.
- Per station
- 6 stepsA hard cap on the loop
- Per observation
- 4,000 charsTruncated before the model sees it
- Without a grant
- Plan onlyNo tool calls attempted
Put it into practice
Run a fleet with these guardrails on from day one.
Hard cost caps, one audited gateway in front of every tool call, and versioned rule files — the defaults, not an enterprise add-on.