--- category: Reference title: MCP Server description: Connect openstatus to AI assistants via the Model Context Protocol --- The openstatus MCP server lets AI assistants (Claude Desktop, Cursor, and other [Model Context Protocol](https://modelcontextprotocol.io) clients) read and manage your status pages, status reports, and maintenance windows directly from a conversation. ## Endpoint ``` https://api.openstatus.dev/mcp ``` The transport is **Streamable HTTP** (stateless). The server speaks JSON-RPC 2.0 over a single endpoint that accepts `GET`, `POST`, and `DELETE`. ## Authentication Every request must include your openstatus API key in the `x-openstatus-key` header — the same key REST and ConnectRPC use. There is no separate MCP-only credential. ```http x-openstatus-key: os_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` Get a key from the dashboard's **Settings → General → API Keys**. ### Scopes Each API key carries a scope that controls what tools the MCP server exposes: - **Read-only** — the server only registers read tools (`list_*` and `get_*`). Mutation tools (`create_*`, `update_*`, `add_*`, `resolve_*`) are not advertised in `tools/list` and cannot be called. - **Read & write** — every tool is available. Scope is set at key creation time and is **immutable**. To change a key's scope, revoke it and issue a new one. For AI agents that should only observe state — health summaries, paging on-call, drafting communication — issue a **Read-only** key. The server gates writes at two layers (the `tools/list` filter is the visible UX; the service-layer scope check is the boundary), so a misconfigured client can't accidentally mutate. ## Tools The server exposes 19 tools (17 on plans without the `audit-log` feature), grouped by resource and scoped to the workspace tied to your API key. Mutations write to the audit log with `actor_type = "mcp"` (see [Audit log](#audit-log)). ### Pages | Tool | Type | Purpose | |------------------------|------|---------| | `list_status_pages` | read | List public status pages with their slug and id. Used to discover the `pageId` required by mutation tools. | | `list_page_components` | read | List components on a status page with their id, name, type (`monitor` / `static`), and linked monitor. Used to discover the `pageComponentIds` accepted by `create_status_report`, `update_status_report`, and `create_maintenance`. | ### Status reports | Tool | Type | Purpose | |----------------------------|----------|---------| | `list_status_reports` | read | List status reports newest-first. `filter: "active" \| "all"` (defaults to active = excludes resolved). Paginated via `page` (1-indexed) and `perPage`; response carries a `pagination` object with `page`, `perPage`, `totalSize`, and `totalPages`. | | `create_status_report` | mutation | Create a new status report on a status page with an initial public update. | | `add_status_report_update` | mutation | Append a public update to an existing status report and bump its status. | | `update_status_report` | mutation | Edit a report's title, status, or affected components without posting a public update. | | `resolve_status_report` | mutation | Mark a report resolved and post a final public update with the supplied message. | ### Maintenance | Tool | Type | Purpose | |----------------------|----------|---------| | `list_maintenances` | read | List maintenance windows newest-first. Paginated via `page` (1-indexed) and `perPage`; response carries a `pagination` object with `page`, `perPage`, `totalSize`, and `totalPages`. | | `create_maintenance` | mutation | Schedule a maintenance window (`from` / `to` are ISO 8601 strings). | ### Monitors | Tool | Type | Purpose | |-----------------------|------|---------| | `list_monitors` | read | List monitors newest-first, including `activeIncidentCount`. Used to discover the numeric `monitorId` the other monitor tools require. | | `get_monitor` | read | Full configuration for one monitor: URL, regions, periodicity, retry/timeout, notification channels, tags. Does not return latency or status. | | `get_monitor_status` | read | Per-region current health (active / degraded / error). No time window. | | `get_monitor_summary` | read | Aggregate success/degraded/error counts, p50–p99 latency, and `lastPingAt` over `1d` (default), `7d`, or `14d`. | | `list_response_logs` | read | Recent per-region HTTP check results — request status, status code, latency — over `1d` (default), `7d`, or `14d`. HTTP monitors only; `limit` ≤ 100. | | `get_response_log` | read | Full detail of one check: URL, timing breakdown (dns/connect/tls/ttfb/transfer), redacted response headers, error message, assertion results. | ### Notifications | Tool | Type | Purpose | |----------------------|------|---------| | `list_notifications` | read | List notification channels and the monitors each one is wired to. Channel credentials are never exposed. | ### Private locations | Tool | Type | Purpose | |--------------------------|------|---------| | `list_private_locations` | read | List the workspace's private locations with their name, status (`active` / `error`), metadata, and `lastSeenAt`. Agent tokens are never exposed. | ### Audit Registered only when the workspace plan includes the `audit-log` feature — otherwise these tools are absent from `tools/list`. | Tool | Type | Purpose | |-------------------|------|---------| | `list_audit_logs` | read | List audit-log entries (mutating actions) newest-first, bounded to the last 14 days. Optional `entityType` + `entityId` filter. | | `get_audit_log` | read | Full before/after snapshots and `changedFields` for a single audit-log entry. | The MCP client gates every tool call behind your approval — the server does not gate again. Each tool also carries [MCP annotations](https://modelcontextprotocol.io/specification/server/tools#tool-annotations) (`readOnlyHint`, `destructiveHint`, `idempotentHint`) so well-behaved clients can decide whether to confirm, cache, or surface differently. ### Notifying subscribers Every publishing tool — `create_status_report`, `add_status_report_update`, `resolve_status_report`, `create_maintenance` — has a **required** `notify: boolean` field, with no default. The tool's input schema rejects calls that omit it, which forces the LLM to make an explicit choice (and therefore ask the user) before firing. `update_status_report` edits metadata only and carries no `notify` field at all. This required-field behaviour is specific to MCP. The dashboard AI assistant and the Slack agent wrap these same tools in an approval step that strips `notify` from the model-facing schema and injects it from a human toggle defaulting to `false`. MCP exposes the raw schema, so the caller must supply `notify` explicitly. Notifications dispatch as part of the same call. There is no separate notify tool: if you create a status report or append an update with `notify: false`, that update will **never** reach subscribers — you cannot retroactively notify the same update later. This matches the dashboard and Slack agent semantics. The mutation and the notify dispatch are sequential, not transactional. The mutation persists first; if the notify step then throws (transient provider issue, partial outage of an integration), the response carries `notified: false` and the row stays. `notified: true` means the dispatch call returned without throwing — **not** that every subscriber received a message. If the workspace plan doesn't include subscriber notifications, the service is a silent no-op and the response will still report `notified: true`. Treat the field as "the dispatch ran cleanly," not as a delivery receipt. | Tool | What `notify: true` sends | |----------------------------|---------------------------| | `create_status_report` | Notification for the initial update | | `add_status_report_update` | Notification for the new update | | `resolve_status_report` | Resolution notification | | `create_maintenance` | Maintenance scheduled notification | | `update_status_report` | n/a — metadata-only edit, never has a notify path | The required `notify` field, combined with the mandatory draft-and-confirm workflow in each tool's description, encodes the contract that LLMs must: 1. Draft the title/status/message/components. 2. Show the draft to the user. 3. Ask **explicitly** whether to notify subscribers. 4. Only call the tool once both content and notify are confirmed. The tool's response includes a `notified: boolean` field so the assistant can confirm what actually went out. If the workspace plan doesn't include subscriber notifications, `notify: true` is a no-op inside the service (no error, just nothing dispatched). ### Lookup-before-mutate `create_status_report` and `create_maintenance` require a `pageId`, and they (plus `update_status_report`) accept an optional `pageComponentIds`. The tool descriptions instruct the model to call `list_status_pages` for the `pageId` and `list_page_components` for component ids first; **never** type a numeric id you don't know — make the assistant resolve it. Ids that don't belong to the workspace (or, for components, to the supplied page) come back as `NOT_FOUND`. ## Configure Claude Desktop Claude Desktop launches MCP servers as local stdio processes, so the remote HTTP server is bridged through [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) — a small open-source proxy that translates Claude Desktop's stdio calls into HTTP requests against `https://api.openstatus.dev/mcp`. You'll need Node.js 18+ on your PATH so Claude Desktop can run `npx`. Add the server to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows): ```json { "mcpServers": { "openstatus": { "command": "npx", "args": [ "mcp-remote", "https://api.openstatus.dev/mcp", "--header", "x-openstatus-key: ${API_KEY}" ], "env": { "API_KEY": "os_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } } } ``` The API key is held in `env` and referenced via `${API_KEY}` in the `--header` value because `mcp-remote` parses header args before shell expansion — inlining the key after the colon breaks parsing. Fully quit and relaunch Claude Desktop (`Cmd+Q` on macOS, **File → Quit** on Windows). The openstatus tools appear in the tool picker after restart. ## Configure Cursor In Cursor, open **Settings → MCP → Add Server**: ```json { "openstatus": { "type": "http", "url": "https://api.openstatus.dev/mcp", "headers": { "x-openstatus-key": "os_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } } ``` ## Errors Errors map by severity: - **Recoverable** (`NOT_FOUND`, `VALIDATION`, `CONFLICT`, `LIMIT_EXCEEDED`, `PRECONDITION_FAILED`) come back as a tool result with `isError: true`. The model can read the message and retry with corrected input. - **Transport-level** (`UNAUTHORIZED`, `FORBIDDEN`, `INTERNAL`, malformed input) come back as a JSON-RPC error. Error messages are not redacted — the consumer is an LLM that benefits from the detail. ## Limits - `list_status_reports` and `list_maintenances` use offset pagination: `page` (1-indexed, default 1) and `perPage` (default 50, max 200). The response's `pagination.totalPages` tells the LLM whether more pages exist. `list_status_pages` is unpaginated (workspaces typically have a handful). - `list_monitors` and `list_audit_logs` page via `perPage` (max 50); `list_notifications` via `perPage` (max 200); `list_private_locations` via `perPage` (max 100); `list_response_logs` via `limit` (max 100) and `offset`. - No per-key rate limiting today. Treat MCP usage like REST: one server call per tool invocation. ## Audit log Every mutation invoked through MCP appears in the workspace audit log with: - `actor_type = "mcp"` — slice by surface: `WHERE actor_type = 'mcp'`. - `actor_id` = the API key's stable identifier (not the workspace id) — trace a specific key with `WHERE actor_id = ''`. - `actor_user_id` = the openstatus user who created the API key (custom keys only) — attribute mutations back to a person with a join to the `user` table.