Generated from the live server registry — always in sync
with the deployed tools.
gsc_clicks(clients: list, start_date: str | None = None, end_date: str | None = None)
Total Search Console clicks per client for a date range.
`clients` (required) is a list of GSC property strings, e.g.
`["sc-domain:example.com", "https://foo.com/"]`. Each is queried
independently; a property that errors is reported with `clicks: null`
and an `error` message rather than failing the whole call.
Dates are ISO `YYYY-MM-DD`; omit them to default to the last 28 days.
Returns JSON {range, clients, total_clicks}.
| Parameter | Type | Description |
|---|
clients | list | |
start_date | str | None | |
end_date | str | None | |
gsc_index_status(url: str, site: str | None = None)
Is a URL already indexed by Google? Returns Google's URL Inspection result.
Inspects one page within a verified Search Console property and returns
Google's response **unmodified** (the raw `inspectionResult` envelope). Read
`inspectionResult.indexStatusResult.verdict` (`PASS` = on Google) and
`.coverageState` for the human detail (e.g. "Submitted and indexed" vs
"URL is unknown to Google"). The service account must be a verified user of
the property (else 403); a bad URL/site or auth error surfaces as an error,
not a captured field.
| Parameter | Type | Description |
|---|
url | str | absolute page URL to inspect, e.g. "https://example.com/". |
site | str | None | the GSC property the URL belongs to, e.g. "sc-domain:example.com" or "https://example.com/". Omit to default to the Domain property of the URL's host (`sc-domain:<host>`); pass it explicitly for a URL-prefix or subfolder property. |
gsc_indexing(urls: list | str)
Notify Google to (re)crawl one or more URLs via the Indexing API.
`urls` (required) is a single absolute URL or a list of them; bare hosts like
`example.com` are accepted and normalized to `https://example.com`. Each
usable URL is submitted independently — one that errors gets `ok: false` plus
an error message instead of failing the whole call. A blank or malformed
entry never reaches the API and is reported under `skipped`. The service
account must be an Owner of the property in Search Console (else a 403).
Returns JSON {type, submitted, succeeded, retries, results, skipped} where
`results` is one {url, ok, ...} per submitted URL and `skipped` is one
{url, reason} per unusable input.
| Parameter | Type | Description |
|---|
urls | list | str | |
gsc_verify_index(urls: list | str)
Check whether one or more URLs are indexed by Google (URL Inspection API).
`urls` (required) is a single absolute URL or a list of them. Each is
inspected and its verdict returned: `INDEXED`, `NOT_INDEXED`, or `FORBIDDEN`
(the service account isn't a verified user of the URL's Search Console
property). URLs are grouped by the `sc-domain:` property derived from their
host; a URL whose host can't be mapped to a property is reported under
`skipped` as `CONFIG_ERROR`.
The Inspection API allows ~2000 calls/day per property, so the run **stops as
soon as Google returns a quota/rate-limit (429)** — `stopped` is `"quota"` in
that case, else `null`. Returns JSON {inspected, indexed, succeeded,
forbidden, config_errors, stopped, rows, skipped} where `rows` is one
{url, status, indexed} per inspected URL.
| Parameter | Type | Description |
|---|
urls | list | str | |
notion_table_read(db_id: str | None = None, max_rows: int | None = None, filter: dict | None = None, sorts: list | None = None, find: dict | None = None)
Read the rows of a Notion database (the table you see in Notion).
Each row is flattened to plain values keyed by column name, with the Notion
page id and link added as `_id` and `_url`. Pagination is followed to the end
unless `max_rows` caps it.
| Parameter | Type | Description |
|---|
db_id | str | None | Notion database id to read. Omit to use the configured SEO database (NOTION_SEO_DB_ID). |
max_rows | int | None | optional cap on the number of rows returned. |
filter | dict | None | a Notion filter object so the server returns only matching rows instead of the whole table, e.g. {"property": "Status", "select": {"equals": "Requested"}}. |
sorts | list | None | a Notion sorts array to order the rows server-side, e.g. [{"property": "Created", "direction": "descending"}]. |
find | dict | None | a {"column": "value"} shorthand compiled to a server-side Notion filter (contains for text, equals for select/status/number/date), so you don't hand-build one, e.g. {"Website": "homeschooltutoring"}. |
runner_job_status(job_id: str)
Full status of one job: state, progress, result/error, and logs (JSON).
| Parameter | Type | Description |
|---|
job_id | str | |
runner_jobs(random_string: str | None = None)
List all jobs this process knows about (JSON, no per-job logs).
| Parameter | Type | Description |
|---|
random_string | str | None | unused placeholder so clients that can't call a zero-argument tool (e.g. Notion AI) have a property to send. |
runner_start(task: str = 'sample_api_query', count: int = 5, delay: float = 1.0, params: dict | None = None, webhook_url: str | None = None)
Start a background job and return its record (JSON, incl. `id`).
`task` picks what to run: `sample_api_query` (recursive — recurses one level
per "page", sleeping `delay` seconds to replicate an API round-trip) or
`sample_job` (a flat loop) — both driven by `count` (pages/steps) — or
`verify_and_index`, the real per-row crawl: for each due page it inspects the
URL and, only if it's not already indexed, submits it to the Indexing API,
writing each verdict back to Notion along with Google's reasoning from the
same inspection (`Coverage State`, `Google Canonical`, `User Canonical`,
`Last Crawl Time`), which costs no extra quota. Pass `params={"max_rows": N}`
to cap the crawl; omit for the whole due table. Or `sitemap_crawl`: for each
client with a sitemap, `Enable Sitemap Crawl` = Enable and `GSC Auth Access`
= Access, expand its `Sitemap XML URL` (recursing a
sitemap index into its child sitemaps) and copy each unique page URL into the
target table as a new row, skipping URLs already there and writing each
client's `Sitemap Crawl Status`/`Sitemap Last Crawl` back — pass
`params={"max_urls": N, "db_id": "<notion-db>"}` (omit `max_urls` to copy every
URL, omit `db_id` for the configured `NOTION_MANUAL_INDEX_DB_ID`). Or
`fetching_insights_analytics`: for each client with `GSC Auth Access` = Access,
query the Search Analytics API for its GSC property and write the distinct
`Queries` count, total `Clicks` and `Impressions` back to that client's row,
stamping `FIA LastUpdate` — pass `params={"max_clients": N, "start_date":
"YYYY-MM-DD", "end_date": "YYYY-MM-DD", "db_id": "<notion-db>"}` (omit
`max_clients` to process every accessible client, omit the dates for the
trailing 28 days, omit `db_id` for the configured `NOTION_WORKSPACES_DB_ID`). Or
`fetching_page_insights`, the per-page counterpart: for each due row of the
per-page table it resolves the row's URL to a Search Console property, fetches
that property's page metrics once for all its rows, and writes each page's own
`Clicks`, `Impressions`, `CTR` and `Average Position` back to its row, stamping
`FPI LastUpdate`. A row whose URL earned no impressions in the window is
recorded as zero clicks/impressions; a row already refreshed today is skipped.
Clients whose row doesn't claim Search Console access are skipped without
spending a call to be refused, and a property Google answers 403 for has that
recorded on its client row as `GSC Auth Access` = `Access Error`. Pass
`params={"max_rows": N, "start_date": "YYYY-MM-DD", "end_date": "YYYY-MM-DD",
"db_id": "<notion-db>", "verified_only": true}` (omit `max_rows` for every due
row, omit the dates for the trailing 28 days, omit `db_id` for the configured
`NOTION_MANUAL_INDEX_DB_ID`, and set `verified_only` false to sweep every
registered client, which is how access granted since the last run is picked up). If `webhook_url` is set, the runner POSTs the
final job state there when it finishes. Poll `runner_job_status` for live
`stats`. `verify_and_index`, `sitemap_crawl`, `fetching_insights_analytics` and
`fetching_page_insights` are singletons: starting one while it's already
running returns an `already_running` error with the running job's id.
| Parameter | Type | Description |
|---|
task | str | |
count | int | |
delay | float | |
params | dict | None | |
webhook_url | str | None | |
runner_status(random_string: str | None = None)
Is the runner busy? Returns JSON {state: IDLE|RUNNING, jobs, counts}.
| Parameter | Type | Description |
|---|
random_string | str | None | unused placeholder so clients that can't call a zero-argument tool (e.g. Notion AI) have a property to send. |
runner_stop(job_id: str)
Request a stop (cooperative); the job moves to STOPPED at its next check.
| Parameter | Type | Description |
|---|
job_id | str | |
runner_stop_all(random_string: str | None = None)
Request a stop for every running/pending job (cooperative); each moves to STOPPED at its next check. Returns JSON {stopped, ids}.
| Parameter | Type | Description |
|---|
random_string | str | None | unused placeholder so clients that can't call a zero-argument tool (e.g. Notion AI) have a property to send. |
sitemap_client_urls(client: str, max_urls: int | None = None, check_existing: bool = False, db_id: str | None = None)
Preview the page URLs in one client's sitemap — the dry run of the crawl.
Read-only: resolves the client's row in the Workspaces DB, expands its
"Sitemap XML URL" (following a sitemap index into its child sitemaps) and
returns the page URLs. Nothing is inspected in Google and no Notion row is
created — use this to check a client's URL list before running the
sitemap_crawl job, which processes every enabled client and writes rows.
| Parameter | Type | Description |
|---|
client | str | the client/workspace name, matched as a case-insensitive substring of the clients table's title column, so a short handle is enough. |
max_urls | int | None | cap on the URLs returned; `truncated` reports whether the cap cut the list short. |
check_existing | bool | also read the target table and split the URLs into already-present vs new (slower — it reads the whole table). |
db_id | str | None | target table to check against when check_existing is true. Omit for the configured Manual Indexing table. |