# MCP server

JustMonitorIt runs a [Model Context Protocol](https://modelcontextprotocol.io)
server, so AI assistants such as Claude, Cursor and VS Code agents can read
your monitors, incidents and contacts, and change them if you allow it. Ask
"is anything down?", "why did the API monitor alert last night?" or "add an
HTTP check for https://example.com from Europe and North America".

| Setting | Value |
|---|---|
| URL | `https://dash.justmonitorit.com/api/v1/mcp` |
| Transport | Streamable HTTP (JSON responses, no SSE stream) |
| Protocol | MCP `2026-07-28` (stateless). Clients on `2025-11-25`, `2025-06-18` or `2025-03-26` also work: the server answers `initialize` without starting a session. |
| Authentication | An API token in the `Authorization: Bearer jmi_…` header on every request |
| Capabilities | Tools (see the [tool reference](generated/mcp-tools.md)) |

The server keeps no state between requests and never issues an
`Mcp-Session-Id`, so you can reconnect, retry or run several agents with
one token.

## 1. Create a token

The MCP server uses the same API tokens as the REST and GraphQL APIs. In the
web app, open **Settings → API tokens**, fill in **Create a token**:

- **Name**: something you will recognise in the audit log, such as "Claude
  Code".
- **Role**: what the assistant may do.
  - `viewer`: read monitors, results, incidents, contacts and the notification
    log. Choose this for assistants that only answer questions.
  - `member`: also create, change, pause and delete monitors, acknowledge
    incidents, add notes and choose which contacts a monitor alerts.
  - `admin`: also list members and read the audit log.
  A token can never do more than you can in that organisation, and it stops
  working if you leave the organisation.
- **Expiry**: 90 days is a good default.

Copy the token (`jmi_…`) when it is shown: it is not shown again. The token
works on that one organisation; create one per organisation if you have
several.

## 2. Connect your client

Each client stores the URL and the `Authorization` header. Keep the token
out of files you commit: most clients can read it from an environment
variable, as below.

### Claude Code

```sh
export JMI_TOKEN=jmi_…
claude mcp add --transport http justmonitorit \
  https://dash.justmonitorit.com/api/v1/mcp \
  --header "Authorization: Bearer $JMI_TOKEN"
```

Add `--scope user` to use it in every project. Check it with `claude mcp
list`, or `/mcp` inside Claude Code.

### Claude Desktop and claude.ai

Custom connectors in Claude Desktop and on claude.ai sign in with OAuth,
which JustMonitorIt does not offer yet. Connect Claude Desktop through
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote), a small local bridge
that adds the header. In **Settings → Developer → Edit Config**
(`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "justmonitorit": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://dash.justmonitorit.com/api/v1/mcp",
        "--header", "Authorization:Bearer ${JMI_TOKEN}"
      ],
      "env": { "JMI_TOKEN": "jmi_…" }
    }
  }
}
```

Restart Claude Desktop after saving.

### Cursor

`~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project):

```json
{
  "mcpServers": {
    "justmonitorit": {
      "url": "https://dash.justmonitorit.com/api/v1/mcp",
      "headers": { "Authorization": "Bearer ${env:JMI_TOKEN}" }
    }
  }
}
```

### VS Code

`.vscode/mcp.json`; VS Code asks for the token once and stores it securely:

```json
{
  "inputs": [
    { "type": "promptString", "id": "jmi-token",
      "description": "JustMonitorIt API token", "password": true }
  ],
  "servers": {
    "justmonitorit": {
      "type": "http",
      "url": "https://dash.justmonitorit.com/api/v1/mcp",
      "headers": { "Authorization": "Bearer ${input:jmi-token}" }
    }
  }
}
```

### Other clients

Any client that speaks Streamable HTTP and can send a custom header works.
The usual JSON shape is:

```json
{
  "justmonitorit": {
    "type": "http",
    "url": "https://dash.justmonitorit.com/api/v1/mcp",
    "headers": { "Authorization": "Bearer jmi_…" }
  }
}
```

Client configuration formats change between versions; if yours differs,
look for "remote", "HTTP" or "streamable HTTP" servers in its documentation.

### MCP Inspector

To try the tools by hand:

```sh
npx @modelcontextprotocol/inspector
```

Choose the **Streamable HTTP** transport, enter the URL, and add the header
`Authorization` with the value `Bearer jmi_…`.

## 3. Tools

`tools/list` returns only the tools your token's role can use. The
[tool reference](generated/mcp-tools.md) lists every tool with its
arguments.

| Area | Tools |
|---|---|
| Your token and organisation | `whoami`, `get_overview`, `list_locations` |
| Monitors | `list_monitors`, `get_monitor`, `create_monitor`, `update_monitor`, `pause_monitor`, `resume_monitor`, `delete_monitor`, `preview_check`, `get_monitor_results`, `get_monitor_uptime`, `set_monitor_contacts` |
| Incidents | `list_incidents`, `get_incident`, `acknowledge_incident`, `add_incident_note` |
| Contacts and alerts | `list_contacts`, `list_notifications` |
| Administration | `list_members`, `get_audit_log` |

Each tool calls the [REST API](generated/rest-reference.md) with your token, so
it follows the same rules: the same validation, plan limits, rate limits and
audit log. Results are the REST API's JSON, as `structuredContent` and as
text. When the API refuses a call (a validation error, a plan limit, a
missing permission), the tool result has `isError: true` and the API's
[problem document](rest.md#errors), so the assistant can see what to fix.

Every tool carries annotations: read tools are `readOnlyHint: true`,
`delete_monitor` is `destructiveHint: true`. Most clients ask before running
a tool that changes something; keep that confirmation on for `member` and
`admin` tokens.

## Example prompts

- "Which monitors are down or degraded right now, and since when?"
- "Show me the incidents of the last 7 days for the checkout API and how
  long each lasted."
- "What did the probes in Frankfurt see when the homepage went down?"
  (uses `get_monitor_results` with a location and time range)
- "Was anyone notified about incident inc_…? Did the SMS arrive?"
- "Check https://status.example.com from London once, then create an HTTP
  monitor for it every 60 seconds from Europe and North America."
- "Acknowledge the open incident on the API monitor and add a note that
  we're rolling back the deploy."
- "Pause every monitor labelled `staging`."
- "Who changed the alert contacts of the billing monitor last week?"
  (needs an `admin` token)

## Security

- **Least privilege.** Give assistants that only answer questions a
  `viewer` token. Use `member` only where the assistant should change
  things, and keep tool confirmations on.
- **One token per client.** Then the audit log shows which tool made each
  change (`data.api_token`), and you can revoke one client without the
  others.
- **Expiry and revocation.** Set an expiry, and revoke a token in
  **Settings → API tokens** as soon as you no longer need it or it may have
  leaked. Revocation takes effect on the next request.
- **Prompt injection.** Monitor names, incident notes and check results can
  contain text written by other people or returned by the sites you check.
  Treat what an assistant proposes to change with the same care as any
  automated change.
- The server accepts tokens only in the `Authorization` header (never in the
  URL), and refuses browser requests from other sites (`Origin` must be
  `https://dash.justmonitorit.com`).

## Protocol details

For client authors; most users can skip this.

- Every message is its own `POST` to the URL above; send `Accept:
  application/json, text/event-stream`. Responses are always
  `application/json`. A notification gets `202 Accepted` with no body.
  Batches (JSON arrays) are refused with `-32600`.
- **Modern requests** (`2026-07-28`) carry `MCP-Protocol-Version: 2026-07-28`,
  `Mcp-Method` and, for `tools/call`, `Mcp-Name` (plain or
  `=?base64?…?=`), and `params._meta` with
  `io.modelcontextprotocol/protocolVersion` and
  `io.modelcontextprotocol/clientCapabilities`. Mismatched or missing headers
  are `400` with `-32020`; missing `_meta` fields `400` with `-32602`; an
  unsupported version `400` with `-32022` and `data.supported`; an unknown
  method `404` with `-32601`. Results carry `resultType: "complete"` and
  `_meta["io.modelcontextprotocol/serverInfo"]`. `server/discover` returns
  the supported versions, capabilities and instructions.
- `tools/list` results carry `ttlMs: 300000` and `cacheScope: "private"`
  (the list depends on the token's role).
- **Legacy clients** send `initialize` first; the reply names the version
  (yours if supported, else `2025-11-25`) and has no `Mcp-Session-Id`. Later
  requests need no session id (one sent is ignored); `ping` answers `{}`.
- `GET` and `DELETE` on the endpoint answer `405`: there is no standalone
  event stream and no session to end.
- Authentication failures are HTTP `401` with `WWW-Authenticate: Bearer
  realm="JustMonitorIt", error="invalid_token"`. Each MCP request and each
  REST call a tool makes count against the token's
  [rate limits](rest.md#rate-limits).

A raw request, for testing with `curl`:

```sh
curl -s https://dash.justmonitorit.com/api/v1/mcp \
  -H "Authorization: Bearer $JMI_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: list_monitors" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
       "params": {"name": "list_monitors", "arguments": {"status": "down"},
                  "_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28",
                            "io.modelcontextprotocol/clientCapabilities": {}}}}'
```
