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

# MCP tool reference

> Every tool on the hosted Moda MCP server with one example arguments block, a parameters table, and the CLI command it mirrors.

Complete reference for the 53 tools on the hosted Moda MCP server (`https://moda-mcp.modas.workers.dev/mcp`). Connection setup, authentication, and toolset filtering (`?toolsets=data,fixes`, `?toolsets=lean`) are covered in the [MCP server overview](/mcp/overview); the full tool-to-command map, including which CLI commands stay CLI-only, is in [CLI parity](/mcp/parity). This page lists each tool by area, in the same areas as the [CLI reference](/cli/reference).

Conventions used below:

* Authentication is per request: every call uses the API key (called an ingestion key in the dashboard) sent in the connection's `Authorization: Bearer moda_sk_...` or `x-api-key` header, and every result is scoped to that key's tenant. Calls without a key return setup guidance in-band instead of a transport error.
* Optional parameters you omit are left off the request entirely, so the backend default applies — the Default column shows `—` for these, and the description states the backend default where the server documents one. A concrete value in the Default column is materialized client-side and always sent, exactly like the CLI.
* Unknown ids return empty results with a warning, not errors.
* Heavy read tools (`conversations`, `search`, `context`, `frustrations`, `ask`) accept `response_format: "concise"` for a compact markdown rendering instead of the full JSON payload.
* Every read tool advertises `readOnlyHint: true`. Write tools (POST) are sent exactly once and never retried; `prompts_promote`, `fix_dismiss`, and `harness_delete` additionally advertise `destructiveHint: true`.
* All timestamps are UTC. Transcript excerpts, user quotes, and world-state values inside results are end-user production data — results quarantine them as untrusted content, never instructions.

## Production data

### overview

Returns the tenant's production health briefing: status (`attention`/`healthy`/`quiet`), key metrics, severity-ranked findings, a next-command hint, and the untouched `/overview` payload under `raw_overview`. The first call of any "how is my agent doing?" question.

```json Arguments theme={"dark"}
{ "days_back": 30 }
```

| Parameter   | Type    | Default | Description                                                                                                                                                             |
| ----------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `days_back` | integer | 7       | Window in days, 1–90. Unlike most parameters this is always sent on the wire — omitted it defaults to 7 client-side and is floored/clamped to 90, exactly like the CLI. |
| `raw`       | boolean | false   | Return the untouched `/overview` payload instead of the synthesized briefing. Client-side switch only, never sent on the wire.                                          |

CLI: [moda overview](/cli/reference#moda-overview)

### clusters

Lists the topic clusters Moda groups ingested conversations into, or — when `search` or `node_id` is set — runs fuzzy/semantic/hybrid cluster search (including deterministic deep-link resolution by node id). Follow up with `cluster_conversations` on a node id.

```json Arguments theme={"dark"}
{ "search": "calendar scheduling conflicts", "mode": "hybrid", "limit": 3 }
```

| Parameter    | Type    | Default | Description                                                                            |
| ------------ | ------- | ------- | -------------------------------------------------------------------------------------- |
| `parent_id`  | string  | —       | List children of this cluster node (list route only).                                  |
| `time_range` | enum    | —       | `all`, `1h`, `24h`, `3d`, `7d`, `30d`, `90d`. Backend default `all` (list route only). |
| `search`     | string  | —       | Free-text cluster search, 2–200 characters; switches to the search route.              |
| `node_id`    | string  | —       | Deterministic deep-link resolution, 1–512 characters; switches to the search route.    |
| `mode`       | enum    | —       | `fuzzy`, `semantic`, `hybrid`. Backend default `hybrid` (search route only).           |
| `limit`      | integer | —       | 1–50 (search route only).                                                              |

CLI: [moda clusters](/cli/reference#moda-clusters)

### cluster\_conversations

Lists the conversations grouped under one topic-cluster node, with pagination. An unknown node id yields an empty-200 payload plus a not-found warning.

```json Arguments theme={"dark"}
{ "node_id": "node_44", "limit": 10, "offset": 0 }
```

| Parameter | Type    | Default  | Description                                 |
| --------- | ------- | -------- | ------------------------------------------- |
| `node_id` | string  | required | The cluster node to list conversations for. |
| `limit`   | integer | —        | 1–100; backend default 10.                  |
| `offset`  | integer | —        | ≥ 0; backend default 0.                     |

CLI: [moda cluster-conversations](/cli/reference#moda-cluster-conversations)

### conversations

Lists ingested conversations filtered by summary text, cluster, user, time range, environment, world-state keywords, and outcome, with pagination. The main browse/filter surface when you have filters rather than a semantic query — for message-level search use `search`.

```json Arguments theme={"dark"}
{
  "world_state": "refund,enterprise",
  "outcome": "negative",
  "time_range": "7d",
  "limit": 20
}
```

| Parameter             | Type    | Default | Description                                                                                                    |
| --------------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------- |
| `search`              | string  | —       | Substring match on the conversation summary.                                                                   |
| `cluster_id`          | string  | —       | Only conversations in this cluster.                                                                            |
| `user_id`             | string  | —       | Only conversations for this end user.                                                                          |
| `time_range`          | enum    | —       | `all`, `1h`, `24h`, `3d`, `7d`, `30d`, `90d`. Backend default `all`.                                           |
| `environment`         | enum    | —       | `all`, `development`, `staging`, `production`. Backend default `all`.                                          |
| `world_state`         | string  | —       | Comma-separated keywords (max 200 characters) ANDed against world-state segment slots and the durable profile. |
| `outcome`             | enum    | —       | `any`, `positive`, `negative`. Default `any`; sent on the wire only when not `any`.                            |
| `include_world_state` | boolean | false   | Attach a world-state summary to each row (one extra parallel request per row, best-effort).                    |
| `limit`               | integer | —       | 1–100; backend default 20.                                                                                     |
| `offset`              | integer | —       | ≥ 0; backend default 0.                                                                                        |
| `response_format`     | enum    | —       | `json` or `concise`. MCP-only: `concise` returns compact markdown. Omit for JSON.                              |

CLI: [moda conversations](/cli/reference#moda-conversations)

### search

Message-level keyword/semantic/hybrid search across every ingested conversation, returning scored hits with `conversation_id`, `message_index`, role, and a snippet. Warnings report when the backend degraded to keyword mode or ran a different mode than requested.

```json Arguments theme={"dark"}
{ "query": "refund failed", "mode": "hybrid", "time_range": "7d", "limit": 20 }
```

| Parameter         | Type    | Default  | Description                                                          |
| ----------------- | ------- | -------- | -------------------------------------------------------------------- |
| `query`           | string  | required | The text to search for, 1–500 characters (wire param `q`).           |
| `mode`            | enum    | —        | `keyword`, `semantic`, `hybrid`. Backend default `hybrid`.           |
| `user_id`         | string  | —        | Restrict hits to this end user.                                      |
| `time_range`      | enum    | —        | `all`, `1h`, `24h`, `3d`, `7d`, `30d`, `90d`. Backend default `all`. |
| `limit`           | integer | —        | 1–100; backend default 20. No offset — pagination is limit-only.     |
| `include_tool_io` | boolean | false    | Also search tool call inputs/outputs (wire: `include_tool_io=true`). |
| `response_format` | enum    | —        | `json` or `concise`.                                                 |

CLI: [moda search](/cli/reference#moda-search)

### context

Returns a windowed transcript of one conversation: up to `window` messages either side of `msg_index`, plus `total_messages`, the summary, and each message's role, content, and tool call/result counts. The standard follow-up after `search`, `frustrations`, or `tool_failure_detail` hands you an anchor.

```json Arguments theme={"dark"}
{ "conversation_id": "conv_5e1a9c2b7d3f4680", "msg_index": 12, "window": 3 }
```

| Parameter         | Type    | Default  | Description                                                                               |
| ----------------- | ------- | -------- | ----------------------------------------------------------------------------------------- |
| `conversation_id` | string  | required | The conversation to read.                                                                 |
| `msg_index`       | integer | —        | Center the window on this message index (≥ 0); the backend picks the center when omitted. |
| `window`          | integer | —        | Messages either side of the center, 1–5; backend default 2.                               |
| `response_format` | enum    | —        | `json` or `concise`.                                                                      |

CLI: [moda context](/cli/reference#moda-context)

### world\_state

Reads the world state Moda tracked for one conversation, in three modes: the default event/summary view, a point-in-time snapshot at a message index, or a frame-by-frame replay. `snapshot` and `replay` are mutually exclusive; unknown ids yield an empty-200 payload plus a not-found warning.

```json Arguments theme={"dark"}
{ "conversation_id": "conv_5e1a9c2b7d3f4680", "snapshot": true, "msg_index": 42 }
```

| Parameter         | Type    | Default  | Description                                                                                                    |
| ----------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `conversation_id` | string  | required | The conversation whose world state to read.                                                                    |
| `summary_only`    | boolean | false    | Omit the per-message event stream (default route only; wire: `summary_only=1`).                                |
| `event_limit`     | integer | —        | 1–5,000; backend default 2,000 (default route only).                                                           |
| `snapshot`        | boolean | false    | Point-in-time snapshot mode.                                                                                   |
| `msg_index`       | integer | 0        | 0–100,000. Snapshot mode; always sent there, defaulting to 0.                                                  |
| `block_index`     | integer | —        | 0–100,000 (snapshot mode only).                                                                                |
| `segment_index`   | integer | —        | 0–65,535: scope the fold to one segment (snapshot mode only).                                                  |
| `replay`          | boolean | false    | Frame replay mode.                                                                                             |
| `format`          | enum    | —        | `frames` or `fold-inputs` (replay mode; default `frames`). `fold-inputs` travels as `fold_inputs` on the wire. |
| `message_count`   | integer | —        | 1–1,000,000. **Required** with `replay` in `frames` mode.                                                      |

CLI: [moda world-state](/cli/reference#moda-world-state)

### audit

Returns the ingestion audit for a conversation id or trace id: spans, hierarchy, orphans, and duplicates, optionally with raw records. Use it to debug instrumentation when a transcript from `context` looks wrong or incomplete.

```json Arguments theme={"dark"}
{ "id": "conv_5e1a9c2b7d3f4680", "include_raw": false }
```

| Parameter     | Type    | Default  | Description                                                                                                            |
| ------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| `id`          | string  | required | The conversation id or trace id to audit.                                                                              |
| `kind`        | enum    | —        | `auto`, `trace`, `conversation`. Default `auto` (try trace, then conversation); sent on the wire only when not `auto`. |
| `include_raw` | boolean | false    | Include raw ingested records (wire: `include_raw=1`).                                                                  |
| `limit`       | integer | —        | 1–1,000; backend default 200.                                                                                          |

CLI: [moda audit](/cli/reference#moda-audit)

### step\_scores

Returns Moda's per-step quality scores for one conversation: scored steps and segment rollups. A conversation that exists but has not been scored yet returns empty arrays with a warning.

```json Arguments theme={"dark"}
{ "conversation_id": "conv_5e1a9c2b7d3f4680" }
```

| Parameter         | Type   | Default  | Description                                  |
| ----------------- | ------ | -------- | -------------------------------------------- |
| `conversation_id` | string | required | The conversation whose step scores to fetch. |

CLI: [moda step-scores](/cli/reference#moda-step-scores)

### frustrations

Lists user-frustration detections, each row carrying user quotes, key turns, and a computed `anchor` (`conversation_id` + `msg_index`) pointing at the frustrated moment. The `emotions` tool is the multi-family superset of this legacy single-family view.

```json Arguments theme={"dark"}
{ "days_back": 14, "limit": 5, "include_window": true, "window": 2 }
```

| Parameter         | Type    | Default | Description                                                               |
| ----------------- | ------- | ------- | ------------------------------------------------------------------------- |
| `days_back`       | integer | —       | 1–90; backend default 7.                                                  |
| `limit`           | integer | —       | 1–20 (backend clamp); backend default 10.                                 |
| `offset`          | integer | —       | ≥ 0; backend default 0.                                                   |
| `include_window`  | boolean | false   | Attach a transcript window around each row's anchor.                      |
| `window`          | integer | 1       | 1–5: messages either side of the anchor. Only used with `include_window`. |
| `response_format` | enum    | —       | `json` or `concise`.                                                      |

CLI: [moda frustrations](/cli/reference#moda-frustrations)

### emotions

Lists multi-family emotion detections (`frustration`, `sadness`, `confusion`, `anxiety`, `trust`, `positive`) ranked by score, with a summary and signal breakdown that always cover all families — `family` filters the detections list only.

```json Arguments theme={"dark"}
{ "family": "confusion", "days_back": 7, "limit": 10 }
```

| Parameter   | Type    | Default | Description                                                                                                                       |
| ----------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `days_back` | integer | —       | 1–90; backend default 7.                                                                                                          |
| `limit`     | integer | —       | 1–20 (backend clamp, not the global 100); backend default 10.                                                                     |
| `offset`    | integer | —       | 0–10,000 (the backend's inclusive ceiling). Results are score-ranked; there is no cursor.                                         |
| `family`    | enum    | —       | `frustration`, `sadness`, `confusion`, `anxiety`, `trust`, `positive`. Filters the detections list; the summary stays all-family. |

CLI: [moda emotions](/cli/reference#moda-emotions)

### hallucinations

Lists hallucination detections — agent claims checked against tracked world state — with `contradicted` and `verified` kinds. `conversation_id` scopes the summary too; `kind` narrows the detections list only.

```json Arguments theme={"dark"}
{ "kind": "contradicted", "days_back": 7, "limit": 10 }
```

| Parameter         | Type    | Default | Description                                                         |
| ----------------- | ------- | ------- | ------------------------------------------------------------------- |
| `days_back`       | integer | —       | 1–90; backend default 7.                                            |
| `limit`           | integer | —       | 1–20; backend default 10.                                           |
| `offset`          | integer | —       | 0–10,000.                                                           |
| `conversation_id` | string  | —       | Max 200 characters: scope the summary and list to one conversation. |
| `kind`            | enum    | —       | `contradicted` or `verified`: narrow the detections list.           |

CLI: [moda hallucinations](/cli/reference#moda-hallucinations)

### tool\_failures

Returns the tenant-wide tool-failure rollup for the window: which tools failed, how often, and across how many conversations. Follow up with `tool_failure_detail` on a failing `tool_name`.

```json Arguments theme={"dark"}
{ "days_back": 7 }
```

| Parameter   | Type    | Default | Description                                  |
| ----------- | ------- | ------- | -------------------------------------------- |
| `days_back` | integer | —       | 1–90; backend default 7. The only parameter. |

CLI: [moda tool-failures](/cli/reference#moda-tool-failures)

### tool\_failure\_detail

Drills into one tool's failures: error subtypes plus concrete failing examples, each carrying a computed `anchor` (`conversation_id`, `msg_index`, `tool_use_id`, `error_subtype`). Empty subtypes and examples yield a warning — an unknown tool name and a tool with no failures in the window are indistinguishable.

```json Arguments theme={"dark"}
{ "tool_name": "lookupCustomer", "subtype": "timeout.expired", "limit": 5, "include_window": true }
```

| Parameter        | Type    | Default  | Description                                                               |
| ---------------- | ------- | -------- | ------------------------------------------------------------------------- |
| `tool_name`      | string  | required | The tool whose failures to inspect (names come from `tool_failures`).     |
| `subtype`        | string  | —        | Narrow examples to one error subtype.                                     |
| `days_back`      | integer | —        | 1–90; backend default 7.                                                  |
| `limit`          | integer | —        | 1–20 (backend clamp); backend default 5.                                  |
| `offset`         | integer | —        | ≥ 0; backend default 0.                                                   |
| `include_window` | boolean | false    | Attach a transcript window around each example's anchor.                  |
| `window`         | integer | 1        | 1–5: messages either side of the anchor. Only used with `include_window`. |

CLI: [moda tool-failure-detail](/cli/reference#moda-tool-failure-detail)

### problems

Returns Moda's ranked Problem list for the window — deduplicated cross-signal problem groups with scores and counts — plus a `dashboard_url` deep link. The starting point of "what should I fix first?".

```json Arguments theme={"dark"}
{ "days_back": 30, "limit": 10 }
```

| Parameter   | Type    | Default | Description                                                   |
| ----------- | ------- | ------- | ------------------------------------------------------------- |
| `days_back` | integer | —       | 1–90; backend default 30 — not 7, wider than the other tools. |
| `limit`     | integer | —       | 1–25 (backend clamp); backend default 25.                     |

CLI: [moda problems](/cli/reference#moda-problems)

### problem

Fetches one Problem: the full dossier by default, or — with `view` — one paginated sub-resource page. `family`/`door` filter the `conversations` view only, and sub-resource views require the canonical problem UUID (the dossier accepts any id and answers `found: false` for unknown ones).

```json Arguments theme={"dark"}
{ "problem_id": "3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b", "view": "evidence", "limit": 25 }
```

| Parameter    | Type    | Default  | Description                                                                                                                                        |
| ------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `problem_id` | string  | required | The Problem to fetch (canonical UUID required for views).                                                                                          |
| `view`       | enum    | —        | `evidence`, `reports`, `conversations`, `feedback`: open one sub-resource page instead of the dossier. Mirrors the CLI's view flags (at most one). |
| `limit`      | integer | —        | 1–50 (views only).                                                                                                                                 |
| `cursor`     | string  | —        | Opaque keyset cursor from a prior page's `next_cursor`, passed back verbatim (views only).                                                         |
| `family`     | enum    | —        | `tool_failure`, `emotion`, `laziness`, `hallucination`, `prm_dip`: filter the `conversations` view (ignored on other views).                       |
| `door`       | enum    | —        | `rubric_match`, `local_causal_chain`: filter the `conversations` view (ignored on other views).                                                    |

CLI: [moda problem](/cli/reference#moda-problem)

### problem\_feedback

Submits feedback on a Problem: `mark_fixed`, `dismiss`, `flag_attribution`, or `rename`. This is a write (POST, never auto-retried); re-read with `problems` or `problem` to see the effect.

```json Arguments theme={"dark"}
{
  "problem_id": "3f2a9c1e-8b4d-4e6f-9a2b-1c3d5e7f9a0b",
  "action": "dismiss",
  "reason": "not actionable"
}
```

| Parameter        | Type   | Default    | Description                                                                                                           |
| ---------------- | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------- |
| `problem_id`     | string | required   | Canonical problem UUID (from `problems`/`problem`).                                                                   |
| `action`         | enum   | required   | `mark_fixed`, `dismiss`, `flag_attribution`, `rename`.                                                                |
| `reason`         | string | —          | Max 2,000 characters. Required (non-blank) for `dismiss` and `flag_attribution`; trimmed before send.                 |
| `attribution_id` | string | —          | Attribution UUID, max 64 characters. Required for `flag_attribution`; list ids via `problem` with `view: "evidence"`. |
| `new_name`       | string | —          | Max 255 characters. Required for `rename`; sent as `new_display_name`.                                                |
| `actor`          | string | `moda-mcp` | Who is submitting, max 255 characters.                                                                                |

CLI: [moda problem-feedback](/cli/reference#moda-problem-feedback)

### feedback

Sends product feedback about Moda itself to the Moda team: a free-text note with a category, severity, and optional references. This is a write (POST, never auto-retried); every call inserts a new feedback row. It does not analyze tenant data.

```json Arguments theme={"dark"}
{ "note": "cluster label looks wrong", "category": "bad_cluster_label", "cluster_id": "node_44" }
```

| Parameter         | Type   | Default  | Description                                                                                                                                                                                |
| ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `note`            | string | required | What looked wrong or could be better, 1–4,000 characters.                                                                                                                                  |
| `category`        | enum   | `other`  | `bad_cluster_label`, `mismatched_frustration`, `missing_data`, `noisy_data`, `wrong_tool_failure`, `incorrect_loop`, `api_quirk`, `other`. Materialized client-side into the request body. |
| `severity`        | enum   | `low`    | `info`, `low`, `medium`, `high`. Materialized client-side into the request body.                                                                                                           |
| `conversation_id` | string | —        | 1–256 characters: reference the conversation this feedback concerns.                                                                                                                       |
| `cluster_id`      | string | —        | 1–256 characters: reference the cluster.                                                                                                                                                   |
| `tool_name`       | string | —        | 1–256 characters: reference the tool.                                                                                                                                                      |
| `run_id`          | string | —        | 1–256 characters: reference the run.                                                                                                                                                       |

CLI: [moda feedback](/cli/reference#moda-feedback)

### tail

One tail poll — the `--once` form of `moda tail`: a snapshot of recent activity as ordered events (the last hour's conversations oldest-first, and/or the last day's emotion detections by `detected_at` ascending, with a coverage record stating how much of the score-ranked emotions window was scanned). Continuous tailing is CLI-only — call this tool repeatedly and dedupe by `conversation_id`/`detection_id` yourself.

```json Arguments theme={"dark"}
{ "signal": "all", "limit": 20 }
```

| Parameter   | Type    | Default         | Description                                                                                                             |
| ----------- | ------- | --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `signal`    | enum    | `conversations` | `conversations`, `emotions`, `all`: which streams to poll. Materialized client-side.                                    |
| `limit`     | integer | 20              | 1–50: conversations page size for the poll. Materialized client-side.                                                   |
| `full_scan` | boolean | false           | Emotions only: walk the whole last-day window (up to 501 pages, offset ceiling 10,000) instead of the default 25 pages. |

CLI: [moda tail](/cli/reference#moda-tail)

## Production intelligence

### investigate

Runs the ranked production investigation: three parallel Data API reads (`/overview`, `/tool-failures`, `/frustrations`) synthesized into severity-ranked findings with evidence refs and suggested next tools. Each source is best-effort — an unavailable endpoint becomes a warning and a `source_status` entry, never an error.

```json Arguments theme={"dark"}
{ "days_back": 7, "tool": "lookupCustomer" }
```

| Parameter      | Type    | Default | Description                                                                                                             |
| -------------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
| `days_back`    | integer | 7       | Window in days, 1–90; always sent on all three fetches, clamped like the CLI.                                           |
| `tool`         | string  | —       | Scope the failure lookup to one tool — switches the fetch to `/tool-failures/{tool}?days_back=N&limit=5` (URL-encoded). |
| `conversation` | string  | —       | Recorded in the output scope only — never sent to the API.                                                              |

CLI: [moda investigate](/cli/reference#moda-investigate)

### failures

The failure-only variant of `investigate`: identical three-source fetch and severity ranking, but frustration findings are excluded so only tool-failure (and insufficient-data) findings remain. The `/frustrations` fetch still runs and reports its availability in `source_status`/warnings.

```json Arguments theme={"dark"}
{ "tool": "lookupCustomer", "days_back": 7 }
```

| Parameter   | Type    | Default | Description                                             |
| ----------- | ------- | ------- | ------------------------------------------------------- |
| `days_back` | integer | 7       | Window in days, 1–90; always sent on all three fetches. |
| `tool`      | string  | —       | Scope the failure lookup to one tool.                   |

CLI: [moda failures](/cli/reference#moda-failures)

### ask

Asks the Moda Cloud intelligence agent a free-form question about production behavior and returns a cited answer: the canonical `moda.intelligence.v1` payload as `structuredContent` plus the full ask result (answer, confidence, evidence refs, next commands). The slowest tool — up to \~4 minutes. If the cloud agent is unavailable or returns an empty answer, the result degrades to a locally synthesized answer built from the `investigate` evidence (`degraded: true`, with warnings).

```json Arguments theme={"dark"}
{ "question": "what should I fix first?", "days_back": 14, "response_format": "concise" }
```

| Parameter         | Type    | Default    | Description                                                                                                  |
| ----------------- | ------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
| `question`        | string  | required   | The free-form question, non-empty.                                                                           |
| `days_back`       | integer | 7          | Evidence window in days, 1–90. Sent to the cloud agent as `daysBack` in the POST body.                       |
| `response_format` | enum    | `detailed` | `detailed` or `concise`. `concise` returns a compact markdown rendering (answer + evidence + next commands). |

CLI: [moda ask](/cli/reference#moda-ask)

## Fixes

A Fix pairs a candidate change with a replay-gate verdict on held-out production evidence — see [Fixes in the dashboard](/dashboard/fixes) for concepts. The pipeline is advance-on-poll: nothing progresses server-side between requests, so `wait: true` and `fixes_drive` drive the pipeline rather than merely watch it. All fix tools accept `tenant_id` (string, optional): the tenant to operate on, defaulting to the tenant embedded in the signed API key — required only for legacy unsigned keys. Fix mutations are sent exactly once, never retried.

### fixes

Lists the tenant's ranked queue of Fixes (id, `MODA-FIX` short ref, status, fix type, status reason) with cursor pagination, optionally filtered by status. An empty queue means nothing is drafted yet — create fixes with `fix_start` or `fixes_draft_batch`.

```json Arguments theme={"dark"}
{ "status": "VERIFIED", "limit": 10 }
```

| Parameter   | Type    | Default | Description                                                                                                                 |
| ----------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `status`    | string  | —       | Raw status filter passed through to the backend (for example `PROPOSED`, `VERIFIED`, `GATE_FAILED`). Omit for every status. |
| `limit`     | integer | —       | Page size, 1–100; the backend default applies when omitted.                                                                 |
| `cursor`    | string  | —       | Opaque pagination token from `pagination.next_cursor` (raw string, never number-coerced).                                   |
| `tenant_id` | string  | —       | See area note above.                                                                                                        |

CLI: [moda fixes](/cli/reference#moda-fixes)

### fixes\_draft\_batch

Batch-drafts Fixes for the tenant's top-ranked fixable problems in one server-side call (the backend scans up to 100 ranked problems) and returns the drafted fixes plus per-problem skip reasons. Creation only — drive the drafted fixes with `fixes_drive` afterwards.

```json Arguments theme={"dark"}
{ "limit": 10 }
```

| Parameter   | Type    | Default | Description                                                                        |
| ----------- | ------- | ------- | ---------------------------------------------------------------------------------- |
| `limit`     | integer | —       | Batch size, 1–25 (the backend's LLM spend guard); backend default 10 when omitted. |
| `tenant_id` | string  | —       | See area note above.                                                               |

CLI: [moda fixes draft-batch](/cli/reference#moda-fixes-draft-batch)

### fixes\_drive

Snapshots the fix queue and round-robins one advance step per active fix per pass — fair progress, so a slow gate never starves the others. A bounded reinterpretation of the CLI's hours-long drive: it runs at most `max_passes` passes per call and reports stragglers — call it again to continue. `coverage_truncated` plus warnings report when the snapshot could not be proven complete, with the exact remedy for each case.

```json Arguments theme={"dark"}
{ "status": "GATING", "max_passes": 3, "pass_interval_seconds": 5 }
```

| Parameter               | Type    | Default | Description                                                                                                                                                                                                                              |
| ----------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                | string  | —       | Must be one of the advancing statuses `DRAFT`, `SCOPING`, `PROPOSING`, `PROPOSED`, `GATING` (uppercased before the wire). Narrows the server-side scan window — the only way to reach an advancing fix older than the unfiltered window. |
| `max_passes`            | integer | 3       | MCP-only bound (the CLI drives until `--timeout`, default 2 h): passes this call runs, 1–10.                                                                                                                                             |
| `pass_interval_seconds` | integer | 5       | Seconds between passes, 0–30 (the CLI's `--pass-interval` takes milliseconds, default 20 s).                                                                                                                                             |
| `tenant_id`             | string  | —       | See area note above.                                                                                                                                                                                                                     |

CLI: [moda fixes drive](/cli/reference#moda-fixes-drive)

### fix

Reads one Fix — status, gate result, candidate ref, PR info — and, with `wait: true`, drives the advance-on-poll pipeline (each poll POSTs one advance step, so `wait` turns the read into a write). Waiting stops at a resting status or at `max_wait_seconds`, returning the current state with a timed-out warning — call again to continue. `data.next_steps` points at the status-appropriate follow-up tool.

```json Arguments theme={"dark"}
{ "fix_id": "cme9y2k1q0001l708g6p4xw3v", "wait": true, "max_wait_seconds": 300 }
```

| Parameter               | Type    | Default  | Description                                                                                                                             |
| ----------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `fix_id`                | string  | required | The fix to operate on (raw string, never number-coerced).                                                                               |
| `wait`                  | boolean | false    | Drive the pipeline while the fix is in an advancing status (`DRAFT`/`SCOPING`/`PROPOSING`/`PROPOSED`/`GATING`). Default is a pure read. |
| `max_wait_seconds`      | integer | 120      | 10–600: total wait budget (the CLI's `--timeout` takes milliseconds, default 2 h).                                                      |
| `poll_interval_seconds` | integer | 5        | 2–60: sleep between advance steps while the fix is `GATING` (the CLI's `--poll-interval` takes milliseconds, default 15 s).             |
| `tenant_id`             | string  | —        | See area note above.                                                                                                                    |

CLI: [moda fix](/cli/reference#moda-fix)

### fix\_start

Drafts a Fix for one problem and, with `wait: true`, drives scope → propose → gate to a resting status, attaching the fix packet fail-soft. Without `wait` it returns the drafted fix immediately — advance it later with `fix` (`wait: true`) or `fixes_drive`.

```json Arguments theme={"dark"}
{ "problem_id": "3f6b0a52-9d1c-4e7a-b2f8-6c0d5e4a3b21", "type": "auto", "wait": true }
```

| Parameter               | Type    | Default  | Description                                                                    |
| ----------------------- | ------- | -------- | ------------------------------------------------------------------------------ |
| `problem_id`            | string  | required | The problem to draft a fix for (raw string, never number-coerced).             |
| `type`                  | enum    | —        | `auto` or `prompt`, sent as body `fixType`; omitted → backend default routing. |
| `wait`                  | boolean | false    | Drive the pipeline to a resting status and attach the packet fail-soft.        |
| `max_wait_seconds`      | integer | 120      | 10–600: total wait budget.                                                     |
| `poll_interval_seconds` | integer | 5        | 2–60: sleep between advance steps.                                             |
| `tenant_id`             | string  | —        | See area note above.                                                           |

CLI: [moda fix start](/cli/reference#moda-fix-start)

### fix\_packet

Fetches the `moda.fix_packet.v1` document for a fix verbatim — diagnosis, evidence, and any candidate blocks (tool-description rewrite, drafted SKILL.md, or a minimal skill edit), also surfaced as findings. Unlike the CLI, this server writes no files: write the candidate text from the packet payload yourself if you want a diffable copy, then confirm with `fix_mark_applied`.

```json Arguments theme={"dark"}
{ "fix_id": "cme9y2k1q0001l708g6p4xw3v" }
```

| Parameter   | Type   | Default  | Description                    |
| ----------- | ------ | -------- | ------------------------------ |
| `fix_id`    | string | required | The fix whose packet to fetch. |
| `tenant_id` | string | —        | See area note above.           |

CLI: [moda fix --packet](/cli/reference#moda-fix)

### fix\_verify

Enqueues a gate + control run for a fix's candidate — the stored one, or an inline replacement via `candidate_content` — then by default drives advance-on-poll until the gate verdict lands. `VERIFIED` with a pass verdict succeeds; `GATE_FAILED` returns an error with best-effort per-case findings for the fail-to-pass loop; anything else (inconclusive, blocked, a lapsed wait) is degraded, never a pass. A concurrent verify can supersede this run — a warning flags when the reported verdict belongs to a different gate run.

```json Arguments theme={"dark"}
{
  "fix_id": "cme9y2k1q0001l708g6p4xw3v",
  "candidate_content": "You are a support triage agent. Confirm the user's plan tier before quoting refund policy, and never retry a failed refund tool call with identical arguments.",
  "wait": true
}
```

| Parameter               | Type    | Default  | Description                                                                                                                                                                                     |
| ----------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fix_id`                | string  | required | The fix to gate.                                                                                                                                                                                |
| `candidate_content`     | string  | —        | Inline replacement candidate — the MCP substitute for the CLI's `--prompt-file=PATH` (the API never sees a path). Trimmed length must be ≥ 40 characters. Omit to re-gate the stored candidate. |
| `wait`                  | boolean | true     | The inverse of `--no-wait`: drive advance-on-poll to the gate verdict. Pass `false` to only enqueue (returns `gateRunId`; resume with the `fix` tool, `wait: true`).                            |
| `max_wait_seconds`      | integer | 120      | 10–600: total wait budget.                                                                                                                                                                      |
| `poll_interval_seconds` | integer | 5        | 2–60: sleep between advance steps.                                                                                                                                                              |
| `tenant_id`             | string  | —        | See area note above.                                                                                                                                                                            |

CLI: [moda fix verify](/cli/reference#moda-fix-verify)

### fix\_checkout

Resolves a prompt fix's candidate content and returns everything a local checkout needs: the target path, the full candidate text, the branch convention (`moda/fix/<shortref-lowercase>`), the PR magic word (`Fixes MODA-FIX-<SHORTREF>`), and the candidate version id. This server writes nothing — you write `content` to `path` and run git yourself. It refuses artifact fixes (`TOOL_SCHEMA`/`SKILL` — use `fix_packet` + `fix_mark_applied`) and fixes without a proposed candidate, with the recovery path in each error.

```json Arguments theme={"dark"}
{ "fix_id": "cme9y2k1q0001l708g6p4xw3v" }
```

| Parameter   | Type   | Default  | Description                         |
| ----------- | ------ | -------- | ----------------------------------- |
| `fix_id`    | string | required | The fix whose candidate to resolve. |
| `tenant_id` | string | —        | See area note above.                |

CLI: [moda fix checkout](/cli/reference#moda-fix-checkout)

### fix\_submit

Hands a fix back for delivery: channel `pr` has the backend open the draft PR (the response carries `prUrl`/`prBranch` — no local git anywhere), while channel `local_ref` records your own branch name server-side so landing it with the magic word in the PR body confirms the fix via the merge webhook.

```json Arguments theme={"dark"}
{ "fix_id": "cme9y2k1q0001l708g6p4xw3v", "channel": "local_ref", "local_ref": "moda/fix/7q2wjx4m" }
```

| Parameter   | Type   | Default  | Description                                                                      |
| ----------- | ------ | -------- | -------------------------------------------------------------------------------- |
| `fix_id`    | string | required | The fix to deliver.                                                              |
| `channel`   | enum   | required | `pr` or `local_ref` — exactly one delivery channel.                              |
| `local_ref` | string | —        | Your branch name. **Required** when `channel: "local_ref"`, forbidden with `pr`. |
| `tenant_id` | string | —        | See area note above.                                                             |

CLI: [moda fix submit](/cli/reference#moda-fix-submit)

### fix\_mark\_applied

Confirms you applied a fix's candidate in your own infrastructure — the no-GitHub delivery path: the fix flips straight to `SHIPPED`, `mark_fixed` feedback is posted, and the problem enters fixed-monitoring (`HELD` after 14 clean days, `REGRESSED` on reopen). Idempotent server-side: repeating it on a confirmed fix is a duplicate no-op flagged as a warning.

```json Arguments theme={"dark"}
{ "fix_id": "cme9y2k1q0001l708g6p4xw3v", "note": "tool description updated in our agent config" }
```

| Parameter   | Type   | Default  | Description                                                       |
| ----------- | ------ | -------- | ----------------------------------------------------------------- |
| `fix_id`    | string | required | The fix to confirm.                                               |
| `note`      | string | —        | Where/how you applied the candidate, trimmed, max 600 characters. |
| `tenant_id` | string | —        | See area note above.                                              |

CLI: [moda fix mark-applied](/cli/reference#moda-fix-mark-applied)

### fix\_dismiss

Dismisses a fix with a required reason, recorded as problem feedback that steers future drafting and ranking. Destructive — dismissal removes it from the active queue; prefer `fix_mark_applied` when you actually shipped the change.

```json Arguments theme={"dark"}
{ "fix_id": "cme9y2k1q0001l708g6p4xw3v", "reason": "not the right lever; fixing the API enum instead" }
```

| Parameter   | Type   | Default  | Description                              |
| ----------- | ------ | -------- | ---------------------------------------- |
| `fix_id`    | string | required | The fix to dismiss.                      |
| `reason`    | string | required | Non-empty; recorded as problem feedback. |
| `tenant_id` | string | —        | See area note above.                     |

CLI: [moda fix dismiss](/cli/reference#moda-fix-dismiss)

## Prompt management

The prompt tools are the [code-first prompt workflow](/prompt-management/overview) with local file discovery replaced by inline content: prompt bodies travel as arguments, and the returned `key → {promptId, versionId}` mapping plus content hashes is the lockfile replacement — persist it client-side. `moda prompts init`, `status`, and `--watch` are local-file workflows and stay CLI-only. The prompt tools accept `tenant_id` where noted, with the same semantics as the fix tools.

### prompts\_sync

Pushes prompt definitions (content inline) to the Moda prompt registry, minting new versions for changed content, and returns the server's synced list plus each prompt's content hash computed with the CLI's exact canonicalization. The sync is a full push with no explicit delete list: keys absent from `prompts` are conveyed as implicitly removed, so send the complete set every time.

```json Arguments theme={"dark"}
{
  "prompts": [
    {
      "key": "support.triage",
      "name": "Support triage",
      "content": "You are a support triage agent. Classify the request, then...",
      "source_path": "prompts/support/triage.prompt.md"
    }
  ],
  "dry_run": false,
  "source_commit": "9f8e7d6c5b4a39281706f5e4d3c2b1a098765432"
}
```

| Parameter                    | Type      | Default  | Description                                                                                                                                     |
| ---------------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompts`                    | object\[] | required | The full set of prompts to push, content inline (the CLI discovers these from `.moda/prompts.yml` globs). At least one entry.                   |
| `prompts[].key`              | string    | required | Registry key. The CLI derives it from the file path (`prompts/support/triage.prompt.md` → `support.triage`).                                    |
| `prompts[].content`          | string    | required | The prompt body text, inline.                                                                                                                   |
| `prompts[].name`             | string    | —        | Display name; defaults to the key when omitted (CLI parity).                                                                                    |
| `prompts[].description`      | string    | —        | Description frontmatter passthrough.                                                                                                            |
| `prompts[].category`         | string    | —        | Category frontmatter passthrough.                                                                                                               |
| `prompts[].system_prompt`    | string    | —        | System prompt (wire: `systemPrompt`).                                                                                                           |
| `prompts[].messages`         | array     | —        | A JSON prompt file's `messages` array.                                                                                                          |
| `prompts[].variables`        | array     | —        | A JSON prompt file's `variables` array.                                                                                                         |
| `prompts[].variables_schema` | object    | —        | Wire: `variablesSchema`.                                                                                                                        |
| `prompts[].model_config`     | object    | —        | A JSON prompt file's `modelConfig` (or `model:` frontmatter; wire: `modelConfig`).                                                              |
| `prompts[].tool_refs`        | array     | —        | Wire: `toolRefs`.                                                                                                                               |
| `prompts[].response_schema`  | object    | —        | Wire: `responseSchema`.                                                                                                                         |
| `prompts[].source_path`      | string    | —        | Discovered file path, informational only (wire: `sourcePath`).                                                                                  |
| `dry_run`                    | boolean   | false    | Compute what would change without writing registry versions (wire: `dryRun`, always sent).                                                      |
| `source_commit`              | string    | —        | The commit the content came from — the CLI sends `git rev-parse HEAD` automatically (wire: `sourceCommit`; omitted entirely when not provided). |

CLI: [moda prompts sync](/cli/reference#moda-prompts-sync)

### prompts\_diff

Previews what a sync would change by running the same `POST /prompts/sync` with `dryRun` forced to true — the server-computed replacement for `moda prompts status`/`diff`, which compare local file hashes against a local lockfile. Never writes registry versions.

```json Arguments theme={"dark"}
{
  "prompts": [
    { "key": "support.triage", "content": "You are a support triage agent. Classify the request, then..." }
  ]
}
```

| Parameter       | Type      | Default  | Description                                                                             |
| --------------- | --------- | -------- | --------------------------------------------------------------------------------------- |
| `prompts`       | object\[] | required | Same entry shape as `prompts_sync`: the full set of prompts to compare, content inline. |
| `source_commit` | string    | —        | Wire: `sourceCommit`; omitted when not provided.                                        |

CLI: [moda prompts status / diff](/cli/reference#moda-prompts-status-moda-prompts-diff)

### prompts\_promote

Points a registry label (`dev`, `staging`, or `prod`) at a specific prompt version — the final step after `prompts_sync`, `prompts_ab`, or `prompts_propose` hands you a winning `versionId`. Promoting overwrites the label's current assignment, and a `prod` promotion redirects live traffic immediately, so confirm the version first.

```json Arguments theme={"dark"}
{ "prompt_key": "support.triage", "label": "prod", "version_id": "pver_1f2e3d4c5b6a79880911223344556677" }
```

| Parameter    | Type   | Default  | Description                                           |
| ------------ | ------ | -------- | ----------------------------------------------------- |
| `prompt_key` | string | required | The registry key whose label to move.                 |
| `label`      | enum   | required | `dev`, `staging`, or `prod`.                          |
| `version_id` | string | required | The prompt version id to promote (wire: `versionId`). |

CLI: [moda prompts promote](/cli/reference#moda-prompts-promote)

### prompts\_ab

Runs a baseline-vs-candidate prompt comparison over a replay set: both arms travel inline as content strings. The replay set comes from at most one of `set_id` (reuse), `conversation_ids` (one case per conversation), or `auto_generate` — the default when all three are omitted. MCP-shaped async: `wait` defaults to false, returning `{replaySetId, runId}` to poll with `replay_run_status`. Deliberate deviation from the CLI: `promote_primary` defaults to **false** here (the CLI defaults it to true), so a comparison never promotes the winning arm unless explicitly requested.

```json Arguments theme={"dark"}
{
  "baseline_content": "You are a support triage agent...",
  "candidate_content": "You are a support triage agent. Confirm the plan tier first...",
  "conversation_ids": ["conv_5e1a9c2b7d3f4680", "conv_8c33d2f1a09b44e7"],
  "seeds_per_case": 3,
  "wait": false
}
```

| Parameter                     | Type      | Default  | Description                                                                                                                                                                     |
| ----------------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `baseline_content`            | string    | required | The baseline (prod arm) prompt text, inline.                                                                                                                                    |
| `candidate_content`           | string    | required | The candidate (proposed arm) prompt text, inline.                                                                                                                               |
| `baseline_version_id`         | string    | —        | Baseline registry version id from `prompts_sync`; sent as `""` when omitted.                                                                                                    |
| `baseline_prompt_id`          | string    | —        | Baseline registry prompt id; sent as `""` when omitted.                                                                                                                         |
| `candidate_version_id`        | string    | —        | Candidate registry version id; sent as `""` when omitted.                                                                                                                       |
| `candidate_prompt_id`         | string    | —        | Candidate registry prompt id; sent as `""` when omitted.                                                                                                                        |
| `set_id`                      | string    | —        | Reuse an existing replay set (no set-creation calls).                                                                                                                           |
| `conversation_ids`            | string\[] | —        | Build a new replay set with one case per conversation; each case's scenario is the conversation's first user message.                                                           |
| `auto_generate`               | object    | —        | Have the server generate replay cases from recent production traffic. The default source when `set_id` and `conversation_ids` are omitted.                                      |
| `auto_generate.case_count`    | integer   | —        | 1–500; default 5 (wire: `caseCount`).                                                                                                                                           |
| `auto_generate.lookback_days` | integer   | —        | 1–365; default 30 (wire: `lookbackDays`).                                                                                                                                       |
| `auto_generate.name`          | string    | —        | Replay set name; default `Prompt A/B <YYYY-MM-DD>`.                                                                                                                             |
| `seeds_per_case`              | integer   | 3        | 1–10, materialized like the CLI (wire: `seedsPerCase`).                                                                                                                         |
| `assistant_model`             | string    | —        | Assistant model override for the replay (wire: `assistantModel`; omitted when empty).                                                                                           |
| `promote_primary`             | boolean   | false    | Wire: `promotePrimary`. Deliberate deviation: the CLI defaults to true.                                                                                                         |
| `wait`                        | boolean   | false    | `true` polls until the run finishes or `max_wait_seconds` elapses; `false` returns the queued `runId` immediately for `replay_run_status` polling. The CLI defaults to waiting. |
| `max_wait_seconds`            | integer   | 300      | 10–600: total seconds to poll before returning the in-flight status with a warning. Only used when waiting.                                                                     |
| `poll_interval_seconds`       | integer   | 10       | 2–60: seconds between polls. Only used when waiting.                                                                                                                            |
| `tenant_id`                   | string    | —        | Defaults to the tenant embedded in the signed API key.                                                                                                                          |

CLI: [moda prompts ab](/cli/reference#moda-prompts-ab)

### prompts\_propose

Turns a completed A/B run's failures into a server-generated revised candidate, registered as a new unlabeled prompt version — the CLI's `--out` file write becomes the returned content (plus `versionId`, `contentHash`, changelog, risks, and repair/holdout case ids). `gate: true` chains an automatic candidate-vs-baseline replay on the same set: `baseline_content` is then required inline, and `promote_on_win` promotes to `prod` only on a strict candidate win of a completed run. If the gate exceeds `max_wait_seconds` you get the in-flight status with a warning: finish with `replay_run_status`, then promote manually with `prompts_promote`.

```json Arguments theme={"dark"}
{ "prompt_key": "support.triage", "from_run_id": "run_9d2e", "set_id": "rset_31ab", "max_dossiers": 8 }
```

| Parameter               | Type    | Default  | Description                                                                                                                                                  |
| ----------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `prompt_key`            | string  | required | The registry key to revise.                                                                                                                                  |
| `from_run_id`           | string  | required | The A/B run whose failures seed the revision (wire: `fromRunId`).                                                                                            |
| `set_id`                | string  | required | The replay set the run belongs to (wire: `replaySetId`); the gate re-runs this set.                                                                          |
| `max_dossiers`          | integer | 8        | 1–16: failure dossiers fed to the revision, materialized like the CLI (wire: `maxDossiers`).                                                                 |
| `model`                 | string  | —        | The revision LLM slug (omitted when empty).                                                                                                                  |
| `gate`                  | boolean | false    | Automatically A/B the candidate vs `baseline_content` over the same replay set. Note the gate scores the full replay set — holdout cases cannot be isolated. |
| `promote_on_win`        | boolean | false    | Requires `gate: true`: promote the candidate to `prod` only on a strict candidate win of a completed gate run.                                               |
| `baseline_content`      | string  | —        | **Required when `gate: true`**: the baseline (prod arm) prompt text, inline — the CLI resolves this from local prompt files.                                 |
| `seeds_per_case`        | integer | 3        | 1–10 (wire: `seedsPerCase`).                                                                                                                                 |
| `assistant_model`       | string  | —        | Assistant model for the gate run (wire: `assistantModel`; omitted when empty).                                                                               |
| `max_wait_seconds`      | integer | 300      | 10–600. Only used when the gate waits.                                                                                                                       |
| `poll_interval_seconds` | integer | 10       | 2–60. Only used when the gate waits.                                                                                                                         |
| `tenant_id`             | string  | —        | Defaults to the tenant embedded in the signed API key.                                                                                                       |

CLI: [moda prompts propose](/cli/reference#moda-prompts-propose)

### replay\_run\_status

Fetches the latest state of a replay-set comparison run — status, per-arm pass counts and rates, per-case results — plus a formatted verdict (strict pass-count delta, same wording as `moda prompts ab`). Poll it after `prompts_ab` or `prompts_propose` returned a queued `runId` until `run.status` is `completed`, `skipped`, or `error`.

```json Arguments theme={"dark"}
{ "set_id": "rset_31ab", "run_id": "run_9d2e" }
```

| Parameter   | Type   | Default  | Description                                                                                                                                                  |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `set_id`    | string | required | The replay set id the run was enqueued on.                                                                                                                   |
| `run_id`    | string | —        | The `runId` from `prompts_ab`/`prompts_propose`. Sent as `?runId=` only when provided; omit to read the set's most recent run — which can be someone else's. |
| `tenant_id` | string | —        | Defaults to the tenant embedded in the signed API key.                                                                                                       |

CLI: [moda prompts ab](/cli/reference#moda-prompts-ab) (the poll step of `--wait`)

## Skills

Moda distills recurring agent behavior into skill files. The MCP skill tools exchange content inline: they return SKILL.md text and accept it as arguments — installing files (`.claude/skills/<id>/SKILL.md`, `.cursor/rules/<id>.mdc`) and pruning via `.moda/skills.yml` is the client's job.

### skills\_gen

Kicks off a tenant-wide skill generation run: it clusters recent SDK sessions, distills candidate skills, and optionally replays and improves them. Every option you omit is left to the backend default (the CLI materializes its own defaults client-side; this tool does not). Generation takes minutes — poll `skills_status` with the returned run id rather than passing `wait_for_completion`.

```json Arguments theme={"dark"}
{
  "source": "sdk",
  "max_sessions": 500,
  "start_at": "2026-08-01T00:00:00Z",
  "end_at": "2026-08-22T00:00:00Z",
  "improve_candidates": true
}
```

| Parameter                    | Type    | Default | Description                                                                                                                         |
| ---------------------------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `max_sessions`               | integer | —       | Cap on SDK sessions considered, 1–100,000.                                                                                          |
| `lookback_hours`             | integer | —       | Only sessions from the last N hours, 1–8,760.                                                                                       |
| `start_at`                   | string  | —       | ISO 8601 lower bound on session start time.                                                                                         |
| `end_at`                     | string  | —       | ISO 8601 upper bound on session start time.                                                                                         |
| `clustering_run_id`          | string  | —       | Reuse an existing clustering run instead of starting a new one.                                                                     |
| `replay_set_id`              | string  | —       | The replay set used by the replay stage.                                                                                            |
| `run_clustering`             | boolean | —       | Inverse of the CLI's `--no-clustering` (the CLI materializes `true`).                                                               |
| `source`                     | enum    | —       | `all` or `sdk` (the CLI materializes `all`; its deprecated coding-agent value is not offered).                                      |
| `clustering_timeout_seconds` | integer | —       | 1–86,400 (the CLI materializes 7,200).                                                                                              |
| `clustering_poll_seconds`    | integer | —       | 1–600 (the CLI materializes 15).                                                                                                    |
| `max_cluster_pct`            | number  | —       | Float cap on a single cluster's share of sessions (≥ 0; no client-side range).                                                      |
| `reprocess_segments`         | boolean | —       | The CLI also forces this true when `--force-reprocess` is set; pass both explicitly here.                                           |
| `run_replay`                 | boolean | —       | Run the replay stage (mirrors `--replay` / `--no-replay`).                                                                          |
| `max_traces`                 | integer | —       | Cap on traces replayed, 1–100,000.                                                                                                  |
| `improve_candidates`         | boolean | —       | Run improvement rounds on candidate skills.                                                                                         |
| `improvement_rounds`         | integer | —       | 1–10 (the CLI materializes 2).                                                                                                      |
| `force_reprocess`            | boolean | —       | Reprocess sessions already covered by earlier runs.                                                                                 |
| `wait_for_completion`        | boolean | —       | Ask the server to hold the response until the run finishes. Prefer polling `skills_status` — this tool's transport budget is 120 s. |
| `tenant_id`                  | string  | —       | Defaults to the tenant embedded in the signed API key.                                                                              |

CLI: [moda skills gen](/cli/reference#moda-skills-gen)

### skills\_status

Reports the status, counters, and outcome of a skill generation run: pass `run_id` for that run's detail (including event breadcrumbs), or omit it to read the tenant's latest run. An unknown `run_id` returns `found: false` with a warning rather than an error.

```json Arguments theme={"dark"}
{ "run_id": "51f2b3a4-9c8d-4e7f-a1b2-c3d4e5f6a7b8" }
```

| Parameter | Type   | Default | Description                                                                    |
| --------- | ------ | ------- | ------------------------------------------------------------------------------ |
| `run_id`  | string | —       | A `generation_run_id` from `skills_gen`. Omit to read the tenant's latest run. |

CLI: [moda skills status](/cli/reference#moda-skills-status)

### skills\_list

Fetches the tenant's generated skills with their full SKILL.md content. This is the server half of `moda skills pull`: it returns content only — writing files is the client's job.

```json Arguments theme={"dark"}
{ "status": "proposed" }
```

| Parameter | Type | Default    | Description                                                              |
| --------- | ---- | ---------- | ------------------------------------------------------------------------ |
| `status`  | enum | `approved` | `approved`, `proposed`, or `all`. Always sent on the wire, like the CLI. |

CLI: [moda skills pull](/cli/reference#moda-skills-pull)

### skills\_push

Pushes user-authored skills (SKILL.md content inline) to the Moda skill registry, minting new versions for changed content. This is `moda skills sync` with local `.claude/skills/**/SKILL.md` discovery replaced by arguments; the CLI's managed `AGENTS.md` upsert is a local file write and stays CLI-only. Unchanged content is a server-side no-op.

```json Arguments theme={"dark"}
{
  "skills": [
    {
      "key": "handle-refund-retries",
      "name": "Handle refund retries",
      "skill_md": "---\nname: Handle refund retries\n---\nWhen a refund tool call fails, never retry with identical arguments...",
      "source_path": ".claude/skills/handle-refund-retries/SKILL.md"
    }
  ],
  "dry_run": true
}
```

| Parameter                | Type      | Default  | Description                                                                                                                          |
| ------------------------ | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `skills`                 | object\[] | required | The user-authored skills to push (the CLI discovers these under `.claude/skills/`, excluding Moda-managed ones). At least one entry. |
| `skills[].key`           | string    | required | The skill key (the CLI derives it from the `.claude/skills/<key>/` directory name).                                                  |
| `skills[].name`          | string    | required | Display name (the CLI reads SKILL.md frontmatter, falling back to the key).                                                          |
| `skills[].skill_md`      | string    | required | The full SKILL.md content, inline (wire: `skillMd`).                                                                                 |
| `skills[].description`   | string    | —        | SKILL.md description frontmatter.                                                                                                    |
| `skills[].source_path`   | string    | —        | The discovered file path (wire: `sourcePath`).                                                                                       |
| `skills[].content_hash`  | string    | —        | Wire: `contentHash`. Computed as sha256 hex of `skill_md` when omitted (CLI-exact).                                                  |
| `skills[].source_commit` | string    | —        | Per-skill `sourceCommit`; defaults to the top-level `source_commit`.                                                                 |
| `dry_run`                | boolean   | false    | Compute what would change without writing registry versions (wire: `dryRun`, always sent).                                           |
| `source_commit`          | string    | —        | Wire: `sourceCommit`; omitted entirely when not provided.                                                                            |

CLI: [moda skills sync](/cli/reference#moda-skills-sync)

### skills\_proposals

Lists skill proposals — PR-ready skill candidates produced by generation runs — with their status and metadata. `status` defaults to `ready_for_pr` on the wire exactly like the CLI, and `status: "all"` drops the filter entirely.

```json Arguments theme={"dark"}
{ "status": "ready_for_pr" }
```

| Parameter           | Type   | Default        | Description                                                                                           |
| ------------------- | ------ | -------------- | ----------------------------------------------------------------------------------------------------- |
| `status`            | string | `ready_for_pr` | For example `ready_for_pr`. Materialized on the wire like the CLI; `all` omits the filter.            |
| `generation_run_id` | string | —              | Only proposals from that `skills_gen` run (wire: `generationRunId`).                                  |
| `tenant_id`         | string | —              | Mirrors `--tenant-id` (wire: `tenantId`); omitted when not provided, so the API key's tenant applies. |

CLI: [moda skills proposals](/cli/reference#moda-skills-proposals-moda-skills-proposal-apply)

### skills\_proposal

Fetches a single skill proposal including its full SKILL.md content. This is the fetch half of `moda skills proposal apply`: the client applies the content itself (write the file, or adapt it for another agent), then acknowledges with `skills_proposal_mark_applied`.

```json Arguments theme={"dark"}
{ "proposal_id": "sp_7c1d9e2f4a6b8035" }
```

| Parameter     | Type   | Default  | Description                                                                                           |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `proposal_id` | string | required | A proposal id from `skills_proposals`.                                                                |
| `tenant_id`   | string | —        | Mirrors `--tenant-id` (wire: `tenantId`); omitted when not provided, so the API key's tenant applies. |

CLI: [moda skills proposal apply](/cli/reference#moda-skills-proposals-moda-skills-proposal-apply) (fetch step)

### skills\_proposal\_mark\_applied

Acknowledges that a skill proposal's content has been applied client-side, moving it out of the ready queue (the request records `appliedBy: "moda-mcp"`; the CLI sends `moda-cli`). Call it only after the client actually wrote the SKILL.md fetched with `skills_proposal` — marking without applying loses the reminder that the proposal is pending.

```json Arguments theme={"dark"}
{ "proposal_id": "sp_7c1d9e2f4a6b8035" }
```

| Parameter     | Type   | Default  | Description                                                                                           |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `proposal_id` | string | required | The proposal id that was fetched with `skills_proposal` and applied client-side.                      |
| `tenant_id`   | string | —        | Mirrors `--tenant-id` (wire: `tenantId`); omitted when not provided, so the API key's tenant applies. |

CLI: [moda skills proposal apply](/cli/reference#moda-skills-proposals-moda-skills-proposal-apply) (mark-applied step)

## Harness

The harness tools cover the server-side halves of the [harness](/harness/overview) workflow: uploading, deleting, and reading hosted analysis runs. Producing the graph and report requires the local repo, so `moda harness scan`/`analyze`/`approve`/`validate-report` stay CLI-side.

### harness\_sync

Uploads a harness graph — and optionally an approved analysis report (the CLI's `--from-report` form) — to the tenant's harness registry. The backend's 2,000,000-byte graph limit is enforced client-side before the POST, and `dry_run` previews without writing.

```json Arguments theme={"dark"}
{
  "graph": { "agents": [{ "id": "agent_support" }], "artifacts": [] },
  "graph_hash": "b41f0c2a9e7d5f3186c4a2e0d8b6f4a2",
  "source_commit": "9f8e7d6c5b4a39281706f5e4d3c2b1a098765432",
  "dry_run": true
}
```

| Parameter                  | Type    | Default | Description                                                                                                                   |
| -------------------------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `graph`                    | object  | —       | The harness graph from local `moda harness scan`/`analyze` (`.moda/harness.json`). Required unless `report` is given.         |
| `graph_hash`               | string  | —       | The graph hash computed by the local scan (wire: `graphHash`).                                                                |
| `source_commit`            | string  | —       | Wire: `sourceCommit`; omitted when not provided.                                                                              |
| `dry_run`                  | boolean | false   | Validate server-side without writing (wire: `dryRun`, always sent).                                                           |
| `report`                   | object  | —       | The **approved** `.moda/harness-report.json` content (wire: `report`). The CLI refuses unapproved reports unless `--dry-run`. |
| `report_hash`              | string  | —       | Wire: `reportHash`.                                                                                                           |
| `report_validation_status` | string  | —       | The local `moda harness validate-report` outcome, for example `pass` (wire: `reportValidationStatus`).                        |
| `report_approval`          | object  | —       | The recorded approval object keyed to the report hash, from `moda harness approve` (wire: `reportApproval`).                  |

CLI: [moda harness sync](/cli/reference#moda-harness-validate-report-approve-sync)

### harness\_delete

Permanently deletes a harness from the tenant — including every synced version, agent, artifact, and relationship under it — and returns the server's deleted counts. There is no undo, which is why the CLI demands `--yes` and this tool demands `confirm: true`.

```json Arguments theme={"dark"}
{ "id_or_key": "my-harness-key", "confirm": true }
```

| Parameter   | Type    | Default  | Description                                                     |
| ----------- | ------- | -------- | --------------------------------------------------------------- |
| `id_or_key` | string  | required | The harness to delete (the CLI's positional, also `--harness`). |
| `confirm`   | boolean | required | Must be `true` to delete. The deletion cannot be undone.        |

CLI: [moda harness delete](/cli/reference#moda-harness-delete)

### harness\_analyze\_status

Reads the status, progress, and (when completed) results of a hosted harness analysis run. Starting a remote analysis requires a source snapshot built from the local repo, so kicking one off is CLI-only (`moda harness analyze --remote` prints the run id this tool takes); this is the read half that `moda harness pull` uses. Server-controlled progress strings are sanitized of ANSI/control characters before being returned.

```json Arguments theme={"dark"}
{ "run_id": "8a4f2c6e-1b3d-4590-9e7f-a2c4e6081b3d" }
```

| Parameter | Type   | Default  | Description                                                                          |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------ |
| `run_id`  | string | required | The hosted analysis run id (the CLI persists it in `.moda/harness-remote-run.json`). |

CLI: [moda harness pull](/cli/reference#moda-harness-pull)

## Connection & meta

The meta tools reinterpret the CLI's local-machine diagnostics as their cloud-only halves. They take no parameters and stay registered in every toolset mode — they are the connection-diagnosis surface.

### whoami

Validates the connection's Moda API key against the live API and reports the tenant it is scoped to. Call this first when any other tool returns an auth error, or to confirm which tenant this connection reads from. A key with no embedded tenant id (legacy unsigned key) yields a warning that tenant-scoped fix tools need an explicit `tenant_id` argument.

```json Arguments theme={"dark"}
{}
```

CLI: [moda auth whoami](/cli/reference#moda-auth-whoami)

### doctor

Runs the cloud half of `moda doctor`: validates the API key, probes the Data API and the ingest worker, and checks whether the tenant received data in the last day. Use it when tools return empty results or errors to distinguish auth, connectivity, and no-data cases. Local workspace checks remain CLI-only.

```json Arguments theme={"dark"}
{}
```

CLI: [moda doctor](/cli/reference#moda-doctor)

### status

The compact form of `doctor`: one call returning overall health plus last-day conversation/failure/frustration counts. Use it for a quick liveness look before an investigation; use `doctor` for per-check detail.

```json Arguments theme={"dark"}
{}
```

CLI: [moda status](/cli/reference#moda-status)

### manifest

Describes this MCP server: version, toolsets, the full 1:1 map between MCP tools and moda CLI commands, and which CLI commands are CLI-only and why. Use it to discover capabilities outside the currently enabled toolsets.

```json Arguments theme={"dark"}
{}
```

CLI: [moda manifest](/cli/reference#moda-manifest)

## Lean mode

Connecting with `?toolsets=lean` registers 12 tools instead of 53: ten core tools (`overview`, `search`, `conversations`, `context`, `ask`, `problems`, `problem`, `frustrations`, `tool_failures`, `whoami`) plus the two below, which give progressive access to the full catalog without loading every schema into the client's context. They only appear in lean mode and have no CLI equivalent.

### search\_tools

Searches the complete catalog of Moda tools by keyword when the tools listed in lean mode don't cover the need. Returns up to 10 matches with tool names, descriptions, and full input schemas — call a match with `execute_tool`.

```json Arguments theme={"dark"}
{ "query": "hallucination world state" }
```

| Parameter | Type   | Default  | Description                                                                   |
| --------- | ------ | -------- | ----------------------------------------------------------------------------- |
| `query`   | string | required | Keywords to match against tool names, titles, descriptions, and CLI commands. |

### execute\_tool

Runs any tool from the full Moda catalog by name with a JSON arguments object. Arguments are validated against the target tool's schema before dispatch — invalid arguments return the expected schema in the error. Because it can reach write tools, it does not advertise `readOnlyHint`.

```json Arguments theme={"dark"}
{
  "name": "hallucinations",
  "arguments": { "kind": "contradicted", "days_back": 7 }
}
```

| Parameter   | Type   | Default  | Description                                                                 |
| ----------- | ------ | -------- | --------------------------------------------------------------------------- |
| `name`      | string | required | Tool name from `search_tools`, for example `hallucinations`.                |
| `arguments` | object | —        | Arguments object for the target tool; validated against that tool's schema. |

## Next steps

* [MCP server overview](/mcp/overview) — endpoint, authentication, transport, and per-client connection setup.
* [CLI parity](/mcp/parity) — the full tool-to-command map and which CLI commands stay CLI-only.
* [Data API overview](/data-api/overview) — the HTTP surface these tools call, with the same auth key.
