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

# Errors, feedback & limits

> prompts.ts, tools.ts, tool-exec.ts, extras.ts, activity-log.ts, log-view.ts, execution-do.ts, leg-state.ts, core/core-base/content-guard.ts, orchestration/api-gateway/lib/auth.ts, MCP SDK 1.29.0, live registry cards (2026-09-24).


Source: `orchestration/mcp-gateway/tool-outcome.ts`, `server.ts` (`wrap`, `validateToolArgs`),
`index.ts`, `shared.ts`, `auth-middleware.ts`, `rate-limiter.ts`, `session-do.ts`, `resources.ts`,
`prompts.ts`, `tools.ts`, `tool-exec.ts`, `extras.ts`, `activity-log.ts`, `log-view.ts`,
`execution-do.ts`, `leg-state.ts`, `core/core-base/content-guard.ts`,
`orchestration/api-gateway/lib/auth.ts`, MCP SDK 1.29.0, live registry cards (2026-09-24).

When something cannot run, the platform does not just fail. It answers with **what is missing
and how to fix it**. Your client reads that answer and usually corrects itself. This chapter is so
that *you* know what is happening: the answer shapes, every error code, the limits, when to retry,
and how to read your own activity log. It applies to `https://mcp.plungeai.com/v1`. The same server
at `https://api.plungeai.com/v1/mcp` differs only at the edge ([4.11](/mcp/protocol#411-the-two-endpoints-at-the-transport-level)).

## 13.1 Three kinds of "no"

```
Your request
 ├─ stopped at the edge: auth, size, rate limit, parse,          → JSON-RPC error + HTTP status (13.6)
 │   unknown or fenced tool, unknown method/resource/prompt          nothing ran
 ├─ reached a tool that answered with guidance                   → a result with an outcome (13.2)
 │   needs_input / needs_connection / needs_api_key /                usually nothing ran
 │   needs_approval / unavailable / error
 └─ reached a tool that answered with a plain failure            → a result, isError: true, text only (13.5)
```

A client should branch in this order: first the HTTP status and the JSON-RPC `error`, then
`result.isError`, then `result.structuredContent.status`.

## 13.2 The structured-feedback contract

The six tools whose `outputSchema` is the outcome envelope **never answer with a bare error**:
`plungeai_execute_workflow`, `plungeai_execute_agent`, `plungeai_execute_tool`,
`plungeai_get_tool_contract`, `plungeai_run_mission` and `plungeai_learn`
([4.9](/mcp/protocol#49-outputschema-and-structuredcontent)). Every answer carries an **outcome**: a `status`
plus a `summary`, and usually a `remediation` saying exactly what to do. `plungeai_get_result` uses
the same envelope for its guidance answers (a missing or unknown id). A paused run is not an
envelope there: its result text ends with a ⏸ block, and `format:"json"` gives `{"content": …}`
with no `status`. For machine-readable pause state, read `structuredContent.continuation` from
`plungeai_get_workflow_status` ([9.3](/mcp/runs-results#93-check-status-plungeai_get_workflow_status)). Two paths
hold the promise for **every** tool: argument validation ([13.3](#133-argument-validation)) and
unexpected exceptions ([13.4](#134-transient-failures)).

| `status` | Text starts with | What it means | What to do | `isError` |
|---|---|---|---|---|
| `ok` | the result itself | It ran. This is your result. | Nothing. | `false` |
| `needs_input` | `📝 NEEDS INPUT` | A required field is missing or malformed. The answer names the exact field and, where the agent has one, **includes its full form** (the contract) with a worked example. A run that paused with a question also uses this status (`⏸ AWAITING USER: …`). | Say *"do what it suggests"*. Your client fills the form and retries. For a question, answer it (`plungeai_continue`). | `false` |
| `needs_connection` | `🔌 NEEDS CONNECTION` | The agent acts as *you* (Google, Microsoft, …) and your **connected account** is missing, expired, short of scopes, or lacks a licence. Nothing was run: the check comes first. | Connect the service once in Studio → Connectors, then ask again. | `false` |
| `needs_api_key` | `🔑 NEEDS API KEY` | A bring-your-own-key agent (wallets, private APIs) has no key saved for you. | Save your key once in Studio. Every surface can then use it. | `false` |
| `needs_approval` | `⏸ NEEDS APPROVAL` | Nothing has executed. Either an agent staged an action and paused (the summary starts `⏸ AWAITING USER APPROVAL: …`, with an `approve_via` action), or the **trust fence** refused an outward, irreversible operation (send, pay, transfer, buy, withdraw) before it ran (no actions, no paused session). | Staged action: reply "yes"/"approve" to proceed (`plungeai_continue`), or anything else to change course. Trust-fence refusal: nothing to continue over MCP; the operation runs only from Ocean Studio, where a person approves it ([6.6](/mcp/tools#66-the-trust-fence--needs-approval)). | `false` |
| `unavailable` | `🚫 UNAVAILABLE` | The agent cannot run here. Its id does not exist (`No such agent: …`), it is not `status:active`, it is registered but not deployed, or it reported itself temporarily unable (for example its data provider's quota ran out). Apart from a made-up id, this is a platform-side gap, **not your mistake**. | Pick one of the live alternatives listed in the answer, or retry later if it gave a reason. | `false` |
| `error` | `❌ ERROR` | A genuine failure, with the real reason stated (for example an upstream API limit, or a timeout). | Fix the input and rerun, or follow the retry hint. Don't keep polling. | **`true`** |

Every non-`ok` answer ends with the line
`_This is structured guidance, not a crash — do not retry the identical call. Follow the remediation, or relay it to the user._`

**The envelope** is the `structuredContent` object: `status`, `summary`, `agent_id`, `operation`,
`result`, `execution_id`, `missing[]`, `warnings[]`, `remediation { message, actions[] }` and
`contract` (field reference in [4.9](/mcp/protocol#49-outputschema-and-structuredcontent)). The Markdown text
shows the same information as a status line, the summary, `Missing required: …`,
`**To proceed:** <message>` and one bullet per action. Each warning becomes a `⚠️` line.

**Remediation actions** (`RemediationAction`), and how each is rendered for your client:

| `action` | Fields | Rendered as |
|---|---|---|
| `provide_field` | `field`, `description?` | Provide `<field>` — `<description>` and retry the same call. |
| `connect_provider` | `provider`, `url`, `scopes?` | Connect **provider**: open `<url>` → Connectors and connect the account. Relay this to the user — it needs their browser. |
| `provide_api_key` | `provider`, `how` | Provide a **provider** API key: `<how>` |
| `approve_via` | `tool: plungeai_continue`, `execution_id`, `note?` | Ask the user to approve, then call `plungeai_continue {execution_id, approve: true}`. NEVER approve on your own. |
| `respond_via` | `tool: plungeai_continue`, `execution_id`, `question?` | Relay the question to the user, then call `plungeai_continue {execution_id, message: "<their answer>"}`. |
| `retry_with` | `changes`, `note?` | Retry with `{…}` (for example `{"mode":"async"}` after a timeout). |
| `use_alternative` | `agent_id`, `note?` | Alternative: `<agent_id>` — `<note>` |

Connection and key remediations point at Ocean Studio, `https://studio.plungeai.com` → Connectors.

**A refusal is not a run.** If the platform stops a call before dispatch (a missing field, a
missing connection, an unknown or inactive agent, an unknown model), no execution is created.
Nothing half-happens, and your history stays clean. Looking up a result by an id that does not
exist gets a `needs_input` answer naming the id (`plungeai_get_result`), never a bare "not found".

**Limits are told to you, never silently applied.** Search engines declare their maximum query
length on their card (live-verified 2026-09-24): `serper-agent` 2,048 characters, `brave-agent`
400, `tavily-agent` 400. Send more and you get `needs_input` with the limit. **Your input is never
truncated behind your back.**

**The real reason comes back.** When a run fails, the classifier (`classifyExecutionError`) passes
the upstream reason through: `Execution failed: <reason>`, `The run timed out: <reason>`, or the
agent's own message on `unavailable`. You do not get a generic "something went wrong".

**Visible mapping, never guessing.** Send only prose to `plungeai_execute_tool` for an agent whose
form has exactly **one** required free-text field (a report generator's `content`, say), and the
platform fills that field with your prose and says so:
`` ⚠️ prose mapped: your prompt was passed as `content` (this card's only required parameter)… ``.
Details are in [Tools](/mcp/tools).

## 13.3 Argument validation

Every `tools/call` is checked against the tool's input schema **before** the tool runs
(`server.ts` `validateToolArgs`, called from `index.ts`). A failure is not a protocol error. It is
a `needs_input` outcome, returned as a normal JSON-RPC result (HTTP 200, plain JSON):

- `summary`: `` `plungeai_list_agents` was not called — its arguments do not match the tool's schema. ``
  followed by one `` `field`: message `` per problem;
- `missing[]`: required fields that were absent;
- `remediation.actions`: one `provide_field` per missing or bad field.

Nothing runs, but the call still counts toward your rate limit. Free to try:

```bash
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_list_agents","arguments":{"user_request":"list agents","limit":500}}}'
```

The bounds every tool shares (`shared.ts`, `tools.ts`). Per-tool parameters are in the
[Tool reference](/mcp/tool-reference).

| Kind of argument | Bound |
|---|---|
| ids (`agent_id`, `execution_id`, `workflow_id`, `task_id`, `model`, …) | string, at most 256 characters |
| short text (`search`, `folder`, `description`, `query`, …) | at most 2,000 characters |
| prompts and messages (`prompt`, `input`, `goal`, `message`, `content`, `source`, …) | at most 65,536 characters |
| `workflow_yaml`, `yaml` | at most 262,144 characters (256 KiB) |
| `user_request` | at most 4,000 characters |
| `limit` | integer 1–100 |
| `inputs` (`plungeai_execute_workflow`) | keys up to 128 characters, values up to 8,192 |
| `max_iterations` (`plungeai_run_mission`) | integer 1–50 |
| `maxTokens` / `max_tokens` (`plungeai_execute_agent`) | integer 1–64,000 |

This pre-screen covers **single-message** requests. Inside a JSON-RPC batch, bad arguments come
back as the SDK's own `isError` text (`Input validation error: …`) instead
([4.4](/mcp/protocol#44-json-rpc-methods)).

## 13.4 Transient failures

If a tool throws unexpectedly (a database hiccup, a service-binding blip), the server still answers
with an outcome (`server.ts` `wrap`):

- `status: "error"`, `isError: true`;
- `summary`: the error message and `(request <request id>)`. Messages that would expose internals
  (database or parser errors) are replaced with
  `An internal error occurred while running this tool.`;
- `remediation.message`: `This is a transient platform failure, not a bad request. Retry once in a few seconds; if it persists, report the request id above.`

`format: "json"` is honoured on this path too. The full error goes to the server logs under the same
request id.

## 13.5 Plain tool errors

Tools without an `outputSchema` answer their own failures as a plain text result with
`isError: true` and no `structuredContent`. Examples from the code:

| Tool | Text |
|---|---|
| any tool without a user (internal calls only) | `Authentication required.` |
| `plungeai_get_workflow_status` | `Execution not found.` (the id is unknown or not yours) |
| `plungeai_get_result`, `plungeai_get_workflow_status` | `That is a scheduler run id, which this tool cannot resolve. Use plungeai_schedule {action: "runs"} …` (ids starting `exec_`) |
| `plungeai_executions` | `execution_id is required for this action.` · `Execution not found.` · `Execution is <status>, not running — nothing to cancel.` |
| `plungeai_list_agents` | `Registry unavailable — try again shortly.` · `Registry search failed (<status>).` |

They are short and say what to do. Treat them like a `needs_input` or `error` outcome.

## 13.6 JSON-RPC and HTTP errors

Protocol errors mean the request never reached a tool. The body is always
`{"jsonrpc":"2.0","error":{"code":…,"message":"…"},"id":…}`. `id` is `null` when the server had not
parsed one.

| HTTP | `code` | `message` | When |
|---|---|---|---|
| 400 | `-32700` | `Parse error` | the body is not valid JSON |
| 400 | `-32700` | `Parse error: Invalid JSON-RPC message` | valid JSON, but not a JSON-RPC message (SDK) |
| 400 | `-32600` | `Invalid Request: Only one initialization request is allowed` | a batch holding `initialize` plus other messages |
| 400 | `-32600` | `Invalid Request: Server already initialized` | a second `initialize` on an opt-in session |
| 400 | `-32000` | `Bad Request: Unsupported protocol version: <v> (supported versions: …)` | an unsupported `MCP-Protocol-Version` header on a request after `initialize` (not checked on `initialize`, nor on a `tools/list` served from cache, [4.3](/mcp/protocol#43-protocol-version-and-initialize)) |
| 401 | `-32001` | `No valid authentication provided. Use an X-API-Key header or "Authorization: Bearer <api key>".` | no key, or the key is wrong, revoked or expired, or a Bearer token that is not `ozk_`. `WWW-Authenticate: Bearer` when you sent no credentials, `Bearer error="invalid_token"` when you did. |
| 401 | `-32001` | `API key not permitted from <ip>` | the key is fenced to certain IP addresses and this is not one |
| 403 | `-32000` | `Invalid Host header: <host>` | the request reached the server under a hostname it does not serve |
| 404 | `-32001` | `Session not found` | an `Mcp-Session-Id` that is unknown, expired, belongs to another key, or is over 128 characters ([4.5](/mcp/protocol#45-stateless-by-default-opt-in-sessions)) |
| 405 | `-32000` | `Method not allowed.` | `GET` or `DELETE` without a session. Header `Allow: POST, OPTIONS`. |
| 413 | `-32600` | `Request body too large (1 MiB max)` | over 1,048,576 bytes ([13.8](#138-body-caps-and-timeouts)) |
| 415 | `-32000` | `Unsupported Media Type: Content-Type must be application/json` | a complete `Accept` header with a non-JSON `Content-Type` |
| 429 | `-32000` | `Rate limit exceeded: Rate limit: <n> requests/minute` (or `/day`) | tier limit reached ([13.7](#137-rate-limits)). `Retry-After` header plus `error.data.retryAfter` (seconds). |
| 429 | `-32000` | `Too many failed authentication attempts` | too many failed logins from your IP. `Retry-After` header. |
| 200 | `-32602` | `Tool not found: <name>` | the tool name does not exist (single-message `tools/call`) |
| 200 | `-32602` | `Tool not permitted for this key: <name>` | the tool exists, but your key is fenced to other tools |
| 200 | `-32602` | `Invalid params: <fields>` | malformed `initialize` params |
| 200 | `-32602` | `MCP error -32602: Prompt not found: <name>` | `prompts/get` with an unknown name |
| 200 | `-32002` | `MCP error -32002: Resource not found: <uri>` (also `Workflow not found` / `Execution not found`) | `resources/read` ([12.11](/mcp/prompts-resources#1211-resource-errors)) |
| 200 | `-32601` | `Method not found` | a method the server does not implement ([4.4](/mcp/protocol#44-json-rpc-methods)) |
| 200 | `-32603` | `Registry error <status>`, or the SDK's validation text | a registry failure behind a resource, or request params that fail the SDK schema |
| 500 | `-32603` | `Internal error` | an unhandled server error |
| 404 | — | `{"error":"Not found. MCP endpoint: POST /v1 (Streamable HTTP)."}` | any path other than `/v1`, `/v1/`, `/mcp` and the public routes (not JSON-RPC) |

Unauthenticated requests, live-verified 2026-09-24:

```bash
curl -s -i -X POST https://mcp.plungeai.com/v1 -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
```

```bash
curl -s -i -X POST https://mcp.plungeai.com/v1 -H 'Authorization: Bearer not-a-key' \
  -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
```

An unknown tool is a protocol error, so it cannot be mistaken for "the tool ran and failed":

```bash
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":2,"method":"tools/call","params":{"name":"plungeai_nope","arguments":{}}}'
```

On `https://api.plungeai.com/v1/mcp`, the router answers its own edge errors in the One API
envelope: `401 unauthorized` (`Bearer ozk_ key required` / `Invalid or revoked API key`),
`429 rate_limited`, `413 payload_too_large`, each with `X-Error-Code`
([One API errors](https://api.plungeai.com/docs#13-errors-limits--headers)). Everything the MCP
server answers passes through unchanged.

## 13.7 Rate limits

Your key's tier sets two fixed windows, per minute and per day (`rate-limiter.ts`):

| Tier | Per minute | Per day |
|---|---|---|
| `free` | 30 | 1,000 |
| `pro` | 100 | 10,000 |
| `enterprise` | 300 | 100,000 |

- **Only tool calls count.** `initialize`, `tools/list`, `resources/*`, `prompts/*` and `ping`
  are free on `https://mcp.plungeai.com/v1` and on `https://api.plungeai.com/v1/mcp`, which forwards your key to the same gateway ([4.11](/mcp/protocol#411-the-two-endpoints-at-the-transport-level)).
- **The counter is per account** (your user id), not per key. Two keys on one account share it.
- **Windows are fixed, not calendar-aligned.** A window starts at the first counted call after the
  previous one ended and lasts 60 seconds (or 24 hours).
- A key with a missing tier is treated as `free`. An operator can give a key its own per-minute
  limit, which replaces the tier's per-minute number (the daily limit stays). Self-service keys use
  the tier limits.
- **Failed logins** are counted per client IP: 20 per minute and 500 per day. After that the server
  answers `429 Too many failed authentication attempts` until the window resets.

On a limit hit you get `429` with `Retry-After` (seconds until the window resets, at least 1) and
`error.data.retryAfter`, and the call does not run. Ask *"how much of my rate limit have I used?"*:
`plungeai_whoami` shows `minute_used` and `day_used` for the current windows. Its own call counts,
so the number includes it.

```bash
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":3,"method":"tools/call","params":{"name":"plungeai_whoami","arguments":{"user_request":"how much of my rate limit have I used?"}}}' \
  | sed -n 's/^data: //p'
```

These limits bound request rates. Spending (model tokens, paid APIs) is metered separately on your
account, and money-moving actions stop at the trust fence ([Tools](/mcp/tools)).

## 13.8 Body caps and timeouts

| Limit | Value | What happens |
|---|---|---|
| Request body | 1 MiB (1,048,576 bytes) | `413 -32600`. Checked against `Content-Length` before auth, then enforced on the stream itself (chunked bodies included). |
| Argument sizes | [13.3](#133-argument-validation) | `needs_input`, nothing runs |
| Keepalive | a progress line every 15 s while a tool runs | keeps the SSE stream from going idle ([4.6](/mcp/protocol#46-toolscall-over-sse-progress-and-keepalive)) |
| Registry-backed reads | 10 s | `Registry error` / `Registry unavailable` |
| Background runs | legs of 10 min; re-entered after 12 min; failed after 3 kills in a row without a checkpoint; `Interrupted (run did not complete)` after 16 min with no heartbeat | [4.7](/mcp/protocol#47-async-mode) |
| Opt-in sessions | 10 min idle | `404 Session not found`; re-initialize |

The server itself sets no request timeout on a `tools/call`. Your client may: Claude Desktop cuts a
single tool call at about 4 minutes (`server.ts`). Claude Code lets you set a per-server `timeout`
(milliseconds) in `.mcp.json`, or `MCP_TOOL_TIMEOUT`
([Claude Code docs](https://code.claude.com/docs/en/mcp), read 2026-09-24). For anything long,
use `mode: "async"` ([4.7](/mcp/protocol#47-async-mode)).

## 13.9 Retry guidance

| You got | Do |
|---|---|
| `429` rate limited | Wait `Retry-After` seconds, then retry. Don't hammer: every retry inside the window is refused again. |
| `401` | Don't retry. Fix or replace the key ([Authentication](/mcp/authentication)). From a new location, check whether the key is IP-fenced. |
| `-32602 Tool not found` | Refresh the tool list: reconnect your client, or call `tools/list` again. |
| `-32602 Tool not permitted for this key` | Don't retry. Use a key that allows the tool. |
| `404 Session not found` | Drop the `Mcp-Session-Id` and re-initialize. |
| `413` | Shrink the request. Move long text into a saved workflow, or split the work. |
| outcome `needs_input`, `needs_connection`, `needs_api_key` | Follow the remediation, then retry. **Never** retry the identical call blind. |
| outcome `needs_approval` with an `approve_via` action (an agent staged the action and paused) | Ask the user. Only after an explicit yes, call `plungeai_continue` with `approve: true`. |
| outcome `needs_approval` with no actions (trust-fence refusal: send / pay / buy / transfer / withdraw …) | Nothing to continue: relay it to the user. The operation runs only from Ocean Studio, where a person approves it ([6.6](/mcp/tools#66-the-trust-fence--needs-approval)). `plungeai_continue` answers `No conversation session…`. |
| outcome `unavailable` | Use an alternative from the answer. If the agent gave a temporary reason, retry later. |
| outcome `error` "transient platform failure" | Retry **once** after a few seconds. If it repeats, report the request id. |
| outcome `error` "The run timed out" | From `plungeai_execute_workflow`, `plungeai_execute_tool` or `plungeai_run_mission`: retry with `mode: "async"` and poll `plungeai_get_workflow_status`. `plungeai_execute_agent` has no `mode` and drops it, so the same retry runs synchronously again: run the agent as a one-task workflow through `plungeai_execute_workflow` with `mode: "async"` instead. |
| outcome `error` "Execution failed: …" | Read the reason and fix the input. Don't keep polling a failed run. |
| `-32603` / `500` | Retry with backoff. If it persists, report it with the request id from your activity log. |

## 13.10 The activity log

The server keeps a readable trail of everything your account did over MCP: one Markdown entry per
request, grouped per account per UTC day (`activity-log.ts`, `log-view.ts`). Use it to see exactly
what your client sent, which is often **not** what you typed, and what came back.

**Read it** with your own key. It is assembled live on every request, newest first:

```bash
curl -s "https://mcp.plungeai.com/log?date=$(date -u +%F)" \
  -H "Authorization: Bearer $PLUNGE_API_KEY"
```

| Query | Meaning |
|---|---|
| `date=YYYY-MM-DD` | the UTC day. Defaults to today. Anything else gets `400 {"error":"date must be YYYY-MM-DD"}`. |
| `format=md` / `format=html` | force Markdown or HTML. Otherwise a browser (`Accept: text/html`) gets a rendered page, and curl gets `text/markdown`. |
| `refresh=1` | also rewrite the stored per-day document now (normally a cron does it every 15 minutes) |
| `key=<your key>` | for opening the log in a browser, where you cannot set a header. The key then sits in the URL and your browser history, so prefer the header. |

The same handler also answers at `/admin/log`. Responses are `Cache-Control: no-store`. A key
reads only **its own account's** trail. A missing or wrong key gets
`401 {"error":"Provide your API key: Authorization: Bearer <key>, X-API-Key, or ?key="}`.
The log is served on `https://mcp.plungeai.com` only.

**What an entry looks like** (the shape `activity-log.ts` renders):

```
### 14:03:12 · `plungeai_whoami` · ✅ · 212ms
- **User:** you@example.com · <your user id> · <client IP> · `<request id>`
- **Key:** <key label>

> **Asked:** how much of my rate limit have I used?

**Q:**
- **format:** markdown

- **A:** <the answer, whole if 300 characters or fewer, else "<n> chars · <first 240 characters>…">
```

| Part | What it holds |
|---|---|
| heading | UTC time, the event (a tool name, or `initialize`, `tools/list`, `resources/read`, …, or `rate_limited`, `tool_denied`), ✅/❌ for tool calls, duration |
| **User** / **Key** | the account, email, client IP and request id, and which key was used (its label). One account can hold several keys. Calls through `https://api.plungeai.com/v1/mcp` show your key label like any other call. |
| **Asked** | your `user_request`, in your own words, when the client sent one. This is why the server asks clients to always pass it. |
| **Q** | the tool arguments as fields, never truncated, including defaults the server filled in (such as `format: markdown`). YAML and multi-line text stay fenced. For protocol traffic, the params (e.g. the `plungeai://` URI read). |
| **Client context** | anything else your client put in `params._meta` besides the progress token |
| **A** | the answer as a receipt: whole when 300 characters or fewer, otherwise its length plus the first 240 characters. The full output stays in your execution history (`plungeai_get_result`). |
| **Error** | the full error text, when there was one. For invalid arguments: `invalid arguments: limit Too big: …`. |

**What is recorded.** Every authenticated request: tool calls, argument rejections, rate-limit hits,
fenced-tool denials and protocol methods. Failed logins are recorded without an account, so they do
not appear in your log. **Secrets are redacted** before anything is written: the value of any
argument whose **name** looks like a secret (`api_key`, `api_secret`, `token`, `secret`, `password`,
`passwd`, `credential`, `authorization`, `cookie`) is stored as `•••redacted•••`, including
everything nested under it (`core/core-base/content-guard.ts`). A secret pasted into free text,
such as a prompt, is **not** detected. Don't paste keys into prompts.
