# MCP tools

> The tools and prompts of the Checkend MCP server, with each input parameter and output field, for Claude Code, Cursor and other agents.

> **Note: Generated page**
> Generated from the Checkend repository at `ee1e2be` (`ee1e2be`) by `npm run sync`. Do not edit this page by hand.

The MCP server is at `<public URL>/mcp` (Streamable HTTP). To connect a client, read [Connect your agent](/docs/start/connect-your-agent.md). Each tool checks the scopes of the token. The REST API and `ce` give the same numbers, because all three use one query core.

| Tool | What it does |
|---|---|
| [`query`](#query) | Page views, visitors, the top 10 pages and the top 10 countries of one site, for a period that ends now. |
| [`timeseries`](#timeseries) | Page views, visitors, conversions, conversion rate and revenue of one site for each hour (period 24h) or each UTC day (7d, 30d, 365d). |
| [`list_sites`](#list_sites) | List the sites that this token can read, with the project and the privacy mode of each site.. |
| [`manage_goals`](#manage_goals) | List, add or delete the goals of a site. |
| [`get_brief`](#get_brief) | The main entry point for an agent: one compact package for a site, a project or all readable sites, for a period that ends now (default 7d). |
| [`get_findings`](#get_findings) | Findings that the rules found, with evidence numbers, the estimated impact, priority, confidence, status and data_until, and the outcome (worked, got_worse, no_clear_change or too_few_to_compare, with the test, the p-value and the power) once measured. |
| [`update_finding`](#update_finding) | Change the status of a finding: claim (you work on it), done (the change is made: give annotation, the ID of an annotation, or text for a new one, with link to the pull request), dismiss (not useful; give a note) or reopen. |
| [`annotate`](#annotate) | Record a change on a site at a time, for example "Title changed on /pricing" with the pull request link. |
| [`search_performance`](#search_performance) | Search Console and Bing rows of one site, by query (default) or by page, for a period that ends at the newest search date (7d, 28d default, 90d, 365d): clicks, impressions, CTR and the average position. |
| [`connect_search_console`](#connect_search_console) | The Search Console (google) and Bing Webmaster Tools (bing) connection of a site. |
| [`get_page`](#get_page) | What the Checkend crawler found on a page now: title, meta description, H1, canonical URL, indexability (and why not), robots.txt, and the newest Search Console URL Inspection. |
| [`web_vitals`](#web_vitals) | Web vitals from real visitors of one site, for a period that ends now (24h, 7d, 30d default, 365d): the 75th percentile of LCP (ms), INP (ms, the longest interaction), CLS (score) and TTFB (ms) for the site and for its top pages (path gives one page), each with n, the web.dev rating (good, needs_improvement, poor) and the share of good loads. |
| [`ai_crawlers`](#ai_crawlers) | AI and search crawlers on one site, from the web server access logs that the admin pushes (ce logs push): the hits, pages, errors and last day of each crawler (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, PerplexityBot, Googlebot, Bingbot and others), with its operator and purpose (training, ai_search, user_request, search), and the top pages with the hits of each crawler. |
| [`indexnow`](#indexnow) | IndexNow of one site: tell Bing and the other IndexNow engines that pages changed. |
| [`get_report`](#get_report) | A stored report (report_v1) of a site or a project for one window in whole days of a time zone: the main numbers against the window before (Poisson rate test for counts, two-proportion z-test for the conversion rate, 95 %), the top pages, sources and channels, the goals, the revenue for each currency (never added), the web vitals p75, the search totals when a source is connected, the findings opened and closed, the outcomes measured (worked, got_worse, no_clear_change, too_few_to_compare), the warnings and the annotations of the window. |
| [`create_site`](#create_site) | Create a site and return the script snippet to add to the site's root layout, and the install prompt (install.prompt): the steps for Next.js, Astro, Rails, WordPress and static HTML, the goal events, the optional first-party proxy route and an install token that can only check the install of this site for 24 hours. |
| [`verify_install`](#verify_install) | Check that the events of a site arrive: status waiting (no event in 7 days), test_only (only the test event; the script tag sends nothing yet) or receiving (page views arrive), with the first and the newest event time and the newest page. |

## query

Page views, visitors, the top 10 pages and the top 10 countries of one site, for a period that ends now. Periods: 24h, 7d, 30d (default), 365d. The answer gives data_until (the newest event), sample_size (the raw event count), the top 10 referrer hosts ("" is Direct) and the totals of the period before (previous; not tested for significance). It also gives conversions (visitors who completed a goal), conversion_rate, revenue for each currency (never added across currencies) and the numbers of each goal. In strict privacy mode conversions count events, not visitors (conversions_unit and conversions_note say so). Needs read access to the site's project.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `period` | string | no | the period: 24h, 7d, 30d or 365d (default 30d) |

**Output fields:** `site` (string), `period` (string), `from` (string), `to` (string), `pageviews` (integer), `visitors` (integer), `top_pages` (object[]), `top_countries` (object[]), `top_sources` (object[]), `top_channels` (object[]), `data_until` (string), `sample_size` (integer), `events` (integer), `conversions` (integer), `conversion_rate` (number), `conversions_unit` (string), `visitors_note` (string), `conversions_note` (string), `revenue` (object[]), `goals` (object[]), `previous` (object).

## timeseries

Page views, visitors, conversions, conversion rate and revenue of one site for each hour (period 24h) or each UTC day (7d, 30d, 365d). Give a goal ID for the conversions and the revenue of one goal. Buckets without events have zeros; the first and the last bucket are partial. Needs read access to the site's project.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `period` | string | no | the period: 24h, 7d, 30d or 365d (default 30d) |
| `goal` | string | no | a goal ID: the conversions and the revenue of this goal only; all goals when empty |

**Output fields:** `site` (string), `period` (string), `interval` (string), `from` (string), `to` (string), `points` (object[]), `goal` (string), `conversions_unit` (string), `data_until` (string), `sample_size` (integer).

## list_sites

List the sites that this token can read, with the project and the privacy mode of each site.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | no | a project ID; all readable projects when empty |

**Output fields:** `sites` (object[]).

## manage_goals

List, add or delete the goals of a site. A goal is a page path pattern (page, for example /pricing or /blog/*) or a custom event name (event, for example Signup). action: list (default; needs read access), add (give name and exactly one of page and event) or delete (give id); add and delete need write access to the site's project. The answer is the goal list after the action.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `action` | string | no | list (default), add or delete |
| `name` | string | no | add: the goal name; default: the event name or the page pattern |
| `page` | string | no | add: a page path pattern, for example /pricing or /blog/* |
| `event` | string | no | add: a custom event name, for example Signup |
| `id` | string | no | delete: the goal ID |

**Output fields:** `site` (string), `goals` (object[]).

## get_brief

The main entry point for an agent: one compact package for a site, a project or all readable sites, for a period that ends now (default 7d). It has the top changes against the period before (each with a Poisson test p-value; significant means 95 % confidence), the open and claimed findings, the annotations and releases of the period, the outcomes of earlier changes, newest first (site memory: what was tried, the result, the test and the power), warnings for each change that made its metric worse (got_worse: do not repeat it) and the data date of each source. Webhooks send the same JSON. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | no | a site domain; give a site or a project, or nothing for all readable sites |
| `project` | string | no | a project ID |
| `period` | string | no | the period that ends now: 24h, 7d (default), 30d or 365d |

**Output fields:** `version` (string), `site` (string), `project` (string), `sites` (string[]), `period` (string), `from` (string), `to` (string), `generated_at` (string), `top_changes` (object[]), `open_findings` (object[]), `annotations` (object[]), `outcomes` (object[]), `warnings` (string[]), `data_dates` (object[]), `sample_size` (integer).

## get_findings

Findings that the rules found, with evidence numbers, the estimated impact, priority, confidence, status and data_until, and the outcome (worked, got_worse, no_clear_change or too_few_to_compare, with the test, the p-value and the power) once measured. By default only open and claimed findings (the work to do); give status for others. Give id for one finding with its history and its outcome history. Before you start on a finding, claim it with update_finding, so that other agents do not repeat the work. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | no | one finding ID: the answer has this finding only, with its history |
| `site` | string | no | a site domain; all readable sites when empty |
| `project` | string | no | a project ID; all readable projects when empty |
| `status` | string | no | statuses, comma-separated; default: open,claimed |
| `limit` | integer | no | the most findings to return, 1 to 200 (default 50) |
| `token` | string | no | the name of the token that claimed the finding |
| `action` | string | no | action types, comma-separated, for example fix_redirect,investigate |
| `period` | string | no | only findings with a change in the period that ends now: 24h, 7d, 30d or 365d; default: all |

**Output fields:** `findings` (object[]).

## update_finding

Change the status of a finding: claim (you work on it), done (the change is made: give annotation, the ID of an annotation, or text for a new one, with link to the pull request), dismiss (not useful; give a note) or reopen. After done, Checkend starts measuring on the next UTC day, and sets the outcome when the 14 days after the change day are complete (D48 tests at 95 %). remeasure (admin token only) computes the outcome again and keeps the history. dry_run true checks the change only. Needs write access to the site's project.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | the finding ID |
| `action` | string | yes | claim, done, dismiss, reopen or remeasure (admin: compute the outcome again) |
| `note` | string | no | why, at most 500 characters; it goes to the history |
| `annotation` | string | no | done: the ID of the annotation of the change |
| `text` | string | no | done: the text of a new annotation of the change (kind agent_change) |
| `link` | string | no | done: a URL about the change for the new annotation, for example the pull request |
| `at` | string | no | done: the time of the change for the new annotation; default now |
| `dry_run` | boolean | no | true: check the change and return the result, but do not make it |

**Output fields:** `id` (string), `site` (string), `project` (string), `type` (string), `action` (string), `page` (string), `query` (string), `summary` (string), `page_now` (object), `evidence` (object), `impact` (object), `priority` (string), `confidence` (string), `status` (string), `claimed_by` (object), `annotation` (string), `change_at` (string), `data_until` (string), `first_seen` (string), `last_seen` (string), `updated_at` (string), `history` (object[]), `outcome` (object), `outcome_history` (object[]).

## annotate

Record a change on a site at a time, for example "Title changed on /pricing" with the pull request link. kind: agent_change (default), release or note. Give finding to link the change to a finding (update_finding done also makes the annotation). at is RFC 3339 or a UTC date; default now. Needs write access to the site's project.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `text` | string | yes | what changed, 1 to 500 characters, for example: Title changed on /pricing |
| `kind` | string | no | agent_change (default), release or note |
| `at` | string | no | the time of the change: RFC 3339 or a UTC date (2026-10-22); default now |
| `finding` | string | no | the ID of the finding that the change is for |
| `link` | string | no | an http or https URL about the change, for example the pull request |

**Output fields:** `id` (string), `site` (string), `at` (string), `kind` (string), `text` (string), `finding` (string), `link` (string), `actor` (object), `created_at` (string).

## search_performance

Search Console and Bing rows of one site, by query (default) or by page, for a period that ends at the newest search date (7d, 28d default, 90d, 365d): clicks, impressions, CTR and the average position. Page rows also have the Checkend page views, visitors, the page views from Organic Search, the conversions of the page's visitors and the current title. position 8-20 gives the striking-distance rows. Search data is 2 to 3 days late, and Search Console leaves out rare queries (notes says so). Without a connection, the answer says the next step. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `period` | string | no | 7d, 28d (default), 90d or 365d; the period ends at the newest search date |
| `by` | string | no | query (default) or page |
| `position` | string | no | keep rows with an average position in this range, for example 8-20 (striking distance) |
| `source` | string | no | google, bing or all (default) |
| `query` | string | no | keep rows whose query contains this text |
| `page` | string | no | keep rows whose page URL contains this text |
| `limit` | integer | no | the most rows (default 50, at most 500) |

**Output fields:** `site` (string), `period` (string), `by` (string), `source` (string), `from` (string), `to` (string), `totals` (object), `previous` (object), `rows` (object[]), `data_until` (string), `sample_size` (integer), `connections` (object[]), `conversions_unit` (string), `notes` (string[]), `next` (string).

## connect_search_console

The Search Console (google) and Bing Webmaster Tools (bing) connection of a site. action status (default; read access) shows each source, its synced dates and the last error. action connect (write access): google returns authorize_url in next, a consent page that a person with access to the property must open in a browser (Checkend stores the refresh token encrypted); bing needs api_key. property: sc-domain:example.com or https://example.com/ (default sc-domain:<site>). action disconnect deletes the stored token of source. action sync syncs now (the job also runs each hour).

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `action` | string | no | status (default), connect, disconnect or sync |
| `source` | string | yes | google (Search Console) or bing (Bing Webmaster Tools) |
| `property` | string | no | google: sc-domain:example.com (domain property) or https://example.com/ (URL-prefix property); bing: the site URL, for example https://example.com/; default: sc-domain:<site> (google) or https://<site>/ (bing) |
| `api_key` | string | no | bing only: the API key from Bing Webmaster Tools (Settings, API Access); Checkend stores it encrypted |

**Output fields:** `site` (string), `connections` (object[]), `google_client` (boolean), `redirect_url` (string), `dev` (boolean).

## get_page

What the Checkend crawler found on a page now: title, meta description, H1, canonical URL, indexability (and why not), robots.txt, and the newest Search Console URL Inspection. Give path for one page, or none for all crawled pages. Use it before you propose a new title. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `path` | string | no | one page path, for example /pricing; all crawled pages when empty |

**Output fields:** `site` (string), `pages` (object[]), `user_agent` (string), `next` (string).

## web_vitals

Web vitals from real visitors of one site, for a period that ends now (24h, 7d, 30d default, 365d): the 75th percentile of LCP (ms), INP (ms, the longest interaction), CLS (score) and TTFB (ms) for the site and for its top pages (path gives one page), each with n, the web.dev rating (good, needs_improvement, poor) and the share of good loads. Use it before you propose a speed change, and to check one. Not tested for significance. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `period` | string | no | 24h, 7d, 30d (default) or 365d, ending now |
| `path` | string | no | one page path, for example /pricing; empty for the top pages |
| `limit` | integer | no | the most pages, 1 to 100 (default 20), with the most reports first |

**Output fields:** `site` (string), `period` (string), `from` (string), `to` (string), `data_until` (string), `sample_size` (integer), `metrics` (object[]), `pages` (object[]), `thresholds` (object[]), `notes` (string[]), `next` (string).

## ai_crawlers

AI and search crawlers on one site, from the web server access logs that the admin pushes (ce logs push): the hits, pages, errors and last day of each crawler (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, PerplexityBot, Googlebot, Bingbot and others), with its operator and purpose (training, ai_search, user_request, search), and the top pages with the hits of each crawler. Period in whole UTC days (24h, 7d, 30d default, 365d); bot gives one crawler. The user agent is not verified. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `period` | string | no | 24h, 7d, 30d (default) or 365d, in whole UTC days |
| `bot` | string | no | one crawler name, for example GPTBot |
| `limit` | integer | no | the most pages, 1 to 100 (default 20) |

**Output fields:** `site` (string), `period` (string), `from` (string), `to` (string), `data_until` (string), `sample_size` (integer), `bots` (object[]), `pages` (object[]), `notes` (string[]), `next` (string).

## indexnow

IndexNow of one site: tell Bing and the other IndexNow engines that pages changed. action status (default; read access) shows on or off, the key file, the last submit and the queue. action submit (write access) adds urls (absolute, on the site host, at most 100) with a reason; the job sends the queue every 10 minutes, at most 1,000 URLs a day. Checkend also queues the pages that the crawler finds changed and the page of each agent change on a finding (update_finding done). Only an admin turns IndexNow on (ce indexnow set).

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `action` | string | no | status (default) or submit |
| `urls` | string[] | no | submit: the changed URLs on the site host |
| `reason` | string | no | submit: why the pages changed |

**Output fields:** `site` (string), `enabled` (boolean), `mode` (string), `key` (string), `key_url` (string), `served_at` (string), `verified_at` (string), `last_submit_at` (string), `last_status` (integer), `last_error` (string), `next_at` (string), `pending` (integer), `submitted_24h` (integer), `max_per_day` (integer), `endpoint` (string), `queue` (object[]), `proxy` (object[]), `queued` (integer), `next` (string).

## get_report

A stored report (report_v1) of a site or a project for one window in whole days of a time zone: the main numbers against the window before (Poisson rate test for counts, two-proportion z-test for the conversion rate, 95 %), the top pages, sources and channels, the goals, the revenue for each currency (never added), the web vitals p75, the search totals when a source is connected, the findings opened and closed, the outcomes measured (worked, got_worse, no_clear_change, too_few_to_compare), the warnings and the annotations of the window. Without id: the newest report that matches site or project, frequency (daily, weekly, monthly) and date (a day in the window, YYYY-MM-DD). format md also gives the report as Markdown text. Schedules make reports and send the webhook report.ready; get_brief gives the live numbers instead. Needs read access.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | no | a report ID; without it, the newest report that matches the other inputs |
| `site` | string | no | a site domain: the reports of this site |
| `project` | string | no | a project ID: the project reports (all sites) of this project |
| `frequency` | string | no | daily, weekly or monthly |
| `date` | string | no | a local date (YYYY-MM-DD): the report whose window has this day |
| `format` | string | no | json (default) or md: the report also as Markdown text from a fixed template, to paste |

**Output fields:** `version` (string), `id` (string), `title` (string), `site` (string), `project` (string), `sites` (string[]), `schedule` (string), `frequency` (string), `time_zone` (string), `window` (object), `previous` (object), `summary` (string[]), `warnings` (string[]), `site_reports` (object[]), `findings` (object), `outcomes` (object), `annotations` (object[]), `data_dates` (object[]), `sample_size` (integer), `generated_at` (string), `created_by` (string).

## create_site

Create a site and return the script snippet to add to the site's root layout, and the install prompt (install.prompt): the steps for Next.js, Astro, Rails, WordPress and static HTML, the goal events, the optional first-party proxy route and an install token that can only check the install of this site for 24 hours. Follow the prompt, then call verify_install. Needs write access to the project. The privacy mode is standard unless you give strict or extended.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `domain` | string | yes | the site domain, for example example.com |
| `project` | string | no | the project ID; the default project when empty |
| `privacy_mode` | string | no | strict, standard (default) or extended |

**Output fields:** `id` (string), `project` (string), `domain` (string), `privacy_mode` (string), `outcome_mde` (number), `created_at` (string), `snippet` (string), `install` (object).

## verify_install

Check that the events of a site arrive: status waiting (no event in 7 days), test_only (only the test event; the script tag sends nothing yet) or receiving (page views arrive), with the first and the newest event time and the newest page. send_test true first sends one test event (a custom event named "Checkend install test"). The install token of the site (from the install prompt) can call this tool and nothing else. A read token can also call it; send_test needs write access or the install token.

**Input**

| Name | Type | Required | Description |
|---|---|---|---|
| `site` | string | yes | the site domain, for example example.com |
| `send_test` | boolean | no | true sends one test event (a custom event named Checkend install test) first, to check that the server takes events |

**Output fields:** `site` (string), `site_id` (string), `status` (string), `pageviews` (integer), `events` (integer), `test_events` (integer), `first_event_at` (string), `newest_event_at` (string), `newest_path` (string), `checked_at` (string), `test_event` (object), `next` (string).

## Prompts

The server also gives MCP prompts. A prompt gives your agent the text of a task and the data. Your own model does the work: Checkend never calls a model.

### weekly_seo_review

**Weekly SEO review.** A weekly SEO review of one site: the brief of the last 7 days, the open findings, the striking-distance queries, the top search pages, the web vitals, the AI crawlers and IndexNow, with the steps of the closed loop (claim, change, annotate, measure).

| Argument | Required | Description |
|---|---|---|
| `site` | yes | the site domain, for example example.com |

### investigate_traffic_drop

**Investigate a traffic drop.** Find the cause of a traffic drop of a site or a page: the numbers against the period before, the daily series, the sources and channels, the changes and releases, the findings, the search rows, the web vitals, the crawl and the install status.

| Argument | Required | Description |
|---|---|---|
| `site` | yes | the site domain, for example example.com |
| `page` | no | a page path, for example /pricing; empty for the whole site |
| `period` | no | 24h, 7d (default), 30d or 365d |

### launch_check

**Launch check for a new page.** Check a new page before and after its launch: the crawl (title, meta description, H1, canonical, indexability), the install status, the goals, the web vitals, the first search rows and IndexNow, with a checklist.

| Argument | Required | Description |
|---|---|---|
| `site` | yes | the site domain, for example example.com |
| `page` | yes | the path of the new page, for example /launch |
