> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plungeai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Executing tools over MCP — execute_tool

> Structured tool-agents (cards with a Parameters table — posting, document conversion, weather, data-table CRUD, calendar operations, payment actions) take typed fields against a published contract, not prose.


<!-- sources-of-truth: orchestration/mcp-gateway/tools.ts, orchestration/mcp-gateway/server.ts, orchestration/mcp-gateway/tool-exec.ts, orchestration/mcp-gateway/registry-cards.ts, orchestration/mcp-gateway/tool-outcome.ts, orchestration/mcp-gateway/credential-preflight.ts | last-synced: 2026-09-24 (execute_tool half of the original discovery-and-execution.md; the list_agents/get_tool_contract half lives in plungeai-discovery, the execute_agent/get_result half lives in plungeai-agents. Re-verified ExecuteToolSchema + reserved-param list against tool-exec.ts/tool-contract.ts — all match, no drift found) -->
Structured tool-agents (cards with a Parameters table — posting, document
conversion, weather, data-table CRUD, calendar operations, payment actions)
take typed fields against a published contract, not prose. Always fetch the
contract first (`plungeai_get_tool_contract`, see `plungeai-discovery`) — the
contract IS the API documentation, generated live from the card and
personalized with the acting user's credential status.

## plungeai_execute_tool

**Purpose:** run one structured tool-agent directly with typed params,
through the same engine pipeline as every other execution (history, results,
HITL all included).

**Parameters**

| Param | Type | Notes |
|---|---|---|
| `agent_id` | string, required | From a live search (`plungeai_list_agents`). |
| `operation` | string ≤128 | One of the contract's operations. Also auto-filled into the card's selector param (`action`/`op`/`operationType`) when the card reads its operation from a param — so passing `operation` alone is safe. |
| `params` | object | Typed fields per the contract's JSON Schema. |
| `prompt` | string | Prose fallback. On a card whose requireds resolve to exactly ONE free-text param, a lone prompt is mapped into it (visibly, with a warning). Never mapped into selectors, enums, or URL params. |
| `mode` | `sync` \| `async` | Async returns an execution id to poll. |
| `format` | `markdown` \| `json` | Response serialization; default markdown. |

**Example**

```json
{"user_request": "what's the weather in Lisbon this weekend?",
 "agent_id": "weather-agent", "operation": "forecast",
 "params": {"location": "Lisbon", "days": 3}}
```

**Returns:** the outcome envelope, always (never a raw error):

| Status | Meaning → your move |
|---|---|
| `ok` | Result markdown + execution-id footer (+ any ⚠️ warnings, e.g. "prose mapped"). Relay verbatim. |
| `needs_input` | Missing/invalid fields; `missing[]` + the full contract ride along so you can fix the SAME call without a second lookup. Nothing was executed. |
| `needs_connection` / `needs_api_key` | The USER must connect a service or save a key in a PlungeAI app. Relay the instructions, wait for them, retry the identical call. Caught pre-flight when possible — before a run exists. |
| `needs_approval` | ⏸ gated operation paused mid-run. Relay verbatim; `plungeai_continue` after the user decides. |
| `unavailable` | Agent refused (unknown/inactive) or temporarily down — real alternatives included. Nothing was executed. |
| `error` | Terminal failure with a reason; follow the remediation (often `retry_with {mode: "async"}` for timeouts). |

**Failures & fixes**

- Reserved param names (colliding with the task envelope) → `needs_input`
  naming them; rename/drop those keys. The reserved names: `agent`, `type`,
  `id`, `operation`, `depth`, `user_id`, `executor_user_id`, `workflow`,
  `execution_id`.
- Prose prompt + no `operation` on a multi-operation card → the run executes
  the card's DEFAULT operation and says so in a warning. If that is not what
  the user meant, re-call with an explicit `operation` + typed params.
- Gated operations are not refused up front — they pause (`needs_approval`).
  That pause is a trust fence: surface it, never bypass it.

## Connected accounts (credential status)

The contract's Credentials section (fetched via `plungeai_get_tool_contract`,
see `plungeai-discovery`) is LIVE per acting user:

- `🔐 platform-managed` — nothing to connect; just call.
- `✅ connected as <email>` — the user's OAuth/API-key connection is live;
  call away.
- `⚠️ NOT connected` / `expired — reconnect` — `plungeai_execute_tool` will
  answer `needs_connection` / `needs_api_key` until the user connects that
  credential in a PlungeAI app (Studio → Connectors). Relay exactly what to
  connect; do not retry until they have.

The credential-status RPC runs speculatively in parallel with the card fetch
on `plungeai_get_tool_contract`, so the contract call itself carries this
status — no separate lookup needed before your first `execute_tool` attempt.

## Putting it together — the canonical multi-step run

```
1. plungeai_list_agents {search: "convert pdf to markdown"}   → markitdown
2. plungeai_get_tool_contract {agent_id: "markitdown"}        → schema; credentials 🔐 platform-managed
3. plungeai_execute_tool {agent_id: "markitdown",
     operation: "convert",
     params: {file_data: "<base64 of the file>", file_name: "report.pdf",
              format: "markdown"},
     mode: "async"}                                           → execution_id E
4. plungeai_get_workflow_status {execution_id: E}             → running → completed
5. plungeai_get_result {execution_id: E}                      → relay verbatim (see plungeai-agents)
```

Note how step 3 follows step 2's contract: `convert` requires `file_data`
(base64-encoded content) and `file_name` for uploaded files (a `url` param
is also accepted for web pages/YouTube/Wikipedia, but a file already in hand
goes through `file_data`). Skip step 2 only for agents whose contract you
fetched earlier in the same conversation. Skip steps 4–5 in sync mode — the
result comes back in the execute call itself.
