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

# Peec AI MCP

Peec AI MCP lets agents analyze authorized AI-search visibility, competitors, citations, sources, sentiment, topics, and trends, and manage supported project configuration with confirmation.

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

## Tools

### Archive prompts

**Slug:** `PEEC_AI_MCP_ARCHIVE_PROMPTS`

Archive up to 50 prompts (sets is_archived = true) so they stop running while keeping their chats and history intact. Prefer this over delete_prompts when the data should be retained. Returns a count of archived prompts plus any skipped (not found) or rejected items. Confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `prompt_ids` | array | Yes | Up to 50 prompt IDs to archive (target 40 or fewer per call), e.g. ["pr_a1b2c3"]. |

### Assign domain classification

**Slug:** `PEEC_AI_MCP_ASSIGN_DOMAIN_CLASSIFICATION`

Assign a built-in classification (Corporate, Competitor, Editorial, Institutional, Other, Reference, UGC, You, Related) or the name of a custom domain classification to a domain. Overrides any heuristic classification. The override applies to the whole root domain, including its subdomains. If the name you pass matches an existing custom classification, the custom one is used — to force a built-in with the same name, pass its enum value (e.g. "OTHER"). Confirm with the user before calling. Returns { success: true } once the override is saved.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `domain` | string | Yes | Domain to classify (any host or root domain — the override covers the whole root domain, including its subdomains) |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `classification` | string | Yes | Built-in classification display name or the name of a custom domain classification |

### Assign url classification

**Slug:** `PEEC_AI_MCP_ASSIGN_URL_CLASSIFICATION`

Assign a built-in classification (Homepage, Category Page, Product Page, Listicle, Comparison, Profile, Alternative, Discussion, How-To Guide, Article, Other) or the name of a custom URL classification to a URL. Overrides any heuristic classification. The override applies to the URL in its normalized form, so it matches the same page regardless of trailing slashes, query-parameter order, or http/https scheme. If the name you pass matches an existing custom classification, the custom one is used — to force a built-in with the same name, pass its enum value (e.g. "OTHER"). Confirm with the user before calling. Returns { success: true } once the override is saved.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | URL to classify |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `classification` | string | Yes | Built-in classification display name or the name of a custom URL classification |

### Create brands

**Slug:** `PEEC_AI_MCP_CREATE_BRANDS`

Create up to 50 brands (competitors or own) in a project in one call. Returns the created brands with their new ids, plus any skipped items. After creation, call list_brands to confirm the requested brands exist before reporting success, even when they appear in this tool's created list. Duplicates are matched case-insensitively on name. A project can track at most 200 brands total (own + competitors); a call that would exceed this fails.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brands` | array | Yes | Up to 50 brands to create (target 40 or fewer per call), e.g. [{"name":"Acme","domains":["acme.com"]}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Create categories

**Slug:** `PEEC_AI_MCP_CREATE_CATEGORIES`

Create product categories. Each item needs a name and an optional parent_id — omit it for a top-level category, or pass another category's id (use list_categories to find it) to nest it underneath. A name must be unique among its siblings. Items apply in order.

Returns per-item results { created, rejected } — the batch never fails as a whole. Rejection reasons: parent_not_found, name_conflict.

Confirm with the user before calling — this mutates project data.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `categories` | array | Yes | Categories to create. Each needs a name (unique among its siblings) and an optional parent_id — omit for a top-level category, or pass another category id (from list_categories) to nest it. Applied in order. e.g. [{"name":"Running Shoes"},{"name":"Trail","parent_id":"3f2504e0-4f89-41d3-9a0c-0305e82c3301"}]. |
| `project_id` | string | Yes | The project ID |

### Create domain classification

**Slug:** `PEEC_AI_MCP_CREATE_DOMAIN_CLASSIFICATION`

Define a new custom domain classification on a project. This creates the classification entity — it does not assign it to any domain (use assign_domain_classification for that). Confirm with the user before calling. Returns the created classification as { name, color }.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Classification name (must be unique per project) |
| `color` | string ("orange" | "red" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "gray") | No | Display color for the classification |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Create global brand

**Slug:** `PEEC_AI_MCP_CREATE_GLOBAL_BRAND`

Create a brand and return its global_brand_id. Use this when list_global_brands finds no match for a brand you need, or when create_products rejects an item with brand_not_found.

Returns { id, name }. The returned name is the brand's canonical name and may differ slightly from the name you sent.

This is separate from create_brands, which creates brands a project tracks for AI-visibility reporting; the two have different ids and are not interchangeable.

Confirm with the user before calling — this creates a brand.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | The brand name. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Create products

**Slug:** `PEEC_AI_MCP_CREATE_PRODUCTS`

Create catalog products (source=CATALOG) in a project — the brand's own claimed/uploaded products, as opposed to LLM products that Peec auto-detects in shopping answers. Each item needs a global_brand_id (use list_global_brands to find it by name, or create_global_brand if the brand doesn't exist yet) and a name unique within that brand; description, image_url, per-currency price_override, and category_ids (use list_categories to find category ids) are optional. When products need new categories, build the category tree first with create_categories (parent_id nests, omit for top-level), then pass the returned category_ids here.

Returns per-item results { created, rejected } — the batch never fails as a whole. Rejection reasons: name_conflict (name already used in the brand), brand_not_found, category_not_found.

Confirm with the user before calling — this mutates project data.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `products` | array | Yes | Products to create (1–1000). Each needs a global_brand_id (from list_global_brands) and a name unique within that brand; description, image_url, per-currency price_override, and category_ids (from list_categories) are optional. e.g. [{"global_brand_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Air Zoom Pegasus 41"}]. |
| `project_id` | string | Yes | The project ID |

### Create prompts

**Slug:** `PEEC_AI_MCP_CREATE_PROMPTS`

Create up to 50 prompts in a project in one call. Returns the created prompts with their new ids, plus any skipped/rejected items. Every prompt needs a topic: pick the best-fitting existing topic, or create one with create_topics first. Accepts existing topic_id and tag_ids only — this tool does not auto-create topics or tags. Confirm with the user before calling — this mutates project data and may consume plan credits.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `prompts` | array | Yes | Up to 50 prompts (target 40 or fewer per call). Each must use existing topic_id and tag_ids; unknown IDs are returned in `rejected`. e.g. [{"text":"best crm for startups","country_code":"US","topic_id":"to_a1b2c3","tag_ids":["tg_a1b2c3"]}]. |
| `project_id` | string | Yes | The project ID |

### Create tags

**Slug:** `PEEC_AI_MCP_CREATE_TAGS`

Create up to 50 tags in a project in one call. Each tag may specify a group (grouped tags share the group's color, so their color field is ignored). Returns the created tags with their new ids, plus any skipped items. Duplicates are matched case-insensitively on name.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | array | Yes | Up to 50 tags to create (target 40 or fewer per call), e.g. [{"name":"Enterprise","group":"Segment"}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Create topics

**Slug:** `PEEC_AI_MCP_CREATE_TOPICS`

Create up to 50 topics in a project in one call. Topics group related prompts. Returns the created topics with their new ids, plus any skipped/rejected items. Duplicates are matched case-insensitively on name. Items beyond the project's topic limit land in `rejected`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topics` | array | Yes | Up to 50 topics to create (target 40 or fewer per call), e.g. [{"name":"CRM software","country_code":"US"}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Create url classification

**Slug:** `PEEC_AI_MCP_CREATE_URL_CLASSIFICATION`

Define a new custom URL classification on a project. This creates the classification entity — it does not assign it to any URL (use assign_url_classification for that). Returns the created classification's name, which is what you pass to assign_url_classification. Confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Classification name (must be unique per project) |
| `color` | string ("orange" | "red" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "gray") | No | Display color for the classification |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Delete brands

**Slug:** `PEEC_AI_MCP_DELETE_BRANDS`

Delete up to 50 brands in a project. Returns a count of deleted brands plus any skipped items (successful items are not echoed back). This is destructive — always confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `brand_ids` | array | Yes | Up to 50 brand IDs to delete (target 40 or fewer per call), e.g. ["br_a1b2c3"]. |
| `project_id` | string | Yes | The project ID |

### Delete categories

**Slug:** `PEEC_AI_MCP_DELETE_CATEGORIES`

Delete categories by id (use list_categories to resolve ids). A deleted category's child categories and products move up to its parent (a top-level delete sends children to the top level and its products to Uncategorized).

Returns per-item results { deleted, rejected } — the batch never fails as a whole. Rejection reasons: not_found, name_conflict (a child being moved up would clash with an existing sibling name).

Confirm with the user before calling — this mutates project data.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `category_ids` | array | Yes | Category ids to delete (from list_categories), e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |

### Delete domain classification

**Slug:** `PEEC_AI_MCP_DELETE_DOMAIN_CLASSIFICATION`

Permanently delete a custom domain classification entity. Cascades through the override table — any domains currently assigned this classification fall back to their heuristic classification. To only clear a single domain's assignment, use unassign_domain_classification instead. This is destructive — always confirm with the user before calling. Returns { success: true } once deleted.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Name of the custom domain classification to delete |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Delete products

**Slug:** `PEEC_AI_MCP_DELETE_PRODUCTS`

Delete products by id (use list_products to resolve ids). Returns per-item results { deleted, skipped } — ids not present in the project are skipped (not_found).

Only delete source=CATALOG products. Deleting LLM-source products is futile — the ingestion pipeline re-creates an LLM product whenever a shopping answer mentions it again.

Confirm with the user before calling — this mutates project data.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `product_ids` | array | Yes | Product ids to delete (1–1000; from list_products), e.g. ["b2c3d4e5-f6a7-8901-bcde-f23456789012"]. |

### Delete prompts

**Slug:** `PEEC_AI_MCP_DELETE_PROMPTS`

Delete up to 50 prompts in a project. Deletions run asynchronously — returns a count of queued deletions plus any skipped (not found / already deleted) or rejected items (queued items are not echoed back). This is destructive — always confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `prompt_ids` | array | Yes | Up to 50 prompt IDs to delete (target 40 or fewer per call), e.g. ["pr_a1b2c3"]. |

### Delete tag groups

**Slug:** `PEEC_AI_MCP_DELETE_TAG_GROUPS`

Delete up to 50 user-defined tag groups. By default each group's tags are kept and simply ungrouped; set delete_tags=true on a group to delete its tags and remove them from every prompt. Returns each group with its affected tag count, plus any skipped (not found) or rejected items. This can be destructive — confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `groups` | array | Yes | Up to 50 tag groups to delete (target 40 or fewer per call), e.g. [{"group":"Segment","delete_tags":false}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Delete tags

**Slug:** `PEEC_AI_MCP_DELETE_TAGS`

Delete up to 50 tags in a project. Removes tag associations from prompts. Returns a count of deleted tags plus any skipped items (successful items are not echoed back). This is destructive — always confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | Yes | Up to 50 tag IDs to delete (target 40 or fewer per call), e.g. ["tg_a1b2c3"]. |
| `project_id` | string | Yes | The project ID |

### Delete topics

**Slug:** `PEEC_AI_MCP_DELETE_TOPICS`

Delete up to 50 topics in a project. Detaches associated prompts (prompts are kept) and deletes any prompt suggestions linked to the topics. Returns a count of deleted topics plus any skipped items (successful items are not echoed back). This is destructive — always confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `topic_ids` | array | Yes | Up to 50 topic IDs to delete (target 40 or fewer per call), e.g. ["to_a1b2c3"]. |
| `project_id` | string | Yes | The project ID |

### Delete url classification

**Slug:** `PEEC_AI_MCP_DELETE_URL_CLASSIFICATION`

Permanently delete a custom URL classification entity. Any URLs currently assigned this classification fall back to their heuristic (default) classification. To only clear a single URL's assignment, use unassign_url_classification instead. This is destructive — always confirm with the user before calling. Returns { success: true } once deleted.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Name of the custom URL classification to delete |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Edit brand perception attribute clusters

**Slug:** `PEEC_AI_MCP_EDIT_BRAND_PERCEPTION_ATTRIBUTE_CLUSTERS`

Rename, delete and reassign brand-perception attribute clusters — the same edits the Manage attributes screen makes. Resolve every cluster_id and attribute with list_brand_perception_attribute_clusters first; both must be copied verbatim.

Pass any combination of renames, delete_cluster_ids and reassignments; at least one is required. The whole call is one transaction — if any edit is rejected nothing is applied — and edits run in the order reassign, rename, delete however they are listed. So merging two clusters is one call: reassign every attribute out of the losing cluster and delete it. A cluster must be empty when it is deleted.

Scores re-aggregate on the next read, so the change is immediate and needs no new scraping run. It rewrites a taxonomy the whole project shares and there is no undo: show the user the exact edits and get their confirmation before calling.

Name each attribute and each cluster at most once per call; repeating one is rejected because the two edits would race. Returns the counts applied. Errors: invalid (a repeated target), not found (unknown cluster or attribute), conflict (a new label collides with another cluster, or a deleted cluster still holds attributes).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `renames` | array | No | Clusters to relabel, e.g. [{"cluster_id":"0199a4c1-9f3e-7c21-9f0f-2c0a5d3b1e42","name":"Racing Heritage"}]. The cluster keeps its id and its history, so scores blend across the rename. |
| `project_id` | string | Yes | The project ID |
| `reassignments` | array | No | Attributes to move between clusters, e.g. [{"attribute":"hand-built","cluster_id":"0199a4c1-9f3e-7c21-9f0f-2c0a5d3b1e42"}]. Works for attributes listed under unassigned too. |
| `delete_cluster_ids` | array | No | Clusters to remove. Each must hold no attributes by the time it is deleted — reassign them out in this same call. |

### Finalize prompt suggestions

**Slug:** `PEEC_AI_MCP_FINALIZE_PROMPT_SUGGESTIONS`

Resolve prompt suggestions listed by list_prompt_suggestions, in one pass over a reviewed set: ids in accepted_ids become tracked prompts and promote their topics into the project, ids in rejected_ids are deleted from the review list. Pass either or both, up to 50 ids in total. Rejecting never touches tracked prompts, since a suggestion has produced none until accepted; a topic left with no suggestions is cleared with them. The accepted set is checked against the project's remaining prompt allowance as a whole and refused as a batch when it does not fit — trim it and retry rather than splitting across calls, and note that a refused accept also leaves the rejections unapplied. Returns a count per outcome, any ids that matched no suggestion, and any allowance warning. Confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `accepted_ids` | array | No | Suggestion IDs to turn into tracked prompts, e.g. ["pr_a1b2c3"]. Target 40 or fewer ids per call across both lists. |
| `rejected_ids` | array | No | Suggestion IDs to delete from the review list, e.g. ["pr_d4e5f6"]. |

### Generate prompt suggestions

**Slug:** `PEEC_AI_MCP_GENERATE_PROMPT_SUGGESTIONS`

Generate prompt suggestions for a project — the same generation Prompt discovery runs in the app. Generation runs in the background: the call returns {queued: true, generation_id}, which get_prompt_suggestion_generation reads until the run succeeds or fails. List successful results with list_prompt_suggestions, then accept or reject them with finalize_prompt_suggestions. topic_id, prompt_suggestion_id and segment each narrow the run and are mutually exclusive; with none of them, every topic awaiting review is generated for again, replacing its current suggestions — pass a scope unless the user asked for a full refresh. Requires the project's prompt_builder.distribution (branding/intent split), set via set_project_profile. Markets are optional: without them the project's own country and language are used. Spends one of the project's hourly prompt generations, so never call it in a loop. Confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `segment` | object | No | Adds suggestions for one slice of a topic, keeping the suggestions it already has. Any dimension left unset is filled from the project's target distribution and markets. |
| `topic_id` | string | No | Generates for this topic alone instead of every topic awaiting review, replacing the suggestions it already has. |
| `project_id` | string | Yes | The project ID |
| `prompt_suggestion_id` | string | No | Replaces this one suggestion, keeping its id, topic and market. |

### Generate topics

**Slug:** `PEEC_AI_MCP_GENERATE_TOPICS`

Compose candidate topics for the project from its brand profile — the same composition Prompt discovery runs in the app. Nothing is saved: review the returned names with the user and create the chosen ones with create_topics. Composition already accounts for the project's existing topics, so it proposes only what fills a gap and can return an empty list when the set is already well covered. Requires the project profile to name products and services (set via set_project_profile). Spends one of the project's hourly topic generations, so never call it in a loop. Returns {topics: [name, ...]}.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `topic_count` | integer | No | How many topics to compose. Defaults to the number the project's plan allows. |

### Get actions

**Slug:** `PEEC_AI_MCP_GET_ACTIONS`

Get Peec's opportunity-scored action recommendations for improving brand visibility in AI search engines. **Always call with `scope=overview` first** to see which slices have the biggest opportunity, then drill down into `owned`, `editorial`, `reference`, or `ugc` with the surfaced url_classification or domain.

## Required parameters (read before calling)

Every call must include:
- `project_id` — the project to analyze.
- `scope` — one of `overview` | `owned` | `editorial` | `reference` | `ugc`. **Start with `scope=overview`.**

Recommended:
- `start_date` and `end_date` (ISO YYYY-MM-DD). Optional — if omitted, defaults to the last 30 days (today − 30d to today). Prefer a 30-day window unless the user asks for a different one.

Per-scope extras (the call will fail without them):
- `scope=owned` → `url_classification` is **required** (e.g. "LISTICLE").
- `scope=editorial` → `url_classification` is **required** (e.g. "LISTICLE").
- `scope=reference` → `domain` is **required** (e.g. "wikipedia.org").
- `scope=ugc` → `domain` is **required** (e.g. "reddit.com", "youtube.com").
- `scope=overview` → no extras beyond the base params.

Use this tool whenever the user asks for recommendations, next steps, what to do, how to improve, "what actions should I take", or any "based on this data, what should I do?" question. Never invent SEO advice.

## Two-step workflow

**Step 1 — `scope=overview`:** returns opportunity rollups grouped by `action_group_type` × (`url_classification` | `domain`). These are *navigation metadata*, NOT the recommendations themselves. Use them to find which slices have the largest gap.

**Step 2 — drill down:** for each high-opportunity slice, call again with the matching scope (`owned` | `editorial` | `reference` | `ugc`) to get the actual textual recommendations (the `text` column, often with markdown links to examples or targets).

Mapping — how to turn an overview row into the follow-up call:
- `action_group_type=OWNED`, `url_classification=X` → call `scope=owned, url_classification=X`.
- `action_group_type=EDITORIAL`, `url_classification=X` → call `scope=editorial, url_classification=X`.
- `action_group_type=REFERENCE`, `domain=Y` → call `scope=reference, domain=Y`.
- `action_group_type=UGC`, `domain=Y` → call `scope=ugc, domain=Y`.

Worked example — overview returns a row `{action_group_type: "UGC", domain: "youtube.com", opportunity_score: 0.30, ...}`. Follow up with `scope=ugc, domain="youtube.com"` and you get rows like `{text: "Contact [AutoPedia](https://...). Ask them for a collaboration.", group_type: "UGC", domain: "youtube.com", opportunity_score: 3, ...}`.

## Response shape

Returns columnar JSON: `{columns, rows, rowCount}`. Each row is an array of values matching column order.

**`scope=overview` columns:**
- `action_group_type`: OWNED | EDITORIAL | REFERENCE | UGC
- `url_classification`: populated for OWNED / EDITORIAL rows (e.g. "LISTICLE", "ARTICLE", "COMPARISON"). `null` for REFERENCE / UGC.
- `domain`: populated for REFERENCE / UGC rows (e.g. "youtube.com", "wikipedia.org"). `null` for OWNED / EDITORIAL.
- `opportunity_score`: continuous. **Use this to sort and rank** — it's the reliable ordering signal.
- `relative_opportunity_score`: 1–3 tier (1=Low, 2=Medium, 3=High). **Use this to label** strength in prose. Too coarse to sort by.
- `gap_percentage`, `coverage_percentage`, `used_ratio`, `used_total`: supporting stats.

Exactly one of `url_classification` / `domain` is populated per overview row — that's the value to pass to the follow-up call.

**`scope=owned | editorial | reference | ugc` columns:**
- `text`: the recommendation string; may include markdown links.
- `group_type`: OWNED | EDITORIAL | REFERENCE | UGC.
- `url_classification`: e.g. "LISTICLE" (may be null).
- `domain`: e.g. "youtube.com" (may be null).
- `opportunity_score`: continuous — sort/rank by this.
- `relative_opportunity_score`: 1–3 tier — label strength with this (1=Low, 2=Medium, 3=High).

## Presenting results

After overview + drill-downs, pick the shape that fits:
- **Strong signal** (top slice's `opportunity_score` is clearly ahead AND its drill-down returned 2+ rows whose `text` contains a markdown link): one sentence of reasoning tied to the user's question (call out the biggest lever), then 2-3 named slices with 2-3 bullets pulled verbatim from the drill-down `text`.
- **Moderate signal**: compact list, one sentence per slice, bullets only where drill-down returned specific targets.
- **Low signal** (overview empty or top `opportunity_score` very low): single line, e.g., "Top opportunity: {slice} (Low). Low signal this period; prompts need a few more daily cycles to stabilize."

## Display conventions — never use raw enum keys in user-facing prose

**Group type** (`action_group_type` / `group_type`) — humanize (Title Case):
- `OWNED` → "Owned" (content on your own domains)
- `EDITORIAL` → "Editorial" (third-party editorial coverage — news, blogs, reviews)
- `REFERENCE` → "Reference" (reference sources like Wikipedia)
- `UGC` → "UGC" (user-generated content — Reddit, YouTube, forums; keep as acronym)
- `OTHER` → "Other"

**URL classification** (`url_classification`) — humanize to lowercase; pluralize naturally when the sentence calls for it:
- `HOMEPAGE` → "homepage"
- `CATEGORY_PAGE` → "category page"
- `PRODUCT_PAGE` → "product page"
- `LISTICLE` → "listicle"
- `COMPARISON` → "comparison page"
- `PROFILE` → "profile"
- `ALTERNATIVE` → "alternative"
- `DISCUSSION` → "discussion"
- `HOW_TO_GUIDE` → "how-to guide"
- `ARTICLE` → "article"
- `OTHER` → "other"

**Opportunity strength** — lead with a **Low / Medium / High** label derived from `relative_opportunity_score` (round to nearest integer, clamp to [1, 3]):
- 1 → "Low"
- 2 → "Medium"
- 3 → "High"

Sort and rank by `opportunity_score` (continuous). **Verbalize** strength with the Low/Medium/High tier above. The raw `opportunity_score` is optional supporting context in parens — never the headline number.

**Gap percentage** (`gap_percentage`, 0–1 ratio) — lead with a plain-language qualifier; the raw % can follow in parens when useful:
- ≥0.90 → "nearly all missing"
- 0.60–0.89 → "wide gap"
- 0.30–0.59 → "partial gap"
- <0.30 → "narrow gap"

**Example of the preferred style** (follow this phrasing):
> The biggest lever is Owned listicles — High, nearly all missing (100%). Build listicle-style pages on yourbrand.com that target "best X" queries.
>
> Secondary: YouTube UGC (Medium, wide gap), Reddit UGC (Medium, partial gap), Editorial listicles (Medium, nearly all missing). Full list: https://app.peec.ai/actions.

Close with one line: "Secondary opportunities: {slice} ({Low|Medium|High}), {slice} ({Low|Medium|High}). Full list: https://app.peec.ai/actions."

Use the drill-down `text` field as the source of truth. Never invent recommendations, targets, or names. Sort by `opportunity_score`; label strength via `relative_opportunity_score`.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `scope` | string ("overview" | "owned" | "editorial" | "reference" | "ugc") | Yes | REQUIRED. Start with 'overview' (opportunity rollups — navigation metadata, not recommendations), then drill down: 'owned' / 'editorial' (with url_classification) or 'reference' / 'ugc' (with domain). |
| `domain` | string | No | REQUIRED for scope=reference and scope=ugc. The domain to target (from an overview row's domain), e.g. 'wikipedia.org' or 'reddit.com'. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Optional filter: only include actions matching these tag ids (from list_tags), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | No | End date (ISO YYYY-MM-DD). Optional — defaults to today if omitted. Override only if the user asks for a different window. |
| `model_ids` | array | No | Optional filter: only include actions for these AI model ids (from list_models), e.g. ["gpt-4o"]. |
| `topic_ids` | array | No | Optional filter: only include actions for these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | No | Start date (ISO YYYY-MM-DD). Optional — defaults to 30 days ago if omitted. Override only if the user asks for a different window. |
| `country_codes` | array | No | Optional filter: ISO 3166-1 alpha-2 country codes (e.g. 'US', 'DE') |
| `model_channel_ids` | array | No | Optional filter: only include actions for these model channel ids (from list_model_channels), e.g. ["openai-0"]. |
| `url_classification` | string ("HOMEPAGE" | "CATEGORY_PAGE" | "PRODUCT_PAGE" | "LISTICLE" | "COMPARISON" | "PROFILE" | "ALTERNATIVE" | "DISCUSSION" | "HOW_TO_GUIDE" | "ARTICLE" | "OTHER") | No | REQUIRED for scope=owned and scope=editorial. The page type to target (from an overview row's url_classification), e.g. 'LISTICLE'. |

### Get agent visits

**Slug:** `PEEC_AI_MCP_GET_AGENT_VISITS`

Aggregate AI agent visit counts from access logs over a date range.

Without group_by: returns total visit count as a single {visits: N} row.
With group_by: returns one row per distinct value of the chosen dimension(s), sorted by visits descending.

group_by dimensions:
- bot_id: break down by bot (use list_bots to resolve names)
- response_status: break down by HTTP response status (200, 404, etc.)
- request_host: break down by hostname
- request_path: break down by URL path

Multiple group_by values produce a cross-dimensional breakdown (e.g. bot_id + response_status gives per-bot per-status counts).

Use bot_ids to filter to specific bots before grouping.

time_bucket: bucket results by time period (hour, day, week, month). Each row will include a `time_bucket` timestamp marking the start of the bucket. Combine with group_by to get e.g. per-bot per-day counts.

Returns: {data: [{bot_id?, response_status?, request_host?, request_path?, time_bucket?, visits}], totalCount}

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of grouped rows to return |
| `offset` | number | No | Number of grouped rows to skip |
| `bot_ids` | array | No | Filter to specific bot IDs. Use list_bots to get available IDs, e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `group_by` | array | No | Dimension(s) to group visit counts by. Omit for total visits only. Available: bot_id, response_status, request_host, request_path. e.g. ["bot_id","response_status"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `time_bucket` | string ("hour" | "day" | "week" | "month") | No | The time_bucket of the visit counts |

### Get brand perception attribute rankings

**Slug:** `PEEC_AI_MCP_GET_BRAND_PERCEPTION_ATTRIBUTE_RANKINGS`

How the project's own brand ranks against competitors for each brand-perception attribute. ranking is the own brand's position among every brand scored for the attribute, ordered by average prominence across all AI answers about it — lower is better, #1 means no brand is more prominent; answers the brand never surfaced in count against it, and null means it never surfaced at all. competitors lists the competing brand names AI associates with the attribute, most prominent first. Rows are sorted best-ranked first (unranked last). Data is aggregated across the brand-perception runs of one industry and one target market (no date range) — pass industry to pick the industry; the target market is always that industry's most recently completed one and cannot be picked. The response reports the scope it used as industry and target_market, and lists the project's other industries under industries when it has more than one — read a different one by passing its name back as industry, and say which industry the numbers describe whenever that field is present. Use get_brand_perception_attribute_sources with a returned name to see the sources feeding an attribute. Optionally filter to specific engine channels via model_channel_ids (see list_model_channels), or narrow by a search substring on the attribute name. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: name, ranking, competitors (array of brand names).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `search` | string | No | Case-insensitive substring filter on the attribute name. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `industry` | string | No | Only read the runs dispatched for this industry. Omit to read the project's default industry — the one with the most recent completed run. The industries field of the response names the alternatives, so a first call needs no argument. |
| `project_id` | string | Yes | The project ID |
| `model_channel_ids` | array | No | Only include answers from these AI engine channel ids (from list_model_channels). Omit to aggregate across all tracked channels. e.g. ["openai-0"]. |

### Get brand perception attribute sources

**Slug:** `PEEC_AI_MCP_GET_BRAND_PERCEPTION_ATTRIBUTE_SOURCES`

The sources feeding one brand-perception attribute: the URLs AI answers cited when describing the brand with this attribute. Pass the attribute name exactly as returned by get_brand_perception_brand_attributes or get_brand_perception_attribute_rankings; an unknown name returns an empty result. Data is aggregated across all brand-perception runs (no date range), and unlike the rankings and competitive-breakdown tools it is not scoped to one industry — there is no industry argument. occurrences = AI answers about the attribute citing this source (all time). retrievals = distinct chats the source was retrieved in. citation_rate = mean citations per retrieved chat — an average, NOT a percentage, can exceed 1, display as-is. Sort with order_by (default occurrences desc). Optionally filter to specific engine channels via model_channel_ids (see list_model_channels). Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: url, title, domain, url_classification, domain_classification, occurrences, retrievals, citation_rate.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `order_by` | array | No | Sort results by one or more fields. Array of {field, direction} entries (direction defaults to "desc"). Sortable fields: occurrences, retrievals, citation_rate. Defaults to occurrences desc when omitted. e.g. [{"field":"occurrences","direction":"desc"}]. |
| `attribute` | string | Yes | The attribute name, exactly as returned by the brand-perception attribute tools. |
| `project_id` | string | Yes | The project ID |
| `model_channel_ids` | array | No | Only include answers from these AI engine channel ids (from list_model_channels). Omit to aggregate across all tracked channels. e.g. ["openai-0"]. |

### Get brand perception brand attributes

**Slug:** `PEEC_AI_MCP_GET_BRAND_PERCEPTION_BRAND_ATTRIBUTES`

How AI models describe the project's own brand: attribute clusters (e.g. "Luxury", "Racing Heritage") with a prominence score. score is 0-100 — the attribute's average prominence per AI answer; higher means the attribute is mentioned earlier and more often. Display scores as-is; they are NOT ratios, never multiply by 100. members lists the raw extracted attributes folded into the cluster. Data is a snapshot of the latest brand-perception run (no date range). An empty result means the first run has not completed yet. Optionally filter to specific engine channels via model_channel_ids (see list_model_channels). Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: name, score, members.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `model_channel_ids` | array | No | Only include answers from these AI engine channel ids (from list_model_channels). Omit to aggregate across all tracked channels. e.g. ["openai-0"]. |

### Get brand perception competitive breakdown

**Slug:** `PEEC_AI_MCP_GET_BRAND_PERCEPTION_COMPETITIVE_BREAKDOWN`

The full competitive breakdown matrix: for every brand-perception attribute, the average prominence of each brand (the project's own brand and its competitors) across AI answers. score is 0-100 — higher means the brand tends to be mentioned earlier when AI talks about that attribute; display as-is, never multiply by 100. is_own marks the project's own brand. One row per attribute x brand cell; attributes are sorted by the own brand's score (strongest first) and brands by score within each attribute. limit/offset paginate ATTRIBUTES, not rows — each attribute contributes one row per brand, and totalCount is the total attribute count. Data is aggregated across the brand-perception runs of one industry and one target market (no date range) — pass industry to pick the industry; the target market is always that industry's most recently completed one and cannot be picked. The response reports the scope it used as industry and target_market, and lists the project's other industries under industries when it has more than one — read a different one by passing its name back as industry, and say which industry the numbers describe whenever that field is present. Optionally filter to specific engine channels via model_channel_ids (see list_model_channels). Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: attribute, brand, score, is_own.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of attributes to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of attributes to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `industry` | string | No | Only read the runs dispatched for this industry. Omit to read the project's default industry — the one with the most recent completed run. The industries field of the response names the alternatives, so a first call needs no argument. |
| `project_id` | string | Yes | The project ID |
| `model_channel_ids` | array | No | Only include answers from these AI engine channel ids (from list_model_channels). Omit to aggregate across all tracked channels. e.g. ["openai-0"]. |

### Get brand perception objections

**Slug:** `PEEC_AI_MCP_GET_BRAND_PERCEPTION_OBJECTIONS`

The recurring arguments AI models make against the project's brand when a buyer is deciding. score is average prominence per AI answer from 0 to 100; display it as-is. members contains the raw phrasings folded into each objection. Data is aggregated across all objection runs and has no date range. Returns columnar JSON with columns, rows, rowCount, and totalCount. Columns: name, score, members.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No |  |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No |  |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `model_channel_ids` | array | No |  |

### Get brand perception objection sources

**Slug:** `PEEC_AI_MCP_GET_BRAND_PERCEPTION_OBJECTION_SOURCES`

The sources AI models retrieved when asked about one objection. Pass the objection name exactly as returned by get_brand_perception_objections; an unknown name returns an empty result. Data is aggregated across all objection runs and has no date range. citation_rate is an average, not a percentage. Returns columnar JSON with columns, rows, rowCount, and totalCount.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No |  |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No |  |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `order_by` | array | No | Sort results by one or more fields. Multiple entries create a multi-key sort. Direction defaults to desc. When omitted, a default ordering is applied. |
| `objection` | string | Yes | The objection name exactly as returned by get_brand_perception_objections |
| `project_id` | string | Yes | The project ID |
| `model_channel_ids` | array | No |  |

### Get brand report

**Slug:** `PEEC_AI_MCP_GET_BRAND_REPORT`

Get a report on brand visibility, sentiment, and position across AI search engines.

Results are aggregated for the entire date range by default. Use the "date", "week", or "month" dimension for time-bucketed breakdowns (daily, ISO week, or calendar month). When the range spans many days, prefer "week" or "month" over "date": coarser buckets return far fewer rows (180 days ≈ 26 weeks or 6 months vs. 180 daily rows), which avoids the row limit truncating the series and keeps the trend readable. Reach for "date" only when day-level granularity is essential and the range is short. Edge buckets may be partial: a range starting or ending mid-week/mid-month produces a first/last bucket that only aggregates the in-range days, so its values can look low — don't read that as a real dip.

Dimensions multiply the row count: rows = entities × dimension value combinations (e.g. 2 brands broken down by "date" over 180 days = 360 rows). The default limit of 100 will silently truncate such queries — raise "limit" to the row count you actually need. If rowCount equals limit, results were truncated; narrow the request (fewer entities, a shorter date range, fewer dimensions) or page through with "offset".

Returns columnar JSON: {columns, rows, rowCount, total}. Each row is an array of values matching column order. Columns:
- brand_id — the brand ID
- brand_name — the brand name
- visibility: 0–1 ratio — fraction of AI responses that mention this brand. 0.45 means 45% of conversations.
- mention_count: number of times the brand was mentioned
- share_of_voice: 0–1 ratio — brand's fraction of total mentions across all tracked brands
- sentiment: 0–100 scale — how positively AI platforms describe the brand (most brands score 65–85)
- position: average ranking when the brand appears (lower is better, 1 = mentioned first)
- Raw aggregation fields (for custom calculations): visibility_count, visibility_total, sentiment_sum, sentiment_count, position_sum, position_count

When dimensions are selected, rows also include the relevant dimension columns: prompt_id, model_id, model_channel_id, tag_id, topic_id, chat_id, date, week, month, country_code. model_channel_id, tag_id and topic_id are each followed by a model_channel_name / tag_name / topic_name column with the human-readable name.

Dimensions explained:
- prompt_id: individual search queries/prompts
- model_id: AI search engine (e.g. chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper) — deprecated, prefer model_channel_id
- model_channel_id: stable engine channel (e.g. openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0) — survives model upgrades
- tag_id: custom user-defined tags
- topic_id: topic groupings
- date: daily bucket (YYYY-MM-DD)
- week: ISO week bucket, value is the Monday start (YYYY-MM-DD)
- month: calendar month bucket, value is the first of the month (YYYY-MM-DD)
- country_code: country (ISO 3166-1 alpha-2, e.g. "US", "DE")
- chat_id: individual AI chat/conversation ID

Two filter inputs at different SQL stages — pick by intent:
- filters (WHERE, pre-aggregation): shrinks BOTH numerator and denominator of ratio metrics. Putting brand_id here narrows everything to that brand — including share_of_voice's denominator — so SoV collapses to 1.0.
- having (HAVING, post-aggregation): selects which aggregated rows are returned WITHOUT shrinking ratio-metric denominators. Use brand_id here for a single-brand report where SoV stays a real [0,1] fraction (X's share against competitors per dimension).

Metric semantics:
- visibility = visibility_count / visibility_total within each (brand × dimension) group. Per-row by construction; both filters and having behave intuitively.
- share_of_voice = brand's mention_count / sum of mentions across all brands in the same (dimension) cell. Pre-aggregation filters narrow num+denom together; post-aggregation having does not.
- When dimensions are requested, share_of_voice's denominator is per-dimension. Example: dimension prompt_id ⇒ share_of_voice for (brand X, prompt P) = X's mentions in P / all brands' mentions in P.

Sort results with order_by: array of {field, direction} entries. Direction defaults to desc. Sortable fields: visibility, visibility_count, mention_count, sentiment, position, share_of_voice, date, week, month. Multiple entries create a multi-key sort. Sorting by a time field (date, week or month) requires the matching dimension to be selected.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of rows to return. Defaults to 100 — too low for dimensional breakdowns, where rows = entities × dimension value combinations (e.g. 2 brands × 180 days with the 'date' dimension needs 360). Raise it to the row count you need rather than to the maximum; if rowCount in the response equals limit, results were truncated. |
| `having` | array | No | Post-aggregation row filters (HAVING). Multiple filters are AND'd. Two shapes: population and brand fields — `model_id` (deprecated), `model_channel_id`, `country_code`, `prompt_id`, `tag_id`, `topic_id`, `chat_id`, `brand_id` — take {field, operator, values} with operator 'in'&#124;'not_in', and the population ones require the matching value in dimensions. e.g. [{"field":"brand_id","operator":"in","values":["br_a1b2c3"]}]. Metric fields — `visibility`, `share_of_voice`, `sentiment`, `position` — take {field, operator, value} with operator 'gt'&#124;'gte'&#124;'lt'&#124;'lte' and need no matching dimension; each compares against the metric in the unit this tool returns it, so visibility and share_of_voice as a 0-1 ratio, sentiment as a 0-100 score, position as a rank where 1 is best, and a value outside that unit is rejected. Two predicates on one field make a range, and a row with no samples for the metric matches neither. Stating the same edge of a field twice, or a range no row could satisfy, is rejected. A metric predicate tests whichever row the grouping produces, so ask for "the prompts in range" with dimensions ["prompt_id"] — the same predicate with no dimensions tests the brand's rolled-up totals instead. e.g. [{"field":"visibility","operator":"gte","value":0.1},{"field":"visibility","operator":"lte","value":0.9}]. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `filters` | array | No | Pre-aggregation row filters (WHERE). {field, operator, values}, operator 'in'&#124;'not_in'. Allowed fields: `model_id` (deprecated), `model_channel_id`, `country_code`, `prompt_id`, `tag_id`, `topic_id`, `chat_id`, `brand_id`. Multiple filters are AND'd. e.g. [{"field":"model_channel_id","operator":"in","values":["openai-0"]}]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | array | No | Sort results by one or more fields. Array of {field, direction} entries (direction defaults to "desc"). Multiple entries create a multi-key sort. Sortable fields: visibility, visibility_count, mention_count, sentiment, position, share_of_voice, date, week, month. Sorting by a time field (date, week or month) requires the matching dimension to be selected. e.g. [{"field":"visibility","direction":"desc"}]. |
| `dimensions` | array | No | Break down results by one or more dimensions. prompt_id = individual search queries, model_id = AI search engine (deprecated), model_channel_id = stable engine channel, tag_id = custom tags, topic_id = topic groupings, date / week / month = time buckets (daily, ISO week, calendar month), country_code = country, chat_id = individual AI chat/conversation. When set, each row includes the corresponding dimension object. e.g. ["model_channel_id","topic_id"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |

### Get chat

**Slug:** `PEEC_AI_MCP_GET_CHAT`

Get the full content of a single chat (one AI engine's response to one prompt on one date). Returns:
- messages: the user prompt and assistant response(s)
- brands_mentioned: brands detected in the response with their position
- sources: URLs the model retrieved, with citation counts and position
- queries: search queries the model issued
- products: product gallery entries extracted from the response
- features: signal flags for special elements detected in the assistant response (SHOPPING, PRODUCT_COMPARISON, AD, MAP, WEB_SEARCH). Use list_chats with the `features` filter to query chats by these.
- maps: local-business map cards (one per business pinned in a map widget) with { name, url } where `url` is the Google Maps directions deeplink for that business.
- ads: paid ad placements rendered by the model. Each ad has { brand_name, url, id, ad_unit_type, ads_request_id, cards } where cards carry { title, body, image_url, target_url } and target_url is the clickout URL (with attribution UTM params).
- prompt: { id }
- model: { id } — deprecated, prefer model_channel
- model_channel: { id } — stable engine channel id (e.g. "openai-0")

Use list_chats to discover chat IDs for a project.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `chat_id` | string | Yes | The chat ID |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Get chats report

**Slug:** `PEEC_AI_MCP_GET_CHATS_REPORT`

Aggregate counts and rich-answer-feature shares over a project's chats for a date range. Answers questions like "how many chats", "what share rendered a map / shopping / web-search / ad / comparison result", and "how many distinct prompts / engines / countries does tag X span" — use it instead of paging through list_chats.

Group by one or more dimensions, or omit dimensions for a single total row over all matching chats.

Metric columns:
- chat_count — chats in the group (denominator of the feature percentages)
- prompt_count / model_channel_count / country_count — distinct prompts / engine channels / countries the group spans (country_count excludes chats with no country)
- shopping_count / shopping_percentage — answers with a shopping / product gallery
- product_comparison_count / product_comparison_percentage — answers with a product comparison
- ad_count / ad_percentage — answers with an ad
- map_count / map_percentage — answers with a map / local-business result
- web_search_count / web_search_percentage — answers with web-search sources

Percentages are 0–1 ratios of chat_count (0.75 = 75%). Coverage counts collapse to 1 for the dimension you group by, so read them against dimensions you did NOT split on. Raise "limit" for time or high-cardinality breakdowns, to the row count you need rather than to the maximum.

Dimensions: prompt_id, model_id (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper — deprecated, prefer model_channel_id), model_channel_id (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0), tag_id, topic_id, date / week / month, country_code, chat_id. tag_id / topic_id / model_channel_id rows also carry a matching *_name column.

Returns {columns, rows, rowCount}; each row is an array of values aligned to columns.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Max rows (default 100). Raise for dimensional breakdowns, to the row count you need rather than to the maximum; if rowCount equals limit, results were truncated. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `filters` | array | No | Restrict which chats are counted. {field, operator, values}, operator 'in'&#124;'not_in', AND'd. Fields: model_id (deprecated), model_channel_id, country_code, prompt_id, tag_id, topic_id, chat_id. e.g. [{"field":"model_channel_id","operator":"in","values":["openai-0"]}]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | array | No | Sort by one or more {field, direction} (direction defaults to "desc"). Sortable: chat_count, prompt_count, model_channel_count, country_count, shopping_count, shopping_percentage, product_comparison_count, product_comparison_percentage, ad_count, ad_percentage, map_count, map_percentage, web_search_count, web_search_percentage, date, week, month. Sorting by date/week/month requires that dimension. e.g. [{"field":"chat_count","direction":"desc"}]. |
| `dimensions` | array | No | Group by one or more of: prompt_id, model_id (deprecated), model_channel_id, tag_id, topic_id, date / week / month, country_code, chat_id. Omit for a single total row. e.g. ["model_channel_id","date"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |

### Get domain report

**Slug:** `PEEC_AI_MCP_GET_DOMAIN_REPORT`

Get a report on source domain visibility and citations across AI search engines.

Results are aggregated for the entire date range by default. Use the "date", "week", or "month" dimension for time-bucketed breakdowns (daily, ISO week, or calendar month). When the range spans many days, prefer "week" or "month" over "date": coarser buckets return far fewer rows (180 days ≈ 26 weeks or 6 months vs. 180 daily rows), which avoids the row limit truncating the series and keeps the trend readable. Reach for "date" only when day-level granularity is essential and the range is short. Edge buckets may be partial: a range starting or ending mid-week/mid-month produces a first/last bucket that only aggregates the in-range days, so its values can look low — don't read that as a real dip.

Dimensions multiply the row count: rows = domains × dimension value combinations (e.g. 5 domains broken down by "date" over 180 days = 900 rows). The default limit of 100 will silently truncate such queries — raise "limit" to the row count you actually need. If rowCount equals limit, results were truncated; narrow the request (fewer entities, a shorter date range, fewer dimensions) or page through with "offset".

Returns columnar JSON: {columns, rows, rowCount}. Each row is an array of values matching column order. Columns:
- domain: the source domain (e.g. "example.com")
- classification: domain type — Corporate (official company sites), Editorial (news, blogs, magazines), Institutional (government, education, nonprofit), UGC (social media, forums, communities), Reference (encyclopedias, documentation), Competitor (direct competitors), You (the user's own domains), Other, or null
- retrieved_percentage: 0–1 ratio — fraction of chats that included at least one URL from this domain. 0.30 means 30% of chats.
- retrieval_rate: average number of URLs from this domain pulled per chat. Can exceed 1.0 — values above 1.0 mean multiple pages from the same domain are retrieved per conversation.
- citation_rate: average number of inline citations when this domain is retrieved. Can exceed 1.0 — higher values indicate stronger content authority.
- retrieved_chat_count: the canonical "retrievals" metric — number of distinct chats in which at least one URL from this domain was retrieved. When a user asks about "retrievals" for a domain, report THIS number; it matches the Domains table in the UI.
- retrieval_count: URL-level count — total number of distinct URL retrievals from this domain across all chats (raw numerator of retrieval_rate). A single chat can retrieve several URLs, so this is always at least as large as retrieved_chat_count and is NOT the canonical "retrievals" number.
- citation_count: total number of citations from this domain (raw count).
- mentioned_brand_ids: array of brand IDs mentioned alongside URLs from this domain (may be empty)

When dimensions are selected, rows also include the relevant dimension columns: prompt_id, model_id, model_channel_id, tag_id, topic_id, chat_id, date, week, month, country_code. model_channel_id, tag_id and topic_id are each followed by a model_channel_name / tag_name / topic_name column with the human-readable name.

Dimensions explained:
- prompt_id: individual search queries/prompts
- model_id: AI search engine (e.g. chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper) — deprecated, prefer model_channel_id
- model_channel_id: stable engine channel (e.g. openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0) — survives model upgrades
- tag_id: custom user-defined tags
- topic_id: topic groupings
- date: daily bucket (YYYY-MM-DD)
- week: ISO week bucket, value is the Monday start (YYYY-MM-DD)
- month: calendar month bucket, value is the first of the month (YYYY-MM-DD)
- country_code: country (ISO 3166-1 alpha-2, e.g. "US", "DE")
- chat_id: individual AI chat/conversation ID

Two filter inputs at different SQL stages — pick by intent:
- filters (WHERE, pre-aggregation): population fields (model/country/prompt/tag/topic/chat) shrink both numerator and denominator (total_chat_count). Source-side fields (domain/url/classification) and per-row mentioned-brand predicates (mentioned_brand_id, mentioned_brand_count, gap) shrink only the source-row scope; total_chat_count is computed from a chat-level table that doesn't carry these columns.
- having (HAVING, post-aggregation): selects which aggregated rows are returned WITHOUT shrinking ratio-metric denominators.

Mentioned-brand predicates and source-side fields exist in both — the WHERE form tests each source row pre-aggregation (shrinks numerators); the HAVING form tests the aggregated mentioned_brands union per domain (purely selective).

Numeric predicates:
- mentioned_brand_count: {field: "mentioned_brand_count", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — filter by number of unique brands mentioned alongside the domain.
- gap: {field: "gap", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — gap analysis filter. Excludes domains where the project's own brand is mentioned, and filters by the number of competitor brands present. Example: {field: "gap", operator: "gte", value: 2} returns domains where the own brand is absent but at least 2 competitors are mentioned.

Metric semantics:
- retrieved_percentage and retrieval_rate use total_chat_count as the denominator. Filtering to a single domain via having still yields a meaningful retrieved_percentage < 1.0 — do not interpret it as 100%.
- When dimensions are requested, total_chat_count is per-dimension. Example: dimension prompt_id ⇒ each row's total_chat_count is the chat count for that prompt, so retrieved_percentage is the per-prompt retrieval rate.
- citation_rate = citation_count / retrieved_chat_count per row; it's independent of total_chat_count and naturally per-(domain × dimension).

Sort results with order_by: array of {field, direction} entries. Direction defaults to desc. Sortable fields: citation_rate, retrieval_count, citation_count, date, week, month. Sorting by a time field (date, week or month) requires the matching dimension to be selected. (retrieved_percentage and retrieval_rate are not sortable because they depend on totalChatCount fetched in a separate query.)

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of rows to return. Defaults to 100 — too low for dimensional breakdowns, where rows = domains × dimension value combinations (e.g. 5 domains × 180 days with the 'date' dimension needs 900). Raise it to the row count you need rather than to the maximum; if rowCount in the response equals limit, results were truncated. |
| `having` | array | No | Post-aggregation row filters (HAVING). {field, operator, values}, operator 'in'&#124;'not_in'. Allowed fields: `model_id` (deprecated), `model_channel_id`, `country_code`, `prompt_id`, `tag_id`, `topic_id`, `chat_id`, `domain`, `domain_classification`, `mentioned_brand_id`, `mentioned_brand_count`, `gap` (population fields require the matching value in dimensions). mentioned_brand_count uses {field, operator: 'gt'&#124;'gte'&#124;'lt'&#124;'lte', value: number}. gap uses {field: 'gap', operator: 'gt'&#124;'gte'&#124;'lt'&#124;'lte', value: number}: surfaces domains where own brand is absent but ≥N competitor brands are present. Multiple filters are AND'd. e.g. [{"field":"gap","operator":"gte","value":2}]. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `filters` | array | No | Pre-aggregation row filters (WHERE). {field, operator, values}, operator 'in'&#124;'not_in'. Allowed fields: `model_id` (deprecated), `model_channel_id`, `country_code`, `prompt_id`, `tag_id`, `topic_id`, `chat_id`, `domain`, `domain_classification`, `url`, `url_classification`, `mentioned_brand_id`, `mentioned_brand_count`, `gap`. mentioned_brand_count and gap use {field, operator: 'gt'&#124;'gte'&#124;'lt'&#124;'lte', value: number}. Multiple filters are AND'd. e.g. [{"field":"domain","operator":"in","values":["example.com"]}]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | array | No | Sort results by one or more fields. Array of {field, direction} entries (direction defaults to "desc"). Multiple entries create a multi-key sort. Sortable fields: citation_rate, retrieval_count, citation_count, date, week, month. Sorting by a time field (date, week or month) requires the matching dimension to be selected. retrieved_percentage and retrieval_rate are not sortable (they depend on total_chat_count fetched in a separate query). e.g. [{"field":"retrieval_count","direction":"desc"}]. |
| `dimensions` | array | No | Break down results by one or more dimensions. prompt_id = individual search queries, model_id = AI search engine (deprecated), model_channel_id = stable engine channel, tag_id = custom tags, topic_id = topic groupings, date / week / month = time buckets (daily, ISO week, calendar month), country_code = country, chat_id = individual AI chat/conversation. When set, each row includes the corresponding dimension object. e.g. ["model_channel_id","topic_id"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |

### Get product

**Slug:** `PEEC_AI_MCP_GET_PRODUCT`

Get one product's detailed metrics over a date range — the drill-down companion to list_products, which returns a thin row per product.

Returns {data, primary_currency}, where data is null if the product is not in the project. data always includes:
- id, name, brand, source (CATALOG or LLM), first_seen_at
- visibility (0–1), win_rate (0–1), avg_position (rank, lower is better), avg_rating (mean 0–5 star rating across the product's AI mentions; null when none carried a rating), mention_count
- *_delta for each metric above (visibility_delta, win_rate_delta, avg_position_delta, avg_rating_delta, mention_count_delta) — change vs the immediately preceding equal-length period; null when the previous period had nothing to compare against

Everything else is opt-in via the 'fields' argument (default: none) to keep the response lean — list any of these to include them:
- description, image_url
- price_range / price_override — per-currency effective catalog range (min/max) and the raw overrides behind it
- ai_price_map / ai_price_delta_map — per-currency median price across the product's AI mentions, and its change
- variants — the product's live catalog variants (SKU-level), each with id, name, and a per-currency price_map (ISO-4217 → price; a variant carries a single entry). Capped at 50 with no pagination: a product with more than 50 variants returns only the first 50 by name.
- four QFO (query fan-out) breakdowns of what surfaced this product, each up to 25 rows with distinct_chat_count (+ its previous-period value and delta): shopping_queries / shopping_query_terms (Google-Shopping fan-out queries and their n-grams) and fanout_queries / fanout_query_terms (web-search fan-out queries and their n-grams). Unrequested QFO breakdowns are skipped, not just omitted — no ClickHouse pass runs for them.

Visibility divides by the product's relevant-prompt chats (prompts it is mentioned in or SODA-linked to), not all shopping chats. Metrics cover [start_date, end_date]; deltas compare against the equal-length period ending the day before start_date.

With merchant_ids, the headline metrics, price maps, and QFO breakdowns count only chats where the product is sold through one of those merchants (the visibility denominator stays unscoped) — pass one merchant id for that seller's rating, price, mentions, and the queries that surface its offers.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Extra `data` fields to include (default: none), e.g. ["description", "image_url"]. Identity and headline metrics always return; opt into any of the heavy/lower-value fields — description, image_url, the price/currency maps, variants (live catalog variants, capped at 50), and the QFO breakdowns (fanout_queries, shopping_queries, and their *_terms variants) — by listing them here. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; see tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `chat_scope` | string ("all" | "shopping") | No | Which chats the visibility denominator counts: every in-scope chat ('all') or only product-gallery ('shopping') chats. Defaults to 'shopping'. |
| `product_id` | string | Yes | The product id to fetch |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `merchant_ids` | array | No | Scope the response to chats where this product is sold through any of these merchants (from list_merchants), e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] — pass a single id for that seller's rating, price, and mention metrics. The QFO breakdowns scope through those chats too; only the visibility denominator stays merchant-free. |
| `tag_operator` | string ("and" | "or") | No | Match any (or, default) or all (and) of tag_ids |
| `country_codes` | array | No | Filter metrics to these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. |
| `model_channel_ids` | array | No | Filter metrics to these model channel ids (from list_model_channels), e.g. ["openai-0"]. |

### Get project profile

**Slug:** `PEEC_AI_MCP_GET_PROJECT_PROFILE`

Read a project's brand profile — the description, industry, brand-identity adjectives, target markets, audience distribution, and product/service list that Peec uses to generate prompt suggestions. Returns { profile } where profile may be null if the project hasn't been profiled yet. Call this before set_project_profile so you can show the user the current values.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Get prompt suggestion generation

**Slug:** `PEEC_AI_MCP_GET_PROMPT_SUGGESTION_GENERATION`

Read the authoritative status of a run returned by generate_prompt_suggestions. QUEUED and RUNNING are non-terminal; SUCCEEDED and FAILED are terminal. A successful run can produce zero suggestions. After success, call list_prompt_suggestions to review the current suggestions.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `generation_id` | string | Yes | The generation ID |

### Get shopping attributes

**Slug:** `PEEC_AI_MCP_GET_SHOPPING_ATTRIBUTES`

Compare the qualitative characteristics and features that AI engines associate with a product (or the whole catalog) against competitors. These attributes are extracted from shopping answers and are qualitative — for the numeric shopping metrics (visibility, win rate, position) use get_shopping_summary or get_shopping_trend instead.

scope=product compares one product (needs product_id); scope=overview compares the whole catalog. compare_by picks the grid columns: brand (default) or product (scope=product only). tab picks the value type.

Returns a nested grid (not columnar): { tab, competitors[], groups[], total_groups }. Each competitor is a brand ({global_brand_id, name, domain, mentions}) or product ({product_id, name, brand_name, image_url, mentions}); every group's per-competitor arrays align to the competitors order. Group shape by tab:
- characteristics: { dimension_id, name, total_mentions(+_delta), value_count, values: [{value, mentions(+_delta), competitor_mentions[]}] }
- facts: { ..., value_type: BOOLEAN, unit, own: {true_count, false_count, unrecognized_count}, competitor_values[] }
- dimensions: { ..., own: {min, max, average, median} | null, competitor_values: (stats | null)[] }
Deltas are vs an explicit comparison window (previous_start_date/previous_end_date) or the equal-length window immediately before.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tab` | string ("characteristics" | "facts" | "dimensions") | No | `characteristics` (qualitative values), `facts` (true/false), `dimensions` (ordinal ratings). |
| `limit` | integer | No | Page size over dimension groups. |
| `scope` | string ("product" | "overview") | No | `product` compares one product (needs product_id); `overview` compares the whole catalog. |
| `offset` | integer | No | Number of dimension groups to skip for pagination, e.g. 0. |
| `search` | string | No | Substring match on dimension name (and value, characteristics). |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; combine with tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `model_ids` | array | No | Filter to these model ids (from list_models), e.g. ["gpt-4o"]. Omit for all models. |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `compare_by` | string ("brand" | "product") | No | Grid columns: competing `brand`s, or competing `product`s. `product` is only valid when scope=product. |
| `product_id` | string | No | Required when scope=product. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `category_ids` | array | No | Filter to products in any of these category ids (from list_categories); selecting a parent also matches its descendants. e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `tag_operator` | string ("and" | "or") | No | Match prompts carrying any (or, default) or all (and) of tag_ids. |
| `country_codes` | array | No | Filter to chats from these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. Omit for all countries. |
| `competitor_count` | integer | No | Grid columns; default 6. `0` disables competitor lookup. |
| `values_per_group` | integer | No | Characteristics tab only; default 12. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. Omit for all engines. |
| `previous_end_date` | string | No | End of the explicit comparison window; provide with previous_start_date or omit both to auto-derive. |
| `previous_start_date` | string | No | Start of an explicit comparison window for deltas; provide with previous_end_date or omit both to auto-derive. |

### Get shopping summary

**Slug:** `PEEC_AI_MCP_GET_SHOPPING_SUMMARY`

Return catalog-wide shopping metrics over a date range — avg_visibility, avg_win_rate, and avg_position, each with a period-over-period delta — as a single aggregated row across the filtered scope. Deltas compare against previous_start_date/previous_end_date when given, else the auto-derived previous period. For a per-entity or per-time breakdown, use get_shopping_trend or list_shopping_performance.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; combine with tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `brand_ids` | array | No | Filter to products of these global_brand_id values (from list_global_brands), e.g. ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]. |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `chat_scope` | string ("all" | "shopping") | No | Which chats the visibility denominator counts: every in-scope chat ('all') or only product-gallery ('shopping') chats. Defaults to 'shopping'. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `product_ids` | array | No | Filter to these product ids (from list_products), e.g. ["b2c3d4e5-f6a7-8901-bcde-f23456789012"]. |
| `category_ids` | array | No | Filter to products in any of these category ids (from list_categories); selecting a parent also matches its descendants. e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `merchant_ids` | array | No | Filter to products sold through any of these merchant ids (from list_merchants), e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"]. |
| `tag_operator` | string ("and" | "or") | No | Match prompts carrying any (or, default) or all (and) of tag_ids. |
| `country_codes` | array | No | Filter to chats from these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. Omit for all countries. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. Omit for all engines. |
| `previous_end_date` | string | No | End of the explicit comparison window; provide with previous_start_date or omit both to auto-derive. |
| `previous_start_date` | string | No | Start of an explicit comparison window for deltas; provide with previous_end_date or omit both to auto-derive. |

### Get shopping trend

**Slug:** `PEEC_AI_MCP_GET_SHOPPING_TREND`

Return a per-bucket shopping time series (visibility, win_rate, avg_position, sov) for specific products or global brands over a date range. Requires bucket (day/week/month) and exactly one of product_ids or brand_ids. Returns { entity_type, series[] }, one series per entity with a points array; has_data flags buckets with no underlying chats. For a single catalog-wide aggregate use get_shopping_summary; for a ranked table use list_shopping_performance.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `bucket` | string ("day" | "week" | "month") | Yes | Time-series bucket granularity. |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; combine with tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `brand_ids` | array | No | Global brand ids to chart, one series each (from list_global_brands), e.g. ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]. Mutually exclusive with product_ids. |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `chat_scope` | string ("all" | "shopping") | No | Which chats the visibility denominator counts: every in-scope chat ('all') or only product-gallery ('shopping') chats. Defaults to 'shopping'. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `product_ids` | array | No | Product ids to chart, one series each (from list_products), e.g. ["b2c3d4e5-f6a7-8901-bcde-f23456789012"]. Mutually exclusive with brand_ids. |
| `category_ids` | array | No | Filter to products in any of these category ids (from list_categories); selecting a parent also matches its descendants. e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `merchant_ids` | array | No | Filter to products sold through any of these merchant ids (from list_merchants), e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"]. |
| `tag_operator` | string ("and" | "or") | No | Match prompts carrying any (or, default) or all (and) of tag_ids. |
| `country_codes` | array | No | Filter to chats from these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. Omit for all countries. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. Omit for all engines. |

### Get url content

**Slug:** `PEEC_AI_MCP_GET_URL_CONTENT`

Fetches and returns the content of a URL for downstream analysis — the markdown Peec extracted from the page.

Returns the exact content Peec's pipeline indexed, with no extra fetch. Works for any URL Peec has scraped for the project; you do not need to call get_url_report first.

If Peec has no record of the URL it returns 404.

Input notes:
- url is the full URL, copied verbatim. Trailing slashes and scheme variations change the resolved source ID.
- max_length caps the returned content (default 100000 characters). If the stored content is longer, truncated=true — re-request with a higher max_length.

Returned fields:
- url, title, domain, channel_title: page metadata
- classification: domain-level classification
- url_classification: page-level classification (HOMEPAGE, LISTICLE, COMPARISON, ...)
- content: the page's markdown content. null when the URL is tracked but scraping hasn't completed yet (can take up to 24h).
- content_length: original character length before truncation (0 when content is null)
- truncated: true if content was truncated to max_length
- content_updated_at: ISO timestamp of last scrape, or null if not yet scraped

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | Full URL of a source. Typically copied from get_url_report output. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `max_length` | integer | No | Maximum number of characters of content to return. Defaults to 100000. |
| `project_id` | string | Yes | The project ID |

### Get url report

**Slug:** `PEEC_AI_MCP_GET_URL_REPORT`

Get a report on source URL visibility and citations across AI search engines.

Results are aggregated for the entire date range by default. Use the "date", "week", or "month" dimension for time-bucketed breakdowns (daily, ISO week, or calendar month). When the range spans many days, prefer "week" or "month" over "date": coarser buckets return far fewer rows (180 days ≈ 26 weeks or 6 months vs. 180 daily rows), which avoids the row limit truncating the series and keeps the trend readable. Reach for "date" only when day-level granularity is essential and the range is short. Edge buckets may be partial: a range starting or ending mid-week/mid-month produces a first/last bucket that only aggregates the in-range days, so its values can look low — don't read that as a real dip.

Dimensions multiply the row count: rows = URLs × dimension value combinations (e.g. 5 URLs broken down by "date" over 180 days = 900 rows). The default limit of 100 will silently truncate such queries — raise "limit" to the row count you actually need. If rowCount equals limit, results were truncated; narrow the request (fewer entities, a shorter date range, fewer dimensions) or page through with "offset".

Returns columnar JSON: {columns, rows, rowCount}. Each row is an array of values matching column order. Columns:
- url: the full source URL (e.g. "https://example.com/page")
- classification: page type — Homepage, Category Page, Product Page, Listicle (list-structured articles), Comparison (product/service comparisons), Profile (directory entries like G2 or Yelp), Alternative (alternatives-to articles), Discussion (forums, comment threads), How-To Guide, Article (general editorial content), Other, or null
- title: page title or null
- channel_title: channel or author name (e.g. YouTube channel, subreddit) or null
- citation_count: total number of explicit citations across all chats
- retrieval_count: total number of distinct chats that retrieved this URL, regardless of whether it was cited
- citation_rate: average number of inline citations per chat when this URL is retrieved. Can exceed 1.0 — higher values indicate more authoritative content.
- mentioned_brand_ids: array of brand IDs mentioned alongside this URL (may be empty)

When dimensions are selected, rows also include the relevant dimension columns: prompt_id, model_id, model_channel_id, tag_id, topic_id, chat_id, date, week, month, country_code. model_channel_id, tag_id and topic_id are each followed by a model_channel_name / tag_name / topic_name column with the human-readable name.

Dimensions explained:
- prompt_id: individual search queries/prompts
- model_id: AI search engine (e.g. chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper) — deprecated, prefer model_channel_id
- model_channel_id: stable engine channel (e.g. openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0) — survives model upgrades
- tag_id: custom user-defined tags
- topic_id: topic groupings
- date: daily bucket (YYYY-MM-DD)
- week: ISO week bucket, value is the Monday start (YYYY-MM-DD)
- month: calendar month bucket, value is the first of the month (YYYY-MM-DD)
- country_code: country (ISO 3166-1 alpha-2, e.g. "US", "DE")
- chat_id: individual AI chat/conversation ID

Two filter inputs at different SQL stages — pick by intent:
- filters (WHERE, pre-aggregation): population fields shrink the chat scope; source-side and per-row mentioned-brand predicates (mentioned_brand_id, mentioned_brand_count, gap) shrink the source-row scope.
- having (HAVING, post-aggregation): selects which aggregated URL rows are returned.

Shared fields exist on both — the WHERE form tests each source row pre-aggregation (shrinks numerators); the HAVING form tests the aggregated mentioned_brands union per URL (purely selective). Use filters to prune source rows early; use having to combine with mentioned_brand_*/gap expressions on the grouped union.

Numeric predicates:
- mentioned_brand_count: {field: "mentioned_brand_count", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — filter by number of unique brands mentioned alongside the URL.
- gap: {field: "gap", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — gap analysis filter. Excludes URLs where the project's own brand is mentioned, and filters by the number of competitor brands present. Example: {field: "gap", operator: "gte", value: 2} returns URLs where the own brand is absent but at least 2 competitors are mentioned.

Metric semantics:
- citation_rate = citation_count / retrieval_count, both aggregated per (url × dimension) row from the same filtered group. There's no cross-row denominator, so neither filters nor having can collapse the metric — it stays meaningful even when narrowing to a single url or domain.
- retrieval_count is the count of distinct chats containing this URL within the filtered + dimension-grouped scope. Compare across rows to gauge a URL's relative retrieval strength. Do not divide retrieval_count by an external chat-count to compute a "retrieval percentage" — use the domain report for that aggregate.

Sort results with order_by: array of {field, direction} entries. Direction defaults to desc. Sortable fields: retrieval_count, retrievals, citation_count, citation_rate, date, week, month. Multiple entries create a multi-key sort. Sorting by a time field (date, week or month) requires the matching dimension to be selected.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of rows to return. Defaults to 100 — too low for dimensional breakdowns, where rows = URLs × dimension value combinations (e.g. 5 URLs × 180 days with the 'date' dimension needs 900). Raise it to the row count you need rather than to the maximum; if rowCount in the response equals limit, results were truncated. |
| `having` | array | No | Post-aggregation row filters (HAVING). {field, operator, values}, operator 'in'&#124;'not_in'. Allowed fields: `model_id` (deprecated), `model_channel_id`, `country_code`, `prompt_id`, `tag_id`, `topic_id`, `chat_id`, `domain`, `domain_classification`, `url`, `url_classification`, `mentioned_brand_id`, `mentioned_brand_count`, `gap` (population fields require the matching value in dimensions). mentioned_brand_count uses {field, operator: 'gt'&#124;'gte'&#124;'lt'&#124;'lte', value: number}. gap uses {field: 'gap', operator: 'gt'&#124;'gte'&#124;'lt'&#124;'lte', value: number}: surfaces URLs where own brand is absent but ≥N competitor brands are present. Multiple filters are AND'd. e.g. [{"field":"gap","operator":"gte","value":2}]. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `filters` | array | No | Pre-aggregation row filters (WHERE). {field, operator, values}, operator 'in'&#124;'not_in'. Allowed fields: `model_id` (deprecated), `model_channel_id`, `country_code`, `prompt_id`, `tag_id`, `topic_id`, `chat_id`, `domain`, `domain_classification`, `url`, `url_classification`, `mentioned_brand_id`, `mentioned_brand_count`, `gap`. mentioned_brand_count and gap use {field, operator: 'gt'&#124;'gte'&#124;'lt'&#124;'lte', value: number}. Multiple filters are AND'd. e.g. [{"field":"url","operator":"in","values":["https://example.com/page"]}]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | array | No | Sort results by one or more fields. Array of {field, direction} entries (direction defaults to "desc"). Multiple entries create a multi-key sort. Sortable fields: retrieval_count, retrievals, citation_count, citation_rate, date, week, month. Sorting by a time field (date, week or month) requires the matching dimension to be selected. e.g. [{"field":"citation_count","direction":"desc"}]. |
| `dimensions` | array | No | Break down results by one or more dimensions. prompt_id = individual search queries, model_id = AI search engine (deprecated), model_channel_id = stable engine channel, tag_id = custom tags, topic_id = topic groupings, date / week / month = time buckets (daily, ISO week, calendar month), country_code = country, chat_id = individual AI chat/conversation. When set, each row includes the corresponding dimension object. e.g. ["model_channel_id","topic_id"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |

### List bots

**Slug:** `PEEC_AI_MCP_LIST_BOTS`

List all known AI crawler/agent bots that visit the site (e.g. GPTBot, ClaudeBot, PerplexityBot). Each bot has an ID, provider, and type.

Bot types:
- training: crawlers that collect data for model training (e.g. GPTBot, ClaudeBot)
- search: bots that retrieve content for AI-powered search (e.g. PerplexityBot)
- userQuery: bots triggered by real-time user queries
- other: miscellaneous bots

Use the returned bot IDs with get_agent_visits to filter visit counts by specific bots.

Returns: {data: [{id, provider, type}]}

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List brand perception attribute clusters

**Slug:** `PEEC_AI_MCP_LIST_BRAND_PERCEPTION_ATTRIBUTE_CLUSTERS`

The editable attribute taxonomy behind brand perception: every cluster with its id, label and the raw extracted attributes grouped under it. Use this to resolve the cluster_id values edit_brand_perception_attribute_clusters needs, and to see attributes that are grouped wrongly. Prefer get_brand_perception_brand_attributes when the question is about scores — this tool carries no scores, but it does list clusters that have no score yet. unassigned holds extracted attributes that belong to no cluster (not paginated). is_custom marks a cluster a person added by hand. Returns columnar JSON: {columns, rows, rowCount, totalCount, unassigned}. Columns: cluster_id, name, is_custom, members.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of clusters to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of clusters to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List brands

**Slug:** `PEEC_AI_MCP_LIST_BRANDS`

List brands tracked in a project — includes the user's own brand and competitors. Use this tool to resolve brand names to IDs before filtering reports (brand_id filter), and to label brand IDs from report output with their human-readable names before presenting results. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, domains, aliases, is_own, regex. aliases are alternate names the brand is matched under. A nonempty regex overrides name and alias matching; null means none is configured. is_own indicates which brand belongs to the user. These are the brands a project tracks for visibility reporting — separate from list_global_brands, which searches the global product catalog used when creating products.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List brand social channels

**Slug:** `PEEC_AI_MCP_LIST_BRAND_SOCIAL_CHANNELS`

List the social channels configured for a project's own brand and competitors. Optionally filter by brand ID. Returns columnar JSON with id, brand_id, platform, channel_id, and channel_handle.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `brand_id` | string | No | Only return this brand |
| `project_id` | string | Yes | The project ID |

### List categories

**Slug:** `PEEC_AI_MCP_LIST_CATEGORIES`

List a project's product categories. Categories are an org-wide tree (e.g. Footwear > Shoes > Running Shoes); each row carries its full `path` and its `parent_id`, so the flat list rebuilds the tree. Use a row's `id` in the `category_ids` of create_products or update_products to categorize products. Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: id, name, path, parent_id.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List chats

**Slug:** `PEEC_AI_MCP_LIST_CHATS`

List chats (individual AI responses) for a project over a date range. Each chat is produced by running one prompt against one AI engine on a given date.

Filters:
- brand_id: only chats that mentioned the given brand
- prompt_id: only chats produced by the given prompt
- model_id: only chats from the given AI engine (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper) — deprecated, prefer model_channel_id
- model_channel_id: only chats from the given engine channel (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0)
- features: only chats that contain all of the given features (SHOPPING, PRODUCT_COMPARISON, AD, MAP, WEB_SEARCH)

If both model_id and model_channel_id are provided, model_channel_id takes precedence and model_id is ignored.

Excludes chats whose prompt has been deleted or archived. Set include_archived_prompts=true to include chats for archived prompts (e.g. historical lookback for a prompt that is no longer tracked). Chats for deleted prompts are always excluded.

Use the returned chat IDs with get_chat to retrieve full message content, sources, and brand mentions.

Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, prompt_id, model_id, model_channel_id, date, features.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `brand_id` | string | No | Filter to chats that mentioned this brand |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `features` | array | No | Filter to chats that contain this feature. Can specify multiple features to filter to chats that contain all of the specified features. e.g. ["SHOPPING","WEB_SEARCH"]. |
| `model_id` | string ("chatgpt-scraper" | "gpt-4o" | "gpt-4o-search" | "gpt-3.5-turbo" | "llama-sonar" | "perplexity-scraper" | "sonar" | "gemini-2.5-flash" | "gemini-3.1-flash-lite-search" | "gemini-scraper" | "google-ai-overview-scraper" | "google-ai-mode-scraper" | "llama-3.3-70b-instruct" | "deepseek-r1" | "deepseek-v4-pro" | "claude-3.5-haiku" | "claude-haiku-4.5" | "claude-sonnet-4" | "grok-scraper" | "microsoft-copilot-scraper" | "grok-4" | "grok-4.3" | "grok-4.6" | "qwen-3-6-plus" | "qwen-3-7-plus" | "amazon-rufus-scraper" | "mistral-small-4" | "mistral-medium-3-5" | "muse-spark-1.2" | "naver-ai-briefing-scraper") | No | Filter to chats from this AI engine. Deprecated — prefer model_channel_id. Ignored if model_channel_id is also provided. |
| `prompt_id` | string | No | Filter to chats produced by this prompt |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `model_channel_id` | string ("openai-0" | "openai-1" | "qwen-0" | "openai-2" | "perplexity-0" | "perplexity-1" | "google-0" | "google-1" | "google-2" | "google-3" | "google-4" | "anthropic-0" | "anthropic-1" | "deepseek-0" | "meta-0" | "meta-1" | "xai-0" | "xai-1" | "microsoft-0" | "amazon-0" | "mistral-0" | "mistral-1" | "naver-0") | No | Filter to chats from this engine channel |
| `include_archived_prompts` | boolean | No | Include chats whose prompt has been archived. Default false. Only set to true when the user explicitly wants historical chats for prompts that are no longer active — don't set it preemptively. Chats for deleted prompts are always excluded regardless of this flag. |

### List domain classifications

**Slug:** `PEEC_AI_MCP_LIST_DOMAIN_CLASSIFICATIONS`

List the custom domain classifications defined for a project. These complement the built-in classifications (Corporate, Competitor, Editorial, Institutional, Other, Reference, UGC, You, Related) and can be assigned to domains via assign_domain_classification. Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: name, color.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List global brands

**Slug:** `PEEC_AI_MCP_LIST_GLOBAL_BRANDS`

Search Peec's global brand catalog — the shared registry of real-world brands (e.g. Nike, Apple) that products attach to. Use it to find the global_brand_id for a brand before creating products with create_products. This is NOT the same as list_brands: list_brands returns the brands you track inside a project (your own brand plus the competitors you monitor for AI visibility), whereas list_global_brands searches every brand in Peec's catalog. The two have different ids and are not interchangeable. Primarily used with `search` to look up a brand by name (matches names and aliases). Pass ownership ('own', 'competitor', or 'all') instead to list the project's shopping brands by global_brand_id — the ids shopping product filters take — ranked by mentions, with their mention_count and is_own. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matches ignoring limit/offset. Columns: id, name, domain, description, mention_count, is_own.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `search` | string | No | Look up a brand by name or alias. The primary way to use this tool. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `ownership` | string ("own" | "competitor" | "all") | No | List the project's shopping brands instead of searching the catalog, ranked by all-time shopping mention count: 'own' for the project's own brands, 'competitor' for competitors appearing in shopping data, 'all' for both. |
| `project_id` | string | Yes | The project ID |

### List merchants

**Slug:** `PEEC_AI_MCP_LIST_MERCHANTS`

List the merchants (sellers, e.g. Amazon / eBay) whose product offers surfaced in AI answers, ranked over a date range. Each row carries mention_count, share_of_voice (0–1: this merchant's mentions over all merchants' mentions in the filtered population), win_rate (0–1 buy-box rate: position-1 mentions over all mentions), avg_position (rank, lower is better), avg_rating (mean 0–5 star rating; null when unrated), and a *_delta for each vs the previous window.

Scope the population with category_ids or product_ids for per-category or per-product seller breakdowns — with product_ids, share_of_voice is the share among that product's sellers. merchant_ids instead keeps only those merchants' rows without shrinking the share_of_voice denominator (e.g. to compare two known sellers). Pass a merchant_id from here to get_product's merchant_ids for that seller's per-product rating and price.

Merchants with no current-window mentions are omitted.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return, e.g. 20. |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip for pagination, e.g. 0. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; combine with tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | string ("mention_count" | "win_rate" | "avg_rating" | "avg_position") | No | Metric to rank merchants by. |
| `direction` | string ("asc" | "desc") | No | Sort direction. |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `product_ids` | array | No | Narrow the population to these product ids for per-product seller breakdowns (from list_products), e.g. ["b2c3d4e5-f6a7-8901-bcde-f23456789012"]. |
| `category_ids` | array | No | Filter to products in any of these category ids (from list_categories); selecting a parent also matches its descendants. e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `merchant_ids` | array | No | Filter to products sold through any of these merchant ids (from list_merchants), e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"]. |
| `tag_operator` | string ("and" | "or") | No | Match prompts carrying any (or, default) or all (and) of tag_ids. |
| `country_codes` | array | No | Filter to chats from these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. Omit for all countries. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. Omit for all engines. |
| `previous_end_date` | string | No | End of the explicit comparison window; provide with previous_start_date or omit both to auto-derive. |
| `previous_start_date` | string | No | Start of an explicit comparison window for deltas; provide with previous_end_date or omit both to auto-derive. |

### List model channels

**Slug:** `PEEC_AI_MCP_LIST_MODEL_CHANNELS`

List the AI engine channels tracked by Peec. A model channel is a stable identifier for an AI engine (e.g. "openai-0" = ChatGPT UI) that persists even as the underlying model is upgraded — use it to filter or break down reports by engine without worrying about model version changes. Use this tool to resolve channel descriptions (e.g. "ChatGPT UI", "Perplexity") to channel IDs before filtering reports (model_channel_id filter), and to label channel IDs from report output before presenting results. The current_model_id column gives the model ID currently active in the channel — pass this as model_id where reports require it. is_active indicates whether the channel is enabled for this project — inactive channels return empty data. unsupported_country_codes lists country codes that cannot be used with this channel (chats requested for those countries are not created). Returns columnar JSON: {columns, rows, rowCount}. Columns: id, description, current_model_id, is_active, unsupported_country_codes.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List models

**Slug:** `PEEC_AI_MCP_LIST_MODELS`

Deprecated — prefer list_model_channels, which returns stable channel IDs that survive model upgrades. List AI engines (models) tracked by Peec. Use this tool to resolve model names (e.g., "ChatGPT", "Perplexity", "Gemini") to IDs before filtering reports (model_id filter/dimension), and to label model IDs from report output with their human-readable names before presenting results. Match user-supplied names against the name column; the id column is the canonical string to pass back as model_id. is_active indicates whether the model is enabled for this project — inactive models will return empty data in reports. Returns columnar JSON: {columns, rows, rowCount}. Columns: id, name, is_active.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List products

**Slug:** `PEEC_AI_MCP_LIST_PRODUCTS`

List a project's products with headline metrics over a date range, filtered by the shopping filter pack.

Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, brand, source (CATALOG = claimed/uploaded, LLM = AI-detected), image_url, price_range (per-currency effective range), categories (category ids), mention_count, win_count, avg_position, avg_rating (mean 0–5 star rating across the product's AI mentions; null when none carried a rating), visibility, share_of_voice. Metrics cover [start_date, end_date].

For catalog cleanup, dedup, or category/name fixes, filter to source=CATALOG — LLM products aren't part of the user's catalog.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return, e.g. 100. |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip for pagination, e.g. 0. |
| `search` | string | No | Filter by name/brand text |
| `source` | string ("CATALOG" | "LLM") | No | Filter by source: CATALOG (claimed/uploaded) or LLM (AI-detected). Omit to return both. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; see tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | string ("visibility" | "win_rate" | "avg_position" | "avg_rating" | "mention_count" | "name") | No | Metric to rank by |
| `brand_ids` | array | No | Filter to products of these global_brand_id values (from list_global_brands), e.g. ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]. |
| `direction` | string ("asc" | "desc") | No |  |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `chat_scope` | string ("all" | "shopping") | No | Which chats the visibility denominator counts: every in-scope chat ('all') or only product-gallery ('shopping') chats. Defaults to 'shopping'. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `product_ids` | array | No | Filter to these product ids (from list_products), e.g. ["b2c3d4e5-f6a7-8901-bcde-f23456789012"]. |
| `category_ids` | array | No | Filter to products in any of these category ids (OR; from list_categories). Selecting a parent category also matches products in its descendants. e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `merchant_ids` | array | No | Filter to products sold through any of these merchant ids (from list_merchants), e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"]. |
| `tag_operator` | string ("and" | "or") | No | Match any (or, default) or all (and) of tag_ids |
| `country_codes` | array | No | Filter to these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. |

### List projects

**Slug:** `PEEC_AI_MCP_LIST_PROJECTS`

List active projects the authenticated user has access to. By default, only projects with an active status (CUSTOMER, PITCH, TRIAL, ONBOARDING, API_PARTNER) are returned — this is what you want in almost every case. Only set include_inactive to true if the user asked for a specific project that wasn't in the active list; do not set it preemptively. Returns columnar JSON: {columns, rows, rowCount}. Columns: id, name, status, language_code (ISO 639-1, the language the project's prompts run in). The id is used as project_id in other tools. Call this first to discover available projects.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `include_inactive` | boolean | No | Include ended/inactive projects. Default false. Only set to true as a fallback when a project the user named was not found in the default active list — don't set it preemptively. |

### List prompts

**Slug:** `PEEC_AI_MCP_LIST_PROMPTS`

List prompts (conversational questions tracked daily across AI engines) in a project. Supports filtering by topic_id, tag_id, country_code, and is_archived. Returns only active prompts by default — pass is_archived=true to list archived prompts instead (e.g. when a prompt_id from older report output isn't found among the active ones). Use the `search` parameter to resolve prompt text to IDs (fuzzy, ranked by relevance) before filtering reports (prompt_id filter/dimension), and use this tool to label prompt IDs from report output with their actual text before presenting results. Search scans at most the 20000 most recent prompts and sets `search_truncated: true` in the result when the project exceeds that. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, text, tag_ids (array of tag ID strings), tags (array of tag names, aligned with tag_ids), topic_id (string or null), topic (topic name, string or null), country_code (ISO 3166-1 alpha-2 country the prompt is tracked from, e.g. "US"; a prompt carrying none of its own reports the project default), volume (relative search volume bucket: "very low" | "low" | "medium" | "high" | "very high", or null when unavailable — describe volume to users using the bucket label), is_archived (boolean — true when the prompt is no longer tracked daily), is_fact_checking_enabled (boolean — true when the prompt is selected for fact-checking), created_at (ISO 8601 date string, e.g. "2025-09-22", when the prompt was created — use to identify prompts older than a given age). When presenting prompts to users, use the name columns (text, topic, tags) — the id columns exist only for filtering. Pass `fields` to return only the columns you need.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `search` | string | No | Free-text search over prompt text. Fuzzy and word-order-insensitive; results are ranked best-match first and total_count reflects matches. Use this to resolve quoted or paraphrased prompt text to prompt IDs instead of paging through all prompts. If nothing relevant is found among active prompts, retry with is_archived=true. |
| `tag_id` | string | No | Filter prompts by tag ID |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `topic_id` | string | No | Filter prompts by topic ID |
| `project_id` | string | Yes | The project ID |
| `is_archived` | boolean | No | Filter by archived status. Omit (or false) to return only active prompts; true to return only archived prompts. |
| `country_code` | string ("AE" | "AL" | "AM" | "AR" | "AT" | "AU" | "BA" | "BE" | "BG" | "BH" | "BO" | "BR" | "BS" | "BY" | "CA" | "CH" | "CL" | "CN" | "CO" | "CR" | "CY" | "CZ" | "DE" | "DK" | "DO" | "EC" | "EE" | "EG" | "ES" | "FI" | "FR" | "GB" | "GE" | "GH" | "GR" | "GT" | "HK" | "HN" | "HR" | "HU" | "ID" | "IE" | "IL" | "IN" | "IQ" | "IS" | "IT" | "JO" | "JP" | "KR" | "KW" | "LB" | "LI" | "LT" | "LU" | "LV" | "MA" | "MD" | "ME" | "MK" | "MN" | "MT" | "MX" | "MY" | "NG" | "NI" | "NL" | "NO" | "NZ" | "OM" | "PK" | "PA" | "PE" | "PH" | "PL" | "PT" | "PY" | "PS" | "QA" | "RO" | "RS" | "SA" | "SE" | "SG" | "SI" | "SK" | "SV" | "TH" | "TN" | "TR" | "TW" | "UA" | "US" | "UY" | "VE" | "VN" | "ZA" | "AD" | "AF" | "AS" | "AZ" | "BB" | "BQ" | "CG" | "CI" | "CM" | "CW" | "DM" | "DZ" | "FO" | "GF" | "GP" | "JM" | "KG" | "KH" | "KI" | "KZ" | "LK" | "LR" | "LS" | "MW" | "NC" | "PG" | "TD" | "TF" | "UG" | "VU" | "ZW") | No | Filter prompts by the country they are tracked from, as an ISO 3166-1 alpha-2 code (e.g. "US"). A prompt carrying no country of its own counts as the project default, so filtering by that default returns it too — the same behaviour as the location filter on the prompts page. |

### List prompt suggestions

**Slug:** `PEEC_AI_MCP_LIST_PROMPT_SUGGESTIONS`

List a project's prompt suggestions — prompts generated by Prompt discovery and awaiting review, which are not tracked and produce no data until accepted. Accept or reject them with finalize_prompt_suggestions. Their ids are prefixed `pr_`, and a suggestion keeps that id when accepted, so the accepted prompt is findable under the same id with list_prompts. Filter by topic_id to review one topic at a time. Returns columnar JSON: {columns, rows, rowCount}. Columns: id, text, topic_id, tag_ids (array of tag ID strings — resolve names with list_tags), country (the market the suggestion was generated for), volume (relative search volume, or null when unavailable). Pass `fields` to return only the columns you need.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `topic_id` | string | No | Filter suggestions by topic ID |
| `project_id` | string | Yes | The project ID |

### List referral sources

**Slug:** `PEEC_AI_MCP_LIST_REFERRAL_SOURCES`

Every AI assistant Peec can attribute referral traffic to, whether or not a project has seen traffic from it. Use it to resolve the exact display name for the `assistants` filter on get_ai_referrals, which rejects a name it does not recognise, and to recognise a raw host in that tool's results.

One row per referrer host, so an assistant reachable on several hosts (chatgpt.com and chat.openai.com) appears once per host. The list is maintained by hand and reviewed quarterly, so a very new assistant may be missing.

For the assistants that actually sent a project traffic, call get_ai_referrals with group_by=["assistant"] instead — this tool takes no project and no date range.

Returns: {columns, rows, rowCount, total_count}.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of rows to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of rows to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |

### List search queries

**Slug:** `PEEC_AI_MCP_LIST_SEARCH_QUERIES`

List the general web-search queries an AI engine fanned out to while answering prompts in a project over a date range — the text sub-queries it sent to a search index, NOT product/shopping lookups (use list_shopping_queries for those). Each row represents one web-search sub-query the engine issued for a given chat.

Filters:
- prompt_id: only queries from chats produced by this prompt
- chat_id: only queries from this chat
- model_id: only queries from this AI engine (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper)
- model_channel_id: only queries from this channel (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0)
- topic_id: only queries from chats whose prompt belongs to this topic
- tag_id: only queries from chats whose prompt carries this tag

Use get_chat with a returned chat_id to inspect the full AI response that produced these sub-queries.

Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: prompt_id, chat_id, model_id, model_channel_id, date, query_index, query_text.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return (default 100, hard cap 1000). Do not request 10000 — paginate with `offset` if you need more. |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `tag_id` | string | No | Filter to queries from chats whose prompt carries this tag |
| `chat_id` | string | No | Filter to queries from this chat |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `model_id` | string ("chatgpt-scraper" | "gpt-4o" | "gpt-4o-search" | "gpt-3.5-turbo" | "llama-sonar" | "perplexity-scraper" | "sonar" | "gemini-2.5-flash" | "gemini-3.1-flash-lite-search" | "gemini-scraper" | "google-ai-overview-scraper" | "google-ai-mode-scraper" | "llama-3.3-70b-instruct" | "deepseek-r1" | "deepseek-v4-pro" | "claude-3.5-haiku" | "claude-haiku-4.5" | "claude-sonnet-4" | "grok-scraper" | "microsoft-copilot-scraper" | "grok-4" | "grok-4.3" | "grok-4.6" | "qwen-3-6-plus" | "qwen-3-7-plus" | "amazon-rufus-scraper" | "mistral-small-4" | "mistral-medium-3-5" | "muse-spark-1.2" | "naver-ai-briefing-scraper") | No | Filter to queries from this AI engine |
| `topic_id` | string | No | Filter to queries from chats whose prompt belongs to this topic |
| `prompt_id` | string | No | Filter to queries from chats produced by this prompt |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `model_channel_id` | string ("openai-0" | "openai-1" | "qwen-0" | "openai-2" | "perplexity-0" | "perplexity-1" | "google-0" | "google-1" | "google-2" | "google-3" | "google-4" | "anthropic-0" | "anthropic-1" | "deepseek-0" | "meta-0" | "meta-1" | "xai-0" | "xai-1" | "microsoft-0" | "amazon-0" | "mistral-0" | "mistral-1" | "naver-0") | No | Filter to queries from this model channel |

### List shopping demand

**Slug:** `PEEC_AI_MCP_LIST_SHOPPING_DEMAND`

Rank the query terms that drive shopping demand by how many distinct chats they appear in, with a trend vs a comparison window. Returns an aggregated ranking, not per-chat rows — for the individual queries in each chat use list_shopping_queries or list_search_queries instead.

kind selects what is ranked:
- shopping_query: product fan-out queries
- fanout_query: web-search fan-out queries
- query_term: n-grams across shopping queries (n sets the width, default 2 = bigrams)

mode selects how the ranking is ordered:
- top: highest distinct-chat count now
- trending: biggest gainers vs the comparison window
- losing: biggest decliners vs the comparison window
- new: appeared in this window only

Columns: kind, text, distinct_chat_count, distinct_chat_count_previous (count in the comparison window), delta (change vs the comparison window).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `n` | integer | No | n-gram width for kind=query_term. |
| `kind` | string ("shopping_query" | "fanout_query" | "query_term") | Yes | What to rank: shopping_query (product fan-out queries), fanout_query (web-search fan-out queries), or query_term (n-grams across shopping queries; width set by n). |
| `mode` | string ("top" | "trending" | "losing" | "new") | No | How the ranking is ordered: top = highest distinct-chat count now, trending = biggest gainers, losing = biggest decliners, new = appeared in this window only. |
| `limit` | number | No | Maximum number of results to return, e.g. 20. |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; combine with tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `category_ids` | array | No | Filter to demand for products in any of these category ids (from list_categories), e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `tag_operator` | string ("and" | "or") | No | Match prompts carrying any (or, default) or all (and) of tag_ids. |
| `country_codes` | array | No | Filter to chats from these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. Omit for all countries. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. Omit for all engines. |
| `previous_end_date` | string | No | End of the explicit comparison window; provide with previous_start_date or omit both to auto-derive. |
| `previous_start_date` | string | No | Start of an explicit comparison window for deltas; provide with previous_end_date or omit both to auto-derive. |

### List shopping performance

**Slug:** `PEEC_AI_MCP_LIST_SHOPPING_PERFORMANCE`

List products or categories (via entity_type) ranked by shopping performance over a date range. Performance columns are visibility, win_rate, avg_position, and appearances, each with a period-over-period delta. order_by picks the ranking metric (visibility, win_rate, or appearances); mode selects which entities to surface: top = highest now, trending = biggest gainers vs the previous period, losing = biggest decliners.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mode` | string ("top" | "trending" | "losing") | No | Which entities to surface: top = highest now, trending = biggest gainers, losing = biggest decliners vs the previous period. |
| `limit` | number | No | Maximum number of results to return, e.g. 20. |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip for pagination, e.g. 0. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `tag_ids` | array | No | Filter to prompts carrying these tag ids (from list_tags; combine with tag_operator), e.g. ["tg_a1b2c3"]. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `order_by` | string ("visibility" | "win_rate" | "appearances") | No | Metric to rank by. |
| `brand_ids` | array | No | For entity_type=product, filter to products of these global_brand_id values (from list_global_brands), e.g. ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]. |
| `direction` | string ("asc" | "desc") | No | Sort direction. |
| `topic_ids` | array | No | Filter to prompts under any of these topic ids (from list_topics), e.g. ["to_a1b2c3"]. |
| `chat_scope` | string ("all" | "shopping") | No | Which chats the visibility denominator counts: every in-scope chat ('all') or only product-gallery ('shopping') chats. Defaults to 'shopping'. |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `category_id` | string | No | For entity_type=category, return only this category (from list_categories). |
| `entity_type` | string ("product" | "category") | Yes | Rank individual products or whole categories. |
| `product_ids` | array | No | For entity_type=product, restrict the ranking to these product ids (from list_products), e.g. ["b2c3d4e5-f6a7-8901-bcde-f23456789012"]. |
| `category_ids` | array | No | Filter to products in any of these category ids (from list_categories); selecting a parent also matches its descendants. e.g. ["3f2504e0-4f89-41d3-9a0c-0305e82c3301"]. |
| `merchant_ids` | array | No | Filter to products sold through any of these merchant ids (from list_merchants), e.g. ["7c9e6679-7425-40de-944b-e07fc1f90ae7"]. |
| `tag_operator` | string ("and" | "or") | No | Match prompts carrying any (or, default) or all (and) of tag_ids. |
| `country_codes` | array | No | Filter to chats from these countries (ISO 3166-1 alpha-2), e.g. ["US", "GB"]. Omit for all countries. |
| `model_channel_ids` | array | No | Filter to these model channel ids (from list_model_channels), e.g. ["openai-0"]. Omit for all engines. |
| `previous_end_date` | string | No | End of the explicit comparison window; provide with previous_start_date or omit both to auto-derive. |
| `previous_start_date` | string | No | Start of an explicit comparison window for deltas; provide with previous_end_date or omit both to auto-derive. |

### List shopping queries

**Slug:** `PEEC_AI_MCP_LIST_SHOPPING_QUERIES`

List the product/shopping queries an AI engine fanned out to while answering prompts in a project over a date range. Each row represents one shopping sub-query and the distinct products returned for it in a given chat.

Filters:
- prompt_id: only queries from chats produced by this prompt
- chat_id: only queries from this chat
- model_id: only queries from this AI engine (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-3.1-flash-lite-search, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, grok-4.6, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper, mistral-small-4, mistral-medium-3-5, muse-spark-1.2, naver-ai-briefing-scraper)
- model_channel_id: only queries from this channel (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, google-4, anthropic-0, anthropic-1, deepseek-0, meta-0, meta-1, xai-0, xai-1, microsoft-0, amazon-0, mistral-0, mistral-1, naver-0)
- topic_id: only queries from chats whose prompt belongs to this topic
- tag_id: only queries from chats whose prompt carries this tag

Use get_chat with a returned chat_id to inspect the full AI response that produced these sub-queries.

Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: prompt_id, chat_id, model_id, model_channel_id, date, query_text, products (array of product names).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `tag_id` | string | No | Filter to queries from chats whose prompt carries this tag |
| `chat_id` | string | No | Filter to queries from this chat |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `end_date` | string | Yes | End date in ISO format (YYYY-MM-DD) |
| `model_id` | string ("chatgpt-scraper" | "gpt-4o" | "gpt-4o-search" | "gpt-3.5-turbo" | "llama-sonar" | "perplexity-scraper" | "sonar" | "gemini-2.5-flash" | "gemini-3.1-flash-lite-search" | "gemini-scraper" | "google-ai-overview-scraper" | "google-ai-mode-scraper" | "llama-3.3-70b-instruct" | "deepseek-r1" | "deepseek-v4-pro" | "claude-3.5-haiku" | "claude-haiku-4.5" | "claude-sonnet-4" | "grok-scraper" | "microsoft-copilot-scraper" | "grok-4" | "grok-4.3" | "grok-4.6" | "qwen-3-6-plus" | "qwen-3-7-plus" | "amazon-rufus-scraper" | "mistral-small-4" | "mistral-medium-3-5" | "muse-spark-1.2" | "naver-ai-briefing-scraper") | No | Filter to queries from this AI engine |
| `topic_id` | string | No | Filter to queries from chats whose prompt belongs to this topic |
| `prompt_id` | string | No | Filter to queries from chats produced by this prompt |
| `project_id` | string | Yes | The project ID |
| `start_date` | string | Yes | Start date in ISO format (YYYY-MM-DD) |
| `model_channel_id` | string ("openai-0" | "openai-1" | "qwen-0" | "openai-2" | "perplexity-0" | "perplexity-1" | "google-0" | "google-1" | "google-2" | "google-3" | "google-4" | "anthropic-0" | "anthropic-1" | "deepseek-0" | "meta-0" | "meta-1" | "xai-0" | "xai-1" | "microsoft-0" | "amazon-0" | "mistral-0" | "mistral-1" | "naver-0") | No | Filter to queries from this model channel |

### List tag groups

**Slug:** `PEEC_AI_MCP_LIST_TAG_GROUPS`

List the user-defined tag groups in a project, each with its shared color and tag count. Use this to discover group names before filtering list_tags by group or before renaming/deleting a group. System groups (branding/intentType) are not included — they appear on tags via list_tags.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List tags

**Slug:** `PEEC_AI_MCP_LIST_TAGS`

List tags in a project. Tags are cross-cutting labels that can be assigned to any prompt. Use this tool to resolve tag names to IDs before filtering (tag_id filter/dimension, list_prompts), and to label tag IDs from report output with their human-readable names before presenting results. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, is_system, group. The `group` column holds a system group for system tags or the user-defined group name for user tags (null if ungrouped). Pass `group` to filter to a single user-defined group; use list_tag_groups to discover group names. System tags (is_system=true) are maintained by Peec and auto-assigned to every prompt by its branding/intent classification — they cannot be created, edited, or deleted, but they can be assigned to prompts. A system tag's group is "branding" (branded / non-branded) or "intentType" (informational / commercial / transactional); these are mutually exclusive: a prompt carries exactly one tag per group, so assigning a system tag (via update_prompts) overwrites whichever tag that prompt already had in that group rather than adding a second. Filter on the existing system tags — never create your own branded/intent tags.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `group` | string | No | Filter to tags in this user-defined group (exact match on the group name). |
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### List topics

**Slug:** `PEEC_AI_MCP_LIST_TOPICS`

List topics in a project. Topics are folder-like groupings — each prompt belongs to exactly one topic. Use this tool to resolve topic names to IDs before filtering (topic_id filter/dimension, list_prompts), and to label topic IDs from report output with their human-readable names before presenting results. Returns the project's confirmed topics; pass include_suggested=true to also return topics still awaiting confirmation, which carry no prompts yet. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, is_suggested (boolean — always false unless include_suggested is set).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `include_suggested` | boolean | No | Also return topics awaiting confirmation, alongside the confirmed ones. |

### List url classifications

**Slug:** `PEEC_AI_MCP_LIST_URL_CLASSIFICATIONS`

List the custom URL classifications defined for a project. These complement the built-in classifications (Homepage, Category Page, Product Page, Listicle, Comparison, Profile, Alternative, Discussion, How-To Guide, Article, Other) and can be assigned to URLs via assign_url_classification. Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: name, color.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | number | No | Maximum number of results to return |
| `fields` | array | No | Subset of columns to return (default: all). Request only what you need (e.g. ["id","name"]) to save tokens. |
| `offset` | number | No | Number of results to skip |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Read doc

**Slug:** `PEEC_AI_MCP_READ_DOC`

Read the full markdown content of a single Peec documentation page. Pass the path returned by search_docs (e.g. 'metrics/brand-metrics/visibility'). Answer strictly from the returned content — never invent product behaviour, metric definitions, limits, or feature claims. Returns {title, path, url, content, videos}, where videos lists the page's YouTube explainer videos as {videoId, title} (pages may also carry [Watch "…"](https://youtu.be/…) links in the content). When a returned video is relevant to what the user asked — a walkthrough or explainer of the feature, metric, or workflow in question — PREFER embedding it over linking to it or describing it in text: put [youtube:<videoId>] on its own line at the point in your reply where the video belongs, and it renders inline. Copy the videoId exactly from this tool's result — never guess or invent one, and never embed a video that did not come from this tool. Don't embed the same video twice, don't pad the answer with tangential videos, and skip embedding only when no returned video is relevant. No project context or auth required.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | Yes | Page path from a search_docs result, e.g. 'understanding-sources' or 'metrics/brand-metrics/visibility'. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |

### Search docs

**Slug:** `PEEC_AI_MCP_SEARCH_DOCS`

Search the Peec docs (https://docs.peec.ai) and return the most relevant pages. The docs carry two kinds of content: product documentation (what a metric means, how a feature behaves, setup steps, plan/credit rules, 'how do I…') and Peec's published GEO/AI-search research under research/ (citation benchmarks, query fan-outs, engine behaviour, content strategy — each page names and links the original public study it republishes). Use this for product questions and for AI-search/GEO strategy questions alike, then call read_doc on the best result to read it in full before answering. Returns columnar JSON {columns, rows, rowCount}. Columns: title, path, url, snippet. Pass the returned path to read_doc. No project context or auth required.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Maximum number of pages to return. Defaults to 5. |
| `query` | string | Yes | Search terms, e.g. 'share of voice' or 'set up prompts'. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |

### Set brand social channels

**Slug:** `PEEC_AI_MCP_SET_BRAND_SOCIAL_CHANNELS`

Replace the complete social-channel list for up to 50 brands. Each channel is supplied as its platform and full profile or community URL. Existing channels omitted for a brand are removed; an empty channel list removes all of that brand's channels. Confirm the exact replacement lists with the user before calling. Returns an updated count plus skipped or rejected brands.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brands` | array | Yes | Up to 50 brands (target 40 or fewer per call) |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Set project profile

**Slug:** `PEEC_AI_MCP_SET_PROJECT_PROFILE`

Set the project's own brand-identity profile — its industry, target audience, tone, and similar descriptive fields. This is the project's own identity, not the competitor/monitored brand entities managed by update_brand/update_brands. All fields are required — the whole profile is overwritten, so first call get_project_profile, merge your changes into the existing values, then send the complete profile here. Saving triggers a background refresh of prompt suggestions. Confirm changes with the user before calling. Audience distribution percentages must sum to 100. The project's display name is not part of the profile and cannot be changed via this tool. Returns { success: true } once the profile is saved.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `industry` | string | Yes | The industry the brand operates in, e.g. 'SaaS'. |
| `personas` | array | No | The audience segments prompts are written from the perspective of, e.g. ["Marketing lead","Procurement manager"]. Omit to leave the project's existing personas untouched. |
| `occupation` | string | Yes | The role or perspective the profile speaks from, e.g. 'Founder'. |
| `project_id` | string | Yes | The project ID |
| `targetMarkets` | array | No | Deprecated camelCase alias of target_markets, e.g. [{"marketSize":"National","location":"United States"}]. |
| `prompt_builder` | object | No | Prompt generation settings. |
| `target_markets` | array | No | The geographic markets the brand targets, e.g. [{"market_size":"National","location":"United States"}]. |
| `brandPresentation` | array | No | Deprecated camelCase alias of brand_presentation, e.g. ["Premium eco-friendly cleaning products"]. |
| `brand_presentation` | array | No | Short statements describing how the brand presents itself, e.g. ["Premium eco-friendly cleaning products"]. |
| `productsAndServices` | array | No | Deprecated camelCase alias of products_and_services, e.g. ["CRM software","Analytics dashboard"]. |
| `audienceDistribution` | object | No | Deprecated camelCase alias of audience_distribution (must sum to 100), e.g. {"simpleRecommendationSeeker":40,"informedShopper":40,"evaluativeResearcher":20}. |
| `audience_distribution` | object | No | Percentage split of the audience across buyer types (must sum to 100), e.g. {"simple_recommendation_seeker":40,"informed_shopper":40,"evaluative_researcher":20}. |
| `products_and_services` | array | No | The products and services the brand offers, e.g. ["CRM software","Analytics dashboard"]. |

### Unarchive prompts

**Slug:** `PEEC_AI_MCP_UNARCHIVE_PROMPTS`

Unarchive up to 50 prompts (sets is_archived = false), reactivating them so they resume running. Subject to the project's active-prompt plan limit — prompts that would exceed it are rejected. Returns a count of reactivated prompts plus any skipped (not found) or rejected (plan_limit) items. Confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `prompt_ids` | array | Yes | Up to 50 prompt IDs to unarchive (target 40 or fewer per call), e.g. ["pr_a1b2c3"]. |

### Unassign domain classification

**Slug:** `PEEC_AI_MCP_UNASSIGN_DOMAIN_CLASSIFICATION`

Clear the classification override on a whole domain (operates on a domain, e.g. "example.com" — for a single URL use unassign_url_classification), restoring the heuristic classification. Does NOT delete the custom classification entity itself (use delete_domain_classification for that). Confirm with the user before calling. Returns { success: true } once the override is cleared.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `domain` | string | Yes | Domain whose classification override should be cleared |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Unassign url classification

**Slug:** `PEEC_AI_MCP_UNASSIGN_URL_CLASSIFICATION`

Clear the classification override on a single URL (operates on one full URL — for a whole domain use unassign_domain_classification), restoring the heuristic classification. Does NOT delete the custom classification entity itself (use delete_url_classification for that). Confirm with the user before calling. Returns { success: true } once the override is cleared.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | URL whose classification override should be cleared |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Update brands

**Slug:** `PEEC_AI_MCP_UPDATE_BRANDS`

Update up to 50 brands in one call (name, regex, aliases, domains, color per item). Before changing aliases, read the brand's regex and aliases with list_brands. A nonempty regex is the active Advanced RegEx and overrides name and alias matching. Explain that aliases will not affect matching while it is active and ask whether the user wants to clear or change it before including regex in an update, unless they already requested that specific change. Approval to update aliases does not authorize clearing RegEx. Changes to name/regex/aliases trigger background metric recalculation per brand; updates during an ongoing recalculation are rejected. Read the saved settings back before confirming success, and do not claim Visibility changed without reading the resulting metrics. Returns a count of updated brands plus any skipped/rejected items (successful items are not echoed back).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `brands` | array | Yes | Up to 50 brands to update (target 40 or fewer per call), e.g. [{"brand_id":"br_a1b2c3","name":"Acme"}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Update categories

**Slug:** `PEEC_AI_MCP_UPDATE_CATEGORIES`

Rename and/or move categories by id (use list_categories to resolve ids). Each item sets name (rename it), parent_id (move it under that category, or null to promote it to the top level), or both.

Returns per-item results { updated, rejected } — the batch never fails as a whole. Rejection reasons: not_found, name_conflict (a sibling already has that name), invalid_move (would nest a category inside itself).

Confirm with the user before calling — this mutates project data.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `categories` | array | Yes | Category updates. Each needs a category_id (from list_categories) plus name (rename) and/or parent_id (move under that category, or null to promote to the top level). e.g. [{"category_id":"3f2504e0-4f89-41d3-9a0c-0305e82c3301","name":"Sneakers"}]. |
| `project_id` | string | Yes | The project ID |

### Update products

**Slug:** `PEEC_AI_MCP_UPDATE_PRODUCTS`

Update up to 1000 products by id in one call (use list_products to resolve ids). Per item, set any of: name (unique within the brand), description, image_url, price_override (per-currency; replaces all overrides, {} clears them), category_ids (replaces the product's categories, [] uncategorizes it; use list_categories to find category ids). Omitted fields are left unchanged.

Returns per-item results { updated, skipped, rejected } — the batch never fails as a whole. An item with nothing to apply is skipped (not_found / no_changes / duplicate_id); an unsatisfiable change (name conflict, unknown category) is rejected.

Confirm with the user before calling — this mutates project data.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `products` | array | Yes | Product updates (1–1000). Each needs a product_id (from list_products) plus any of name, description, image_url, price_override, category_ids; omitted fields stay unchanged. e.g. [{"product_id":"b2c3d4e5-f6a7-8901-bcde-f23456789012","name":"Air Zoom Pegasus 41"}]. |
| `project_id` | string | Yes | The project ID |

### Update prompts

**Slug:** `PEEC_AI_MCP_UPDATE_PROMPTS`

Update the topic, tags, location and/or fact-checking selection of up to 50 prompts in one call. Per item, pass tag_ids to fully replace the prompt's tag set, topic_id = null to detach its topic, country_code to move the prompt to another market while keeping the prompt and its chat history, or is_fact_checking_enabled to enable or disable fact-checking. Topic and tag ids must already exist. Moving a prompt to a market the project does not already track takes one of the plan's location slots, unless the prompts leaving their old market in this call are the last ones tracking it, in which case that slot pays for the move; items that would leave the project over the limit come back rejected with reason "limit_exceeded". Returns a count of updated prompts plus any skipped/rejected items (successful items are not echoed back).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `prompts` | array | Yes | Up to 50 prompts to update (target 40 or fewer per call), e.g. [{"prompt_id":"pr_a1b2c3","topic_id":"to_a1b2c3","tag_ids":["tg_a1b2c3"],"country_code":"US"}]. |
| `project_id` | string | Yes | The project ID |

### Update prompt suggestions

**Slug:** `PEEC_AI_MCP_UPDATE_PROMPT_SUGGESTIONS`

Edit up to 50 pending prompt suggestions from list_prompt_suggestions before finalizing: rewrite the text and/or replace the tag set per item. Prefer this over rejecting and regenerating when a suggestion is close but not quite right. Editing keeps a suggestion pending — accept it with finalize_prompt_suggestions to create the tracked prompt. tag_ids fully replaces the item's tags and must be existing tag ids from list_tags; system tags keep at most one per group, the last one passed wins. Returns a count of updated suggestions plus any skipped/rejected items (successful items are not echoed back).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
| `suggestions` | array | Yes | Up to 50 suggestions to edit (target 40 or fewer per call), e.g. [{"prompt_suggestion_id":"pr_a1b2c3","text":"Best CRM for small teams"}]. |

### Update tag groups

**Slug:** `PEEC_AI_MCP_UPDATE_TAG_GROUPS`

Rename and/or recolor up to 50 user-defined tag groups in one call. The change applies to every tag in each group. Per group, provide a new name, a new color, or both (a group with neither is rejected). Returns each updated group with its affected tag count, plus any skipped (not found) or rejected items. Confirm with the user before calling.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `groups` | array | Yes | Up to 50 tag groups to update (target 40 or fewer per call), e.g. [{"group":"Segment","name":"Segments","color":"blue"}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Update tags

**Slug:** `PEEC_AI_MCP_UPDATE_TAGS`

Update up to 50 tags in one call (name, color, and/or group per item). Set a tag's group to move it into a group (inherits the group's color), `null` to ungroup, or omit to leave unchanged. Returns a count of updated tags plus any skipped/rejected items (successful items are not echoed back); system tags are skipped.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | array | Yes | Up to 50 tags to update (target 40 or fewer per call), e.g. [{"tag_id":"tg_a1b2c3","name":"Enterprise"}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |

### Update topics

**Slug:** `PEEC_AI_MCP_UPDATE_TOPICS`

Rename up to 50 topics in one call. Returns a count of updated topics plus any skipped/rejected items (successful items are not echoed back).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topics` | array | Yes | Up to 50 topics to update (target 40 or fewer per call), e.g. [{"topic_id":"to_a1b2c3","name":"CRM software"}]. |
| `context` | string | Yes | Describe the user's underlying goal in one sentence — not the tool you are calling. |
| `project_id` | string | Yes | The project ID |
