# SpeedWorkers tools (Botify MCP)

Six read-only tools read a project's SpeedWorkers configuration, inventory and pages directly. They are the fastest way to answer "what is configured", "what does a bot get for this URL" and "what happened to this URL", without going through the interface or writing SQL. Every call needs `organization` and `project`, and the project must have SpeedWorkers enabled. For calling conventions (`main_task`, one item per call, `batch_run`, temporary storage URLs) see the `botify-mcp` skill.

| Question | Tool |
|---|---|
| What is configured in production, in the draft, or in version N? | `speedworkers_config` |
| When did a settings change go live, who pushed it, with which note? | `speedworkers_versions` |
| How many pages are indexed / stale / expired / not indexed, day by day? Can the refresh speed keep up? | `speedworkers_inventory` |
| What does a bot receive for this URL right now, and why is it (not) delivered? | `speedworkers_page_preview` |
| When was this URL cached, with which status code, and could it be served each time? | `speedworkers_page_history` |
| What exactly did SpeedWorkers store during one past indexation? | `speedworkers_page_snapshot` |
| Distributions, joins with crawl or logs, thousands of URLs | SQL on the tables, see `data-tables.md` |

All of them have a fixed cost per item, except `speedworkers_page_history`, which also pays for the data it scans in the indexation table.

Sections are the cache behaviors (and inventory optimization rules) of the data model. Match them across tools and tables by name: `section_name` in the inventory and the history, `metadata.section.name` in the preview, `sectionName` in the tables.

## `speedworkers_versions`

No input. Returns every settings version, most recent first: `version`, `date_released_on_production` (null when it was never pushed, which is the case of the current draft), `last_edited_by` and the push `comment`.

Use it to date a change before comparing metrics around it, and to get version numbers for `speedworkers_config`.

## `speedworkers_config`

Input: `version` = `"production"` (default), `"draft"`, or a version number from `speedworkers_versions`.

Returns two blocks:

- `website` — beam domain, delivery stop (the Control Center delivery toggle), interceptor checks (the service status tests).
- `version` — the full settings of that version: hosts, max speeds, cache refresh and refresh intervals; versioning metadata; **sections**, which hold both the cache behaviors and the inventory optimization rules, with their advanced refresh rules and Markdown rules; JS snippets with their code per rendering or delivery step, whether they are active, and the `sections_stable_ids` they are declared on (or applied to the whole site); rendering configs; inventory sources; Live Update feeds; quality controls.

The output is large. Read it for the part the question is about rather than restating it. To see what a push changed, call it for the two version numbers and compare the relevant blocks. The draft is not live: never describe draft settings as what bots receive.

## `speedworkers_inventory`

Inputs:

- `date_start` / `date_end` — inclusive ISO dates. Default: the last 30 days, up to today (UTC). At most 366 days.
- `dimensions` — breakdown on top of the date: `section_name` (default), `config_id`, `priority` (emergency, high, medium, low), `refresh_frequency` (minutes). `[]` returns one total per day. Each dimension multiplies the rows.
- `include_samples` — up to 5 sample URLs per state and row.

Returns:

- `inventory_stats` — one row per day and dimension combination, oldest first: `count`, `count_in_index`, `count_in_stale`, `count_expired`, `count_not_indexed` (and the `samples_*` lists with `include_samples`). These are the interface's Indexed, Outdated / Stale, Expired and Not Indexed states. A day without a snapshot has no row.
- `truncated` — true when the oldest days were dropped to stay within 2,000 rows. Shorten the period or drop a dimension.
- `crawl_stats` — today's rendering capacity: `max_speed` and `required_speed` (pages/s), `max_pages_refresh` and `required_pages_refresh` (pages/day), `pages_in_inventory`, `load` = required / max, `info` (`success`, or why it could not be computed), and the `load` of each section. Read `load` like Refresh Capacity: below 0.9 safe, 0.9–1 at risk, above 1 some pages will go stale or expire. The per-section loads show which cache behavior to slow down or shrink first. `crawl_stats` is null when the inventory has never been computed, which means the cache refresh is not enabled on the production version.

## `speedworkers_page_preview`

The tool version of the interface Preview: the page SpeedWorkers would deliver to a bot now.

Inputs: `url`; `device` = `desktop` (default), `mobile` or `na` (when the desktop or mobile variant is not cached, SpeedWorkers falls back to `na`); `output_format` = `html`, `markdown` (Markdown inside an HTML page) or `pure_markdown`, by default whatever the page's cache behavior delivers. Config: `save_to_tmp_storage`.

Returns `html` (as delivered, uncleaned), `status_code`, `headers` (credential-bearing values masked), `reason` when no HTML came back, and `metadata`:

- `delivered_from` — cache, fetch, liverender, section, ignoredparams, livefetch or nowhere.
- `not_delivered_reason` — `cached` when the page is delivered normally, otherwise one of the "not cached" reasons of `metrics-and-diagnostics.md`, in snake_case (`not_cached`, `quality_controls_failed`, `last_refresh_5xx`…).
- `last_index_time`, `refresh_time`, `redirect_url`, `output_format`.
- `original_processing_time_ms` (origin fetch and render) versus `beam_processing_time_ms` (SpeedWorkers delivery): the speed gain for that page.
- `section` (cache behavior), `quality_controls` (`status` 0 success, 1 warning, 2 fail, 3 error), `inputs` (inventory sources), `properties` (page properties), `js_error`.

There is no bot or user-agent input: the preview shows what the matching cache behavior delivers, so to know which AI bots get Markdown, read the behavior's Markdown rules in `speedworkers_config`.

With `save_to_tmp_storage: true` the HTML is replaced by `html_tmp_storage_url`. For more than a handful of URLs, run it through `batch_run` with that option, then search the stored HTML with `tmp_storage_grep`, `html_grep` or `html_code_executor`.

## `speedworkers_page_history`

Past indexations of URLs, newest first. It reads `catalog.speedworkers_indexed_pages`, so it covers a URL's write side; for what bots were served, query `speedworkers_served_pages`.

Inputs:

- `urls` — **all the URLs in one item**, up to 1,000. Each call scans the indexation table once however many URLs it lists, so one call with 200 URLs costs about the same as one call with 1. Do not fan this tool out per URL with `batch_run`.
- `days` — lookback, 15 by default, at most 90.
- `limit` — events per URL, 50 by default, at most 500.
- `resolve_url` — true by default. History is stored under the URL as SpeedWorkers resolves it (ignored parameters stripped…), so each URL is resolved first, with the requested URL as a fallback.

A call is refused when unique URLs × limit × (2 when `resolve_url`) exceeds 10,000. Lower `limit` or split the list.

Returns, per URL in the order given: `queried_url`, `resolved_url` (when it differs), `reason` (when no event was found), and `events`:

- `event_id` — opaque ID for `speedworkers_page_snapshot`. Pass it as is.
- `indexed_at`, `indexed` (stored in cache or not), `http_code` (from the origin), `device`, `page_size`, `section_id` / `section_name`.
- `ready_to_be_served` and `not_ready_to_be_served_reason` (`status_code`, `quality_controls`, `not_indexed`).
- `not_deliverable_reason` — `delivered`, `cached`, or a "not cached" reason in snake_case.
- `canonical_url`, `canonical_equal`, `next_refresh_time`, `expiration_time`, `ttl_minutes`, `time_to_fetch_ms`, `time_to_render_ms`.

## `speedworkers_page_snapshot`

Input: `event_id` from `speedworkers_page_history`. Config: `save_to_tmp_storage` (returns `html_tmp_storage_url` instead of `html`).

Returns what SpeedWorkers stored during that indexation: `html` (as received and rendered from the origin), the `url` fetched, `indexed_at`, `status_code` and `headers` from the origin (credentials masked), `canonical`, `js_rendering` (`device`, `rendered`, `status`, `redirect_url`, `version_code`, `rendering_time_ms`, `nb_resources` requested and `nb_resources_error` failed), and `job` (cache behavior, rendering config and its device, inventory source IDs, page properties at that time, the status codes known before the job).

Old events can be purged: the tool then answers that the content is no longer available. Use `speedworkers_page_preview` for the current page.

## Recipes

**A bot gets an empty, incomplete or wrong page.** `speedworkers_page_preview` on the URL, with the bot's device: read `not_delivered_reason`, `quality_controls`, `js_error`, then the HTML itself. If it is not delivered, `speedworkers_page_history` shows whether recent indexations failed and with which origin status code. To see when the content broke, take the `event_id` of the last good and first bad events and compare their `speedworkers_page_snapshot`s (`js_rendering.nb_resources_error` against `nb_resources` often explains a partial render). If the preview is fine but a bot still gets something else, the problem is upstream: routing at the CDN, see `architecture.md`.

**Metrics moved after a date.** `speedworkers_versions` to find a push near that date, `speedworkers_config` on that version and the previous one to see what changed, then `speedworkers_inventory` over a period spanning the push, broken down by `section_name`, to see which cache behaviors moved.

**Refresh capacity or stale pages.** `speedworkers_inventory` with the default `section_name` breakdown: `crawl_stats.load` and the per-section loads give where the capacity goes; rising `count_in_stale` or `count_expired` show the consequence. Add `include_samples` and preview a few sampled URLs to check what bots get. Remediation levers are in `metrics-and-diagnostics.md`.

**Check that a setting does what it should.** Read it in `speedworkers_config` (production, not draft), then preview a URL in its scope: `metadata.section` must be the expected cache behavior, `output_format` must be Markdown for an AI bot behavior with Markdown enabled, and the expected quality controls must appear.
