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

# MCP tools

> Reference for the 12 account-scoped tools exposed by the PlainRouter MCP server.

PlainRouter MCP server version `0.3.0` exposes exactly 12 tools. All use the advertising account selected during workspace-token issuance.

| Tool                         | Type                              | Purpose                                                                                                       |
| ---------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `get_account_state`          | Read-only, idempotent             | Confirm authorization, account, workspace, destination, and available capabilities.                           |
| `get_signal_health`          | Read-only, idempotent             | Diagnose event flow, delivery, match quality, and reconciliation. Not evidence for spend-affecting proposals. |
| `get_performance`            | Read-only, idempotent             | Read the admissible 1–90 day stored gateway-versus-Meta comparison for proposal evidence.                     |
| `verify_signal_ingestion`    | Idempotent verification write     | Write one identity-free verification event and confirm ledger receipt.                                        |
| `propose-actions`            | Idempotent write to PlainRouter   | Submit an evidence-backed recommendation to policy and approval.                                              |
| `get-creative-library`       | Read-only, open-world             | Read Meta images and videos with associated ads and last-30-day history.                                      |
| `upload-asset`               | Idempotent proposal write         | Stage a JPEG or PNG and create a governed upload proposal.                                                    |
| `duplicate-ad-with-creative` | Idempotent proposal write         | Propose a permanently paused copy of a source Meta ad with a selected asset.                                  |
| `launcher.draft_batch`       | Non-destructive PlainRouter write | Create a Launcher draft batch from already-synced Drive assets.                                               |
| `launcher.batch_status`      | Read-only, idempotent             | Read the bounded status of one Launcher batch.                                                                |
| `launcher.preview_batch`     | Non-destructive PlainRouter write | Evaluate the authoritative preflight gate without provider work.                                              |
| `launcher.execute_batch`     | Closed execution entry            | Returns a bounded refusal during Launcher Phase B-1 and performs no mutation.                                 |

<Warning>
  Proposal-producing tools do not write to Meta during the MCP request. In **Suggest only**, approval ends without execution. In an executable mode, supported creative work can run only after the policy and approval pipeline authorizes it.
</Warning>

## Shared authorization and scope

Signals read tools require an active principal with `ad-account.read`. `verify_signal_ingestion` requires `signals.verify`. `propose-actions` requires `actions.propose`. Creative-library reads require `creative.read`. Creative proposal tools require both `creative.write` and `actions.propose`. Every Launcher tool requires the `ad-account.read` baseline.

Clients authenticate with an owner-issued [workspace execution token](/docs/actions/workspace-tokens). A **Read** token derives the read scopes. **Write** and **Admin** tokens also derive the proposal scopes. OAuth management credentials cannot authenticate to the MCP server.

`launcher.batch_status` accepts a Read, Write, or Admin token. `launcher.draft_batch`, `launcher.preview_batch`, `launcher.execute_batch`, and the file-import adapter require the Write or Admin tier. Drafting also requires `creative.write` and `actions.propose`; preview and the closed execution entry require `actions.propose`.

Scoped responses identify:

```json theme={null}
{
  "workspace_id": 42,
  "account_id": 91,
  "destination_id": "01J...",
  "dataset_id": "1234567890"
}
```

`destination_id` and `dataset_id` are `null` until the authorized account has a matching destination. The caller cannot provide a different account ID.

## `get_account_state`

Call this first in every account-specific workflow. It returns:

* Agent ID, name, and granted permissions.
* Shared scope.
* Advertising account ID, platform-native ID, name, platform, currency, timezone, and status.
* Workspace ID and name.
* Platform connection status and expiry.
* Signal ID, status, collection domain, and connected destination.
* Available capability flags.
* Response generation time.

Current capability flags are intentionally explicit:

```json theme={null}
{
  "signal_health": true,
  "verified_performance": true,
  "marketing_api_reads": false,
  "writes": false
}
```

`verified_performance` becomes true only when the matching destination is active. It describes stored reconciliation availability, not live target performance. The `marketing_api_reads` and `writes` flags describe this Signals-oriented account-state call; use the separately permissioned creative tools for live creative-library reads and governed creative proposals.

## `get_signal_health`

This tool accepts no input. It returns the shared scope plus:

* Overall status, label, and summary.
* Signal, hostname, Meta connection, and dataset state.
* Event counts for 24 hours, seven days, and all time.
* Last event time.
* Accepted, failed, pending, and total non-test deliveries from the last 24 hours.
* Delivery acceptance rate.
* Latest Event Match Quality score, week-over-week change, alert state, measurement time, and recent history.
* A seven-day stored performance comparison by default.
* Structured diagnostics.
* Data-source names and response generation time.

The tool reads `event_ledger`, `delivery_ledger`, `emq_snapshots`, and `reconciliation_reports`. It reports `marketing_api_called: false`.

<Warning>
  Treat this tool as diagnostic. PlainRouter rejects it as evidence for any proposal that can affect spend or delivery, including a mixed batch containing one such action. Use `get_performance` for stored reconciliation-derived performance evidence.
</Warning>

## `get_performance`

Use this tool for a stored reconciliation window.

PlainRouter excludes modeled and portfolio-only events from this proposal-evidence path. Only eligible observed reconciliation data contributes to its performance fields.

### Input

| Field  | Type    | Required | Rules                       |
| ------ | ------- | -------- | --------------------------- |
| `days` | Integer | No       | From 1 to 90. Default is 7. |

```json theme={null}
{
  "days": 30
}
```

### Output

The response includes:

* Shared scope.
* Requested period with `days`, `since`, and `until`.
* Measurability state.
* Gateway-verified accepted count.
* Meta-reported count.
* Observed gap.
* Alignment percentage.
* Number of alert days.
* Latest report date.
* Totals grouped by event.
* Daily report rows.
* `data_source: "stored_reconciliation_reports"`.
* `marketing_api_called: false`.
* Response generation time.

### Measurability

```json theme={null}
{
  "status": "measurable",
  "reason": null,
  "report_count": 7,
  "latest_report_at": "2026-08-14T00:00:00+00:00",
  "stale_after_hours": 48,
  "counts_semantics": "observed_reconciliation_counts",
  "quantitative_citations_allowed": true
}
```

Possible non-measurable reasons are `no_history` and `stale_history`. When `quantitative_citations_allowed` is false, an agent must not cite count, gap, alignment, alert-day, daily, or by-event values as proposal evidence.

## `verify_signal_ingestion`

Use this tool when an active Signal is waiting for its authoritative server-side onboarding check. It accepts no input and resolves the Signal from the authorized workspace.

The tool writes one idempotent `signal_verification` event and returns:

```json theme={null}
{
  "event_id": "signal_verification",
  "duplicate": false
}
```

The event is identity-free, modeled, excluded from enforcement, and never dispatched to Meta. A repeated call returns the same event with `duplicate: true`. The tool has no spend capability.

The call fails if the connection lacks `signals.verify` or the authorized workspace has no active Signal.

## `launcher.draft_batch`

This tool creates a Launcher draft batch from Drive assets already synced into the token's workspace. It writes only the PlainRouter draft model; it does not write to Meta, evaluate the final execution gate, or activate spend.

It requires an owner-issued [workspace token](/docs/actions/workspace-tokens) with the **Write** or **Admin** tier.

### Input

| Field              | Required | Rules                                                                                                    |
| ------------------ | -------- | -------------------------------------------------------------------------------------------------------- |
| `source_adset_id`  | Yes      | Numeric Meta ad set ID used as the duplication or destination template.                                  |
| `source_ad_id`     | Yes      | Numeric Meta ad ID used as the creative template.                                                        |
| `mode`             | Yes      | `duplicate_adset`, `duplicate_adset_per_media`, or `add_to_adset`.                                       |
| `folder_to_adset`  | No       | Map each source folder to its own ad set. Defaults to `false`.                                           |
| `grouping_enabled` | No       | Select current placement groups together. Defaults to `true`.                                            |
| `items`            | Yes      | One or more ordered items containing an already-synced `drive_link` and optional copy or link overrides. |

Overrides use contract version `v: 1` and can contain up to five `primary_texts`, `headlines`, and `descriptions`, plus `website_url` and `display_link`.

Workspace and account IDs are never accepted as input. PlainRouter derives them from the execution token. A Drive link that is unknown, stale, failed, removed, or belongs to another workspace is rejected without widening the token scope.

### Output

The response includes the draft batch ID and status, frozen tier and capacity limits, item and media counts, accepted items, bounded rejected-item reason codes, import provenance when present, and an advisory gate result.

Possible rejection codes are:

* `drive_asset_unresolved`
* `overrides_invalid`
* `overrides_limit_exceeded`
* `batch_limit_exceeded`
* `media_limit_exceeded`

The current MCP entry point returns an advisory gate with `not_evaluated`. Treat the result as a draft, not authorization to execute.

## Launcher batch status and preview

These tools accept one `batch_id`: the 26-character ULID returned by `launcher.draft_batch` or the file-import adapter. PlainRouter resolves it only inside the workspace and ad account bound to the token. A caller cannot inspect another account by guessing an ID.

### `launcher.batch_status`

Use this read-only tool to inspect a batch without evaluating its gate or contacting a provider. It returns:

* Batch status, mode, and item counts.
* Sub-batch status.
* The latest stored gate result.
* Ordered item status, bounded failure stage and code, and linked Actions proposal IDs.
* Partition status and latest proposal attempts for duplicate-ad-set modes.

The tool requires only a Read-tier token and the shared `ad-account.read` scope.

### `launcher.preview_batch`

Use this non-destructive checkpoint before presenting a batch as ready. PlainRouter re-evaluates the authoritative gate and returns `status: "previewed"` or `status: "gated"`, the resulting batch status, and checks for source relationships, tracking verification, tracked-domain coverage, ledger health, and Actions readiness.

The `previews` array is intentionally empty in this release. No provider preview, Meta write, Actions proposal, or spend activation happens during the call. A successful preview is not execution authorization; any future execution path must evaluate the gate again.

This tool requires a Write or Admin token with `actions.propose`.

### `launcher.execute_batch`

The execution entry is registered so clients can discover the complete Phase B-1 contract, but it is deliberately closed. After validating the token and token-bound batch, every call returns `not_executable_in_phase_b1`.

It does not evaluate the gate, advance the batch, create an Actions proposal or receipt, contact Meta, or mutate provider state.

### Refusals

All three tools can return `invalid_token`, `insufficient_scope`, `invalid_arguments`, `batch_not_found`, or `internal_error`. Preview can also return `batch_not_operable` or `gate_receipt_raced`. Execute returns `not_executable_in_phase_b1` after successful authorization and batch resolution.

## `propose-actions`

This tool validates the action shapes, captures fresh evidence provenance, evaluates policy, and creates one batch.

### Top-level input

| Field             | Rules                                                            |
| ----------------- | ---------------------------------------------------------------- |
| `actions`         | 1–25 typed action objects.                                       |
| `rationale`       | Required plain-language batch rationale, up to 4,000 characters. |
| `idempotency_key` | Required stable retry key, up to 64 characters.                  |
| `evidence`        | 1–3 source declarations.                                         |
| `target_source`   | Must be `human_supplied`.                                        |

Each evidence declaration contains:

```json theme={null}
{
  "source_tool": "get_performance",
  "fields_used": [
    "gateway_verified",
    "alignment_percent"
  ]
}
```

Aggregate `source_tool` values are `get_account_state`, `get_signal_health`, and `get_performance`. Creative actions can also use the action-bound sources `get-creative-library` and `staged-asset-manifest`.

Every field path must exist in a fresh server-side capture. Aggregate sources cannot include `action_index`. An action-bound source requires `action_index` and can cite only its matching creative action.

### Action object

Every action has this common shape:

```json theme={null}
{
  "type": "set_status",
  "target_entity": {
    "type": "campaign",
    "id": "120012345",
    "name": "Prospecting"
  },
  "params": {
    "status": "paused"
  },
  "rationale": "Pause the human-selected campaign for manual review."
}
```

Supported target types are `ad_account`, `campaign`, `ad_set`, and `ad`.

### Action parameters

<AccordionGroup>
  <Accordion title="adjust_budget">
    Requires `new_daily_budget_minor`, a positive integer in the account currency's minor unit. For example, `4900` means 49.00 for a two-decimal currency.
  </Accordion>

  <Accordion title="set_status">
    Requires `status` equal to `active` or `paused`.
  </Accordion>

  <Accordion title="duplicate_ad_with_creative">
    Requires `asset_id` and `status: "paused"`. An optional `name_suffix` can identify the duplicate.
  </Accordion>

  <Accordion title="upload_asset">
    Requires `staged_asset_id`, `asset_type` (`image` or `video`), `filename`, and a lowercase 64-character `content_sha256`. Optional fields include detected `mime_type` and positive `size_bytes`.
  </Accordion>
</AccordionGroup>

### Evidence restrictions

PlainRouter rejects evidence paths that claim campaign, ad set, ad, target, CPA, spend, budget, or learning-phase state from aggregate Signals reads.

`get_signal_health` is diagnostic and cannot support a batch containing `adjust_budget`, `set_status`, or `duplicate_ad_with_creative`. This rule applies to the entire batch: adding an `upload_asset` action does not make a mixed spend-affecting proposal eligible. An upload-only `upload_asset` batch is the current non-spend exception and may retain signal-health context.

Use `get_performance` when a spend-affecting proposal needs stored reconciliation-derived performance evidence.

It also rejects:

* Unknown source tools.
* Field paths absent from the cited response.
* Quantitative performance fields from non-measurable history.
* A target source other than `human_supplied`.
* Proposals from a connection without `actions.propose`.

### Response

The response includes the batch ID, scope, evidence provenance, status, policy decision and reasons, approval requirement, queue URL, proposer, rationale, and normalized action records.

Evidence provenance records the source service, scope level, exact fields, observation time, reporting window, measurability, and the target-selection boundary.

## `get-creative-library`

This tool makes live Meta Marketing API reads for the approved account. It requires `creative.read` and an active Meta connection with `ads_read` or `ads_management`.

### Input

| Field        | Required | Rules                                                                             |
| ------------ | -------- | --------------------------------------------------------------------------------- |
| `asset_type` | No       | `all`, `image`, or `video`. Default is `all`.                                     |
| `limit`      | No       | Integer from 1 to 100 per requested asset type. Default is 25.                    |
| `cursor`     | No       | Opaque Meta cursor from a previous call. Choose `image` or `video` when using it. |

### Output

The response includes:

* Images as `image:<hash>` and videos as `video:<id>`.
* Names, status, dimensions or duration, previews, and creation time when available.
* Ads historically associated with each asset.
* Aggregated last-30-day impressions, clicks, spend, and conversions.
* Separate image and video pagination cursors.
* `performance_window: "last_30d"`.

<Warning>
  The performance block is descriptive account history, not causal lift. When this tool supplies proposal provenance, PlainRouter persists only the selected asset's identity, account ownership, readiness, and status.
</Warning>

## `upload-asset`

This tool validates and privately stages an image, then creates one canonical `upload_asset` proposal. It requires `creative.write` and `actions.propose`.

| Field             | Required        | Rules                                                                                 |
| ----------------- | --------------- | ------------------------------------------------------------------------------------- |
| `asset_type`      | Yes             | Must be `image`.                                                                      |
| `filename`        | Yes             | Up to 100 characters with `.jpg`, `.jpeg`, or `.png` matching the detected MIME type. |
| `content_base64`  | Yes in practice | Raw base64 image bytes without a data URI prefix.                                     |
| `rationale`       | Yes             | 1–1,000 characters.                                                                   |
| `idempotency_key` | Yes             | Stable retry key up to 64 characters.                                                 |

The response wraps the canonical proposal with its ID, scope, evidence provenance, immutable execution-mode snapshot, status, policy result, approval requirement, approval-queue URL, and action record.

PlainRouter freezes the staged bytes and records only the approved manifest fields: staged ID, SHA-256 digest, detected MIME type, byte size, account binding, and frozen/immutable state. Raw bytes do not enter the action or audit record.

Only JPEG and PNG upload execution is supported. Video upload execution remains blocked.

## `duplicate-ad-with-creative`

This tool creates one canonical `duplicate_ad_with_creative` proposal. It requires `creative.write` and `actions.propose`.

| Field             | Required | Rules                                                   |
| ----------------- | -------- | ------------------------------------------------------- |
| `source_ad`       | Yes      | Numeric Meta ID of the human-selected ad to copy.       |
| `asset_id`        | Yes      | `image:<hash>` or `video:<id>` returned by PlainRouter. |
| `rationale`       | Yes      | 1–1,000 characters.                                     |
| `name_suffix`     | No       | Optional copied-ad name suffix up to 100 characters.    |
| `idempotency_key` | Yes      | Stable retry key up to 64 characters.                   |

PlainRouter re-reads the source ad and selected asset under the approved account before creating the proposal. The normalized action always contains `status: "paused"`.

If approved in an executable mode, the worker rechecks policy, creates the copy as `PAUSED`, persists the provider ad ID in a durable receipt, and verifies the exact receipt-addressed object. `IN_PROCESS` can enter scheduled verification for up to 48 hours. The batch is not Landed until every receipt is exactly verified.
