MCP tool reference
Parameters, returned data, scopes, costs, and availability for every SearchChamp MCP tool.
SearchChamp publishes 54 tools from one versioned registry. OAuth clients receive the full catalog; each call still checks scope and role. User-generated tokens receive only the tools marked in the Token column, filtered by the organization's current plan and the token's scope.
Input and result rules
- Inputs are strict: unknown fields, wrong types, out-of-range numbers, and malformed UUIDs return
invalid_inputbefore the handler runs. - A
siteaccepts a domain or display name. Calllist_siteswhen unsure. A foreign/missing site returnssite_not_found; an ambiguous name returns only your own matching candidates. - Tool results contain human-readable
contentand machine-readablestructuredContent. - A missing product prerequisite is normally a valid
{ setup_required: true, reason, next_step }result, not an exception. - Credits are debited after a handler runs. Denied/invalid calls do not debit a successful tool call.
Core SEO reads
| Tool | Required input and optional bounds | Returned data | Scope | Credits | Token |
|---|---|---|---|---|---|
list_sites | None | Addressable sites: slug, domain, workspace | mcp:read | 1 | All paid plans |
get_daily_brief | site; date? as YYYY-MM-DD | Headline, findings, open actions, or setup guidance | mcp:read | 1 | OAuth only |
get_site_snapshot | site | Rank/AI Visibility/audit/credit rollup plus open actions, goals, and strategy cards | mcp:read | 1 | OAuth only |
get_rank_movers | site; days? 1–90 (default 7), limit? 1–100 (default 10) | Keywords ordered by movement, including baseline/current ranks and delta | mcp:read | 1 | OAuth only |
get_rankings | Same as get_rank_movers | Same rank-mover data under the token-catalog name | mcp:read | 1 | All paid plans |
get_ai_visibility | site | Score, prompt coverage, appearance/share-of-voice summary, or setup guidance | mcp:read | 1 | All paid plans |
get_opportunities | site; limit? 1–100 (default 10) | Top active opportunities with title, lane, and impact | mcp:read | 1 | OAuth only |
get_audit_summary | site | Latest completed audit scores and severity counts, or setup guidance | mcp:read | 1 | OAuth only |
get_audit_issues | site; limit? 1–100 (default 20) | Actual severity-ordered findings: title, page URL, description, kind/lens, and fix availability | mcp:read | 1 | Business/Pro+ token |
get_backlinks_summary | site | Active total, 30-day gained/lost, spam count, top referring domains | mcp:read | 1 | Business/Pro+ token |
get_tracked_keywords | site; limit? 1–500 (default 50) | Current positions, unranked flags, volumes, and cluster labels | mcp:read | 1 | OAuth only |
get_bot_access_report | site | Latest AI Access Score, per-bot verdict/evidence, grouped coverage, recent history; { report: null } if no scan | mcp:read | 1 | OAuth only |
get_volatility | site; days? 1–90 (default 30) | Volatility score and most-volatile keywords, or setup guidance | mcp:read | 1 | Business/Pro+ token |
Evidence and keyword research
These tools let an agent diagnose a page and propose keywords from the evidence SearchChamp already holds, without opening the app. Every read names its source and reporting range, and an unavailable integration is a named state (not_connected, needs_reconnect, read_failed), never an empty success. Nothing in this lane adds keywords, changes tracking, edits a page, publishes, or starts an integration sync; the single spending tool is run_keyword_research, and it starts nothing without the user's explicit confirmation of a previewed scope.
| Tool | Required input and optional bounds | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
get_integration_health | site | Whether Search Console and GA4 are connected, their latest reportable dates, the last manual Search Console sync outcome, and what the nightly keyword join has stored | mcp:read | 1 | Business/Pro+ token |
get_search_console_performance | site; dimension? site/page/query/country/device/date, days? 1–480 (default 28) or start_date+end_date, page?, query_contains?, country? alpha-3, device?, limit? 1–500 (default 50) | Live, finalised clicks, impressions, CTR and position at the chosen grain; totals match the Search Console UI | mcp:read | 1 | Business/Pro+ token |
get_ga4_performance | site; report? overview/pages/channels/trend, days? 1–365 (default 28), page_path?, limit? 1–250 (default 25) | Live GA4 sessions, page views, engagement, channels or daily trend for the authorised property; the range ends yesterday | mcp:read | 1 | Business/Pro+ token |
get_keyword_inventory | site; cluster_id?, search?, source?, tracking? daily/weekly/any/none, limit?, offset? | Stored keywords with cluster, market, metrics and their data date, provenance, Search Console evidence, tracking state and mapped-page count; exact total with paging | mcp:read | 1 | Business/Pro+ token |
get_keyword_clusters | site | Clusters with keyword count, stored volume, tracked counts and a per-source breakdown, plus the ungrouped count | mcp:read | 1 | Business/Pro+ token |
get_page_keyword_coverage | site, url on the site's domain; limit? 1–200 (default 50) | Keywords mapped to one page (and how), plus stored Search Console queries for that page flagged as in or out of the keyword library | mcp:read | 1 | Business/Pro+ token |
get_keyword_page_coverage | site; keyword text or keyword_id | For one keyword: the pages carrying evidence for it with verbatim stored excerpts and how each matched (exact wording, normalized phrase, all terms in one sentence, similar meaning), Search Console figures per page and query, each page's stored title and main heading, the single suggested target with its basis (similar meaning, or stored page evidence when meaning cannot be compared) and the reasons for it, or the exact abstention reason (tied, conflicting or insufficient evidence), every saved page mapping with its provenance and any user confirmation, and the cannibalization pairs touching those pages. Read-only: nothing is written, embedded or fetched from a provider | mcp:read | 1 | Business/Pro+ token |
get_page_diagnosis | site, url on the site's domain; days? 1–365 (default 28) | One-call page diagnosis: live Search Console and GA4 performance, latest audit findings, internal-link opportunities, backlinks and keyword coverage, each section reporting its own availability | mcp:read | 1 | Business/Pro+ token |
preview_keyword_research | site, seeds; country?/language? or location_code?/language_code?, url? | The exact scope a run would use, its cost, balance, remaining daily runs, seeds already in the library, blockers, and a scope_hash; spends nothing | mcp:read | 1 | Business/Pro+ token |
run_keyword_research | The previewed scope plus user_confirmed: true, scope_hash, idempotency_key | Starts the research job and returns a job_id; requires a signed-in Editor and the user's explicit confirmation; candidates are reviewable only and nothing is added to the library | mcp:spend | 1 + the research run's own credits | OAuth only |
get_keyword_research_result | site, job_id | Reviewable candidates and the reasons any were rejected, or the job's current state | mcp:read | 1 | Business/Pro+ token |
run_keyword_research is a confirmed spend: the preview shows the scope and cost, and the run refuses a scope_hash that does not match it.
Content and publishing
| Tool | Required input and optional bounds | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
get_content_drafts | site; limit? 1–100 (default 25) | Draft/ready article identifiers, slugs, statuses, and titles | mcp:read | 1 | Business/Pro+ token |
list_draft_articles | site; limit? 1–100 (default 25) | Drafts plus MCP claim state: unclaimed, claimed/lease, or published/verification | mcp:read | 1 | Business/Pro+ token |
get_article_content | article_id | Markdown/HTML body, SEO metadata, slug, hero image, and publisher JSON-LD graph | mcp:read | 1 | Business/Pro+ token |
get_page_recommendations | site; url? | Audit issues and opportunities reshaped as rewrite guidance | mcp:read | 1 | OAuth only |
get_brand_voice | site | Composed tone, do/don't lists, banned phrases, audience, and available named voices | mcp:read | 1 | OAuth only |
start_publish | article_id | Claims a draft for 30 minutes; returns claimed/reclaimed/already-claimed state | mcp:act | 10 | OAuth only |
report_publish | article_id, canonical_url on the site's domain | Records the external publish and queues independent verification; idempotent for article+URL | mcp:act | 10 | OAuth only |
publish_article | article_id, provider (wordpress or shopify); as_draft? default false | SearchChamp enqueues its standard CMS publisher and returns job_id; feature-enable and CMS gates apply | mcp:act | 10 | OAuth only |
start_publish/report_publish are for a coding agent that performs the site write itself. publish_article asks SearchChamp to perform the CMS write. Poll get_job after publish_article.
Content Writer and Research Studio editorial tools
These five tools give an AI client the same editorial feed the public REST routes expose, backed by the identical query and authorization code — there is one implementation per capability, so REST and MCP can never drift apart.
These tools read existing content and flip status only
list_content_articles, list_research_sessions, and get_research_session read content SearchChamp has already generated. set_content_article_status and set_research_session_status change a status value in SearchChamp's own database and nothing else.
None of them starts a Content Writer generation run or a Research Studio research run, and neither status tool pushes anything to WordPress, Shopify, or any other connected CMS. Starting a run is a SearchChamp app workflow. For a real CMS publish, use publish_article, which is a different tool with a different scope and a different cost.
| Tool | Required input and optional bounds | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
list_content_articles | site; status? exactly draft or published; limit? 1–100 (default 25); offset? ≥ 0 | { articles[], total, returnedCount, limit, offset } — a stable editorial feed. Each article carries its raw status alongside the draft/published grouping. Archived articles are never returned. | mcp:read | 1 | Business/Pro+ token |
list_research_sessions | site; status? exactly draft or published; limit? 1–100 (default 25); offset? ≥ 0 | { sessions[], total, returnedCount, limit, offset }. Each session carries its raw status. Archived and failed sessions are never returned. | mcp:read | 1 | Business/Pro+ token |
get_research_session | session_id UUID | One session with its latest generated brief, pinned sources, and keywords. brief is null when the session has not been synthesized yet — that is a valid result, not an error. Large sessions report pinsTruncated / keywordsTruncated instead of silently capping. | mcp:read | 1 | Business/Pro+ token |
set_content_article_status | article_id UUID, status — only "published" is accepted | Marks the article published in SearchChamp's database. Idempotent: a repeat reports already_published and does not re-stamp the publish time. Returns the article as the database holds it after the write. | mcp:act | 5 | Business/Pro+ token |
set_research_session_status | session_id UUID, status — only "published" is accepted | Marks the session published in SearchChamp's database. A draft session needs a completed brief; a reviewing session needs nothing further. Idempotent. | mcp:act | 5 | Business/Pro+ token |
status is enforced at the schema boundary, not just inside the handler, so a value outside the documented vocabulary is rejected as invalid_input before anything runs. The accepted target is an allow-list of exactly published: when SearchChamp's internal state machines gain new values, those values do not silently become remotely settable.
Status grouping and what is hidden
status=draft / status=published is a deliberate, stable grouping over internal state machines that have more values and keep growing. Each item's raw value always comes back in its status field.
| Filter | Article statuses | Research session statuses |
|---|---|---|
draft | draft, generating, brief_ready, ready_for_review, approved, failed | draft, researching, reviewing, brief_ready, handed_off |
published | published | published |
| omitted | both groups | both groups |
| never returned | archived | archived, failed |
Research sessions with no site are not visible to MCP clients
A Research Studio session whose site is unset is not readable and not publishable through any external surface.
research_sessions carries no organization column, so a session's tenancy is proven through its site. A session with no site cannot be proven to belong to the calling organization, and SearchChamp treats an unprovable authorization as a failure rather than a pass. Such a session is absent from list_research_sessions and get_research_session reports it as not found. Assign the session a site in the SearchChamp app to make it addressable.
publishedAt is a SearchChamp timestamp, not a CMS one
Both feeds and both status tools return publishedAt — for research sessions this is research_sessions.published_at, a real column with a real producer. It is null for anything not published, and also null for a session that was published before the column existed.
It records when the status was flipped inside SearchChamp. It is not a CMS publication time, because nothing on this surface talks to a CMS.
Worked example — list drafts, then publish one
Example prompt
Show me the draft articles for example.com, then mark "How to choose SEO tools" as published in SearchChamp. Don't publish it to WordPress.
The client would call list_content_articles to find the id, then set_content_article_status with it.
Step 1 — list_content_articles
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_content_articles",
"arguments": {
"site": "example.com",
"status": "draft",
"limit": 25,
"offset": 0
}
}
}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "37 articles for example.com" }],
"structuredContent": {
"articles": [
{
"id": "0f2c9c2a-5f6f-4a53-9a1e-2b2a1f5f0c11",
"title": "How to choose SEO tools",
"slug": "how-to-choose-seo-tools",
"status": "ready_for_review",
"locale": "en",
"wordCount": 1420,
"targetKeyword": "seo tools",
"createdAt": "2026-08-20T09:14:00.000Z",
"updatedAt": "2026-08-27T16:02:00.000Z",
"publishedAt": null
}
],
"total": 37,
"returnedCount": 1,
"limit": 25,
"offset": 0
}
}
}Step 2 — set_content_article_status
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "set_content_article_status",
"arguments": {
"article_id": "0f2c9c2a-5f6f-4a53-9a1e-2b2a1f5f0c11",
"status": "published"
}
}
}'{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{ "type": "text", "text": "Article marked published in SearchChamp." }],
"structuredContent": {
"article": {
"id": "0f2c9c2a-5f6f-4a53-9a1e-2b2a1f5f0c11",
"status": "published",
"publishedAt": "2026-08-29T11:02:41.000Z"
},
"already_published": false
}
}
}Worked example — read a research session and publish it
Example prompt
Pull up the published research sessions for example.com, read the brief for the Q4 competitor one, and publish the draft session that already has a completed brief.
get_research_session
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_research_session",
"arguments": { "session_id": "9c1b6f3e-77d4-4c2a-b0aa-3e5c9f2d1a44" }
}
}'{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [{ "type": "text", "text": "Q4 competitor landscape" }],
"structuredContent": {
"session": {
"id": "9c1b6f3e-77d4-4c2a-b0aa-3e5c9f2d1a44",
"title": "Q4 competitor landscape",
"status": "reviewing",
"publishedAt": null,
"brief": {
"briefDocVersion": 1,
"contentMd": "## Summary\n…",
"generatedAt": "2026-08-26T13:30:00.000Z",
"exportedToContentWriter": false
},
"pins": [],
"pinsTruncated": false,
"keywords": [],
"keywordsTruncated": false
}
}
}
}set_research_session_status
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "set_research_session_status",
"arguments": {
"session_id": "9c1b6f3e-77d4-4c2a-b0aa-3e5c9f2d1a44",
"status": "published"
}
}
}'When a status change is refused
Both status tools accept only a resource already in a publishable state.
| Resource | Publishable from | Anything else |
|---|---|---|
| Article | approved, ready_for_review, or draft with a body | invalid_status, reporting the current status |
| Research session | reviewing, or draft with a completed brief | invalid_status, reporting the current status |
A refusal names the current state as data rather than prose, and adds a reason when the status alone does not explain it: empty_body for an article, no_brief for a session. Calling again on an already-published resource is not a refusal — it succeeds, reports already_published, and leaves the original publish time untouched.
Search Console, GA4, and keyword research
These ten tools let an AI client diagnose a page and propose keywords from evidence, without the user switching to the SearchChamp app. Nine are read-only. One — run_keyword_research — spends credits, and only after the end user explicitly confirms a scope they were shown.
What these tools will and will not do
- Read-only by default. No tool here adds a keyword to the library, changes rank tracking, edits or publishes a page, or starts an integration sync.
- Research is review-only.
run_keyword_researchreturns candidates for the user to review. Keeping any of them is a separate action the user takes on the Keywords page in the SearchChamp app. - Research needs explicit consent. Call
preview_keyword_research, show the user the scope and cost, and callrun_keyword_researchonly after they say yes. An AI client must not setuser_confirmedon the user's behalf. - A Search Console sync is never started from MCP.
get_integration_healthreports whether a manual sync is possible; the user runs it in the app.
| Tool | Required input and optional bounds | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
get_integration_health | site | Whether Search Console and GA4 are connected, the connected property, the latest date each can report, the last manual Search Console sync and its outcome, and what the nightly keyword join has stored. Connection is read from the stored integration, not verified against Google. | mcp:read | 1 | Business/Pro+ token |
get_search_console_performance | site; dimension? one of site (default), page, query, country, device, date; either days? 1–480 (default 28) or start_date + end_date as YYYY-MM-DD; filters page? (a URL on this site, matched across http/https, www, and trailing-slash variants), query_contains?, country? (ISO alpha-3, e.g. are), device? (desktop, mobile, tablet); limit? 1–500 (default 50) | Live clicks, impressions, CTR, and average position with the source, property, exact reporting range, and latest available date. Totals come from a property-level query, so they match the Search Console UI. | mcp:read | 1 | Business/Pro+ token |
get_ga4_performance | site; report? one of overview (default), pages, channels, trend; days? 1–365 (default 28); page_path? starting with / (matched with or without its trailing slash); limit? 1–250 (default 25) | Live sessions, page views, engaged sessions, engagement rate, and average session duration — overall, per page, per default channel group, or per day — with the source, property, and exact reporting range. | mcp:read | 1 | Business/Pro+ token |
get_keyword_inventory | site; cluster_id? UUID; search?; source?; tracking? one of daily, weekly, any, none; limit? 1–200 (default 50); offset? ≥ 0 | The keywords the site already has: cluster, market, stored volume/difficulty/CPC/intent, provenance, the data date of those metrics and a stale flag, joined Search Console evidence, tracked state and cadence, and mapped-page count. Exact filtered total. | mcp:read | 1 | Business/Pro+ token |
get_keyword_clusters | site | Each cluster's keyword count, total stored volume, tracked and daily-tracked counts, and keyword count per provenance source; plus the ungrouped keyword count. | mcp:read | 1 | Business/Pro+ token |
get_page_keyword_coverage | site, url (a page on this site); limit? 1–100 (default 50) | The keywords mapped to that page and how, plus the Search Console queries stored for it (trailing 28 days as of dataDate), each flagged inInventory. | mcp:read | 1 | Business/Pro+ token |
get_page_diagnosis | site, url (a page on this site); days? 1–365 (default 28) | One call combining the page's live Search Console performance and top queries, live GA4 engagement, latest audit findings, pending internal-link opportunities, backlinks, and keyword coverage. Each section reports its own availability. | mcp:read | 1 | Business/Pro+ token |
preview_keyword_research | site, seeds (1–10 phrases, each ≤ 120 chars); market as either country + language (ISO codes, e.g. AE + en) or location_code + language_code, or omit both to use the site's stored keyword market; url? (a page on this site) | Spends nothing. The resolved scope, the cost to start and what else a run can cost, credit balance, research runs remaining today, whether each seed is already in the library, any blockers, and a confirmation.scope_hash. | mcp:read | 1 | Business/Pro+ token |
run_keyword_research | The same scope fields as the preview, plus user_confirmed: true, scope_hash from the preview, and idempotency_key 1–128 chars | Spends credits. Starts the same research the Keywords page runs and returns a job_id. Refused unless user_confirmed is true and scope_hash matches this exact scope. Needs a signed-in user and the Editor role. | mcp:spend | 1 | OAuth only |
get_keyword_research_result | site, job_id UUID | Status while running. When finished, result is candidates, empty, failed, or canceled. With candidates: new_opportunities and already_earning_impressions as separate lists, each with metrics and provenance. Always an outcome with counts at every stage, a sample of rejected terms with reasons, and a plain-language reason when nothing is returned. | mcp:read | 1 | Business/Pro+ token |
Data sources, freshness, and "no data"
Every Search Console and GA4 result names its source, the property, and the exact reporting_range, so a client can always say where a number came from and which dates it covers.
| Data | Where it comes from | How fresh |
|---|---|---|
get_search_console_performance | Google Search Console, read live | Finalised data only. Google finalises on a two-day lag, so latest_available_date is two days ago. Cached for 30 minutes per site (served_from_cache). |
get_ga4_performance | Google Analytics 4, read live | The newest whole day is yesterday — today is incomplete in GA4. Cached for 30 minutes per site. |
get_keyword_inventory, get_keyword_clusters | SearchChamp's stored keyword library | Stored figures, not a fresh lookup. metrics.dataDate says when volume and difficulty were pulled. metrics.stale: true means they were measured for a different market than the keyword now sits in. In get_keyword_inventory, provenance.research.basis: "keyword_suggestions" marks a keyword saved from keyword-suggestions research — search data, not model inference — even though its source bucket reads AI-Inference; requestedMarket is the market that research ran for. research: null means no basis was recorded, and nothing is inferred. |
get_page_keyword_coverage | The nightly Search Console join SearchChamp stores | A trailing 28 days as of dataDate. |
A connection problem is always a named state, never an empty result:
state | Meaning |
|---|---|
ok | Connected and read. |
not_connected | The integration is not connected for this site. Connect it in the SearchChamp app. |
needs_reconnect | It is connected, but the stored authorisation is no longer valid. Reconnect it in the app. |
read_failed | It is connected, but Google did not return the data just now. Try again later. |
A metric of null means the provider had no data for it. Never read null as zero.
Search Console sync
Search Console performance is read live, so it does not depend on a sync. get_integration_health reports the last manual sync as never_synced, in_progress, ok, partial, or failed, and whether another is possible. A manual sync calls Google on the workspace's quota, so it stays a user-approved action: an Editor or above chooses Sync now on the Search Console page in the app. No MCP tool starts one.
The keyword research workflow
Candidate provenance. A recorded originating provider stays attached when keyword suggestions are reused from cache. A suggestion served through SearchChamp's data gateway returns provenance.provider: "DataForSEO" only when the gateway reports that item's own source as DataForSEO. Gateway results without a per-item source, an unknown source, and older cached suggestions without originating-provider metadata return provenance.provider: null; the app shows provider unavailable. A source label such as “Keyword suggestions” or a transport label such as “cache” is not provider attribution. A cache fetch time is not a measurement date: missing reporting dates remain data_date: null. Re-reading an existing research result preserves its recorded evidence; it does not retroactively infer a provider or refresh its data.
Research is a two-step workflow so that a credit is never spent without the user's agreement.
- Check what exists. Call
get_keyword_inventory(andget_page_keyword_coveragefor a specific page) so you do not research what the site already has. - Preview. Call
preview_keyword_researchwith the seeds, target market, and optional page URL. Nothing is spent. - Ask the user. Show them the scope and the cost (see What research costs).
- Run, only on a yes. Call
run_keyword_researchwith the same seeds, market, and URL,user_confirmed: true, and the preview'sscope_hash. If anything changed since the preview, the call returnsconfirmation_mismatchand spends nothing — preview again. Ascope_hashis valid only on the UTC day it was previewed. - Review. Poll
get_keyword_research_resultwith thejob_id, and read itsresultfield.
What research costs
The preview's estimated_cost separates three charges, because they are not all known in advance:
| Field | What it is |
|---|---|
start_credits | Charged when the run starts. Returned if the run fails or finds no new candidates. |
tool_call_credits | The MCP charge for the run_keyword_research call itself. |
minimum_total_credits | start_credits + tool_call_credits — what starting a run costs. |
recent_usage_credits | A run that returns candidates also records usage-based credits for the research itself. The amount varies, so the preview reports this site's recent completed runs (count, average, maximum) as a guide. null means the site has no recent history — not that the usage is zero. |
Tell the user both parts: the credits to start, and that a successful run records additional usage-based credits.
The target market is never guessed. Give a country and language, or leave both out to use the site's stored keyword market. If the site has no keyword market set, the preview returns market_required rather than researching a default country. English, Spanish, and Arabic markets are supported; any other market returns unsupported_market, and that research is run from the Keywords page in the app instead.
Relevance and location checks. The research uses the same rules as the Keywords page in the app. A term that does not match the seed topic is rejected as off_topic. A term that names a place outside the target market — for example a New York term in a United Arab Emirates run — is rejected as off_market, unless the user named that place in a seed. Rejected terms are listed in outcome.rejected_sample with their reason; they are never returned as recommendations.
An empty result always has a reason. Read result, not status: a run that finds nothing new is recorded as failed internally so that its start credit is returned, and it comes back as result: "empty". outcome.reason and outcome.explanation say which of these happened:
outcome.reason | Meaning |
|---|---|
candidates_available | New candidates are ready for review. |
duplicate_only | Every relevant term is already in the keyword library. |
rejected_only | The provider returned terms, but none passed the relevance and target-market checks. |
provider_empty | The provider returned no keyword data for these seeds in this market. |
provider_unavailable / provider_error | The provider could not be reached or returned an error. |
research_incomplete | The run did not finish cleanly. Do not read this as "no keywords exist". |
outcome.stage_counts reports how many terms were proposed, unique, rejected (unobserved, off-topic, off-market), duplicated (within the run, or already in the library), and accepted.
Worked example — preview, confirm, and run research
Example prompt
Find new keywords for our iOS app development page in Dubai. Tell me what it costs before you run anything.
Step 1 — preview_keyword_research
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "preview_keyword_research",
"arguments": {
"site": "example.ae",
"seeds": ["iOS app development Dubai"],
"country": "AE",
"language": "en",
"url": "https://example.ae/ios-app-development-dubai"
}
}
}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "Preview only — nothing was spent. Researching 1 seed(s) for AE / en costs 2 credit(s) to start (the start credit is returned if nothing new is found), plus usage-based credits if candidates are found. Ask the user to confirm this scope and cost before calling run_keyword_research." }],
"structuredContent": {
"action": "preview",
"scope": {
"site": "example.ae",
"seeds": ["iOS app development Dubai"],
"market": { "country": "AE", "location_code": 2784, "language_code": "en", "source": "explicit" },
"target_url": "https://example.ae/ios-app-development-dubai"
},
"estimated_cost": {
"start_credits": 1,
"tool_call_credits": 1,
"minimum_total_credits": 2,
"recent_usage_credits": { "completed_runs_last_90_days": 4, "average": 3.6, "maximum": 5.2 }
},
"daily_runs": { "limit": 20, "used": 3, "remaining": 17 },
"existing_coverage": {
"keywords_in_this_market": 57,
"seeds": [{ "seed": "iOS app development Dubai", "already_in_library": true }]
},
"can_run": true,
"blockers": [],
"confirmation": { "scope_hash": "5f0c9c2a5f6f4a539a1e2b2a1f5f0c11" }
}
}
}The client now tells the user the scope, the 2 credits to start, and that a run returning candidates also records usage-based credits (recently about 3.6 on this site) — and waits for a yes.
Step 2 — run_keyword_research, after the user confirms
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "run_keyword_research",
"arguments": {
"site": "example.ae",
"seeds": ["iOS app development Dubai"],
"country": "AE",
"language": "en",
"url": "https://example.ae/ios-app-development-dubai",
"user_confirmed": true,
"scope_hash": "5f0c9c2a5f6f4a539a1e2b2a1f5f0c11",
"idempotency_key": "ios-dubai-2026-10-06"
}
}
}'Step 3 — get_keyword_research_result
curl --fail-with-body 'https://mcp.searchchamp.com/mcp' \
--header "Authorization: Bearer $SEARCHCHAMP_MCP_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_keyword_research_result",
"arguments": { "site": "example.ae", "job_id": "9c1b6f3e-77d4-4c2a-b0aa-3e5c9f2d1a44" }
}
}'Limits
run_keyword_researchis OAuth only. A user-generated token can preview research and read a result, but cannot start a run, because a token has no signed-in user to give the confirmation.- Research runs are capped per site per day; the preview reports how many remain.
get_integration_healthdoes not contact Google, so it cannot tell that an authorisation was revoked. A performance read reports that asneeds_reconnect.get_page_keyword_coveragereads stored data.status: "no_stored_data"means nothing is stored for that page — the integration may not be connected, the nightly join may not have run yet, or the page may have no impressions. Useget_integration_healthandget_search_console_performanceto tell which.- Page tools accept only a URL on the addressed site's own domain. Anything else returns
url_not_on_site.
Custom-site publication artifacts
| Tool | Required input and optional bounds | Returned data | Scope | Credits | Token |
|---|---|---|---|---|---|
list_pending_publication_pages | site; limit? 1–100 (default 25), offset? 0–10,000 | Exact total and bounded page of Content Writer pages awaiting custom-site publication | mcp:read | 1 | Business/Pro+ token |
get_pending_publication_page | site, article_id UUID | Source/proposed versions and a drift-safe ChangeArtifactV1 | mcp:read | 1 | Business/Pro+ token |
list_internal_link_edits | site; limit? 1–100 (default 25), offset? 0–10,000 | Exact total and bounded pending edits without full page bodies | mcp:read | 1 | Business/Pro+ token |
get_internal_link_edit | site, suggestion_id UUID | Exact-context, drift-safe ChangeArtifactV1 | mcp:read | 1 | Business/Pro+ token |
Page Optimizer apply and verification
| Tool | Required input and optional bounds | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
list_approved_changes | site; approved status?; limit? 1–100 (default 25) | Approved changes with page/section identity, summary, lifecycle status, and verification timestamps | mcp:read | 1 | Business/Pro+ token |
get_change | id | Exact approved before/after HTML, page URL, anchor, heading, kind, summary, selector hints, and status | mcp:read | 1 | Business/Pro+ token |
mark_applied | id | Reports a live application and triggers an independent re-crawl; settles verified or applied-unverified | mcp:act | 10 | OAuth only |
list_approved_changes defaults to approved but not conclusively verified work (accepted, applied, and applied_unverified). mark_applied never accepts HTML from the caller and is rate-bounded/idempotent; the agent's report is not the final verification.
llms.txt
| Tool | Required input and optional bounds | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
get_llms_txt | site | Current configuration/content and latest generation state | mcp:read | 1 | Business/Pro+ token |
generate_llms_txt | site, idempotency_key 1–128 chars | Creates or reuses a generation job | mcp:spend | 20 | Business/Pro+ Full token |
get_llms_txt_job | site, run_id UUID | One site-scoped generation job | mcp:read | 1 | Business/Pro+ token |
list_llms_txt_versions | site; limit? 1–100 (default 25), offset? 0–10,000 | Exact total and bounded generated-version list | mcp:read | 1 | Business/Pro+ token |
diff_llms_txt_versions | site, from_version_id, to_version_id UUIDs | Exact unified diff between two versions from the same site | mcp:read | 1 | Business/Pro+ token |
The caller supplies a stable idempotency_key so reconnects/retries do not enqueue equivalent generation work twice.
Actions, natural-language answers, and jobs
| Tool | Required input | Returned data / effect | Scope | Credits | Token |
|---|---|---|---|---|---|
list_actions | site; status? | Site agent actions, optionally filtered | mcp:read | 1 | OAuth only |
approve_action | action_id | Audited, idempotent approval. Eligible CMS meta writes can execute only when mcp:act is also granted and execution is enabled; spending runs otherwise remain proposed for in-app approval | mcp:approve | 5 | OAuth only |
decline_action | action_id | Audited, idempotent decline | mcp:approve | 5 | OAuth only |
ask_atlio | site, message | Grounded answer plus suggested actions (capability, params, billing preview, action class); spending commands are proposed, not silently run | mcp:read | 20 | OAuth only |
get_job | job_id | Status, stage, percent, partial summary, and poll_after_ms; poll until succeeded/failed/canceled | mcp:read | 1 | OAuth only |
ask_atlio requires editor-or-higher access to the addressed site. Although it requires the read scope, it is LLM-backed and costs 20 credits. The server's published cost is authoritative if this table and a live tools/list ever differ.
Token catalog summary
User-generated tokens intentionally expose a smaller catalog:
- Every paid plan:
list_sites,get_rankings,get_ai_visibility. - Business/Pro and higher: also
get_audit_issues,get_backlinks_summary,get_content_drafts,get_volatility, the approved-change read tools, custom-publication artifact reads, llms.txt reads, the editorial readslist_draft_articles,get_article_content,list_content_articles,list_research_sessions, andget_research_session, and — withmcp:act— the two status toolsset_content_article_statusandset_research_session_status. - Business/Pro and higher: also the ten Search Console, GA4, and keyword read tools —
get_integration_health,get_search_console_performance,get_ga4_performance,get_keyword_inventory,get_keyword_clusters,get_page_keyword_coverage,get_keyword_page_coverage,get_page_diagnosis,preview_keyword_research, andget_keyword_research_result. - Business/Pro and higher Full tokens: also
generate_llms_txtthroughmcp:spend. run_keyword_researchremains OAuth-only even for a Full token: it spends on the end user's explicit confirmation, and a token has no signed-in user.- Approval, action-decision,
ask_atlio,mark_applied, and CMS publishing tools (start_publish,report_publish,publish_article) remain OAuth-only. The twoset_*_statustools are not CMS publishing and are available to Business/Pro+ tokens holdingmcp:act.
The organization plan is evaluated at call time. Downgrading can immediately reduce a token's visible catalog; upgrading expands it without reminting the token.