# Citation Optimizer tools

The Citation Optimizer tools let a connected assistant drive the full **score → revise → re-score** loop: pick a query to optimize for, find the right page, score it for AI citation readiness, generate an improved draft, and re-score to measure the lift — repeating until the content is publish-ready.

This is the one place in the MCP server that **writes** to your account — but only in a contained way. It creates *your own* scoring runs and **draft** revisions so you can iterate. It never publishes anything, never edits your live pages, and never changes your existing reports or data. For the scoring model itself, see the [Citation Optimizer methodology](/docs/methodology/citation-optimizer).

<Callout>
Only the **ChatGPT** pipeline can be scored today. Fan-outs for other platforms (Claude, Google AI Overviews) are *tracked* but return `scoreable: false` — use ChatGPT to optimize. The tools tell you this via a `scoreable` flag.
</Callout>

## Fire-and-poll

Scoring and revising run as background jobs, so three tools are **fire-and-poll**: they enqueue work and return an id immediately, and you poll a companion tool until it's done.

| Kick off (returns an id) | Poll until done |
|---|---|
| `score_citation_pipeline` → `runId` | `get_pipeline_run(runId)` until `completed` / `stopped` |
| `revise_content` → `revisionId` | `get_revision(revisionId)` until `completed` |
| `rescore_revision` → `runId` | `get_pipeline_run(runId)` until `completed` / `stopped` |

## Step 1 — Pick what to optimize for

You need a **query** and a **target** (a page, URL, or draft).

### `list_tracked_fanouts` — start here

Lists a property's **real tracked fan-out queries** — the queries the platform's pipeline actually generated (from the property's latest AI Visibility report), highest-impact first. Starting here beats inventing a keyword: you optimize for queries the platform is observed to produce. Each fan-out carries an `isGap` flag (the brand doesn't rank for it yet).

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `platform` | `chatgpt` \| `claude` \| `google_aio` \| `google_aim` (optional) | Default `chatgpt` — the only scoreable pipeline today. |
| `limit` | number (optional) | Max fan-outs (default 25, max 100). |

You can also score a free-text query the user brings (a target from another SEO tool, a phrase they want to rank for) — that's fully supported. A tracked fan-out just adds confidence because the platform was observed generating it.

### Find the target page

| Tool | Use it to |
|---|---|
| `match_pages_for_fanout` | Rank a property's pages by **content similarity** to a fan-out query — pick the closest existing page so two of your pages don't compete for the same citation. Returns candidates with a match score and an overlap flag. |
| `list_property_pages` | List / search a property's pages by path or title, to resolve a page the user *names* ("optimize my pricing page") into a `propertyPageId`. |
| `list_placements` | List a property's PR placements (with PQS and a `hasContent` flag) to find one to optimize. |
| `get_placement` | Read a placement's content so you can score it as a draft — feed its `content` to `score_citation_pipeline` as `draftMarkdown`. |

## Step 2 — Score

### `score_citation_pipeline`

Scores a page, URL, or pasted draft against a fan-out query for AI citation readiness (ChatGPT pipeline). Runs in the background — returns a `runId`; poll `get_pipeline_run(runId)`. **Provide exactly one** of `propertyPageId`, `url`, or `draftMarkdown`.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | The property id. |
| `fanOutQuery` | string | The query to score against, used verbatim. A tracked fan-out or any keyword the user brings. |
| `groundingSearchId` | string (optional) | The id from `list_tracked_fanouts` when scoring a **tracked** fan-out — marks it verified and lets the SERP gate reuse stored standings. Omit for a custom keyword. |
| `pageType` | enum (optional) | `product`, `homepage`, `informational`, `press_release`, `general`, `unknown` (default `informational`). Drives the revise template. |
| `propertyPageId` | string (optional) | Score one of the property's pages (from `match_pages_for_fanout`). |
| `url` | string (optional) | Score an arbitrary URL (may be earned media). |
| `draftMarkdown` | string (optional) | Score pasted markdown (no live URL). |
| `metaTitle` / `metaDescription` | string (optional) | Optional page meta. |

### `get_pipeline_run`

The poll target: a run's status, overall score, per-gate results, recommendations, and — crucially — a **readiness verdict**.

| Parameter | Type | Notes |
|---|---|---|
| `runId` | string | The run id. |

`readiness.recommendation` is one of:

| Verdict | What it means | What to do |
|---|---|---|
| `revise_again` | There's meaningful headroom. | Call `revise_content`. |
| `publish_ready` | The content is citation-ready. | **Stop** — `readiness.assistantInstruction` says so. |
| `plateaued` | Further revisions aren't moving the score. | **Stop** revising. |

The readiness verdict is what keeps the loop from running forever — the assistant revises only while it says `revise_again`.

## Step 3 — Revise

### `revise_content`

Generates a revised draft that improves the scored run's citation readiness, grounded in the gate recommendations and the page-type template. Background job — returns a `revisionId`; poll `get_revision`. Only call this when `get_pipeline_run` readiness is `revise_again`.

| Parameter | Type | Notes |
|---|---|---|
| `runId` | string | The scored run to revise. |

### `get_revision`

The poll target: when completed, returns the revised markdown, revised meta, JSON-LD, and a **change log** tracing each edit back to the recommendation that motivated it.

| Parameter | Type | Notes |
|---|---|---|
| `revisionId` | string | The revision id. |

## Step 4 — Re-score (close the loop)

### `rescore_revision`

Re-scores a completed revision to measure the improvement: creates a new draft run from the revised content and scores it. Returns a new `runId` — poll `get_pipeline_run(runId)` for the new score and readiness verdict.

| Parameter | Type | Notes |
|---|---|---|
| `revisionId` | string | The completed revision to re-score. |

From the new run's readiness verdict, the loop either revises again or stops.

## The loop end-to-end

<Steps>

<Step>
**Choose a query.** `list_tracked_fanouts` (or a keyword the user brings).
</Step>

<Step>
**Find the target.** `match_pages_for_fanout` / `list_property_pages` / `list_placements` + `get_placement`.
</Step>

<Step>
**Score.** `score_citation_pipeline` → poll `get_pipeline_run` until it completes.
</Step>

<Step>
**Check readiness.** If `revise_again`, continue. If `publish_ready` or `plateaued`, stop.
</Step>

<Step>
**Revise.** `revise_content` → poll `get_revision`.
</Step>

<Step>
**Re-score.** `rescore_revision` → poll `get_pipeline_run`. Loop back to *Check readiness*.
</Step>

</Steps>

## Related

- [Citation Optimizer methodology](/docs/methodology/citation-optimizer) — how the gates and readiness verdict work
- [Citation Optimizer dashboard](/docs/dashboards/citation-optimizer) — the same loop in the app
- [Scoring tools](/docs/mcp/scoring) — AIPVS / PQS scoring for publishers and placements
