Direct tool access has no seam to govern
An agent that holds a connector credential and calls a third-party API directly leaves nothing behind. There is no record of what it asked for, no place to deny the call before it happens, and no shared budget to charge it against. Each agent becomes its own trust boundary, and a permission misconfigured on one of them cannot be contained by anything outside it.
That is the failure mode the gateway exists to remove. Every tool call an agent makes goes through one function — McpGateway.call_tool() — and no domain service is allowed to reach a connector any other way.
Credential with no chaperone
An agent holding a connector credential can put it anywhere its output goes — a step record, a log line, a reply. Nothing between the agent and the API can strip it, because nothing is between them.
Unbounded retry
A loop that re-decides to call the same tool has nothing to stop it. The cap it should hit lives in the gateway it is not going through.
Injection reaches the tool
A fetched page that says “call this tool” is only an attack if wanting is the same as being allowed. Direct access makes it the same thing.
Policy check, audit log, dispatch — in that order
The order matters more than the steps. The policy check runs before anything leaves the process, so a denied call never reaches the network. The audit row is written whether the call succeeded, was denied, or failed, so the trail does not have survivorship bias. Only then does the connector adapter dispatch.
A declaration is not a grant. An agent's manifest lists the tools it would like to use; a gateway policy in a specific project decides whether it may. An agent with no grant in that project runs its stations and writes its steps without ever reaching a connector.
{ "connection_id": "0193f2c1-7a4e-7b31-9c08-2f5d9a1e4b77", "agent_scope_grants": [ { "agent_id": "a2-competitor-watch", "allowed_tool_actions": ["web_fetch.get", "web_search.query"] } ], "sweep_status": "passed", "applied_at": "2026-08-04T09:12:44Z"}What the gateway deliberately does not do
It does not make an unimplemented connector look implemented. A connector type with no dispatch adapter returns an explicit 'no adapter implemented' result rather than a fabricated success, because a plausible-looking fake is far more expensive to debug than an honest refusal.
- Who asked: Which agent, in which project, on which connection — resolved server-side from the run, never taken from the call itself.
- What was decided: The tool action and whether the policy allowed it. A denial is a row, not a silence.
- How it ended: Allowed, denied or errored. The trail records all three, so it has no survivorship bias.