Skip to content

Connect an assistant

DumbMonit has a built-in MCP server: the Model Context Protocol, the way assistants such as Claude, ChatGPT, Cursor or VS Code’s agent call outside tools. Connect one, then ask it things like:

  • “Is everything fine?”
  • “Silence the NAS for two hours, I’m swapping a disk.”
  • “What happened last night?”
  • “How full is the backup server, and is it getting worse?”
  • “Post an update on the status page: the mail server is back.”

The assistant sees what the interface sees — monitors, alerts, history, metrics, agents, status pages — and uses the same words: reporting, unreachable, waiting, advisory, warning, suppressed by parent. It runs inside the server: nothing to install next to DumbMonit, and the tools call the same code as the web UI.

Settings → API & assistants (/settings#assistant). Name the token after the assistant or the machine it runs on (“Claude on my laptop”), then choose:

SettingChoices
ScopeRead: look — status, monitors, alerts, history, metrics, rules, agents, status pages. It can never change anything. Read and write: also act — silence, acknowledge, probe, add a monitor, restart a container, post an incident… (the write tools).
Expires30 days, 90 days (the default), 1 year or never. An expired token stops working; create a new one.
Allowed networksOptional. Addresses or CIDR ranges the token may be used from (192.168.1.0/24, 10.8.0.5). Empty: anywhere.

The token looks like dmt_ followed by 32 hexadecimal characters. It is shown once, right after creation; DumbMonit only keeps a hash. Lose it, revoke it and create another one.

Prefer a read token unless you actually want the assistant to act.

The same token also opens the REST API to scripts and dashboards — curl -H "Authorization: Bearer dmt_…" https://monit.example.lan/api/targets — where read sees what a viewer sees and write does what an administrator does, accounts, tokens and backups excepted.

The settings page fills these in with your server’s address and the new token. Below, https://monit.example.lan stands for your server.

Terminal window
claude mcp add --transport http dumbmonit https://monit.example.lan/api/mcp \
--header "Authorization: Bearer dmt_…"

Add --scope user to have it in every project, not only the current one.

Claude Desktop starts local servers from its configuration file; mcp-remote bridges it to DumbMonit’s HTTP endpoint (it needs Node.js). DumbMonit authenticates with a bearer header rather than OAuth, so add it through this file rather than the Connectors screen. Settings → Developer → Edit Config, then add to claude_desktop_config.json and restart Claude Desktop:

{
"mcpServers": {
"dumbmonit": {
"command": "npx",
"args": [
"-y", "mcp-remote", "https://monit.example.lan/api/mcp",
"--header", "Authorization:${DUMBMONIT_AUTH}"
],
"env": { "DUMBMONIT_AUTH": "Bearer dmt_…" }
}
}
}

The header goes through an environment variable, with no space after the colon, because some platforms split arguments on spaces. DumbMonit only serves HTTPS, so --allow-http is not needed.

.vscode/mcp.json in a workspace (or the command MCP: Add Server → HTTP):

{
"servers": {
"dumbmonit": {
"type": "http",
"url": "https://monit.example.lan/api/mcp",
"headers": { "Authorization": "Bearer dmt_…" }
}
}
}

.cursor/mcp.json, and the same shape for most clients that support Streamable HTTP servers with custom headers:

{
"mcpServers": {
"dumbmonit": {
"url": "https://monit.example.lan/api/mcp",
"headers": { "Authorization": "Bearer dmt_…" }
}
}
}

A client that only starts local (stdio) servers can use the mcp-remote bridge shown for Claude Desktop.

Settings → Connectors → Create (developer mode). MCP server URL: https://monit.example.lan/api/mcp, authorization header: Bearer dmt_….

ChatGPT connects from OpenAI’s servers, not from your browser: DumbMonit must be reachable from the internet over HTTPS. See the security notes before doing that.

What a client needs to know: the endpoint is https://<server>/api/mcp, the transport is Streamable HTTP, and every request carries Authorization: Bearer dmt_…. A raw call, to check the token and the network path from a terminal:

Terminal window
curl -s https://monit.example.lan/api/mcp \
-H 'Authorization: Bearer dmt_…' \
-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: get_status' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_status","arguments":{},
"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}'

Twenty-seven tools. Every one has a title, a description, an input schema, an output schema and annotations (readOnlyHint, destructiveHint, idempotentHint), so a client can tell which ones only look and ask you before running the others.

ToolScopeDoes
get_statusreadThe bulletin, the answer to “is everything fine?”: monitor counts, firing and building-up alerts, one sentence (“2 advisories, 1 unreachable.”).
list_devicesreadMonitors and services with state, parent and tags; filter by substring or state.
get_devicereadOne monitor: configuration, state, active alerts, 24-hour CPU / memory / fullest-disk summary (availability and latency for a service check).
list_alertsreadActive alerts with severity words, since when, value, suppression, acknowledgement.
alert_historyreadAlert transitions, most recent first (since or hours, optional monitor, limit).
query_metricsreadA MetricsQL range query, at most 60 points per series.
list_silencesreadMaintenance windows and whether they are active now.
list_rulesreadAlert rules with kind, severity, threshold, enabled.
list_device_typesreadThe monitor types this server can monitor, with the options and credentials each one expects — what add_device needs.
list_agentsreadMachines running the agent: OS, agent version, relay mode, site, monitors relayed, last contact, binding.
list_containersreadThe Docker containers of a machine running the agent: state, health, restarts, update available, last command.
list_heartbeatsreadHeartbeat monitors: expected interval, last call, verdict (waiting, on time, missed, reported down). The secret URL is never shown.
list_status_pagesreadStatus pages, published or not, with their public path (/s/<slug>), the monitors they show and their open incidents.
list_channelsreadNotification channels: kind, enabled, last error, last message sent. Never their secrets.
list_packsreadInstalled integration packs: version, monitor type, enabled, monitors using it, errors.
silence_devicewriteA one-off maintenance window on a monitor, starting now (default 1 hour, at most a week).
remove_silencewriteRemoves a maintenance window.
schedule_maintenancewriteA one-off maintenance window at a given date, on one monitor or the whole instance, a week at most.
acknowledge_alertwriteAcknowledges one alert by fingerprint for a number of hours (default 4): reminders pause, the resolution is still notified. hours: 0 lifts it.
dismiss_alertwriteDismisses one alert by fingerprint (“got it”): hidden from the active alerts and silent, resolution included, until the condition clears; a later return alerts again. undo: true restores it.
probe_devicewriteProbes a monitor immediately and reports what was measured.
set_device_enabledwriteEnables or disables monitoring of a monitor.
set_rule_enabledwriteEnables or disables an alert rule.
add_devicewriteAdds a monitor: name, type, address, interval, parent, options, credentials, relay agent.
discover_networkwriteScans a network (CIDR, 4096 addresses at most) for SNMP devices and suggests a profile for each.
restart_containerwriteAsks a machine’s agent to restart one of its containers.
test_channelwriteSends a test message through a notification channel.
post_incidentwriteOpens an incident on a status page, or posts an update to an existing one.

A read token calling a write tool gets a clear refusal as the tool result (isError: true); the assistant explains it and nothing changes. The same goes for anything the tool cannot do — an unknown monitor, an ambiguous name, a window longer than a week: the message says what to fix.

POST /api/mcp implements MCP over the Streamable HTTP transport: JSON-RPC 2.0, one request per POST, answered with plain JSON (no server-sent events). It is stateless: no Mcp-Session-Id is issued, one a client sends is ignored, and GET or DELETE on the endpoint answer 405. Only the tools capability is offered — no resources, prompts, sampling or OAuth.

Two generations of the protocol are served on the same endpoint:

VersionsHow a client speaks it
2026-07-28Stateless. Every request carries io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities in params._meta, and the headers MCP-Protocol-Version, Mcp-Method and, for tools/call, Mcp-Name, which must match the body. Methods: server/discover, tools/list, tools/call. Results carry resultType: "complete" and the server’s identity in _meta (io.modelcontextprotocol/serverInfo); tools/list also carries ttlMs and cacheScope.
2025-11-25, 2025-06-18, 2025-03-26The initialize handshake picks the version (an unknown one gets the newest of these three); notifications/initialized is acknowledged with 202; then ping, tools/list, tools/call.

Errors, besides the 401, 403 and 429 of the token checks, which come before any JSON-RPC:

HTTPJSON-RPC codeWhen
400-32020 (header mismatch)A 2026-07-28 request without MCP-Protocol-Version, Mcp-Method or Mcp-Name, or with values that differ from the body.
400-32022 (unsupported protocol version)A version this server does not speak; error.data lists the supported ones and the requested one.
400-32602 (invalid params)A 2026-07-28 request missing the required _meta fields.
404-32601 (method not found)A 2026-07-28 request for a method this server does not implement.
200-32602An unknown tool, or arguments that is not an object.
-32700, -32600Unreadable JSON; not a JSON-RPC 2.0 request (batches are not supported).
202—A notification: accepted, no body.

A tool that runs but fails — missing scope, unknown monitor — is not a protocol error: it answers normally with isError: true and a sentence the assistant can relay.

  • A token is a password. Anyone holding it reads everything the interface shows — and, with a write token, changes anything an administrator can through the REST API (monitors, rules, channels), accounts, tokens and backups excepted. Keep it out of shared chats, screenshots and repositories. Revoke a token you are not sure about; create another one in a minute.
  • Prefer read. Most questions (“is everything fine?”) need no write scope. Create a write token only for an assistant you actually want to act, and name it so you recognise it in the list. Most clients ask before running a tool that is not marked read-only; leave that confirmation on.
  • Give it a lifetime and a network. A token that expires in 90 days and only works from your LAN or VPN (192.168.1.0/24) is worth little to anyone who finds it. Behind a reverse proxy, declare the proxy in DUMBMONIT_TRUSTED_PROXIES, otherwise every call seems to come from the proxy and a network restriction refuses them all.
  • Bound to its creator. A token stops working when the account that created it is disabled, loses its write power when that account is no longer an admin, and is revoked when that account is deleted.
  • Nothing secret comes back. The tools never return monitor credentials (only their kind), channel secrets, heartbeat URLs or tokens. add_device does take credentials in: they travel through your assistant’s conversation, and so through its provider. For a monitor with sensitive credentials, prefer the web interface.
  • Browsers and Origin. A request that carries an Origin header (a browser-based client) is refused with 403 unless that origin is the server’s own or is allowed by DUMBMONIT_API_CORS_ORIGINS (by default any origin, since the token is required anyway). List your origins there to narrow it; see cross-origin requests.
  • Rate limit. Each token is limited to 120 calls per minute, REST and MCP together: an assistant stuck in a loop gets 429 with Retry-After, not your server’s full attention.
  • HTTPS before the internet. ChatGPT’s connectors need a public URL. Put DumbMonit behind a reverse proxy with a public certificate (Caddy, Traefik, nginx…) that talks HTTPS to the container. Claude Desktop, Claude Code, VS Code and Cursor run on your machine and can reach a LAN address directly, so they need no exposure at all.
  • Watch “last used”. The token list shows when each token was last used and from which address. A token used at a time you were not talking to your assistant, or from an address you do not know, deserves a revocation.
  • Logs. Every tool call is logged at info level with the token name and the tool, never the arguments; every write through the REST API with the token name, the method, the path and the status, never the body. Creating and revoking tokens is in the audit log (Settings → Security).
SymptomCause and fix
401 / “A valid API token is required.”The header is missing or mistyped (Authorization: Bearer dmt_…, one space after Bearer), or the token was revoked. Create a new one.
401 / “The API token ”…” expired on …”Create a new token; pick a longer lifetime if it was too short.
403 / “cannot be used from …”The token is restricted to other networks. The address in the message is the one the server sees: behind a reverse proxy, declare the proxy in DUMBMONIT_TRUSTED_PROXIES.
403 on every call from a browser-based clientIts Origin is not allowed: add it to DUMBMONIT_API_CORS_ORIGINS.
A tool answers “needs a token with the “write” scope”The token is read-only, or its creator is no longer an administrator. Create a write token if you want the assistant to act.
429120 calls in a minute: the assistant is probably looping. Wait for Retry-After, then ask a narrower question.
“Unsupported protocol version”The client speaks a version DumbMonit does not; update the client.
Claude Desktop shows the server as failedNode.js must be installed for npx; the URL must start with https://.
ChatGPT cannot connectIt needs a public HTTPS URL; a LAN address does not work from OpenAI’s servers.