Developer guide

API reference

Endpoints, headers, auth and error codes for the tool-call hook.

API reference

The endpoints your agent uses to get tool calls governed. All calls authenticate with an agent key.

Authentication

Send your agent key (it starts with sk-mcp-) as a bearer token on every request.

bash
Authorization: Bearer sk-mcp-...

Headers

HeaderPurpose
AuthorizationBearer sk-mcp-... agent key. Required.
x-vw-agent-run-idGroups model calls into the same run. Aliases: x-session-id, helicone-session-id.

POST /v1/mcp/hook

Ask for a decision on a native tool call. The gateway never runs the tool; it only returns a decision.

Checks run in this order: the agent key's allowed and blocked tool lists (only when the key's apply_tool_lists_to_hook setting is on), then the guardrail scan, then approval rules, then the rate limit. A tool the key's lists don't permit gets a deny with the reason "Tool '<name>' is not allowed for this agent key".

Request body:

FieldDescription
tool_nameName of the tool, e.g. "Bash".
argumentsObject with the tool arguments, e.g. { "command": "ls" }.
session_idThe run id for this turn. Used to group the call into a run.
tool_use_idA unique id for this specific call.
bash
curl -X POST "$VW_GATEWAY_URL/v1/mcp/hook" \
  -H "Authorization: Bearer $VW_AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool_name":"Bash","arguments":{"command":"ls"},"session_id":"run-123","tool_use_id":"call-1"}'

Responses, one per decision:

json
{ "decision": "allow" }

{ "decision": "deny", "reason": "policy violation", "detections": [] }

{ "decision": "approval_required", "approval_id": 42,
  "poll_endpoint": "/v1/mcp/approvals/42/status",
  "expires_at": "2026-04-20T15:30:00+00:00" }

{ "decision": "rate_limited", "reason": "rate limit exceeded" }

For a monitor only agent key the decision is always allow (or rate_limited). When a rule matched, the response adds an optional monitored object. Treat the call as allowed either way; the field is for logging.

FieldDescription
monitored.wouldWhat an enforcing key would have got: "deny" or "approval_required". The most severe match.
monitored.reasonWhy, for that match.
monitored.sourceWhich check produced that match: "guardrail", "approval_rule" (a require approval rule), "tool_acl" (the key's allowed or blocked tool lists: on the MCP proxy, and on native calls when the key's apply_tool_lists_to_hook is on) or "tool_requires_approval" (a tool marked as requiring approval, MCP proxy only).
monitored.rule{ "id", "name" } of the rule behind that match, or null when it came from the key's tool lists or a tool marked as requiring approval.
monitored.outcomesEvery match, each with would, reason, source and rule.
json
{ "decision": "allow",
  "monitored": { "would": "approval_required",
    "reason": "rule 'Approve rm' requires approval",
    "source": "approval_rule",
    "rule": { "id": 21, "name": "Approve rm" },
    "outcomes": [ { "would": "approval_required",
      "reason": "rule 'Approve rm' requires approval",
      "source": "approval_rule",
      "rule": { "id": 21, "name": "Approve rm" } } ] } }

POST /v1/mcp/hook/result

Report the result of a tool that has already run, so the run shows what happened. Best-effort; it never blocks.

FieldDescription
tool_nameName of the tool that ran.
tool_responseObject with the tool result. An object with a non-null "error" key is recorded as a failed call, whatever the tool. Results over 10240 bytes (as serialized JSON) are cut.
session_idSame run id used on the hook call.
tool_use_idSame call id used on the hook call.
python
import httpx, os
httpx.post(
    f"{os.environ['VW_GATEWAY_URL']}/v1/mcp/hook/result",
    headers={"Authorization": f"Bearer {os.environ['VW_AGENT_KEY']}"},
    json={
        "tool_name": "Bash",
        "tool_response": {"stdout": "file1\nfile2"},
        "session_id": "run-123",
        "tool_use_id": "call-1",
    },
)

GET /v1/mcp/approvals/{id}/status

Check whether a pending approval has been decided. Returns a status of pending, approved, denied or expired.

bash
curl "$VW_GATEWAY_URL/v1/mcp/approvals/42/status" \
  -H "Authorization: Bearer $VW_AGENT_KEY"

JSON-RPC error codes

On the MCP proxy path (POST /v1/mcp), an approval requirement comes back as a JSON-RPC error.

CodeMeaning
-32001Tool requires approval. error.data has approval_id, poll_endpoint and expires_at.

With a monitor only agent key the proxy forwards the call instead of returning an error. When something matched, the JSON-RPC result carries the same monitored object as the hook in result._meta["verifywise.ai/monitored"].

Agent key mode

Each agent key has an enforcement_mode: "enforce" (the default) blocks and pauses calls by policy, and "monitor" only records what would have happened. It is set on the Agent keys page. Agent key responses include these fields:

FieldDescription
enforcement_mode"enforce" or "monitor". Accepted when creating or updating a key (Admin only); any other value is rejected with a 400.
enforcement_mode_changed_atWhen the mode last changed (ISO 8601), or null if it was never changed from enforce.
enforcement_mode_changed_byId of the user who last changed the mode, or null (also when that user has since been deleted).
enforcement_mode_changed_by_nameName of that user, or null. Returned when listing keys.
apply_tool_lists_to_hooktrue or false (default false). When true, the key's allowed and blocked tool lists also apply to native tool calls through POST /v1/mcp/hook; when false they apply to the MCP proxy only. Accepted when creating or updating a key (Admin only); a value that is not a boolean is rejected with a 400.

What gets scanned

For file-write tools, the gateway scans the content being written, not the file path or the text being removed. For other tools it scans the full arguments. This means writing sensitive data can be blocked, while deleting it is allowed.

PreviousGoverning tool calls
NextPlatform REST API
API reference - Developer guide - VerifyWise User Guide