# MCP server

imsu.io runs a remote [Model Context Protocol](https://modelcontextprotocol.io) server at:

```text
https://imsu.io/mcp
```

With it, an AI client can list, read, create, change, pause, resume and delete the monitors of your imsu.io account. You sign in to imsu.io in your browser once and choose what the client may do; the client never sees your password.

- Transport: Streamable HTTP, without sessions. Every message is a `POST` to `https://imsu.io/mcp`.
- Protocol versions: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.
- Sign-in: OAuth 2.1 with PKCE, started by the client ([Authentication](https://imsu.io/docs/authentication)). Clients without OAuth can send an API key instead.
- Available on every plan. The plan's limits apply as in the web app.

The steps below come from each client's own documentation. Menus change; if one doesn't match, look for "custom connector" or "remote MCP server" in the client's help.

## Connect a client

### Claude (web, desktop and mobile)

1. On [claude.ai](https://claude.ai) or in Claude Desktop, open **Customize → Connectors**.
2. Click **+ Add**, then **Add custom connector**.
3. Name it `imsu.io` and enter the URL `https://imsu.io/mcp`. Click **Continue**.
4. Click **Continue** on the sign-in settings Claude detected, keep its defaults and click **Add**. Sign in to imsu.io in the browser window that opens and click **Allow**.
5. In a chat, open **+ → Connectors** and turn imsu.io on.

Connectors run from Anthropic's servers, so a connector you add on the web works in Claude Desktop and the mobile apps too. On Team and Enterprise plans an owner adds it under **Organization settings → Connectors**, and each member connects their own imsu.io account. Free Claude accounts can have one custom connector.

### Claude Code

```sh
claude mcp add --transport http imsu https://imsu.io/mcp
```

Then, in Claude Code, run `/mcp`, choose imsu, and sign in in the browser. To use the server in every project, add it with `--scope user`; to share it with your team through `.mcp.json`, with `--scope project`:

```sh
claude mcp add --transport http --scope user imsu https://imsu.io/mcp
```

#### Claude Code without a browser

For CI or a headless machine, create an API key in [Settings → API keys](https://imsu.io/settings) and pass it as a header:

```sh
claude mcp add --transport http imsu https://imsu.io/mcp \
  --header "Authorization: Bearer $IMSU_API_KEY"
```

Or in `.mcp.json`, with the key read from the environment:

```json
{
  "mcpServers": {
    "imsu": {
      "type": "http",
      "url": "https://imsu.io/mcp",
      "headers": {
        "Authorization": "Bearer ${IMSU_API_KEY}"
      }
    }
  }
}
```

### Cursor

Add the server to `~/.cursor/mcp.json` (every project) or `.cursor/mcp.json` (one project):

```json
{
  "mcpServers": {
    "imsu": {
      "url": "https://imsu.io/mcp"
    }
  }
}
```

Cursor signs in with OAuth when the server asks for it; allow access in the browser. To use an API key instead, add `"headers": { "Authorization": "Bearer ${env:IMSU_API_KEY}" }`.

### VS Code

Run **MCP: Add Server** from the Command Palette and choose **HTTP**, or add the server to `.vscode/mcp.json` (or to your user configuration, **MCP: Open User Configuration**):

```json
{
  "servers": {
    "imsu": {
      "type": "http",
      "url": "https://imsu.io/mcp"
    }
  }
}
```

VS Code opens the browser to sign in the first time it connects. To use an API key instead, prompt for it so it isn't stored in the file:

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "imsu-api-key",
      "description": "imsu.io API key",
      "password": true
    }
  ],
  "servers": {
    "imsu": {
      "type": "http",
      "url": "https://imsu.io/mcp",
      "headers": {
        "Authorization": "Bearer ${input:imsu-api-key}"
      }
    }
  }
}
```

### MCP Inspector

```sh
npx @modelcontextprotocol/inspector --server-url https://imsu.io/mcp --transport http
```

Press **Connect**, then sign in and allow access in the browser. The Inspector registers itself as an OAuth client. With an API key, add `--header "Authorization: Bearer $IMSU_API_KEY"`.

### ChatGPT and other clients

Any client that supports remote MCP servers over Streamable HTTP with OAuth can connect: give it `https://imsu.io/mcp`. In ChatGPT, custom MCP connectors are added in developer mode (**Settings → Apps**); whether it is offered depends on your ChatGPT plan. Clients that only take a fixed header can send an API key as `Authorization: Bearer imsu_pat_…`.

## Authentication {#authentication}

Every request needs a credential in the `Authorization` header:

- an **OAuth access token** (`imsu_at_…`) that an MCP client got through the flow in [Authentication](https://imsu.io/docs/authentication), or
- an **API key** (`imsu_pat_…`) the user created in [Settings → API keys](https://imsu.io/settings).

Cookies never work here, and tokens in the query string are ignored. A request without a credential gets `401`, and the `WWW-Authenticate` header says where to start OAuth:

```http
POST /mcp HTTP/1.1
Host: imsu.io
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"}}}
```

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://imsu.io/.well-known/oauth-protected-resource/mcp", scope="monitors:read monitors:write"
Content-Type: application/problem+json
```

```json
{
  "type": "https://imsu.io/docs/mcp#authentication",
  "title": "Authorization required",
  "status": 401,
  "detail": "Connect with OAuth: MCP clients start it from the resource_metadata URL in WWW-Authenticate. Headless clients can send an API key from Settings → API keys as Authorization: Bearer imsu_pat_….",
  "traceId": "00-2efde6ac7f7c19f6e2d4d7cd3305f9d6-332da20ca9be86ae-00"
}
```

A token that is unknown, expired or revoked, or whose account is suspended, gets the same `401` with `error="invalid_token"` added to the challenge and the title "Invalid token". An MCP client then refreshes its access token, or signs in again if that fails. An OAuth token issued for another resource gets the title "Token not accepted here".

## Insufficient scope {#insufficient-scope}

On the consent page the user may allow reading only. A connection like that can call `list_monitors`, `get_monitor` and `get_account_limits`, but a call to a tool that changes monitors is refused before the tool runs:

```http
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="monitors:read monitors:write", resource_metadata="https://imsu.io/.well-known/oauth-protected-resource/mcp"
Content-Type: application/problem+json
```

```json
{
  "type": "https://imsu.io/docs/mcp#insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "This tool changes monitors, which needs the monitors:write scope. Connect to imsu.io again and allow it to create, change, pause and delete monitors.",
  "traceId": "00-42da06f26ce861e15a29fa3ac5ed5672-9838b78501419194-00"
}
```

The client should tell the user and run the authorization again with `scope=monitors:read monitors:write` (a step-up). The user then sees the consent page again and can allow changes.

An API key can't be re-authorized, so a read-only key gets a tool result with `isError: true` instead: "This API key can only read monitors. To let the agent change them, create a key with read and write access in Settings → API keys on imsu.io and use it instead."

## Rate limits {#rate-limits}

Each credential (a connected app or an API key) may send 120 requests a minute to `/mcp`. This budget is separate from the REST API's. Over it, the answer is `429` with `Retry-After` in seconds:

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 37
Content-Type: application/problem+json
```

```json
{
  "type": "https://imsu.io/docs/mcp#rate-limits",
  "title": "Too many requests",
  "status": 429,
  "detail": "This credential sent more requests than its limit allows. Wait 37 seconds (Retry-After) and try again.",
  "traceId": "00-6b0f9a2c1d7e4f3a8b5c2d1e0f9a8b7c-1a2b3c4d5e6f7a8b-00"
}
```

Wait that many seconds, then retry. The plan's limits still cap how many monitors there can be.

## Body too large {#body-too-large}

A request body can be at most 256 KB; a tool call takes a few hundred bytes. A larger one gets `413`:

```json
{
  "type": "https://imsu.io/docs/mcp#body-too-large",
  "title": "Request body too large",
  "status": 413,
  "detail": "The body can be at most 256 KB.",
  "traceId": "00-921cbe558ba8d0a6f9350f597eaa64f4-50c075d511578f06-00"
}
```

## Other HTTP answers

- `GET` and `DELETE` on `/mcp` get `405 Method Not Allowed` with `Allow: POST`: the server keeps no sessions or streams. Clients of 2025-06-18 and 2025-11-25 take this as "carry on without a stream".
- A JSON-RPC batch (a JSON array) and an unknown `MCP-Protocol-Version` header get `400` with a JSON-RPC error.
- A body in a charset the server can't read gets `415` "Unsupported charset". Send UTF-8 JSON.
- Browser-based clients may call from any origin: preflights are answered without a credential, and `WWW-Authenticate` and `Retry-After` are readable.

## Tool results and errors

Each tool returns `structuredContent` that matches its output schema, plus two text blocks for clients that only show text: a sentence for people, then the same result as JSON. For example, `create_monitor` answers:

```text
Created "Shop" (https://shop.example.com/health), checked every 5 min. First check within a minute. id: 01a1c6de-07ae-72e1-9b05-b184f79ad5ec
```

```json
{
  "id": "01a1c6de-07ae-72e1-9b05-b184f79ad5ec",
  "name": "Shop",
  "type": "http",
  "target": "https://shop.example.com/health",
  "status": "unknown",
  "is_paused": false,
  "interval_seconds": 300,
  "url": "https://shop.example.com/health",
  "method": "GET",
  "timeout_seconds": 10,
  "expected_status": "200-299",
  "keyword": "ok",
  "follow_redirects": true,
  "failure_threshold": 2,
  "host": null,
  "port": null,
  "expect_closed": null,
  "grace_seconds": null,
  "run_timeout_seconds": null,
  "ping_url": null,
  "last_ping_at": null,
  "uptime_24h": null,
  "last_check_at": null,
  "open_incident": null,
  "created_at": "2026-11-10T19:10:02.212627+00:00"
}
```

Monitors come back in snake_case. Settings of other types are `null`. `status` is `up`, `down`, `unknown` (not checked yet, or a heartbeat without its first ping) or `paused`. `open_incident` is `{ "started_at", "cause" }` while the monitor is down.

Problems the model can fix come back as a tool result with `isError: true` and one sentence that says how to fix the call. Arguments are checked against the tool's input schema first, so a wrong name or type is named:

```text
Invalid arguments. interval_minutes is not an argument of create_monitor, which takes: name, type, url, host, port, interval_seconds, timeout_seconds, expected_status, keyword, method, follow_redirects, failure_threshold, expect_closed, grace_seconds, run_timeout_seconds, allow_duplicate. Correct them and call create_monitor again.
```

```text
Invalid arguments. interval_seconds: Interval must be between 300 and 86400 seconds on your plan. Correct them and call create_monitor again.
```

```text
No monitor with id dd48baea-6ec5-4c2f-b3b3-ef3fc26f2e22 in this account. Call list_monitors to find the right id.
```

```text
The account's plan allows 10 monitors, and all 10 are in use (paused ones count). Tell the user, who can delete a monitor they no longer need (higher limits are set by imsu.io staff); get_account_limits shows the numbers.
```

Results contain only the account's own names and settings and errors imsu.io wrote, such as "Timed out after 10 s". They never contain what a monitored site sent.

## Tools

The list below is generated from the server's `tools/list`, in the order the server lists the tools. "Read-only" tools change nothing; "destructive" means the change can't be undone; "idempotent" means calling again with the same arguments changes nothing more; "closed world" means the tool only touches the imsu.io account. Tools that change monitors need the `monitors:write` scope; the others need `monitors:read`.

<!-- tools:start -->
<!-- Generated from the server's tools/list by DocsTests (tests/Imsu.Api.Tests); don't edit it by hand. To update it, run those tests with IMSU_UPDATE_DOCS=1. -->

### Server instructions

The server sends these to the client when it connects:

> imsu.io checks websites (http), TCP ports (port) and cron jobs (heartbeat) on a schedule and alerts the account owner when they go down. Monitor ids are UUIDs: find them with list_monitors; never guess. Intervals are in seconds and can't be shorter than the account's minimum (get_account_limits). Pausing stops checks and alerts; deleting removes the monitor and hides its history. Ask the user before deleting.

### list_monitors

**List monitors.** Lists the account's monitors with their id, name, type, target, status and uptime over the last 24 hours, and counts every monitor by status. Use it to find a monitor's id. Filters combine. Monitors come oldest first, a page at a time: when next_cursor is not null, call again with that cursor for the next page.

- Scope: `monitors:read`
- Annotations: read-only, idempotent, closed world
- Arguments:
  - `status` (one of `up`, `down`, `unknown`, `paused`; optional): Only monitors in this state: up, down, unknown (not checked yet, or a heartbeat without its first ping) or paused.
  - `type` (one of `http`, `port`, `heartbeat`; optional): Only monitors of this type: http (websites and APIs), port (TCP ports) or heartbeat (jobs that ping imsu.io).
  - `search` (string; optional): Text to find in the name or the target (URL or host:port), ignoring case.
  - `limit` (integer; optional, default `50`): How many monitors to return, 1 to 200. Default 50.
  - `cursor` (string; optional): The next_cursor of the previous call, for the next page. Leave it out for the first page.
  - `ssl_expiring_within_days` (integer; optional): Only monitors whose SSL certificate expires within this many days, or has expired, e.g. 30.

### get_monitor

**Get a monitor.** Gets one monitor: all its settings, its status, its uptime over the last 24 hours, the open incident while it is down, and for heartbeats the URL their job must ping.

- Scope: `monitors:read`
- Annotations: read-only, idempotent, closed world
- Arguments:
  - `id` (UUID; required): The monitor's id, a UUID. Find it with list_monitors; never guess it.

### get_account_limits

**Get account limits.** Gets the account's plan limits: how many monitors it may have, how many it has, and the shortest interval_seconds allowed. Call it when unsure whether a monitor fits or how often it may be checked.

- Scope: `monitors:read`
- Annotations: read-only, idempotent, closed world
- Arguments: none

### create_monitor

**Create a monitor.** Creates a monitor and starts checking it; the account owner is alerted when it goes down. Types: http requests a URL (needs url), port connects to a TCP port (needs host and port), heartbeat waits for a job to request its ping_url at least every interval_seconds. An http or port monitor is first checked within a minute; a heartbeat's first ping is due within interval_seconds plus grace_seconds. Settings left out get their defaults. Refuses a second monitor of the same type for the same target (heartbeats: with the same name) unless allow_duplicate is true.

- Scope: `monitors:write`
- Annotations: changes monitors, not destructive, not idempotent, closed world
- Arguments:
  - `name` (string; required): A short name people recognize, at most 100 characters, e.g. "Shop" or "Nightly backup".
  - `type` (one of `http`, `port`, `heartbeat`; optional, default `http`): What to check: http (a URL; the default), port (a TCP port) or heartbeat (a cron job or worker that pings imsu.io). Can't be changed later.
  - `url` (string; optional): HTTP only: the absolute http:// or https:// URL to request, e.g. https://shop.example.com/health. Required for http.
  - `host` (string; optional): Port only: a domain name or IP address, without a scheme or path, e.g. db.example.com. Required for port.
  - `port` (integer; optional): Port only: the TCP port, 1 to 65535, e.g. 5432. Required for port.
  - `interval_seconds` (integer; optional): Seconds between checks: 60 = 1 minute, 300 = 5 minutes, 3600 = 1 hour, 86400 = 1 day (the most). For heartbeats, the longest time expected between pings, up to 2678400 (31 days). At least the account's minimum interval; call get_account_limits if unsure. Default: the account's minimum.
  - `timeout_seconds` (integer; optional): HTTP and port: seconds to wait for an answer before the check fails, 1 to 30, and shorter than the interval. Default 10.
  - `expected_status` (string; optional): HTTP only: the status codes that count as up, as codes and ranges, e.g. "200-299" or "200,301". Default "200-299".
  - `keyword` (string; optional): HTTP only: text the response body must contain (case-sensitive, at most 200 characters), or the check fails. Not possible with method HEAD. Default: none.
  - `method` (string; optional): HTTP only: GET or HEAD. Default GET.
  - `follow_redirects` (boolean; optional): HTTP only: follow redirects and check the final response. Default true.
  - `failure_threshold` (integer; optional): HTTP and port: failed checks in a row before the monitor is down and alerts go out, 1 to 10. Default 2.
  - `expect_closed` (boolean; optional): Port only: a reverse check that alerts when the port accepts connections, e.g. for a database that must not be reachable from the internet. Default false.
  - `grace_seconds` (integer; optional): Heartbeat only: how many seconds a ping may be late, on top of interval_seconds, before the monitor is down; 0 to 86400. Default 300 (5 minutes).
  - `run_timeout_seconds` (integer; optional): Heartbeat only: how many seconds a run that reports progress may take to reach 100%; 60 to 86400. Default 3600 (1 hour).
  - `ssl_expiry_alerts` (boolean; optional): HTTP with an https URL only: warn by email and chat before the SSL certificate expires. Default true for https URLs.
  - `ssl_alert_days` (integer; optional): HTTP only: days before the certificate expires that the first warning goes out: 1, 3, 7, 14 or 30. Another follows at each of 14, 7, 3 and 1 days left below it. Default 14.
  - `allow_duplicate` (boolean; optional, default `false`): Create the monitor even if one of the same type already checks the same target (heartbeats: has the same name). Pass true only when the user really wants a second monitor. Default false.

### update_monitor

**Change a monitor.** Changes some of a monitor's settings: pass its id and only the settings to change; the others keep their values. The type can't change: create a new monitor instead. Settings of another type than the monitor's are ignored.

- Scope: `monitors:write`
- Annotations: changes monitors, not destructive, idempotent, closed world
- Arguments:
  - `id` (UUID; required): The monitor's id, a UUID. Find it with list_monitors; never guess it.
  - `name` (string; optional): A short name people recognize, at most 100 characters, e.g. "Shop" or "Nightly backup".
  - `url` (string; optional): HTTP only: the absolute http:// or https:// URL to request, e.g. https://shop.example.com/health.
  - `host` (string; optional): Port only: a domain name or IP address, without a scheme or path, e.g. db.example.com.
  - `port` (integer; optional): Port only: the TCP port, 1 to 65535, e.g. 5432.
  - `interval_seconds` (integer; optional): Seconds between checks: 60 = 1 minute, 300 = 5 minutes, 3600 = 1 hour, 86400 = 1 day (the most). For heartbeats, the longest time expected between pings, up to 2678400 (31 days). At least the account's minimum interval; call get_account_limits if unsure.
  - `timeout_seconds` (integer; optional): HTTP and port: seconds to wait for an answer before the check fails, 1 to 30, and shorter than the interval.
  - `expected_status` (string; optional): HTTP only: the status codes that count as up, as codes and ranges, e.g. "200-299" or "200,301".
  - `keyword` (string; optional): HTTP only: text the response body must contain (case-sensitive, at most 200 characters), or the check fails. Not possible with method HEAD. null or "" removes the keyword.
  - `method` (string; optional): HTTP only: GET or HEAD.
  - `follow_redirects` (boolean; optional): HTTP only: follow redirects and check the final response.
  - `failure_threshold` (integer; optional): HTTP and port: failed checks in a row before the monitor is down and alerts go out, 1 to 10.
  - `expect_closed` (boolean; optional): Port only: a reverse check that alerts when the port accepts connections, e.g. for a database that must not be reachable from the internet.
  - `grace_seconds` (integer; optional): Heartbeat only: how many seconds a ping may be late, on top of interval_seconds, before the monitor is down; 0 to 86400.
  - `run_timeout_seconds` (integer; optional): Heartbeat only: how many seconds a run that reports progress may take to reach 100%; 60 to 86400.
  - `ssl_expiry_alerts` (boolean; optional): HTTP with an https URL only: warn by email and chat before the SSL certificate expires.
  - `ssl_alert_days` (integer; optional): HTTP only: days before the certificate expires that the first warning goes out: 1, 3, 7, 14 or 30. Another follows at each of 14, 7, 3 and 1 days left below it.

### pause_monitor

**Pause a monitor.** Pauses a monitor: its checks and alerts stop until it is resumed. Its settings and history are kept, and it still counts toward the plan's monitors. Pausing a paused monitor changes nothing.

- Scope: `monitors:write`
- Annotations: changes monitors, not destructive, idempotent, closed world
- Arguments:
  - `id` (UUID; required): The monitor's id, a UUID. Find it with list_monitors; never guess it.

### resume_monitor

**Resume a monitor.** Resumes a paused monitor: checks and alerts start again. An http or port monitor is checked right away; a heartbeat waits interval_seconds plus grace_seconds for its job's next ping. Resuming an active monitor changes nothing.

- Scope: `monitors:write`
- Annotations: changes monitors, not destructive, idempotent, closed world
- Arguments:
  - `id` (UUID; required): The monitor's id, a UUID. Find it with list_monitors; never guess it.

### delete_monitor

**Delete a monitor.** Deletes a monitor: its checks and alerts stop, and its history is no longer shown. This can't be undone. Ask the user to confirm first, naming the monitor. To stop checks for a while, pause it instead.

- Scope: `monitors:write`
- Annotations: changes monitors, destructive, idempotent, closed world
- Arguments:
  - `id` (UUID; required): The monitor's id, a UUID. Find it with list_monitors; never guess it.

<!-- tools:end -->

## Example conversations

What the model calls, and what the tools answer. Ids and ping URLs differ in your account.

### "Watch my shop's health endpoint every 5 minutes and check it says ok"

```json
{ "name": "create_monitor", "arguments": { "name": "Shop", "url": "https://shop.example.com/health", "interval_seconds": 300, "keyword": "ok" } }
```

```text
Created "Shop" (https://shop.example.com/health), checked every 5 min. First check within a minute. id: 01a1c6de-07ae-72e1-9b05-b184f79ad5ec
```

### "Alert me if the database port is ever open to the internet"

```json
{ "name": "create_monitor", "arguments": { "name": "Database", "type": "port", "host": "db.example.com", "port": 5432, "expect_closed": true } }
```

```text
Created "Database" (db.example.com:5432), checked every 5 min. First check within a minute. id: 01a1c6de-084f-7aef-a79d-2b97f1373433
```

### "Tell me if my nightly backup stops running"

```json
{ "name": "create_monitor", "arguments": { "name": "Nightly backup", "type": "heartbeat", "interval_seconds": 86400 } }
```

```text
Created the heartbeat "Nightly backup": it is down when no ping arrives for 1 day plus 5 min of grace. The job must request https://imsu.io/hb/CsiKMWtrqPBR6x39mVXvFw after each run, e.g. with this crontab line, which runs it every 1 day:
0 3 * * * /path/to/job.sh && curl -fsS -m 10 --retry 3 https://imsu.io/hb/CsiKMWtrqPBR6x39mVXvFw > /dev/null
id: 01a1c6de-086f-7a31-bb4f-1e4ead467e8c
```

The model then shows the user the crontab line. The job can also report a failed run by requesting the ping URL with `/fail` at the end. For `run_timeout_seconds` to apply, the job must report each run's progress: see [heartbeat pings](https://imsu.io/docs/api#heartbeat-pings).

### "Is anything down?"

```json
{ "name": "list_monitors", "arguments": { "status": "down" } }
```

```text
The account has 3 monitors: 2 up, 1 down, 0 unknown, 0 paused. Showing 1:
- "Shop" (https://shop.example.com/health): down, every 5 min, 97.92% up in 24 h. id: 01a1c6de-07ae-72e1-9b05-b184f79ad5ec
```

For the cause, the model calls `get_monitor` with that id and reads `open_incident`.

### "Rename the shop monitor and drop the keyword check"

```json
{ "name": "update_monitor", "arguments": { "id": "01a1c6de-07ae-72e1-9b05-b184f79ad5ec", "name": "Shop health", "keyword": null } }
```

```text
Saved "Shop health" (https://shop.example.com/health), checked every 5 min. id: 01a1c6de-07ae-72e1-9b05-b184f79ad5ec
```

Settings left out keep their values; `"keyword": null` removes the keyword.

### "Pause the shop monitor while we deploy"

```json
{ "name": "pause_monitor", "arguments": { "id": "01a1c6de-07ae-72e1-9b05-b184f79ad5ec" } }
```

```text
Paused "Shop health" (https://shop.example.com/health): no checks or alerts until it is resumed. id: 01a1c6de-07ae-72e1-9b05-b184f79ad5ec
```

Later, `resume_monitor` with the same id: "Resumed "Shop health" (https://shop.example.com/health), checked every 5 min; the first check runs now."

### "Delete the shop monitor"

The model first asks: "Delete "Shop health" (https://shop.example.com/health)? Its history will no longer be shown." Only after the user says yes:

```json
{ "name": "delete_monitor", "arguments": { "id": "01a1c6de-07ae-72e1-9b05-b184f79ad5ec" } }
```

```text
Deleted "Shop health" (https://shop.example.com/health). Its checks and alerts have stopped, and its history is no longer shown.
```
