Ask your AI assistant "was I alerted?" — okokumo's MCP server
2026-10-19
The question we get most about alerting isn't "is it down?" The dashboard answers that. It's "was I told?" Those are different questions. A check can go down, the state change can be recorded correctly, and the alert can still reach nobody, for example because the only channel was never confirmed. We added a per-check alert log for exactly this after a beta tester spent six days believing an expiry alert had gone out.
Now an assistant can ask that question for you. okokumo runs an official,
hosted Model Context Protocol server at
https://app.okokumo.com/mcp. It is available on every plan, Free included.
This post covers what it can do, what it can't, and why the gap between those
two is deliberately small.
The whole design in one sentence
Every MCP tool is a call to a /v1 route you could already make with curl.
This isn't just a loose analogy. A tool call is dispatched as an in-process
request through the API's own router, at /v1/..., carrying the caller's
credential. The request then goes through the same auth middleware,
organization scoping, role checks, plan limits and validation as a REST
request from your terminal. If we change a rule in a /v1 handler, the MCP
server picks up the change automatically, because there is no second copy of
the rule to forget. The MCP endpoint can do nothing your key can't already
do over REST.
In practice:
- A member's key can manage checks but can't write incidents. That's the
same rule as over REST, and the assistant gets the same
403. - A Free organization at its 5-check limit gets
plan limit reached (5 checks)fromcreate_check, just as it would fromPOST /v1/checks. /mcpspends your organization's/v1rate window: 120 requests per minute, shared with REST. An assistant stuck in a loop can't exceed your budget. It can only use it up.
The 18 tools
There are 10 read tools:
| Tool | Calls |
|---|---|
get_account |
GET /me |
list_checks |
GET /checks |
get_check |
GET /checks/{id} |
get_check_stats |
GET /checks/{id}/stats (period: day, week or month) |
list_check_transitions |
GET /checks/{id}/transitions |
list_check_alerts |
GET /checks/{id}/alerts |
get_check_channels |
GET /checks/{id}/channels |
list_channels |
GET /channels (config redacted) |
get_status_page |
GET /status-page |
list_incidents |
GET /status-page/incidents |
And 8 write tools: create_check, update_check, pause_check,
resume_check, set_check_channels, create_incident, update_incident and
post_incident_update.
The list leaves some things out on purpose. There are no delete tools and no
API-key tools. REST has DELETE /status-page/incidents/{id}, but the MCP
server doesn't expose it. Deleting anything and managing keys stay in the
dashboard.
Some differences from raw REST are also deliberate:
- Partial updates. A
/v1PUTreplaces the whole record.update_check,pause_check,resume_checkandupdate_incidentread the current record first and overlay only the arguments the model passed. Without this, an assistant that sent onlynamewould reset every other field to its default. - Seconds, everywhere. The arguments are
interval_seconds,timeout_secondsandgrace_seconds. Minutes are the classic model mistake, so the tool schemas and the server instructions both say "SECONDS". Unknown arguments are refused with the list of accepted ones, so agrace_minutesis rejected loudly rather than silently ignored. - No secrets in the transcript.
list_channelsreturnsid,name,type,validated_atandcreated_at, and nothing else. Webhook URLs, signing secrets and Telegram chat ids are credentials. - Destructive hints.
pause_check,update_check,set_check_channelsandupdate_incidentare markeddestructiveHint, so clients ask you before running them. Pausing a check and narrowing its alert routing both mean alerts that won't be sent. The tool descriptions tell the model to confirm with you first. Incidents are private drafts until someone asks forpublished: true.
One exchange, on the wire
Say you ask: "Did anyone get paged when nightly-backup went down last night?"
- The assistant calls
list_checkswith no arguments. The server makes an in-processGET /v1/checksand returns your checks with their state. It also adds a ready-madeping_urlfor heartbeat and cron checks. - It finds
nightly-backupand callslist_check_alertswith{"check_id": "<uuid>"}. That becomesGET /v1/checks/<uuid>/alerts, which returns the last 20 deliveries for the check, newest first:outcome(delivered,failedorskipped),channel_type,channel_name,detailandcreated_at. - Suppose the top row is
skippedwith the detail none of the organization's 1 channel(s) are confirmed. The assistant can now give you the actual answer: no, nobody was paged, because your only channel was never verified. It can calllist_channelsto show you thatvalidated_atis unset.
All three steps are reads. Each one cost a single unit of your rate window. Nothing changed.
Writes are just as direct. "Pause nightly-backup, I'm moving it to a new
host" becomes pause_check with {"check_id": "<uuid>"}. Your client
should ask you to confirm because the tool is marked destructive. The server
then sends GET /v1/checks/<uuid> followed by PUT /v1/checks/<uuid> with
the full current record plus enabled: false. Because it makes two /v1
calls, it costs 2 units. A paused check isn't monitored and never alerts. If
you only want to mute alerts while the check keeps measuring, the MCP server
has no tool for that. Maintenance windows
are in the dashboard.
Connecting: an API key, or just the URL
With a key. Create one in Dashboard → Account → API keys. Only owners and
admins can do this. The key acts with the role of the person who created it,
and there is no read-only key yet. Use one key per client so you can revoke
each one separately. Revocation takes effect at the client's next message,
because the key is checked on every request. For Cursor, put this in
~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project):
{
"mcpServers": {
"okokumo": {
"url": "https://app.okokumo.com/mcp",
"headers": { "Authorization": "Bearer sk_…" }
}
}
}
The MCP page has the equivalents for Claude Code and Claude Desktop.
With the browser. Clients that implement MCP authorization only need the
URL. In Claude Desktop that's Settings → Connectors → Add custom connector,
with https://app.okokumo.com/mcp. The client calls /mcp and gets a 401
whose WWW-Authenticate header points to okokumo's authorization server.
It then identifies itself and opens your browser on a consent screen. That
screen shows the app's name, where it will send you back to, which
organization it's for, and what it gets: the same access as an API key, with
your role. There is only one OAuth scope, mcp, and it is worth exactly an
API key, no more. As with keys, only owners and admins can approve a
connection. Only approve one you started yourself.
Access tokens last an hour. Refresh tokens last 30 days and rotate on every use. If a refresh token is presented twice, the whole connection is ended, because one of those copies is in the wrong hands. Every approved app is listed under Dashboard → Account → Connected apps, with who connected it and when it was last used. Owners and admins can revoke any of them there.
What has been tested, and what hasn't
The browser flow has been tested end to end with the official Go SDK's OAuth client, Claude Code and the MCP Inspector. claude.ai and Claude Desktop connectors, ChatGPT, Cursor and VS Code implement the same MCP authorization spec, but we haven't tried them against this server live yet. If one fails, tell us.
There is no stdio npm package. The hosted endpoint is the only way to connect.
A read-only key or scope, so an assistant can look without being able to touch anything, is the obvious next step. It isn't built yet. If you'd use it, tell us.