MCP Tool-Call Policy
The MCP proxy puts AgentWatch between your MCP clients (Claude Code, Cursor, an agent framework, an IDE) and the MCP servers they call. The client connects to AgentWatch instead of the server; AgentWatch decides whether each call may go through, forwards it, and records what happened. Your clients need no plugin — they only point at a different URL.
Three independent checks run on every tool call, in this order:
| Check | Question it answers | When it refuses |
|---|---|---|
| Access list | May this caller use this server at all? | HTTP 403 Access denied to this MCP server |
| Tool rules | May this caller use this tool on this server? | JSON-RPC error -32001, data.blockCode: "mcp_tool_denied" |
| DLP on arguments | Do the tool’s arguments carry sensitive data? | JSON-RPC error -32600, data.blockCode: "mcp_dlp_blocked" |
A refused call never reaches the MCP server, and every refusal is written to the Usage Insights audit trail as a Blocked row with its reason — beside the calls that were allowed.
How do I put an MCP server behind AgentWatch?
Section titled “How do I put an MCP server behind AgentWatch?”- In the console, open MCP Proxy in the sidebar.
- Tick MCP Proxy Enabled, DLP Scanning and Log Tool Calls, and save.
- Click Add Server, give it a name (this becomes its id, e.g.
deepwiki), enter the server’s URL (e.g.https://mcp.deepwiki.com/mcp), choose Streamable HTTP, and save. Test should list the server’s tools. - Point the client at AgentWatch, authenticating with the employee’s gateway key:
{ "mcpServers": { "deepwiki": { "url": "https://agentwatch.example.com/mcp-proxy/deepwiki/mcp", "headers": { "Authorization": "Bearer ak_..." } } }}Settings changes take up to 30 seconds to reach the gateway. Tool-rule changes take effect on the next call.
Can I block one tool on a server, or only the whole server?
Section titled “Can I block one tool on a server, or only the whole server?”Both. The Access Control Lists panel decides who may use a server at all; the Tool Rules panel decides tool by tool.
A tool rule has four parts:
| Field | Meaning |
|---|---|
| Caller | A user email, or * for everyone |
| Server | One proxied server, or All servers |
| Tool | A tool name, or a pattern with * and ? (delete_*) |
| Action | Block or Allow, plus a priority (lower is checked first) |
How rules combine, for one caller on one server:
- No rule applies → the tool is allowed (tool rules are not in force for that caller there).
- The first rule, by priority, whose tool pattern matches decides. On a tie, Block wins.
- No rule matches the tool: if the caller has only Block rules on that server, the tool is allowed (a block list). If they have any Allow rule there, the tool is refused (an allow list).
So to stop everyone using one tool, add a single rule — Caller *, Server deepwiki, Tool ask_wiki_question, Action Block — and every other tool on that server keeps working. To give a contractor read-only access, add Caller contractor@example.com, Tool read_*, Action Allow: everything else on that server is refused for them alone.
A blocked tool is also removed from the caller’s tool list (tools/list), so the client is not offered a tool it will be refused.
The same rules are available over the API (company admin):
curl -s https://agentwatch.example.com/api/admin/agent-tool-whitelists \ -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \ -d '{"agentIdentifier":"*","serverId":"deepwiki","toolPattern":"ask_wiki_question","mode":"deny","priority":100}'POST /api/admin/agent-tool-whitelists/test with {"agentIdentifier":"user@example.com","serverId":"deepwiki","toolNames":["ask_wiki_question","read_wiki_structure"]} returns the decision the gateway would make for each tool — the same function the proxy enforces.
What does an allowed and a refused call look like?
Section titled “What does an allowed and a refused call look like?”Allowed — the server’s answer comes back unchanged:
curl -s https://agentwatch.example.com/mcp-proxy/deepwiki/mcp \ -H "Authorization: Bearer ak_..." -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_wiki_structure","arguments":{"repoName":"facebook/react"}}}'Refused by a tool rule:
{"jsonrpc":"2.0","id":2,"error":{"code":-32001, "message":"Tool 'ask_wiki_question' is blocked by your organization's MCP tool policy on server 'deepwiki'", "data":{"blockCode":"mcp_tool_denied"}}}Refused by DLP (for example a Social Security number in the arguments):
{"jsonrpc":"2.0","id":3,"error":{"code":-32600, "message":"Tool call blocked by DLP: sensitive data detected (PII; risk: 0.90)", "data":{"blockCode":"mcp_dlp_blocked"}}}Where do I see the log entry for each call?
Section titled “Where do I see the log entry for each call?”Open Usage Insights → Audit trail. Every MCP call is a row whose model reads mcp-<server> and whose preview reads tool_name({arguments}):
| Outcome | Status | Block code | Reason shown |
|---|---|---|---|
| Allowed | Completed | — | the tool’s response preview |
| Blocked by tool rule | Blocked | mcp_tool_denied |
which rule refused it |
| Blocked by DLP | Blocked | mcp_dlp_blocked |
the data category; also appears on the Security page |
| Blocked by access list | Blocked | mcp_server_access_denied |
the caller and the server |
The arguments of a refused call are scrubbed before they are stored — a refused call’s arguments are the part most likely to hold the very data it was refused for — so the preview shows [SSN-REDACTED], never the value. Each refusal is also written to Settings → Audit Logs as mcp.mcp_tool_denied, mcp.mcp_dlp_blocked or mcp.mcp_server_access_denied (at most one entry per caller, server and tool per minute, so a client retrying in a loop cannot flood it). Creating, changing or deleting a tool rule is audited as mcp.tool_rule_created / _updated / _deleted.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
403 Access denied to this MCP server |
The caller is not on the server’s Allowed list, or is on its Denied list | Add their email or team in Access Control Lists, or clear the server’s lists to allow everyone |
-32001 … blocked by your organization's MCP tool policy |
A tool rule refused the tool | Check Tool Rules; an Allow rule for a caller turns their access on that server into an allow list |
| A tool vanished from the client’s tool list | A tool rule blocks it for that caller | Same as above |
-32600 Tool call blocked by DLP |
Sensitive data in the tool arguments | Remove it, or add the value to the DLP allow list if it is legitimately permitted |
404 MCP proxy is not enabled |
The proxy is off for the caller’s organization | Tick MCP Proxy Enabled and save; wait 30 seconds |
| A refused call has no Blocked row | The caller sent no gateway key (header-only identity), so there is no employee to attribute the row to | Send Authorization: Bearer ak_...; the refusal is still in Settings → Audit Logs |
Upgrade note (2.0.377): tool rules created through the API before this release were stored but not enforced; they are enforced after the upgrade. Review MCP Proxy → Tool Rules when upgrading. The MCP Proxy admin pages now require a company administrator.
Sources and references
Section titled “Sources and references”- Agent Fleet & Insider Risk — how MCP governance fits the agent surface
- Data Protection & Guardrails — the DLP engine that scans tool arguments
- Observability & Insights — the audit trail these rows appear in