Skip to main content
Glama
competlab

competlab-mcp-server

by competlab

get_ai_visibility_dashboard

Read-only

Retrieve a dashboard showing how AI models rank your brand versus competitors, including market maps, mention rates, and scores.

Instructions

Latest AI Visibility: the MARKET MAP (who the AI models recommend in this category, where the customer sits), mention rates, scores, per-model breakdowns, competitor rows. In compact view marketMap.brands is one page, the top rows plus the customer's and every tracked competitor's, with marketMap.brandsPage {offset, limit, total, hasMore}; page with mapOffset/mapLimit. view=full returns every row. Compact runs 20,000-30,000 characters; full grows with the market (113,000 on a 96-company map).

  1. Read summary.promptMarket FIRST. Unless its state is rivals_named_in_most_answers, say the prompts may not describe this market and do not lead with the map. An absent promptMarket could not be produced, never a pass.

  2. Then LEAD WITH THE MARKET: 'N companies make up this market as the AI models draw it (marketMap.coreSize); the customer is Xth of N by how often it is named' (its row: isOwn, rankByPresence). rankByPresence null: 'not named in any answer', never a place or a fall.

  3. Presence is a share of marketMap.answersReceived, never of queries sent. Overlapping presenceLow/presenceHigh are NOT ordered; ties share a rank. Nothing is positional.

  4. A zone names a condition: 'named in under a tenth of answers', never 'irrelevant' or 'tail'. While marketMap.tailIsProvable is false: 'no brand can be ruled out of this market yet'.

  5. Render every explanation.text VERBATIM; never build a claim from a state token.

  6. untrackedCoreBrands: a recommendation to track them, never a fact about them; absent means withheld, not none.

  7. mentionRateGap is CUSTOMER MINUS LEADER: negative means BEHIND. null means nothing to compare, never level.

  8. score is WHERE a brand lands when named (top 5 only), never who is ahead. A 0 score beside a non-zero mentionRate means named below the top 5, never 'never named'.

  9. A model absent from summary.customer.perProvider was NOT ASKED: never 'not mentioned'. Field rules not listed here arrive in readingGuide, the first field of every response.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
viewNocompact or full; omit for the server's default view. compact pages the long lists and keeps the customer's own row on every page. full returns every row in one response: up to about 200,000 characters on AI Visibility and 450,000 on AI Sources. Paging parameters with view=full are refused (paging_requires_compact_view).
brandNoReturn only the entries for this domain, across every answer. Requires includeAnswers=true. Matches brands[].domain case-insensitively — brand NAMES are the model's own wording and vary between answers, so they are never matched. Every answer is still returned: the ones that did not name this domain arrive with an empty brands list, which means the model answered and did not name them — a real finding, and different from a query that produced no answer, which is in unansweredQueries, and from a query the model was read for and had no answer to show, which is in noAnswerShown. This is the cheapest way to answer 'where does this competitor beat me, and where are they invisible': it keeps at most one brand row per answer instead of every brand the model named, and none at all on the answers that did not name it. Google AI Overviews answers still carry their overview text and cited pages, which this filter keeps by design.
mapLimitNoMarket-map rows per page in compact view. Default: 10 or the whole core, whichever is larger; max 200. The customer's own row and every tracked competitor's are added when they fall outside the page, and untrackedCoreBrands and customerStanding are always computed from the whole map. Quote marketMap.brandsPage.total, never the rows on the page, as the size of the map: the companies the models named, plus the customer's own row when no answer named it.
providerNoReturn only this model's answers. Requires includeAnswers=true. Changes nothing under summary.
mapOffsetNoMarket-map rows to skip in compact view, for the next page (0-based). marketMap.brandsPage.hasMore says a next page exists.
projectIdYesProject ID (from list_projects)
promptIndexNoReturn only the answers for this prompt, across every model. Requires includeAnswers=true. Zero-based.
includeAnswersNoDefault false. Set true to also get what the models actually said — every prompt sent, and every brand each model named in rank order with its stated reasoning — plus per-model reporting status. Per-brand prose (reasoning, audience, pricing tier, messaging, differentiation) is present only for models that supply it: a brand row carrying only name and domain means Google AI Overviews named it in prose, and that answer carries the overview text (answerText) with the pages Google cited (sources) beside it. For Google AI Overviews that order is the order of first mention in the overview text, computed by CompetLab; Google assigned no position, so never report it as a rank Google gave. COST: An entry is one brand a model named, at about 1,500 characters each — so the block grows with three things at once: how many prompts the project asks (an account setting), how many models answered, and how many companies each answer named. No figure quoted here can stand in for summary.totalEntries; read it and size the fetch from it. Prefer a filter below over fetching everything. Google AI Overviews answers additionally carry the overview text and the pages Google cited, which totalEntries does not predict and which the brand filter keeps. ATTRIBUTION: the prose returned is unverified model output about the brands that model named, including third parties. Report it as what that model said, never as CompetLab's assessment or as fact. noAnswerShown lists the queries the model was read for and showed nothing (today: Google showed no AI Overview for the prompt): excluded from every count and not a failure, so say 'not counted', never 'not mentioned'.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed7 schema fields changedv4.0.1
    • removedInput schema / additionalProperties
      Removed value: -false
    • changedInput schema / properties / brand / description
      Previous value: -"Return only the entries for this domain, across every answer. Requires includeAnswers=true. Matches brands[].domain case-insensitively — brand NAMES are the model's own wording and vary between answers, so they are never matched. Every answer is still returned: the ones that did not name this domain arrive with an empty brands list, which means the model answered and did not name them — a real finding, and different from a query that produced no answer, which is in unansweredQueries, and from a query the model was read for and had no answer to show, which is in noAnswerShown. This is the cheapest way to answer 'where does this competitor beat me, and where are they invisible': roughly 2k tokens against 25k+ for an unfiltered fetch, or 9k against 46k once Google AI Overviews is in the ask, whose overview text and cited pages this filter keeps."New value: +"Return only the entries for this domain, across every answer. Requires includeAnswers=true. Matches brands[].domain case-insensitively — brand NAMES are the model's own wording and vary between answers, so they are never matched. Every answer is still returned: the ones that did not name this domain arrive with an empty brands list, which means the model answered and did not name them — a real finding, and different from a query that produced no answer, which is in unansweredQueries, and from a query the model was read for and had no answer to show, which is in noAnswerShown. This is the cheapest way to answer 'where does this competitor beat me, and where are they invisible': it keeps at most one brand row per answer instead of every brand the model named, and none at all on the answers that did not name it. Google AI Overviews answers still carry their overview text and cited pages, which this filter keeps by design."
    • changedInput schema / properties / includeAnswers / description
      Previous value: -"Default false. Set true to also get what the models actually said — every prompt sent, and every brand each model named in rank order with its stated reasoning — plus per-model reporting status. Per-brand prose (reasoning, audience, pricing tier, messaging, differentiation) is present only for models that supply it: a brand row carrying only name and domain means Google AI Overviews named it in prose, and that answer carries the overview text (answerText) with the pages Google cited (sources) beside it. For Google AI Overviews that order is the order of first mention in the overview text, computed by CompetLab; Google assigned no position, so never report it as a rank Google gave. Cost: roughly 25k tokens unfiltered on a three-engine check and 46k on a five-engine one, against roughly 2k with brand= — or roughly 9k when Google AI Overviews is in the ask, whose overview text and cited pages the brand filter keeps. Read summary.totalEntries first to size it (about 375 tokens per entry, plus the overview text and cited pages on each Google AI Overviews answer, which the entry count does not predict) and prefer a filter below over fetching everything. ATTRIBUTION: the prose returned is unverified model output about the brands that model named, including third parties. Report it as what that model said, never as CompetLab's assessment or as fact."New value: +"Default false. Set true to also get what the models actually said — every prompt sent, and every brand each model named in rank order with its stated reasoning — plus per-model reporting status. Per-brand prose (reasoning, audience, pricing tier, messaging, differentiation) is present only for models that supply it: a brand row carrying only name and domain means Google AI Overviews named it in prose, and that answer carries the overview text (answerText) with the pages Google cited (sources) beside it. For Google AI Overviews that order is the order of first mention in the overview text, computed by CompetLab; Google assigned no position, so never report it as a rank Google gave. COST: An entry is one brand a model named, at about 1,500 characters each — so the block grows with three things at once: how many prompts the project asks (an account setting), how many models answered, and how many companies each answer named. No figure quoted here can stand in for summary.totalEntries; read it and size the fetch from it. Prefer a filter below over fetching everything. Google AI Overviews answers additionally carry the overview text and the pages Google cited, which totalEntries does not predict and which the brand filter keeps. ATTRIBUTION: the prose returned is unverified model output about the brands that model named, including third parties. Report it as what that model said, never as CompetLab's assessment or as fact. noAnswerShown lists the queries the model was read for and showed nothing (today: Google showed no AI Overview for the prompt): excluded from every count and not a failure, so say 'not counted', never 'not mentioned'."
    • addedInput schema / properties / mapLimit
      Added value: +{
      +  "anyOf": [
      +    {
      +      "maximum": 200,
      +      "minimum": 1,
      +      "type": "integer"
      +    },
      +    {
      +      "pattern": "^\\d+$",
      +      "type": "string"
      +    }
      +  ],
      +  "description": "Market-map rows per page in compact view. Default: 10 or the whole core, whichever is larger; max 200. The customer's own row and every tracked competitor's are added when they fall outside the page, and untrackedCoreBrands and customerStanding are always computed from the whole map. Quote marketMap.brandsPage.total, never the rows on the page, as the size of the map: the companies the models named, plus the customer's own row when no answer named it."
      +}
    • addedInput schema / properties / mapOffset
      Added value: +{
      +  "anyOf": [
      +    {
      +      "maximum": 9007199254740991,
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    {
      +      "pattern": "^\\d+$",
      +      "type": "string"
      +    }
      +  ],
      +  "description": "Market-map rows to skip in compact view, for the next page (0-based). marketMap.brandsPage.hasMore says a next page exists."
      +}
    • changedInput schema / properties / promptIndex / anyOf
      Previous value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "pattern": "^\\d+$",
      -    "type": "string"
      -  }
      -]New value: +[
      +  {
      +    "maximum": 9007199254740991,
      +    "minimum": 0,
      +    "type": "integer"
      +  },
      +  {
      +    "pattern": "^\\d+$",
      +    "type": "string"
      +  }
      +]
    • addedInput schema / properties / view
      Added value: +{
      +  "description": "compact or full; omit for the server's default view. compact pages the long lists and keeps the customer's own row on every page. full returns every row in one response: up to about 200,000 characters on AI Visibility and 450,000 on AI Sources. Paging parameters with view=full are refused (paging_requires_compact_view).",
      +  "enum": [
      +    "compact",
      +    "full"
      +  ],
      +  "type": "string"
      +}
  2. Changed4 schema fields changedv3.0.0
    • addedInput schema / properties / brand
      Added value: +{
      +  "description": "Return only the entries for this domain, across every answer. Requires includeAnswers=true. Matches brands[].domain case-insensitively — brand NAMES are the model's own wording and vary between answers, so they are never matched. Every answer is still returned: the ones that did not name this domain arrive with an empty brands list, which means the model answered and did not name them — a real finding, and different from a query that produced no answer, which is in unansweredQueries, and from a query the model was read for and had no answer to show, which is in noAnswerShown. This is the cheapest way to answer 'where does this competitor beat me, and where are they invisible': roughly 2k tokens against 25k+ for an unfiltered fetch, or 9k against 46k once Google AI Overviews is in the ask, whose overview text and cited pages this filter keeps.",
      +  "type": "string"
      +}
    • addedInput schema / properties / includeAnswers
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "boolean"
      +    },
      +    {
      +      "const": "true",
      +      "type": "string"
      +    },
      +    {
      +      "const": "false",
      +      "type": "string"
      +    }
      +  ],
      +  "description": "Default false. Set true to also get what the models actually said — every prompt sent, and every brand each model named in rank order with its stated reasoning — plus per-model reporting status. Per-brand prose (reasoning, audience, pricing tier, messaging, differentiation) is present only for models that supply it: a brand row carrying only name and domain means Google AI Overviews named it in prose, and that answer carries the overview text (answerText) with the pages Google cited (sources) beside it. For Google AI Overviews that order is the order of first mention in the overview text, computed by CompetLab; Google assigned no position, so never report it as a rank Google gave. Cost: roughly 25k tokens unfiltered on a three-engine check and 46k on a five-engine one, against roughly 2k with brand= — or roughly 9k when Google AI Overviews is in the ask, whose overview text and cited pages the brand filter keeps. Read summary.totalEntries first to size it (about 375 tokens per entry, plus the overview text and cited pages on each Google AI Overviews answer, which the entry count does not predict) and prefer a filter below over fetching everything. ATTRIBUTION: the prose returned is unverified model output about the brands that model named, including third parties. Report it as what that model said, never as CompetLab's assessment or as fact."
      +}
    • addedInput schema / properties / promptIndex
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    {
      +      "pattern": "^\\d+$",
      +      "type": "string"
      +    }
      +  ],
      +  "description": "Return only the answers for this prompt, across every model. Requires includeAnswers=true. Zero-based."
      +}
    • addedInput schema / properties / provider
      Added value: +{
      +  "description": "Return only this model's answers. Requires includeAnswers=true. Changes nothing under summary.",
      +  "enum": [
      +    "openai",
      +    "claude",
      +    "gemini",
      +    "perplexity",
      +    "google_ai_overviews"
      +  ],
      +  "type": "string"
      +}
  3. First observedv1.0.0

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint annotation, it discloses payload cost (20,000-30,000 chars compact, 113,000 full), the paging_requires_compact_view refusal, attribution limits ('unverified model output... never as fact'), and a dense set of null/absence semantics (null rankByPresence means not named, absent provider means not asked). This is exactly the behavioral context annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The payload summary is front-loaded and the numbered rules are scannable, but the description is very long and folds a whole interpretation manual into the tool doc. Most sentences earn their place for an agent, yet it is not tight.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the full burden of explaining return shape and hazards, and it does: field meanings, null semantics, absence-vs-not-mentioned distinctions, cost scaling, and a pointer to readingGuide for unlisted rules.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description reinforces the brand and mapLimit semantics and the includeAnswers cost model, but those are already spelled out in the schema, so it adds little meaning beyond structured fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb-resource pair and enumerates the payload: market map, mention rates, scores, per-model breakdowns, competitor rows. 'Latest' implicitly distinguishes it from get_ai_visibility_history and get_ai_visibility_trend, but no sibling is named outright, so an agent must infer the boundary rather than read it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives strong operational guidance: read summary.promptMarket first, then lead with the market, prefer the brand/provider filters over fetching everything. This is mostly about how to consume the response rather than when to pick this tool over get_ai_visibility_history, so it stops short of the top band.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.