> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wardin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agentic governance

> Extend policy enforcement and signed receipts from model calls to agent tool calls.

Wardin's enforcement point normally sits on **model calls**: a request is authenticated, policy-checked, metered, and signed into the receipt chain before it reaches a provider. Agentic governance extends that same lifecycle to **agent tool calls** — so when an agent asks to invoke a tool (file a PR, hit a database, send a message), the **governance decision on that action** gets the same tamper-evident, audit-grade record as a token spend.

<Note>
  Two ways to govern a tool call ship today. The **decision endpoint** (`/mcp/tool-call`) returns an allow/deny verdict for a `{server, tool}` without forwarding — for an agent runner that enforces the decision itself. The **live proxy** (`/mcp/servers/<name>`) sits in the path: point an MCP client at it and Wardin governs the `tools/call`, forwards it to your configured upstream MCP server, records the real upstream outcome, and relays the response. Both write the same signed receipt. Scope is **gateway-routed tool calls to remote HTTP MCP servers** — see [Honest scope](#honest-scope).
</Note>

## The three pieces

### 1. Agent identity registry

An **agent** is a durable, named actor bound to one virtual key (a key can front many agents — e.g. a CI key running `pr-bot` and `deploy-bot`). Register agents in **Console → MCP / Agents**. Identity is deliberately *not* taken from an agent runner's ephemeral per-spawn headers — it comes from the registry, bound to the authenticating key, so "which agent, on whose behalf" is answerable from the signed record and cannot be forged by presenting another key's agent id.

A tool call presents its agent via the `X-Wardin-Agent-Id` header. The gateway accepts it only if it resolves to a registered agent **bound to the same key** the request authenticated with; a cross-key or cross-tenant agent id is rejected with a `403` and recorded as an `AGENT_IDENTITY: FAIL` check in the chain.

### 2. Tool allowlist policy

`tool_allowlist` is a [policy type](/concepts/policies) — which tools an agent may invoke. Patterns are globs, authored in **Console → Policies**:

```yaml theme={null}
allowed_tools:
  - github__*
  - read_file
```

Like `model_allowlist`, the gateway fail-opens on an empty list (so an empty allowlist is blocked at save time). An off-list tool is denied with a structured `403` and a signed `TOOL_ALLOWLIST: FAIL` check.

### 3. Enforcement mode

Governance follows an **observe → enforce** ramp, set per tenant in **Console → Organization → Agentic Governance**:

| Mode              | `/mcp/*` endpoints       | Tool calls                                                         | Agent identity                                                             |
| ----------------- | ------------------------ | ------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| **off** (default) | disabled — returns `404` | —                                                                  | —                                                                          |
| **permissive**    | live                     | allowed, metered, signed; only an explicit `tool_allowlist` denies | a presented agent id must resolve, but is optional (falls back to the key) |
| **enforced**      | live                     | must pass a covering `tool_allowlist` (fail-closed)                | a registered `X-Wardin-Agent-Id` is **required**                           |

Mode changes are picked up by the gateway on its refresh cycle — no redeploy.

## The governance endpoint

Point a tool call at the gateway's `/mcp/tool-call` endpoint with your virtual key and (optionally) an agent id:

```bash theme={null}
curl https://gw.wardin.ai/mcp/tool-call \
  -H "Authorization: Bearer wardin_sk_..." \
  -H "X-Wardin-Agent-Id: <agent-id>" \
  -H "Content-Type: application/json" \
  -d '{"server": "github-mcp", "tool": "create_pull_request"}'
```

An allowed call returns `200 {"decision":"allow", ...}`; a denied call returns a structured `403` (`TOOL_NOT_ALLOWED`, `AGENT_REQUIRED`, or `AGENT_INVALID`). Either way a signed receipt is written.

## The live proxy

The decision endpoint tells you *whether* a tool call is allowed; the **live proxy** actually runs it. Connect an upstream MCP server in **Console → MCP / Agents** (name, URL, credential), then point any MCP client at `https://gw.wardin.ai/mcp/servers/<name>` with your virtual key. See the [connect-an-MCP-server guide](/guides/connect-mcp-server) for a full walkthrough.

For each request the proxy:

1. Authenticates the virtual key and checks the tenant's [enforcement mode](#3-enforcement-mode) (`off` → `404`).
2. Resolves the upstream server by `<name>` from your connected servers.
3. **Governs a `tools/call`** with the same enforcement as the decision endpoint (agent identity → tool allowlist). Other JSON-RPC methods — `initialize`, `tools/list`, notifications — are forwarded transparently so the MCP handshake works; only a tool *invocation* is governed.
4. Forwards an allowed call to the upstream with the stored credential, captures the **real upstream outcome** as a signed `UPSTREAM: PASS`/`FAIL` check, and relays the response. A denied call never reaches the upstream — it returns a JSON-RPC error and a `FAIL` receipt.

Because the proxy speaks JSON-RPC, a denial is delivered as a JSON-RPC error (the MCP-native shape) rather than an HTTP `403`, so the agent's client parses it as the call's outcome.

Each governed tool call is bucketed into the caller's **[session](/concepts/sessions)** (idle-timeout, or an explicit `X-Wardin-Session-Id`), so the signed record answers *which agent → which tools* within one task. **Console → MCP / Agents** shows a per-server **LAST CALL** badge — the most recent forwarded outcome, so a failing upstream is visible without leaving the product.

## Signed tool-call receipts

Every governed tool call — allowed, tool-denied, or identity-denied — is chained into the **same per-tenant [receipt chain](/concepts/receipts)** as model calls, so an auditor verifies one continuous, tamper-evident chain of model *and* agent activity. Tool receipts reuse the canonical receipt fields (`provider` = the MCP server, `model` = the tool name, tokens/cost = 0) and carry the enforcement decision as signed `AGENT_IDENTITY` / `TOOL_ALLOWLIST` checks — plus, for a forwarded proxy call, the `UPSTREAM` outcome. The `kind` marker (distinguishing `tool_call` from `model_call` receipts for querying) and the `session_id` linking a call to its task are both **unsigned side channels** — they don't change the signed content, so the [offline verifier](/concepts/offline-verification) needs no special handling.

## Honest scope

Agentic governance covers **gateway-routed tool calls only** — tools an agent invokes outside the gateway are not seen. The mappings and controls here are runtime records, not a certification.

The live proxy forwards to **remote HTTP MCP servers** (a hosted gateway can't sit in a local stdio pipe). Upstream credentials are encrypted at rest. Today it handles the request/response tool-call shape; it does not yet consume server-initiated SSE streams, and `tools/list` is forwarded whole rather than filtered to the allowlist (so an agent may *see* a tool it cannot *invoke* — the invocation is still blocked). Session→tools lineage is recorded; wiring a tool's outcome into quality scoring is the remaining roadmap item.
