Skip to content

MCP for agents

Point an MCP client at Perstat and it can answer “what is broken and who owns it” from your live data, then act within its authorization. The full catalogue has fifteen tools: seven read state, two act on an incident, and six manage projects and monitors. The connected server’s tools/list is authoritative for the credential in front of you. No defined tool deletes a record.

Endpoint https://api.perstat.io/mcp
Transport Streamable HTTP, stateless
Protocol versions 2025-06-18 (preferred), 2025-03-26, 2024-11-05

One JSON-RPC message in by POST, one JSON response out. GET /mcp answers 405: this server opens no server-initiated event stream. JSON-RPC batches were dropped in 2025-06-18 and are rejected with a clear message.

In Claude Code:

Terminal window
claude mcp add --transport http perstat https://api.perstat.io/mcp \
--header "Authorization: Bearer pst_…"

This form works in clients that accept a custom HTTP authorization header. Note that the key ends up in your shell history this way.

For Claude Code there is a plugin. It wires the same server and adds four skills that turn the tools into workflows: handling an incident, managing monitors, and reading the current state.

Terminal window
claude plugin marketplace add datargo/perstat-plugin
claude plugin install perstat@datargo

On the first Perstat tool call Claude opens the OAuth sign-in in your browser. Choose the organization the connection should use; there is no key to copy or shell profile to edit. The connection is bound to that organization and your current role. Check it with claude mcp list; Perstat should report Connected.

Use the API-key one-liner above for CI, cron, or a deliberately project-narrowed credential. Use the plugin for an interactive, browser-authenticated workflow.

Perstat accepts a scoped organization API key as a bearer credential:

Authorization: Bearer pst_…

A client that supports OAuth can instead use Perstat’s published Authorization Code flow with PKCE. Discovery is available at https://api.perstat.io/.well-known/oauth-protected-resource/mcp and https://api.perstat.io/.well-known/oauth-authorization-server; refresh and revocation endpoints are published there as well. A browser session cookie alone does not authenticate MCP: the OAuth flow ends in a bearer token.

OAuth clients are accepted from Claude, Claude Code and ChatGPT, identified by their client documents under https://claude.ai/oauth/ and https://chatgpt.com/oauth/. The consent screen names the host the client comes from, not only the name it gives itself, and a client only receives the permissions its tools need: monitors and incidents, each read or write.

An organization API key belongs to exactly one organization and carries scopes. Together that makes its rights predictable: it can never do more than the key says.

Owners and admins create keys in the app under Integrations, API keys.

tools/list shows only what the current credential is allowed to do. tools/call checks the same scope again, because a list is not a safeguard.

The full catalogue has fifteen tools. Browser OAuth currently grants monitor and incident scopes, so plugin users do not receive list_status_pages; an API key with status-pages:read does. The connected server’s tools/list is the practical truth for the credential in front of you.

Tool Scope What it does
get_organization_summary monitors:read The whole picture in one call: services by state, open incidents
list_projects monitors:read Projects with their monitor counts. The entry point for create_monitor
list_monitors monitors:read Services with their last confirmed state, filterable. Pass archived: true to see the archive
get_monitor monitors:read One service with configuration, regions, and recent incidents. Custom request header values come back as [redacted]
list_incidents incidents:read Open and resolved incidents, newest first
get_incident incidents:read One incident in detail
acknowledge_incident incidents:write Take the incident, which stops the alert and the escalation
resolve_incident incidents:write Close an open incident by hand
list_status_pages status-pages:read The organization’s status pages
create_project monitors:write Create the folder monitors live in. Organization-wide credentials only
create_monitor monitors:write Start monitoring a new service. Organization-wide keys only
update_monitor monitors:write Change the name, settings, check interval, or regions
set_monitor_enabled monitors:write Pause a monitor or put it back into service
archive_monitor monitors:write Take a service out of service and free its slot in your plan
restore_monitor monitors:write Bring an archived service back. It returns paused and needs a free plan slot

write implies read on the same resource, never across resources.

Everything the agent sees is English: tool names, descriptions, schema text, and error reasons. The activity log inside the app is unaffected, because people read that one.

create_project and create_monitor are the two tools that are not idempotent, and they declare that to the client. Repeating either call can create a second resource or fail on the name. Look first with list_projects or list_monitors.

Tools that change or end existing state (acknowledge_incident, resolve_incident, update_monitor, set_monitor_enabled, archive_monitor) are marked as destructive, so a client may ask before it calls them. Creating and restoring only add.

Custom request headers of a monitor are credentials and are never read back through a key or an agent: get_monitor shows their names with the value [redacted]. Send a configuration back through update_monitor with the placeholder in place and the stored value is kept, as long as the URL and the method stay exactly the same. After any change to either, the placeholder is refused, so a credential never follows a request its author did not configure. The web app still shows the values to signed-in members.

Archived through MCP, not deleted through MCP

Section titled “Archived through MCP, not deleted through MCP”

An agent can archive a service. It can never delete one, and the reason is not caution.

Deleting a monitor cascades through eleven tables and takes the incident history, the daily SLA rollups, and the exclusion windows with it. For a product whose whole point is the record, that is not something an agent call may reach. So the tool that would do it does not exist.

archive_monitor stops the service, removes it from active lists, and frees its slot in your plan while keeping the resource restorable. Archiving itself does not delete its history, but normal object-specific retention rules still apply; it is not permanent storage. restore_monitor brings it back, paused, and claims a plan slot again: on a full plan the restore is refused with the same message as create_monitor until another monitor is archived or the plan grows. The name is released on archive, so a new monitor can take it.

This matters for your quota, and the two are not the same: pausing does not free a slot, the monitor is still there and still meant. Archiving does. If you truly want something gone for good, delete it in the app, where a person is looking at the consequences.

Getting back must not depend on memory. list_monitors with archived: true shows the archive, and get_monitor finds archived services too, so you can look before restoring. Without that, an archived monitor would be unreachable over MCP the moment someone lost the ID. What does not work on an archived monitor is changing it or switching it on: update_monitor and set_monitor_enabled reject it with “is archived, call restore_monitor first” rather than with “not found”, because it is not missing.

Also out of write reach over MCP: status-page changes, connectors, API keys, members, and your plan. list_status_pages is a read-only exception for API keys with status-pages:read; browser OAuth does not currently receive that scope. Operational writes stay inside monitoring and incidents.

An action over MCP is attributed to the credential’s human identity, the API-key creator or the OAuth subject, so taking ownership during an incident carries a name and not just a token. The incident timeline additionally records that it came in over MCP. Monitors an agent creates or changes appear in your organization’s activity log the same way, under that identity’s name.

Because that identity is borrowed for attribution, it also governs permission. On every single write the server re-checks that the identity is still a member of the organization and still carries the role the action needs. The threshold matches the app: handling incidents needs whoever is allowed to respond, down to a responder, while managing monitors needs whoever is allowed to manage projects, so an owner, admin, or developer. Downgrade or remove that identity and its credential stops writing from the next call onward. Reading is unaffected and depends on the credential’s scopes. If that person’s account has been deleted, the server refuses the write as well, and says why.

A key restricted to specific projects or monitors sees only its own resources in list_monitors and list_incidents. Everything else simply does not exist for it, with no way to probe whether it might. The same holds when writing: update_monitor and set_monitor_enabled report that there is no monitor with that ID rather than that it is forbidden.

Organization-wide tools, get_organization_summary, list_status_pages, create_project, and create_monitor, are not offered to a narrowed key at all. The two create tools would otherwise let it write into projects it knows nothing about.

The list tools report total_matching and truncated alongside the rows, so a shortened answer is recognizable as one instead of looking like a complete result.

A failure inside a tool is not a protocol error. It comes back as a result marked as an error with a plain-language reason, so the model can see what went wrong and try something else. Protocol errors stay reserved for real malformed requests.