---
canonical_url: "https://docs.composio.dev/toolkits/airops_mcp"
markdown_url: "https://docs.composio.dev/toolkits/airops_mcp.md"
product: "platform"
version: null
last_updated: null
---

# AirOps MCP

AirOps MCP lets agents access authorized AirOps brand context, knowledge bases, content workflows, and tracked Brand Kit editing capabilities.

- **Category:** model context protocol
- **Auth:** DCR_OAUTH
- **Composio-managed OAuth available?** No
- **Tools:** 96
- **Triggers:** 0
- **Slug:** `AIROPS_MCP`
- **Version:** 20260910_00

## Tools

### Accept opportunity

**Slug:** `AIROPS_MCP_ACCEPT_OPPORTUNITY`

Accept pending opportunities for a campaign and add them to the campaign action grid. For v2 campaigns, pass opportunity_ids; acceptance uses the original rationale and every opportunity context. Before calling this tool, summarize the opportunities or opportunity items that will be accepted and get explicit user confirmation.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `play_id` | integer | Yes | Campaign ID. |
| `opportunity_ids` | array | No | Opportunity IDs to accept. Required for v2; for v1 this accepts every pending item in each opportunity. |
| `opportunity_item_ids` | array | No | V1-only opportunity item IDs to accept. Use this for item-level acceptance. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | No |  |
| `meta` | object | No |  |

### Add aeo region

**Slug:** `AIROPS_MCP_ADD_AEO_REGION`

Add a region (ISO alpha-2 country code) to a Brand Kit's configured AEO regions.

Why this tool exists: AEO prompts and prompt-assignments can only reference regions
that are configured on the Brand Kit. When `create_aeo_prompt` or
`update_aeo_prompt_assignments` returns a `validation_error` mentioning that the
country code is "not configured on this brand kit", call this tool to add the
missing region first, then retry.

Behavior:
- Re-adding an already-configured region is a no-op (idempotent) and returns success.
- The country code must be a valid ISO 3166-1 alpha-2 code supported by the platform
  (e.g. US, GB, DE, FR, JP, BR, IN, ...).
- Optional `add_to_all_prompts` (default `false`): when `true`, the new region is
  also assigned to every existing live prompt in the Brand Kit. This may incur
  additional answer credits/month; if the workspace's estimated answers limit would
  be exceeded, the call is rejected with a `validation_error` containing
  `estimated_answers` and `max_answers` in `details`. Defaults to `false` so the
  agent does not silently incur credits.

IMPORTANT: Always show the user the region you plan to add (and whether
`add_to_all_prompts` is on) and get explicit confirmation before calling. Adding a
region cannot be undone via this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_kit_id` | integer | Yes | The Brand Kit ID to add the region to. |
| `country_code` | string | Yes | ISO 3166-1 alpha-2 country code (e.g. "US", "GB", "JP"). Must be one of the platform-supported codes. |
| `add_to_all_prompts` | boolean | No | If true, also assigns this region to every existing live prompt in the Brand Kit (subject to the workspace answers limit). Defaults to false. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No |  |
| `countries` | array | No | The full list of configured country codes on the Brand Kit after the add. |
| `brand_kit_id` | integer | No |  |
| `country_code` | string | No |  |
| `assigned_to_existing_prompts` | boolean | No | True only if add_to_all_prompts was requested AND the brand kit had at least one live prompt to fan out to. False otherwise (including when add_to_all_prompts: true on a brand kit with no live prompts). |

### Add grid column

**Slug:** `AIROPS_MCP_ADD_GRID_COLUMN`

Add a new column to a grid table. Use this before write_grid when you need to write to a column that does not exist yet. Can also add a playbook column or workflow column. Workflow columns use data_type app_execution with inputs mapped to grid columns, static values, a Brand Kit, or a Knowledge Base. Each input mapping requires type and value. A column mapping's value must be a column ID from grid_table_id. When it references a playbook column, also provide artifact_name from that playbook's active version as a sibling of type and value. Returns the created column's ID, title, data type, and position. A created playbook column also returns its active-version artifacts; use one of those artifact names when mapping the column into another execution column. For an existing playbook column, get the same metadata from read_grid.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | The column title. |
| `inputs` | object | No | Playbook or workflow input mappings keyed by active-version input name. Playbook mapping types are column, value, and brandkit. Workflow mapping types are column, value, brandkit, and knowledge_base. Every mapping requires type and value. For example, map a normal grid column with {"type":"column","value":123}. If column 123 is a playbook column, select one of its active-version artifacts with {"type":"column","value":123,"artifact_name":"Final Article"}. |
| `grid_id` | integer | Yes | The grid ID. Get it from list_grids. |
| `position` | integer | No | Optional column position. If omitted, appended at the end. |
| `data_type` | string ("text" | "number" | "datetime" | "markdown" | "url" | "html" | "image_from_url" | "json" | "single_select" | "brand_kit" | "file" | "playbook" | "app_execution") | Yes | The data type for the column. |
| `playbook_id` | integer | No | Required when data_type is playbook. The playbook must have an active published version. |
| `workflow_id` | integer | No | Required when data_type is app_execution. The workflow must belong to the grid workspace and have an active published version. |
| `grid_table_id` | integer | Yes | The grid table (sheet) ID. Get it from list_grids with includes=["grid_tables.grid_columns"]. Every mapped column must belong to this table. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `column` | object | No |  |
| `grid_id` | integer | No |  |
| `grid_table_id` | integer | No |  |
| `dependent_columns` | array | No |  |

### Analytics chart

**Slug:** `AIROPS_MCP_ANALYTICS_CHART`

Query analytics data and display it as an interactive chart. Returns data with a UI reference for visualization. Today's data is still being processed — always use yesterday (2026-09-09) or earlier as end_date. CONSTRAINTS: (1) Only metrics from the same scale group can be plotted together: percentage metrics (mention_rate, positive_answer_rate, share_of_voice, citation_rate, citation_share, sentiment_score, first_mention_rate), count metrics (citation_count, answer_count), or position metrics (average_position). (2) Area and bar charts support only single metrics with single dimensions. (3) Line charts support up to 2 dimensions (date + optional grouping), but when using 2 dimensions only a single metric is allowed.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | string | No | Filter by tags. A bare array of tag IDs matches ANY (IN). An object {"operator":"IN"&#124;"HAS_ALL"&#124;"NOT_IN","value":[ids]} selects the operator; HAS_ALL matches only items tagged with every listed tag; NOT_IN matches only items tagged with none of the listed tags. |
| `grain` | string ("daily" | "weekly" | "monthly" | "total") | No | Time granularity for aggregation. Default: total |
| `title` | string | No | Optional chart title. If not provided, a title will be auto-generated. |
| `themes` | array | No | Filter sentiment data by theme IDs. Only applies to sentiment_score metric. |
| `topics` | array | No | Filter by topic IDs |
| `metrics` | array | Yes | Metrics to calculate. Options: mention_rate (brand mention frequency, 0-100), positive_answer_rate (percentage of answers where your brand is positively mentioned, out of all answers where you have been mentioned and sentiment is determined; null when unavailable), share_of_voice (percentage of total mentions, 0-100), citation_rate (citation frequency, 0-100), citation_share (relative citation volume, 0-100), citation_count (total citations), sentiment_score (0-100, negative to positive; null when unavailable), average_position (ranking placement), answer_count (total responses), first_mention_rate (first position frequency, 0-100) |
| `end_date` | string | No | End date (YYYY-MM-DD). Defaults to yesterday. Must be before today because today's data may still be processing and is incomplete — yesterday is used to ensure robust, complete data. Leave blank unless a specific date is requested. |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by country codes (ISO 3166-1 alpha-2) |
| `providers` | array | No | Filter by AI providers |
| `chart_type` | string ("line" | "bar" | "pie" | "area") | No | Type of chart to render. Line for time series, bar for comparisons, pie for proportions, area for comparison and visualizing totals with filled area under the curve. Default: line. |
| `dimensions` | array | No | Dimensions to group by (max 3). |
| `start_date` | string | No | Start date (YYYY-MM-DD). Default: 7 days ago |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to query analytics for |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | No | Query results with requested dimensions and metrics |
| `meta` | object | No |  |
| `chart` | object | No | Chart configuration for the UI |
| `error` | string | No | Error message if query failed |

### Automated feedback report

**Slug:** `AIROPS_MCP_AUTOMATED_FEEDBACK_REPORT`

**AirOps only feedback tool. Issues from other MCP servers should be rejected.**
Make sure the issue is related to AirOps MCP tools before using this tool. 

Submit a feedback report when a user query cannot be resolved with existing tools
or when errors occur repeatedly. **Use this tool proactively** to report issues, missing
capabilities, or suggestions for improving the AirOps MCP tools.
The report will be stored for review by the AirOpsdevelopment team.
No output is generated - the tool simply acknowledges receipt.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `report` | string | Yes | A detailed description of the issue, including: what the user was trying to accomplish, which tools were attempted, what errors occurred (if any), and any suggestions for improvement. |
| `metadata` | object | No | Optional. Additional context that may help debug the issue. Examples: tool_name, error_messages, attempted_parameters, error_count. |
| `brand_kit_id` | integer | No | Optional. The brand kit ID relevant to the issue, if applicable. |
| `workspace_id` | integer | No | Optional. The workspace ID relevant to the issue, if applicable. |

### Bulk update aeo prompt tags

**Slug:** `AIROPS_MCP_BULK_UPDATE_AEO_PROMPT_TAGS`

Apply a single tag operation (add or remove) to a batch of AEO prompts in one
Brand Kit, atomically.

Operations:
- `add` — adds the supplied tag_ids to each prompt's existing tags. Duplicates
  are silently deduped.
- `remove` — removes the supplied tag_ids from each prompt. Tags not currently
  on a prompt are silently no-oped.

Specifying tags:
- Pass `tag_ids` (use `list_tags` to discover them).
- Tags must already exist on the Brand Kit. This tool does NOT create new tags.

Atomicity:
- The call is fully atomic. If ANY supplied `prompt_id` is missing/discarded/
  cross-brand-kit, or ANY `tag_id` is unknown on the Brand Kit, the call is
  refused with a `validation_error` listing every problem, and no taggings are
  changed.
- On success, all listed prompts receive the operation in a single DB transaction.

Limits:
- Up to 100 `prompt_ids` per call.

IMPORTANT: Always show the user the prompts and tags you plan to operate on, and
get explicit confirmation before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tag_ids` | array | Yes | Tag IDs to add or remove. Must exist on the Brand Kit. Use `list_tags` to discover available tag_ids. |
| `operation` | string ("add" | "remove") | Yes | Tag operation to apply to every listed prompt. |
| `prompt_ids` | array | Yes | AEO prompt (question) IDs to operate on. All must belong to the Brand Kit and not be discarded. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the prompts and tags. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No |  |
| `operation` | string | No |  |
| `brand_kit_id` | integer | No |  |
| `updated_count` | integer | No |  |
| `updated_prompts` | array | No | Each listed prompt with its final tag_ids after the operation. |

### Bulk update aeo prompt topics

**Slug:** `AIROPS_MCP_BULK_UPDATE_AEO_PROMPT_TOPICS`

Reassign a batch of AEO prompts to an existing topic in one Brand Kit.

Specifying the destination topic:
- Pass `topic_id` (use `list_topics` to discover them).
- The topic must already exist on the Brand Kit. This tool does NOT create topics.
  To create a new topic first, use `create_topic`, then pass its id here.

Specifying prompts:
- Pass `prompt_ids` (use `list_aeo_prompts` filtered by `topic_id` to find prompts
  currently under a source topic).
- IDs that are missing, discarded, or not in the Brand Kit are skipped; only
  matching prompts are updated.

IMPORTANT: Always show the user the prompts and destination topic you plan to
operate on, and get explicit confirmation before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topic_id` | integer | Yes | Destination topic ID. Must exist on the Brand Kit. Use `list_topics` to discover available topic_ids. Does NOT create topics — use `create_topic` first if needed. |
| `prompt_ids` | array | Yes | AEO prompt (question) IDs to reassign. Unknown or discarded IDs are skipped. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the prompts and topic. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No |  |
| `topic_id` | integer | No |  |
| `brand_kit_id` | integer | No |  |
| `updated_count` | integer | No |  |
| `updated_prompts` | array | No | Prompts that were reassigned, with their new topic_id. |

### Commit aeo prompt assignments

**Slug:** `AIROPS_MCP_COMMIT_AEO_PROMPT_ASSIGNMENTS`

Commit the current prompt-assignment draft for a Brand Kit to live. Replaces all
live country, persona, and platform assignments for the brand kit's prompts with
the draft data.

The workspace's estimated answers limit is enforced. If committing would push the
workspace over its quota, the call is rejected with an `AnswersLimitExceeded`
validation error containing `estimated_answers` and `max_answers` in `details`.
Recover by calling `update_aeo_prompt_assignments` to adjust, or
`discard_aeo_prompt_assignments` to abandon.

IMPORTANT:
- This action affects live data and is not undoable except by manually editing
  assignments again. Always get explicit user confirmation before calling.
- If the human user has unsaved UI edits in the same draft, those will also be
  committed. Surface this to the user before proceeding.
- After a successful commit, a fresh empty draft is automatically re-mirrored from
  the new live data. You may immediately stage further edits with
  `update_aeo_prompt_assignments`.
- This tool does NOT return the new live estimated-answers/month. If you or the
  user need the post-commit estimates, call `get_aeo_prompt_assignments_status`.
  Do NOT reuse the pre-commit `draft_estimated_answers` from
  `update_aeo_prompt_assignments` and do NOT compute the estimate yourself —
  repetition-times, persona/country/platform interactions, and concurrent edits
  can all shift the result.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_kit_id` | integer | Yes | The Brand Kit ID whose draft should be committed. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No |  |
| `committed` | boolean | No |  |
| `brand_kit_id` | integer | No |  |

### Create action grid

**Slug:** `AIROPS_MCP_CREATE_ACTION_GRID`

Create a grid pre-wired with power steps to take action on AEO insights. This is the ONLY supported way to run power steps from MCP: each action_type maps to a specific set of power step columns (e.g. content optimization, citation outreach, question answering) that are auto-configured on the grid and ready to execute against the rows you provide. Use this whenever the user wants to act on AEO data — citations, questions, page opportunities, etc. — for a specific brand kit; pass the AEO records as `rows` and the matching `action_type`. Do NOT use create_grid + add_grid_column to assemble a power step grid by hand; that path will not produce a working power step setup. Use create_grid only for empty, general-purpose grids unrelated to AEO.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `rows` | array | Yes | Array of row data objects. Fields vary by action_type. |
| `grid_name` | string | No | Optional custom name for the grid. If not provided, uses template default. |
| `action_type` | string ("visibility_gap" | "popular_query_gap" | "content_outreach" | "community_outreach" | "competitor_dominated_keyword" | "highly_cited_thread" | "rising_third_party_content" | "losing_ai_visibility" | "almost_page_one" | "blue_ocean") | Yes | The type of action grid to create |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to create the grid for |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | No | The created grid information |
| `meta` | object | No |  |
| `error` | object | No |  |

### Create aeo persona

**Slug:** `AIROPS_MCP_CREATE_AEO_PERSONA`

Create a new AEO persona on a Brand Kit. Personas represent the characters used to
simulate AI-search queries when measuring AI visibility, citations, and mentions.

Behavior:
- `title` must be unique within the Brand Kit (max 200 chars) and `description` is
  required (max 5000 chars).
- Optional `add_to_all_prompts` (default `false`): when `true`, the new persona is
  also assigned to every existing live prompt in the Brand Kit. This may incur
  additional answer credits/month; if the workspace's estimated answers limit would
  be exceeded, the call is rejected with a `validation_error` containing
  `estimated_answers` and `max_answers` in `details`. Defaults to `false` so the
  agent does not silently incur credits.

Writing guidance: avoid including specific brand names in the persona's title or
description — the LLM may then mention the brand in its answer, which the analyzer
will count as a mention and skew the data.

IMPORTANT: Always show the user the persona you plan to create (title, description,
and whether `add_to_all_prompts` is on) and get explicit confirmation before
calling. You can verify the persona was created by calling `list_personas` sorted
by `created_at` descending.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | The persona title (max 200 characters). Must be unique within the Brand Kit. Example: "Enterprise CTO". |
| `description` | string | Yes | A description of the persona's perspective and concerns (max 5000 characters). Avoid including specific brand names. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to add the persona to. |
| `add_to_all_prompts` | boolean | No | If true, also assigns this persona to every existing live prompt in the Brand Kit (subject to the workspace answers limit). Defaults to false. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `title` | string | No |  |
| `message` | string | No |  |
| `created_at` | string | No | Creation timestamp (ISO 8601) |
| `description` | string | No |  |
| `brand_kit_id` | integer | No |  |
| `assigned_to_existing_prompts` | boolean | No | True only if add_to_all_prompts was requested AND the brand kit had at least one live prompt to fan out to. False otherwise (including when add_to_all_prompts: true on a brand kit with no live prompts). |

### Create aeo prompt

**Slug:** `AIROPS_MCP_CREATE_AEO_PROMPT`

Create a new AEO prompt for a Brand Kit. Prompts are questions that can be asked
about a brand to AI search engines, used to track AI visibility and citations.

After creation, background jobs automatically analyze the prompt on search engines,
extract keywords, and compute volume metrics.

You can optionally assign countries, personas, and platforms to the prompt at creation
time. The workspace's estimated answers limit is enforced — if the assignments would
push the workspace over its quota, creation is rejected.

IMPORTANT: Always show the user exactly which fields you plan to use (text, topic_id,
and any assignments) and ask for their confirmation before calling this tool. User
confirmation is mandatory. You can verify the prompt was created by calling
`list_aeo_prompts` sorted by `created_at` descending.

A topic_id is required. If the user hasn't specified a topic, use the `list_topics`
tool first to discover available topics and either suggest one or ask the user to choose.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `text` | string | Yes | The prompt text (max 512 characters). Must be unique within the Brand Kit. |
| `topic_id` | integer | Yes | Topic ID to associate with the prompt. Must belong to the same Brand Kit. Use `list_topics` to discover available topics and either suggest one or ask the user to choose. |
| `countries` | array | No | ISO alpha-2 country codes to assign (e.g., ["US", "GB"]). Must be configured on the Brand Kit. |
| `platforms` | array | No | Platforms to assign. Valid values: chat_gpt, gemini, perplexity, google_ai_mode, google_ai_overview. |
| `persona_ids` | array | No | Persona IDs to assign. Must belong to the same Brand Kit. Use `list_personas` to discover available personas. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to add the prompt to |
| `include_default_persona` | boolean | No | Whether to track the persona-less default baseline answer (default: true). Set false to skip the generic baseline; at least one named persona is then required. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The created prompt ID |
| `text` | string | No | The prompt text |
| `message` | string | No | Success message |
| `topic_id` | integer | No | Associated topic ID |
| `created_at` | string | No | Creation timestamp (ISO 8601) |

### Create aeo tag

**Slug:** `AIROPS_MCP_CREATE_AEO_TAG`

Create a new AEO tag on a Brand Kit. Tags are user-defined labels that can be applied
to prompts via `bulk_update_aeo_prompt_tags`.

Behavior:
- `name` must be unique within the Brand Kit (case-insensitive). The model enforces
  this via a unique index on (brand_kit_id, lower(name)).
- `color` is optional. If omitted, a color is auto-assigned from the platform
  palette. Valid colors: light_grey, grey, green, teal, blue, purple, lilac, pink, red, coral, orange.
- This tool does NOT apply the new tag to any prompts. Use
  `bulk_update_aeo_prompt_tags` with `operation: 'add'` to assign it afterward.

IMPORTANT: Always show the user the tag you plan to create (name and color) and get
explicit confirmation before calling. You can verify the tag was created by calling
`list_tags` filtered by name.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | The tag name. Must be unique within the Brand Kit (case-insensitive). |
| `color` | string ("light_grey" | "grey" | "green" | "teal" | "blue" | "purple" | "lilac" | "pink" | "red" | "coral" | "orange") | No | Optional. Named color from the platform palette. If omitted, a color is auto-assigned. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to add the tag to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `color` | string | No |  |
| `message` | string | No |  |
| `created_at` | string | No | Creation timestamp (ISO 8601) |
| `brand_kit_id` | integer | No |  |

### Create brand kit direct upload

**Slug:** `AIROPS_MCP_CREATE_BRAND_KIT_DIRECT_UPLOAD`

Initiate a direct file upload for use with Brand Kit visual tools.

This returns an upload URL (pointing directly to the storage service), upload headers,
and a signed_id. The workflow is:
1. Call this tool with the Brand Kit ID and file metadata (filename, content_type, byte_size, checksum).
   The checksum must be the Base64-encoded MD5 digest of the file contents.
2. Upload the file to the returned `upload_url` using an HTTP PUT with **all** the provided `upload_headers`.
   No additional authentication is needed — the URL is a time-limited presigned URL.
   Example: curl -X PUT -H "Content-Type: <type>" -H "Content-MD5: <checksum>" --upload-file <path> "<upload_url>"
3. Pass the returned `signed_id` to a manage tool (e.g. manage_brand_kit_logo_variant,
   manage_brand_kit_visual_example, or manage_brand_kit_font).

IMPORTANT: Always confirm with the user before initiating an upload.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `checksum` | string | Yes | Base64-encoded MD5 digest of the file contents |
| `filename` | string | Yes | The filename including extension, e.g. "logo.png" or "brand-font.woff2" |
| `byte_size` | integer | Yes | Size of the file in bytes |
| `brand_kit_id` | integer | Yes | The Brand Kit ID this file is intended for |
| `content_type` | string ("image/png" | "image/jpeg" | "image/svg+xml" | "image/gif" | "image/webp" | "font/ttf" | "font/otf" | "font/woff" | "font/woff2" | "application/vnd.ms-fontobject") | Yes | MIME type of the file |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `signed_id` | string | No |  |
| `upload_url` | string | No |  |
| `upload_headers` | object | No |  |

### Create brand kit recap entry

**Slug:** `AIROPS_MCP_CREATE_BRAND_KIT_RECAP_ENTRY`

Record a recap entry summarizing the changes you made to a Brand Kit.

Call this once, near the end of a session that mutated the Brand Kit draft — not for every edit.
Do not call this tool if you made no Brand Kit draft mutations this session (for example,
you only read the Brand Kit, suggested edits the user rejected, or worked on Playbooks,
Campaigns, or Insights). Never create a recap that says nothing changed.
Put the evidence behind the changes in `body_markdown`: verbatim quotes, source URLs, and pages.

Example:
  create_brand_kit_recap_entry(
    brand_kit_id: 123,
    title: "Refreshed tone & voice and added 2 regions",
    body_markdown: "Made the tone more concise per the latest brand guidelines (https://acme.com/brand). Added US East and US West to support the Q3 launch."
  )

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | One-line summary of what changed (max 200 characters) |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `body_markdown` | string | No | Markdown detail with the evidence behind the changes, e.g. quotes, source URLs, pages (max 4000 characters) |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `title` | string | No |  |
| `source` | string | No |  |
| `message` | string | No |  |

### Create grid

**Slug:** `AIROPS_MCP_CREATE_GRID`

Create a new empty, general-purpose grid with the given name. The grid is created with a single empty sheet (zero rows, zero columns). Use add_grid_column to add columns and write_grid to add rows. If the user belongs to more than one workspace, workspace_id is required. Do not use this tool to act on AEO insights — use create_action_grid instead, which pre-populates power step columns and rows for a given AEO action type.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | The name for the new grid. |
| `workspace_id` | integer | No | Optional workspace ID. Defaults to the user's only workspace when unambiguous. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `grid` | object | No |  |
| `grid_table` | object | No |  |

### Create grid sheet

**Slug:** `AIROPS_MCP_CREATE_GRID_SHEET`

Create a new sheet (grid table) within an existing grid. The sheet is created with zero rows and zero columns. Use add_grid_column to add columns and write_grid to add rows. A grid can contain at most 10 sheets.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | The name for the new sheet. |
| `grid_id` | integer | Yes | The ID of the grid to add the sheet to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `sheet` | object | No |  |
| `grid_id` | integer | No |  |

### Create opportunity

**Slug:** `AIROPS_MCP_CREATE_OPPORTUNITY`

Create one pending opportunity for a Campaign. In interactive Quill or OAuth sessions, summarize the proposed name, description, and target resources and get explicit confirmation first. Campaign strategy Playbooks infer their own Campaign. Other Playbooks must provide play_id, inspect existing Opportunities before creating more, and may create pending Opportunities without interactive confirmation when the Campaign write feature is enabled. For page_refresh and page_creation Campaigns, read the matching Opportunity rationale skill and pass its complete Markdown brief unchanged in description.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Opportunity name. |
| `items` | array | No | Required for v1 campaigns and rejected for v2 campaigns. |
| `play_id` | integer | No | Campaign ID. Required except in its own Campaign strategy session. |
| `contexts` | array | No | Ordered supporting resources for v2 campaigns. Required for Reddit Engagement campaigns and rejected for v1 campaigns. |
| `end_date` | string | No | Reddit citation window end date (ISO 8601). Must match candidate discovery. |
| `target_id` | integer | No | Required for Reddit Engagement campaigns. Durable Reddit SocialContent ID. |
| `start_date` | string | No | Reddit citation window start date (ISO 8601). Must match candidate discovery. |
| `description` | string | Yes | Opportunity description. |
| `target_type` | string ("SocialContent") | No | Required for Reddit Engagement campaigns. Use SocialContent. |
| `target_page_id` | integer | No | Required for v2 page-refresh campaigns and rejected otherwise. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | string | No |  |

### Create page

**Slug:** `AIROPS_MCP_CREATE_PAGE`

Add a web page to a Brand Kit's AEO pages. The URL is normalized before the page is
created, and the page is associated with the Brand Kit's configured AEO domain.

The URL must be unique within the Brand Kit.

IMPORTANT: Always show the user the URL and Brand Kit you plan to use and get explicit
confirmation before calling this tool. You can verify the page was created by calling
`list_pages` filtered by URL.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | The web page URL. Must be unique within the Brand Kit. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to add the page to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The created web page ID |
| `url` | string | No | The normalized web page URL |
| `message` | string | No |  |
| `tracked` | boolean | No |  |
| `created_at` | string | No | Creation timestamp (ISO 8601) |
| `brand_kit_id` | integer | No |  |
| `aeo_domain_id` | integer | No | The Brand Kit's AEO domain ID |

### Create topic

**Slug:** `AIROPS_MCP_CREATE_TOPIC`

Create a new AEO topic on a Brand Kit. Topics are categories used to group AEO prompts.

Behavior:
- `name` must be unique within the Brand Kit.
- `color` is optional. If omitted, a color is auto-assigned from the platform palette.
  Valid colors: light_grey, grey, green, teal, blue, purple, lilac, pink, red, coral, orange.
- This tool does NOT create or assign prompts. Use `create_aeo_prompt` with the returned
  topic ID to add prompts to the topic.

IMPORTANT: Always show the user the topic you plan to create (name and color) and get
explicit confirmation before calling. You can verify the topic was created by calling
`list_topics` filtered by name.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | The topic name. Must be unique within the Brand Kit. |
| `color` | string ("light_grey" | "grey" | "green" | "teal" | "blue" | "purple" | "lilac" | "pink" | "red" | "coral" | "orange") | No | Optional. Named color from the platform palette. If omitted, a color is auto-assigned. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to add the topic to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `color` | string | No |  |
| `message` | string | No |  |
| `created_at` | string | No | Creation timestamp (ISO 8601) |
| `brand_kit_id` | integer | No |  |

### Delete aeo prompt

**Slug:** `AIROPS_MCP_DELETE_AEO_PROMPT`

Delete an AEO prompt from a Brand Kit.

Use `list_aeo_prompts` to find the prompt ID and verify the prompt text before deletion.

IMPORTANT: This action is destructive. Always show the user the exact prompt text and
get explicit confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `prompt_id` | integer | Yes | The ID of the prompt to remove. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the prompt. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The ID of the removed prompt. |
| `text` | string | No | The text of the removed prompt. |
| `message` | string | No |  |
| `brand_kit_id` | integer | No |  |

### Delete aeo tag

**Slug:** `AIROPS_MCP_DELETE_AEO_TAG`

Delete an AEO tag from a Brand Kit.

Behavior:
- This is a HARD delete. The tag is removed from the Brand Kit entirely.
- All taggings on prompts that referenced this tag are also deleted (cascade via
  `Aeo::Tag has_many :taggings, dependent: :destroy`). Every prompt that had this
  tag will lose it.
- The response includes `tagged_prompts_count`: the number of prompts that lost the
  tag. Use this to communicate the blast radius back to the user.

IMPORTANT: This action is destructive and cannot be undone via this tool. Always
show the user the tag name AND `tagged_prompts_count` (look it up first via
`list_tags` + `list_aeo_prompts` if needed) and get explicit confirmation before
calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tag_id` | integer | Yes | The tag ID to delete. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the tag. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The ID of the deleted tag. |
| `name` | string | No | The name of the deleted tag (echoed for user confirmation messaging). |
| `message` | string | No |  |
| `brand_kit_id` | integer | No |  |
| `tagged_prompts_count` | integer | No | How many prompts lost this tag as a result of the delete. |

### Delete brand kit writing rules

**Slug:** `AIROPS_MCP_DELETE_BRAND_KIT_WRITING_RULES`

Delete one or more writing rules from a Brand Kit.
This edits the Brand Kit draft version only; it does not change the active (live) version.

A failure deleting one rule does not block or roll back the others: the response reports
which rules were deleted and which could not be.

IMPORTANT: Always show the user exactly which writing rules will be deleted and ask for
confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `writing_rule_ids` | array | Yes | The IDs of the writing rules to delete (up to 10) |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `failed` | array | No |  |
| `message` | string | No |  |
| `deleted_ids` | array | No |  |

### Delete grid column

**Slug:** `AIROPS_MCP_DELETE_GRID_COLUMN`

Delete a grid column and any dependent columns (e.g. workflow output columns, playbook artifact columns). Cannot delete while row executions are running for the grid table. Returns the IDs of all columns deleted.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `grid_id` | integer | Yes | The ID of the grid. |
| `grid_table_id` | integer | Yes | The ID of the grid table (sheet). |
| `grid_column_id` | integer | Yes | The ID of the grid column to delete. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `grid_id` | integer | No |  |
| `grid_table_id` | integer | No |  |
| `deleted_column_ids` | array | No |  |

### Delete topic

**Slug:** `AIROPS_MCP_DELETE_TOPIC`

Delete an AEO topic from a Brand Kit.

Behavior:
- This is a HARD delete. The topic is removed from the Brand Kit entirely.
- Deletion is blocked when the topic has associated prompts. Use `list_aeo_prompts`
  filtered by `topic_id` to inspect prompts before deleting.
- Candidate questions and question recommendations for the topic are deleted by model
  associations when the topic is deleted.

IMPORTANT: This action is destructive and cannot be undone via this tool. Always show
the user the topic name and associated prompt count, then get explicit confirmation
before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topic_id` | integer | Yes | The topic ID to delete. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the topic. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The ID of the deleted topic. |
| `name` | string | No | The name of the deleted topic. |
| `message` | string | No |  |
| `brand_kit_id` | integer | No |  |
| `prompts_count` | integer | No | How many prompts were associated with the topic before deletion. |

### Discard aeo prompt assignments

**Slug:** `AIROPS_MCP_DISCARD_AEO_PROMPT_ASSIGNMENTS`

Discard the current prompt-assignment draft for a Brand Kit. Throws away ALL
uncommitted edits — both your own and any unsaved edits the human user made in the
UI — and re-mirrors a fresh empty draft from live.

Live assignments are never touched.

IMPORTANT:
- This action is destructive and unrecoverable. Pending UI edits the user has not
  committed will be lost.
- Always get explicit user confirmation before calling.
- Calling discard when no draft exists is a no-op; a fresh empty draft is still
  created so further `update_aeo_prompt_assignments` calls work without setup.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_kit_id` | integer | Yes | The Brand Kit ID whose draft should be discarded. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No |  |
| `discarded` | boolean | No |  |
| `brand_kit_id` | integer | No |  |
| `new_draft_id` | integer | No | The ID of the fresh empty draft created after discarding. |

### Get aeo ad

**Slug:** `AIROPS_MCP_GET_AEO_AD`

Get one ad observed in ChatGPT answers. The 'id' parameter is the observed_creative_id returned by list_aeo_ads.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | Yes | The observed_creative_id of the ad to look up |
| `tags` | object | No | Filter by tags. Object form { "operator": "IN" &#124; "HAS_ALL" &#124; "NOT_IN", "value": [tag ids] }. IN matches items tagged with ANY of the tags; HAS_ALL matches only items tagged with ALL; NOT_IN matches only items tagged with NONE. |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **observed_creative_id**: Fingerprint identifying one normalized creative observed in collected answers, hashed from advertiser domain, destination, title, body and image. Not an OpenAI ad ID. - **observed_family_id**: Looser fingerprint hashed from advertiser domain and a single anchor, so creative variants of the same ad collapse together. Not an OpenAI ad ID. - **advertiser_name**: Advertiser name, falling back to the domain when unknown - **advertiser_domain**: Canonical advertiser domain - **advertiser_type**: Relationship to the Brand Kit: 'owned', 'competitor' or 'other' - **logo_url**: Advertiser logo, falling back to the favicon - **title**: Ad headline - **body**: Ad copy - **image_url**: Creative image. Returned by get_aeo_ad only. - **target_url**: Normalized destination URL. Returned by get_aeo_ad only. - **appearances**: Observed occurrences of this ad in the selected period - **answers**: Distinct answers this ad appeared in during the selected period - **prompts**: Distinct prompts this ad appeared under during the selected period - **frequency**: Percentage of answers for those prompts that carried this ad (0-100) - **first_seen**: Date this ad was first observed (ISO 8601). Lifetime, not windowed. - **last_seen**: Date this ad was last observed (ISO 8601). Lifetime, not windowed. - **days_active**: Days between first and last observation, inclusive |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **topic_id** (EQ, IN): Filter by the topic IDs of the prompts the ad was observed under. An ad observed under several topics is returned for each of them. - **prompt_id** (EQ, IN): Filter by the prompt IDs the ad was observed under - **brand_mentioned** (EQUALS): Filter by prompt query type: brand or category |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to the last analysis date. |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by the country codes the answers were collected for |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get aeo citation

**Slug:** `AIROPS_MCP_GET_AEO_CITATION`

Get prompts citing a specific URL. The 'id' parameter is the URL to look up.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | Yes | The citation URL to look up. URL-encode special characters; the server CGI-unescapes before lookup. |
| `tags` | object | No | Filter by tags. Object form { "operator": "IN" &#124; "HAS_ALL" &#124; "NOT_IN", "value": [tag ids] }. IN matches items tagged with ANY of the tags; HAS_ALL matches only items tagged with ALL; NOT_IN matches only items tagged with NONE. |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **url**: The cited URL - **domain**: Domain ID - **domain_name**: Name of the domain - **domain_category**: Category: Owned, Competitors, Social, etc. - **logo_url**: URL to the domain logo - **citation_count**: Total number of citations for this URL - **citation_count_trend**: Change in citation count vs previous period - **citation_share**: Percentage of total citations (0-100) - **citation_share_trend**: Change in citation share vs previous period - **citation_rate**: Percentage of answers citing this URL (0-100) - **citation_rate_trend**: Change in citation rate vs previous period - **influence_score**: Overall influence score (0-100) - **influence_score_breakdown**: Breakdown: coverage_score, impact_score, da_score - **page_type**: Type of page - **brand_mentioned**: Whether brand is mentioned on the page - **brand_sentiment**: Sentiment toward brand on the page - **mentioned_competitor_domains**: Competitors mentioned on the page - **domain_authority**: Domain authority score - **social**: Platform-neutral social content and account enrichment |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **domain_category** (EQUALS, IN): Filter by domain category - **domain_id** (EQ, IN): Filter by domain ID - **topic_id** (EQ, IN): Filter by topic ID - **brand_mentioned** (EQUALS): Filter by query type: brand or category - **page_type** (EQUALS): Filter by page type - **brand_referenced** (EQ): Filter by whether brand is referenced on the citing page - **brand_sentiment** (EQUALS): Filter by brand sentiment - **mentioned_competitor_domains** (IN): Filter by competitor domain IDs mentioned on the citing page - **url** (CONTAINS): Filter by case-insensitive substring match on the citation URL. Matches against the normalized URL (without scheme or www). |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to today. |
| `personas` | array | No | Filter metrics by persona IDs |
| `countries` | array | No | Filter metrics by country codes |
| `providers` | array | No | Filter metrics by AI providers |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get aeo page content update

**Slug:** `AIROPS_MCP_GET_AEO_PAGE_CONTENT_UPDATE`

Get a specific page content update by ID. Its change_detection object carries the detection status and categories, plus a nested changes array (the before/after diff) on this single-update fetch. Track content updates.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Unique identifier for the content update - **url**: The URL associated with the content update - **type**: Type of content update - **source**: Source that created the content update - **brand_kit_id**: ID of the associated Brand Kit - **workspace_id**: ID of the associated workspace - **created_at**: When the content update was created - **updated_at**: When the content update was last updated - **change_detection**: Change detection for this content update: detection status, the categories of detected changes, and whether a before/after diff is available. On a single-update fetch it also carries a nested `changes` array (the before/after diff). Null when change detection has not run for the update, including workspaces where it is not enabled. |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **url** (EQUALS, CONTAINS): Filter by content update URL - **type** (EQ, IN): Filter by content update type - **source** (EQ, IN): Filter by content update source - **brand_kit_id** (EQ): Filter by Brand Kit ID - **created_at** (GT, LT, GEQ, LEQ): Filter by creation date - **has_changes** (EQ): Filter to content updates where change detection found at least one confirmed change (true) or found none / has no detection run (false) |
| `includes` | array | No | Include related resources.  **Available includes:** - **metadata_entries**: Include metadata key-value pairs for each event |
| `brand_kit_id` | integer | No | Optional Brand Kit ID to filter content updates by |
| `workspace_id` | integer | No | ID of the workspace to retrieve results from. If not provided, returns results from all workspaces the user belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get aeo prompt assignments status

**Slug:** `AIROPS_MCP_GET_AEO_PROMPT_ASSIGNMENTS_STATUS`

Inspect the current prompt-assignment draft state for a Brand Kit without modifying
anything. Read-only.

This is the authoritative source for workspace estimated-answers numbers (live
and draft). Call it whenever you need them — never compute or guess them yourself.
In particular:
- At the start of a session, to detect a pre-existing draft from unsaved UI edits.
- Immediately after `commit_aeo_prompt_assignments`, to read the new live
  `estimated_answers` — the commit tool does not return them.
- Before staging more changes via `update_aeo_prompt_assignments`, to compare
  against `max_answers`.

Returns:
- `has_draft`: whether a draft session exists for this brand kit.
- `has_changes`: whether the draft has uncommitted edits relative to live (null if
  no draft).
- `draft_id`: the draft's ID (null if no draft).
- `live_estimated_answers`: current workspace estimated answers/month based on live
  assignments.
- `draft_estimated_answers`: what the workspace estimated answers/month would be if
  the draft were committed now (null if no draft).
- `max_answers`: the workspace's effective answers limit.
- `limit_exceeded`: whether committing the draft now would exceed the limit (false
  if no draft).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_kit_id` | integer | Yes | The Brand Kit ID to inspect. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `draft_id` | string | No |  |
| `has_draft` | boolean | No |  |
| `has_changes` | string | No |  |
| `max_answers` | integer | No |  |
| `brand_kit_id` | integer | No |  |
| `limit_exceeded` | boolean | No |  |
| `live_estimated_answers` | integer | No |  |
| `draft_estimated_answers` | string | No |  |

### Get answer

**Slug:** `AIROPS_MCP_GET_ANSWER`

Get a specific AI answer by ID with full text content. Get a single AI answer with full details. Use this to retrieve the complete answer text (not truncated), citations, and mentions. Use includes parameter to get citations, mentions, and persona details.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Unique identifier for the answer - **created_at**: When the answer was created - **text**: The full answer text content - **provider**: AI provider(chat_gpt, gemini, perplexity, google_ai_mode, google_ai_overview) - **country**: Country code for this answer (e.g., US, GB) - **brand_mentioned**: Whether the brand was mentioned in the answer - **brand_cited**: Whether the brand was cited in the answer - **citations.id**: Citation ID - **citations.url**: Citation URL - **citations.title**: Citation title - **citations.position**: Citation position in the answer - **mentions.id**: Mention ID - **mentions.name**: Mentioned entity name - **mentions.position**: Mention position in the answer - **persona.id**: Persona ID - **persona.name**: Persona name - **persona.description**: Persona description |
| `includes` | array | No | Include related resources.  **Available includes:** - **citations**: Include citations with domain details - **mentions**: Include entity mentions with domain details - **persona**: Include persona details |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get brand kit

**Slug:** `AIROPS_MCP_GET_BRAND_KIT`

Fetch a Brand Kit's brand identity (writing_tone, writing_persona) and associated entities
(product lines, audiences, content types, regions, writing rules, custom variables,
visual/design assets). Call AFTER `list_brand_kits` confirms `brand_management_enabled: true`.

# Decision flow

1. Specific entity by ID →
   `includes=["<type>"]`, `filters=[{field: "<type>.id", operator: "EQ", value: <id>}]`.
2. Entity by name (product_lines, content_types, audiences, regions, custom_variables,
   visual_examples) →
   `filters=[{field: "<type>.name", operator: "CONTAINS", value: "<fragment>"}]`.
3. Find text content ("rule about tone", "any mention of Red List") → filter by the text
   field: `filters=[{field: "writing_rules.text", operator: "CONTAINS", value: "tone"}]`.
4. Structural discovery → small `includes` with `fields=["<type>.id","<type>.name"]` only.

# Scale (typical kit vs outliers)

Most kits are small — these rarely need filtering:
- audiences, regions, usage_rules, palettes, content_samples: usually 0-2, rarely >10
- content_types, product_lines: usually 1-3

These have tails you must handle:
- writing_rules: usually 3, outliers exceed 200. Never include in full without a filter —
  filter by text (CONTAINS) or by id.
- competitors: usually 10, outliers exceed 250. Include via product_lines.competitors only
  when needed, and consider a name filter.
- custom_variables: outliers exceed 150. Filter by name (CONTAINS) for known variables
  (e.g. "Red List"), or by value (CONTAINS) for content.
- Scoped writing_rules via audience/region/content_type nesting: outliers exceed 200.
  Filter the parent by id or name first.

# Writing rules — global vs scoped

Writing rules are EITHER global OR scoped to a single audience/content_type/region (never
both). For content targeting a specific audience/region/content_type, fetch BOTH:
  1. Top-level `writing_rules` include → global rules.
  2. Nested writing_rules on the scoping parent → scoped rules.
Merge client-side.

# Anti-patterns

- `includes=["writing_rules","custom_variables","product_lines.competitors",
  "content_types.writing_rules"]` without filters → blows context on outlier kits.
- Calling without `fields` on a large kit → returns full text (template_outline,
  rule text, ICP) for every row.
- Forgetting filters on `writing_rules`, `custom_variables`, or `competitors` — those
  are the three biggest tails.

# Versioning

`version: "draft"` reads pending edits; defaults to `"active"`. Use active for on-brand
output unless the user explicitly asks for draft.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Unique identifier for the Brand Kit - **workspace_name**: Name of the workspace this Brand Kit belongs to - **brand_name**: Name of the brand - **brand_url**: URL of the brand website - **brand_about**: Description/overview of the brand - **writing_persona**: The persona/voice used in brand writing - **writing_tone**: The tone of voice for brand content - **status**: Brand Kit status (pending, processing, generated, setup_flow_generated, failed, user_edited, published) - **unpublished_changes**: True when the draft version has changes not yet published. - **created_at**: When the Brand Kit was created - **updated_at**: When the Brand Kit was last updated - **draft_version_user_id**: ID of the user who last edited the draft version - **draft_version_playbook_id**: ID of the playbook that last edited the draft version - **freshener_playbook_id**: ID of the Freshener Playbook associated with this Brand Kit, if any - **product_lines.id**: Product line ID - **product_lines.name**: Product line name - **product_lines.details**: Product line details - **product_lines.positioning**: Product positioning - **product_lines.ideal_customer_profile**: Ideal customer profile - **product_lines.url**: Product line URL - **product_lines.generation_status**: Generation status (pending, running, success, error) - **audiences.id**: Audience ID - **audiences.name**: Audience name - **audiences.description**: Audience description - **content_types.id**: Content type ID - **content_types.name**: Content type name - **content_types.template_outline**: Template outline - **content_types.cta_text**: Call-to-action text - **content_types.cta_url**: Call-to-action URL - **content_types.header_case**: Header case style - **content_types.header_case_custom_value**: Custom header case value - **content_types.generation_status**: Generation status (pending, running, success, error) - **regions.id**: Region ID - **regions.name**: Region name - **regions.description**: Region description - **regions.icon_name**: Icon name - **writing_rules.id**: Writing rule ID - **writing_rules.text**: Writing rule text - **custom_variables.id**: Custom variable ID - **custom_variables.name**: Custom variable name - **custom_variables.value**: Custom variable value - **logo_variants.id**: Logo variant ID - **logo_variants.name**: Logo variant name - **logo_variants.background_color**: Background color as a hex value (e.g. #ffffff) - **logo_variants.usage_instructions**: Instructions for agents on when and how to use this logo - **logo_variants.file_url**: URL of the uploaded logo image - **logo_sizes.id**: Logo size ID - **logo_sizes.name**: Logo size name - **logo_sizes.width**: Width in pixels - **logo_sizes.height**: Height in pixels - **logo_sizes.usage_instructions**: Instructions for agents on when and how to use this logo size - **usage_rules.id**: Usage rule ID - **usage_rules.applies_to**: Category this rule applies to: "logo" (placement, spacing, backgrounds), "typography" (font usage, sizing), or "color" (palette usage, backgrounds, data viz) - **usage_rules.name**: Usage rule text - **palettes.id**: Palette ID - **palettes.name**: Palette name - **palettes.colors**: Colors in this palette - **fonts.id**: Font ID - **fonts.name**: Font name - **fonts.usage_instructions**: Usage instructions for agents - **fonts.google_font_link**: Google Fonts URL for this font - **fonts.file_url**: URL of the uploaded font file - **type_sizes.id**: Type size ID - **type_sizes.font_id**: ID of the associated font - **type_sizes.name**: Type size name - **type_sizes.weight**: Font weight - **type_sizes.size**: Font size in pixels - **type_sizes.line_height**: Line height multiplier (e.g. 1.5) - **type_sizes.usage_instructions**: Usage instructions for agents - **visual_examples.id**: Visual example ID - **visual_examples.title**: Title of the visual example - **visual_examples.sample_url**: Optional URL to a live sample - **visual_examples.file_url**: URL of the uploaded image - **visual_examples.usage_instructions**: Instructions for agents on when and how to use this visual example - **visual_examples.visual_use_case_id**: ID of the Visual Use Case this visual example is grouped under - **visual_use_cases.id**: Visual use case ID - **visual_use_cases.name**: Visual use case name - **visual_use_cases.instructions**: Free-form instructions for when and how to apply this visual use case |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **workspace_name** (EQUALS, CONTAINS): Filter by workspace name - **brand_name** (EQUALS, CONTAINS): Filter by brand name - **brand_url** (EQUALS, CONTAINS): Filter by brand URL - **status** (EQUALS, IN): Filter by Brand Kit status - **created_at** (EQ, GT, LT, GEQ, LEQ): Filter by creation date - **product_lines.id** (EQ, IN): Filter by product line ID - **product_lines.name** (EQUALS, CONTAINS): Filter by product line name - **product_lines.details** (CONTAINS): Find product lines whose details contain a substring - **product_lines.positioning** (CONTAINS): Find product lines whose positioning contains a substring - **product_lines.ideal_customer_profile** (CONTAINS): Find product lines whose ICP contains a substring - **audiences.id** (EQ, IN): Filter by audience ID - **audiences.name** (EQUALS, CONTAINS): Filter by audience name - **content_types.id** (EQ, IN): Filter by content type ID - **content_types.name** (EQUALS, CONTAINS): Filter by content type name - **content_types.template_outline** (CONTAINS): Find content types whose template_outline contains a substring - **regions.id** (EQ, IN): Filter by region ID - **regions.name** (EQUALS, CONTAINS): Filter by region name - **writing_rules.id** (EQ, IN): Filter by writing rule ID - **writing_rules.text** (CONTAINS): Find writing rules containing a substring (case-insensitive) - **custom_variables.id** (EQ, IN): Filter by custom variable ID - **custom_variables.name** (EQUALS, CONTAINS): Filter by custom variable name (e.g. "Red List") - **custom_variables.value** (CONTAINS): Find custom variables whose value contains a substring - **logo_variants.id** (EQ, IN): Filter by logo variant ID - **logo_variants.name** (EQUALS, CONTAINS): Filter by logo variant name - **logo_sizes.id** (EQ, IN): Filter by logo size ID - **logo_sizes.name** (EQUALS, CONTAINS): Filter by logo size name - **usage_rules.id** (EQ, IN): Filter by usage rule ID - **usage_rules.applies_to** (EQUALS, IN): Filter by category this rule applies to - **usage_rules.name** (CONTAINS): Find usage rules whose text contains a substring - **palettes.id** (EQ, IN): Filter by palette ID - **palettes.name** (EQUALS, CONTAINS): Filter by palette name - **fonts.id** (EQ, IN): Filter by font ID - **fonts.name** (EQUALS, CONTAINS): Filter by font name - **type_sizes.id** (EQ, IN): Filter by type size ID - **type_sizes.font_id** (EQ, IN): Filter by associated font ID - **type_sizes.name** (EQUALS, CONTAINS): Filter by type size name - **visual_examples.id** (EQ, IN): Filter by visual example ID - **visual_examples.title** (EQUALS, CONTAINS): Filter by visual example title - **visual_use_cases.id** (EQ, IN): Filter by visual use case ID - **visual_use_cases.name** (EQUALS, CONTAINS): Filter by visual use case name |
| `version` | string ("active" | "draft") | No | Brand Kit version to read from (`active` or `draft`). Defaults to `active`. |
| `includes` | array | No | Include related resources.  **Available includes:** - **product_lines**: Include product lines for this Brand Kit (can nest: competitors). Each product line has positioning, details, and ideal customer profile. Competitors include domain names, useful for link exclusion in content (can nest: competitors) - **audiences**: Include target audiences for this Brand Kit (can nest: writing_rules). Audiences define who the content is for. Nested writing_rules are scoped specifically to that audience (can nest: writing_rules) - **content_types**: Include content types for this Brand Kit (can nest: content_samples, writing_rules). Each content type has a template_outline defining structure and conventions. Nested content_samples provide reference examples; nested writing_rules are scoped to that content type (can nest: content_samples, writing_rules) - **regions**: Include regions for this Brand Kit (can nest: writing_rules). Regions define geographic/market-specific guidelines. Nested writing_rules are scoped specifically to that region (can nest: writing_rules) - **writing_rules**: Include global writing rules for this Brand Kit. These apply to all content. For content-type, audience, or region-specific rules, use the nested writing_rules under those includes instead - **custom_variables**: Include custom variables for this Brand Kit. Custom variables store brand-specific data such as forbidden language lists (Red Lists), customer quotes, terminology guidelines, or other reusable content - **logo_variants**: Include logo variants for this Brand Kit - **logo_sizes**: Include logo sizes for this Brand Kit - **usage_rules**: Include usage rules for this Brand Kit — covers logo placement, typography, color usage, and visual example styling. Filter by applies_to field to find rules for a specific category - **palettes**: Include color palettes for this Brand Kit - **fonts**: Include fonts for this Brand Kit - **type_sizes**: Include type sizes for this Brand Kit - **visual_examples**: Include visual examples for this Brand Kit — editorial illustrations, AI image generation templates, data visualizations, and infographic templates. Check titles for usage guidance (some are marked as AI-generation templates) - **visual_use_cases**: Include visual use cases for this Brand Kit — named groupings of visual examples with shared instructions for when and how to apply them. Every visual example belongs to a visual use case (via visual_use_case_id). |
| `workspace_id` | integer | No | ID of the workspace to retrieve brand kits from. If not provided, returns brand kits from all workspaces the user belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get campaign

**Slug:** `AIROPS_MCP_GET_CAMPAIGN`

Get a campaign by ID, including action grid IDs needed to inspect or update its grid. Campaigns are Plays that coordinate strategy playbooks, action grids, and related opportunities for improving AEO performance.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Campaign ID - **name**: Campaign name - **status**: Campaign status - **action_type**: Behavior this campaign performs - **resource_type**: Action-derived resource type this campaign works on - **brand_kit_id**: Brand Kit this campaign belongs to - **workspace_id**: Workspace this campaign belongs to - **strategy_playbook_id**: Strategy playbook generated for this campaign - **opportunity_schema_version**: Persisted Opportunity schema version - **created_at**: When the campaign was created - **updated_at**: When the campaign was last updated - **action_grid_id**: Action grid ID to use with grid tools - **action_grid_table_id**: Action grid table ID to use with grid tools - **custom_instructions**: Campaign-specific guidance for working related opportunities |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by campaign name - **status** (EQUALS, IN): Filter by campaign status - **workspace_id** (EQ, IN): Filter by workspace ID - **brand_kit_id** (EQ, IN): Filter by Brand Kit ID - **resource_type** (EQUALS, IN): Filter by campaign resource type |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get insights settings

**Slug:** `AIROPS_MCP_GET_INSIGHTS_SETTINGS`

Get AEO insights configuration for a Brand Kit, this includes the relevant information to use any AEO and analytics tools. Returns competitors, personas, prompt count, and other relevant information. Use `list_brand_kits` first to find Brand Kits with `aeo_enabled: true`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Unique identifier for the Brand Kit - **brand_name**: Name of the brand - **brand_url**: URL of the brand website - **countries**: Array of country codes for brand targeting (e.g., ["US", "GB", "CA"]) - **aeo_enabled**: Whether AEO analytics data is available for this Brand Kit - **prompts_count**: Number of prompts/questions associated with this Brand Kit - **brand_customer**: Description of the target customer - **brand_competitors**: Information about brand competitors - **domains**: Owned domains associated with this Brand Kit - **competitors**: AEO competitors with their domain URLs - **personas**: AEO personas for this Brand Kit |
| `workspace_id` | integer | No | ID of the workspace to retrieve results from. If not provided, returns results from all workspaces the user belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get page details

**Slug:** `AIROPS_MCP_GET_PAGE_DETAILS`

Get AEO metrics for a specific web page. Page details include citation share, citation rate, unique cited questions count, and Google Search Console metrics (clicks, impressions, position, CTR) with trends. 

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Unique identifier for the web page - **url**: URL of the web page - **folder**: Folder name of the web page - **primary_keyword**: Primary keyword of the web page - **tracked**: Whether the page is tracked and scraped to track content changes - **citation_share**: Citation share percentage - what percentage of all citations belong to this page - **citation_share_trend**: Citation share trend compared to previous period - **citation_rate**: Citation rate percentage - how frequently this page is cited among answers with citations - **citation_rate_trend**: Citation rate trend compared to previous period - **unique_cited_questions_count**: Number of unique questions/prompts that cite this page - **unique_cited_questions_count_trend**: Unique cited questions count trend (absolute difference from previous period) - **clicks**: Total Google Search Console clicks for this page - **clicks_trend**: Clicks trend percentage compared to previous period - **impressions**: Total Google Search Console impressions for this page - **impressions_trend**: Impressions trend percentage compared to previous period - **position**: Average Google Search Console position for this page - **position_trend**: Position trend percentage compared to previous period - **ctr**: Click-through rate (clicks / impressions) - **ctr_trend**: CTR trend compared to previous period (absolute difference) |
| `end_date` | string | No | End date for metrics period (YYYY-MM-DD format). Defaults to current date. |
| `start_date` | string | No | Start date for metrics period (YYYY-MM-DD format). Defaults to 1 month ago. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get page prompts

**Slug:** `AIROPS_MCP_GET_PAGE_PROMPTS`

Get prompts citing a specific web page. Returns AI prompts that cite the page along with citation metrics (citation_rate, mention_rate) and trends. Use includes: ["topic"] to get topic details for each prompt.Prompts are the questions that can be asked about a brand or web page. They are the search queries that are used to get AI answers with citations and mentions.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the prompt - **text**: The prompt text - **keyword**: Associated keyword for the prompt - **topic_id**: ID of the associated topic (if any) - **volume**: Relative volume score (0-1) indicating search popularity - **total_answers**: Total number of AI answers with citations - **answers_with_citations**: Number of answers citing this web page - **citation_rate**: Ratio of answers citing this page to total answers - **mention_rate**: Ratio of answers mentioning the brand - **citation_rate_trend**: Change in citation rate vs previous period - **mention_rate_trend**: Change in mention rate vs previous period - **volume_trend**: Change in volume vs previous period |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **topic.name** (EQUALS, CONTAINS): Filter by topic name |
| `end_date` | string | No | End date for analysis period (ISO 8601 format, defaults to today) |
| `includes` | array | No | Include related resources.  **Available includes:** - **topic**: Include the associated topic with name |
| `per_page` | integer | No | Items per page |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by country codes (AE, AR, AT, AU, BE, BR, CA, CH, CL, CN, CO, CZ, DE, DK, ES, FI, FR, GB, GR, HR, HU, ID, IE, IL, IN, IS, IT, JP, KE, KR, MX, MY, NG, NL, NO, NZ, PE, PH, PL, PT, SA, SE, SG, TH, TR, TW, US, VN, ZA) |
| `providers` | array | No | Filter by AI providers (e.g., chat_gpt, gemini, perplexity, google_ai_mode, google_ai_overview, claude, grok, microsoft_copilot) |
| `topic_ids` | array | No | Filter by topic IDs |
| `start_date` | string | No | Start date for analysis period (ISO 8601 format, defaults to 1 month ago) |
| `web_page_id` | integer | Yes | ID of the web page to get citing prompts for |
| `brand_kit_id` | integer | Yes | ID of the brand kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### Get prompt answers

**Slug:** `AIROPS_MCP_GET_PROMPT_ANSWERS`

Get AI answers for a specific prompt/question. Prompt answers are the AI answers for a specific question/prompt asked to multiple AI providers and the answers are aggregated. The answers include whether the brand was mentioned and cited in the answer. To get full answer text, citations, and mentions, use the get_answer tool with the answer ID.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **created_at/-created_at**: Sort by answer creation date |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the answer - **created_at**: When the answer was created - **text**: The answer text content (truncated to 200 chars) - **provider**: AI provider(chat_gpt, gemini, perplexity, google_ai_mode, google_ai_overview) - **country**: Country code for this answer (e.g., US, GB) - **brand_mentioned**: Whether the brand was mentioned in the answer - **brand_cited**: Whether the brand was cited in the answer |
| `end_date` | string | No | End date for analysis period (ISO 8601 format, defaults to today) |
| `includes` | array | No | Include related resources.  **Available includes:** - **persona**: Include persona details |
| `per_page` | integer | No | Items per page |
| `personas` | string | No | Comma-separated persona IDs to filter by.Use "default" for the default persona. |
| `countries` | string | No | Comma-separated country codes to filter by using the ISO 3166-1 alpha-2 format (e.g., AE,AR,AT,AU,BE,BR,CA,CH,CL,CN,CO,CZ,DE,DK,ES,FI,FR,GB,GR,HR,HU,ID,IE,IL,IN,IS,IT,JP,KE,KR,MX,MY,NG,NL,NO,NZ,PE,PH,PL,PT,SA,SE,SG,TH,TR,TW,US,VN,ZA) |
| `prompt_id` | integer | Yes | ID of the question/prompt to get answers for |
| `start_date` | string | No | Start date for analysis period (ISO 8601 format, defaults to 1 month ago) |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### Get report

**Slug:** `AIROPS_MCP_GET_REPORT`

Get a specific report by ID with its module configurations. Reports are saved analytics views for a Brand Kit. They contain modules that display different analytics metrics.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | Yes | Resource ID |
| `fields` | array | No | Select specific fields to return.  **Available fields:** - **id**: Unique identifier for the report - **name**: Report name - **created_at**: When the report was created - **updated_at**: When the report was last updated - **modules.id**: Unique identifier for the module - **modules.title**: Module display title - **modules.metrics**: Metrics being measured (e.g., citation_rate, mention_rate) - **modules.dimensions**: Dimensions for grouping data (e.g., date, provider) - **modules.grain**: Time aggregation level (daily, weekly, monthly, total) - **modules.visualization**: Chart type (value, line, bar, table) - **modules.filters**: Filter configuration including date range and providers - **modules.layout**: Grid layout position (x, y, w, h) |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by report name |
| `includes` | array | No | Include related resources.  **Available includes:** - **modules**: Include the report modules with their configurations |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | object | Yes |  |

### Get sentiment theme answers

**Slug:** `AIROPS_MCP_GET_SENTIMENT_THEME_ANSWERS`

Get individual AI answers with sentiment details for a specific theme. Returns answer text, sentiment (positive/neutral/negative), confidence score, and provider. Use this to understand WHY a theme has a particular sentiment score. Today's data is still being processed — always use yesterday (2026-09-09) or earlier as end_date.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number. Default: 1 |
| `topics` | array | No | Filter by topic IDs |
| `end_date` | string | No | End date (YYYY-MM-DD). Defaults to yesterday. Must be before today. |
| `per_page` | integer | No | Results per page (1-50). Default: 10 |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by country codes (ISO 3166-1 alpha-2) |
| `providers` | array | No | Filter by AI providers |
| `start_date` | string | No | Start date (YYYY-MM-DD). Default: 30 days ago |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `sentiment_theme_id` | integer | Yes | The sentiment theme ID to drill into. Use query_analytics with dimensions=[theme] to discover available theme IDs first. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `error` | string | No | Error message if query failed |
| `answers` | array | No |  |
| `pagination` | object | No |  |

### Knowledge base add file

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_ADD_FILE`

Step 2 of the two-step file ingestion flow for a Knowledge Base. Consumes a `signed_id`
returned by `knowledge_base_create_direct_upload` (step 1) plus the file's metadata, and
registers the document with the Knowledge Base. Returns immediately with the new
`document_id` in `pending` status — poll `knowledge_base_get_status` to monitor indexing.

Workflow:
1. Call `knowledge_base_create_direct_upload` with the file metadata to get back a
   `signed_id` and a presigned `upload_url`.
2. PUT the file bytes to the `upload_url` with the provided headers.
3. Call this tool with the `signed_id`, the desired `name` (used as the document's display
   name), the `document_type` MIME (must match the supported types), and an optional
   `metadata` hash.

The `metadata` hash, if provided, is a single-level key/value object. It **replaces** any
existing metadata on the document (no merge) and is filterable at search time via
`search_knowledge_base`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Display name for the document. |
| `metadata` | object | No | Optional single-level key/value hash to attach to the document. Filterable via `search_knowledge_base`. |
| `signed_id` | string | Yes | The `signed_id` returned by `knowledge_base_create_direct_upload`. |
| `document_type` | string ("application/pdf" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/vnd.ms-powerpoint" | "application/rtf" | "application/epub+zip" | "text/plain" | "text/markdown" | "text/csv" | "text/html" | "message/rfc822" | "image/png" | "image/jpeg" | "image/gif" | "image/webp" | "image/svg+xml") | Yes | MIME type to index the document as. Typically matches the `content_type` passed to `knowledge_base_create_direct_upload`, but may intentionally differ to select a different loader (e.g. registering a `text/csv` blob as `text/plain` to bypass CSV-specific parsing). |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base to register the document with. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string ("pending") | No |  |
| `message` | string | No |  |
| `document_ids` | array | No |  |
| `knowledge_base_id` | integer | No |  |

### Knowledge base add urls

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_ADD_URLS`

Bulk-ingest one or more web pages into a Knowledge Base. Each URL becomes a separate
document that fetches and indexes asynchronously. The call returns immediately with the
new document IDs in `pending` state — poll `knowledge_base_get_status` to check progress.

URLs must be absolute and include the `http://` or `https://` scheme (e.g.
`https://example.com/page`). No documents are created if any URL fails validation.

The `metadata` hash, if provided, is applied identically to every document in the batch
(single-level keys only); it replaces (not merges) any existing metadata and is
filterable at search time.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `urls` | array | Yes | One or more absolute URLs starting with `http://` or `https://` (e.g. `https://example.com/page`). Each URL becomes its own document. Bare hostnames without a scheme are rejected. |
| `metadata` | object | No | Optional single-level hash applied identically to every new document. Replaces existing metadata (no merge). Filterable via search_knowledge_base. |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base to add URLs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string ("pending") | No |  |
| `message` | string | No |  |
| `document_ids` | array | No |  |
| `knowledge_base_id` | integer | No |  |

### Knowledge base create direct upload

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_CREATE_DIRECT_UPLOAD`

Initiate a direct file upload for a Knowledge Base. Returns a presigned S3 upload URL,
the required upload headers, and a `signed_id` you'll use with `knowledge_base_add_file`
to register the document.

This is the first call in the two-step file ingestion flow — large files (PDFs, DOCX,
MD, HTML, etc.) don't fit through the MCP transport, so the file bytes go directly from
your client to S3.

Workflow:
1. Call this tool with the file metadata (filename, content_type, byte_size, checksum).
   The checksum must be the Base64-encoded MD5 digest of the file contents.
2. PUT the raw file bytes to the returned `upload_url` with **all** the provided
   `upload_headers`. No additional authentication is needed — the URL is a time-limited
   presigned URL.
3. Pass the returned `signed_id` to `knowledge_base_add_file` to register the document.
   Only after step 3 is the file searchable.

Example:
> knowledge_base_create_direct_upload(knowledge_base_id: 1, filename: "report.pdf",
    content_type: "application/pdf", byte_size: 1234567, checksum: "...")
# → { signed_id: "abc...", upload_url: "https://s3...", upload_headers: { ... } }
> knowledge_base_add_file(knowledge_base_id: 1, signed_id: "abc...",
    name: "Q4 Report", document_type: "application/pdf")
# → { document_ids: [42], status: "pending" }

Maximum file size is 256 MB.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `checksum` | string | Yes | Base64-encoded MD5 digest of the file contents. |
| `filename` | string | Yes | The filename including extension, e.g. "report.pdf" or "transcript.md". |
| `byte_size` | integer | Yes | Size of the file in bytes. Maximum: 256 MB. |
| `content_type` | string ("application/pdf" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/vnd.ms-powerpoint" | "application/rtf" | "application/epub+zip" | "text/plain" | "text/markdown" | "text/csv" | "text/html" | "message/rfc822" | "image/png" | "image/jpeg" | "image/gif" | "image/webp" | "image/svg+xml") | Yes | MIME type of the file. |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base the file will be ingested into. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `signed_id` | string | No |  |
| `upload_url` | string | No |  |
| `upload_headers` | object | No |  |

### Knowledge base delete

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_DELETE`

Permanently delete a Knowledge Base and ALL of its documents. This cascades through
every document in the KB and drops the underlying vectors. This action cannot be
undone.

IMPORTANT: Always warn the user that deletion is permanent and irreversible, name
the Knowledge Base being deleted, and ask for explicit confirmation before calling
this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base to delete. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `deleted` | boolean | No |  |
| `knowledge_base_id` | integer | No |  |

### Knowledge base delete document

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_DELETE_DOCUMENT`

Permanently delete a single document from a Knowledge Base. This action cannot be undone.

IMPORTANT: Always warn the user that deletion is permanent and ask for explicit
confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `document_id` | integer | Yes | The ID of the document to delete. |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base the document belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `deleted` | boolean | No |  |
| `document_id` | integer | No |  |
| `knowledge_base_id` | integer | No |  |

### Knowledge base get document

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_GET_DOCUMENT`

Read the full reconstructed text content of a Knowledge Base document end-to-end —
the loader-extracted text from every chunk concatenated in `position` order.

Use when chunked search results aren't enough: summarizing a whole document,
answering questions across an entire report, or reading back a doc before
delete-and-recreate. For finding specific passages, prefer `search_knowledge_base`.

Returns up to `max_chars` characters starting at `offset` (defaults to 0 and
200000). When the document is larger than the slice, the response
includes `next_offset` (the value to pass on the next call to continue reading);
when fully read, `next_offset` is `null`.

For documents still indexing (`status: pending`) or in error (`status: error`),
content may be empty — check `status` first.

Content is reconstructed by joining chunks; minor overlap or duplication between
adjacent chunks is possible depending on the original loader's chunking strategy.
For the original source file, use the existing download path outside MCP.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `offset` | integer | No | Character offset to start reading from (default 0). Use the `next_offset` from a previous call to paginate. |
| `max_chars` | integer | No | Maximum characters to return in this call (default 200000, cap 500000). For larger documents, paginate via `next_offset`. |
| `document_id` | integer | Yes | The ID of the document to read. |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base the document belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No |  |
| `error` | string | No |  |
| `offset` | integer | No |  |
| `status` | string | No |  |
| `content` | string | No |  |
| `metadata` | object | No |  |
| `error_code` | string | No |  |
| `document_id` | integer | No |  |
| `next_offset` | string | No |  |
| `total_chars` | integer | No |  |
| `document_type` | string | No |  |
| `knowledge_base_id` | integer | No |  |

### Knowledge base get status

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_GET_STATUS`

Get the indexing status of a Knowledge Base and its documents.
Returns a Knowledge Base–level rollup (status, pending and total document counts) plus a
paginated list of per-document statuses. Use this to monitor indexing after writes — only
documents with status "ready" are returned by search_knowledge_base. Pass the returned
`cursor` back to fetch the next page; pass `document_ids` to filter to specific documents.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `items` | integer | No | Optional. Page size for the documents array (1–100, default 25). |
| `cursor` | integer | No | Optional. Cursor token returned by a previous call. Pass it back to fetch the next page. |
| `document_ids` | array | No | Optional. Filter the documents array to only the listed document IDs. |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base to inspect. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `meta` | object | No |  |
| `status` | string ("empty" | "processing" | "error" | "ready") | No |  |
| `documents` | array | No |  |
| `total_count` | integer | No |  |
| `pending_count` | integer | No |  |
| `knowledge_base_id` | integer | No |  |
| `knowledge_base_name` | string | No |  |

### Knowledge base manage

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_MANAGE`

Create or update a Knowledge Base. Omit `knowledge_base_id` to create a new one; pass it
to update an existing one. On create, `name` is required; pass `workspace_id` if you have
access to more than one workspace. On update, only the fields you pass change.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | Display name. Required on create; optional on update (omit to leave unchanged). |
| `workspace_id` | integer | No | Workspace ID for the new Knowledge Base. Required on create when the caller has access to multiple workspaces; inferred otherwise. Ignored on update (workspace is fixed by the existing Knowledge Base). |
| `knowledge_base_id` | integer | No | Omit to create a new Knowledge Base; pass it to update an existing one. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `status` | string ("empty" | "processing" | "error" | "ready") | No |  |
| `created` | boolean | No |  |
| `workspace_id` | integer | No |  |
| `document_count` | integer | No |  |

### Knowledge base update document metadata

**Slug:** `AIROPS_MCP_KNOWLEDGE_BASE_UPDATE_DOCUMENT_METADATA`

Replace a document's user-facing metadata in full. Accepts a single-level hash that
**replaces** (not merges) the existing user-facing metadata. To remove a key, pass the
full new hash that omits it. To clear all metadata, pass `{}`. Filterable at search
time via `search_knowledge_base`.

Metadata-only — does not re-embed the document. Loader-derived metadata (scraped page
metadata for URLs, chunker metadata for files) is not touched. Search filters may
take a few seconds to reflect the new values.

The document must be fully indexed (`status: ready`) before calling this tool. If
indexing isn't complete, the tool returns a validation error.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `metadata` | object | Yes | Single-level key/value hash. Replaces the existing user-facing metadata in full. Values must be a string, number, or boolean (no nested objects / arrays). String values are capped at 512 characters. |
| `document_id` | integer | Yes | The ID of the document whose metadata to replace. |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base the document belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `metadata` | object | No |  |
| `document_id` | integer | No |  |
| `knowledge_base_id` | integer | No |  |

### List aeo ads

**Slug:** `AIROPS_MCP_LIST_AEO_ADS`

List ads observed in ChatGPT answers for a Brand Kit. Ads observed by AirOps in collected ChatGPT answers. Rows are unique ads: observations sharing the same normalized creative are grouped into one, with appearance counts describing how often that ad was observed. Covers ads AirOps has seen in its own answer collection, so it does not describe full market coverage, targeting intent, impression delivery, or advertiser spend.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `tags` | object | No | Filter by tags. Object form { "operator": "IN" &#124; "HAS_ALL" &#124; "NOT_IN", "value": [tag ids] }. IN matches items tagged with ANY of the tags; HAS_ALL matches only items tagged with ALL; NOT_IN matches only items tagged with NONE. |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **observed_creative_id**: Fingerprint identifying one normalized creative observed in collected answers, hashed from advertiser domain, destination, title, body and image. Not an OpenAI ad ID. - **observed_family_id**: Looser fingerprint hashed from advertiser domain and a single anchor, so creative variants of the same ad collapse together. Not an OpenAI ad ID. - **advertiser_name**: Advertiser name, falling back to the domain when unknown - **advertiser_domain**: Canonical advertiser domain - **advertiser_type**: Relationship to the Brand Kit: 'owned', 'competitor' or 'other' - **logo_url**: Advertiser logo, falling back to the favicon - **title**: Ad headline - **body**: Ad copy - **image_url**: Creative image. Returned by get_aeo_ad only. - **target_url**: Normalized destination URL. Returned by get_aeo_ad only. - **appearances**: Observed occurrences of this ad in the selected period - **answers**: Distinct answers this ad appeared in during the selected period - **prompts**: Distinct prompts this ad appeared under during the selected period - **frequency**: Percentage of answers for those prompts that carried this ad (0-100) - **first_seen**: Date this ad was first observed (ISO 8601). Lifetime, not windowed. - **last_seen**: Date this ad was last observed (ISO 8601). Lifetime, not windowed. - **days_active**: Days between first and last observation, inclusive |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **topic_id** (EQ, IN): Filter by the topic IDs of the prompts the ad was observed under. An ad observed under several topics is returned for each of them. - **prompt_id** (EQ, IN): Filter by the prompt IDs the ad was observed under - **brand_mentioned** (EQUALS): Filter by prompt query type: brand or category |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to the last analysis date. |
| `per_page` | integer | No | Items per page |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by the country codes the answers were collected for |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List aeo advertisers

**Slug:** `AIROPS_MCP_LIST_AEO_ADVERTISERS`

List advertisers observed in ChatGPT answers for a Brand Kit. Advertisers observed by AirOps in collected ChatGPT answers. Rows roll up every observed ad to the advertiser that ran it, identified by canonical domain. Covers ads AirOps has seen in its own answer collection, so it does not describe full market coverage, targeting intent, impression delivery, or advertiser spend. Use list_aeo_ads for the individual ads behind these counts.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("advertiser_name" | "-advertiser_name" | "advertiser_domain" | "-advertiser_domain" | "ads" | "-ads" | "appearances" | "-appearances" | "answers" | "-answers" | "prompts" | "-prompts" | "first_seen" | "-first_seen" | "last_seen" | "-last_seen") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **advertiser_name/-advertiser_name**: Sort alphabetically by advertiser name - **advertiser_domain/-advertiser_domain**: Sort alphabetically by advertiser domain - **ads/-ads**: Sort by unique ad count. Use -ads for the most prolific first. - **appearances/-appearances**: Sort by observed occurrences - **answers/-answers**: Sort by distinct answers appeared in - **prompts/-prompts**: Sort by distinct prompts appeared under - **first_seen/-first_seen**: Sort by first observation date - **last_seen/-last_seen**: Sort by last observation date |
| `tags` | object | No | Filter by tags. Object form { "operator": "IN" &#124; "HAS_ALL" &#124; "NOT_IN", "value": [tag ids] }. IN matches items tagged with ANY of the tags; HAS_ALL matches only items tagged with ALL; NOT_IN matches only items tagged with NONE. |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **advertiser_id**: Stable advertiser identifier: the AirOps domain ID of the advertiser. Null when the domain could not be resolved, in which case advertiser_domain is the identity. - **advertiser_name**: Advertiser name, falling back to the domain when unknown. Resolved per advertiser, so it can vary across creatives. - **advertiser_domain**: Canonical advertiser domain - **advertiser_type**: Relationship to the Brand Kit: 'owned', 'competitor' or 'other' - **logo_url**: Advertiser logo, falling back to the favicon - **ads**: Unique ads this advertiser ran in the selected period - **appearances**: Observed occurrences of this advertiser's ads in the selected period - **answers**: Distinct answers this advertiser appeared in during the selected period - **prompts**: Distinct prompts this advertiser appeared under during the selected period - **first_seen**: Date this advertiser was first observed (ISO 8601). Lifetime, not windowed. - **last_seen**: Date this advertiser was last observed (ISO 8601). Lifetime, not windowed. |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **topic_id** (EQ, IN): Filter by the topic IDs of the prompts the advertiser was observed under - **prompt_id** (EQ, IN): Filter by the prompt IDs the advertiser was observed under - **brand_mentioned** (EQUALS): Filter by prompt query type: brand or category |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to the last analysis date. |
| `per_page` | integer | No | Items per page |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by the country codes the answers were collected for |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List aeo citations

**Slug:** `AIROPS_MCP_LIST_AEO_CITATIONS`

List citations (URLs) with metrics for a Brand Kit. Citations (URLs) with detailed metrics including influence score. Shows individual URLs being cited in AI answers with page-level context.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("citation_count" | "-citation_count" | "citation_share" | "-citation_share" | "citation_rate" | "-citation_rate" | "influence_score" | "-influence_score") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **citation_count/-citation_count**: Sort by citation count - **citation_share/-citation_share**: Sort by citation share percentage - **citation_rate/-citation_rate**: Sort by citation rate percentage - **influence_score/-influence_score**: Sort by influence score |
| `tags` | object | No | Filter by tags. Object form { "operator": "IN" &#124; "HAS_ALL" &#124; "NOT_IN", "value": [tag ids] }. IN matches items tagged with ANY of the tags; HAS_ALL matches only items tagged with ALL; NOT_IN matches only items tagged with NONE. |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **url**: The cited URL - **domain**: Domain ID - **domain_name**: Name of the domain - **domain_category**: Category: Owned, Competitors, Social, etc. - **logo_url**: URL to the domain logo - **citation_count**: Total number of citations for this URL - **citation_count_trend**: Change in citation count vs previous period - **citation_share**: Percentage of total citations (0-100) - **citation_share_trend**: Change in citation share vs previous period - **citation_rate**: Percentage of answers citing this URL (0-100) - **citation_rate_trend**: Change in citation rate vs previous period - **influence_score**: Overall influence score (0-100) - **influence_score_breakdown**: Breakdown: coverage_score, impact_score, da_score - **page_type**: Type of page - **brand_mentioned**: Whether brand is mentioned on the page - **brand_sentiment**: Sentiment toward brand on the page - **mentioned_competitor_domains**: Competitors mentioned on the page - **domain_authority**: Domain authority score - **social**: Platform-neutral social content and account enrichment |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **domain_category** (EQUALS, IN): Filter by domain category - **domain_id** (EQ, IN): Filter by domain ID - **topic_id** (EQ, IN): Filter by topic ID - **brand_mentioned** (EQUALS): Filter by query type: brand or category - **page_type** (EQUALS): Filter by page type - **brand_referenced** (EQ): Filter by whether brand is referenced on the citing page - **brand_sentiment** (EQUALS): Filter by brand sentiment - **mentioned_competitor_domains** (IN): Filter by competitor domain IDs mentioned on the citing page - **url** (CONTAINS): Filter by case-insensitive substring match on the citation URL. Matches against the normalized URL (without scheme or www). |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to today. |
| `per_page` | integer | No | Items per page |
| `personas` | array | No | Filter metrics by persona IDs |
| `countries` | array | No | Filter metrics by country codes |
| `providers` | array | No | Filter metrics by AI providers |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List aeo domains

**Slug:** `AIROPS_MCP_LIST_AEO_DOMAINS`

List domains cited in AI answers for a Brand Kit. Cited domains aggregated by domain with citation metrics. Shows which domains are being cited in AI answers for a Brand Kit.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("citation_count" | "-citation_count" | "citation_share" | "-citation_share" | "citation_rate" | "-citation_rate") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **citation_count/-citation_count**: Sort by citation count - **citation_share/-citation_share**: Sort by citation share percentage - **citation_rate/-citation_rate**: Sort by citation rate percentage |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **domain_id**: Unique identifier for the domain - **domain_name**: Name of the cited domain - **domain_category**: Category: Owned, Competitors, Social, Communities, etc. - **logo_url**: URL to the domain logo - **citation_count**: Total number of citations for this domain - **citation_count_trend**: Change in citation count vs previous period - **url_count**: Number of unique URLs cited from this domain - **citation_share**: Percentage of total citations (0-100) - **citation_share_trend**: Change in citation share vs previous period - **citation_rate**: Percentage of answers citing this domain (0-100) - **citation_rate_trend**: Change in citation rate vs previous period |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **domain_category** (EQUALS, IN): Filter by domain category - **topic_id** (EQ, IN): Filter by topic ID - **brand_mentioned** (EQUALS): Filter by query type: brand or category - **page_type** (EQUALS): Filter by page type - **brand_referenced** (EQ): Filter by whether brand is referenced on the citing page - **brand_sentiment** (EQUALS): Filter by brand sentiment on the citing page - **domain_name** (CONTAINS): Filter by case-insensitive substring match on the domain name. |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to today. |
| `per_page` | integer | No | Items per page |
| `personas` | array | No | Filter metrics by persona IDs |
| `countries` | array | No | Filter metrics by country codes |
| `providers` | array | No | Filter metrics by AI providers |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List aeo page content updates

**Slug:** `AIROPS_MCP_LIST_AEO_PAGE_CONTENT_UPDATES`

List page content updates for a workspace. Track content updates.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("created_at" | "-created_at" | "updated_at" | "-updated_at" | "url" | "-url") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **created_at/-created_at**: Sort by creation date - **updated_at/-updated_at**: Sort by last update date - **url/-url**: Sort alphabetically by URL |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the content update - **url**: The URL associated with the content update - **type**: Type of content update - **source**: Source that created the content update - **brand_kit_id**: ID of the associated Brand Kit - **workspace_id**: ID of the associated workspace - **created_at**: When the content update was created - **updated_at**: When the content update was last updated - **change_detection**: Change detection for this content update: detection status, the categories of detected changes, and whether a before/after diff is available. On a single-update fetch it also carries a nested `changes` array (the before/after diff). Null when change detection has not run for the update, including workspaces where it is not enabled. |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **url** (EQUALS, CONTAINS): Filter by content update URL - **type** (EQ, IN): Filter by content update type - **source** (EQ, IN): Filter by content update source - **brand_kit_id** (EQ): Filter by Brand Kit ID - **created_at** (GT, LT, GEQ, LEQ): Filter by creation date - **has_changes** (EQ): Filter to content updates where change detection found at least one confirmed change (true) or found none / has no detection run (false) |
| `includes` | array | No | Include related resources.  **Available includes:** - **metadata_entries**: Include metadata key-value pairs for each event |
| `per_page` | integer | No | Items per page |
| `brand_kit_id` | integer | No | Optional Brand Kit ID to filter content updates by |
| `workspace_id` | integer | No | ID of the workspace to retrieve results from. If not provided, returns results from all workspaces the user belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List aeo prompts

**Slug:** `AIROPS_MCP_LIST_AEO_PROMPTS`

List AEO prompts for a specific Brand Kit. Questions are the AI prompts that can be asked about a brand. They are used to get AI answers with citations and mentions.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("text" | "-text" | "created_at" | "-created_at" | "prompt_volume" | "-prompt_volume" | "mention_rate" | "-mention_rate" | "positive_answer_rate" | "-positive_answer_rate" | "citation_rate" | "-citation_rate") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **text/-text**: Sort alphabetically by prompt text - **created_at/-created_at**: Sort by creation date - **prompt_volume/-prompt_volume**: Sort by prompt volume level - **mention_rate/-mention_rate**: Sort by mention rate - **positive_answer_rate/-positive_answer_rate**: Sort by positive answer rate - **citation_rate/-citation_rate**: Sort by citation rate |
| `tags` | object | No | Filter by tags. Object form { "operator": "IN" &#124; "HAS_ALL" &#124; "NOT_IN", "value": [tag ids] }. IN matches items tagged with ANY of the tags; HAS_ALL matches only items tagged with ALL; NOT_IN matches only items tagged with NONE. |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the prompt - **text**: The prompt text - **keyword**: Associated keyword for the prompt - **brand_mentioned**: Whether the brand is mentioned in the prompt - **prompt_volume**: Prompt volume level - **search_volume**: Actual search volume behind the prompt_volume level - **topic_id**: ID of the associated topic - **created_at**: When the prompt was created - **updated_at**: When the prompt was last updated - **mention_rate**: Percentage of AI answers mentioning the brand (0-100) - **positive_answer_rate**: Percentage of answers where your brand is positively mentioned, out of all answers where you have been mentioned and sentiment is determined - **citation_rate**: Percentage of AI answers citing the brand (0-100) - **mention_rate_trend**: Change in mention_rate vs previous period - **positive_answer_rate_trend**: Change in positive_answer_rate vs previous period - **citation_rate_trend**: Change in citation_rate vs previous period - **query_fanouts**: Search queries the AI ran internally when answering this question. Each entry has text (the raw query), normalized_text (lowercase/trimmed), and observations_count (how many times it appeared — higher = stronger signal). Empty if query fanout capture is not enabled for your workspace or if we do not support it for this provider. - **countries**: ISO alpha-2 country codes currently assigned to the prompt (live). - **persona_ids**: IDs of personas currently assigned to the prompt (live). Use `list_personas` to resolve to titles. - **platforms**: AI platforms currently assigned to the prompt (live). - **est_answers_mo**: Estimated monthly answer credits this prompt will consume given its current live country, persona, and platform assignments. |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **text** (EQUALS, CONTAINS): Filter by prompt text - **strategy** (EQ, IN): Filter by strategy: 0 = manual, 1 = deep_research - **brand_mentioned** (EQ): Filter by whether brand is mentioned - **topic_id** (EQ, IN): Filter by topic ID - **prompt_volume** (EQUALS, IN): Filter by prompt volume level - **mention_rate** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by mention rate percentage (0-100) - **positive_answer_rate** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by positive answer rate percentage (0-100) - **citation_rate** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by citation rate percentage (0-100) - **country** (EQ, IN): Filter prompts by an assigned country (ISO alpha-2). EQ matches a single code; IN matches any in the list. - **persona_id** (EQ, IN): Filter prompts by an assigned persona ID. Use `list_personas` to discover persona IDs. - **platform** (EQ, IN): Filter prompts by an assigned AI platform. |
| `end_date` | string | No | End date for metrics (ISO 8601). Defaults to today. |
| `includes` | array | No | Include related resources.  **Available includes:** - **topic**: Include the associated topic - **tags**: Include the associated tags |
| `per_page` | integer | No | Items per page |
| `personas` | array | No | Filter metrics by persona IDs |
| `countries` | array | No | Filter metrics by country codes |
| `providers` | array | No | Filter metrics by AI providers |
| `start_date` | string | No | Start date for metrics (ISO 8601). Defaults to 1 month ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List answers

**Slug:** `AIROPS_MCP_LIST_ANSWERS`

List AI answers for a brand kit with filters for date range, providers, countries, prompt_id, and brand_mentioned. Individual AI answers with their cited URLs and brand/competitor mentions.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Answer ID - **date**: Analysis date (YYYY-MM-DD) - **text**: Answer text. Truncated to 200 chars with ellipsis on list; full on show. - **prompt**: Prompt (question) text - **intent_stage**: Prompt intent stage - **citations**: URLs cited by the answer (deduped) - **mentions**: Brand names mentioned in the answer (self brand + competitors) - **persona**: Persona name or "default" - **provider**: AI provider - **country**: ISO alpha-2 country code - **brand_mentioned**: Whether the brand was mentioned in this answer |
| `end_date` | string | No | End date (ISO 8601) on aeo_analyses.created_at. Defaults to today. |
| `per_page` | integer | No | Items per page |
| `countries` | array | No | Filter by ISO alpha-2 country codes. |
| `prompt_id` | integer | No | Filter answers by a single prompt (question) ID. |
| `providers` | array | No | Filter by AI providers. |
| `start_date` | string | No | Start date (ISO 8601) on aeo_analyses.created_at. Defaults to 30 days ago. |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |
| `brand_mentioned` | boolean | No | When set, filter to answers where the brand was (or was not) mentioned. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List brand kits

**Slug:** `AIROPS_MCP_LIST_BRAND_KITS`

List all Brand Kits the user has access to. Returns `brand_management_enabled` and `aeo_enabled` flags for each brand kit. Use `get_brand_kit` for brand management details, or `get_insights_settings` for AEO configuration. Optionally filter by workspace_name, workspace_id, brand_management_enabled, or aeo_enabled. IMPORTANT: If multiple brand kits are returned and the user has not specified which one to use, present the list and ask them to confirm. Do NOT arbitrarily pick one.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("brand_name" | "-brand_name" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **brand_name/-brand_name**: Sort alphabetically by brand name - **created_at/-created_at**: Sort by creation date |
| `fields` | array | No | Select additional fields to return.  Default fields (id, brand_name, brand_about, brand_url, workspace_name, brand_management_enabled, aeo_enabled) are always included.  **Optional fields:** - **created_at**: When the Brand Kit was created - **updated_at**: When the Brand Kit was last updated |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **workspace_name** (EQUALS, CONTAINS): Filter by workspace name - **brand_name** (EQUALS, CONTAINS): Filter by brand name - **brand_url** (EQUALS, CONTAINS): Filter by brand URL - **brand_management_enabled** (EQ): Filter by whether the Brand Kit is enabled for brand management - **aeo_enabled** (EQ): Filter by whether AEO analytics data is available |
| `version` | string ("active" | "draft") | No | Brand Kit version to read from (`active` or `draft`). Defaults to `active`. |
| `per_page` | integer | No | Items per page |
| `workspace_id` | integer | No | ID of the workspace to retrieve results from. If not provided, returns results from all workspaces the user belongs to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List campaigns

**Slug:** `AIROPS_MCP_LIST_CAMPAIGNS`

List campaigns the authenticated user has access to. Use get_campaign to retrieve action grid IDs and custom instructions for a campaign. Campaigns are Plays that coordinate strategy playbooks, action grids, and related opportunities for improving AEO performance.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "created_at" | "-created_at" | "updated_at" | "-updated_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort by campaign name - **created_at/-created_at**: Sort by creation date - **updated_at/-updated_at**: Sort by last updated date |
| `fields` | array | No | Select additional fields to return.  Default fields (id, name, status, action_type, resource_type, brand_kit_id, workspace_id, strategy_playbook_id, opportunity_schema_version, created_at, updated_at) are always included.  **Optional fields:** - **action_grid_id**: Action grid ID to use with grid tools - **action_grid_table_id**: Action grid table ID to use with grid tools - **custom_instructions**: Campaign-specific guidance for working related opportunities |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by campaign name - **status** (EQUALS, IN): Filter by campaign status - **workspace_id** (EQ, IN): Filter by workspace ID - **brand_kit_id** (EQ, IN): Filter by Brand Kit ID - **resource_type** (EQUALS, IN): Filter by campaign resource type |
| `per_page` | integer | No | Items per page |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List grids

**Slug:** `AIROPS_MCP_LIST_GRIDS`

List grids the authenticated user has access to. Use includes=[\"grid_tables.grid_columns\"] to get table and column structure needed for read_grid and write_grid calls. Grids are spreadsheet-like tables for organizing and processing data. Each grid contains one or more tables (sheets), and each table has columns and rows. Use includes to retrieve table and column structure needed for read_grid and write_grid calls.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "updated_at" | "-updated_at" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort by grid name - **updated_at/-updated_at**: Sort by last updated date - **created_at/-created_at**: Sort by creation date |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Grid ID (use as grid_id in read_grid/write_grid) - **name**: Grid name - **workspace_id**: Workspace this grid belongs to |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by grid name - **workspace_id** (EQ): Filter by workspace ID |
| `includes` | array | No | Include related resources.  **Available includes:** - **grid_tables**: Include grid tables (sheets) with their columns (can nest: grid_columns) |
| `per_page` | integer | No | Items per page |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List knowledge bases

**Slug:** `AIROPS_MCP_LIST_KNOWLEDGE_BASES`

List all Knowledge Bases the authenticated user has access to. Knowledge Bases store documents for semantic search. Use the status field to determine if a Knowledge Base is searchable (status: "ready").

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort alphabetically by Knowledge Base name - **created_at/-created_at**: Sort by creation date |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the Knowledge Base - **name**: Human-readable name of the Knowledge Base - **workspace_id**: ID of the workspace this Knowledge Base belongs to - **status**: Current status. Only "ready" Knowledge Bases can be searched. - **document_count**: Total number of documents in this Knowledge Base |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **workspace_name** (EQUALS, CONTAINS): Filter by workspace name - **name** (EQUALS, CONTAINS): Filter by Knowledge Base name. Use EQUALS for exact match, CONTAINS for partial. - **workspace_id** (EQ): Filter by workspace ID to list Knowledge Bases from a specific workspace. |
| `per_page` | integer | No | Items per page |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List opportunities

**Slug:** `AIROPS_MCP_LIST_OPPORTUNITIES`

List opportunities for a campaign. V1 responses include matching opportunity items; v2 responses include parent review state, the target page, and ordered contexts.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Maximum number of opportunities to return. Defaults to 30. |
| `status` | array | No | Optional item statuses for v1 or parent statuses for v2. Defaults to all. |
| `play_id` | integer | Yes | Campaign ID. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | No |  |

### List pages

**Slug:** `AIROPS_MCP_LIST_PAGES`

List web pages with daily metrics (AEO citations, GSC clicks/impressions, GA4 traffic, and configured GA4 key events) for a brand kit. Supports filtering by metrics and text fields, sorting, and pagination.Web page metrics are the daily metrics of citations, clicks, impressions, traffic, and configured GA4 key events for a specific web page.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("url" | "-url" | "folder_name" | "-folder_name" | "citations_count" | "-citations_count" | "citations_count_diff" | "-citations_count_diff" | "citation_rate" | "-citation_rate" | "citation_rate_diff" | "-citation_rate_diff" | "prompts_count" | "-prompts_count" | "prompts_count_diff" | "-prompts_count_diff" | "clicks" | "-clicks" | "clicks_diff" | "-clicks_diff" | "impressions" | "-impressions" | "impressions_diff" | "-impressions_diff" | "ctr" | "-ctr" | "ctr_diff" | "-ctr_diff" | "position" | "-position" | "position_diff" | "-position_diff" | "traffic" | "-traffic" | "traffic_diff" | "-traffic_diff" | "sessions" | "-sessions" | "sessions_diff" | "-sessions_diff" | "engagement" | "-engagement" | "engagement_diff" | "-engagement_diff" | "average_session_engagement" | "-average_session_engagement" | "average_session_engagement_diff" | "-average_session_engagement_diff") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **url/-url**: Sort alphabetically by URL - **folder_name/-folder_name**: Sort alphabetically by folder name - **citations_count/-citations_count**: Sort by citation count - **citations_count_diff/-citations_count_diff**: Sort by citation change - **citation_rate/-citation_rate**: Sort by citation rate - **citation_rate_diff/-citation_rate_diff**: Sort by citation rate change - **prompts_count/-prompts_count**: Sort by prompts count - **prompts_count_diff/-prompts_count_diff**: Sort by prompts count change - **clicks/-clicks**: Sort by GSC clicks - **clicks_diff/-clicks_diff**: Sort by clicks change - **impressions/-impressions**: Sort by GSC impressions - **impressions_diff/-impressions_diff**: Sort by impressions change - **ctr/-ctr**: Sort by click-through rate - **ctr_diff/-ctr_diff**: Sort by CTR change - **position/-position**: Sort by search position - **position_diff/-position_diff**: Sort by position change - **traffic/-traffic**: Sort by GA4 traffic - **traffic_diff/-traffic_diff**: Sort by traffic change - **sessions/-sessions**: Sort by GA4 sessions - **sessions_diff/-sessions_diff**: Sort by sessions change - **engagement/-engagement**: Sort by GA4 engagement - **engagement_diff/-engagement_diff**: Sort by engagement change - **average_session_engagement/-average_session_engagement**: Sort by avg session duration - **average_session_engagement_diff/-average_session_engagement_diff**: Sort by avg session duration change |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the web page - **web_page_id**: Web page ID (same as id) - **url**: Full URL of the web page - **folder_name**: First path segment of the URL (folder/section) - **primary_keyword**: Primary keyword associated with this page - **tracked**: Whether the page is tracked and scraped to track content changes - **citations_count**: Number of citations this page received - **citations_count_diff**: Percentage change in citations vs comparison period - **citation_rate**: Ratio of citing answers to total answers with citations - **citation_rate_diff**: Absolute change in citation rate vs comparison period - **prompts_count**: Number of unique prompts citing this page - **prompts_count_diff**: Change in prompts count vs comparison period - **clicks**: Google Search Console clicks - **clicks_diff**: Percentage change in clicks vs comparison period - **impressions**: Google Search Console impressions - **impressions_diff**: Percentage change in impressions vs comparison period - **ctr**: Click-through rate (clicks/impressions) - **ctr_diff**: Absolute change in CTR vs comparison period - **position**: Average search position - **position_diff**: Percentage change in position vs comparison period - **traffic**: GA4 active users - **traffic_diff**: Percentage change in traffic vs comparison period - **sessions**: GA4 sessions count - **sessions_diff**: GA4 Percentage change in sessions vs comparison period - **engagement**: GA4 engaged sessions - **engagement_diff**: GA4Percentage change in engagement vs comparison period - **average_session_engagement**: GA4 Average session duration in seconds - **average_session_engagement_diff**: GA4 Percentage change in average session duration vs comparison period - **events**: Configured GA4 key events for this page. Each event includes event_name, event_count, and event_diff. - **amplitude_events**: Configured Amplitude events for this page. Each event includes event_name, totals, and totals_diff. |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **clicks** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by GSC clicks count - **impressions** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by GSC impressions count - **ctr** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by click-through rate - **position** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by average search position - **citations_count** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by citation count - **citation_rate** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by citation rate - **prompts_count** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by prompts count - **traffic** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by GA4 traffic - **sessions** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by GA4 sessions - **engagement** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by GA4 engaged sessions - **average_session_engagement** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by average session duration - **clicks_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by clicks change percentage - **impressions_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by impressions change percentage - **ctr_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by CTR change - **position_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by position change percentage - **citations_count_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by citations change percentage - **citation_rate_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by citation rate change - **prompts_count_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by prompts count change - **traffic_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by traffic change percentage - **sessions_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by sessions change percentage - **engagement_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by engagement change percentage - **average_session_engagement_diff** (EQ, NEQ, GT, LT, GEQ, LEQ): Filter by average session duration change - **last_refreshed_at** (LEQ): Filter by pages last refreshed before a preset window. Valid values: 1m, 3m, 6m, 1y. - **url** (EQUALS, CONTAINS, STARTS_WITH, IN): Filter by page URL. Use CONTAINS for partial match. - **folder_name** (EQUALS, CONTAINS, STARTS_WITH, IN): Filter by folder/section name - **primary_keyword** (EQUALS, CONTAINS, STARTS_WITH, IN): Filter by primary keyword - **session_source** (EQUALS, CONTAINS, STARTS_WITH, IN): Filter by GA4 session source. Valid values: (direct), google, chatgpt.com, perplexity.ai, perplexity, reddit.com, gemini.google.com, youtube.com, linkedin, linkedin.com, lnkd.in, bing, claude.ai - **session_medium** (EQUALS, CONTAINS, STARTS_WITH, IN): Filter by GA4 session medium. Valid values: organic, cpc, email, social, direct, referral - **tracked** (EQ): Filter by tracked status |
| `end_date` | string | No | End date for analysis period (ISO 8601 format, defaults to today) |
| `per_page` | integer | No | Items per page |
| `start_date` | string | No | Start date for analysis period (ISO 8601 format, defaults to 1 month ago) |
| `event_names` | array | No | Optional configured GA4 key event names to include. If omitted, all active key events configured for the brand kit are included. |
| `brand_kit_id` | integer | Yes | ID of the brand kit to retrieve web page metrics for |
| `smart_filter` | string ("almost_page_one" | "losing_clicks" | "rankings_slipping" | "losing_ai_visibility" | "citation_rate_decline" | "stale_pages") | No | Apply a predefined filter preset.  **Available presets:** - **almost_page_one**: Pages ranking 10-20 - **losing_clicks**: Pages losing clicks despite stable rankings - **rankings_slipping**: Pages declining in SERP - **losing_ai_visibility**: Pages losing AI citations - **citation_rate_decline**: Pages losing citation rate - **stale_pages**: Pages not updated in 6 months |
| `amplitude_event_names` | array | No | Optional configured Amplitude event names to include. If omitted, all active Amplitude events configured for the brand kit are included when the feature is enabled. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List personas

**Slug:** `AIROPS_MCP_LIST_PERSONAS`

List personas for a specific Brand Kit. Personas are the characters that can be used to ask questions about a brand. They are used to get AI answers with citations and mentions.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("title" | "-title" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **title/-title**: Sort alphabetically by persona title - **created_at/-created_at**: Sort by creation date |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the persona - **title**: The persona title - **description**: The persona description - **created_at**: When the persona was created - **updated_at**: When the persona was last updated |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **title** (EQUALS, CONTAINS): Filter by persona title |
| `per_page` | integer | No | Items per page |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List reports

**Slug:** `AIROPS_MCP_LIST_REPORTS`

List saved analytics reports for a specific Brand Kit. Reports are saved analytics views for a Brand Kit. They contain modules that display different analytics metrics.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "created_at" | "-created_at" | "updated_at" | "-updated_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort alphabetically by report name - **created_at/-created_at**: Sort by creation date - **updated_at/-updated_at**: Sort by last update |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the report - **name**: Report name - **created_at**: When the report was created - **updated_at**: When the report was last updated |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by report name |
| `includes` | array | No | Include related resources.  **Available includes:** - **modules**: Include the report modules with their configurations |
| `per_page` | integer | No | Items per page |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List tags

**Slug:** `AIROPS_MCP_LIST_TAGS`

List tags for a specific Brand Kit. Tags are user-defined labels applied to prompts within a Brand Kit. They are typically used to filter analytics by arbitrary groupings (e.g. priority, campaign, theme) that go beyond topics.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort alphabetically by tag name - **created_at/-created_at**: Sort by creation date |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the tag - **name**: The tag name - **color**: The tag color (hex code) |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by tag name |
| `per_page` | integer | No | Items per page |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List topics

**Slug:** `AIROPS_MCP_LIST_TOPICS`

List topics for a specific Brand Kit. Topics are the categories of questions that can be asked about a Brand Kit. They are used to group questions together and to create a taxonomy of questions.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort alphabetically by topic name - **created_at/-created_at**: Sort by creation date |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the topic - **name**: The topic name - **color**: The topic color (hex code) |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by topic name |
| `per_page` | integer | No | Items per page |
| `brand_kit_id` | integer | Yes | The ID of the Brand Kit |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### List workspaces

**Slug:** `AIROPS_MCP_LIST_WORKSPACES`

List all workspaces the authenticated user has access to. Workspaces are the top-level container for all resources in the AirOps platform. A user can have multiple workspaces, and each workspace can have multiple resources like Brand Kits, Pages, Prompts, etc.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number |
| `sort` | string ("name" | "-name" | "created_at" | "-created_at") | No | Sort field. Prefix with - for descending.  **Available sort fields:** - **name/-name**: Sort alphabetically by workspace name (A-Z ascending, Z-A descending) - **created_at/-created_at**: Sort by creation date. Use -created_at for newest first (recommended default). |
| `fields` | array | No | Select additional fields to return.  **Optional fields:** - **id**: Unique identifier for the workspace - **name**: Human-readable workspace name set by the user - **slug**: URL-friendly identifier derived from workspace name and ID - **tier**: Subscription tier (e.g., "free", "pro", "platform") determining available features |
| `filters` | array | No | Filter results. Nested fields (e.g. `writing_rules.text`) filter within an included association and require that association in `includes`.  **Available fields:** - **name** (EQUALS, CONTAINS): Filter by workspace name. Use EQUALS for exact match, CONTAINS for partial. - **tier** (EQ, IN): Filter by subscription tier. Use EQ for single tier, IN for multiple. |
| `per_page` | integer | No | Items per page |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | Yes |  |
| `meta` | object | Yes |  |

### Manage brand kit audience

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_AUDIENCE`

Create or update an audience for a Brand Kit draft.
Omit `id` to create a new audience; provide `id` to update an existing one.
On update, only provided fields are changed.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Audience ID (omit to create new) |
| `name` | string | No | Audience name (required on create) |
| `description` | string | No | Audience description |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `description` | string | No |  |

### Manage brand kit competitor

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_COMPETITOR`

Create or update a competitor for a Brand Kit.
Omit `id` to create a new competitor; provide `id` to update an existing one.
On update, only provided fields are changed.
Competitors must be associated with at least one product line from the same brand kit.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Competitor ID (omit to create new) |
| `name` | string | No | Competitor name (required on create) |
| `domain` | string | No | Competitor domain (e.g. "example.com") |
| `details` | string | No | Freeform markdown notes about the competitor (positioning, strengths, etc.) |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `product_line_ids` | array | No | Product line IDs to associate (must belong to this brand kit, at least one required) |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `domain` | string | No |  |
| `created` | boolean | No |  |
| `details` | string | No |  |
| `product_line_ids` | array | No |  |

### Manage brand kit content sample

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_CONTENT_SAMPLE`

Create or update a content sample for a Brand Kit.
Omit `id` to create a new content sample; provide `id` to update an existing one.
On create, provide either `url` (content will be extracted asynchronously) or `content` (plain text, stored immediately).
On update, only `content`, `url`, `audience_ids`, and `region_ids` can be changed.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Content sample ID (omit to create new) |
| `url` | string | No | URL of the content sample. On create, provide either url or content (not both). |
| `content` | string | No | Plain text content for the sample. On create, provide either content or url (not both). |
| `region_ids` | array | No | Region IDs to associate (must belong to this brand kit). Pass [] to clear. |
| `audience_ids` | array | No | Audience IDs to associate (must belong to this brand kit). Pass [] to clear. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `content_type_id` | integer | No | Content type ID (required on create, must belong to this brand kit) |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The content sample ID |
| `url` | string | No | The source URL for this content sample |
| `content` | string | No | The extracted (markdown) or edited content body |
| `created` | boolean | No | Whether the record was created (true) or updated (false) |
| `region_ids` | array | No | Region IDs associated with this content sample |
| `audience_ids` | array | No | Audience IDs associated with this content sample |
| `process_status` | string | No | Processing state for URL content extraction |
| `content_type_id` | integer | No | The ID of the associated content type |

### Manage brand kit content type

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_CONTENT_TYPE`

Create or update a content type for a Brand Kit.
Omit `id` to create a new content type; provide `id` to update an existing one.

On create, you may provide a `sample_url` to seed the content type with a content sample:
- If only `sample_url` is provided (no content fields), the system will scrape the URL and use AI to generate all content type fields automatically.
- If `sample_url` is provided along with content fields, the URL will be scraped for reference but the provided field values will be kept as-is.
- If no `sample_url` is provided, the content type is created with the given fields only.

On update, only provided fields are changed and `sample_url` is ignored.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Content type ID (omit to create new) |
| `name` | string | No | Content type name (required on create) |
| `cta_url` | string | No | Call-to-action URL |
| `cta_text` | string | No | Call-to-action text |
| `sample_url` | string | No | URL of a content sample (only used on create) |
| `header_case` | string ("title_case" | "sentence_case" | "custom") | No | Header case style |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `template_outline` | string | No | Template outline |
| `header_case_custom_value` | string | No | Custom header case rules (when header_case is custom) |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `cta_url` | string | No |  |
| `cta_text` | string | No |  |
| `header_case` | string | No |  |
| `template_outline` | string | No |  |
| `generation_status` | string | No |  |
| `header_case_custom_value` | string | No |  |

### Manage brand kit custom variable

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_CUSTOM_VARIABLE`

Before creating a custom variable, you MUST analyze the user's intent and suggest the appropriate Brand Kit dimension instead. Map the request to these alternatives first:
- Region + Writing Rule: For location/language-specific instructions (e.g., "US writing tone", "UK spelling")
- Audience + Writing Rule: For audience-specific guidelines (e.g., "developer tone", "enterprise style")
- Content Type: For format-specific rules (e.g., "blog post structure", "email CTA")
- Writing Rule (global): For general writing guidelines
- Writing Tone / Writing Persona: For overall voice and style

Create or update a custom variable for a Brand Kit.
Omit `id` to create a new custom variable; provide `id` to update an existing one.
On create, `name` and `value` are required. On update, only provided fields are changed.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Custom variable ID (omit to create new) |
| `name` | string | No | Custom variable name (required on create) |
| `value` | string | No | Custom variable value (required on create, editable on update) |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | The ID of the custom variable |
| `name` | string | No | The name of the custom variable |
| `value` | string | No | The value of the custom variable |
| `created` | boolean | No | True if the custom variable was created, false if it was updated |

### Manage brand kit font

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_FONT`

Create or update a font for a Brand Kit.
Omit `id` to create a new font; provide `id` to update an existing one.
On update, only provided fields are changed.

To attach a font file, either:
- Use `signed_id` with the token from create_brand_kit_direct_upload after direct upload (preferred).
- Use `file_url` with a publicly accessible URL to a font file (TTF, OTF, WOFF, WOFF2, or EOT).
Alternatively, provide `google_font_link` with a Google Fonts URL.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Font ID (omit to create new) |
| `name` | string | No | Font name, e.g. "Inter" or "Brand Heading Font" (required on create) |
| `file_url` | string | No | Publicly accessible URL to a font file (TTF, OTF, WOFF, WOFF2, or EOT). Use signed_id instead if the file was uploaded via create_brand_kit_direct_upload. Pass null or empty to leave the existing file unchanged. |
| `signed_id` | string | No | Signed blob ID returned by create_brand_kit_direct_upload after a direct upload. Preferred over file_url when the user has a local file. Pass null to leave the existing file unchanged. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `google_font_link` | string | No | Google Fonts URL for this font (e.g. https://fonts.google.com/specimen/Inter). Pass null or empty string to clear. |
| `usage_instructions` | string | No | Instructions for agents on when and how to use this font. Pass null or empty string to clear. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `file_url` | string | No |  |
| `google_font_link` | string | No |  |
| `usage_instructions` | string | No |  |

### Manage brand kit logo size

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_LOGO_SIZE`

Create or update a logo size for a Brand Kit.
Omit `id` to create a new logo size; provide `id` to update an existing one.
On update, only provided fields are changed.

At least one of `width` or `height` must be provided on create.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Logo size ID (omit to create new) |
| `name` | string | No | Logo size name, e.g. "Web Banner" or "Social Media Square" (required on create) |
| `width` | string | No | Width in pixels. Pass null to clear. |
| `height` | string | No | Height in pixels. Pass null to clear. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `usage_instructions` | string | No | Instructions for agents on when and how to use this logo size. Pass null to clear. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `width` | string | No |  |
| `height` | string | No |  |
| `created` | boolean | No |  |
| `usage_instructions` | string | No |  |

### Manage brand kit logo variant

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_LOGO_VARIANT`

Create or update a logo variant for a Brand Kit.
Omit `id` to create a new logo variant; provide `id` to update an existing one.
On update, only provided fields are changed.

To attach a logo image, either:
- Use `signed_id` with the token from create_brand_kit_direct_upload after direct upload (preferred).
- Use `file_url` with a publicly accessible URL to a PNG or SVG image.

Always provide `usage_instructions` when creating a logo variant — describe when and how to use this logo (e.g. "Primary logo for light backgrounds", "Monochrome version for print materials").

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Logo variant ID (omit to create new) |
| `name` | string | No | Logo variant name, e.g. "Primary Logo" or "Dark Background Logo" (required on create) |
| `file_url` | string | No | Publicly accessible URL to a PNG or SVG image. Use signed_id instead if the file was uploaded via create_brand_kit_direct_upload. Pass null to leave the existing file unchanged. |
| `signed_id` | string | No | Signed blob ID returned by create_brand_kit_direct_upload after a direct upload. Preferred over file_url when the user has a local file. Pass null to leave the existing file unchanged. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `background_color` | string | No | Background color as a hex value (e.g. #ffffff). Pass null to clear. |
| `usage_instructions` | string | No | Instructions for agents on when and how to use this logo, e.g. "Use on dark backgrounds only". Pass null to clear. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `file_url` | string | No |  |
| `background_color` | string | No |  |
| `usage_instructions` | string | No |  |

### Manage brand kit palette

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_PALETTE`

Create or update a color palette for a Brand Kit.
Omit `id` to create a new palette; provide `id` to update an existing one.
On update, only provided fields are changed.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Palette ID (omit to create new) |
| `name` | string | No | The palette name, e.g. "Primary" (required on create) |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |

### Manage brand kit palette color

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_PALETTE_COLOR`

Create or update a color within a Brand Kit palette.
Omit `id` to create a new color; provide `id` to update an existing one.
On create, `palette_id` is required.
On update, only provided fields are changed.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Color ID (omit to create new) |
| `name` | string | No | Color name, e.g. "Brand Blue" (required on create) |
| `value` | string | No | Hex color value, e.g. "#0055ff" (required on create) |
| `palette_id` | integer | No | The palette ID (required on create) |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `usage_instructions` | string | No | Instructions for agents on when and how to use this color |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `value` | string | No |  |
| `created` | boolean | No |  |
| `palette_id` | integer | No |  |
| `usage_instructions` | string | No |  |

### Manage brand kit product line

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_PRODUCT_LINE`

Create or update a product line for a Brand Kit.
Omit `id` to create a new product line; provide `id` to update an existing one.
On update, only provided fields are changed.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Product line ID (omit to create new) |
| `url` | string | No | Product line URL |
| `name` | string | No | Product line name (required on create) |
| `details` | string | No | Product line details |
| `positioning` | string | No | Product positioning |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `ideal_customer_profile` | string | No | Ideal customer profile |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `url` | string | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `details` | string | No |  |
| `positioning` | string | No |  |
| `generation_status` | string | No |  |
| `ideal_customer_profile` | string | No |  |

### Manage brand kit region

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_REGION`

Create or update a region for a Brand Kit.
Omit `id` to create a new region; provide `id` to update an existing one.
On update, only provided fields are changed.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Region ID (omit to create new) |
| `name` | string | No | Region name (required on create) |
| `icon_name` | string ("flag-ad" | "flag-ae" | "flag-af" | "flag-ag" | "flag-ai" | "flag-al" | "flag-am" | "flag-ao" | "flag-ar" | "flag-as" | "flag-at" | "flag-au" | "flag-aw" | "flag-az" | "flag-ba" | "flag-bb" | "flag-bd" | "flag-be" | "flag-bf" | "flag-bg" | "flag-bh" | "flag-bi" | "flag-bj" | "flag-bn" | "flag-bo" | "flag-br" | "flag-bs" | "flag-bt" | "flag-bw" | "flag-by" | "flag-bz" | "flag-ca" | "flag-cd" | "flag-cf" | "flag-cg" | "flag-ch" | "flag-ci" | "flag-ck" | "flag-cl" | "flag-cm" | "flag-cn" | "flag-co" | "flag-cr" | "flag-cu" | "flag-cv" | "flag-cw" | "flag-cy" | "flag-cz" | "flag-de" | "flag-dj" | "flag-dk" | "flag-dm" | "flag-do" | "flag-dz" | "flag-ec" | "flag-ee" | "flag-eg" | "flag-en" | "flag-er" | "flag-es" | "flag-et" | "flag-eu" | "flag-fi" | "flag-fj" | "flag-fk" | "flag-fm" | "flag-fo" | "flag-fr" | "flag-ga" | "flag-gb" | "flag-gd" | "flag-ge" | "flag-gg" | "flag-gh" | "flag-gi" | "flag-gl" | "flag-gm" | "flag-gn" | "flag-gq" | "flag-gr" | "flag-gt" | "flag-gu" | "flag-gw" | "flag-gy" | "flag-hk" | "flag-hn" | "flag-hr" | "flag-ht" | "flag-hu" | "flag-id" | "flag-ie" | "flag-il" | "flag-im" | "flag-in" | "flag-io" | "flag-iq" | "flag-ir" | "flag-is" | "flag-it" | "flag-je" | "flag-jm" | "flag-jo" | "flag-jp" | "flag-ke" | "flag-kg" | "flag-kh" | "flag-ki" | "flag-km" | "flag-kp" | "flag-kr" | "flag-kw" | "flag-ky" | "flag-kz" | "flag-la" | "flag-lb" | "flag-lc" | "flag-li" | "flag-lk" | "flag-lr" | "flag-ls" | "flag-lt" | "flag-lu" | "flag-lv" | "flag-ly" | "flag-ma" | "flag-mc" | "flag-md" | "flag-me" | "flag-mg" | "flag-mh" | "flag-mk" | "flag-ml" | "flag-mm" | "flag-mn" | "flag-mo" | "flag-mp" | "flag-mr" | "flag-ms" | "flag-mt" | "flag-mu" | "flag-mv" | "flag-mw" | "flag-mx" | "flag-my" | "flag-mz" | "flag-na" | "flag-ne" | "flag-nf" | "flag-ng" | "flag-ni" | "flag-nl" | "flag-no" | "flag-np" | "flag-nr" | "flag-nu" | "flag-nz" | "flag-om" | "flag-pa" | "flag-pe" | "flag-pf" | "flag-pg" | "flag-ph" | "flag-pk" | "flag-pl" | "flag-pn" | "flag-pr" | "flag-ps" | "flag-pt" | "flag-pw" | "flag-py" | "flag-qa" | "flag-ro" | "flag-rs" | "flag-ru" | "flag-rw" | "flag-sa" | "flag-sb" | "flag-sc" | "flag-sd" | "flag-se" | "flag-sg" | "flag-si" | "flag-sk" | "flag-sl" | "flag-sm" | "flag-sn" | "flag-so" | "flag-sr" | "flag-ss" | "flag-st" | "flag-sv" | "flag-sx" | "flag-sy" | "flag-sz" | "flag-tc" | "flag-td" | "flag-tg" | "flag-th" | "flag-tj" | "flag-tk" | "flag-tl" | "flag-tm" | "flag-tn" | "flag-to" | "flag-tr" | "flag-tt" | "flag-tv" | "flag-tw" | "flag-tz" | "flag-ua" | "flag-ug" | "flag-us" | "flag-uy" | "flag-uz" | "flag-va" | "flag-vc" | "flag-ve" | "flag-vg" | "flag-vi" | "flag-vn" | "flag-vu" | "flag-wl" | "flag-ws" | "flag-xk" | "flag-ye" | "flag-za" | "flag-zm" | "flag-zw" | "") | No | Flag icon name (e.g. flag-us, flag-gb). Pass empty string or null to clear. |
| `description` | string | No | Region description |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `icon_name` | string | No |  |
| `description` | string | No |  |

### Manage brand kit type size

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_TYPE_SIZE`

Create or update a type size for a Brand Kit.
Omit `id` to create a new type size; provide `id` to update an existing one.
On update, only provided fields are changed.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Type size ID (omit to create new) |
| `name` | string | No | Type size name, e.g. "H1 Display" or "Body Regular" (required on create) |
| `size` | string | No | Font size in pixels. |
| `weight` | string | No | Font weight as an integer (100–900), e.g. 400 or 700. |
| `font_id` | string | No | ID of the font this type size belongs to |
| `line_height` | string | No | Line height as a decimal multiplier, e.g. 1.5. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `usage_instructions` | string | No | Instructions for agents on when and how to use this type size. Pass null or empty string to clear. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `size` | string | No |  |
| `weight` | string | No |  |
| `created` | boolean | No |  |
| `font_id` | string | No |  |
| `line_height` | string | No |  |
| `usage_instructions` | string | No |  |

### Manage brand kit usage rule

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_USAGE_RULE`

Create or update a usage rule for a Brand Kit.
Omit `id` to create a new usage rule; provide `id` to update an existing one.
On update, only provided fields are changed. `applies_to` is set on creation and cannot be changed.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Usage rule ID (omit to create new) |
| `name` | string | No | The usage rule text, e.g. "Use only on white backgrounds" (required on create) |
| `applies_to` | string ("logo" | "color" | "typography") | No | What this rule applies to. Required on create; ignored on update. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `applies_to` | string | No |  |

### Manage brand kit visual example

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_VISUAL_EXAMPLE`

Create or update a visual example for a Brand Kit's Data Visualization section.
Omit `id` to create a new visual example; provide `id` to update an existing one.
On update, only provided fields are changed.

Visual examples must be grouped under a Visual Use Case — pass `visual_use_case_id`
on create. Use `manage_brand_kit_visual_use_case` first if no use case exists yet.

To attach an image, either:
- Use `signed_id` with the token from create_brand_kit_direct_upload after direct upload (preferred).
- Use `file_url` with a publicly accessible URL to an image.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Visual example ID (omit to create new) |
| `title` | string | No | Title of the visual example, e.g. "Dashboard Overview" (required on create) |
| `file_url` | string | No | Publicly accessible URL to a PNG, JPG, SVG, GIF, or WebP image. Use signed_id instead if the file was uploaded via create_brand_kit_direct_upload. Pass null to leave the existing file unchanged. |
| `signed_id` | string | No | Signed blob ID returned by create_brand_kit_direct_upload after a direct upload. Preferred over file_url when the user has a local file. Pass null to leave the existing file unchanged. |
| `sample_url` | string | No | Optional URL to a live sample. Pass null to clear. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `usage_instructions` | string | No | Instructions for agents on when and how to use this visual example. Pass null to clear. |
| `visual_use_case_id` | integer | No | ID of the Visual Use Case to group this visual example under. Required on create. Use `manage_brand_kit_visual_use_case` to create or list visual use cases on this brand kit. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `title` | string | No |  |
| `created` | boolean | No |  |
| `file_url` | string | No |  |
| `sample_url` | string | No |  |
| `usage_instructions` | string | No |  |
| `visual_use_case_id` | integer | No |  |

### Manage brand kit visual use case

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_VISUAL_USE_CASE`

Create or update a Visual Use Case for a Brand Kit.
A Visual Use Case is a named grouping of visual examples that share a common set of instructions
for when and how to apply them (e.g., "Hero sections", "Social posts", "Email headers").

Omit `id` to create a new visual use case; provide `id` to update an existing one.
On update, only provided fields are changed.

This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Visual use case ID (omit to create new) |
| `name` | string | No | Name of the visual use case, e.g. "Hero sections" (required on create) |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `instructions` | string | No | Free-form instructions for when and how to apply the visual examples grouped under this visual use case. Pass null to clear. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `created` | boolean | No |  |
| `instructions` | string | No |  |

### Manage brand kit writing rule

**Slug:** `AIROPS_MCP_MANAGE_BRAND_KIT_WRITING_RULE`

Create or update a writing rule for a Brand Kit.
Omit `id` to create a new rule; provide `id` to update an existing one.

Scoping: A rule can optionally be scoped to exactly one dimension — a single
content type, audience, OR region. Provide at most one of `content_type_id`,
`audience_id`, or `region_id` on create. They are mutually exclusive.
A rule with no scoping is global (applies everywhere).

Scoping is immutable: it is set on create only.
On update, only `text` can be changed.
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be created or changed and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Writing rule ID (omit to create new) |
| `text` | string | No | Writing rule text (required on create) |
| `region_id` | integer | No | Region ID to scope this rule to (mutually exclusive with content_type_id and audience_id). Only on create. |
| `audience_id` | integer | No | Audience ID to scope this rule to (mutually exclusive with content_type_id and region_id). Only on create. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |
| `content_type_id` | integer | No | Content type ID to scope this rule to (mutually exclusive with audience_id and region_id). Only on create. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `text` | string | No |  |
| `created` | boolean | No |  |
| `is_global` | boolean | No |  |
| `region_id` | string | No |  |
| `audience_id` | string | No |  |
| `content_type_id` | string | No |  |

### Publish brand kit

**Slug:** `AIROPS_MCP_PUBLISH_BRAND_KIT`

Publish a Brand Kit's current draft so changes become active.
This promotes the current draft to active and creates a fresh draft from it.

IMPORTANT: Always confirm with the user before publishing.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_kit_id` | integer | Yes | The Brand Kit ID to publish |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `message` | string | No |  |
| `draft_version_id` | integer | No |  |
| `active_version_id` | integer | No |  |

### Query analytics

**Slug:** `AIROPS_MCP_QUERY_ANALYTICS`

Query analytics data for a Brand Kit with flexible metrics, dimensions, and filters. Today's data is still being processed — always use yesterday (2026-09-09) or earlier as end_date . "Use this to set appropriate date ranges when users ask about recent metrics without specifying dates. IMPORTANT: USE chart_image_url WHEN YOU NEED TO EMBED A CHART IN NOTION/GOOGLE DOCS, ETC."

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | string | No | Filter by tags. A bare array of tag IDs matches ANY (IN). An object {"operator":"IN"&#124;"HAS_ALL"&#124;"NOT_IN","value":[ids]} selects the operator; HAS_ALL matches only items tagged with every listed tag; NOT_IN matches only items tagged with none of the listed tags. |
| `grain` | string ("daily" | "weekly" | "monthly" | "total") | No | Time granularity for aggregation. Default: total |
| `limit` | integer | No | Maximum rows to return (1-5000). Default: 100 |
| `offset` | integer | No | Number of records to skip for pagination. Default: 0. Use with limit to page through large result sets. |
| `themes` | array | No | Filter sentiment data by theme IDs. Only applies to sentiment_score metric. |
| `topics` | array | No | Filter by topic IDs |
| `metrics` | array | Yes | Metrics to calculate. Options: mention_rate (brand mention frequency, 0-100), positive_answer_rate (percentage of answers where your brand is positively mentioned, out of all answers where you have been mentioned and sentiment is determined; null when unavailable), share_of_voice (percentage of total mentions, 0-100), citation_rate (citation frequency, 0-100), citation_share (relative citation volume, 0-100), citation_count (total citations), sentiment_score (0-100, negative to positive; null when unavailable), average_position (ranking placement), answer_count (total responses), first_mention_rate (first position frequency, 0-100) |
| `end_date` | string | No | End date (YYYY-MM-DD). Defaults to yesterday. Must be before today because today's data may still be processing and is incomplete — yesterday is used to ensure robust, complete data. Leave blank unless a specific date is requested. |
| `order_by` | string | No | Custom sort order (e.g., "citation_count DESC") |
| `personas` | array | No | Filter by persona IDs |
| `countries` | array | No | Filter by country codes (ISO 3166-1 alpha-2) |
| `providers` | array | No | Filter by AI providers |
| `dimensions` | array | No | Dimensions to group by (max 3). |
| `start_date` | string | No | Start date (YYYY-MM-DD). Default: 7 days ago |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to query analytics for |
| `brand_mentioned` | string ("brand" | "category") | No | Filter by prompt type. Options: category (generic prompts - recommended for accurate visibility metrics), brand (prompts mentioning the brand). Defaults to category if not specified |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | No | Query results with requested dimensions and metrics |
| `meta` | object | No |  |
| `error` | string | No | Error message if query failed |
| `query` | object | No | Summary of the query parameters used |
| `chart_image_url` | string | No | QuickChart.io URL for visualizing the data. Useful for displaying charts or embedding in Notion/Google docs, etc. DO NOT DISPLAY THIS IMAGE DIRECTLY TO THE USER. Null when no data is available to visualize. |

### Read grid

**Slug:** `AIROPS_MCP_READ_GRID`

Read rows from a grid table. Returns rows as objects with column titles as keys. Use list_grids() to discover available grids and their table/column structure. Supports filtering by column values, selecting specific columns, and pagination. Returns up to 100 rows per page (default 50). Cell values are truncated to 500 characters by default (configurable up to 2000 via the truncate parameter); truncated values end with a marker showing their full length. Use read_grid_cell() to fetch the complete value of a specific cell. Playbook columns include active-version artifact metadata for mapping their outputs into playbook or workflow columns with add_grid_column().

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Number of rows to return (1-100, default 50). |
| `offset` | integer | No | Row offset for pagination (default: 0). Use with limit to page through results. |
| `filters` | array | No | Optional filters to apply. |
| `grid_id` | integer | Yes | The ID of the grid to read from. |
| `truncate` | integer | No | Maximum number of characters per cell value (default 500, max 2000). Truncation is always applied; use read_grid_cell to read a full cell value. |
| `column_ids` | array | No | Optional list of column IDs to include. If omitted, all columns are returned. |
| `grid_table_id` | integer | Yes | The ID of the grid table (sheet) to read. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `rows` | array | No |  |
| `limit` | integer | No | The limit used for this page |
| `offset` | integer | No | The offset used for this page |
| `columns` | array | No | Columns included in the result |
| `grid_id` | integer | No |  |
| `has_more` | boolean | No | Whether more rows exist beyond this page |
| `grid_name` | string | No |  |
| `row_count` | integer | No |  |
| `total_rows` | integer | No | Total number of rows matching the filters |
| `grid_table_id` | integer | No |  |

### Read grid cell

**Slug:** `AIROPS_MCP_READ_GRID_CELL`

Read the full value of a single grid cell. read_grid() truncates cell values; use this tool when you need the complete content of one cell (e.g. a full article, brief, or HTML payload). Identify the cell via the row __id and column id returned by read_grid().

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `row_id` | integer | Yes | The row ID (the __id field returned by read_grid). |
| `grid_id` | integer | Yes | The ID of the grid. |
| `column_id` | integer | Yes | The grid column ID (from the columns array returned by read_grid). |
| `grid_table_id` | integer | Yes | The ID of the grid table (sheet). |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `value` | string | No | The full cell value |
| `row_id` | integer | No |  |
| `grid_id` | integer | No |  |
| `column_id` | integer | No |  |
| `value_type` | string ("text" | "json") | No | json means the cell holds structured data serialized as JSON |
| `column_title` | string | No |  |
| `grid_table_id` | integer | No |  |

### Reject opportunity

**Slug:** `AIROPS_MCP_REJECT_OPPORTUNITY`

Reject pending opportunities for a campaign. For v2 campaigns, pass opportunity_ids; opportunity item selection and rejection reasons are v1-only. Before calling this tool, summarize the opportunities or opportunity items that will be rejected and get explicit user confirmation.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `play_id` | integer | Yes | Campaign ID. |
| `opportunity_ids` | array | No | Opportunity IDs to reject. Required for v2; for v1 this rejects every pending item in each opportunity. |
| `rejection_reason` | string | No | V1-only optional reason to store on rejected opportunity items. |
| `opportunity_item_ids` | array | No | V1-only opportunity item IDs to reject. Use this for item-level rejection. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `data` | array | No |  |
| `meta` | object | No |  |

### Search knowledge base

**Slug:** `AIROPS_MCP_SEARCH_KNOWLEDGE_BASE`

Search a Knowledge Base for relevant content using semantic similarity.
Use list_knowledge_bases() first to find available Knowledge Bases and their IDs.
Only Knowledge Bases with status "ready" can be searched.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | The search query. Use natural language to describe what you are looking for. |
| `top_k` | integer | No | Number of results to return (1-20, default 5). |
| `knowledge_base_id` | integer | Yes | The ID of the Knowledge Base to search. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | No |  |
| `results` | array | No |  |
| `result_count` | integer | No |  |
| `knowledge_base_id` | integer | No |  |
| `knowledge_base_name` | string | No |  |

### Suggest brand kit edits

**Slug:** `AIROPS_MCP_SUGGEST_BRAND_KIT_EDITS`

Suggest edits to a Brand Kit's fields without applying them.
Returns a comparison of current vs suggested values for user review.
The user will see a UI to accept or reject each suggestion before changes are applied.

Only include fields you want to suggest changes for.
Call this tool once per entity (e.g. once per audience, once per content type).

Supported entity types and their fields:
- brand_kit: brand_name, brand_url, brand_about, writing_persona, writing_tone
- audience: name, description
- content_type: name, template_outline, cta_text, cta_url, header_case, header_case_custom_value
- product_line: name, details, positioning, ideal_customer_profile, url
- region: name, description, icon_name (flag code in format flag-{iso2}, e.g. flag-us, flag-gb, flag-br)
- writing_rule: text, region_id (create only), content_type_id (create only), audience_id (create only)
- competitor: name, domain, details, product_line_ids
- content_sample: url (create only), content_type_id (create only), content (update only), audience_ids, region_ids
- custom_variable: name, value
- logo_variant: name, background_color, usage_instructions (to upload a logo image use manage_brand_kit_logo_variant directly with file_url)
- logo_size: name, width, height, usage_instructions
- font: name, usage_instructions, google_font_link, file_url (publicly accessible URL to a font file)
- type_size: name, weight, size, line_height, usage_instructions
- palette: name

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No | Record ID for existing association records. Omit to suggest creating a new record.               Not used for brand_kit entity type. |
| `title` | string | No | Optional heading to display in the review UI |
| `entity_type` | string ("brand_kit" | "audience" | "content_type" | "product_line" | "region" | "writing_rule" | "competitor" | "content_sample" | "custom_variable" | "logo_variant" | "logo_size" | "font" | "type_size" | "palette") | No | Which entity to suggest edits for. Defaults to brand_kit. |
| `suggestions` | object | Yes | Field name to suggested value pairs. Valid fields depend on entity_type. Use arrays for multi_select fields (e.g. product_line_ids). |
| `brand_kit_id` | integer | Yes | The Brand Kit ID |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | No |  |
| `fields` | object | No |  |
| `entity_type` | string | No |  |
| `update_tool` | string | No |  |
| `entity_label` | string | No |  |
| `update_params` | object | No |  |

### Track aeo page content update

**Slug:** `AIROPS_MCP_TRACK_AEO_PAGE_CONTENT_UPDATE`

Track a page content update (publish/refresh) to correlate future analytics with content changes.

Call this tool after a page is published or refreshed through a CMS (e.g. WordPress,
Webflow, Contentful) or any MCP server that modifies page content. This lets AirOps
correlate page performance metrics — AI visibility, citations, mentions — with
specific content updates over time.

The URL must match a domain configured in one of the workspace's Brand Kits.
If no matching Brand Kit domain is found, the content update cannot be created.
If the URL matches multiple Brand Kits (for example regional variants sharing a domain),
a content update is created for each, routed by the most specific brand_url path
(e.g. "https://example.com/es/post" maps to the Brand Kit whose brand_url is "https://example.com/es/").

IMPORTANT: Always show the user exactly which fields you plan to use (url, type, source)
and ask for their confirmation before calling this tool. User confirmation is mandatory.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | The page URL to track. The URL must belong to a brand_url or domain configured in one of the workspace's Brand Kits (use get_insights_settings to see domains). For example, if the Brand Kit domain is "example.com", URLs like "https://example.com/blog/post" will match. |
| `type` | string ("page_published" | "page_refreshed") | Yes | The type of content update to track |
| `workspace_id` | integer | Yes | The workspace ID to create the content update in |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No | Success message |
| `content_updates` | array | No | The created content updates, one per matching Brand Kit |

### Update aeo prompt assignments

**Slug:** `AIROPS_MCP_UPDATE_AEO_PROMPT_ASSIGNMENTS`

Update the country, persona, and platform assignments of one or more existing AEO
prompts on a Brand Kit. Writes to the brand kit's draft session ONLY — changes do
NOT take effect until you call `commit_aeo_prompt_assignments`.

Semantics:
- For each entry in `prompts`, a non-empty array REPLACES that prompt's current
  draft assignments for the given dimension.
- An empty array CLEARS that dimension on the prompt.
- Omitting a dimension or passing null leaves that dimension UNCHANGED.
- At most 100 prompts can be updated per call.

Workflow:
1. Call `update_aeo_prompt_assignments` to stage your changes in the draft.
2. Inspect `limit_exceeded` in the response. If true, the workspace would exceed
   its estimated-answers quota on commit. Adjust by calling
   `update_aeo_prompt_assignments` again, or call
   `discard_aeo_prompt_assignments` to abandon.
3. When ready, call `commit_aeo_prompt_assignments` to apply the draft to live.
   Always confirm with the user before committing.

IMPORTANT — shared draft with the UI:
- There is one draft per brand kit, shared between MCP and the human-facing UI.
- If the user has unsaved UI edits, your edits accumulate in the same draft. A
  subsequent commit will publish both sets of edits together; a discard will
  destroy both. Surface this to the user before committing or discarding.

To discover prompt IDs and current assignments, use `list_aeo_prompts`.
To inspect current draft state without modifying it, use
`get_aeo_prompt_assignments_status`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `prompts` | array | Yes | Per-prompt assignment updates. Empty arrays clear a dimension; omitted or null dimensions remain unchanged. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID owning the prompts to update. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | No |  |
| `draft_id` | integer | No | The draft session ID. |
| `has_changes` | boolean | No | Whether the draft has uncommitted changes relative to live. |
| `max_answers` | integer | No | The workspace's effective answers limit. |
| `brand_kit_id` | integer | No |  |
| `limit_exceeded` | boolean | No | Whether committing the draft now would exceed the workspace limit. |
| `updated_prompts` | array | No | Echo of the updated prompts with computed est_answers_mo. |
| `draft_estimated_answers` | integer | No | Workspace-wide estimated answers/month if the draft were committed now. |

### Update aeo tag

**Slug:** `AIROPS_MCP_UPDATE_AEO_TAG`

Update an existing AEO tag's name and/or color on a Brand Kit.

Behavior:
- At least one of `name` or `color` must be provided.
- If `name` is provided, it must remain unique within the Brand Kit
  (case-insensitive).
- Valid colors: light_grey, grey, green, teal, blue, purple, lilac, pink, red, coral, orange.
- Existing taggings on prompts are preserved — only the tag's own attributes change.

IMPORTANT: Always show the user the tag (current name) and the change you plan to
apply, and get explicit confirmation before calling. You can verify the change by
calling `list_tags`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | Optional. New name for the tag. Must be unique within the Brand Kit (case-insensitive). |
| `color` | string ("light_grey" | "grey" | "green" | "teal" | "blue" | "purple" | "lilac" | "pink" | "red" | "coral" | "orange") | No | Optional. New color from the platform palette. |
| `tag_id` | integer | Yes | The tag ID to update. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the tag. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `color` | string | No |  |
| `message` | string | No |  |
| `updated_at` | string | No | Update timestamp (ISO 8601) |
| `brand_kit_id` | integer | No |  |

### Update brand kit

**Slug:** `AIROPS_MCP_UPDATE_BRAND_KIT`

Update a Brand Kit's base fields. Only provided fields are changed.
Use get_brand_kit first to see current values.

Updatable fields include brand identity (name, URL, about) and writing style
(persona, tone).
This tool edits the Brand Kit draft version only; it does not change the active (live) version.

IMPORTANT: Always show the user exactly which fields will be changed
and ask for confirmation before calling this tool.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brand_url` | string | No | URL of the brand website |
| `brand_name` | string | No | Name of the brand |
| `brand_about` | string | No | Description/overview of the brand |
| `brand_kit_id` | integer | Yes | The Brand Kit ID to update |
| `writing_tone` | string | No | The tone of voice for brand content |
| `writing_persona` | string | No | The persona/voice used in brand writing |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `message` | string | No |  |
| `updated_fields` | array | No |  |

### Update grid column

**Slug:** `AIROPS_MCP_UPDATE_GRID_COLUMN`

Update an existing grid column's title, data type, or input mappings. Cannot reassign a playbook or workflow column to a different playbook or workflow, and cannot reposition a column. Passing inputs replaces the entire input mapping, not just the keys given, so include every input you want to keep, not only the ones changing. Returns the updated column's ID, title, data type, and position.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | No | The new column title. |
| `inputs` | object | No | Playbook or workflow input mappings keyed by active-version input name. Playbook mapping types are column, value, and brandkit. Workflow mapping types are column, value, brandkit, and knowledge_base. |
| `grid_id` | integer | Yes | The ID of the grid. |
| `data_type` | string ("text" | "number" | "datetime" | "markdown" | "url" | "html" | "image_from_url" | "json" | "single_select" | "brand_kit" | "file" | "playbook" | "app_execution") | No | The new data type for the column. |
| `grid_table_id` | integer | Yes | The ID of the grid table (sheet). |
| `grid_column_id` | integer | Yes | The ID of the grid column to update. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `column` | object | No |  |
| `grid_id` | integer | No |  |
| `grid_table_id` | integer | No |  |

### Update topic

**Slug:** `AIROPS_MCP_UPDATE_TOPIC`

Update an existing AEO topic's name and/or color on a Brand Kit.

Behavior:
- At least one of `name` or `color` must be provided.
- If `name` is provided, it must remain unique within the Brand Kit.
- Valid colors: light_grey, grey, green, teal, blue, purple, lilac, pink, red, coral, orange.
- Existing prompt assignments are preserved. This tool cannot move a topic to another
  Brand Kit.

IMPORTANT: Always show the user the topic (current name) and the change you plan to
apply, and get explicit confirmation before calling. You can verify the change by
calling `list_topics`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | Optional. New name for the topic. Must be unique within the Brand Kit. |
| `color` | string ("light_grey" | "grey" | "green" | "teal" | "blue" | "purple" | "lilac" | "pink" | "red" | "coral" | "orange") | No | Optional. New color from the platform palette. |
| `topic_id` | integer | Yes | The topic ID to update. |
| `brand_kit_id` | integer | Yes | The Brand Kit ID that owns the topic. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | integer | No |  |
| `name` | string | No |  |
| `color` | string | No |  |
| `message` | string | No |  |
| `updated_at` | string | No | Update timestamp (ISO 8601) |
| `brand_kit_id` | integer | No |  |

### Write grid

**Slug:** `AIROPS_MCP_WRITE_GRID`

Create or update rows in a grid table. When mode is 'create', rows are added as new rows with column titles as keys. When mode is 'update', each row must include a '__id' key with the row ID to update. To update a single cell, pass a single-element array with the row ID and the column to change. Maximum 1000 rows per operation.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mode` | string ("create" | "update") | Yes | 'create' to add new rows, 'update' to modify existing rows (requires __id in each row). |
| `rows` | array | Yes | Array of row objects. Keys are column titles, values are cell values. For update mode, include __id with the row ID. |
| `grid_id` | integer | Yes | The ID of the grid to write to. |
| `grid_table_id` | integer | Yes | The ID of the grid table (sheet) to write to. |

#### Output

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mode` | string | No |  |
| `grid_id` | integer | No |  |
| `warning` | string | No |  |
| `rows_created` | string | No |  |
| `rows_updated` | string | No |  |
| `cells_updated` | string | No |  |
| `grid_table_id` | integer | No |  |
