MCP
Tools
Source: orchestration/mcp-gateway/tool-exec.ts (ExecuteToolSchema, executeTool), orchestration/mcp-gateway/tool-outcome.ts (ToolOutcome, renderOutcome, pauseOutcome, classifyExecutionError), orchestration/mcp-gateway/credential-preflight.ts, orchestration/mcp-gateway/registry-cards.ts, orchestration/mcp-gateway/execution.ts, orchestration/mcp-gateway/execution-do.ts, orchestration/mcp-gateway/conversation.ts, orchestration/mcp-gateway/server.ts, orchestration/mcp-gateway/index.ts (argument pre-screen), core/core-base/tool-contract.ts, core/core-base/call-agent.ts (ALWAYS_GATED), orchestration/cnl-engine/dispatch.ts (gatedDispatchOp); live registry cards read 2026-09-24.
A structured tool-agent is a registry agent whose card has a Parameters table: weather-agent, markitdown, google-gmail, resend, x402-wallet-agent, … It takes typed fields, not a sentence. plungeai_execute_tool runs one operation of one such agent. It uses the same engine pipeline as every other execution tool, so the run lands in your execution history, shows in Studio's sidebar, and can be followed up. Every answer is a structured outcome, never a raw error.
Find ──► plungeai_list_agents the agent id (Discovery)
Inspect ──► plungeai_get_tool_contract fields, operations, gates, your credentials
Execute ──► plungeai_execute_tool pre-flight checks → engine → outcomeBase URL https://mcp.plungeai.com/v1. https://api.plungeai.com/v1/mcp is the same server; the One API also has a REST twin of this chapter (https://api.plungeai.com/docs#6-tools-plane (opens in a new tab)). Always pass user_request (Protocol & transport). Parameter types and limits are in the Tool reference.
6.1 Run a tool — plungeai_execute_tool
| Argument | Meaning |
|---|---|
agent_id | The registry agent id (required). Take it from plungeai_list_agents |
operation | One of the contract's operations[].name. Leave it out only for agents without operations |
params | The typed fields, exactly as the contract's inputSchema names them |
prompt | Free text. It satisfies missing requireds on prompt-capable cards, and in one narrow case is mapped onto the only required field (6.3) |
mode | sync (default) or async: return an execution id at once and run in the background |
format | markdown (default) or json (Protocol & transport) |
Your client usually does this for you. It fetches the contract, fills the form, then calls the agent. You just ask.
Try asking: "Give me a five-day weather forecast for Sun Valley, including air quality." The client runs weather-agent with typed fields, by place name, with no coordinates needed. The weather agent covers the full OpenWeather surface: current conditions, hourly and daily forecasts, air quality, geocoding and more. Here that means two calls, forecast and air-quality, or one one-call.
This call runs a real agent and writes an execution to your history (not run by the docs verifier):
curl -s https://mcp.plungeai.com/v1 \
-H "Authorization: Bearer $PLUNGE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"plungeai_execute_tool","arguments":{"user_request":"Give me a five-day weather forecast for Sun Valley, including air quality.","agent_id":"weather-agent","operation":"forecast","params":{"location":"Sun Valley,ID,US","units":"imperial"}}}}'A tools/call answers as a text/event-stream: live progress frames first, then the result (4.6). On success the result text is the agent's own output, followed by the execution footer:
…the forecast, as the agent rendered it…
---
execution: `<execution id>` — use this id for plungeai_get_result / plungeai_followup on this run.structuredContent carries the envelope:
{
"status": "ok",
"agent_id": "weather-agent",
"operation": "forecast",
"summary": "`weather-agent · forecast` completed.",
"result": "…",
"execution_id": "<execution id>"
}The run appears in your history as weather-agent · forecast. Its title is your user_request (or the prompt, or the params with secrets redacted, at most 500 characters).
6.2 What happens on a call
The order is fixed (executeTool in tool-exec.ts). Steps 1–4 can refuse the call, and a refusal there is not a run: nothing is dispatched and no execution is written.
- Argument check. The gateway validates the arguments against the tool's
inputSchemabefore the tool runs (validateToolArgs,index.ts). A missingagent_idor an out-of-range value returnsneeds_input("plungeai_execute_toolwas not called — its arguments do not match the tool's schema.") with the fields named. - Card fence (active-only). An unknown id returns
unavailable("No registry card with id<id>exists — agent ids cannot be guessed or carried over from other platforms. Nothing was executed."). An inactive card returnsunavailable("<id>is registered but notstatus:active— only active agents are callable over MCP."). Both include up to three live alternatives. If the registry itself does not answer, the call proceeds (fail-open) with a thin contract, and the engine is the backstop. - Contract validation. Params whose names belong to the task envelope (
agent,type,id,operation,depth,user_id,executor_user_id,workflow,execution_id) are rejected withneeds_input"These params collide with task-envelope fields and cannot be passed". Missing required fields (global, plus the chosen operation'srequired_params) are rejected withneeds_input"Required parameters are missing. Nothing was executed.", with the full contract attached. The missing-field check is skipped when you send aprompt. - Credential pre-flight. If the agent needs one of your connected accounts or your own key and it is not connected, expired or in error, the answer is
needs_connectionorneeds_api_keywith the exact thing to connect (6.7). This check fails open: if the status lookup fails, the call proceeds. - Selector fill and prose mapping (6.3).
- Execution row. A row opens in your history (
workflow_executions, statusrunning). - Dispatch.
mode: "async"hands the run to its execution Durable Object and returns at once (6.8). Sync runs the engine and streams progress. - Engine fence. An outward, irreversible operation is refused here, before the agent is called (6.6).
- Outcome. A failure is classified into the outcome vocabulary (6.4). A success is checked for a pause (approval or question), then returned. The classified status is also written to the execution's
outcomecolumn, so dashboards and D1 agree with what your client saw.
6.3 Typed params, prose and operations
Cards are written by people and under-document. The gateway smooths five things. All but the first add a ⚠️ warning line to a successful answer (warnings[] in structuredContent):
- Operation selector. The envelope carries
operation, but many agents read their operation from a param namedaction,oporoperationType, and silently run their default when it is absent. If the card declares one of these params and you did not set it,operationis copied into it. For example,resendreadsaction. - Prose onto the one required field. If you send only a
prompt(no params), and the card's requireds come down to exactly one free-text string field, the prompt is passed as that field. A report generator whose only required field iscontentis one example. You then get the warning "prose mapped: your prompt was passed as<field>(this card's only required parameter). If that is not what you meant, retry with typed params." The prompt is never mapped into an operation selector, an enum field, or a URL field (action,op,operation,operationtype,function,endpoint,url,uri,link): a sentence is not an action name and not a URL. - Prose on a multi-operation card. A structured agent does not interpret a prompt. Without
operationit runs its default operation, which reads as success while ignoring the request. So you get the warning "structured agent: the prose prompt is not interpreted — withoutoperationit ran its default operation. For anything else, pick an operation from the contract and pass typed params." - Unknown operation. An operation that is not on the card is not rejected; the call proceeds with a warning, because cards can under-document.
- Extras and aliases. Params not on the card are passed through verbatim (
params not on the card (passed through verbatim): …). When a card marks two alternative fields both required (queryandprompt), filling one is enough (card also marks … required — likely alias/alternative fields; proceeding with what you provided.).
If a form is still missing something, the answer names the exact fields and shows the form. It never comes back as a bare error, so "do what it suggests" is always enough.
6.4 The outcome vocabulary
Every plungeai_execute_tool answer, and every other execution tool's answer, is one ToolOutcome (tool-outcome.ts). It is sent as Markdown text plus the same object as structuredContent, and the tool's outputSchema declares it:
{
"status": "ok | needs_input | needs_connection | needs_api_key | needs_approval | unavailable | error",
"summary": "…",
"agent_id": "…", "operation": "…",
"result": "…", "execution_id": "…",
"missing": ["…"], "warnings": ["…"],
"remediation": { "message": "…", "actions": [ { "action": "…" } ] },
"contract": { }
}status | Header in text | isError | Produced when | What to do |
|---|---|---|---|---|
ok | (the result itself) | false | The run finished. An async start is also ok | Show the result. Keep the execution id |
needs_input | 📝 NEEDS INPUT | false | A field is missing, malformed or reserved; the agent rejected its inputs (the contract is attached); the run paused with a question (⏸ AWAITING USER: …) | Fix the call from the contract, or relay the question and answer with plungeai_continue |
needs_connection | 🔌 NEEDS CONNECTION | false | A required connected account is missing or expired (pre-flight, nothing run); the upstream API answered 401; the account lacks a license; the OAuth token lacks scopes | The user connects the service in Ocean Studio → Connectors, then the client retries the identical call |
needs_api_key | 🔑 NEEDS API KEY | false | A bring-your-own-key agent has no key saved for this user (pre-flight); a run named a missing API key or developer token | The user saves the key once in Ocean Studio → Connectors, then the client retries |
needs_approval | ⏸ NEEDS APPROVAL | false | An outward, irreversible operation was refused by the engine; or the run staged an action and paused (⏸ AWAITING USER APPROVAL: …) | Relay it to the user verbatim; never approve on your own. A staged action (an approve_via action) continues with plungeai_continue {approve: true} after the user's yes. An engine refusal (no actions) cannot be continued: it runs only from Ocean Studio (6.6) |
unavailable | 🚫 UNAVAILABLE | false | The id is unknown or not active; the agent is registered but not deployed (a platform gap, not your mistake); the agent reported a temporary condition itself (quota, upstream down) | Pick one of the alternatives in the answer |
error | ❌ ERROR | true | A genuine failure, with the real reason: the run timed out; the agent failed; a transient platform error | Fix the input and rerun, or relay the reason. Don't keep polling |
isError is true only for error. The guidance statuses are answers your client acts on, not protocol failures (MCP Tools → Error Handling). JSON-RPC protocol errors are kept for the edge: authentication, rate limits, unknown tools and malformed requests (Errors, feedback & limits).
A non-ok answer is rendered in this order (renderOutcome):
📝 NEEDS INPUT — `weather-agent` air-quality-history
Required parameters are missing. Nothing was executed.
Missing required: `start`, `end`
**To proceed:** Fill the missing fields (contract below) and retry the same call.
- Provide `start` — Unix start time — (operation: air-quality-history, required there) and retry the same call.
- Provide `end` — Unix end time — (operation: air-quality-history, required there) and retry the same call.
---
# Weather Agent (`weather-agent`)
…the full contract…
_This is structured guidance, not a crash — do not retry the identical call. Follow the remediation, or relay it to the user._That is the real answer for {"agent_id":"weather-agent","operation":"air-quality-history","params":{"location":"Sun Valley,ID,US"}} (rendered from the live card, 2026-09-24). It is refused before dispatch, so it is free and writes no execution. When the outcome carries an execution id, an _(execution: …)_ line comes before the last line. Warnings follow at the end as ⚠️ lines.
A refusal is not a run. The argument check, the card fence, contract validation and the credential pre-flight all answer before an execution exists, so your history stays clean. From step 6 on, an execution row exists, even when the outcome is needs_input (the agent rejected its inputs), needs_approval (the engine fence) or error.
Classification of run failures
When a run fails inside the engine, classifyExecutionError maps the raw error, in this priority order:
- An explicit
[outcome:<status>]tag from the agent or engine wins. The tag is stripped from the text. - A declared
needs_approvalbecomes the trust-fence answer (6.6). - "requires a valid license" →
needs_connection(an external license constraint: the connected tenant needs a license from its admin; not a wrong call). ACCESS_TOKEN_SCOPE_INSUFFICIENT/ "insufficient authentication scopes" →needs_connection: reconnect in Studio to grant more scopes.- A named missing key ("… API key not configured", "No … API key available") or
developer_token→needs_api_key. 401, "invalid authentication",UNAUTHENTICATED→needs_connection, orneeds_api_keywhen the agent declares a bring-your-own key and no OAuth connector. The provider is named when the agent declares one (onlyplungeai_execute_toolpasses the declared credentials to the classifier).- "service binding … not found" →
unavailable("registered but not deployed … a platform-side gap"). - Bad-input phrasing ("missing", "invalid parameters", "please provide", "unknown operation", …) →
needs_inputwith the contract attached. - A timeout →
errorwithRetry with {"mode":"async"} — then poll plungeai_get_workflow_status. - Anything else →
error:Execution failed: <the real message, up to 800 characters>.
Two guarantees come with it. Limits are told to you, never silently applied: search agents declare their maximum query length on the card (for example brave-agent: "Maximum: 400 characters — over-limit queries refused"), and an over-limit query comes back as needs_input instead of being truncated. Provider failures surface with the real reason: a provider stream that fails leaves an error record, so the run reports the provider's error instead of waiting out a streaming timeout (execution.ts).
6.5 Remediation actions
remediation.actions[] is machine-readable. Each action also renders as one bullet under To proceed::
action | Fields | Rendered as |
|---|---|---|
provide_field | field, description? | Provide `<field>` — <description> and retry the same call. |
connect_provider | provider, url, scopes? | Connect **<provider>**: open https://studio.plungeai.com → Connectors and connect the account (scopes: …). Relay this to the user — it needs their browser. |
provide_api_key | provider, how | Provide a **<provider>** API key: Save your <provider> credential in Ocean Studio (https://studio.plungeai.com) → Connectors, then retry this exact call. |
approve_via | tool: plungeai_continue, execution_id | Ask the user to approve, then call plungeai_continue {execution_id: "<id>", approve: true}. NEVER approve on your own. |
respond_via | tool: plungeai_continue, execution_id, question? | Relay the question to the user ("<question>"), then call plungeai_continue {execution_id: "<id>", message: "<their answer>"}. |
retry_with | changes, note? | Retry with {"mode":"async"} — then poll plungeai_get_workflow_status. |
use_alternative | agent_id, note? | Alternative: `<agent_id>` — <first 120 chars of its description> |
The rule for clients, which the server also sends in its initialize instructions: follow the remediation, never retry the identical call blind, and never approve a gated action on your own. For needs_connection and needs_api_key, the step needs the user's browser. Relay it, wait for them to confirm, then retry the identical call. Your client usually self-corrects on needs_input: "do what it suggests" is enough.
6.6 The trust fence — NEEDS APPROVAL
Some operations are outward and irreversible: they move money or cause real-world side effects. The platform will not run them unattended. Two mechanisms produce needs_approval.
1. The engine floor refuses outright. The floor list is fixed in code and no YAML, prompt or card can remove it (ALWAYS_GATED, core/core-base/call-agent.ts):
buy · purchase · fetch_paid · send · transfer · pay · send_payment · withdraw · shop
At every deployed-agent dispatch, the engine reads the task's operation (or action). If it is on the list, the engine refuses before the agent is called (gatedDispatchOp, orchestration/cnl-engine/dispatch.ts). The MCP answer:
⏸ NEEDS APPROVAL — `resend` send
This operation is outward and irreversible (it can move money or cause real-world side effects) and will NOT run unattended. operation "send" on resend is gated (outward/irreversible) and requires human approval — it cannot run unattended.
**To proceed:** Relay this to the user and get their explicit go-ahead before it runs. Never approve it on your own. To perform it, run it from an interactive session (Ocean Studio) where a human can approve the action.
_(execution: `<execution id>`)_
_This is structured guidance, not a crash — do not retry the identical call. Follow the remediation, or relay it to the user._Nothing reached the agent. plungeai_execute_tool and plungeai_execute_agent refuse a floor operation before a run exists, so no execution row is written; a floor operation that only appears at run time (a templated operation in a workflow task) is refused by the engine and leaves a failed execution with outcome needs_approval. There is no paused session behind either, so plungeai_continue cannot resume it (it answers "No conversation session for this execution (expired or not a conversational agent)"). The operation can only run from an interactive session where a human approves it: Ocean Studio. The same floor applies to any task in a workflow you run with plungeai_execute_workflow.
This request is refused by design (not run by the docs verifier):
curl -s https://mcp.plungeai.com/v1 \
-H "Authorization: Bearer $PLUNGE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"plungeai_execute_tool","arguments":{"user_request":"Email the report to the team","agent_id":"resend","operation":"send","params":{"from":"reports@example.com","to":"team@example.com","subject":"Weekly report","text":"…"}}}}'2. An agent stages the action and pauses. Agents with their own approval step (the x402 broker, the concierge, bots) stop before spending and store a pending action. The run comes back as:
⏸ NEEDS APPROVAL — `x402-broker`
⏸ AWAITING USER APPROVAL: <the staged action> (<its price>). Relay this to the user verbatim and ask them to decide — NEVER approve on your own.
…the agent's own message so far (what it found, what it proposes)…
**To proceed:** User approval required before the action runs.
- Ask the user to approve, then call plungeai_continue {execution_id: "<id>", approve: true}. NEVER approve on your own.
_(execution: `<id>`)_
_This is structured guidance, not a crash — do not retry the identical call. Follow the remediation, or relay it to the user._Here plungeai_continue is the way forward, and only after the user said yes (Runs, results & follow-ups).
| Engine floor refusal | Agent pause | |
|---|---|---|
| Trigger | operation/action on the floor list | The agent staged a pending action |
| Agent called? | No | Yes. It stopped before the action |
remediation.actions | none | approve_via → plungeai_continue |
| Execution row | none when refused before the run; failed, outcome needs_approval when the engine floor fires at run time | completed, outcome needs_approval (the paused session is stored) |
| How to proceed | Run it from Ocean Studio with a human approving | User says yes → plungeai_continue {approve: true} |
Card-level gates. A card can mark more operations with gated:<op> tags (for example x402-broker → gated:execute). The contract shows them as ⚠️ GATED. The MCP gateway checks card tags itself before the run: a typed call that names a gated: operation comes back ⏸ NEEDS APPROVAL. A prompt-only call to the broker carries no operation, so it reaches the agent, which stops for approval before it pays. For the broker, the agent itself always stops for approval before paying (its card: "execute (pay-and-call in USDC — always stops for user approval, never auto-runs)").
Payment and blockchain categories. Agents in the payment and blockchain categories are not callable over MCP unattended, whatever the operation, exactly as on the One API's tools plane (https://api.plungeai.com/docs#6-tools-plane (opens in a new tab)). plungeai_execute_tool, plungeai_execute_agent and every task of plungeai_execute_workflow come back ⏸ NEEDS APPROVAL with nothing run. Today that is crossmint-agent, x402-wallet-agent, circle-agent and circle-wallet-agent, read operations included. Run them from Ocean Studio, where a human approves. Inside a mission, the loop's call_agent refuses gated operations and payment/blockchain agents unattended.
6.7 Connected accounts and your own keys
Some agents act as you: your Gmail, your OneDrive, your wallet. They need a credential only you can provide. The requirement comes from one ladder (resolveCredentialRequirements, credential-preflight.ts):
cred:<tier>:<provider>tags on the card (authoritative, for examplex402-wallet-agent→cred:byok_api_key:x402-wallet);- else a curated map of 40 agents (frozen; new agents declare credentials on their card);
- else platform-managed: nothing for you to connect.
| Tier | Meaning | Families |
|---|---|---|
platform | The platform's own keys (Secret Store) | Most agents: search, documents, weather, SEC, LLMs, … |
oauth_connector (private connector) | Your account, connected by OAuth | Google Workspace (provider google: Gmail, Calendar, Drive, Docs, Sheets, Slides, Meet, Tasks, Forms, Chat, Contacts, Keep, Vault, Classroom, Admin, YouTube, Analytics, Ads, Search Console, Business Profile, Merchant Center, Workspace assistant, Docs export); Microsoft 365 (provider azure: Outlook mail, calendar and contacts, OneDrive, Teams, SharePoint, OneNote, Excel, Word, PowerPoint, To Do, Planner) |
byok_api_key (bring-your-own key) | A key or wallet credential you saved | x402-wallet-agent and crossmint-agent (x402-wallet), circle-wallet-agent (circle-agent-wallet), circle-agent (circle); amazon-shopping-agent (amazon, optional: shown, never blocking) |
Where you connect: Ocean Studio, https://studio.plungeai.com (opens in a new tab) → Connectors. Connect once. Every surface (MCP, Studio, the apps, the One API) then uses the same connection. For how accounts flow into calls, see https://api.plungeai.com/docs#7-connectors--connected-accounts (opens in a new tab).
When it is checked:
plungeai_get_tool_contractshows the live status: "✅ connected as you@…", "⚠️ NOT connected", or "expired — reconnect" (5.4).plungeai_execute_toolchecks before anything runs. A missing, expired or errored required credential returnsneeds_connection(if any unmet requirement is OAuth) orneeds_api_key(if all are bring-your-own-key). The summary names the state (not connected,expired — reconnect it,in error state — reconnect it) and ends "Nothing was executed." The remediation says: "This needs the user, not you: relay the connect step to them, wait for confirmation, then retry the identical call." Aconnect_provideraction includes up to eight OAuth scopes.plungeai_execute_agentandplungeai_execute_workflowdo not pre-flight. A missing credential surfaces when the run fails (401 →needs_connection), and the answer asks you to connect "the required service", because those paths do not pass the agent's declared provider to the classifier.
The pre-flight fails open. If the status service does not answer, the call runs and the run-time classification (6.4) is the backstop. Ask your client "what does this agent need?" to see the live status before first use.
6.8 Long runs: mode: "async"
mode: "async" returns immediately with an ok outcome and the execution id:
Started `weather-agent · forecast` (async). Poll plungeai_get_workflow_status with execution_id `<id>`, then fetch output with plungeai_get_result. If the status reports ⏸ AWAITING USER APPROVAL or AWAITING USER, relay it to the user and continue with plungeai_continue.
---
execution: `<id>` — use this id for plungeai_get_result / plungeai_followup on this run.The run is held by its own Durable Object in ten-minute legs that resume from checkpoints (Workflows, 8.4). There is no live feed. Failures, pauses and results are read with plungeai_get_workflow_status and plungeai_get_result (Runs, results & follow-ups). Use async when a run may outlast your client's tool-call limit: Claude Desktop cuts a single call at about 4 minutes. For shorter runs the sync call keeps the stream alive with a heartbeat every 15 seconds.
6.9 Payments
Two agents handle money on the open x402 payment ecosystem. Both are active (live registry, 2026-09-24):
| Agent | Category | What it does | Credential | Gates |
|---|---|---|---|---|
x402-wallet-agent | payment | Your x402 wallet: wallet_info (address + USDC balance), check_price (the exact price of a known x402 URL), fetch_paid (pay a known URL within strict caps), status (poll a paid async job), payment_requirements. It never searches or chooses services: give it a URL | Your x402 Wallet connector (x402-wallet, bring-your-own) | Every operation over MCP (payment category); run it from Ocean Studio |
x402-broker | shopping | Prompt-driven, human-in-the-loop broker: discover services (Coinbase CDP Bazaar + Agentic.Market), inspect live prices at the origin, execute (pay-and-call in USDC), status, list wallets, ask you a question | Uses your connected x402 / Circle wallets | execute (card tag). Always stops for your approval |
Try asking
- "Check my x402 wallet balance, then use the broker to find and buy access to a weather API." The wallet call is refused over MCP (payment category); the broker handles discovery and asks for your approval before it spends anything.
- "Use the x402 broker to find StablePhone and place a call to [phone number]." The broker discovers and pays for the calling service, asking your approval first, then places the call. Check on it afterwards with the broker (
status).
The wallet is a payment agent, so every call to it over MCP comes back ⏸ NEEDS APPROVAL with nothing run, wallet_info included although it only reads. The refusal comes before the credential check, so it is the same with or without a connected wallet. Check your balance in Ocean Studio.
The broker takes a sentence. This runs a model and can lead to a purchase after your approval (not run by the docs verifier):
curl -s https://mcp.plungeai.com/v1 \
-H "Authorization: Bearer $PLUNGE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"plungeai_execute_tool","arguments":{"user_request":"Find an x402 weather API and tell me the price. Do not pay yet.","agent_id":"x402-broker","prompt":"Find an x402 weather API and inspect its exact live price at the origin. Do not pay anything."}}}'A direct fetch_paid through plungeai_execute_tool is refused by the floor (6.6). Unattended, it never spends. Pay through the broker's approval flow, or from Ocean Studio.
The other money agents in the active catalog (live registry, 2026-09-24): crossmint-agent (payment, x402-wallet credential), circle-agent and circle-wallet-agent (blockchain, your Circle credentials) are refused over MCP whatever the operation, like the wallet. The shopping agents amazon-shopping-agent and shopping-zinc (ecommerce-agents) follow the floor list: any operation on it (buy, purchase, pay, transfer, withdraw, shop, …) comes back ⏸ NEEDS APPROVAL with nothing run. Check an agent's contract first: its ⚠️ GATED operations and its credential status are both listed there (5.4).
When a run pauses to ask you something
Some agents stop instead of finishing: always before spending money, and sometimes to ask a question ("which wallet?", "what should the call say?"). Your client relays it and waits for you:
- The run comes back with ⏸ AWAITING USER APPROVAL (a staged action and its cost), as a
needs_approvaloutcome, or ⏸ AWAITING USER (a question), as aneeds_inputoutcome. Nothing has been executed yet. - Your client asks you to decide. Reply naturally: "yes" / "approve" to proceed, "no" or any other answer to change course. The client is instructed never to approve on its own.
- On approve, the client calls
plungeai_continue {execution_id, approve: true}and the held action runs exactly once. A stale or repeated approve is harmless: the agent answers that nothing is awaiting approval, and it can never pay twice. To deny or change course, the client passes your words asmessage.
Background (async) runs surface pending approvals too. plungeai_get_workflow_status returns them as structuredContent.continuation, and plungeai_get_result appends the ⏸ block, so a paused background run is never invisible (Runs, results & follow-ups).