# Cronitor MCP Server

The Cronitor MCP server lets compatible clients create and manage resources in your Cronitor account. Connect once, then use the client's normal conversation interface to inspect monitors, configure checks, manage alerting, and work with the rest of the Cronitor API.

**Server URL:** `https://cronitor.io/mcp`

If you are an agent working from a human request, start with the
[agent quick start](https://cronitor.io/docs/agent-quickstart.md). This page is the
connection and tool reference it links to.

The server uses Streamable HTTP with JSON responses. OAuth is recommended for interactive clients: you sign in with your normal Cronitor
login in a browser and the client stores the resulting token. Cronitor's existing
SDK Integration API key is the manual/headless fallback for clients that can safely
supply a Bearer token.

## Protocol support

* Streamable HTTP with JSON responses at `https://cronitor.io/mcp`.
* The `2026-07-28` stateless revision and the `2025-03-26`, `2025-06-18`, and `2025-11-25` revisions with their `initialize` lifecycle.
* No sessions: the server never issues `Mcp-Session-Id`.

Clients using an MCP SDK do not need to care about any of this; connect and use the tools. Header and caching details are in [Protocol details](#protocol-details) at the end of this page.

## Safety and data boundaries

Use the Cronitor MCP server as a control plane for inspecting and managing Cronitor resources. It does not replace runtime instrumentation: jobs and heartbeats must send telemetry from the workload's real execution path through CronitorCLI, an SDK, or the Telemetry API.

Start with read-only summary tools. Event messages, captured command output, uptime request headers or bodies, and private status-page configuration can contain customer data or secrets, so request expanded fields only when they are needed and the human has approved that access.

Monitor creation may return a telemetry or ping URL. Prefer CronitorCLI or a Cronitor SDK to report telemetry; calling that URL directly from the job is a fine fallback. The URL contains a telemetry-only key and is not a secret. CronitorCLI and the SDKs read `CRONITOR_API_KEY`, which must be the SDK Integration key.

Ordinary `list_status_pages` and `get_status_page` MCP results omit the generated `auth_secret` used to access a private page's RSS feed. Configure private RSS access through the Cronitor dashboard; do not ask a user to paste that token or a status-page password into the conversation.

MCP results omit monitor request headers, cookies, and bodies, and replace credential-bearing monitor request URLs with `[REDACTED URL]`, including in YAML exports. Other resources' saved URLs are not rewritten. A redacted export is for inspection, not a backup of request credentials. Browser authorization links and the connection-session tokens needed to complete integrations remain available.

`update_monitor` preserves omitted nested `request` fields. An unchanged `[REDACTED URL]` marker preserves the existing credential-bearing URL; edited markers are rejected, never saved. To change or remove URL credentials, supply a complete replacement URL. Explicit `headers: {}` and `cookies: {}` clear those maps, and `body: ""` clears the body; provided header/cookie maps replace rather than merge. Changing the URL's origin (scheme, host, or port) requires explicitly replacing or clearing any populated headers, cookies, and body so hidden credentials cannot transfer to another endpoint. `setup_monitor` preserves omitted request fields when updating an existing check, but requires a complete URL; use `update_monitor` for origin changes requiring cookie replacement. `create_monitors` retains the API's replacement/upsert contract: supply complete request configuration, never a redacted read result. Both creation tools reject URL redaction markers. Error redaction recognizes credential fields without treating ordinary JSON body values as secrets.

## Connect your MCP client

OAuth is recommended for interactive clients. The client discovers Cronitor's authorization server from the protected-resource metadata published at `https://cronitor.io/.well-known/oauth-protected-resource/mcp`, opens a browser for sign-in and consent, and stores the resulting token. Sign-in is Cronitor's ordinary login page: an existing account can use its password, a linked Google or GitHub identity, or organization-required SAML. If you do not have a Cronitor account, create one in that same browser step with Google, GitHub, or email and password; authorization then continues. MCP tokens stay scoped to the Cronitor account you signed in with; CLI machine credentials stay on the CLI audience.

A token follows the Cronitor account you signed in with and its current organization. There is no organization picker during authorization; switch your current organization in Cronitor and reconnect if you need a different one.

Once connected, install the Cronitor skill with `npx skills add cronitorio/agent-skills` so your agent knows how to audit, add, and investigate monitoring, or point it at the [agent quick start](https://cronitor.io/docs/agent-quickstart.md).

### Claude

In Claude on the web or Claude Desktop:

1. Open **Customize → Connectors**.
2. Select **Add custom connector** and enter `https://cronitor.io/mcp`.
3. Select **Connect**, sign in to Cronitor, and approve access.
4. Enable the Cronitor connector for the conversation where you want to use it.

On Team and Enterprise plans, an Owner must first add the URL under **Organization settings → Connectors**. Each member then connects their own Cronitor account. See [Claude's custom connector instructions](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) for the client controls.

### ChatGPT

ChatGPT accepts custom MCP servers on Plus, Pro, Business, Enterprise, and Education plans on the web. Turn on **Developer mode** under **Settings → Security and login**, then open [chatgpt.com/plugins](https://chatgpt.com/plugins), select **+**, enter `https://cronitor.io/mcp`, and sign in when prompted. See [OpenAI's developer mode guide](https://developers.openai.com/api/docs/guides/developer-mode).

### Claude Code

Install the Cronitor plugin. It adds this MCP server and the [Cronitor skill](https://cronitor.io/docs/agent-quickstart.md#install-the-cronitor-skill) together:

```bash
claude plugin marketplace add cronitorio/agent-skills
claude plugin install cronitor@cronitor
```

Then run `/mcp`, select `cronitor`, and sign in in your browser. To add only the MCP server, without the skill, add it as a user-scoped remote HTTP server:

```bash
claude mcp add --transport http --scope user cronitor https://cronitor.io/mcp
```

Start Claude Code, run `/mcp`, select `cronitor`, and complete the OAuth flow in your browser. Use `claude mcp get cronitor` to inspect the saved connection. See [Claude Code's MCP documentation](https://code.claude.com/docs/en/mcp) for project and organization configuration options.

### Codex

Install the Cronitor plugin. It adds this MCP server and the [Cronitor skill](https://cronitor.io/docs/agent-quickstart.md#install-the-cronitor-skill) together, then signs in:

```bash
codex plugin marketplace add cronitorio/agent-skills
codex plugin add cronitor@cronitor
codex mcp login cronitor
```

To add only the MCP server, without the skill, add this entry to `~/.codex/config.toml`:

```toml
[mcp_servers.cronitor]
url = "https://cronitor.io/mcp"
```

Then run `codex mcp login cronitor` and complete authorization in your browser. The
Codex CLI and IDE extension share this configuration. In an interactive Codex
session, use `/mcp` to verify that Cronitor is connected. See the
[Codex MCP documentation](https://learn.chatgpt.com/docs/extend/mcp).

### Cursor

Open **Customize → MCP**, add a remote Streamable HTTP server named `cronitor`, and
enter `https://cronitor.io/mcp`. Choose OAuth when prompted and complete sign-in in
your browser.

### VS Code/GitHub Copilot

Run **MCP: Add Server**, choose HTTP, enter `https://cronitor.io/mcp`, and save it
to your user profile. Start the server from **MCP: List Servers** and complete
OAuth in your browser.

### MCP Inspector

MCP Inspector works with OAuth for protocol debugging: select Streamable HTTP, enter `https://cronitor.io/mcp`, and choose OAuth.

### Other Streamable HTTP clients

Create a remote or Streamable HTTP connection named `cronitor` with URL `https://cronitor.io/mcp`, then choose OAuth when the client prompts for authentication. Cronitor publishes protected-resource metadata that points at its authorization server, which supports authorization-code with PKCE and identifies clients by Client ID Metadata Document (CIMD). Dynamic client registration is not enabled. Other standards-compliant clients are supported on a best-effort basis.

## Authentication and permissions

An OAuth token identifies the Cronitor user who signed in. It does not carry a
scope selection: every MCP request receives exactly the permissions allowed by
that user's current Cronitor role, capped at the same permission bundle as the
SDK Integration key. Read-only users can only call read tools. Users whose role
allows editing can also create, update, pause, resolve, and delete resources.
OAuth never grants API key, team, billing, or unrelated administration
permissions, and it does not add a ping tool to MCP; instrument workloads with a
Cronitor SDK, CronitorCLI, or a direct ping URL.

Cronitor re-checks the user's account, organization membership, role, and the
organization's MCP feature access on every request. Changing a user's role,
removing them from the organization, or disabling MCP for the organization takes
effect immediately, without waiting for the OAuth token to expire. To disconnect
a client, remove the Cronitor server from that client; a new browser
authorization is required to reconnect. API keys remain on the API Settings page.

### Use the existing SDK Integration key

API-key authentication is the manual/headless fallback for automation and for clients that can send a private Bearer header. Copy the organization's existing **SDK Integration** key from [API Settings](https://cronitor.io/app/settings/api). It is an organization-wide, long-lived credential and may already power Cronitor SDK or CronitorCLI installations. Rotating it can interrupt every installation that still has the previous value, so update those consumers together. There is no dedicated MCP key type or MCP-key UI.

Send the key as an HTTP Bearer token. Keep it in an environment variable or the client's own secret input, never in a checked-in configuration file, a command-line argument, or shell history. For a temporary Bash or Zsh session, this non-echoing prompt keeps the value out of the command and out of history:

```bash
printf 'Paste the SDK Integration key: ' >&2
IFS= read -r -s CRONITOR_API_KEY
printf '\n' >&2
export CRONITOR_API_KEY
```

Start a command-line client from that shell so it inherits the variable, and run `unset CRONITOR_API_KEY` when you are done. GUI apps do not inherit a terminal's environment; use the client's secret input where it has one. Hosted clients such as Claude.ai, Claude Desktop, and ChatGPT do not accept a private header; use OAuth with those.

#### Claude Code

With `CRONITOR_API_KEY` set, copy this command exactly. The single-quoted
JSON saves the environment-variable placeholder, not the secret value, in the
user-scoped configuration:

```bash
claude mcp add-json --scope user cronitor \
  '{"type":"http","url":"https://cronitor.io/mcp","headers":{"Authorization":"Bearer ${CRONITOR_API_KEY}"}}'
claude mcp get cronitor
```

Start `claude` from the same injected environment and use `/mcp` to verify the
connection.

#### Codex

With `CRONITOR_API_KEY` set, add this secret-free entry to `~/.codex/config.toml`:

```toml
[mcp_servers.cronitor]
url = "https://cronitor.io/mcp"
bearer_token_env_var = "CRONITOR_API_KEY"
```

Start `codex` from the same shell and use `codex mcp list` or `/mcp` to verify the
connection. The IDE extension shares the configuration but must be launched with
the variable in its environment.

#### Cursor

With `CRONITOR_API_KEY` set, add this secret-free entry to the global `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "cronitor": {
      "url": "https://cronitor.io/mcp",
      "headers": {
        "Authorization": "Bearer ${env:CRONITOR_API_KEY}"
      }
    }
  }
}
```

Quit Cursor, then run `cursor .` from the same shell so it starts with the variable.
Open **Customize → MCP** and confirm that `cronitor` connects.

#### VS Code/GitHub Copilot

Run **MCP: Open User Configuration** and use this configuration. VS Code prompts
for the key with hidden input and stores it securely; do not put this in the
workspace's `.vscode/mcp.json`. Interactive inputs are local-extension-host only
and are not forwarded to GitHub Copilot Agent Host sessions.

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "cronitor-sdk-key",
      "description": "Cronitor SDK Integration key",
      "password": true
    }
  ],
  "servers": {
    "cronitor": {
      "type": "http",
      "url": "https://cronitor.io/mcp",
      "headers": {
        "Authorization": "Bearer ${input:cronitor-sdk-key}"
      }
    }
  }
}
```

Run **MCP: List Servers**, start `cronitor`, and paste the key only into the masked
prompt.

## Control plane, not data plane

Use MCP to configure and inspect monitoring. Send telemetry directly from the monitored workload to its Cronitor ping URL.

For a heartbeat, the durable sequence is:

1. Call `setup_monitor` with a stable key and schedule.
2. Have the monitored workload report to the monitor, preferably through a
   Cronitor SDK or CronitorCLI, or by calling the returned `ping_url` directly.
   Do not paste the URL into the conversation or commit it to source control.
3. Let that workload report only after each useful-work checkpoint.
4. Use MCP later to inspect status or change monitor configuration.

For a bounded job, use the same integration choices but report its lifecycle,
not merely one successful request. CronitorCLI and the SDKs report start,
completion, failure, duration, and exit status. A direct HTTP integration must
send `state=run` followed by `state=complete` or `state=fail`.

Never send telemetry through the MCP server. If the client or its MCP connection
stops, Cronitor must still hold the deadline and detect the missing direct ping.
`setup_monitor` automatically adds the `via:mcp` tag.

Checks are different: Cronitor probes the target, so `setup_monitor` returns a `monitor_url` and probe instructions instead of a ping URL.

## Tool reference

The server exposes four task-oriented tools, three discovery tools, and 64 explicit resource operations covering the complete CronitorCLI resource command tree plus read-only metrics: 71 tools in total. Each published input schema declares `additionalProperties: false`; send only fields present in that tool's schema.

### `setup_monitor`

Create or update a monitor by stable key. Repeating the same call updates the existing monitor instead of creating a duplicate. Requires a role that can edit monitors with OAuth, or `monitor:write` with an API key.

Common input fields:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | yes | `heartbeat`, `job`, or `check` |
| `key` | string | yes | Stable monitor key used for idempotent upserts |
| `schedule` | string | heartbeat/job: yes; check: no | Cron expression or interval such as `every 30 minutes` |
| `timezone` | string | no | IANA timezone (for example `America/New_York`) in which a cron expression is evaluated; defaults to the account's timezone |
| `tags` | array of strings | no | Additional tags; `via:mcp` is always added |
| `notify` | array of strings | no | Notification-list keys or prefixed destinations such as `email:alerts@example.com` |

For `type=heartbeat` and `type=job`, this additional field is accepted:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `grace_seconds` | integer | no | Extra time allowed after the expected schedule |

Every response returns the monitor `key` and `type`. Over the plan's monitor limit, the monitor is saved but disabled, and the response adds `monitor_quota`, as described for `create_monitors`. Heartbeat and job responses
also return `ping_url`, `monitor_url`, and `instructions`. Have the workload report to the monitor with CronitorCLI or an
SDK, or by calling `ping_url` directly. The URL contains a telemetry-only key
and is not a secret. The workload-specific instruction
distinguishes a heartbeat checkpoint from a job lifecycle and does not repeat
the URL. `setup_monitor` creates one monitor from a human description; use
`create_monitors` for bulk creation or an exact monitor shape.

For `type=check`, these additional fields are accepted:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `url` | string | yes | Public HTTP(S) URL or host and port for a port check |
| `method` | string | no | HTTP method; the `port` preset selects TCP |
| `headers` | object of strings | no | Request headers sent by Cronitor probes |
| `body` | string | no | Request body |
| `assertions` | array of strings | no | Cronitor response assertions; plain HTTP checks default to `response.code < 400` |
| `preset` | string | no | `port` for a TCP port check |

The `port` preset creates a TCP probe. Converting an existing HTTP check to this preset clears its HTTP-only headers, cookies, and body while preserving its timeout and regions. Changing an HTTP check to another HTTP origin still requires explicitly replacing or clearing hidden credentials. New checks use Cronitor's default regions when regions are omitted.

To monitor an MCP server, create a plain HTTP check that POSTs a `tools/list` request using the `url`, `method`, `headers`, `body`, and `assertions` fields. See [Monitor an MCP Server](https://cronitor.io/guides/monitor-mcp-servers.md) for the exact request shape, modern and legacy probe recipes, protected-server options, and an end-to-end alert test plan.

Check responses also return `monitor_url` and `instructions`; they do not contain `ping_url` because Cronitor performs the probe.

### `get_status`

Return an account rollup or the current status of one monitor. Requires any role with OAuth, or `monitor:read` with an API key.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | no | Monitor key; omit for the account rollup |
| `env` | string | no | Environment key or name; defaults to the organization's default environment |

With no key, the result is:

```text
{total, healthy, failing, paused, failing_monitors}
```

`failing_monitors` contains at most 25 objects with `key`, `name`, `type`, `state`, and `latest_event_at`.

When the account has no monitors, the result also contains `message` with `No monitors yet. Use setup_monitor to create one.`

With a key, the result returns `key`, `name`, `type`, `state`, `latest_event`, `passing_rules`, and `failing_rules`, plus `schedule` for jobs and heartbeats or `interval_seconds` for checks. `latest_event` is `null` or contains `event`, `at`, and optional `metrics` and `message` fields.

### `get_setup_context`

Return what an agent needs before it sets up monitoring. Takes no arguments. Requires any role with OAuth, or `monitor:read` with an API key.

The result contains:

- `organization`: the organization `name`.
- `plan`: `code`, `is_free`, and `limits` with `monitors` (`null` on metered plans), `check_min_interval_seconds`, `integrations` (services the plan allows), `sms`, and `team_seats`.
- `default_environment`, `default_notification_list`, and `default_timezone` (the account's timezone, used for cron schedules without one).
- `telemetry_url_base`: the base of every telemetry URL. It contains a telemetry-only key and is not a secret.
- `sdk_key`: the environment variable (`CRONITOR_API_KEY`), key name (SDK Integration), and the settings page where the human finds it. The tool never returns a secret key.
- `check_regions`: region keys and names allowed by this account's check validator, including its static-IP configuration, and `discovery_tools`: the tools to call for assertions, RUM queries, issue assignees, and integration services.

### Discovery tools

`describe_monitor_assertions({"type":"check"})` returns the parser's selectors, operators, value syntax, and working examples. Omit `type` to include jobs, heartbeats, sites, and custom-metric assertions. For a JSON health endpoint, use `response.code = 200` and `response.json status = ok`. JSON paths compare the first match, not every array item. Site assertions require a time window such as `site.sessions > 0 over 1 hour`.

`describe_site_query({})` returns metric definitions and units, compatible query shapes, dimensions, filter operators, allowed buckets, defaults, and an example for seven complete days in the account timezone. `session_count` counts unique session IDs. Named time ranges can include the current partial day; query timezone defaults to UTC. Custom ranges expand to calendar bucket boundaries. `query_site` accepts `env` by key or name; omitted `env` uses the account's default environment. Use `filters` for analytics. `search_options` requires `search` and at least one search field in `metrics`, returning up to three suggestions per field.

`list_issue_assignees({"search":"Alex Smith","page_size":25})` finds active members of the current organization by name or email. It returns only email, name, and assignability, with pagination. Use the email for issue assignment. Assertion discovery needs `monitor:read`, RUM discovery needs `site:read`, and assignee discovery needs `issue:write`, even though the lookup does not mutate anything. Read-only API keys and read-only OAuth users cannot enumerate the member directory.

### Compact and structured results

Use `list_monitors({"detail":"summary"})` and `list_issues({"detail":"summary"})` for inventory and triage. Summary mode defaults to 25 rows, allows at most 100, and returns `next_page`. Monitor summaries retain identity, passing/running flags, schedules, tags, and environment membership while omitting request configuration, notes, and invocation output. Issue summaries omit update history. Relationship arrays are capped at 25 entries, with `truncated_fields` and `relationship_counts` when shortened. Expanded event or relationship flags cannot be combined with summary mode; use the corresponding detail tool instead.

Omitting `detail`, or selecting `detail=full`, preserves the existing JSON-text-only response without duplicating the full payload. The two list tools add `structuredContent` only for `detail=summary`, alongside the same bounded JSON text; they do not advertise an unconditional `outputSchema`. `get_setup_context` and the three discovery tools advertise `outputSchema` and return `structuredContent` alongside matching JSON text. Redaction happens before either representation is produced. Summary output is partial configuration and must not be used to replace an entire resource.

### `list_failing_monitors`

List monitors currently failing in one environment. Requires any role with OAuth, or `monitor:read` with an API key.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `env` | string | no | Environment key or name; defaults to the organization's default environment |

The result contains `failing_monitors`. Each item returns `key`, `name`, `type`, `tags`, `failing_since`, and `latest_alert_reason`. When nothing is failing, the result also includes a friendly `message` distinguishing an empty account from an account where all monitors are healthy.

### `pause_monitor`

Pause alerting for a monitor. Repeating the call is safe. Requires a role that can edit monitors with OAuth, or `monitor:write` with an API key.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | yes | Monitor key |
| `hours` | integer | no | Number of hours to suppress alerts, at least 1; omit for an indefinite pause |

### `unpause_monitor`

Resume alerting for a monitor. Unpausing a monitor that is not paused is a safe no-op. Requires a role that can edit monitors with OAuth, or `monitor:write` with an API key.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | yes | Monitor key |

### Resource tools

Resource tools mirror the resource and subcommand layout of CronitorCLI. Tool names identify both the resource and operation; there is no generic `resource` selector or untyped `data` object to reconstruct. Every call traverses the same DRF view, permission classes, filters, serializers, validation, pagination, and soft-delete behavior as the REST API and CLI.

The `tools/list` response is the field-level reference. Each tool schema identifies accepted fields, required combinations, enums, expansions, replacement semantics, and safety metadata.

Nested arguments are validated against these schemas before dispatch. `setup_monitor` accepts singular `schedule`; the advanced `create_monitors` and `update_monitor` tools use `schedules`, an array. `list_monitors` accepts `passing` and applies all requested tags before pagination.

Bulk monitor deletion validates every requested key before deleting anything: a missing or ambiguous key returns an error with zero deletions. Repeating a timed monitor or group pause moves its deadline. Repeating an issue update without update keys can append duplicate history; supplying `updates` replaces the complete history and deletes omitted updates. Deleting a site also deletes its site monitors, and deleting a group removes status-page components backed by that group.

| CLI resource | MCP tools | API-key scope |
|--------------|-----------|---------------|
| Monitor | `list_monitors`, `search_monitors`, `get_monitor`, `create_monitors`, `update_monitor`, `delete_monitors`, `export_monitors`, `clone_monitor`, `pause_monitor`, `unpause_monitor` | `monitor:*` |
| Status page | `list_status_pages`, `get_status_page`, `create_status_page`, `update_status_page`, `delete_status_page` | `statuspage:*` |
| Status-page component | `list_status_page_components`, `create_status_page_component`, `update_status_page_component`, `delete_status_page_component` | `statuspage:*` |
| Issue | `list_issues`, `get_issue`, `create_issue`, `update_issue`, `resolve_issue`, `delete_issue`, `bulk_update_issues` | `issue:*` |
| Notification list | `list_notification_lists`, `get_notification_list`, `create_notification_list`, `update_notification_list`, `delete_notification_list` | `monitor:*` |
| Monitor group | `list_groups`, `get_group`, `create_group`, `update_group`, `delete_group`, `pause_group`, `resume_group` | `monitor:*` |
| Environment | `list_environments`, `get_environment`, `create_environment`, `update_environment`, `delete_environment` | `monitor:*` |
| Maintenance window | `list_maintenance_windows`, `get_maintenance_window`, `create_maintenance_window`, `update_maintenance_window`, `delete_maintenance_window` | `issue:*` |
| RUM site | `list_sites`, `get_site` (pass `with_snippet: true` for the tracking snippet), `create_site` (returns the snippet), `update_site`, `delete_site`, `query_site`, `list_site_errors`, `get_site_error` | `site:*` |
| Integration | `list_integrations`, `get_integration_services`, `create_integration`, `connect_integration`, `check_integration_connection`, `delete_integration` | `integration:*` |
| Metrics | `get_metrics`, `get_aggregates` | `monitor:*` |

This is 64 explicit resource operations: 62 CLI resource operations plus the two read-only metrics tools. CLI-only presentation flags such as output format, output file, or reading a local YAML file are represented by structured MCP inputs and results rather than separate tools.

Local runtime commands such as `exec`, `ping`, `list`, and `sync` operate on the machine running the CLI and are not emulated by the server. The CLI's `metric get` and `metric aggregate` queries map to the metrics tools described below.

With OAuth, read operations are available to every role; create, update, delete,
clone, pause/resume, resolve, and bulk operations require a role that can edit
that resource family. API keys retain the resource family's granular read or
write scope shown above.

`create_monitors` accepts a non-empty `monitors` array. A single item uses the REST create route; multiple items use the existing bulk upsert route. As with the REST API, monitors over the plan's monitor limit are saved but disabled. When that happens, the result is `{monitors, monitor_quota}`, where `monitor_quota` has `disabled_monitors`, a `message`, and an `upgrade_url`. `export_monitors` walks every matching API page and returns one Cronitor YAML document.

CLI query capabilities are preserved as typed fields rather than command-line flags. For example, `list_monitors` supports `with_events` and `with_invocations`, `list_groups` supports `page_size`, and maintenance-window reads use `with_monitors` to match the CLI's `--with-monitors` expansion.

Relationship arrays use the underlying DRF contract. In particular, `update_issue.statuspages`, `update_issue.affected_components`, and `update_group.monitors` are complete replacement lists: omit a field to preserve it and pass `[]` to clear it. Issue `updates` is a synchronized history collection; fetch the issue before changing it because omitted update keys are deleted.

### Metrics

`get_metrics` returns time-series metrics for monitors selected by `monitors`, `groups`, `tags`, or `types`, over a named `time` range (`24h` through `365d`, `today`, or `yesterday`) or explicit `start` and `end` unix timestamps, optionally filtered by `env` and `regions`. Pass exactly one of `fields` (built-in rollups such as `duration_p90`, `fail_count`, `success_rate`, or `run_count`), `metric` (one custom name, returning `avg` and `count` per bucket), or `metric_names` (list known custom names). Without `time`, `start`, or `end`, the range defaults to the last hour. `with_nulls` includes empty data points; custom metric gaps are nulls, never zeros. It mirrors `cronitor metric get` and the [Metrics API](https://cronitor.io/docs/metrics-api.md) and requires `monitor:read`. See [Custom Metrics](https://cronitor.io/docs/custom-metrics.md).

`get_aggregates` takes the same selectors and time range without `fields` and returns per-monitor, per-environment (per-region for checks) totals: `run_count`, `complete_count`, `fail_count`, `tick_count`, `alert_count`, `duration_mean`, `downtime_seconds`, `uptime`, and a derived `success_rate`. Use it for questions such as "how many times did this job fail last week"; it mirrors `cronitor metric aggregate` and requires `monitor:read`.

## Integrations

These six tools manage notification destinations (alert integrations). They do not return Slack webhook URLs, PagerDuty keys, or `api_key` values.

**`list_integrations`** (read-only)
**`get_integration_services`** (read-only)
**`create_integration`** (write)
**`connect_integration`** (write)
**`check_integration_connection`** (read-only)
**`delete_integration`** (write)

`list_integrations` accepts optional `service`, `label`, `page`, and `page_size`. `create_integration` requires `service`, `name`, and `fields` (Slack and PagerDuty return `connect_required`). `connect_integration` starts a Slack or PagerDuty browser session — hand `authorize_url` to the human. `check_integration_connection` polls by `token`. `create_integration` and `connect_integration` accept `add_to`, a list of notification-list keys such as `["default"]`; the new destination joins each list and the list keeps its other destinations. For Slack and PagerDuty this happens when the human finishes in the browser, even if the agent stops polling, and the completed check reports `added_to`. `add_to` also needs `monitor:write`. `delete_integration` soft-deletes by `service` and `label`; in-use destinations error unless `force` is true.

Agents must never echo integration secrets. Tools do not return Slack webhook URLs, PagerDuty keys, or `api_key` values.

## Rate limits and errors

Permission, rate-limit, and request-shape failures are returned as JSON-RPC errors. The codes differ by protocol generation because the `2026-07-28` revision reserves the range the legacy codes used:

| Meaning | `2026-07-28` clients | Legacy clients | `error.data` fields |
|---------|----------------------|----------------|---------------------|
| Insufficient permission: the token or role does not allow the tool | `-31001` | `-32001` | `required_scope` |
| Rate limit exceeded for this account | `-31002` | `-32002` | `rate_limit` (`read` or `write`), `limit`, `window_seconds`, `retry_after_seconds` |
| `MCP-Protocol-Version`, `Mcp-Method`, or `Mcp-Name` header does not match the request body (HTTP 400) | `-32020` | n/a | |
| Unsupported protocol version (HTTP 400) | `-32022` | `-32022` | |

Tool calls are rate limited per account, with separate limits for read tools and write tools over a fixed time window. The `error.data` fields report which limit was hit and the window size. On a rate-limit error, wait `retry_after_seconds` before retrying instead of retrying immediately: the counter resets at the window boundary, so earlier retries fail with the same error.

Plan-limit violations are different: they are not JSON-RPC errors. They come back as a tool result with `isError` set, whose payload has the shape described below.

## Plan-limit errors

`setup_monitor` does not silently weaken a requested check or retry indefinitely when the account's plan cannot accept it. Monitor-quota and check-frequency violations return a structured error with exactly these fields:

| Field | Description |
|-------|-------------|
| `code` | Stable machine-readable limit code |
| `message` | Explanation suitable for showing to the user |
| `upgrade_url` | Cronitor URL where the user can review plan options |

A client should stop retrying the unchanged call, show the `message` and `upgrade_url` to its user, and wait for either the account limit or the requested configuration to change.

## Which MCP server?

Cronitor has two MCP servers with different jobs. They complement each other and can be connected at the same time.

| Server | Where it runs | Use it when you need to |
|--------|---------------|-------------------------|
| **Local CronitorCLI MCP server** | On the machine where CronitorCLI runs | Discover and edit crontabs on this server, then sync those local jobs with Cronitor |
| **Cronitor MCP server** | At `cronitor.io/mcp` | Manage Cronitor API resources from any compatible client: monitors, status pages and components, issues, notification lists, groups, and environments |

The local server can see the machine's crontab; the hosted server cannot. The hosted server can manage account-level monitoring without shell access to the monitored host. See [Using CronitorCLI](https://cronitor.io/docs/using-cronitor-cli.md) for CronitorCLI installation, or the [Cronitor documentation](https://cronitor.io/docs.md) for the complete monitoring model.

## Protocol details

For client and SDK authors. Under the `2026-07-28` revision every request is self-contained: there is no `initialize` handshake. Send `tools/list` or `tools/call` directly, carry `io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities` in each request's `_meta`, and include the `MCP-Protocol-Version` and `Mcp-Method` headers, plus `Mcp-Name` on `tools/call`. The server validates that the headers match the request body and rejects a mismatch with error `-32020`. `server/discover` returns the supported versions, capabilities, and instructions. `tools/list` and `server/discover` responses include `ttlMs` and `cacheScope` hints so clients can cache tool schemas instead of re-fetching them every conversation. Legacy revisions follow the `initialize`/`initialized` lifecycle exactly as those revisions specify; JSON-RPC batches are accepted only under `2025-03-26`.
