# Composway documentation URL: /docs Reference and guides for connecting to Composway and working with its data. Composway is an All-in-one Search Visibility Analytics Platform. These docs cover the programmatic ways into it. ## Looking for something else? [#looking-for-something-else] * **The product itself** lives at [composway.ai](/). * **A quick connect walkthrough**, with per-assistant steps and a copyable server URL, is on the [MCP landing page](/mcp). --- # Connect your assistant URL: /docs/mcp/connect Add the Composway MCP server to Claude Code, Claude, Cursor, VS Code, Codex, Zed, ChatGPT or any other MCP client. Everything below uses one URL: ``` https://mcp.composway.ai/mcp ``` It speaks **Streamable HTTP** and authenticates with **OAuth 2.1**. Your client opens a browser window the first time it connects; sign in with your Composway account and approve the connection. There is no API key, and nothing to paste into a config file beyond the URL. The server acts as you. If you do not have an account yet, sign up at [Composway](https://app.composway.ai/register) and create a workspace before connecting - an assistant with no workspace can read very little. Otherwise, find your client below. They differ only in which file the URL goes in. ## Claude apps [#claude-apps] In the Claude desktop or web app: Open **Settings → Connectors** . Choose **Add custom connector** and paste the server URL. Sign in to Composway in the window that opens. For users under Claude **Team** or **Enterprise** licenses, an organization Owner adds the connector first under **Organization Settings → Connectors**, and members then connect individually - a member cannot add it alone. ## ChatGPT [#chatgpt] Turn on **Developer mode** under **Settings → Apps** . Create an app named Composway and paste the URL. Choose **OAuth** , sign in, and enable Composway. ## Claude Code [#claude-code] Install the plugin. It sets up the connection **and** adds skills that drive the tools - how to triage a site, which read settles which finding, what each class of traffic loss means: ```sh claude mcp add --transport http --scope user composway https://mcp.composway.ai/mcp ``` Then run `/mcp` to sign in. Use `--scope project` instead to commit the server to `.mcp.json` and share it with everyone on the repository: ```json title=".mcp.json" { "mcpServers": { "composway": { "type": "http", "url": "https://mcp.composway.ai/mcp" } } } ``` ## Codex [#codex] Add the server, then sign in: ```sh codex mcp add composway --url https://mcp.composway.ai/mcp codex mcp login composway ``` `codex mcp login` is a separate step, and it is the one people miss - without it the server is configured but unauthenticated, and every tool call fails. The equivalent by hand: ```toml title="~/.codex/config.toml" [mcp_servers.composway] url = "https://mcp.composway.ai/mcp" ``` Use `.codex/config.toml` in a trusted project to scope it there. ## Cursor [#cursor] Use this [**Add to Cursor**](cursor://anysphere.cursor-deeplink/mcp/install?name=composway\&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vbWNwLmNvbXBvc3dheS5haS9tY3AifQ==) link, or write it yourself: ```json title="~/.cursor/mcp.json" { "mcpServers": { "composway": { "type": "http", "url": "https://mcp.composway.ai/mcp" } } } ``` Reload Cursor, then sign in when the browser opens. Use `.cursor/mcp.json` inside a project to scope it to that project instead. ## Zed [#zed] ```json title="settings.json" { "context_servers": { "composway": { "url": "https://mcp.composway.ai/mcp" } } } ``` Leave out any `headers` - with no `Authorization` header configured, Zed runs the standard MCP OAuth flow and prompts you to sign in. ## Any other MCP client [#any-other-mcp-client] Point it at the URL as a remote Streamable HTTP server and let it run its OAuth flow. Clients differ in what they call this - "custom connector", "remote MCP server", "add server by URL" - but the inputs are the same, and none of them need anything from you beyond the URL. If a client still speaks only stdio, bridge it with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote): ```json { "mcpServers": { "composway": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.composway.ai/mcp"] } } } ``` ## Check that it worked [#check-that-it-worked] Ask your assistant to call `get_server_status`. A healthy connection returns the server version, the current tools schema version, and your signed-in identity. If it reports you as unauthenticated, the OAuth flow did not complete - reconnect. Then ask it to call `list_workspaces`. That is the id you will need for nearly everything else. The tool surface changes as the product does. `get_server_status` reports a `schemaVersion`; when it differs from what your client cached, the client should re-run tool discovery. Well-behaved clients do this on their own - the server also emits a `tools/list_changed` notification - but a long-lived session that skips it can end up calling a tool with a stale schema. --- # Find the right tool URL: /docs/mcp/find Describe what you want to do and get the Composway MCP tools that answer it - filtered by whether they read or write, and by what they cost. Your MCP client picks tools on its own from the descriptions it discovers, so you rarely need this page to *use* Composway - describe the outcome you want and the client chooses. It is here for the other question: what is in the box, and which tool answers a particular question. Type the outcome, not the tool name. The matcher reads the same descriptions your client reads, so "why did my traffic drop" finds [`diagnose_traffic_losses`](/docs/mcp/tools/diagnose_traffic_losses) even though the word "traffic" does not appear in its title. ## If that did not find it [#if-that-did-not-find-it] * **[The tool reference](/docs/mcp/tools)** lists all 48 grouped by what they operate on, with the same read/write and cost columns. * **[The cookbook](/docs/mcp/cookbook)** goes the other way round: it starts from a job ("diagnose a drop", "decide what to write") and names the tools each one uses, in order. * **Ask your own assistant.** Every page here is served as plain Markdown at [/llms.txt](/llms.txt) and [/llms-full.txt](/llms-full.txt), so you can point Claude, ChatGPT or anything else at the whole reference and ask it there. --- # Composway MCP server URL: /docs/mcp A hosted MCP server that gives your AI assistant Composway's research, search-analytics and content tools. Composway runs a hosted [MCP](https://modelcontextprotocol.io) server. Connect it to an assistant that speaks MCP - Claude Code, Claude, Cursor, Codex, ChatGPT and others - and that assistant can read your SEO performancedata, research a topic, score a draft and save work back, in the conversation you are already having. ``` https://mcp.composway.ai/mcp ``` There is no API key to manage. You sign in with your Composway account the first time your client connects, and the server acts as you from then on: the same workspaces, the same permissions, the same quotas as the web app. ## What Composway MCP can do [#what-composway-mcp-can-do] The capability of Composway MCP splits into four groups, described in full in the [tool reference](/docs/mcp/tools): * **Search analytics** - Google Search Console and Bing Webmaster Tools reads: performance trends, keyword discovery, content decay, cannibalization, gaps and assembled reports. * **Writes** - create workspaces and content campaigns, confirm a campaign's search intent and pain points, evaluate and optimize an article, save a draft. * **Workspaces & campaigns** - read workspaces, content campaigns, entities, related questions and content drafts. * **Diagnostics** - what version the server is on and whether your client's view of the tools is current. --- # Troubleshooting URL: /docs/mcp/troubleshooting The failures people actually hit - stalled calls, missing confirmations, empty results - and what each one means. ## A tool call hangs, then times out [#a-tool-call-hangs-then-times-out] Almost always a long-running analysis being awaited synchronously. Evaluation and optimization are **asynchronous by design**: `evaluate_article` and `optimize_article` return a job id straight away, and the result arrives through `get_evaluation_result`, polled about every 5 seconds. If your assistant appears to be waiting on one of those directly, ask it to poll the job instead. Campaign creation behaves the same way - poll `get_campaign`, do not wait on `create_ai_campaign`. ## The server asked me to confirm something, but no prompt appeared [#the-server-asked-me-to-confirm-something-but-no-prompt-appeared] Some MCP clients advertise support for the server's detailed confirmation prompts and then never show them. Rather than hang the call, the server waits 60 seconds and falls back to your client's own approval step - the one that fires for any non-read-only tool. The practical effect: on those clients you approve the *tool*, not the specific change, so read what your assistant says it is about to do. Spending caps are enforced in the backend either way and do not depend on the prompt. ## A write finished, but I never approved it [#a-write-finished-but-i-never-approved-it] Check whether your client auto-approves tools. Some allow a "always allow this tool" setting that suppresses both layers of confirmation. Composway cannot detect or override that from the server side - the client is what asks you. ## An evaluation result comes back shorter than the one in the app [#an-evaluation-result-comes-back-shorter-than-the-one-in-the-app] Deliberate. A full evaluation result runs to roughly 1.8 MB, mostly per-entity suggestions, which is too large to travel over an MCP connection - clients stall rather than deliver it. Results returned through MCP are a compact digest: every field is kept except the full entity-suggestion map, which is replaced with the top 20 suggestions by quality plus a total count. The web app still shows the complete result. If you need every entity, open the campaign in Composway. ## My site has no data [#my-site-has-no-data] Run `get_connection_status` before concluding anything. It reports whether Google Search Console and Bing Webmaster Tools are actually attached and how fresh each one is. An unattached engine reads as `read: false` in tool responses, which is different from measuring zero. Two more things that look like missing data but are not: * **Search Console history starts when the property was verified.** A short backfill is usually Google's limit, not a bug. * **Bing reports position weekly only**, and both engines' traffic feeds are top-N above a certain size. Tools say so in `unsupportedInputs` when it applies to the window you asked for. ## Tools behave differently than my assistant expects [#tools-behave-differently-than-my-assistant-expects] The tool surface changes as the product does. `get_server_status` returns a `schemaVersion` and a changelog; when the version differs from what your client cached, the client should re-run discovery and rebuild its tool arguments. `get_tool_manifest` gives a hash per tool schema for the same purpose. The server emits a `tools/list_changed` notification when it notices a live session on an old version, but a client that ignores it can keep calling an old shape. Reconnecting the server clears it. ## Everything fails as unauthenticated [#everything-fails-as-unauthenticated] Your session could not be refreshed - revoked, or lapsed after a long idle period. Remove and re-add the server in your client and sign in again. See [Authentication](/docs/mcp/authentication). --- # Get the brand and competitor sets right URL: /docs/mcp/cookbook/brand-and-competitors Two small lists that silently decide what several other tools report - worth checking before you trust a branded split or a share of voice. **Use this when** a branded/non-branded split looks wrong, a competitor is missing from an AI-visibility answer, or you are setting a workspace up for the first time. Neither list is cosmetic: brand terms decide which queries count as branded and which ones `find_low_hanging_keywords` excludes, and they are the same term set the AI-answer mention monitor scans for. The competitor set decides every competitive metric there is. **[`list_workspaces`](/docs/mcp/tools/list_workspaces)** - both tools here take a `workspaceId`, not a site. [`list_sites`](/docs/mcp/tools/list_sites) also carries the mapping if you only know the domain. **[`get_workspace_brand_terms`](/docs/mcp/tools/get_workspace_brand_terms)** * `brandTerms` is the effective list (title plus confirmed aliases) and is what the other tools actually use. `aliases` is the editable set behind it; `candidates` are proposals awaiting confirmation, and are matched against nothing until promoted. **[`analyze_branded_vs_nonbranded`](/docs/mcp/tools/analyze_branded_vs_nonbranded)** * check the classification rather than assuming it. The terms applied come back in `brandTerms` and the biggest branded queries in `sampleBrandedQueries`, which is where a missing former name or a stray generic word shows up. **[`set_workspace_brand_terms`](/docs/mcp/tools/set_workspace_brand_terms)**, then re-run the split. `replace` sets the list outright and cannot be combined with `add`/`remove`; removals apply before additions, so a rename is one call. An alias that fails vetting - too short, longer than four words, duplicate, or over the eight-alias cap - comes back in `rejected` while the rest of the write still applies. Aliases need not resemble the workspace title; a former name or an acronym is exactly what this is for. **[`get_workspace_competitors`](/docs/mcp/tools/get_workspace_competitors)** * the tracked brands plus your own for reference (`isSelf: true`), each with the facts the detectors match on (`aliases` in answer text, `domains` and `socialUrls` in cited sources) and `measuredDays`, which says whether the brand has any history at all. **[`set_workspace_competitors`](/docs/mcp/tools/set_workspace_competitors)** * edit from what the previous step returned, because `update` replaces each list you pass **wholesale** (send the full list; `[]` clears one) and leaves omitted lists alone. Facts are vetted per value: a shared platform host such as `linkedin.com` is refused as a `domain`, since it would credit the brand with every citation of that platform - pass the profile URL, path included, in `socialUrls` instead. A brand added today reads 0 until the next monitoring run, and measurement starts from that run onward. `remove` also discards the brand's recorded visibility history - check `measuredDays` and say what is being given up before running it. One reading caveat on the branded split itself: the shares are shares of the rows each engine returned, not of the property. Brand queries are high-volume and sit inside Google's top \~5,000 while the non-branded long tail is exactly what the ceiling cuts, so on a truncated window **the branded share is overstated**. The response says whether that applies in `unsupportedInputs`. And `total.avgPosition` is always null by design - the engines define and report position differently, so read the per-engine figures. --- # Decide what to write next URL: /docs/mcp/cookbook/decide-what-to-write Two different questions - demand you are visible for and losing, and demand you have no page for at all - asked in the order that keeps them apart. **Use this when** the output is a brief rather than a fix. The order matters because the cheap half of the answer is free and the expensive half exists only because the free half is structurally blind to it. **[`find_content_gaps`](/docs/mcp/tools/find_content_gaps)** - half A, always returned and free: queries the site is already visible for (impressions above the threshold) where no page of yours ranks well. This is demand you are losing, not demand you are missing. **Read each row's `cannibalizationStatus` before briefing anything** - `confirmed` re-labels the row `gapType: "cannibalized"` and lists the `competingPages`: consolidate, do not publish against yourself. `possible` names an unread sitemap URL in `suspectedPages` - open it first. `none` is the row this tool actually cleared. `unknown` means there is no usable sitemap inventory, so the row is a candidate rather than a cleared one. **[`find_emerging_keywords`](/docs/mcp/tools/find_emerging_keywords)** - queries being asked more often, read on **impressions** rather than clicks. Clicks lag by however long a page takes to rank, so rising impressions with flat clicks is exactly the row worth acting on. `pattern: "accelerating"` means the last rise was bigger than the one before it. **`find_content_gaps` with `includeUncovered: true`** - half B: topics the workspace's confirmed products and audiences imply and the site has no page for. Half A cannot see these at all, because both engines list a query only if the site already drew an impression for it. **This half costs money on every call** (one LLM expansion plus one keyword-volume request per engine), is capped per workspace per day, and asks for confirmation first. **Follow `recommendedAction` on each half-B topic** - `create` (nothing of yours is near it), `refresh` (a page of yours is adjacent, so expand `pageToRefresh` rather than publishing a second page against it), or `verify_inventory` (fewer pages were searched than the site has, so the absence is a limit, not a finding). Read `coverage.nearestDistance` alongside the verdict; the thresholds are declared, not calibrated. Read `uncoveredStatus` before concluding anything from it. `not_requested` means you did not ask for half B. `unavailable` means the workspace has no confirmed products or audiences for a topic to be implied from. `failed` means the expansion could not run. None of the three means the site has no uncovered demand. --- # Diagnose a traffic drop URL: /docs/mcp/cookbook/diagnose-a-drop From "clicks are down" to a named cause per query - and the different action each cause implies. **Use this when** clicks fell and you do not yet know why. The point of the sequence is that a drop in clicks looks the same whatever caused it, so the cause has to be attributed before any work is chosen: a rank loss, a CTR loss and a fall in demand each want a different response, and one of them wants no response at all. **[`get_site_snapshot`](/docs/mcp/tools/get_site_snapshot)** - confirm the drop is real and get its size, per engine. A fall on one engine only is a different story from a fall on both. **[`get_performance_trend`](/docs/mcp/tools/get_performance_trend)** - find *when* it happened. A weekly or monthly series separates one step down from a slow slide; a window-over-window comparison cannot. **[`diagnose_traffic_losses`](/docs/mcp/tools/diagnose_traffic_losses)** - for the queries that lost the most clicks, the cause. It locates the period the series actually stepped and decomposes around that, rather than across calendar windows, because a mid-window rank drop is smeared by a window average and its impressions loss then reads as falling demand. **Act on the class, not the number** - the classes carry the whole answer: * **A ranking** - outranked in place. Strengthen the page; [`get_query_pages`](/docs/mcp/tools/get_query_pages) tells you which page held the slot. * **A′ page swap** - a different page of yours took the slot. Consolidate rather than update; [`detect_keyword_cannibalization`](/docs/mcp/tools/detect_keyword_cannibalization) names the survivor to keep. * **B CTR** - ranking intact, clicks gone. The cheapest fix in the set: title, meta, schema. [`find_ctr_opportunities`](/docs/mcp/tools/find_ctr_opportunities) finds the rest of them. * **C visibility** - impressions gone, ranking intact, demand ruled out. No search-console feed exposes what else was on the page, so this one needs the live SERP (next step). * **D** - a rank loss across the page-one boundary. The impressions followed the ranking; never report it as demand. * **E demand** - fell on both engines with position flat. Nothing to fix. **[`explain_query_serp`](/docs/mcp/tools/explain_query_serp)** on a case C (or case B) query - fetches that query's Google SERP live and diffs it against the newest one stored for the workspace, which is how an AI Overview, a new entrant or a rewritten competitor title becomes visible. **This costs money on every call** and is capped per workspace per day, so run it on the queries the previous step flagged rather than sweeping a keyword list. ## Reading the result honestly [#reading-the-result-honestly] A case E row supports "fewer people searched *this phrasing*", not "fewer people searched for this" - nothing in the diagnosis looks at neighbouring queries, so a reformulation is indistinguishable from a real fall in demand. And "this is demand, not you" is a useful result rather than a failure: it stops a content cycle being spent on a page that is fine. Two more things specific to this recipe: * **The row ceiling matters more here than anywhere else**, because this tool attributes a *cause*. A period Google truncated is an impressions fall with nothing behind it, and the cross-engine control does not catch it either - a query missing from both feeds looks like two engines agreeing. Read `unsupportedInputs` before reporting a cause. * **A baseline can be absent.** In `explain_query_serp`, `baselineCapturedAt: null` means nothing was stored to compare against, so the movement lists are empty *for want of a baseline* - not because nothing changed. Only the workspace's roughly 100 weekly-captured queries have one; outside that set (`isTracked: false`) your first call establishes it. --- # Cookbook URL: /docs/mcp/cookbook Recipes for the jobs people bring to the MCP server - each one a tool order that works, and the reading it depends on. Most tools carry their own instructions about what to call next, and an assistant following them will usually get the sequence right on its own. These recipes are the paths worth knowing anyway, so you can tell whether yours is on track - and, where the order matters for a reason the tool cannot state on its own page, why it is that order. Each recipe names the job, the sequence, and the way it is most often misread. Nothing here is required: they compose, and a session usually walks two or three of them. ## Search performance [#search-performance] ## Brand & competitors [#brand--competitors] ## Reporting [#reporting] ## Read this before any of them [#read-this-before-any-of-them] Google compacts each single-dimension breakdown to roughly the top 5,000 rows per day once a day is a few days old, and Bing's traffic feeds are top-N by nature. Above those ceilings a query's absence stops being evidence that it got no traffic. The tools detect this per request and report it in `unsupportedInputs` rather than quietly returning a short list - read that field before treating an empty result as a finding. Two more habits that save a wrong answer later: * **Call [`get_server_status`](/docs/mcp/tools/get_server_status) once at the start of a session.** It reports the tools schema version, so a client holding a stale tool list finds out before it builds arguments against it. * **Check the connection before concluding a site is quiet.** [`get_connection_status`](/docs/mcp/tools/get_connection_status) says whether Google and Bing are actually attached and how fresh each one is. An unsynced engine and a dead engine look identical in every number downstream. --- # Turn findings into one work queue URL: /docs/mcp/cookbook/prioritize-work Answer "what should I work on" with four kinds of finding priced in a single unit, then drill into the ones you pick. **Use this when** nothing is obviously broken and you want a ranked list rather than an investigation. One tool does the ranking; the value of the recipe is in what you do with a row after you pick it. **[`list_sites`](/docs/mcp/tools/list_sites)** - resolve the exact domain, and check which engines the site has. **[`score_seo_opportunities`](/docs/mcp/tools/score_seo_opportunities)** * one prioritized queue over four tools that each answer a different question: [`find_low_hanging_keywords`](/docs/mcp/tools/find_low_hanging_keywords), [`find_ctr_opportunities`](/docs/mcp/tools/find_ctr_opportunities), [`find_content_decay`](/docs/mcp/tools/find_content_decay) and [`find_content_gaps`](/docs/mcp/tools/find_content_gaps). Narrow `types` when you already know the shape of problem you are chasing - `low_hanging`, `ctr` and `gap` share one scan, while `decay` costs a second. **Read `estimatedClicks` with its `basis`** - every row is priced in clicks per `windowDays` at stake if the finding were fixed. A `decay` row is clicks that were measurably earned and lost; the other three are projections onto the site's own CTR curve. `estimateBases` in the response spells each one out. **Drill into the rows you actually chose** - the queue fixes each source's thresholds at its defaults, so once a row is picked, its own tool is the better read: `find_content_decay` for the `changepoint` behind a decay row, [`get_query_pages`](/docs/mcp/tools/get_query_pages) for which page a low-hanging query is already ranking with, [`get_page_queries`](/docs/mcp/tools/get_page_queries) for what a page is carrying before you rewrite its title. **Check cannibalization before optimizing a page** - [`detect_keyword_cannibalization`](/docs/mcp/tools/detect_keyword_cannibalization) with the query. "No page ranks well for this" and "several of our pages are splitting it" look identical in a queue and want opposite work: strengthening one of two competing pages makes the split worse. ## Reading the queue honestly [#reading-the-queue-honestly] `estimatedClicks` takes the **maximum** of overlapping signals on one entity, never the sum - a query that is both low-hanging and under-clicking is one recovery, not two. A null value means the site's data could not price that row, not that it is worth zero; those rows sort last. * **The queue mixes queries and pages** (`entityType`), because the four sources work on different objects. The same string as a query and as a page is two rows. * **Say what was evaluated.** `sources` reports which types ran, which you excluded, and how many candidates each produced. A ranked queue reads as "these are the best available actions" in a way a filtered list does not - so when the response declares a truncated window, it is a top-N of what was *visible*, not of the site. --- # Read a site's search performance URL: /docs/mcp/cookbook/read-a-site The baseline read - what a site is doing now, what it was doing before, and which named question to ask next. **Use this when** you want the current picture of a site and have no particular suspicion yet. Everything else in the cookbook starts from somewhere in this sequence. **[`list_sites`](/docs/mcp/tools/list_sites)** - the sites in your workspaces, with the search engines attached to each. It is also how you learn the exact domain string the other tools want. **[`get_connection_status`](/docs/mcp/tools/get_connection_status)** - whether Google and Bing are actually connected and how fresh their data is. Worth checking before concluding that a site has no traffic. **[`get_site_snapshot`](/docs/mcp/tools/get_site_snapshot)** or **[`get_performance_trend`](/docs/mcp/tools/get_performance_trend)** - the overall picture. The snapshot compares a window against the one immediately before it; the trend gives the daily, weekly or monthly series, which is what tells you *when* something changed. **A named question** - [`find_low_hanging_keywords`](/docs/mcp/tools/find_low_hanging_keywords), [`find_content_decay`](/docs/mcp/tools/find_content_decay), [`find_ctr_opportunities`](/docs/mcp/tools/find_ctr_opportunities), [`find_content_gaps`](/docs/mcp/tools/find_content_gaps) and the rest each answer one thing and explain their rows. Prefer them over the generic [`get_advanced_search_analytics`](/docs/mcp/tools/get_advanced_search_analytics), which only groups and counts. **[`score_seo_opportunities`](/docs/mcp/tools/score_seo_opportunities)** ranks several of those into one queue priced in clicks, and [`generate_search_performance_report`](/docs/mcp/tools/generate_search_performance_report) assembles them into a report. ## Where it goes next [#where-it-goes-next] * Clicks are down and you want the cause → [Diagnose a traffic drop](/docs/mcp/cookbook/diagnose-a-drop). * Nothing is obviously wrong and you want a work list → [Turn findings into one work queue](/docs/mcp/cookbook/prioritize-work). * You need to drill from a query to its pages or back - [`get_query_pages`](/docs/mcp/tools/get_query_pages) and [`get_page_queries`](/docs/mcp/tools/get_page_queries) are the two directions. Google reports daily and Bing largely weekly, and position especially so. A figure that is null for one engine means *not measurable there*, not zero - so a Google-only number is a Google finding, not a site finding. The per-engine values are always in the response; prefer them to a combined average when the answer turns on position. --- # Report across several sites URL: /docs/mcp/cookbook/report-across-sites One assembled report over up to ten sites - which sections to ask for, and the one argument that makes a portfolio total possible. **Use this when** the deliverable is a report rather than an investigation: one site read the same way every month, or a handful of sites read together. **[`list_sites`](/docs/mcp/tools/list_sites)** - get the exact domains, and check [`get_connection_status`](/docs/mcp/tools/get_connection_status) for any site whose numbers you expect to be low. An unsynced engine in a report reads as a decline. **Pick the sections.** [`generate_search_performance_report`](/docs/mcp/tools/generate_search_performance_report) defaults to `summary,trend,topMovers`. The other four - `opportunities`, `siteSections`, `brandSplit`, `crossEngine` - each cost at least one more scan **per site**, so ask for them when the report is about them. **Pass `to` to pin the window.** Without it each site anchors to its own latest measured day, which is what the standalone tools do but leaves the sites on different windows - and suppresses the cross-site total. **Call it with `sites` comma-separated**, at most 10. They are read one after another, so a wide report with extra sections is a slow call. **Report the failures as failures.** A site that could not be reached comes back as a `status: "error"` row, and a section that failed as `status: "failed"` inside that site's `sections`. Partial results are normal here and are labelled rather than hidden - presenting the rest as the whole picture is the mistake. The report is assembled by calling the other tools and embedding their responses whole, so each section reads exactly as that tool's own output does - `unsupportedInputs` included. If a section looks surprising, the tool's own page explains it, and calling that tool directly gets you its thresholds and arguments. `portfolio` is a cross-site total of clicks and impressions only, and is null unless every counted site resolved to the same window - which is what step 3 is for. Average position is deliberately not totalled anywhere. --- # Analyze branded vs non-branded search URL: /docs/mcp/tools/analyze_branded_vs_nonbranded Splits the window's Google + Bing queries into branded and non-branded demand and reports each side's clicks, impressions, CTR, average position and share of the engine's total. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `analyze_branded_vs_nonbranded` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Splits the window's Google + Bing queries into branded and non-branded demand and reports each side's clicks, impressions, CTR, average position and share of the engine's total. A query is branded when it contains the workspace title or one of its confirmed brand aliases, matched whole and case-insensitively - the same term set the AI-answer mention monitor uses. The terms applied come back in `brandTerms` and the biggest branded queries in `sampleBrandedQueries`, so you can check the classification; correct it with set\_workspace\_brand\_terms and re-run. `total` sums clicks and impressions and counts each query once across engines, but `total.avgPosition` is always null - the engines define position differently and report it on different cadences, so read the per-engine figures instead. The shares here are shares of the rows each engine returned, not of the property, and Google's row ceiling makes that directional rather than merely approximate: brand queries are high-volume and sit inside Google's top \~5,000, while the non-branded long tail is exactly what the ceiling cuts, so on a truncated window **the branded share is overstated**. The response says whether that applies to this window in `unsupportedInputs`. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "brandTerms": { "type": "array", "items": { "type": "string" } }, "google": { "type": "object", "properties": { "attached": { "type": "boolean" }, "branded": { "type": "object", "properties": { "queryCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicksShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "queryCount", "clicks", "impressions", "ctr", "avgPosition", "positionCoverageImpressions", "clicksShare", "impressionsShare" ], "additionalProperties": {} }, "nonBranded": { "type": "object", "properties": { "queryCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicksShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "queryCount", "clicks", "impressions", "ctr", "avgPosition", "positionCoverageImpressions", "clicksShare", "impressionsShare" ], "additionalProperties": {} } }, "required": ["attached", "branded", "nonBranded"], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "attached": { "type": "boolean" }, "branded": { "type": "object", "properties": { "queryCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicksShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "queryCount", "clicks", "impressions", "ctr", "avgPosition", "positionCoverageImpressions", "clicksShare", "impressionsShare" ], "additionalProperties": {} }, "nonBranded": { "type": "object", "properties": { "queryCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicksShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "queryCount", "clicks", "impressions", "ctr", "avgPosition", "positionCoverageImpressions", "clicksShare", "impressionsShare" ], "additionalProperties": {} } }, "required": ["attached", "branded", "nonBranded"], "additionalProperties": {} }, "total": { "type": "object", "properties": { "attached": { "type": "boolean" }, "branded": { "type": "object", "properties": { "queryCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicksShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "queryCount", "clicks", "impressions", "ctr", "avgPosition", "positionCoverageImpressions", "clicksShare", "impressionsShare" ], "additionalProperties": {} }, "nonBranded": { "type": "object", "properties": { "queryCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicksShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsShare": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "queryCount", "clicks", "impressions", "ctr", "avgPosition", "positionCoverageImpressions", "clicksShare", "impressionsShare" ], "additionalProperties": {} } }, "required": ["attached", "branded", "nonBranded"], "additionalProperties": {} }, "sampleBrandedQueries": { "type": "array", "items": { "type": "string" } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "brandTerms", "google", "bing", "total", "sampleBrandedQueries", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Analyze site sections URL: /docs/mcp/tools/analyze_site_sections Aggregates Google + Bing page performance into URL directories (the leading `sectionDepth` path segments) and compares each section's current total against the immediately preceding period. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `analyze_site_sections` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Aggregates Google + Bing page performance into URL directories (the leading `sectionDepth` path segments) and compares each section's current total against the immediately preceding period. Bing page metrics are a top-N-by-traffic feed, so section totals are not strictly comparable across engines (see `unsupportedInputs`). Caller-defined custom section patterns are not yet exposed on this tool. A section total is a sum over rows, so missing rows subtract from it directly - never read a section as a share of the site; use get\_site\_snapshot for the property's own totals. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `sectionDepth` | `integer` | No | Number of leading URL path segments that define a section. Defaults to `1`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "sectionDepth": { "default": 1, "description": "Number of leading URL path segments that define a section.", "type": "integer", "minimum": 1, "maximum": 5 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "section": { "type": "string" }, "pageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "google": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["clicks", "impressions"], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["clicks", "impressions"], "additionalProperties": {} }, "previousClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "previousImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "section", "pageCount", "google", "bing", "previousClicks", "previousImpressions", "deltaClicks", "deltaImpressions" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Attach target AI prompt URL: /docs/mcp/tools/attach_target_ai_prompt Attach a new target AI prompt (topic + location + language + LLM providers) to a campaign. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `attach_target_ai_prompt` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Attach a new target AI prompt (topic + location + language + LLM providers) to a campaign. Kicks off prompt-query fan-out analysis; poll get\_task\_status with recordType=target\_ai\_prompts and the returned promptId. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | --------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `campaignId` | `string (uuid)` | Yes | - | | `topic` | `string` | Yes | - | | `location` | `{ id, code, isoCode, name }` | Yes | DataForSEO location reference. Do NOT invent these fields - call search\_reference with type="location" (e.g. query="United States") and pass one returned result object here verbatim. | | `language` | `{ id, code, isoCode, name }` | Yes | DataForSEO language reference. Do NOT invent these fields - call search\_reference with type="language" (e.g. query="English") and pass one returned result object here verbatim. | | `llmProviderIds` | `(string (uuid))[]` | No | Defaults to `[]`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "topic": { "type": "string" }, "location": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "description": "DataForSEO location reference. Do NOT invent these fields - call search_reference with type=\"location\" (e.g. query=\"United States\") and pass one returned result object here verbatim." }, "language": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "description": "DataForSEO language reference. Do NOT invent these fields - call search_reference with type=\"language\" (e.g. query=\"English\") and pass one returned result object here verbatim." }, "llmProviderIds": { "default": [], "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } } }, "required": ["campaignId", "topic", "location", "language"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "promptId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "taskId": { "type": "string" }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["promptId", "taskId", "campaignId"], "additionalProperties": false } ```
--- # Confirm pain points URL: /docs/mcp/tools/confirm_pain_points Confirm the campaign background: the selected pain points, target audiences, and search-intent ids. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `confirm_pain_points` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Confirm the campaign background: the selected pain points, target audiences, and search-intent ids. You MUST provide at least one pain point AND at least one search-intent id - omitting the search-intent ids leaves the campaign unconfirmed. Get the pain-point candidates from get\_campaign\_related\_questions and the search-intent-classification ids from get\_campaign first. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------- | ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------- | | `campaignId` | `string (uuid)` | Yes | - | | `painPoints` | `({ id, name })[]` | Yes | At least one selected pain point. Candidates come from get\_campaign\_related\_questions. | | `targetAudiences` | `({ id, name })[]` | Yes | - | | `searchIntentIds` | `(string (uuid))[]` | Yes | At least one search-intent-classification id to confirm for the campaign. Get these ids from get\_campaign. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "painPoints": { "minItems": 1, "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" } }, "required": ["name"] }, "description": "At least one selected pain point. Candidates come from get_campaign_related_questions." }, "targetAudiences": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" } }, "required": ["name"] } }, "searchIntentIds": { "minItems": 1, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "description": "At least one search-intent-classification id to confirm for the campaign. Get these ids from get_campaign." } }, "required": [ "campaignId", "painPoints", "targetAudiences", "searchIntentIds" ] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {}, "additionalProperties": false } ```
--- # Confirm search intent URL: /docs/mcp/tools/confirm_search_intent Confirm the chosen search-intent classification for a campaign. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `confirm_search_intent` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Confirm the chosen search-intent classification for a campaign. Get candidates from get\_campaign first. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - | | `searchIntentId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchIntentId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId", "searchIntentId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchIntentId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId", "searchIntentId"], "additionalProperties": false } ```
--- # Create AI campaign URL: /docs/mcp/tools/create_ai_campaign Create an AEO (AI) campaign in a workspace from one or more target prompts. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `create_ai_campaign` **Cost:** Counts against your plan's monthly campaign quota - the same allowance the web app draws on. Nothing is billed per call. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Create an AEO (AI) campaign in a workspace from one or more target prompts. Counts against the monthly campaign quota and kicks off background analysis. After creating, the campaign runs background analysis. Poll get\_campaign with the returned campaign id about every 30 seconds until its status becomes "awaiting-pain-point-confirmation" AND it returns at least one pain point (pain\_points) and at least one search intent. The search intents are in search\_intents\[].results\[] - each entry is a SearchIntentDetail and its `id` is the search-intent id. Once that state is reached, present the returned pain points and search intents to the user and ask them to confirm which to keep, then call confirm\_pain\_points with the chosen pain points and the selected SearchIntentDetail ids (searchIntentIds). Do NOT call confirm\_pain\_points before the status is awaiting-pain-point-confirmation, and never with an empty search-intent list. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------- | ------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `workspaceId` | `string (uuid)` | Yes | - | | `targetPrompts` | `({ topic, location, language, llmProviderIds })[]` | Yes | - | | `targetAudiences` | `({ id, name })[]` | Yes | At least one target audience is REQUIRED - the backend rejects the campaign otherwise. If the user did not specify an audience, do NOT invent one: call get\_workspace first and ask the user to pick one of the workspace's target audiences to attach (pass its id + name to inherit it), or to provide a new audience name. | | `aiMode` | `"ai" \| "manual"` | No | Defaults to `"ai"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "targetPrompts": { "minItems": 1, "type": "array", "items": { "type": "object", "properties": { "topic": { "type": "string", "description": "The prompt/topic to target." }, "location": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "description": "DataForSEO location reference. Do NOT invent these fields - call search_reference with type=\"location\" (e.g. query=\"United States\") and pass one returned result object here verbatim." }, "language": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "description": "DataForSEO language reference. Do NOT invent these fields - call search_reference with type=\"language\" (e.g. query=\"English\") and pass one returned result object here verbatim." }, "llmProviderIds": { "default": [], "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } } }, "required": ["topic", "location", "language"] } }, "targetAudiences": { "minItems": 1, "type": "array", "items": { "type": "object", "properties": { "id": { "description": "Set only when inheriting a workspace audience.", "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" } }, "required": ["name"] }, "description": "At least one target audience is REQUIRED - the backend rejects the campaign otherwise. If the user did not specify an audience, do NOT invent one: call get_workspace first and ask the user to pick one of the workspace's target audiences to attach (pass its id + name to inherit it), or to provide a new audience name." }, "aiMode": { "default": "ai", "type": "string", "enum": ["ai", "manual"] } }, "required": ["workspaceId", "targetPrompts", "targetAudiences"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "targetPrompts": { "type": "array", "items": { "type": "object", "properties": { "topic": { "type": "string" }, "location": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "language": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "llmProviderIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "promptId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "targetAudiences": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "painPointConnected": { "type": "boolean" }, "painPointId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] } }, "required": ["topic", "location", "language"], "additionalProperties": {} } }, "aiMode": { "type": "string" } }, "required": ["workspaceId", "targetPrompts", "aiMode"], "additionalProperties": false } ```
--- # Create workspace URL: /docs/mcp/tools/create_workspace Create a new workspace. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `create_workspace` **Cost:** Counts against your plan's monthly workspace quota - the same allowance the web app draws on. Nothing is billed per call. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Create a new workspace. Counts against the user's monthly workspace quota. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------- | --------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `title` | `string` | Yes | - | | `description` | `string` | No | - | | `landingUrls` | `string[]` | Yes | Landing/seed URLs for the workspace. | | `defaultLocation` | `{ id, code, isoCode, name }` | No | DataForSEO location reference. Do NOT invent these fields - call search\_reference with type="location" (e.g. query="United States") and pass one returned result object here verbatim. | | `defaultLanguage` | `{ id, code, isoCode, name }` | No | DataForSEO language reference. Do NOT invent these fields - call search\_reference with type="language" (e.g. query="English") and pass one returned result object here verbatim. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "landingUrls": { "type": "array", "items": { "type": "string" }, "description": "Landing/seed URLs for the workspace." }, "defaultLocation": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "description": "DataForSEO location reference. Do NOT invent these fields - call search_reference with type=\"location\" (e.g. query=\"United States\") and pass one returned result object here verbatim." }, "defaultLanguage": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "description": "DataForSEO language reference. Do NOT invent these fields - call search_reference with type=\"language\" (e.g. query=\"English\") and pass one returned result object here verbatim." } }, "required": ["title", "landingUrls"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "defaultLocation": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "defaultLanguage": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} } }, "required": ["id", "title", "defaultLocation", "defaultLanguage"], "additionalProperties": false } ```
--- # Deep research URL: /docs/mcp/tools/deep_research Perform deep research on how people ask on a related topic. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `deep_research` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Perform deep research on how people ask on a related topic. Returns the campaign's related questions and pain points together with the authoritative sources competitors are cited for. Call this when drafting the article. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------ | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": {} } }, "painPoints": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "urls": { "type": "array", "items": { "type": "string" } } }, "required": ["name", "urls"], "additionalProperties": {} } }, "workspaceId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "promptIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "authoritativeSources": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "url": { "type": "string" }, "cited": { "type": "object", "properties": { "max": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "min": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "webpageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["max", "min", "webpageCount"], "additionalProperties": {} }, "coveredWebpages": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": {} } } }, "required": ["name", "url", "cited"], "additionalProperties": {} } } }, "required": [ "data", "painPoints", "campaignId", "promptIds", "authoritativeSources" ], "additionalProperties": false } ```
--- # Detect keyword cannibalization URL: /docs/mcp/tools/detect_keyword_cannibalization Queries more than one of the site's own pages competes for, read from two sources. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `detect_keyword_cannibalization` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Queries more than one of the site's own pages competes for, read from two sources. **The search data alone cannot answer this**: both engines show one result per site, so the page that loses every time draws no impressions and is invisible in every feed - and that settled case is the damaging one. So each query gets a `verdict` on a four-rung ladder, and the rung says what you may do about it. `confirmed`: two or more pages really did draw impressions, listed with each one's share of them, so a consolidation has a survivor to keep - `pattern` says whether they surfaced together (`co_occurring`) or never on the same day (`alternating`, the engine still choosing). `possible`: one page ranks and a URL in the site's own sitemap matches the query on slug and title tokens - **that page was never read**, so open the URL in `sitemapCandidates` and decide; this is the ceiling of what the tool can claim about it. `none`: one page ranks and nothing else in the sitemap targets the query - the query is clear, and it is safe to optimize that page. `unknown`: no usable sitemap inventory, or one a walk truncated, so absence proves nothing. `rows` carries the `confirmed` and `possible` queries; `verdicts` counts every rung including the cleared ones, and `inventory` says how complete and how old the page list behind them is. Pass `query` to get one query's row on whatever rung it lands. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum window impressions for a page to count as competing for the query - keeps one stray impression from making a healthy query look contested. Defaults to `10`. | | `query` | `string` | No | Ask about one query only, and return its row whatever verdict it gets - including `none` and `unknown`, which the unfiltered response only counts. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 10, "description": "Minimum window impressions for a page to count as competing for the query - keeps one stray impression from making a healthy query look contested.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "query": { "description": "Ask about one query only, and return its row whatever verdict it gets - including `none` and `unknown`, which the unfiltered response only counts.", "type": "string", "minLength": 1 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "query": { "type": "string" }, "verdict": { "type": "string" }, "evidence": { "type": "string" }, "pageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "pages": { "type": "array", "items": { "type": "object", "properties": { "page": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "clicks", "impressions", "position", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "clicks", "impressions", "position", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "totalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressionShare": { "type": "number" } }, "required": [ "page", "google", "bing", "totalClicks", "totalImpressions", "impressionShare" ], "additionalProperties": {} } }, "google": { "type": "object", "properties": { "attached": { "type": "boolean" }, "pageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "pattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "multiPageDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["attached", "pageCount", "multiPageDays"], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "attached": { "type": "boolean" }, "pageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "pattern": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "multiPageDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["attached", "pageCount", "multiPageDays"], "additionalProperties": {} }, "sitemapCandidates": { "type": "array", "items": { "type": "object", "properties": { "page": { "type": "string" }, "url": { "type": "string" }, "lastmod": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "matchedTokens": { "type": "array", "items": { "type": "string" } }, "coverage": { "type": "number" }, "titleMatched": { "type": "boolean" } }, "required": [ "page", "url", "matchedTokens", "coverage", "titleMatched" ], "additionalProperties": {} } } }, "required": [ "query", "verdict", "evidence", "pageCount", "totalClicks", "totalImpressions", "pages", "google", "bing", "sitemapCandidates" ], "additionalProperties": {} } }, "inventory": { "type": "object", "properties": { "status": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "usable": { "type": "boolean" }, "fetchedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "pageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "indexedPageCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "truncated": { "type": "boolean" }, "siteUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "usable", "pageCount", "indexedPageCount", "truncated" ], "additionalProperties": {} }, "verdicts": { "type": "object", "properties": { "confirmed": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "possible": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "none": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "unknown": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "inventoryTested": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "queriesExamined": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "confirmed", "possible", "none", "unknown", "inventoryTested", "queriesExamined" ], "additionalProperties": {} }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "rows", "inventory", "verdicts", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Diagnose traffic losses URL: /docs/mcp/tools/diagnose_traffic_losses For the queries that lost the most clicks, says *why* - and each cause implies a different action, so the class matters more than the number. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} {/* The prose section below comes from workers/mcp-server/docs/tools/diagnose_traffic_losses.mdx - edit it there. */} **Tool name:** `diagnose_traffic_losses` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. For the queries that lost the most clicks, says *why* - and each cause implies a different action, so the class matters more than the number. It locates the period the series actually stepped and decomposes around that, not across calendar windows, because a mid-window rank drop is smeared by a window average and its impressions loss then reads as falling demand. The classes: **A ranking** - outranked in place, strengthen the page. **A′ page swap** - a different page of yours took the slot, so consolidate rather than update. **B CTR** - ranking intact, clicks gone, the cheapest fix in the set (title/meta/schema). **C visibility** - impressions gone with the ranking intact and demand ruled out; a residual *here*, because no search-console feed exposes SERP composition at any grain, so an AI Overview or a feature block cannot be seen from this data. Run explain\_query\_serp on a case C (or case B) query to see it: it fetches the SERP live and diffs it against the last one stored. Only the workspace's \~100 weekly-captured queries have a stored baseline; outside that set the first call establishes one. **D** - a rank loss across the page-one boundary; the impressions followed the ranking, so never report it as demand. **E demand** - fell on both engines with position flat. Two things to carry into any answer built on this. A case E row supports 'fewer people searched *this phrasing*' and not 'fewer people searched for this' - nothing here looks at neighbouring queries, so a reformulation is indistinguishable. And 'this is demand, not you' is a useful result, not a failure: it stops a content cycle being spent on a page that is fine. This tool attributes a *cause*, so the row ceiling matters more here than anywhere else on this surface: a period Google truncated is an impressions fall with nothing behind it, and the cross-engine control does not catch it either, since Bing's own feeds are top-N and a query missing from both looks like two engines agreeing. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## How it decides [#how-it-decides] A drop in clicks looks the same whatever caused it, so the tool works through the causes in order and stops at the first one that fits. It runs the checks that cost nothing before the ones that cost anything. The first step is not a question but a measurement: **find the week or month the numbers actually stepped**, and compare before against after. Comparing whole calendar windows averages a mid-window drop away, and a ranking loss hidden that way reads as falling demand. The last question is why the tool reads both engines. A query that fell on Google and held on Bing did not lose its audience - something changed on Google's results page. Falling on both is the one signature that points at demand. ## What each answer means [#what-each-answer-means] | What you see | What it means | What to do | | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | Ranking slipped, same page of yours | A competitor overtook you in place. | Strengthen that page. | | Ranking slipped, and a *different* page of yours now ranks | Two of your pages are competing for one slot. | Consolidate them. Updating both makes it worse. | | Ranking slipped off page one | A ranking loss. The drop in how often you were shown is the *result* of that, not evidence that interest fell. | Treat it as a ranking problem, not a demand one. | | Ranking held, shown as often, fewer clicks | Something took the click without taking the position - an AI Overview or featured snippet, a title Google rewrote, or a competitor with a better-written result. | Usually the cheapest fix on the list: rewrite the title and description, target the snippet, add schema. | | Ranking held, shown less often, Google only | You lost space on the results page itself. | Run `explain_query_serp` on the query - this is the one cause no search-console data can show. | | Ranking held, shown less often, on both engines | Fewer people are searching this phrasing. | Reallocate the effort. Check whether it recurs each year before rewriting anything. | The last two rows are the ones an analyst is most tempted to skip. Being told *this is demand, not you* is worth as much as a fixable problem - it stops a content cycle being spent on a page that is fine. One caveat on that last row: nothing here looks at neighbouring queries, so it cannot tell falling interest apart from people searching a different wording for the same thing. It supports "fewer people searched **this phrasing**" - not "fewer people searched for this". ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `granularity` | `"weekly" \| "monthly"` | No | Length of one period in the series. Daily is deliberately not offered: these tools ask whether a change was sustained across periods, and a one-day period turns that into weekday seasonality. Defaults to `"monthly"`. | | `periods` | `integer` | No | How many whole periods to read. Defaults to 13 monthly periods, the shortest series the seasonal-versus-secular test can run on; shorter windows still diagnose every other class and report seasonality "untested". Defaults to `13`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum impressions over the whole window per query. Defaults to `50`. | | `excludeBranded` | `boolean` | No | Exclude branded queries. On by default: branded queries carry far higher CTR at the same position, so leaving them in pulls the expected-CTR curve upward and under-reports the CTR class on exactly the non-branded queries that matter. Defaults to `true`. | | `checkPageSwap` | `boolean` | No | Run the page↔query join to separate class A from A′. Costs two extra lake scans and is skipped automatically when no row reached class A; turn it off to keep the call cheap and accept `pageSwap.status: "unknown"`. Defaults to `true`. | | `limit` | `integer` | No | How many of the biggest losers to diagnose. Kept small - each row is a paragraph of evidence, not a table row. Defaults to `10`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "granularity": { "default": "monthly", "description": "Length of one period in the series. Daily is deliberately not offered: these tools ask whether a change was sustained across periods, and a one-day period turns that into weekday seasonality.", "type": "string", "enum": ["weekly", "monthly"] }, "periods": { "default": 13, "description": "How many whole periods to read. Defaults to 13 monthly periods, the shortest series the seasonal-versus-secular test can run on; shorter windows still diagnose every other class and report seasonality \"untested\".", "type": "integer", "minimum": 2, "maximum": 104 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 50, "description": "Minimum impressions over the whole window per query.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "excludeBranded": { "default": true, "description": "Exclude branded queries. On by default: branded queries carry far higher CTR at the same position, so leaving them in pulls the expected-CTR curve upward and under-reports the CTR class on exactly the non-branded queries that matter.", "type": "boolean" }, "checkPageSwap": { "default": true, "description": "Run the page↔query join to separate class A from A′. Costs two extra lake scans and is skipped automatically when no row reached class A; turn it off to keep the call cheap and accept `pageSwap.status: \"unknown\"`.", "type": "boolean" }, "limit": { "default": 10, "description": "How many of the biggest losers to diagnose. Kept small - each row is a paragraph of evidence, not a table row.", "type": "integer", "exclusiveMinimum": 0, "maximum": 50 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "periodWindow": { "type": "object", "properties": { "granularity": { "type": "string" }, "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "window": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "periodStarts": { "type": "array", "items": { "type": "string" } }, "excludedPartialPeriod": { "anyOf": [ { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, { "type": "null" } ] } }, "required": [ "granularity", "periods", "window", "periodStarts", "excludedPartialPeriod" ], "additionalProperties": {} }, "rows": { "type": "array", "items": { "type": "object", "properties": { "query": { "type": "string" }, "primaryEngine": { "type": "string", "enum": ["google", "bing"] }, "case": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "caseLabel": { "type": "string" }, "confidence": { "type": "string" }, "recommendation": { "type": "string" }, "explanation": { "type": "string" }, "estimatedClicksLost": { "type": "number" }, "change": { "type": "object", "properties": { "changepoint": { "type": "object", "properties": { "periodStart": { "type": "string" }, "before": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} }, "after": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} } }, "required": ["periodStart", "before", "after"], "additionalProperties": {} }, "deltaClicksPerPeriod": { "type": "number" }, "deltaImpressionsPerPeriod": { "type": "number" }, "deltaPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "deltaCtr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "decomposition": { "type": "object", "properties": { "impressionsTerm": { "type": "number" }, "ctrTerm": { "type": "number" } }, "required": ["impressionsTerm", "ctrTerm"], "additionalProperties": {} }, "series": { "type": "array", "items": { "type": "object", "properties": { "periodStart": { "type": "string" }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "periodStart", "clicks", "impressions", "position" ], "additionalProperties": {} } } }, "required": [ "changepoint", "deltaClicksPerPeriod", "deltaImpressionsPerPeriod", "deltaPosition", "deltaCtr", "decomposition", "series" ], "additionalProperties": {} }, "crossEngine": { "type": "object", "properties": { "control": { "type": "string" }, "otherEngine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "otherDeltaImpressionsPerPeriod": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "control", "otherDeltaImpressionsPerPeriod" ], "additionalProperties": {} }, "expectedCtr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "actualCtr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "pageSwap": { "type": "object", "properties": { "status": { "type": "string" }, "beforeTopPage": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "afterTopPage": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["status"], "additionalProperties": {} }, "seasonality": { "type": "string" } }, "required": [ "query", "primaryEngine", "case", "caseLabel", "confidence", "recommendation", "explanation", "estimatedClicksLost", "change", "crossEngine", "expectedCtr", "actualCtr", "pageSwap", "seasonality" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "periodWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Evaluate article URL: /docs/mcp/tools/evaluate_article Start an asynchronous evaluation job for an article in a campaign. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `evaluate_article` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Start an asynchronous evaluation job for an article in a campaign. Scores SEO + entity coverage. By default persistContent=true, so the submitted article is saved as campaign content; set persistContent=false when you only want to score without saving. Returns immediately with \{ jobId, status, kind, cached, result, error }. This tool usually does not return the final score inline. After calling it, use get\_evaluation\_result(\{ jobId }) to poll the job about every 5 seconds while status is "queued" or "running". If status is "completed", read result. If status is "failed", read error. If cached=true and status is already "completed", the result is included inline and no polling is needed. Do not call evaluate\_article again just to poll. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------- | --------------- | -------- | ------------------- | | `article` | `string` | Yes | - | | `campaignId` | `string (uuid)` | Yes | - | | `workspaceId` | `string (uuid)` | Yes | - | | `scoreOnly` | `boolean` | No | Defaults to `true`. | | `persistContent` | `boolean` | No | Defaults to `true`. | | `metaTitle` | `string` | No | - | | `metaDescription` | `string` | No | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "article": { "type": "string" }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "scoreOnly": { "default": true, "type": "boolean" }, "persistContent": { "default": true, "type": "boolean" }, "metaTitle": { "type": "string" }, "metaDescription": { "type": "string" } }, "required": ["article", "campaignId", "workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "jobId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "status": { "type": "string" }, "kind": { "type": "string" }, "cached": { "type": "boolean" }, "result": { "anyOf": [ { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, { "type": "null" } ] }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["jobId", "status", "kind"], "additionalProperties": false } ```
--- # Explain a query's SERP URL: /docs/mcp/tools/explain_query_serp Fetch this query's Google SERP **live** and diff it against the newest SERP stored for this workspace. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `explain_query_serp` **Cost:** Paid on every call - a live DataForSEO SERP fetch, billed per call rather than served from cache, and capped per workspace per day. The response reports what it cost. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Fetch this query's Google SERP **live** and diff it against the newest SERP stored for this workspace. This is the answer to the case that diagnose\_traffic\_losses reports as case C - impressions fell while the ranking held - because search console can see the rank but never what else is on the page: an AI overview that appeared above the results, new entrants that pushed the fold down, or a competitor whose rewritten title now answers the question outright. **This costs money on every call.** It is a paid DataForSEO fetch, not a cached read, and it is capped per workspace per day. Run diagnose\_traffic\_losses first and use this on the queries it flags, rather than sweeping a keyword list. `baselineCapturedAt: null` means nothing was stored to compare against, so every movement list is empty *for want of a baseline* - do not report that as 'nothing changed'. A baseline exists from the next weekly capture onward. `isTracked: false` means the query is outside the workspace's weekly capture set (its top \~100 queries by impressions), so this call is the only observation of it so far. Movement is reported over the top 10 only, which is the depth captured. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | --------- | -------- | -------- | ------------------------------------- | | `site` | `string` | Yes | Domain or URL, e.g. `example.com`. | | `query` | `string` | Yes | The search query to fetch a SERP for. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "Domain or URL, e.g. `example.com`." }, "query": { "type": "string", "minLength": 1, "description": "The search query to fetch a SERP for." } }, "required": ["site", "query"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "query": { "type": "string" }, "capturedAt": { "type": "string" }, "baselineCapturedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "baselineSource": { "type": "string", "enum": ["weekly_capture", "explain", "campaign", "none"] }, "isTracked": { "type": "boolean" }, "aiOverview": { "type": "object", "properties": { "presentBefore": { "type": "boolean" }, "presentNow": { "type": "boolean" }, "citesUs": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": ["presentBefore", "presentNow", "citesUs"], "additionalProperties": {} }, "features": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string" }, "presentBefore": { "type": "boolean" }, "presentNow": { "type": "boolean" } }, "required": ["type", "presentBefore", "presentNow"], "additionalProperties": {} } }, "ourPosition": { "type": "object", "properties": { "positionBefore": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "positionNow": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "metaTitleBefore": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "metaTitleNow": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "urlBefore": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "urlNow": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "positionBefore", "positionNow", "metaTitleBefore", "metaTitleNow", "urlBefore", "urlNow" ], "additionalProperties": {} }, "enteredTop10": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string" }, "domain": { "type": "string" }, "position": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "metaTitle": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["url", "domain", "position", "metaTitle"], "additionalProperties": {} } }, "leftTop10": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string" }, "domain": { "type": "string" }, "position": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "metaTitle": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["url", "domain", "position", "metaTitle"], "additionalProperties": {} } }, "movedInTop10": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string" }, "domain": { "type": "string" }, "positionBefore": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "positionNow": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "metaTitleBefore": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "metaTitleNow": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "url", "domain", "positionBefore", "positionNow", "metaTitleBefore", "metaTitleNow" ], "additionalProperties": {} } }, "resultTypeMix": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string" }, "countBefore": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "countNow": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["type", "countBefore", "countNow"], "additionalProperties": {} } }, "costUsd": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } } }, "required": [ "site", "workspaceId", "query", "capturedAt", "baselineCapturedAt", "baselineSource", "isTracked", "aiOverview", "features", "ourPosition", "enteredTop10", "leftTop10", "movedInTop10", "resultTypeMix", "costUsd", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find decaying pages or queries URL: /docs/mcp/tools/find_content_decay Pages or queries in *sustained* decline across several whole periods - not a window-over-window drop, which cannot tell a decline from a bad month. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_content_decay` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Pages or queries in *sustained* decline across several whole periods - not a window-over-window drop, which cannot tell a decline from a bad month. Two shapes are reported and they imply different investigations: `sustained` is still falling period on period, and `stepped` is one drop that has held since, which is what a lost ranking, a redirect or a de-indexed page looks like (its `decliningPeriods` is 0 - flat at the lower level). `changepoint` gives the period the level changed and the two levels either side of it. Read the two engines' series differently: a Google period with no row arrives as 0, a Bing one arrives as null, because Bing's feeds are top-N cursors and an absence there is not a fall. That Google zero is what the row ceiling undermines: on a truncated period it is a missing row, not a measured zero, and a run of them reads as exactly the decline this tool looks for. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | --------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `by` | `"query" \| "page"` | No | Decay pages (the usual question) or queries. Defaults to `"page"`. | | `granularity` | `"weekly" \| "monthly"` | No | Length of one period in the series. Daily is deliberately not offered: these tools ask whether a change was sustained across periods, and a one-day period turns that into weekday seasonality. Defaults to `"monthly"`. | | `periods` | `integer` | No | How many whole periods to read. The period containing the anchor date is excluded when it is still running - see periodWindow\.excludedPartialPeriod. Defaults to `6`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum impressions over the whole window for an entity to be read at all. A floor on the range, not on each period - a per-period floor would delete the quiet periods a decline ends in. Defaults to `50`. | | `minDecliningPeriods` | `integer` | No | Consecutive falling periods required for `sustained`. Defaults to `3`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "by": { "default": "page", "description": "Decay pages (the usual question) or queries.", "type": "string", "enum": ["query", "page"] }, "granularity": { "default": "monthly", "description": "Length of one period in the series. Daily is deliberately not offered: these tools ask whether a change was sustained across periods, and a one-day period turns that into weekday seasonality.", "type": "string", "enum": ["weekly", "monthly"] }, "periods": { "default": 6, "type": "integer", "minimum": 2, "maximum": 104, "description": "How many whole periods to read. The period containing the anchor date is excluded when it is still running - see periodWindow.excludedPartialPeriod." }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 50, "description": "Minimum impressions over the whole window for an entity to be read at all. A floor on the range, not on each period - a per-period floor would delete the quiet periods a decline ends in.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "minDecliningPeriods": { "default": 3, "description": "Consecutive falling periods required for `sustained`.", "type": "integer", "minimum": 2, "maximum": 12 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "by": { "type": "string" }, "periodWindow": { "type": "object", "properties": { "granularity": { "type": "string" }, "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "window": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "periodStarts": { "type": "array", "items": { "type": "string" } }, "excludedPartialPeriod": { "anyOf": [ { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, { "type": "null" } ] } }, "required": [ "granularity", "periods", "window", "periodStarts", "excludedPartialPeriod" ], "additionalProperties": {} }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "pattern": { "type": "string" }, "metric": { "type": "string" }, "decliningPeriods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "dropFromPeak": { "type": "number" }, "estimatedClicksLost": { "type": "number" }, "changepoint": { "anyOf": [ { "type": "object", "properties": { "periodStart": { "type": "string" }, "before": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} }, "after": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} } }, "required": [ "periodStart", "before", "after" ], "additionalProperties": {} }, { "type": "null" } ] }, "series": { "type": "array", "items": { "type": "object", "properties": { "periodStart": { "type": "string" }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "periodStart", "clicks", "impressions", "position" ], "additionalProperties": {} } } }, "required": [ "pattern", "metric", "decliningPeriods", "dropFromPeak", "estimatedClicksLost", "changepoint", "series" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "pattern": { "type": "string" }, "metric": { "type": "string" }, "decliningPeriods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "dropFromPeak": { "type": "number" }, "estimatedClicksLost": { "type": "number" }, "changepoint": { "anyOf": [ { "type": "object", "properties": { "periodStart": { "type": "string" }, "before": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} }, "after": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} } }, "required": [ "periodStart", "before", "after" ], "additionalProperties": {} }, { "type": "null" } ] }, "series": { "type": "array", "items": { "type": "object", "properties": { "periodStart": { "type": "string" }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "periodStart", "clicks", "impressions", "position" ], "additionalProperties": {} } } }, "required": [ "pattern", "metric", "decliningPeriods", "dropFromPeak", "estimatedClicksLost", "changepoint", "series" ], "additionalProperties": {} }, { "type": "null" } ] }, "totalEstimatedClicksLost": { "type": "number" } }, "required": [ "label", "google", "bing", "totalEstimatedClicksLost" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "by", "periodWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find content gaps URL: /docs/mcp/tools/find_content_gaps Two halves under one name, labelled per row. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_content_gaps` **Cost:** Free by default, paid on `includeUncovered: true` - half A is a free read, while half B makes paid calls (one LLM expansion plus one keyword-volume request per engine), capped per workspace per day and confirmed before it runs. This tool changes nothing. One of its modes is opt-in and calls a paid external API, so the server asks you to confirm before running that part - and the backend caps it per workspace per day regardless of your answer. Everything else runs without a prompt. Two halves under one name, labelled per row. **Half A** (`rows`, always returned, free): visible queries (impressions above the threshold) for which no page on the site ranks well (worse than `positionThreshold`, or not ranking at all). **Half B** (`uncovered`, only on `includeUncovered=true`): topics the workspace's confirmed products and audiences imply and the site has no page for. Half B exists because half A is structurally blind to it - both engines list a query only if the site already drew an impression for it, so a topic you have no page for produces no row in any feed, and "this query is absent" is never evidence of anything. **Half B costs money on every call**: one LLM expansion of the workspace's content graph plus one keyword-volume request per engine. It is capped per workspace per day and asks for confirmation first. Read `uncoveredStatus` before concluding anything from an empty `uncovered`: `not_requested` means you did not ask for it, `unavailable` means the workspace has no confirmed products or audiences to imply a topic from, and `failed` means the expansion could not run - none of the three means the site has no uncovered demand. Half-B rows are clustered by topic with query variants nested, and `recommendedAction` is the whole answer: `create` (nothing of yours is near it), `refresh` (a page of yours is adjacent - expand `pageToRefresh` rather than publishing a second page against it), or `verify_inventory` (we searched fewer pages than the site has, so the absence is a limit and not a finding). Read `coverage.nearestDistance` alongside the verdict: the thresholds are declared, not calibrated. Every half-A row carries a `cannibalizationStatus`, because "no page ranks well for this" and "several of our pages are splitting it" look identical here and want opposite advice. `"confirmed"` re-labels the row `gapType: "cannibalized"` with `competingPages` listed: consolidate (see detect\_keyword\_cannibalization), do not strengthen one of them. `"possible"` names an unread sitemap URL in `suspectedPages` - open it before optimizing, since the two pages may be competing. `"none"` is the row this tool actually cleared: one page ranks and nothing else in the site's sitemap targets the query, so optimizing that page is the right move. `"unknown"` means the site has no usable sitemap inventory, so the row is a candidate to optimize rather than a cleared one. See `unsupportedInputs`. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `positionThreshold` | `number` | No | A query ranking worse than this position counts as a gap. Defaults to `15`. | | `minImpressions` | `integer` | No | Minimum current-window impressions for a query to count as visible. Defaults to `10`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `includeUncovered` | `boolean` | No | Run half B: expand the workspace's confirmed product/audience/pain-point graph into candidate search topics and report the ones it has no page for. **Paid** - one LLM expansion plus one keyword-volume request per engine - capped per workspace per day, and confirmed with the user first. Off by default, so half A stays a free read. Defaults to `false`. | | `uncoveredLimit` | `integer` | No | Maximum half-B topics to return, highest demand first. Defaults to `20`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "positionThreshold": { "default": 15, "description": "A query ranking worse than this position counts as a gap.", "type": "number", "minimum": 1 }, "minImpressions": { "default": 10, "description": "Minimum current-window impressions for a query to count as visible.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "includeUncovered": { "default": false, "description": "Run half B: expand the workspace's confirmed product/audience/pain-point graph into candidate search topics and report the ones it has no page for. **Paid** - one LLM expansion plus one keyword-volume request per engine - capped per workspace per day, and confirmed with the user first. Off by default, so half A stays a free read.", "type": "boolean" }, "uncoveredLimit": { "default": 20, "description": "Maximum half-B topics to return, highest demand first.", "type": "integer", "exclusiveMinimum": 0, "maximum": 100 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "impressions", "clicks", "position" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "impressions", "clicks", "position" ], "additionalProperties": {} }, { "type": "null" } ] }, "gapType": { "type": "string" }, "cannibalizationStatus": { "type": "string" }, "competingPages": { "type": "array", "items": { "type": "string" } }, "suspectedPages": { "type": "array", "items": { "type": "string" } } }, "required": [ "label", "google", "bing", "gapType", "cannibalizationStatus", "competingPages", "suspectedPages" ], "additionalProperties": {} } }, "uncovered": { "type": "array", "items": { "type": "object", "properties": { "gapType": { "type": "string" }, "topic": { "type": "string" }, "seed": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "seedKind": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mergedFrom": { "type": "array", "items": { "type": "string" } }, "queries": { "type": "array", "items": { "type": "object", "properties": { "query": { "type": "string" }, "searchVolume": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "demandSource": { "type": "string" }, "competition": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "competitionIndex": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "cpc": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "nearestDistance": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "query", "searchVolume", "demandSource", "competition", "competitionIndex", "cpc", "nearestDistance" ], "additionalProperties": {} } }, "coverage": { "type": "object", "properties": { "verdict": { "type": "string" }, "test": { "type": "string" }, "nearestPage": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "nearestDistance": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "nearestExcerpt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "searchedPages": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "siteKnownPages": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "siteKnownPagesReliable": { "type": "boolean" }, "inventoryComplete": { "type": "boolean" }, "coveredMaxDistance": { "type": "number" }, "adjacentMaxDistance": { "type": "number" } }, "required": [ "verdict", "test", "nearestPage", "nearestDistance", "nearestExcerpt", "searchedPages", "siteKnownPages", "siteKnownPagesReliable", "inventoryComplete", "coveredMaxDistance", "adjacentMaxDistance" ], "additionalProperties": {} }, "demand": { "type": "object", "properties": { "searchVolume": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "source": { "type": "string" }, "variantsPriced": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "variantsTotal": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "searchVolume", "source", "variantsPriced", "variantsTotal" ], "additionalProperties": {} }, "cannibalizationStatus": { "type": "string" }, "pageToRefresh": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "recommendedAction": { "type": "string" } }, "required": [ "gapType", "topic", "seed", "seedKind", "mergedFrom", "queries", "coverage", "demand", "cannibalizationStatus", "pageToRefresh", "recommendedAction" ], "additionalProperties": {} } }, "uncoveredStatus": { "type": "object", "properties": { "status": { "type": "string" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "topicsProposed": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "topicsReturned": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "topicsCovered": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "topicsVisibleInSearch": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "queriesExpanded": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "costUsd": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "status", "reason", "topicsProposed", "topicsReturned", "topicsCovered", "topicsVisibleInSearch", "queriesExpanded", "costUsd" ], "additionalProperties": {} }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "rows", "uncovered", "uncoveredStatus", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find cross-engine performance gaps URL: /docs/mcp/tools/find_cross_engine_gaps Queries or pages where Google and Bing performance diverge materially in the current window. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_cross_engine_gaps` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Queries or pages where Google and Bing performance diverge materially in the current window. Requires both engines to be connected for this site - an unconnected engine is not a 'gap', so the tool returns no rows and says why in `unsupportedInputs` instead. For the same reason this tool cannot answer for a non-web searchVertical at all: Bing has no image or video vertical to compare against, so ask for 'web' here. A position gap should be read against Bing's weekly cadence, not treated as a daily signal. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `by` | `"query" \| "page"` | No | Dimension to read `/breakdown` by. Defaults to `"query"`. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum impressions on the leading engine to consider a row. Defaults to `10`. | | `minGapRatio` | `number` | No | Minimum (larger / smaller) ratio, on clicks or impressions, to flag a row as a gap. Defaults to `2`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "by": { "default": "query", "description": "Dimension to read `/breakdown` by.", "type": "string", "enum": ["query", "page"] }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 10, "description": "Minimum impressions on the leading engine to consider a row.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "minGapRatio": { "default": 2, "description": "Minimum (larger / smaller) ratio, on clicks or impressions, to flag a row as a gap.", "type": "number", "exclusiveMinimum": 1 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "by": { "type": "string" }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": ["clicks", "impressions", "position"], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": ["clicks", "impressions", "position"], "additionalProperties": {} }, "leadingEngine": { "type": "string", "enum": ["google", "bing"] }, "clicksGapRatio": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressionsGapRatio": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "label", "google", "bing", "leadingEngine", "clicksGapRatio", "impressionsGapRatio" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "by", "currentWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find CTR opportunities URL: /docs/mcp/tools/find_ctr_opportunities Queries or pages whose actual CTR falls well below what the site's own position-based CTR curve predicts for their rank. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_ctr_opportunities` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Queries or pages whose actual CTR falls well below what the site's own position-based CTR curve predicts for their rank. The benchmark is site-wide only - there is no per-country or per-device curve (see `unsupportedInputs`). Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `by` | `"query" \| "page"` | No | Dimension to read `/breakdown` by. Defaults to `"query"`. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum current-window impressions to consider a row. Defaults to `10`. | | `underperformanceRatio` | `number` | No | How far below the expected CTR (as a fraction of it) a row must fall to be flagged, e.g. 0.3 = at least 30% below. Defaults to `0.3`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "by": { "default": "query", "description": "Dimension to read `/breakdown` by.", "type": "string", "enum": ["query", "page"] }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 10, "description": "Minimum current-window impressions to consider a row.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "underperformanceRatio": { "default": 0.3, "description": "How far below the expected CTR (as a fraction of it) a row must fall to be flagged, e.g. 0.3 = at least 30% below.", "type": "number", "exclusiveMinimum": 0, "maximum": 1 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "by": { "type": "string" }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "position": { "type": "number" }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "type": "number" }, "expectedCtr": { "type": "number" }, "residual": { "type": "number" } }, "required": [ "position", "impressions", "clicks", "ctr", "expectedCtr", "residual" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "position": { "type": "number" }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "type": "number" }, "expectedCtr": { "type": "number" }, "residual": { "type": "number" } }, "required": [ "position", "impressions", "clicks", "ctr", "expectedCtr", "residual" ], "additionalProperties": {} }, { "type": "null" } ] } }, "required": ["label", "google", "bing"], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "by", "currentWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find emerging keywords URL: /docs/mcp/tools/find_emerging_keywords Queries with sustained or accelerating growth over several whole periods - find_content_decay's classifier with the sign flipped. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_emerging_keywords` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Queries with sustained or accelerating growth over several whole periods - find\_content\_decay's classifier with the sign flipped. Read on **impressions**, not clicks: an emerging query is one being asked more often, and clicks lag it by however long the page takes to rank, so a query with rising impressions and flat clicks is exactly the row worth acting on. `pattern` is `accelerating` when the last rise was bigger than the one before it. `noveltyStatus` is `new` when the query drew nothing at the start of the window - on Bing it is always `unsupported`, because a query appearing there may only have entered a top-N cursor, and on a window Google truncated the same doubt applies to Google. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------------ | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `granularity` | `"weekly" \| "monthly"` | No | Length of one period in the series. Daily is deliberately not offered: these tools ask whether a change was sustained across periods, and a one-day period turns that into weekday seasonality. Defaults to `"monthly"`. | | `periods` | `integer` | No | How many whole periods to read. The period containing the anchor date is excluded when it is still running - see periodWindow\.excludedPartialPeriod. Defaults to `6`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum impressions over the whole window. Keep this low - an emerging query is small by definition. Defaults to `20`. | | `minRisingPeriods` | `integer` | No | Consecutive rising periods required to qualify. Defaults to `3`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "granularity": { "default": "monthly", "description": "Length of one period in the series. Daily is deliberately not offered: these tools ask whether a change was sustained across periods, and a one-day period turns that into weekday seasonality.", "type": "string", "enum": ["weekly", "monthly"] }, "periods": { "default": 6, "type": "integer", "minimum": 2, "maximum": 104, "description": "How many whole periods to read. The period containing the anchor date is excluded when it is still running - see periodWindow.excludedPartialPeriod." }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 20, "description": "Minimum impressions over the whole window. Keep this low - an emerging query is small by definition.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "minRisingPeriods": { "default": 3, "description": "Consecutive rising periods required to qualify.", "type": "integer", "minimum": 2, "maximum": 12 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "periodWindow": { "type": "object", "properties": { "granularity": { "type": "string" }, "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "window": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "periodStarts": { "type": "array", "items": { "type": "string" } }, "excludedPartialPeriod": { "anyOf": [ { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, { "type": "null" } ] } }, "required": [ "granularity", "periods", "window", "periodStarts", "excludedPartialPeriod" ], "additionalProperties": {} }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "pattern": { "type": "string" }, "risingPeriods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "growthRatio": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "noveltyStatus": { "type": "string" }, "latestClicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "latestImpressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "series": { "type": "array", "items": { "type": "object", "properties": { "periodStart": { "type": "string" }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "periodStart", "clicks", "impressions", "position" ], "additionalProperties": {} } } }, "required": [ "pattern", "risingPeriods", "growthRatio", "noveltyStatus", "latestClicks", "latestImpressions", "series" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "pattern": { "type": "string" }, "risingPeriods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "growthRatio": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "noveltyStatus": { "type": "string" }, "latestClicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "latestImpressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "series": { "type": "array", "items": { "type": "object", "properties": { "periodStart": { "type": "string" }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "periodStart", "clicks", "impressions", "position" ], "additionalProperties": {} } } }, "required": [ "pattern", "risingPeriods", "growthRatio", "noveltyStatus", "latestClicks", "latestImpressions", "series" ], "additionalProperties": {} }, { "type": "null" } ] }, "totalLatestImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "label", "google", "bing", "totalLatestImpressions" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "periodWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find low-hanging keywords URL: /docs/mcp/tools/find_low_hanging_keywords High-impression queries ranking just off page one, with an estimated click uplift if promoted to the site's own page-one (`pos1_3`) CTR - built from the site's own current-window rows, not an industry benchmark. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_low_hanging_keywords` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. High-impression queries ranking just off page one, with an estimated click uplift if promoted to the site's own page-one (`pos1_3`) CTR - built from the site's own current-window rows, not an industry benchmark. `estimatedUpliftClicks` is null where the site has too little page-one data of its own to project from. Branded queries are excluded by default - you cannot optimize your way up your own brand's SERP, and their outlier CTR would inflate the page-one target every other row is measured against. Set excludeBranded=false to see them; if the workspace has no brand terms configured, that is reported in `unsupportedInputs` rather than silently ignored. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `positionMin` | `number` | No | Lower bound (inclusive) of the 'close to page one' band. Defaults to `4`. | | `positionMax` | `number` | No | Upper bound (inclusive) of the 'close to page one' band. Defaults to `20`. | | `minImpressions` | `integer` | No | Minimum current-window impressions to consider a query. Defaults to `10`. | | `excludeBranded` | `boolean` | No | Drop queries containing the workspace's brand terms. Inspect or change those with get/set\_workspace\_brand\_terms. Defaults to `true`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "positionMin": { "default": 4, "description": "Lower bound (inclusive) of the 'close to page one' band.", "type": "number", "minimum": 1 }, "positionMax": { "default": 20, "description": "Upper bound (inclusive) of the 'close to page one' band.", "type": "number", "minimum": 1 }, "minImpressions": { "default": 10, "description": "Minimum current-window impressions to consider a query.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "excludeBranded": { "default": true, "description": "Drop queries containing the workspace's brand terms. Inspect or change those with get/set_workspace_brand_terms.", "type": "boolean" }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "position": { "type": "number" }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "type": "number" }, "estimatedUpliftClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "position", "impressions", "clicks", "ctr", "estimatedUpliftClicks" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "position": { "type": "number" }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "type": "number" }, "estimatedUpliftClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "position", "impressions", "clicks", "ctr", "estimatedUpliftClicks" ], "additionalProperties": {} }, { "type": "null" } ] } }, "required": ["label", "google", "bing"], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Find new and lost keywords URL: /docs/mcp/tools/find_new_and_lost_keywords Query-level `/breakdown` rows classified per engine as new, lost, improved, declined, or stable between the current window and the immediately preceding one of the same length. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `find_new_and_lost_keywords` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Query-level `/breakdown` rows classified per engine as new, lost, improved, declined, or stable between the current window and the immediately preceding one of the same length. Rows where neither engine shows a real signal are omitted. On Bing, a query going quiet cannot be told apart from it simply falling out of Bing's top-N-by-traffic feed - a would-be 'lost' verdict on Bing is reported as `status: "unsupported"` instead of inferred. Above Google's row ceiling the same applies to Google: on a window Google truncated, a 'lost' verdict is downgraded the same way, and so is 'new' when the *comparison* window was the truncated one. 'improved' and 'declined' are never downgraded - they compare two rows that both exist. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | A period needs at least this many impressions to count as 'visible' - below it, a row reads as insufficient data rather than a guessed new/lost verdict. Defaults to `5`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 5, "description": "A period needs at least this many impressions to count as 'visible' - below it, a row reads as insufficient data rather than a guessed new/lost verdict.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "comparisonWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "type": "object", "properties": { "status": { "type": "string" }, "current": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "clicks", "impressions", "position" ], "additionalProperties": {} }, "previous": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "clicks", "impressions", "position" ], "additionalProperties": {} }, "deltaClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "status", "current", "previous", "deltaClicks", "deltaImpressions", "deltaPosition" ], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "status": { "type": "string" }, "current": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "clicks", "impressions", "position" ], "additionalProperties": {} }, "previous": { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "clicks", "impressions", "position" ], "additionalProperties": {} }, "deltaClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "status", "current", "previous", "deltaClicks", "deltaImpressions", "deltaPosition" ], "additionalProperties": {} } }, "required": ["label", "google", "bing"], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "comparisonWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Generate search performance report URL: /docs/mcp/tools/generate_search_performance_report A Google + Bing performance report for one site or several, assembled by calling the other tools and embedding their responses whole - so a section here is the same answer the tool gives on its own, and you can read it the same way, `unsup… {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `generate_search_performance_report` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. A Google + Bing performance report for one site or several, assembled by calling the other tools and embedding their responses whole - so a section here is the same answer the tool gives on its own, and you can read it the same way, `unsupportedInputs` included. `sites` is comma-separated and capped at 10; they are read one after another, so a wide report is a slow call. Sections default to summary + trend + topMovers; the other four (opportunities, siteSections, brandSplit, crossEngine) each cost at least one more lake scan **per site**, so ask for them only when the report is about them. Partial results are normal and are labelled, never hidden: a site you cannot reach comes back as a `status: "error"` row and a section that failed as `status: "failed"` in that site's `sections`. Report those rather than presenting the rest as the whole picture. `portfolio` is a cross-site total of clicks and impressions only, and is null unless every counted site resolved to the same window - pass `to` to pin them and get one. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------------ | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `sites` | `string` | Yes | Comma-separated sites (bare domains or URLs), at most 10. Each must resolve to a workspace you own; call list\_sites if unsure of the exact domains. | | `sections` | `string` | No | Comma-separated sections: summary, trend, topMovers, opportunities, siteSections, brandSplit, crossEngine. Defaults to `"summary,trend,topMovers"`. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the window (YYYY-MM-DD), applied to every site. Omitted, each site anchors to its own latest measured day - which is what the standalone tools do, but leaves the sites on different windows and suppresses the portfolio total. | | `trendGranularity` | `"daily" \| "weekly" \| "monthly"` | No | Bucket size for the trend section. Weekly by default: a daily series per site makes a multi-site report very large for no extra signal at report altitude. Defaults to `"weekly"`. | | `rowLimit` | `integer` | No | Rows per table-shaped section, per site. Defaults to `10`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "sites": { "type": "string", "minLength": 1, "description": "Comma-separated sites (bare domains or URLs), at most 10. Each must resolve to a workspace you own; call list_sites if unsure of the exact domains." }, "sections": { "default": "summary,trend,topMovers", "description": "Comma-separated sections: summary, trend, topMovers, opportunities, siteSections, brandSplit, crossEngine.", "type": "string" }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the window (YYYY-MM-DD), applied to every site. Omitted, each site anchors to its own latest measured day - which is what the standalone tools do, but leaves the sites on different windows and suppresses the portfolio total.", "type": "string" }, "trendGranularity": { "default": "weekly", "description": "Bucket size for the trend section. Weekly by default: a daily series per site makes a multi-site report very large for no extra signal at report altitude.", "type": "string", "enum": ["daily", "weekly", "monthly"] }, "rowLimit": { "default": 10, "description": "Rows per table-shaped section, per site.", "type": "integer", "exclusiveMinimum": 0, "maximum": 100 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["sites"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "requestedSites": { "type": "array", "items": { "type": "string" } }, "sections": { "type": "array", "items": { "type": "string" } }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "dateTo": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "siteReports": { "type": "array", "items": { "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "status": { "type": "string" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "summary": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "trend": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "topMovers": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "opportunities": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "siteSections": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "brandSplit": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "crossEngine": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "sections": { "type": "array", "items": { "type": "object", "properties": { "section": { "type": "string" }, "status": { "type": "string" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["section", "status"], "additionalProperties": {} } } }, "required": [ "site", "workspaceId", "status", "searchVertical", "sections" ], "additionalProperties": {} } }, "portfolio": { "anyOf": [ { "type": "object", "properties": { "sitesCounted": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "window": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "comparisonWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "totalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "previousTotalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "previousTotalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "deltaImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "sitesCounted", "window", "comparisonWindow", "totalClicks", "totalImpressions", "ctr", "previousTotalClicks", "previousTotalImpressions", "deltaClicks", "deltaImpressions" ], "additionalProperties": {} }, { "type": "null" } ] }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } } }, "required": [ "requestedSites", "sections", "searchVertical", "windowDays", "siteReports", "portfolio", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Run a custom search-analytics query URL: /docs/mcp/tools/get_advanced_search_analytics The composable read: you choose the grouping, filters, sort and limit instead of asking one of the named questions above. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_advanced_search_analytics` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. The composable read: you choose the grouping, filters, sort and limit instead of asking one of the named questions above. Use it when no other tool asks what you need - a named tool is better whenever one fits, because it also classifies and explains its rows, and this one only groups and counts. **Dimensions** are `date`, `query`, `page`, `searchEngine` and `searchVertical` - the columns the data actually has. There is no country or device breakdown for either engine: Bing Webmaster Tools exposes none on any traffic endpoint, and we do not ingest Google's. Every result is whole-property, and switching engine will not change that. Asking for them is an error, not an empty result. **Filters** are `{field, operator, value}` and are ANDed; there is no OR and no nesting. Text fields (`query`, `page`) take eq / contains / notContains / startsWith / endsWith, matched case-insensitively; metrics (`clicks`, `impressions`, `ctr`, `position`) take eq / gt / gte / lt / lte and are applied after the engines are merged, so a threshold is on the row you see. `page` is matched on its canonical form - no scheme, no `www.`, no trailing slash - so use `contains` for a section like `/blog/` rather than `startsWith`. Filtering a field you do not group by is fine and is often the point. **`position` is null on every row unless you group by `searchEngine`.** Google's and Bing's position scales are not confirmed comparable, so a blended figure would not be a real number on either engine. Add `searchEngine` to dimensions to get it. **Read `sources` before trusting an absence.** A request with no `query` or `page` involved is served from the complete daily rollups and agrees exactly with get\_performance\_trend. Anything grouped or filtered by one of them alone is served from a breakdown Google stores as roughly the top 5,000 rows per day, so a missing row is not evidence of no traffic; using `query` and `page` together reads the deeper raw feed instead. `sources[].read: false` means that engine was not read at all - not attached, or excluded - which is different from measuring zero. `truncated` covers both your `limit` and the read's own scan ceiling. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `dimensions` | `("date" \| "query" \| "page" \| "searchEngine" \| "searchVertical")[]` | No | What to group rows by. An empty list returns one row for the whole window. Order is cosmetic. Defaults to `["query"]`. | | `filters` | `({ field, operator, value })[]` | No | Predicates, ANDed together. Defaults to `[]`. | | `sort` | `{ field, direction }` | No | Defaults to clicks descending. A dimension may be sorted on only when it is one of the dimensions grouped by. Rows with no value for the sort field always come last. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum window impressions for a row to be returned. Applied in storage, so raising it is the cheapest way to keep a wide query - `date` plus `query`, say - from hitting the read's scan ceiling. Defaults to `1`. | | `limit` | `integer` | No | Maximum rows returned. `matchedRows` reports how many matched before this cut. Defaults to `100`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Pin one Google vertical. Unlike the other tools this has no default and is optional, because 'searchVertical' can also be a dimension - pin it or split by it, not both. Either way Bing is excluded from the response whenever a vertical is involved, since it splits none. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "dimensions": { "default": ["query"], "description": "What to group rows by. An empty list returns one row for the whole window. Order is cosmetic.", "type": "array", "items": { "type": "string", "enum": [ "date", "query", "page", "searchEngine", "searchVertical" ] } }, "filters": { "default": [], "description": "Predicates, ANDed together.", "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string", "enum": [ "query", "page", "searchEngine", "clicks", "impressions", "ctr", "position" ] }, "operator": { "type": "string", "enum": [ "eq", "contains", "notContains", "startsWith", "endsWith", "gt", "gte", "lt", "lte" ] }, "value": { "type": "string", "minLength": 1, "description": "Text to match, or a number for a metric field." } }, "required": ["field", "operator", "value"] } }, "sort": { "description": "Defaults to clicks descending. A dimension may be sorted on only when it is one of the dimensions grouped by. Rows with no value for the sort field always come last.", "type": "object", "properties": { "field": { "type": "string", "enum": [ "clicks", "impressions", "ctr", "position", "date", "query", "page", "searchEngine", "searchVertical" ] }, "direction": { "default": "desc", "type": "string", "enum": ["asc", "desc"] } }, "required": ["field"] }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 1, "description": "Minimum window impressions for a row to be returned. Applied in storage, so raising it is the cheapest way to keep a wide query - `date` plus `query`, say - from hitting the read's scan ceiling.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "limit": { "default": 100, "description": "Maximum rows returned. `matchedRows` reports how many matched before this cut.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "description": "Pin one Google vertical. Unlike the other tools this has no default and is optional, because 'searchVertical' can also be a dimension - pin it or split by it, not both. Either way Bing is excluded from the response whenever a vertical is involved, since it splits none.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "anyOf": [ { "type": "string", "enum": ["web", "image", "video"] }, { "type": "null" } ] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "dimensions": { "type": "array", "items": { "type": "string", "enum": [ "date", "query", "page", "searchEngine", "searchVertical" ] } }, "filters": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "operator": { "type": "string" }, "value": { "type": "string" } }, "required": ["field", "operator", "value"], "additionalProperties": {} } }, "sort": { "type": "object", "properties": { "field": { "type": "string" }, "direction": { "type": "string" } }, "required": ["field", "direction"], "additionalProperties": {} }, "limit": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "grain": { "type": "string", "enum": ["site", "query", "page", "query_page"] }, "sources": { "type": "array", "items": { "type": "object", "properties": { "searchEngine": { "type": "string", "enum": ["google", "bing"] }, "read": { "type": "boolean" }, "datasets": { "type": "array", "items": { "type": "string" } }, "rowCapped": { "type": "boolean" }, "note": { "type": "string" } }, "required": [ "searchEngine", "read", "datasets", "rowCapped", "note" ], "additionalProperties": {} } }, "rows": { "type": "array", "items": { "type": "object", "properties": { "date": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "query": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "page": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "searchEngine": { "anyOf": [ { "type": "string", "enum": ["google", "bing"] }, { "type": "null" } ] }, "searchVertical": { "anyOf": [ { "type": "string", "enum": ["web", "image", "video"] }, { "type": "null" } ] }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "required": [ "searchEngine", "searchVertical", "clicks", "impressions", "ctr", "position", "positionCoverageImpressions" ], "additionalProperties": {} } }, "matchedRows": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "truncated": { "type": "boolean" }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "dimensions", "filters", "sort", "limit", "grain", "sources", "rows", "matchedRows", "truncated", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Get campaign URL: /docs/mcp/tools/get_campaign Get an AEO campaign's detail: SERP snapshot, analysis, brief, and search-intent-classification ids. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_campaign` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Get an AEO campaign's detail: SERP snapshot, analysis, brief, and search-intent-classification ids. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------ | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string" }, "serpSnapshotIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "contentAnalysisIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "contentBriefIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "searchIntentClassificationIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "searchIntents": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": {} } }, "location": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "language": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] }, "painPoints": { "anyOf": [ { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": {} } }, { "type": "null" } ] }, "targetAudiences": { "anyOf": [ { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["id", "name"], "additionalProperties": {} } }, { "type": "null" } ] }, "targetPrompts": { "type": "array", "items": { "type": "object", "properties": { "topic": { "type": "string" }, "location": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "language": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "llmProviderIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "promptId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "targetAudiences": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "painPointConnected": { "type": "boolean" }, "painPointId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] } }, "required": ["topic", "location", "language"], "additionalProperties": {} } }, "fanOutTriggered": { "type": "boolean" } }, "required": ["id", "status", "searchIntents"], "additionalProperties": false } ```
--- # Get entity list URL: /docs/mcp/tools/get_campaign_entities Get the extracted entity list for a campaign. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_campaign_entities` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Get the extracted entity list for a campaign. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------ | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "object", "properties": {}, "additionalProperties": {} } }, "prompt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "queriesFanOut": { "anyOf": [ { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": {} } }, { "type": "null" } ] } }, "required": ["data"], "additionalProperties": false } ```
--- # Get related questions & pain points URL: /docs/mcp/tools/get_campaign_related_questions Get a campaign's related questions and the discovered pain points. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_campaign_related_questions` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Get a campaign's related questions and the discovered pain points. Call this BEFORE confirm\_pain\_points so you can present the pain points to the user. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------ | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": {} } }, "painPoints": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "urls": { "type": "array", "items": { "type": "string" } } }, "required": ["name", "urls"], "additionalProperties": {} } }, "workspaceId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "promptIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } } }, "required": ["data", "painPoints", "campaignId", "promptIds"], "additionalProperties": false } ```
--- # Get search-engine connection status URL: /docs/mcp/tools/get_connection_status Live: connected Google + Bing accounts, the property/site attached to this site's workspace, and the last time its search data synced. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_connection_status` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Live: connected Google + Bing accounts, the property/site attached to this site's workspace, and the last time its search data synced. Quota is not included - neither engine's quota consumption is tracked today. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | --------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "google": { "type": "object", "properties": { "connected": { "type": "boolean" }, "credentials": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "googleAccountEmail": { "type": "string" }, "scopes": { "type": "string" }, "createdAt": { "type": "string" }, "reauthRequiredAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "googleAccountEmail", "scopes", "createdAt" ], "additionalProperties": {} } }, "selectedProperty": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] } }, "required": ["connected", "credentials"], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "connected": { "type": "boolean" }, "credentials": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "scopes": { "type": "string" }, "createdAt": { "type": "string" }, "reauthRequiredAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["id", "scopes", "createdAt"], "additionalProperties": {} } }, "selectedSite": { "anyOf": [ { "type": "object", "properties": {}, "additionalProperties": {} }, { "type": "null" } ] } }, "required": ["connected", "credentials"], "additionalProperties": {} }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["site", "workspaceId", "google", "bing"], "additionalProperties": false } ```
--- # Get evaluation/optimization result URL: /docs/mcp/tools/get_evaluation_result Poll an evaluate_article or optimize_article background job by its jobId. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_evaluation_result` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Poll an evaluate\_article or optimize\_article background job by its jobId. Returns \{ jobId, status, kind, cached, result, error }. Status is one of "queued", "running", "completed", "failed". When "completed", `result` holds the score (and, for optimize, the improved article). When "failed", `error` explains why. Poll about every 5 seconds while queued/running. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | --------- | --------------- | -------- | ----- | | `jobId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "jobId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["jobId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "jobId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "status": { "type": "string" }, "kind": { "type": "string" }, "cached": { "type": "boolean" }, "result": { "anyOf": [ { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, { "type": "null" } ] }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["jobId", "status", "kind"], "additionalProperties": false } ```
--- # Get latest article draft URL: /docs/mcp/tools/get_latest_article Get the latest user-generated content (article draft + meta tags) for a campaign - the input for evaluate_article, optimize_article, and quick_save. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_latest_article` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Get the latest user-generated content (article draft + meta tags) for a campaign - the input for evaluate\_article, optimize\_article, and quick\_save. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------ | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["campaignId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "originalContent": { "type": "string" }, "version": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "updatedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "metaTitle": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "metaDescription": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["originalContent", "version", "campaignId"], "additionalProperties": false } ```
--- # Get the queries one page ranks for URL: /docs/mcp/tools/get_page_queries The queries a specific page drew impressions and clicks for, per engine, over the window. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_page_queries` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. The queries a specific page drew impressions and clicks for, per engine, over the window. `page` is matched canonically, so scheme, `www.`, a fragment and a trailing slash do not matter. These rows are a page×query join and under-count the page's own totals on both engines - Google drops anonymized queries from any grouping that names `query`, and Bing's rows are fanned out from its top-pages cursor on weekly snapshot dates. Compare queries with each other here; take the page's real totals from get\_performance\_trend. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `page` | `string` | Yes | The page to look up, as a full URL or a path - matched canonically against both engines' spellings. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum window impressions for a query to be returned. Defaults to `1`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "page": { "type": "string", "minLength": 1, "description": "The page to look up, as a full URL or a path - matched canonically against both engines' spellings." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 1, "description": "Minimum window impressions for a query to be returned.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site", "page"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "page": { "type": "string" }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "clicks", "impressions", "position", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "clicks", "impressions", "position", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "totalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "label", "google", "bing", "totalClicks", "totalImpressions" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "page", "currentWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Get site performance trend URL: /docs/mcp/tools/get_performance_trend Daily, weekly, or monthly Google + Bing performance trend for a site. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_performance_trend` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Daily, weekly, or monthly Google + Bing performance trend for a site. Weekly/monthly points roll up the daily series (impression-weighted position, summed clicks/impressions). Site-level only in this version - filtering by a specific page or query is not yet supported. Omit from/to to get the full available history. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `granularity` | `"daily" \| "weekly" \| "monthly"` | No | Defaults to `"daily"`. | | `from` | `string` | No | Start date (YYYY-MM-DD), inclusive. Omit for full history. | | `to` | `string` | No | End date (YYYY-MM-DD), inclusive. Omit for full history. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "granularity": { "default": "daily", "type": "string", "enum": ["daily", "weekly", "monthly"] }, "from": { "description": "Start date (YYYY-MM-DD), inclusive. Omit for full history.", "type": "string" }, "to": { "description": "End date (YYYY-MM-DD), inclusive. Omit for full history.", "type": "string" }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "granularity": { "type": "string" }, "points": { "type": "array", "items": { "type": "object", "properties": { "date": { "type": "string" }, "google": { "type": "object", "properties": { "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "additionalProperties": {} }, "bing": { "type": "object", "properties": { "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "additionalProperties": {} } }, "required": ["date", "google", "bing"], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "granularity", "points", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Get the pages that rank for one query URL: /docs/mcp/tools/get_query_pages The pages of the site that surfaced for a specific query, per engine, over the window. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_query_pages` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. The pages of the site that surfaced for a specific query, per engine, over the window. Same join as get\_page\_queries with the pivot flipped, and the same coverage caveats. More than one row here means several of the site's pages compete for the query - detect\_keyword\_cannibalization is the tool that classifies that across the whole site. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `query` | `string` | Yes | The search query to look up; matched case-insensitively. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `minImpressions` | `integer` | No | Minimum window impressions for a page to be returned. Defaults to `1`. | | `limit` | `integer` | No | Maximum rows to return, sorted by relevance. Defaults to `50`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "query": { "type": "string", "minLength": 1, "description": "The search query to look up; matched case-insensitively." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 1, "description": "Minimum window impressions for a page to be returned.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "limit": { "default": 50, "description": "Maximum rows to return, sorted by relevance.", "type": "integer", "exclusiveMinimum": 0, "maximum": 500 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site", "query"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "query": { "type": "string" }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "rows": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "google": { "anyOf": [ { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "clicks", "impressions", "position", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "bing": { "anyOf": [ { "type": "object", "properties": { "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "clicks", "impressions", "position", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "totalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "label", "google", "bing", "totalClicks", "totalImpressions" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "query", "currentWindow", "rows", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Get server info URL: /docs/mcp/tools/get_server_info Return Composway MCP server freshness metadata: server version, tools schema version, git/deploy metadata, and Worker version metadata when available. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_server_info` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Return Composway MCP server freshness metadata: server version, tools schema version, git/deploy metadata, and Worker version metadata when available. ## Parameters [#parameters] This tool takes no parameters.
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {} } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "name": { "type": "string" }, "serverVersion": { "type": "string" }, "toolsSchemaVersion": { "type": "string" }, "gitSha": { "type": "string" }, "deployedAt": { "type": "string" }, "workerVersionId": { "type": "string" }, "workerVersionTag": { "type": "string" } }, "required": [ "name", "serverVersion", "toolsSchemaVersion", "gitSha", "deployedAt" ], "additionalProperties": false } ```
--- # Get server status URL: /docs/mcp/tools/get_server_status Start here. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_server_status` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Start here. Return Composway MCP status for AI clients: current server version, tools schema version, auth status, available capability groups, changelog, and instructions for detecting changed tool schemas or workflow guidance. ## Parameters [#parameters] This tool takes no parameters.
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {} } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "server": { "type": "string" }, "version": { "type": "string" }, "schemaVersion": { "type": "string" }, "lastUpdated": { "type": "string" }, "changelog": { "type": "array", "items": { "type": "string" } }, "capabilityGroups": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "tools": { "type": "array", "items": { "type": "string" } }, "readOnly": { "type": "boolean" }, "instruction": { "type": "string" } }, "required": [ "name", "description", "tools", "readOnly", "instruction" ], "additionalProperties": {} } }, "auth": { "type": "object", "properties": { "status": { "type": "string", "const": "authenticated" }, "subject": { "type": "string" }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "accessTokenExpiresInSeconds": { "type": "number" } }, "required": ["status"], "additionalProperties": {} }, "toolSchemaStatus": { "type": "object", "properties": { "currentSchemaVersion": { "type": "string" }, "currentToolCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "toolManifestAvailable": { "type": "boolean" }, "instruction": { "type": "string" } }, "required": [ "currentSchemaVersion", "currentToolCount", "toolManifestAvailable", "instruction" ], "additionalProperties": {} }, "workflowInstructionStatus": { "type": "object", "properties": { "source": { "type": "string" }, "instruction": { "type": "string" } }, "required": ["source", "instruction"], "additionalProperties": {} }, "recommendedNextStep": { "type": "string" } }, "required": [ "server", "version", "schemaVersion", "lastUpdated", "changelog", "capabilityGroups", "auth", "toolSchemaStatus", "workflowInstructionStatus", "recommendedNextStep" ], "additionalProperties": false } ```
--- # Get site performance snapshot URL: /docs/mcp/tools/get_site_snapshot Summarizes a site's overall Google + Bing search performance over a window and compares it with the immediately preceding window of the same length. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_site_snapshot` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Summarizes a site's overall Google + Bing search performance over a window and compares it with the immediately preceding window of the same length. Returns scalars only (totals, CTR, weighted average position, rank-bucket counts for one anchor date) - never the daily series; call get\_performance\_trend for that. `total.avgPosition` is always null (Bing's position scale against Google's is unverified, so a blended figure would not be a real number on either engine) and there is no cross-engine rank-bucket total (distinct-query counts are not additive across engines). No country or device breakdown is available for either engine - Bing exposes none on any traffic endpoint, and we do not ingest Google's. `searchVertical` does work, but Google-only: on image or video the bing and rankBuckets.bing blocks are absent and `total` is Google's figure alone. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current (and comparison) window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `rankBucketsDate` | `string` | No | Anchor date (YYYY-MM-DD) for the rank-bucket snapshot. Resolved per engine by default to the latest date that engine has bucket data for - the two engines usually differ. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current (and comparison) window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "rankBucketsDate": { "description": "Anchor date (YYYY-MM-DD) for the rank-bucket snapshot. Resolved per engine by default to the latest date that engine has bucket data for - the two engines usually differ.", "type": "string" }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "comparisonWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "google": { "type": "object", "properties": { "current": { "type": "object", "properties": { "attached": { "type": "boolean" }, "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgClicksPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgImpressionsPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "attached", "totalClicks", "totalImpressions", "ctr", "avgPosition", "positionCoverageImpressions", "avgClicksPerDay", "avgImpressionsPerDay", "measuredDays", "windowDays" ], "additionalProperties": {} }, "comparison": { "type": "object", "properties": { "attached": { "type": "boolean" }, "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgClicksPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgImpressionsPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "attached", "totalClicks", "totalImpressions", "ctr", "avgPosition", "positionCoverageImpressions", "avgClicksPerDay", "avgImpressionsPerDay", "measuredDays", "windowDays" ], "additionalProperties": {} }, "delta": { "type": "object", "properties": { "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "totalClicks", "totalImpressions", "ctr", "avgPosition" ], "additionalProperties": {} } }, "required": ["current", "comparison", "delta"], "additionalProperties": false }, "bing": { "type": "object", "properties": { "current": { "type": "object", "properties": { "attached": { "type": "boolean" }, "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgClicksPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgImpressionsPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "attached", "totalClicks", "totalImpressions", "ctr", "avgPosition", "positionCoverageImpressions", "avgClicksPerDay", "avgImpressionsPerDay", "measuredDays", "windowDays" ], "additionalProperties": {} }, "comparison": { "type": "object", "properties": { "attached": { "type": "boolean" }, "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgClicksPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgImpressionsPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "attached", "totalClicks", "totalImpressions", "ctr", "avgPosition", "positionCoverageImpressions", "avgClicksPerDay", "avgImpressionsPerDay", "measuredDays", "windowDays" ], "additionalProperties": {} }, "delta": { "type": "object", "properties": { "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "totalClicks", "totalImpressions", "ctr", "avgPosition" ], "additionalProperties": {} } }, "required": ["current", "comparison", "delta"], "additionalProperties": false }, "total": { "type": "object", "properties": { "current": { "type": "object", "properties": { "attached": { "type": "boolean" }, "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgClicksPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgImpressionsPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "attached", "totalClicks", "totalImpressions", "ctr", "avgPosition", "positionCoverageImpressions", "avgClicksPerDay", "avgImpressionsPerDay", "measuredDays", "windowDays" ], "additionalProperties": {} }, "comparison": { "type": "object", "properties": { "attached": { "type": "boolean" }, "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "positionCoverageImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgClicksPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgImpressionsPerDay": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "windowDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "attached", "totalClicks", "totalImpressions", "ctr", "avgPosition", "positionCoverageImpressions", "avgClicksPerDay", "avgImpressionsPerDay", "measuredDays", "windowDays" ], "additionalProperties": {} }, "delta": { "type": "object", "properties": { "totalClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "totalImpressions": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "avgPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "totalClicks", "totalImpressions", "ctr", "avgPosition" ], "additionalProperties": {} }, "coverage": { "type": "object", "properties": { "googleMeasuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "bingMeasuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "googleFirstMeasured": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "googleLastMeasured": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bingFirstMeasured": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bingLastMeasured": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "alignedDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "googleMeasuredDays", "bingMeasuredDays", "alignedDays" ], "additionalProperties": {} } }, "required": ["current", "comparison", "delta", "coverage"], "additionalProperties": false }, "rankBuckets": { "type": "object", "properties": { "google": { "type": "object", "properties": { "asOf": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "current": { "type": "object", "properties": { "pos13": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos410": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos1120": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos21Plus": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "additionalProperties": {} }, "comparison": { "type": "object", "properties": { "pos13": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos410": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos1120": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos21Plus": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "additionalProperties": {} }, "noImpressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "required": ["current", "comparison"], "additionalProperties": {} }, "bing": { "type": "object", "properties": { "asOf": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "current": { "type": "object", "properties": { "pos13": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos410": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos1120": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos21Plus": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "additionalProperties": {} }, "comparison": { "type": "object", "properties": { "pos13": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos410": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos1120": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "pos21Plus": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "additionalProperties": {} }, "noImpressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] } }, "required": ["current", "comparison"], "additionalProperties": {} } }, "required": ["google", "bing"], "additionalProperties": false }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "comparisonWindow", "google", "bing", "total", "rankBuckets", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Get task status URL: /docs/mcp/tools/get_task_status Poll the status of a long-running analysis task by the domain record it is linked to (e.g. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_task_status` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Poll the status of a long-running analysis task by the domain record it is linked to (e.g. recordType=campaigns with the campaignId). Use to track progress after a create/confirm/attach that kicks off background analysis. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------ | --------------------------------------------------------------------------------------------------------------------------------- | -------- | ----- | | `recordType` | `"content_analyses" \| "serp_snapshots" \| "campaigns" \| "search_intent_classifications" \| "target_ai_prompts" \| "workspaces"` | Yes | - | | `recordId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "recordType": { "type": "string", "enum": [ "content_analyses", "serp_snapshots", "campaigns", "search_intent_classifications", "target_ai_prompts", "workspaces" ] }, "recordId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["recordType", "recordId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "taskId": { "type": "string" }, "status": { "type": "string" }, "createdAt": { "type": "string" }, "result": { "anyOf": [ {}, { "type": "null" } ] }, "error": { "anyOf": [ {}, { "type": "null" } ] }, "message": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["taskId", "status", "createdAt"], "additionalProperties": false } ```
--- # Get tool manifest URL: /docs/mcp/tools/get_tool_manifest Return the canonical tool manifest for this running MCP server, including the tools schema version and a SHA-256 hash of each tool input schema. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_tool_manifest` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Return the canonical tool manifest for this running MCP server, including the tools schema version and a SHA-256 hash of each tool input schema. ## Parameters [#parameters] This tool takes no parameters.
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {} } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "version": { "type": "string" }, "serverVersion": { "type": "string" }, "tools": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string" }, "inputSchemaHash": { "type": "string" }, "outputSchemaHash": { "type": "string" } }, "required": ["name", "inputSchemaHash"], "additionalProperties": {} } } }, "required": ["version", "serverVersion", "tools"], "additionalProperties": false } ```
--- # Get workspace URL: /docs/mcp/tools/get_workspace Get a single workspace with its target audiences, products, default location and language, and its campaigns. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_workspace` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Get a single workspace with its target audiences, products, default location and language, and its campaigns. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------- | --------------- | -------- | ----- | | `workspaceId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "status": { "type": "string" }, "defaultLocation": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "defaultLanguage": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} }, "campaigns": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string" } }, "additionalProperties": {} } }, "targetAudiences": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["id", "name"], "additionalProperties": {} } }, "targetProducts": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["id", "name"], "additionalProperties": {} } } }, "required": [ "status", "defaultLocation", "defaultLanguage", "campaigns", "targetAudiences", "targetProducts" ], "additionalProperties": false } ```
--- # Get workspace brand terms URL: /docs/mcp/tools/get_workspace_brand_terms The brand terms a workspace matches queries and AI answers against: `brandTerms` is the effective list (title plus confirmed aliases) and is what analyze_branded_vs_nonbranded and find_low_hanging_keywords actually use. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_workspace_brand_terms` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. The brand terms a workspace matches queries and AI answers against: `brandTerms` is the effective list (title plus confirmed aliases) and is what analyze\_branded\_vs\_nonbranded and find\_low\_hanging\_keywords actually use. `aliases` is the editable set behind it, each tagged `derived` (proposed by workspace analysis and confirmed) or `user` (typed by someone). `candidates` are proposals still awaiting confirmation - they are never matched against until promoted with set\_workspace\_brand\_terms. Takes a workspaceId, not a site; call list\_workspaces or list\_sites if you only know the domain. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------- | --------------- | -------- | ----- | | `workspaceId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "type": "string" }, "brandTerms": { "type": "array", "items": { "type": "string" } }, "aliases": { "type": "array", "items": { "type": "object", "properties": { "alias": { "type": "string" }, "origin": { "type": "string" } }, "required": ["alias", "origin"], "additionalProperties": {} } }, "candidates": { "type": "array", "items": { "type": "string" } }, "rejected": { "type": "array", "items": { "type": "object", "properties": { "alias": { "type": "string" }, "reason": { "type": "string" } }, "required": ["alias", "reason"], "additionalProperties": {} } } }, "required": [ "workspaceId", "title", "brandTerms", "aliases", "candidates", "rejected" ], "additionalProperties": false } ```
--- # Get workspace competitors URL: /docs/mcp/tools/get_workspace_competitors The brands this workspace measures itself against in AI answers: the tracked competitors plus, for reference, the workspace's own brand (`selfBrand`, `isSelf: true`). {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `get_workspace_competitors` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. The brands this workspace measures itself against in AI answers: the tracked competitors plus, for reference, the workspace's own brand (`selfBrand`, `isSelf: true`). Each carries the facts the detectors match on - `aliases` in answer text, `domains` and `socialUrls` in cited sources - plus `measuredDays` and `lastMeasuredOn`, which say whether the brand has any recorded history yet and what removing it would discard. A competitor added today reads 0 until the next monitoring run. Call this before set\_workspace\_competitors and edit from what it returns; that tool replaces each list wholesale. Takes a workspaceId, not a site - call list\_workspaces if you only know the domain. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------- | --------------- | -------- | ----- | | `workspaceId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "selfBrand": { "anyOf": [ { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "aliases": { "type": "array", "items": { "type": "string" } }, "domains": { "type": "array", "items": { "type": "string" } }, "socialUrls": { "type": "array", "items": { "type": "string" } }, "employeeLinkedinUrls": { "type": "array", "items": { "type": "string" } }, "isSelf": { "type": "boolean" }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "lastMeasuredOn": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "aliases", "domains", "socialUrls", "employeeLinkedinUrls", "isSelf", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "competitors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "aliases": { "type": "array", "items": { "type": "string" } }, "domains": { "type": "array", "items": { "type": "string" } }, "socialUrls": { "type": "array", "items": { "type": "string" } }, "employeeLinkedinUrls": { "type": "array", "items": { "type": "string" } }, "isSelf": { "type": "boolean" }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "lastMeasuredOn": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "aliases", "domains", "socialUrls", "employeeLinkedinUrls", "isSelf", "measuredDays" ], "additionalProperties": {} } }, "limit": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "rejected": { "type": "array", "items": { "type": "object", "properties": { "competitor": { "type": "string" }, "field": { "type": "string" }, "value": { "type": "string" }, "reason": { "type": "string" } }, "required": ["competitor", "field", "value", "reason"], "additionalProperties": {} } } }, "required": ["workspaceId", "competitors", "limit", "rejected"], "additionalProperties": false } ```
--- # Tool reference URL: /docs/mcp/tools Every tool the Composway MCP server exposes - 48 in total, generated from the running server's own registry. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} The Composway MCP server exposes **48 tools**. This page is generated from the server's own tool registry, so it always matches what a connected client discovers via `tools/list`. Current tools schema version: `2026-09-02.5`. **Reads or writes** is the tool's own annotation, and it is what decides when you get interrupted: a read runs without asking, while a write is prompted by your client and then confirmed again by the server, describing exactly what is about to change. **Cost** is a separate question - nearly everything here is free, and the few tools that spend money or draw on a plan quota say so on their own page too. ## Diagnostics [#diagnostics] Server freshness, schema versioning and identity. Call get\_server\_status at the start of a session. | Tool | What it does | Reads or writes | Cost | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | ---- | | [`get_server_status`](/docs/mcp/tools/get_server_status) | Start here. | Read | Free | | [`get_server_info`](/docs/mcp/tools/get_server_info) | Return Composway MCP server freshness metadata: server version, tools schema version, git/deploy metadata, and Worker version metadata when available. | Read | Free | | [`get_tool_manifest`](/docs/mcp/tools/get_tool_manifest) | Return the canonical tool manifest for this running MCP server, including the tools schema version and a SHA-256 hash of each tool input schema. | Read | Free | ## Workspaces & campaigns [#workspaces--campaigns] Read the workspaces, campaigns, entities and article drafts the rest of the surface operates on. | Tool | What it does | Reads or writes | Cost | | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ---- | | [`search_reference`](/docs/mcp/tools/search_reference) | Resolve a human-readable location or language name into the DataForSEO reference object (\{ id, code, isoCode, name }) required by campaign/workspace writes. | Read | Free | | [`whoami`](/docs/mcp/tools/whoami) | Return the authenticated user's Kinde claims (sub, email, name). | Read | Free | | [`list_workspaces`](/docs/mcp/tools/list_workspaces) | List the current user's workspaces (id, title). | Read | Free | | [`get_workspace`](/docs/mcp/tools/get_workspace) | Get a single workspace with its target audiences, products, default location and language, and its campaigns. | Read | Free | | [`get_workspace_competitors`](/docs/mcp/tools/get_workspace_competitors) | The brands this workspace measures itself against in AI answers: the tracked competitors plus, for reference, the workspace's own brand (`selfBrand`, \`isSelf:… | Read | Free | | [`list_campaigns`](/docs/mcp/tools/list_campaigns) | List the AEO/AI campaigns in a workspace. | Read | Free | | [`get_campaign`](/docs/mcp/tools/get_campaign) | Get an AEO campaign's detail: SERP snapshot, analysis, brief, and search-intent-classification ids. | Read | Free | | [`get_campaign_related_questions`](/docs/mcp/tools/get_campaign_related_questions) | Get a campaign's related questions and the discovered pain points. | Read | Free | | [`get_campaign_entities`](/docs/mcp/tools/get_campaign_entities) | Get the extracted entity list for a campaign. | Read | Free | | [`get_latest_article`](/docs/mcp/tools/get_latest_article) | Get the latest user-generated content (article draft + meta tags) for a campaign - the input for evaluate\_article, optimize\_article, and quick\_save. | Read | Free | | [`deep_research`](/docs/mcp/tools/deep_research) | Perform deep research on how people ask on a related topic. | Read | Free | | [`get_evaluation_result`](/docs/mcp/tools/get_evaluation_result) | Poll an evaluate\_article or optimize\_article background job by its jobId. | Read | Free | | [`get_task_status`](/docs/mcp/tools/get_task_status) | Poll the status of a long-running analysis task by the domain record it is linked to (e.g. | Read | Free | ## Search analytics [#search-analytics] Search Console and Bing Webmaster Tools reads: performance, keyword discovery, decay, gaps and reporting. | Tool | What it does | Reads or writes | Cost | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | ------------- | | [`list_sites`](/docs/mcp/tools/list_sites) | Live: lists every Google Search Console property and Bing Webmaster Tools site visible to the caller's connected accounts, tagged with searchEngine, and joined… | Read | Free | | [`get_connection_status`](/docs/mcp/tools/get_connection_status) | Live: connected Google + Bing accounts, the property/site attached to this site's workspace, and the last time its search data synced. | Read | Free | | [`get_site_snapshot`](/docs/mcp/tools/get_site_snapshot) | Summarizes a site's overall Google + Bing search performance over a window and compares it with the immediately preceding window of the same length. | Read | Free | | [`get_performance_trend`](/docs/mcp/tools/get_performance_trend) | Daily, weekly, or monthly Google + Bing performance trend for a site. | Read | Free | | [`find_new_and_lost_keywords`](/docs/mcp/tools/find_new_and_lost_keywords) | Query-level `/breakdown` rows classified per engine as new, lost, improved, declined, or stable between the current window and the immediately preceding one of… | Read | Free | | [`find_cross_engine_gaps`](/docs/mcp/tools/find_cross_engine_gaps) | Queries or pages where Google and Bing performance diverge materially in the current window. | Read | Free | | [`find_low_hanging_keywords`](/docs/mcp/tools/find_low_hanging_keywords) | High-impression queries ranking just off page one, with an estimated click uplift if promoted to the site's own page-one (`pos1_3`) CTR - built from the site's… | Read | Free | | [`find_ctr_opportunities`](/docs/mcp/tools/find_ctr_opportunities) | Queries or pages whose actual CTR falls well below what the site's own position-based CTR curve predicts for their rank. | Read | Free | | [`find_content_gaps`](/docs/mcp/tools/find_content_gaps) | Two halves under one name, labelled per row. | Read - confirmed when paid | Paid (opt-in) | | [`get_page_queries`](/docs/mcp/tools/get_page_queries) | The queries a specific page drew impressions and clicks for, per engine, over the window. | Read | Free | | [`get_query_pages`](/docs/mcp/tools/get_query_pages) | The pages of the site that surfaced for a specific query, per engine, over the window. | Read | Free | | [`detect_keyword_cannibalization`](/docs/mcp/tools/detect_keyword_cannibalization) | Queries more than one of the site's own pages competes for, read from two sources. | Read | Free | | [`find_content_decay`](/docs/mcp/tools/find_content_decay) | Pages or queries in *sustained* decline across several whole periods - not a window-over-window drop, which cannot tell a decline from a bad month. | Read | Free | | [`find_emerging_keywords`](/docs/mcp/tools/find_emerging_keywords) | Queries with sustained or accelerating growth over several whole periods - find\_content\_decay's classifier with the sign flipped. | Read | Free | | [`diagnose_traffic_losses`](/docs/mcp/tools/diagnose_traffic_losses) | For the queries that lost the most clicks, says *why* - and each cause implies a different action, so the class matters more than the number. | Read | Free | | [`analyze_site_sections`](/docs/mcp/tools/analyze_site_sections) | Aggregates Google + Bing page performance into URL directories (the leading `sectionDepth` path segments) and compares each section's current total against the… | Read | Free | | [`analyze_branded_vs_nonbranded`](/docs/mcp/tools/analyze_branded_vs_nonbranded) | Splits the window's Google + Bing queries into branded and non-branded demand and reports each side's clicks, impressions, CTR, average position and share of t… | Read | Free | | [`get_workspace_brand_terms`](/docs/mcp/tools/get_workspace_brand_terms) | The brand terms a workspace matches queries and AI answers against: `brandTerms` is the effective list (title plus confirmed aliases) and is what analyze\_brand… | Read | Free | | [`score_seo_opportunities`](/docs/mcp/tools/score_seo_opportunities) | One prioritized queue over four tools that each answer a different question: find\_low\_hanging\_keywords, find\_ctr\_opportunities, find\_content\_decay and find\_con… | Read | Free | | [`generate_search_performance_report`](/docs/mcp/tools/generate_search_performance_report) | A Google + Bing performance report for one site or several, assembled by calling the other tools and embedding their responses whole - so a section here is the… | Read | Free | | [`get_advanced_search_analytics`](/docs/mcp/tools/get_advanced_search_analytics) | The composable read: you choose the grouping, filters, sort and limit instead of asking one of the named questions above. | Read | Free | ## Writes [#writes] Everything that creates or changes data. Every tool here asks a human to confirm before it runs. | Tool | What it does | Reads or writes | Cost | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | --------------------- | ----- | | [`create_workspace`](/docs/mcp/tools/create_workspace) | Create a new workspace. | **Write** - confirmed | Quota | | [`create_ai_campaign`](/docs/mcp/tools/create_ai_campaign) | Create an AEO (AI) campaign in a workspace from one or more target prompts. | **Write** - confirmed | Quota | | [`confirm_search_intent`](/docs/mcp/tools/confirm_search_intent) | Confirm the chosen search-intent classification for a campaign. | **Write** - confirmed | Free | | [`confirm_pain_points`](/docs/mcp/tools/confirm_pain_points) | Confirm the campaign background: the selected pain points, target audiences, and search-intent ids. | **Write** - confirmed | Free | | [`attach_target_ai_prompt`](/docs/mcp/tools/attach_target_ai_prompt) | Attach a new target AI prompt (topic + location + language + LLM providers) to a campaign. | **Write** - confirmed | Free | | [`evaluate_article`](/docs/mcp/tools/evaluate_article) | Start an asynchronous evaluation job for an article in a campaign. | **Write** - confirmed | Free | | [`optimize_article`](/docs/mcp/tools/optimize_article) | Start an asynchronous optimization job for an article in a campaign. | **Write** - confirmed | Free | | [`quick_save`](/docs/mcp/tools/quick_save) | Save an article draft (and meta tags) to a specific user-generated-content version of a campaign. | **Write** - confirmed | Free | | [`set_workspace_brand_terms`](/docs/mcp/tools/set_workspace_brand_terms) | Add, remove, or replace a workspace's confirmed brand aliases. | **Write** - confirmed | Free | | [`set_workspace_competitors`](/docs/mcp/tools/set_workspace_competitors) | Add, edit, or stop tracking the competitors a workspace is measured against in AI answers. | **Write** - confirmed | Free | | [`explain_query_serp`](/docs/mcp/tools/explain_query_serp) | Fetch this query's Google SERP **live** and diff it against the newest SERP stored for this workspace. | **Write** - confirmed | Paid | --- # List campaigns URL: /docs/mcp/tools/list_campaigns List the AEO/AI campaigns in a workspace. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `list_campaigns` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. List the AEO/AI campaigns in a workspace. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------- | --------------- | -------- | ----- | | `workspaceId` | `string (uuid)` | Yes | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string" } }, "additionalProperties": {} } }, "status": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["data"], "additionalProperties": false } ```
--- # List Search Console + Bing Webmaster sites URL: /docs/mcp/tools/list_sites Live: lists every Google Search Console property and Bing Webmaster Tools site visible to the caller's connected accounts, tagged with searchEngine, and joined to the workspace each is already attached to (workspaceId is null when a site i… {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `list_sites` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Live: lists every Google Search Console property and Bing Webmaster Tools site visible to the caller's connected accounts, tagged with searchEngine, and joined to the workspace each is already attached to (workspaceId is null when a site is visible but not yet attached to any workspace). This is a live API call, not a lake read - use it to find the exact `site` string the other search-data tools expect. ## Parameters [#parameters] This tool takes no parameters.
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {} } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "sites": { "type": "array", "items": { "type": "object", "properties": { "searchEngine": { "type": "string", "enum": ["google", "bing"] }, "siteUrl": { "type": "string" }, "credentialId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "permissionLevel": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "isVerified": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "workspaceId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "workspaceTitle": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["searchEngine", "siteUrl", "credentialId"], "additionalProperties": {} } } }, "required": ["sites"], "additionalProperties": false } ```
--- # List workspaces URL: /docs/mcp/tools/list_workspaces List the current user's workspaces (id, title). {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `list_workspaces` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. List the current user's workspaces (id, title). Use this to find a workspaceId before creating or listing campaigns. ## Parameters [#parameters] This tool takes no parameters.
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {} } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "type": "string" }, "websiteUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "campaigns": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string" } }, "additionalProperties": {} } }, "status": { "type": "string" } }, "required": ["id", "title", "status"], "additionalProperties": {} } } }, "required": ["data"], "additionalProperties": false } ```
--- # Optimize article URL: /docs/mcp/tools/optimize_article Start an asynchronous optimization job for an article in a campaign. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `optimize_article` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Start an asynchronous optimization job for an article in a campaign. optimizeType may be entity, authoritative\_source, or full. The completed result includes the optimized article and its new score. Returns immediately with \{ jobId, status, kind, result, error }. This tool does not return the optimized article inline at queue time. After calling it, use get\_evaluation\_result(\{ jobId }) to poll the job about every 5 seconds while status is "queued" or "running". If status is "completed", read result. If status is "failed", read error. Optimize jobs are never cached. Do not call optimize\_article again just to poll. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------------ | ---------------------------------------------- | -------- | ----- | | `article` | `string` | Yes | - | | `optimizeType` | `"entity" \| "authoritative_source" \| "full"` | Yes | - | | `campaignId` | `string (uuid)` | Yes | - | | `workspaceId` | `string (uuid)` | Yes | - | | `targetAiPromptId` | `string (uuid)` | No | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "article": { "type": "string" }, "optimizeType": { "type": "string", "enum": ["entity", "authoritative_source", "full"] }, "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "targetAiPromptId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": ["article", "optimizeType", "campaignId", "workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "jobId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "status": { "type": "string" }, "kind": { "type": "string" }, "cached": { "type": "boolean" }, "result": { "anyOf": [ { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, { "type": "null" } ] }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": ["jobId", "status", "kind"], "additionalProperties": false } ```
--- # Quick save article URL: /docs/mcp/tools/quick_save Save an article draft (and meta tags) to a specific user-generated-content version of a campaign. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `quick_save` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Save an article draft (and meta tags) to a specific user-generated-content version of a campaign. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ----------------- | --------------- | -------- | ----- | | `campaignId` | `string (uuid)` | Yes | - | | `contentId` | `string (uuid)` | Yes | - | | `article` | `string` | Yes | - | | `metaTitle` | `string` | No | - | | `metaDescription` | `string` | No | - |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "campaignId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "contentId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "article": { "type": "string" }, "metaTitle": { "type": "string" }, "metaDescription": { "type": "string" } }, "required": ["campaignId", "contentId", "article"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "type": "boolean" }, "status": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["ok", "status"], "additionalProperties": false } ```
--- # Score SEO opportunities URL: /docs/mcp/tools/score_seo_opportunities One prioritized queue over four tools that each answer a different question: find_low_hanging_keywords, find_ctr_opportunities, find_content_decay and find_content_gaps. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `score_seo_opportunities` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. One prioritized queue over four tools that each answer a different question: find\_low\_hanging\_keywords, find\_ctr\_opportunities, find\_content\_decay and find\_content\_gaps. Use it to answer "what should I work on" - call the individual tools when you already know which kind of problem you are chasing, since each carries thresholds this one fixes at their defaults. Every row is priced in one unit: `estimatedClicks`, the clicks per `windowDays` at stake if the finding were fixed. Three things about that number you must carry into any answer. It takes the **maximum** of overlapping signals on one entity, never the sum - a query that is both low-hanging and under-clicking is one recovery, not two - so do not add the `signals` up. Its `basis` says what it assumes, and `estimateBases` in the response spells each one out: a `decay` row is clicks that were measurably earned and lost, while the other three are projections onto the site's own CTR curve. And a null `estimatedClicks` means the site's data could not price that row, not that it is worth zero - those rows sort last. The queue mixes queries and pages (`entityType`), because the sources work on different objects; the same string as a query and as a page is two rows. `sources` reports which types were evaluated, which you excluded, and how many candidates each produced - say so rather than implying the queue is exhaustive. That matters most for Google's row ceiling: a *ranked queue* reads as "these are the best available actions" in a way a filtered list does not, so when the response declares a truncated window, say the queue is a top-N of what was visible rather than of the site. Google stores only about 5,000 rows per day per single-dimension breakdown once a day is more than roughly three days old, so on a large site these rows are a top-N by impressions rather than a complete list. When that applies to the window you asked for, the response says so in `unsupportedInputs` with the dates - there is nothing to fetch, so read a missing row as 'not in the top \~5,000', never as 'zero'. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ---------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `site` | `string` | Yes | The site to look up, as a bare domain ("example.com") or a URL - scheme, [www](http://www)., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list\_sites first if unsure of the exact domain. | | `windowDays` | `integer` | No | Length of the current window, in days. Defaults to `28`. | | `to` | `string` | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. | | `types` | `string` | No | Comma-separated opportunity types to evaluate: low\_hanging, ctr, decay, gap. Narrow it to keep the call cheap - low\_hanging/ctr/gap share one lake scan, and decay costs a second. Defaults to `"low_hanging,ctr,decay,gap"`. | | `minImpressions` | `integer` | No | Shared floor: entities below this many impressions over the window are not opportunities on any source. Defaults to `10`. | | `excludeBranded` | `boolean` | No | Exclude branded queries. On by default - you cannot optimize your way up your own brand SERP, and their outlier CTR raises the target every projection here is measured against. Defaults to `true`. | | `decayBy` | `"query" \| "page"` | No | Dimension the decay source reads. Pages by default, matching find\_content\_decay: a decaying page is the thing you act on. Defaults to `"page"`. | | `decayGranularity` | `"weekly" \| "monthly"` | No | Period length for the decay source only. Defaults to `"monthly"`. | | `decayPeriods` | `integer` | No | How many whole periods the decay source reads. Defaults to `6`. | | `checkCannibalization` | `boolean` | No | Run the page↔query join so a `gap` row several of your own pages already split is recommended as a consolidation rather than as a page to create - opposite advice. Costs one extra lake read, and is skipped automatically when no gap row was found. Defaults to `true`. | | `limit` | `integer` | No | How many opportunities to return, highest first. Defaults to `25`. | | `searchVertical` | `"web" \| "image" \| "video"` | No | Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'. Defaults to `"web"`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL - scheme, www., path and trailing slash are all stripped. Must resolve to a workspace the caller owns; call list_sites first if unsure of the exact domain." }, "windowDays": { "default": 28, "description": "Length of the current window, in days.", "type": "integer", "exclusiveMinimum": 0, "maximum": 365 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "types": { "default": "low_hanging,ctr,decay,gap", "description": "Comma-separated opportunity types to evaluate: low_hanging, ctr, decay, gap. Narrow it to keep the call cheap - low_hanging/ctr/gap share one lake scan, and decay costs a second.", "type": "string" }, "minImpressions": { "default": 10, "description": "Shared floor: entities below this many impressions over the window are not opportunities on any source.", "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "excludeBranded": { "default": true, "description": "Exclude branded queries. On by default - you cannot optimize your way up your own brand SERP, and their outlier CTR raises the target every projection here is measured against.", "type": "boolean" }, "decayBy": { "default": "page", "description": "Dimension the decay source reads. Pages by default, matching find_content_decay: a decaying page is the thing you act on.", "type": "string", "enum": ["query", "page"] }, "decayGranularity": { "default": "monthly", "description": "Period length for the decay source only.", "type": "string", "enum": ["weekly", "monthly"] }, "decayPeriods": { "default": 6, "description": "How many whole periods the decay source reads.", "type": "integer", "minimum": 2, "maximum": 104 }, "checkCannibalization": { "default": true, "description": "Run the page↔query join so a `gap` row several of your own pages already split is recommended as a consolidation rather than as a page to create - opposite advice. Costs one extra lake read, and is skipped automatically when no gap row was found.", "type": "boolean" }, "limit": { "default": 25, "description": "How many opportunities to return, highest first.", "type": "integer", "exclusiveMinimum": 0, "maximum": 200 }, "searchVertical": { "default": "web", "description": "Which Google search vertical to read: web (default), image, or video. Google-only - Bing Webmaster Tools does not split verticals on any traffic endpoint, so anything other than 'web' returns Google alone, with Bing excluded and declared in `unsupportedInputs`. A null or absent Bing figure then means 'not measurable', never 'zero'.", "type": "string", "enum": ["web", "image", "video"] } }, "required": ["site"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": ["web", "image", "video"] }, "currentWindow": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "periodWindow": { "anyOf": [ { "type": "object", "properties": { "granularity": { "type": "string" }, "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "window": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, "periodStarts": { "type": "array", "items": { "type": "string" } }, "excludedPartialPeriod": { "anyOf": [ { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": ["start", "end"], "additionalProperties": false }, { "type": "null" } ] } }, "required": [ "granularity", "periods", "window", "periodStarts", "excludedPartialPeriod" ], "additionalProperties": {} }, { "type": "null" } ] }, "rows": { "type": "array", "items": { "type": "object", "properties": { "entityType": { "type": "string" }, "label": { "type": "string" }, "estimatedClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "leadingKind": { "type": "string" }, "confidence": { "type": "string" }, "recommendation": { "type": "string" }, "kinds": { "type": "array", "items": { "type": "string" } }, "signals": { "type": "array", "items": { "type": "object", "properties": { "kind": { "type": "string" }, "engine": { "type": "string" }, "estimatedClicks": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "basis": { "type": "string" }, "confidence": { "type": "string" }, "detail": { "type": "string" }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "kind", "engine", "estimatedClicks", "basis", "confidence", "detail", "position", "impressions", "clicks" ], "additionalProperties": {} } }, "totalImpressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalClicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "entityType", "label", "estimatedClicks", "leadingKind", "confidence", "recommendation", "kinds", "signals", "totalImpressions", "totalClicks" ], "additionalProperties": {} } }, "sources": { "type": "array", "items": { "type": "object", "properties": { "kind": { "type": "string" }, "entityType": { "type": "string" }, "status": { "type": "string" }, "candidates": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "pricedCandidates": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "kind", "entityType", "status", "candidates", "pricedCandidates" ], "additionalProperties": {} } }, "estimateBases": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "string" } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": ["field", "reason"], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "currentWindow", "periodWindow", "rows", "sources", "estimateBases", "unsupportedInputs" ], "additionalProperties": false } ```
--- # Search location/language reference URL: /docs/mcp/tools/search_reference Resolve a human-readable location or language name into the DataForSEO reference object ({ id, code, isoCode, name }) required by campaign/workspace writes. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `search_reference` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Resolve a human-readable location or language name into the DataForSEO reference object (\{ id, code, isoCode, name }) required by campaign/workspace writes. Call this to obtain a `location`/`language`/`defaultLocation`/`defaultLanguage` value - never invent those ids. Pass a chosen result object through verbatim. Locations are restricted to countries. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | --------- | -------------------------- | -------- | ----------------------------------------------------------------------------------------- | | `type` | `"location" \| "language"` | Yes | - | | `query` | `string` | No | Name prefix to search, e.g. "United States" or "English". Omit to list the first results. | | `limit` | `integer` | No | Defaults to `10`. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "type": { "type": "string", "enum": ["location", "language"] }, "query": { "description": "Name prefix to search, e.g. \"United States\" or \"English\". Omit to list the first results.", "type": "string" }, "limit": { "default": 10, "type": "integer", "exclusiveMinimum": 0, "maximum": 50 } }, "required": ["type"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "type": { "type": "string" }, "query": { "type": "string" }, "results": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "code": { "type": "string" }, "isoCode": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "code", "isoCode", "name"], "additionalProperties": {} } } }, "required": ["type", "query", "results"], "additionalProperties": false } ```
--- # Set workspace brand terms URL: /docs/mcp/tools/set_workspace_brand_terms Add, remove, or replace a workspace's confirmed brand aliases. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `set_workspace_brand_terms` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Add, remove, or replace a workspace's confirmed brand aliases. These decide which queries analyze\_branded\_vs\_nonbranded counts as branded and which ones find\_low\_hanging\_keywords excludes, and they are also the term set the AI-answer mention monitor scans for - so widening them changes reported visibility, not just this session's output. Call get\_workspace\_brand\_terms first and show the user the current terms. `replace` sets the list outright and cannot be combined with `add`/`remove`. Removals are matched case-insensitively and are applied before additions, so renaming a term is one call. An alias that fails vetting (too short, longer than four words, a duplicate, or over the eight-alias cap) is returned in `rejected` with a reason while the rest of the write still applies - always report those back to the user. Aliases need not resemble the workspace title: a former name or an unrelated acronym is exactly what this is for. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------- | --------------- | -------- | -------------------------------------------------------------------------------------- | | `workspaceId` | `string (uuid)` | Yes | - | | `add` | `string[]` | No | Aliases to add to the confirmed set. | | `remove` | `string[]` | No | Aliases to drop, matched case-insensitively. | | `replace` | `string[]` | No | Set the confirmed aliases to exactly this list. Cannot be combined with add or remove. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "add": { "description": "Aliases to add to the confirmed set.", "type": "array", "items": { "type": "string" } }, "remove": { "description": "Aliases to drop, matched case-insensitively.", "type": "array", "items": { "type": "string" } }, "replace": { "description": "Set the confirmed aliases to exactly this list. Cannot be combined with add or remove.", "type": "array", "items": { "type": "string" } } }, "required": ["workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "title": { "type": "string" }, "brandTerms": { "type": "array", "items": { "type": "string" } }, "aliases": { "type": "array", "items": { "type": "object", "properties": { "alias": { "type": "string" }, "origin": { "type": "string" } }, "required": ["alias", "origin"], "additionalProperties": {} } }, "candidates": { "type": "array", "items": { "type": "string" } }, "rejected": { "type": "array", "items": { "type": "object", "properties": { "alias": { "type": "string" }, "reason": { "type": "string" } }, "required": ["alias", "reason"], "additionalProperties": {} } } }, "required": [ "workspaceId", "title", "brandTerms", "aliases", "candidates", "rejected" ], "additionalProperties": false } ```
--- # Set workspace competitors URL: /docs/mcp/tools/set_workspace_competitors Add, edit, or stop tracking the competitors a workspace is measured against in AI answers. {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `set_workspace_competitors` **Cost:** Free - not billed, and does not draw on a plan quota. This tool creates or changes data. It is not annotated `readOnlyHint`, so your MCP client prompts you before it runs, and the server asks for an explicit confirmation describing exactly what will change. Declining cancels the call cleanly. Add, edit, or stop tracking the competitors a workspace is measured against in AI answers. The tracked set decides every competitive metric - share of voice, the visibility index, the competitor table - so adding a brand starts measuring it from the next monitoring run onward, and it is NOT backfilled. Call get\_workspace\_competitors first and show the user the current set. `update` replaces each list you pass wholesale (send the full list, not just the new entries; `[]` clears one) and leaves the lists you omit alone. `remove` takes ids or names and is permanent: it also discards that brand's recorded history, so check `measuredDays` first and tell the user what they are giving up. Facts are vetted per value. A shared platform host (linkedin.com, x.com…) is refused as a `domain` because it would credit the brand with every citation of that platform - pass the profile URL, path included, in `socialUrls` instead. A refused value comes back in `rejected` with a reason while the rest of the write still applies; always report those back to the user. The workspace's own brand cannot be edited here - use set\_workspace\_brand\_terms for it. ## Parameters [#parameters] | Parameter | Type | Required | Notes | | ------------- | ----------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------- | | `workspaceId` | `string (uuid)` | Yes | - | | `add` | `({ name, aliases, domains, socialUrls, employeeLinkedinUrls })[]` | No | Competitors to start tracking. | | `update` | `({ id, name, newName, aliases, domains, socialUrls, employeeLinkedinUrls })[]` | No | Edits to competitors already tracked. | | `remove` | `string[]` | No | Competitor ids or names to stop tracking. Permanently discards their recorded visibility history. |
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "add": { "description": "Competitors to start tracking.", "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "The competitor's brand name, as answers say it." }, "aliases": { "description": "Other spellings to match in answer text (legal name, former name).", "type": "array", "items": { "type": "string" } }, "domains": { "description": "The brand's own domains, for citation matching. A shared platform (linkedin.com, x.com…) is refused here - put the profile URL in socialUrls.", "type": "array", "items": { "type": "string" } }, "socialUrls": { "description": "Full profile URLs including the path, e.g. https://x.com/acme. A bare host is refused: it would match every account on that platform.", "type": "array", "items": { "type": "string" } }, "employeeLinkedinUrls": { "description": "linkedin.com profile URLs of the brand's people.", "type": "array", "items": { "type": "string" } } }, "required": ["name"] } }, "update": { "description": "Edits to competitors already tracked.", "type": "array", "items": { "type": "object", "properties": { "id": { "description": "The competitor to edit; or match by name.", "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "description": "Current name, used to find the competitor when id is omitted.", "type": "string" }, "newName": { "description": "Rename the competitor to this.", "type": "string" }, "aliases": { "type": "array", "items": { "type": "string" } }, "domains": { "type": "array", "items": { "type": "string" } }, "socialUrls": { "type": "array", "items": { "type": "string" } }, "employeeLinkedinUrls": { "type": "array", "items": { "type": "string" } } } } }, "remove": { "description": "Competitor ids or names to stop tracking. Permanently discards their recorded visibility history.", "type": "array", "items": { "type": "string" } } }, "required": ["workspaceId"] } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "selfBrand": { "anyOf": [ { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "aliases": { "type": "array", "items": { "type": "string" } }, "domains": { "type": "array", "items": { "type": "string" } }, "socialUrls": { "type": "array", "items": { "type": "string" } }, "employeeLinkedinUrls": { "type": "array", "items": { "type": "string" } }, "isSelf": { "type": "boolean" }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "lastMeasuredOn": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "aliases", "domains", "socialUrls", "employeeLinkedinUrls", "isSelf", "measuredDays" ], "additionalProperties": {} }, { "type": "null" } ] }, "competitors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "aliases": { "type": "array", "items": { "type": "string" } }, "domains": { "type": "array", "items": { "type": "string" } }, "socialUrls": { "type": "array", "items": { "type": "string" } }, "employeeLinkedinUrls": { "type": "array", "items": { "type": "string" } }, "isSelf": { "type": "boolean" }, "measuredDays": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "lastMeasuredOn": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "aliases", "domains", "socialUrls", "employeeLinkedinUrls", "isSelf", "measuredDays" ], "additionalProperties": {} } }, "limit": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "rejected": { "type": "array", "items": { "type": "object", "properties": { "competitor": { "type": "string" }, "field": { "type": "string" }, "value": { "type": "string" }, "reason": { "type": "string" } }, "required": ["competitor", "field", "value", "reason"], "additionalProperties": {} } } }, "required": ["workspaceId", "competitors", "limit", "rejected"], "additionalProperties": false } ```
--- # Who am I URL: /docs/mcp/tools/whoami Return the authenticated user's Kinde claims (sub, email, name). {/* AUTOGENERATED by workers/mcp-server/scripts/generate-docs.ts - DO NOT EDIT. Edit the tool's title/description/schema in workers/mcp-server/src/tools/ and rerun `yarn docs:generate`. */} **Tool name:** `whoami` **Cost:** Free - not billed, and does not draw on a plan quota. This tool only reads. It never creates or changes anything, so an assistant may call it without asking you first. Return the authenticated user's Kinde claims (sub, email, name). ## Parameters [#parameters] This tool takes no parameters.
Full input JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": {} } ```
## Response [#response]
Full output JSON Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "sub": { "type": "string" }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "additionalProperties": false } ```