User guideDeveloper guideGoverning tool calls
Developer guide

Governing tool calls

The four decisions, approval polling, run correlation and fail-modes.

Governing tool calls

When your agent asks the gateway about a tool call, it gets back one of four decisions. This page explains each one, how to group calls into a run and how failures are handled.

The four decisions

DecisionWhat it means and what to do
allowThe call passed all checks. Run the tool.
denyA guardrail, or the agent key's tool lists when they apply to native calls, blocked the call. Do not run the tool. The response includes a reason.
approval_requiredA human must approve first. The response includes an approval id and a poll endpoint. Wait for approval, then retry.
rate_limitedThe agent key went over its per-minute limit. Back off and retry later.

Agent key tool lists

An agent key's allowed and blocked tool lists always apply to MCP server tools called through the proxy. They apply to native tool calls (the hook script and the Python SDK) only when the key has "Also apply tool lists to native tool calls" turned on. It is off by default, because lists written for MCP tools would otherwise deny Bash, Edit and other built-in tools of a key shared with Claude Code or Cursor.

When the setting is on, the native hook checks the lists first, before any guardrail or approval rule. A tool that is not on the allowed list, or is on the blocked list, is denied straight away. A key with no lists is not affected.

json
{
  "decision": "deny",
  "reason": "Tool 'Bash' is not allowed for this agent key"
}

Handling approval

When the decision is approval_required, the gateway has created a request for a human to approve or deny. Poll the status endpoint until you get a final answer.

json
{
  "decision": "approval_required",
  "approval_id": 42,
  "poll_endpoint": "/v1/mcp/approvals/42/status",
  "expires_at": "2026-04-20T15:30:00+00:00"
}
  1. Call GET /v1/mcp/approvals/{approval_id}/status with your agent key.
  2. When the status is approved, run the tool.
  3. When the status is denied, do not run it.
  4. When the status is expired, the window passed with no decision; treat it as a block.

On the MCP proxy path the same information comes back as a JSON-RPC error with code -32001, carrying the approval id, poll endpoint and expiry in error.data.

Monitor only mode

An Admin can set an agent key to monitor only, in the Mode setting of the Agent keys page. Use it to see what your rules would do to an agent before you enforce them. Every rule is still checked, but the call is never blocked or paused: the decision is always allow and the agent runs the tool.

  • What is checked: Guardrails (a block or mask match), require approval rules, the key's allowed and blocked tool lists on the MCP proxy (and on native tool calls when the key's "Also apply tool lists to native tool calls" setting is on), and on the MCP proxy tools marked as requiring approval.
  • No approvals: No approval request is created and nobody is notified, so there is nothing to poll.
  • Still enforced: The key's rate limit, and on the MCP proxy the circuit breaker. They protect the gateway and your MCP servers rather than express policy, so a monitor only key can still get rate_limited.
  • Tool list on the proxy: tools/list is not filtered by the key's tool lists, so the agent sees every tool.

When something matched, the allow response carries a monitored object saying what would have happened. On the MCP proxy the same object is in the tool result under _meta["verifywise.ai/monitored"]. See the API reference for its fields.

json
{
  "decision": "allow",
  "monitored": {
    "would": "deny",
    "reason": "PII detected",
    "source": "guardrail",
    "rule": { "id": 11, "name": "No emails" },
    "outcomes": [
      { "would": "deny", "reason": "PII detected", "source": "guardrail", "rule": { "id": 11, "name": "No emails" } }
    ]
  }
}

In Activity these calls appear with the status Would block or Would need approval instead of blocked or approval required. When a call matched several things, the status is the most severe one and each match is listed in the call's events. The result summary is the tool's own result, as for any allowed call.

Grouping calls into a run

A run groups one turn of work: the model calls and the tool calls that belong together. They are tied by a shared run id.

  • Tool calls: The native hook reuses the agent session id it already sends, so tool calls are grouped automatically.
  • Model calls: Send the same id on your model requests using the header x-vw-agent-run-id. The aliases x-session-id and helicone-session-id are also accepted.
Use the same id on every call
A turn only groups correctly when the agent sends the same run id on every call, including sub-calls. A call with no id is still checked and logged; it just will not appear inside a run.

Failures and fail-modes

  • Gateway unreachable: With VW_FAIL_MODE=open (the default) the call runs anyway, so an outage never blocks your agent. Set it to closed to block instead.
  • Approval timed out: Controlled by VW_APPROVAL_FAIL_MODE (default closed, which blocks). Set it to open to allow on timeout.
  • Rate limited: Follows VW_FAIL_MODE. The call is an infra outcome, not a policy block.
PreviousConnect Python agents
NextAPI reference
Governing tool calls - Developer guide - VerifyWise User Guide