Skip to main content
AI Growth Agent helps companies get recommended in AI search — Google AI Overviews, Google organic results, and ChatGPT — through autonomous programmatic content generation. This page defines the domain model behind every MCP tool, so you know what the numbers mean before you ask your client for them.

The search universe

Your search universe is the set of seed terms your account tracks in the content planner. Each seed term represents a topical cluster, and each cluster contains queries — long-tail searches your ideal customers actually make, derived from real search evidence. The API and some tools call queries “questions”. content_planner_list_seed_terms lists the seed terms in your universe; content_planner_list_questions lists the queries (API: questions) under one seed term. The tracked universe is the campaign and standard tiers — the scope every reporting_* figure uses, and the planner’s default. Backlog terms sit outside it. See campaigns, standard, backlog, and archived.

Citations and brand mentions

Two atomic units measure performance:
  • Citation — a URL used as a source. Google has two kinds: AI Overview citations (a URL used as a source in the AI Overview) and organic citations (the traditional blue-link ranking). ChatGPT citations are URLs used as sources in ChatGPT responses.
  • Brand mention — your brand is named in a Google AI Overview or a ChatGPT response.
These map to four modes, two per channel: Pass a mode as the mode parameter on the content planner tools.

Which tools cover which channel

Do not mix channels when reading figures. Each tool family covers one side:
  • All reporting_* tools return Google data only — traditional organic rankings and Google AI Overview citations/mentions. “AI” anywhere in reporting output means Google AI Overview, never ChatGPT. The account overview payload says so explicitly: sourceChannel: "google".
  • ChatGPT data comes from the content planner — pass mode: "chatgpt-ranking" (citations) or mode: "chatgpt-mentions" (brand mentions) to content_planner_list_seed_terms or content_planner_list_questions. Per-query ChatGPT responses are available via search_intelligence_get_serp with type: "chatgpt".
  • The content planner returns an aggregate view per mode — every planner response includes domainCounts (the most-cited domains) and brandCounts (the most-mentioned brands) computed for the selected mode, over exactly the seed terms that call returned. Both are a top-50 list, and each count is the number of queries where that domain or brand appears — at most once per query — not a citation total. This makes the ChatGPT competitive mix answerable in one call: content_planner_list_seed_terms with a chatgpt-* mode, parallel to reporting_get_top_entities for Google. Read both lists with reading counts and ranks correctly.
  • The gsc_* tools are a different lens entirely — actual Google Search Console performance (clicks, impressions, CTR, position) for your site, not citation tracking. See Search Console performance and the AGA split.
Google is the primary channel — its data is stable, comprehensive, and covers the vast majority of searches, so strategy and execution are prioritized against it. ChatGPT is a secondary signal: it biases heavily toward training data and is highly volatile. Report significant ChatGPT citations or brand mentions when they appear, but never sound alarms over low ChatGPT mention rates — they are the norm, not a problem. Always name the channel when you cite a figure (“in Google AI Overviews, …”, “in ChatGPT, …”), and never combine figures from different channels into one metric.

Aggregate metrics

At the account level, reporting tracks how many searches you monitor, how many of them you rank for or get mentioned in, the resulting rate, and average positions — on Google (organic + AI Overviews). Aggregate views also show the most-cited domains and most-mentioned brands across your universe — reporting_get_top_entities returns these (mode: "ranking" for domains, mode: "mention" for brands), and reporting_get_account_overview bundles them into a one-call briefing. For the ChatGPT competitive mix, use content_planner_list_seed_terms with a chatgpt-* mode — its domainCounts and brandCounts give the same kind of aggregate for ChatGPT. Every one of these figures covers campaign + standard seed terms only — see campaigns, standard, backlog, and archived.

Reading counts and ranks correctly

The entity lists are shaped for reference, not for ranking off array position. Read them like this:
  • The list is a top 10 plus a pinned reference row. reporting_get_top_entities (entities) and reporting_get_account_overview (topCitedDomains, topMentionedBrands) return the top 10 by count, plus your own domain or brand appended as a pinned reference row when it falls outside that top 10. Array position is not a rank, and list length is not a count of competitors. Read rank (1-indexed, null on a pinned row) and isPinnedReference. Report a pinned row as outside the top 10 with its exact rank not returned — never as 11th, and never as last place.
  • A pinned row with count: 0 means not cited or not mentioned at all. Say that, rather than reading it as a low rank.
  • The two overview lists have separate denominators. Every percent in topCitedDomains is against topCitedDomainsTotal (queries with Google search results analyzed); every percent in topMentionedBrands is against topMentionedBrandsTotal (queries whose Google AI Overview named a brand). Quote each count or share against its own total. Never read a mention figure against topCitedDomainsTotal or against universe.totalQuestions. On reporting_get_top_entities, the single total field carries whichever denominator the requested mode selected.
  • The planner’s domainCounts and brandCounts count differently. They are a top-50 list, and each count is the number of queries where that domain or brand appears — at most once per query — not a citation total. Neither list pins an own-entity row or carries an isOwn flag, so your own domain or brand is not necessarily present in them.
  • Query rows carry no lifecycle tier. Tier is a seed-term property — read it from the parent seed term on content_planner_list_seed_terms.

Campaigns, standard, backlog, and archived

Every seed term sits in one of four lifecycle tiers: Your tracked universe is campaign + standard. A backlog term is in consideration only — it has no generated queries and no SERP data — so it is excluded from every reporting_* figure and from the content planner’s default scope. Never count backlog terms in a tracked-universe figure without saying so. content_planner_list_seed_terms returns campaign + standard terms when you omit priorityTier, and echoes the applied scope as tierScope. Pass priorityTier: ["backlog"] to inspect backlog terms, and label that read as a backlog read. On reporting_get_top_entities and reporting_get_account_overview, campaign: false (the default) does not mean “every seed term”: the universe is always campaign + standard. campaign: true narrows it to campaign-tier terms only. Backlog is excluded either way, and the same campaign + standard scope applies to reporting_get_trend, reporting_get_wins, and reporting_get_urls. Because the planner default matches, a universe size from the reporting tools reconciles with a planner count taken at that default scope. Archived is a separate toggle, not a tier filter. showArchived: true returns the archived rows instead of the active ones, never both. Both planner tools echo which set came back as archivedOnly.

Publishing density

Where more content is published against queries, your account gets cited more. Low-density seed terms naturally have lower ranking and mention rates — a weak rate on a term with little published content is a coverage gap, not necessarily a performance problem. Check publishedCount per seed term in content_planner_list_seed_terms before judging a term’s rates.

Competitor views

You can view the same universe from a competitor’s perspective:
  • Pass a competitor domain as referenceUrl on the content planner tools to see which queries that domain ranks for.
  • Pass a competitor brand name as referenceBrand to see which queries that brand gets mentioned for.
On reporting_get_urls, use the search filter (substring on the host) or host filter (exact host, url view only) to drill into a competitor’s citations across your universe.

Search Console performance and the AGA split

The gsc_* tools read your site’s actual organic performance from Google Search Console — clicks, impressions, CTR, and average position — rather than tracking citations in AI answers. This is the ground truth of what the content does once published. Prerequisite: these tools work only when [email protected] is added to your Google Search Console property with Full access (Search Console → Settings → Users and permissions). The tools read through that shared service account; without access they can’t resolve your property. Three things shape how the numbers are assembled:
  • Property resolution. Your account’s publishing URL (the reverse-proxy rewrite when set, otherwise the CMS URL) is matched against the GSC properties the service account can see. A domain property (sc-domain:example.com) covers the whole site including subdomains. When an account only has URL-prefix properties, AGA content may live on a separate property from the main site — for example resources.example.com tracked apart from example.com. Neither property alone is the true picture, so the aggregate tools query every same-domain property and merge the results; a perProperty breakdown in every response shows the composition. Accounts still on the shared placeholder space (*.sites.aigrowthagent.co — a test space for accounts not fully set up, not an official domain) are never matched to a GSC property — never the aigrowthagent.co property, which belongs to AI Growth Agent — and the performance tools refuse to pull until the real domain is set.
  • The AGA split. Aggregate tools separate performance into AGA content vs the main domain — what share of clicks, impressions, and growth comes from AI Growth Agent slugs — by matching GSC page URLs against your published articles’ exact slugs and publishing base. Exact slug matches, not subdirectory prefixes: a subdirectory can mix AGA and non-AGA content, so a prefix match would misstate the split. Each split reports its strategy and confidence; when an account can’t be segmented confidently, the tools say so instead of estimating.
  • Recency lag. GSC finalizes data with a delay, so default date ranges end about 3 days ago and the most recent days are always incomplete. Every performance response lists the affected dates in meta.incompleteDays — only dates inside the real lag window (today−3 … today at call time) are flagged, so historical pulls carry no flags. Never trend on flagged days.
For deep analysis, export the full dataset with gsc_export_dataset — the response includes a pre-signed downloadUrl for the raw file (curl it immediately; the signature expires in ~1 hour and the file lives on the MCP server) — and slice it with gsc_query_export. Compact summaries are the default everywhere else. Before trusting query-level numbers, read GSC data fidelity — Google withholds rare queries, so named queries are a biased floor; query-frame responses carry a queryBiasNote saying so.

Evaluating AGA content performance

The standard reading path, and the stance behind it:
  • Impressions come first. AI traffic doesn’t click, so impressions are the primary signal of how visible your content is to AI. Clicks and CTR describe human traffic only — report them as such, and never present a click decline as an AI-visibility decline. When the AGA split is available, lead with the AGA impression share.
  • Aggregate, then split. Start with the site-wide property-frame trend (gsc_get_overview), then read the AGA-vs-main-domain split: what percent of total impressions AGA content drives, and how the two populations move separately. The overview’s AGA share divides AGA’s page-filtered impressions by the property total, and main is the property total minus AGA. It matches the GSC UI (page filter on the publish path ÷ unfiltered total), so report it as measured. gsc_query_analytics and gsc_export_dataset splits stay same-frame (AGA ÷ the classified page rows).
  • Cadence follows account maturity. The maturity block on gsc_get_context reports how long the account has been publishing. In the first ~8 weeks, read week-over-week (gsc_compare_periods with periodPreset: "wow") to see which pages are picking up. After that, read month-over-month and 3-month-over-3-month ("mom" / "qoq"). Never read a mature account week-over-week.
  • Indexing coverage for new accounts. What percent of published slugs have any impressions? Join articles_search (statuses ["published"]) against a ["page"]-dimension pull or export on the slug. Pages published or updated long ago with persistent zero impressions are retire candidates — retiring them can help the domain over time. Diagnose suspects with gsc_inspect_url and gsc_list_sitemaps.
  • Web Stories are a separate format. AGA ships a Web Story twin per article under a /web-stories/ path, so the same slug appears twice in GSC. tags.isWebStory splits them — analyze Web Story URLs separately from article URLs.
  • Split the AI and human stories. Use tags.queryShape to tell which queries and pages win human vs AI-shaped traffic. Alongside the impressions-first principle, this is the AI-visibility narrative.
  • An article’s impact is not confined to its target query. One article can rank or be cited across many queries — its blast radius. The planner prioritizes queries where the account is not yet ranking or mentioned, so a query the account is winning may have no dedicated article; trace the ranking or cited URL and it is usually an AGA article published against a different query. Highlight articles with disproportionate cross-query impact as wins — they are the corpus’s workhorses, and the top candidates to refresh and to replicate the pattern from.

Reading Search Console data correctly

GSC numbers depend on how they were grouped and retrieved. Every performance response carries a meta block stating the assumptions behind its numbers — read it before quoting a figure.
  • Aggregation frames never reconcile. meta.aggregationFrame labels the frame: property (date/device/country groupings without a page filter — the true site total), page (above the property total — GSC counts one page impression per URL shown, so a search surfacing three of your URLs counts three times; only a little above when searches show one of your URLs, far above with sitelinks and brand results, more than 80% on one measured account), query (named queries only — anonymized queries are excluded), or page_query. The same question answered in two frames gives two different numbers; neither is wrong. Pick one frame per analysis and name it.
  • The page-filter trap. A page filter combined with device, country, query, or any other page-less grouping silently drops to the named-query table, losing roughly half the impressions. The response is labelled query, so queryBiasNote fires. ["page"] or ["date"] alone plus a page filter stays page-level (["date"] is the GSC UI chart with a page filter) and is labelled page.
  • URL matching is exact — inspect the canonical form. The URL Inspection API and the GSC page filter match exact URL strings. The wrong slash variant answers “Page with redirect” or “URL is unknown to Google” for a page that is actually indexed and ranking — and the same wrong-URL cause produces different-looking verdicts. There is no universal slash rule: WordPress canonicalizes to a trailing slash, Next.js and Framer away from it. Match the site’s canonical form. The tools do this for you: slug-resolved URLs are built in canonical form, gsc_inspect_url re-inspects the googleCanonical URL (or the toggled slash variant when Google reports none) on a non-PASS verdict and records what happened in canonicalNote, and gsc_get_page_performance retries the toggled variant when a page returns zero rows.
  • Completeness is measured, not assumed. The API caps each request at 25,000 rows, returned clicks-first — so a capped pull keeps clicks intact but discards the zero-click impression tail. The tools auto-partition capped pulls (by device, then by page-regex) and reconcile the union against an uncapped denominator pulled with the same filters. Check meta.complete and meta.coverageRatio (union ÷ denominator) before any impression-based analysis; click-based analysis survives capping. meta.partitionsUsed shows what was split, and coverageNote warns when coverage falls short.
  • Query data is never the whole story. Named queries cover roughly 52–65% of impressions; the rest are anonymized by Google. On measured properties the anonymized residual converts better than named queries. On ["page", "query"] pulls, meta.anonymization reports named vs anonymized impressions, clicks, and CTR so the hidden share is visible.
  • Row tags pre-classify the data. Query rows carry tags.queryChars and tags.queryShape (human under 30 chars, ambiguous 30–59, ai_shaped 60 and over — a behavioural inference from length, since GSC exposes no AI Overview flag; the fixed threshold deliberately under-counts AI-shaped traffic rather than misclassifying human queries). Page rows carry tags.hostname and tags.pageClass: canonical (your publish base), cms_origin (the CMS host indexed alongside the reverse proxy — a duplicate-content split), tenant_subdomain (other customers’ *.sites.* sites inside a domain property — exclude these), main_site (a different, brand-query-driven population), or other. Page rows also carry tags.isWebStory, marking Web Story twins of articles — split them off before judging article performance.
  • CTR and position are derived. ctr is a fraction (0.0028 = 0.28%); position is 1-indexed and impression-weighted within a row. Re-weight position by impressions after any aggregation — never average positions.
  • Pass accountId explicitly. The session’s current account is an in-memory override that can reset silently.
  • compare_periods totals are exact; movers follow your dimensions. Pass periodPreset (wow / mom / qoq) or explicit dates. Period totals, deltas, and growth-share denominators come from an extra ["date"]-grouped pull per period, so they match the GSC UI on any account size. Movers and segments are in the requested dimensions’ frame (meta.aggregationFrame). Each growth share is the page-filtered AGA delta ÷ the unfiltered property delta — the same numbers the GSC UI shows. Shares can be negative or exceed 1.
Always read rate changes against universe size. When you add new seed terms or queries, the denominator grows and the rate naturally dips — that is expansion, not decline. Trends answer whether growth or decline is happening on a stable universe base. reporting_get_trend carries what you need to tell the difference: compare totalQuestions per snapshot and the comparison.universe change against the rate movement before concluding anything about performance. The trend is Google-only; for ChatGPT movement, compare content planner chatgpt-* mode results over time.

Where to go next