Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
MDX
at commit 052d111a
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200---category: Referencetitle: MCP Serverdescription: 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.
```httpx-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 = '<keyId>'`.- `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.