MCP tools
The SEOAIO MCP server exposes 17 tools over the HTTP transport at https://app.seoaio.ai/api/mcp. Per-customer authentication is required; nothing is exposed without it.
The only writes are propose_competitor, propose_fact, propose_measurement, propose_prompt and propose_remove_competitor, and every one of them queues a change request for a human to approve rather than changing anything. Nothing here sends email, charges a card, edits your site, or spends money on an engine call.
Call order
list_assetsto get anassetId. Never invent one.get_measurement_status: how fresh the data is, whether anything is queued or running, and which proposals already await approval.- The read tools, as needed. Every share arrives with its n and interval.
- A
propose_*tool only for a change the user asked for, and only after checking nothing identical is pending. A person approves it athttps://app.seoaio.ai/dashboard/requests.
Read tools (12)
Every share comes back with its sample size and a 95% Wilson interval, and every value with a provenance of measured, estimated or derived. A pillar that was not measured says so rather than returning zero.
- get_answer_share
Get the north-star Answer Share for an asset (share of AI-engine answers citing the brand), aggregate + per engine, each with provenance (measured / estimated / mixed).
- get_citation_metrics
Citation headline metrics for an asset over a trailing window: total citations, cited-run counts and owned-citation counts from real engine answers. Raw measured aggregates; absent inputs mean not measured.
- get_crawler_activity
Observed AI-crawler hits on an asset over the last 30 days (FI-262): which bots fetched which pages, aggregated per day. Real server-reported hits, never estimated; empty means no collector data, not zero traffic.
- get_measurement_status
How fresh an asset's data is and whether anything is in flight: the age of the latest analysis and citation snapshots, the last real answer per engine, jobs queued or running now, and the change requests already waiting for a human. Call it before quoting a number as current, and before proposing anything, so you do not propose the same thing twice. A null snapshot means not measured, never zero.
- get_visibility_score
Get the latest Visibility Score for an asset, with its component breakdown and each value's provenance (measured / estimated / derived).
- list_assets
List the Visibility Assets in YOUR organization, with their id and hostname. Call this first: every other tool needs an assetId.
- list_cited_passages
The passages AI engines actually quoted from the brand's own pages in sampled answers: passage, URL, title, how many times it was quoted, on which engines, best position. Most quoted first. This is what engines quote FROM you; how they describe you in their own words is not stored, so it is not measured here.
- list_competitors
The competitors tracked on an asset and, per engine, how many sampled answers cited the rival where the brand was not cited, as a share with its n and Wilson interval. Call it before propose_competitor, so you do not propose one that is already tracked. An empty list is a fact about the tracked set, not a measurement.
- list_findings
List the findings from the latest analysis of an asset (what is happening and why).
- list_prompt_outcomes
Per tracked question, what the engines did in the current sweep: cited (used one of your pages as a source), named (mentioned you, sourced elsewhere), category-only (answered the category and named a competitor instead of you) or absent. Each question carries n (answers pooled), the counts, the cited share with its Wilson interval, and the split per engine. Worst first, so the questions you lose come first. No sweep means not measured, never zero.
- list_recommendations
List the ranked Action Plan for an asset: each recommendation with its Predicted Visibility Gain, confidence, and status.
- list_sources
The domains and pages AI answers cite for this asset's questions inside a trailing window, yours and everyone else's: classification (target_owned, competitor_owned, neutral_third_party, unknown), citations, the answers citing the domain as a share of sampled answers with n and a Wilson interval, a URL, a title, best position, engines. Most cited first. Filters: a domain substring, a classification. No sampled answer in the window means not measured, never zero.
Propose tools (5)
5 of them, and none of them changes anything on its own. Each queues a change request for a human to approve.
- propose_competitor
Propose adding a competitor domain to this asset's tracked set. QUEUES A REQUEST FOR HUMAN APPROVAL; it does not add the competitor. Returns applied:false and a change-request id.
- propose_fact
Propose an owner-declared fact for Answer Accuracy to check AI answers against (e.g. a price, a founding year, a service area). QUEUES A REQUEST FOR HUMAN APPROVAL. A fact nobody with authority confirmed is not a fact.
- propose_measurement
Propose running a fresh measurement for this asset. QUEUES A REQUEST FOR HUMAN APPROVAL; it does not start a run. A sweep spends real money with AI vendors, so a person decides.
- propose_prompt
Propose adding a prompt to the sampled set for this asset. QUEUES A REQUEST FOR HUMAN APPROVAL. Prompts cost money to run on every sweep, which is part of why a person decides.
- propose_remove_competitor
Propose removing a competitor from this asset's tracked set. QUEUES A REQUEST FOR HUMAN APPROVAL; it does not remove anything.
When to use which
| Tool | Use it when | Do not use it when |
|---|---|---|
| get_answer_share | You need the share of sampled AI answers that cite the brand, with its evidence. | You need which questions are lost; that is list_prompt_outcomes. |
| get_citation_metrics | You need raw citation counts over a trailing window. | You need a share with an interval; counts here are aggregates, not proportions. |
| get_crawler_activity | You need to know which AI crawlers actually fetched the site. | The site has no collector installed; the tool will say the activity is not measured. |
| get_measurement_status | Before quoting any number as current, and before proposing anything. | You need the numbers themselves; they live on the tools that measure them. |
| get_visibility_score | You need the composite, or the reason it is held, with each pillar's provenance. | You need what to change; that is list_findings and list_recommendations. |
| list_assets | You need an assetId, or you are in a new conversation. | You already hold a confirmed assetId from this conversation. |
| list_cited_passages | You need the passages engines actually quoted from the brand's own pages. | You need how engines describe the brand in their own words; that text is not stored, so it is not measured here. |
| list_competitors | You need the tracked rivals, and how often each is cited where the brand is not. | You are about to invent a competitor name, or to propose one without checking it is not already tracked. |
| list_findings | You need what is happening on the site and why, from the latest analysis. | You need a number; findings carry severity and impact, not scores. |
| list_prompt_outcomes | You need to know which tracked questions the brand loses, and on which engine. | You need one headline share; that is get_answer_share. |
| list_recommendations | You need what to change next, ranked by predicted gain. | You need proof that a change worked; that is a fresh measurement, not a prediction. |
| list_sources | You need which domains and pages AI answers cite for this site's questions, yours and everyone else's. | You need a headline share; that is get_answer_share. You need what was quoted from you; that is list_cited_passages. |
| propose_competitor | The user asked to track a specific competitor domain, and list_competitors shows it is not tracked yet. | You are guessing who the competitors might be. |
| propose_fact | The user stated a fact about their business that AI answers should be checked against. | You inferred the fact yourself; a fact nobody with authority confirmed is not a fact. |
| propose_measurement | The user changed something and wants it measured, or asked for a fresh run. | A run is already queued or running; check get_measurement_status first. |
| propose_prompt | The user asked to track a specific question. | You want more data; every prompt costs money on every sweep, so a person decides. |
| propose_remove_competitor | The user asked to stop tracking a specific competitor. | A competitor merely looks irrelevant to you. |
What every result carries
dataStatus, one vocabulary on every tool, beside the specific fields; and on every read, an href to the dashboard page that holds the evidence, for the right site.
| dataStatus | Meaning |
|---|---|
| ok | There is at least one result, or a measured zero. |
| not_measured | No measurement of this kind exists yet for the asset. Not zero, and not a broken connection. |
| empty_filter | Data exists, and the filter you passed matched none of it. Retry without the filter. |
| queued | A propose tool queued a change request for a person; nothing has changed. |
Errors that apply to every tool
Quoted from the code that sends them. A request without a valid credential answers 401 with the message below; that first 401 is how an MCP client starts OAuth, not a failure.
Unauthorized. Present a SEOAIO API key or an OAuth access token as a Bearer token. MCP clients: start OAuth from the resource_metadata URL in the WWW-Authenticate header.
| When | Message | On the wire |
|---|---|---|
| The assetId is not one of yours, or does not exist. Both cases read the same, and the message names your own sites. | Asset not found. Call list_assets and use one of its ids. Your sites: example.com. | tools/call result with isError: true |
| A tool name the server does not have. The list from tools/list is complete. | Unknown tool: get_history | JSON-RPC error -32602 |
| A JSON-RPC method the server does not implement, such as a subscribe. | Method not found: resources/subscribe | JSON-RPC error -32601 |
| A required argument is missing. | Error: assetId (string) is required | tools/call result with isError: true |
Empty results are not errors: a null block is a measurement that does not exist yet. The rest is on the troubleshooting page.
