Skip to content

MCP human-in-the-loop: ask a reviewer before an agent acts

A tested MCP human-in-the-loop example using ask_human and get_action. See how to route a decision to a reviewer and enforce it before another tool runs.

Suppose an agent drafts a public status update after an incident. The draft says the issue is resolved. Before publishing that sentence, someone who knows the incident should check it. An MCP human-in-the-loop flow gives the agent a way to ask, wait, and read the answer. The publishing system still has to enforce that answer before posting anything.

This guide uses ActionBox's public MCP tools for a small, tested example. The status update is fictional. No publishing tool is connected in the test.

The three parts of an MCP approval

PartResponsibility
Agent or MCP hostPrepare the exact proposed operation and call ask_human
ReviewerRead the request and answer in the hosted ActionBox inbox
Application that owns publishingCheck the current decision and draft, then run or skip the publishing tool

That last part matters. Giving an agent an approval tool does not prevent it from calling a different MCP server directly. If the operation must be impossible to perform without permission, put the check in the application or gateway that controls that operation. The MCP security and authorization guide covers that boundary in more detail.

Connect ActionBox to an MCP host

Create a Source in the hosted ActionBox dashboard. Install the public CLI, configure its Source credential outside the chat, and check the connection:

bash
python -m pip install actionbox
actionbox configure
actionbox doctor

For an MCP host that can launch a local command, add the stdio bridge:

json
{
  "mcpServers": {
    "actionbox": {
      "command": "actionbox",
      "args": ["mcp"]
    }
  }
}

The bridge connects to ActionBox's hosted MCP service. Keep the Source key in the CLI's protected configuration; do not paste it into the host configuration, a prompt, or a repository. The MCP server documentation also covers the remote endpoint and the current tool inventory.

Example: review a public status update

Give the agent a proposed message and ask it to call ask_human. These are the tool arguments used in our backend test; they are not a complete JSON-RPC envelope:

json
{
  "title": "Publish the proposed status update?",
  "description": "The update says the incident is resolved. Check that claim before publishing.",
  "interaction": { "type": "boolean", "label": "Publish this update?" },
  "context": [{
    "type": "key_value",
    "title": "Proposed update",
    "items": {
      "Incident": "INC-482",
      "Destination": "Public status page",
      "Draft": "Service is restored. We are monitoring for recurrence."
    }
  }],
  "context_request_mode": "unsupported",
  "idempotency_key": "status-update-incident-482"
}

ask_human returns an Action ID, an open status, a version, and a fingerprint. Save them with the incident ID and the exact draft the reviewer saw. This call creates a request; it does not pause every other tool in the host, publish the update, or count as approval. The idempotency_key lets a retry recover the same logical request. Use a distinct key for each incident and draft version. Set context_request_mode to unsupported here because this simple example has no way to answer follow-up questions while it waits.

A person now reviews the request in ActionBox. While it remains open, get_action returns status: "OPEN" with no response. After the reviewer answers, call get_action with the returned Action ID:

json
{ "action_id": "<the Action ID returned by ask_human>" }

In the tested approval path, the result becomes status: "RESOLVED", resolved_by: "user", and response: {"type": "boolean", "value": true}. The rejection path returns the same type with value: false. Those are two different outcomes; a resolved status by itself is not permission to publish.

The test creates a request through MCP, reads it while open, submits a human decision through the reviewer API, and reads the resulting typed response through MCP for both values. It does not claim that a third-party publishing tool was exercised.

Put the gate around the publishing tool

The host or gateway that owns publish_status_update needs a rule like this. This is pseudocode for your integration, not a built-in ActionBox policy or a copy-and-run SDK snippet:

plaintext
save the exact draft and incident ID
ask_human to review that draft
wait for a decision, or stop when the request expires
read the Action again by its ID

if the Action was resolved by a human
   and its typed response is boolean true
   and the reviewed draft, recipient, and incident still match:
       call publish_status_update once with a stable downstream idempotency key
       record whether publishing actually succeeded
else:
       do not publish

The application must also prevent a direct path to publish_status_update that skips this check. Bind the decision to the draft version: an approval for “service restored” should not authorize a later draft that promises a refund. If the draft changes, ask again. If a worker restarts, recover the saved Action ID and draft rather than creating a fresh request or treating a timeout as consent.

Do not use ActionBox's resolve_action tool to manufacture an approval. It records a machine-selected resolution, not a human answer. Restrict the tools available to the agent when it only needs ask_human and get_action.

When the MCP host's own prompt is enough

For an immediate, low-stakes call, an MCP host's confirmation or elicitation UI may be the simpler choice. The current MCP specification supports asking for input during a tool interaction. Use that path when the person at the keyboard is the right reviewer and the decision can happen in the same session.

A durable Action helps when the reviewer is someone else, may answer later or from a phone, or when the decision must be retrieved after the agent session ends. It does not replace the host's tool permissions, and it does not turn every call into an approval requirement automatically.

Test the failure paths before using a live tool

Start with a no-op publishing function. Confirm that yes reaches it once and no, expiry, cancellation, and no response never reach it. Then test a changed draft, a restarted worker, a repeated MCP request, and a publishing call that succeeds just before the worker loses its connection. Approval and successful execution are separate facts.

If your workflow is outside MCP, the same decision pattern appears in the human approval workflow guide and the human approval API. Create a free Source to try the hosted MCP tools, or email info@actionbox.cloud if you want help placing the gate in your application.

About the author

Suson Sapkota

Suson founded ActionBox and works in software and data engineering. He writes about approval workflows, background jobs, and how to verify what happened after a human decision.

Turn the next risky operation into a reviewable decision.

Create a free Source, run the example from this guide, and keep the decision and execution outcome connected.