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

# MCP tool reference

> \"Complete reference for all 10 ai-cmo.dev MCP tools.\"

The server exposes 10 tools and no resources or prompts. Every tool except `aicmo_whoami` (which takes no parameters at all) accepts an optional `workspace` parameter (a ai-cmo.dev workspace slug). When omitted, the server resolves the workspace from the credential or picks the sole accessible workspace; if multiple workspaces are available the tool returns an error asking you to choose.

***

## `aicmo_whoami`

**Title:** ai-cmo.dev identity and workspaces\
**Description:** List the workspaces accessible to this ai-cmo.dev credential and its authorized default.

**Input parameters:** None

**REST endpoint:** `GET /api/v1/workspaces`\
**Scope:** `workspace:read`

**Example result:**

```json theme={null}
{
  "authorizedWorkspace": "acme",
  "workspaces": [
    {
      "id": "acme",
      "displayName": "Acme"
    },
    {
      "id": "acme-eu",
      "displayName": "Acme Europe"
    }
  ]
}
```

***

## `aicmo_score`

**Title:** ai-cmo.dev score\
**Description:** Get the explainable GEO score for a workspace.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

<ParamField name="date" type="string" optional>
  Score date in `YYYY-MM-DD` format (regex: `^\d{4}-\d{2}-\d{2}$`).
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/score[?date=]`\
**Scope:** `metrics:read`

**Example result:**

```json theme={null}
{
  "workspace": "acme",
  "date": "2026-08-10",
  "value": 61.4,
  "delta": 2.7,
  "n": 480,
  "formulaVersion": "geo-score-v1",
  "components": [
    {
      "key": "visibility",
      "label": "Visibility",
      "description": "Share of answers that mention the brand.",
      "rawValue": 0.42,
      "score": 42.0,
      "weight": 0.25,
      "normalizedWeight": 0.25,
      "n": 480
    },
    {
      "key": "relative_sov",
      "label": "Relative share of voice",
      "description": "Brand mentions against competitor mentions.",
      "rawValue": 0.71,
      "score": 71.0,
      "weight": 0.35,
      "normalizedWeight": 0.35,
      "n": 480
    }
  ],
  "missingComponents": [
    "sentiment"
  ],
  "trend": [
    {
      "date": "2026-08-03",
      "value": 58.7,
      "n": 460
    },
    {
      "date": "2026-08-10",
      "value": 61.4,
      "n": 480
    }
  ],
  "volumeProxy": {
    "source": "engines.yaml grounded usage weights",
    "engineWeights": {
      "chatgpt": 0.3,
      "gemini": 0.2,
      "perplexity": 0.2,
      "claude": 0.2,
      "grok": 0.1
    }
  }
}
```

***

## `aicmo_scoreboard`

**Title:** ai-cmo.dev scoreboard\
**Description:** Get per-engine visibility metrics for a workspace.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/scoreboard`\
**Scope:** `metrics:read`

**Example result:**

```json theme={null}
{
  "brand": "acme",
  "date": "2026-08-10",
  "composite": {
    "value": 0.42,
    "ciLow": 0.38,
    "ciHigh": 0.46,
    "n": 480,
    "entity": "acme",
    "deltas": {
      "previous": 0.03,
      "7d": 0.05,
      "28d": 0.11
    }
  },
  "engines": [
    {
      "engine": "chatgpt",
      "metrics": {
        "visibility": {
          "value": 0.48,
          "ciLow": 0.42,
          "ciHigh": 0.54,
          "n": 160,
          "entity": "acme",
          "deltas": {
            "previous": 0.02,
            "7d": 0.04,
            "28d": 0.09
          }
        }
      }
    }
  ]
}
```

***

## `aicmo_recs_list`

**Title:** List ai-cmo.dev recommendations\
**Description:** List recommendations grouped by lifecycle status.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/recs`\
**Scope:** `recs:read`

**Example result:**

```json theme={null}
{
  "proposed": [
    {
      "rec_id": "rec_7f3a",
      "brand": "acme",
      "created_at": "2026-08-10T08:00:00.000Z",
      "type": "new_comparison_page",
      "topic": "pricing",
      "title": "Publish an Acme vs Competitor One comparison page",
      "rationale": "Competitor One is cited in 68% of comparison answers; Acme in 12%.",
      "brief": {},
      "evidence": {},
      "scores": {},
      "evidence_weeks": 4,
      "status": "proposed",
      "state_version": 1,
      "compliance_flag": false,
      "current_draft_id": null,
      "restore_target": null,
      "visibility_snapshot": null
    }
  ],
  "todo": [],
  "in_review": [],
  "done": [],
  "archived": []
}
```

***

## `aicmo_rec_update`

**Title:** Update an ai-cmo.dev recommendation\
**Description:** Apply a guarded recommendation lifecycle transition.

<Warning>
  This is the **only tool that mutates workspace records**. (`aicmo_ask` also writes: it creates a thread and posts a message.) It uses optimistic-concurrency guards
  (`expectedStatus` + `expectedVersion`) to prevent conflicting updates.
</Warning>

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

<ParamField name="id" type="string" required>
  Recommendation ID.
</ParamField>

<ParamField name="action" type="enum" required>
  One of: `accept`, `decline`, `submit`, `verify`, `request_changes`, `stale_reopen`, `reopen`, `archive`, `restore`.
</ParamField>

<ParamField name="expectedStatus" type="enum" required>
  Expected current status: `proposed`, `todo`, `in_review`, `done`, `archived`. Must match the actual status or the transition is rejected.
</ParamField>

<ParamField name="expectedVersion" type="integer" required>
  Expected version number (positive integer). Must match or the transition is rejected.
</ParamField>

<ParamField name="idempotencyKey" type="string" required>
  Unique idempotency key (1-200 characters).
</ParamField>

<ParamField name="note" type="string" optional>
  Transition note (max 300 characters).
</ParamField>

<ParamField name="complianceApproved" type="boolean" default="false" optional>
  Whether compliance review has been completed.
</ParamField>

<Note>
  This MCP tool does **not** expose `reviewEvidence`, `visibilitySnapshot`, or `draftId`, even though the REST endpoint accepts them. Use the REST API directly if you need those fields.
</Note>

**REST endpoint:** `POST /api/v1/workspaces/{workspace}/recs/{id}/status`\
**Scope:** `recs:write`

**Example result:**

```json theme={null}
{
  "stdout": "{\"rec_id\": \"rec_7f3a\", \"status\": \"todo\", \"version\": 4}\n",
  "stderr": ""
}
```

***

## `aicmo_runs_list`

**Title:** List ai-cmo.dev runs\
**Description:** List measurement runs for a workspace.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/runs`\
**Scope:** `runs:read`

**Example result:**

```json theme={null}
[
  {
    "runId": "run_2026_08_10_acme",
    "brand": "acme",
    "date": "2026-08-10",
    "preset": "standard",
    "status": "succeeded",
    "engines": [
      "chatgpt",
      "claude",
      "gemini",
      "perplexity"
    ],
    "okCount": 476,
    "errorCount": 4,
    "skippedCount": 0,
    "totalCostUsd": 1.87,
    "budgetCapUsd": 5.0,
    "budgetCapped": false
  }
]
```

***

## `aicmo_run_get`

**Title:** Get an ai-cmo.dev run\
**Description:** Get one workspace run job by ID.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

<ParamField name="id" type="string" required>
  Run ID.
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/runs/{id}`\
**Scope:** `runs:read`

**Example result:**

```json theme={null}
{
  "id": "job_2f81c4",
  "brand": "acme",
  "kind": "run",
  "pid": 48213,
  "logPath": "data/logs/job_2f81c4.log",
  "startedAt": "2026-08-10T09:14:02.114Z",
  "finishedAt": null,
  "status": "running",
  "progress": {
    "completedCalls": 180,
    "totalCalls": 480,
    "percent": 37.5,
    "source": "calls"
  },
  "lastLines": [
    "engine=chatgpt prompt=42/120 ok",
    "engine=claude prompt=18/120 ok"
  ]
}
```

***

## `aicmo_reports_list`

**Title:** List ai-cmo.dev reports\
**Description:** List stable report identities for a workspace.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/reports`\
**Scope:** `reports:read`

**Example result:**

```json theme={null}
[
  {
    "id": "rep_2026_08_10_acme",
    "date": "2026-08-10"
  },
  {
    "id": "rep_2026_08_03_acme",
    "date": "2026-08-03"
  }
]
```

***

## `aicmo_report_get`

**Title:** Get an ai-cmo.dev report\
**Description:** Get a report by stable report ID.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

<ParamField name="reportId" type="string" required>
  Stable report ID.
</ParamField>

**REST endpoint:** `GET /api/v1/workspaces/{workspace}/reports/{reportId}`\
**Scope:** `reports:read`

**Example result:**

```json theme={null}
{
  "identity": {
    "id": "rep_2026_08_10_acme",
    "date": "2026-08-10"
  },
  "report": {
    "brand": "acme",
    "date": "2026-08-10",
    "summary": "Visibility up 2.7 points week over week."
  },
  "scoreboard": {
    "header": {
      "brand": "acme",
      "date": "2026-08-10",
      "display_name": "Acme",
      "engines_covered": [
        "chatgpt",
        "claude"
      ],
      "skipped_engines": []
    },
    "scoreboard": {
      "brand": "acme",
      "date": "2026-08-10",
      "composite": {
        "value": 0.42,
        "ciLow": 0.38,
        "ciHigh": 0.46,
        "n": 480,
        "entity": "acme",
        "deltas": {
          "previous": 0.03,
          "7d": 0.05,
          "28d": 0.11
        }
      },
      "engines": []
    }
  },
  "markdown": "# Acme - 2026-08-10\n\nVisibility up 2.7 points week over week.\n",
  "competitorLinks": [
    {
      "name": "Competitor One",
      "url": "https://one.example"
    }
  ]
}
```

***

## `aicmo_ask`

**Title:** Ask ai-cmo.dev\
**Description:** Ask the workspace assistant and return its streamed response as one result.

**Input parameters:**

<ParamField name="workspace" type="string" optional>
  ai-cmo.dev workspace slug. Must match `^[a-z0-9-]+$`.
</ParamField>

<ParamField name="prompt" type="string" required>
  The prompt text. Must be non-empty after trimming.
</ParamField>

<ParamField name="threadId" type="string" optional>
  Existing thread ID to continue a conversation. When omitted, a new thread is created automatically.
</ParamField>

**REST endpoint:** `POST /api/v1/chat` (with thread creation via `POST /api/v1/threads`)\
**Scope:** `chat:write`

Unlike the other tools, the result is not a passthrough of a REST response: the
server consumes the chat SSE stream and folds every `text-delta` into one string,
returning its own aggregated envelope.

**Example result:**

```json theme={null}
{
  "threadId": "thread_abc123",
  "text": "Based on your recent scores, the biggest opportunity is improving visibility for brand positioning queries. I recommend focusing on...",
  "events": []
}
```
