MCP Server
Warden serves a Model Context Protocol endpoint, so an AI assistant can answer questions about your monitoring: what is down, what happened overnight, why a monitor failed, and whether latency was getting worse before the outage. With an editor key, the same assistant can also create and organise monitors.
The endpoint is at /api/mcp and speaks Streamable HTTP. It sits behind the same authentication as the rest of the API, and what it offers depends on the role of the key you connect with.
Connect an AI assistant
Section titled “Connect an AI assistant”- Create an API key in Settings → API Keys with the role
viewer. - Replace
warden.example.comwith your Warden hostname andsk_live_...with that key. Keep the real key out of shared commands, screenshots and repositories. - For Claude Code, run:
claude mcp add --transport http warden https://warden.example.com/api/mcp \ --header "Authorization: Bearer sk_live_..."- Ask the assistant to list your monitors to verify the connection.
Then ask questions like “what is down right now”, “what happened to the checkout API yesterday”, or “did anything fail while I was asleep”.
For other clients, point them at the same URL with the same Authorization header.
The key’s role decides what the assistant can do
Section titled “The key’s role decides what the assistant can do”The tool set follows the API key. A viewer key is offered only the read and diagnostic tools; the write tools are not advertised and calling one by name returns unknown tool. An editor key adds the five management tools below.
That boundary is enforced by the server, not by asking the model nicely. Give out a viewer key unless you actually want things created.
Reading
Section titled “Reading”| Tool | Answers |
|---|---|
list_monitors | What is down right now. Takes an optional status (up, down, degraded, paused). |
get_monitor | How one monitor is doing: status, uptime over 24h/7d/30d, recent events. |
list_incidents | What happened over a period, and whether several monitors failed together. |
get_monitor_events | Why a monitor failed: the individual check events with the error each one returned. |
Diagnosing
Section titled “Diagnosing”| Tool | Answers |
|---|---|
get_monitor_latency | Was it degrading before it fell over, or did it drop suddenly? |
get_notification_config | Why did this outage not produce an alert? |
list_ssl_warnings | Which certificates are about to expire? |
check_now | Did my fix work? Runs the check immediately instead of waiting for the interval. |
get_notification_config reports the sustained-alert ladder (alertAfterSeconds, reminderMinutes, repeatReminderMinutes) alongside the toggles, because “it was down for 90 seconds” is now the most common answer to “why was I not told”. digestEvents lists what the daily summary covers and no longer suppresses the immediate alert — those are separate decisions. It also reports the correlation thresholds, the repeat-offender limit and which monitors have their alerts muted, since any of those can be the reason a real outage never reached you. It never returns webhook URLs.
get_monitor_latency returns the monitor’s learned baseline (baselineP50Ms, baselineP95Ms) and the line it must cross to count as slow (degradedAboveMs) alongside the samples. Without them a raw number of milliseconds cannot be judged — 650ms is unremarkable for one target and a two-and-a-half-times regression for another. The fields are omitted for a monitor that has not built a baseline yet.
See Adaptive Latency Baselines for the formula, learning period, threshold precedence, and guidance for migrations between networks or regions.
list_insights returns the patterns Warden has found over the last 14 days — latency that climbs and resets, trouble clustered at one time of day, monitors that fail together, week-over-week slowdowns. It answers what is quietly wrong rather than what is broken right now, and an empty result means nothing stood out rather than an error. See Patterns.
Writing (editor key only)
Section titled “Writing (editor key only)”| Tool | Does |
|---|---|
create_monitors | Creates monitors from a list of targets, in one call. |
create_group | Creates a group to organise monitors into. |
rename_group | Renames a group. |
move_monitor | Moves a monitor into another group, keeping its history. |
set_monitor_paused | Stops or restarts checking a monitor, keeping its history. |
move_monitor takes a name or an id for both the monitor and the destination group, and reports moved: false when the monitor is already there rather than claiming a change it did not make. Unlike creation, it will not fall back to the Default group: a move with no destination is an error, because guessing one would silently regroup a monitor nobody asked to touch.
Deleting is deliberately not offered. Removing monitors takes their history with them, and it is not something worth doing on a model’s initiative; use the dashboard.
Handing it a list of domains
Section titled “Handing it a list of domains”create_monitors takes the whole list at once rather than making the model call it once per domain, which means one round trip and a clear report at the end:
Create monitors for example.com, example.org and example.net in a group called Clients
Only url is required per entry. The name defaults to the url, the type to http, the group to Default, and the interval to 60 seconds.
Pass type to create a tcp, ping or dns monitor. The target format follows the type, the same as it does in the dashboard: host:port for tcp, a hostname for ping and dns. See Monitor Types.
A bad entry does not sink the batch. Each one comes back with its own result, so a list with one typo in it creates everything else and tells you what to fix:
{ "createdCount": 2, "failedCount": 1, "results": [ { "url": "https://example.com", "id": "m-example-a1b2c3", "created": true }, { "url": "not-a-url", "created": false, "error": "invalid monitor: invalid URL format" }, { "url": "https://example.net", "id": "m-example-d4e5f6", "created": true } ]}The rules are the same ones the dashboard enforces, because both go through the same code: valid http or https URL, an interval of at least 10 seconds, an existing group, and no duplicate names.
Anywhere a tool takes a monitor, it accepts the name or the id. “Production API” works as well as m-production-api-a1b2c3.
Calls are bounded so a broad question cannot drag the whole database into the conversation: 200 events, 100 outages (the result says when it was cut), and 100 monitors created per call.
Each request is judged on its own credentials. The endpoint is stateless, so there is no session whose privileges a later request could inherit.
What leaves your server
Section titled “What leaves your server”Worth knowing before you enable this, because the answers go to a model.
get_monitor_events returns the response body your monitored target returned when a check failed, up to 2KB, along with a filtered set of its headers. That is what makes it useful for diagnosis: an error like {"error":"upstream_unavailable","detail":"payments-db pool exhausted"} is the answer. It also means that if one of your targets puts something sensitive in an error body, it is included. The header allowlist already drops cookies and authorization headers, but bodies are passed through as stored.
Everything else is monitoring metadata: names, targets, statuses, latencies, uptime percentages, outage times and your notification thresholds.
Deliberately never returned:
- Webhook URLs, including the Slack one, even though
get_notification_configreports the channels - Users, sessions, passwords and API keys
- Status page configuration and SSO settings
If a target of yours returns sensitive data on failure and that is a problem, either keep the MCP endpoint off with MCP_ENABLED=false or do not point an assistant at that instance.
Keeping it safe
Section titled “Keeping it safe”Use a viewer key unless you are creating things
Section titled “Use a viewer key unless you are creating things”Failed response bodies can contain instructions written by whoever controls the monitored target. Treat those responses as untrusted data.
Use a viewer key for diagnosis: the server does not expose write tools to it. Use a separate editor key only when you want the assistant to change monitoring configuration, and supervise those changes.
Give each client its own key
Section titled “Give each client its own key”Create one key per machine or client and name it accordingly. Settings shows when each key was last used, so an unexpected timestamp is a signal, and you can revoke one without breaking the others.
Keep the key out of your repository
Section titled “Keep the key out of your repository”claude mcp add defaults to local scope, which is what you want. Avoid --scope project: that writes the server definition to a .mcp.json meant to be checked in, and your key with it.
claude mcp add --transport http warden https://warden.example.com/api/mcp \ --header "Authorization: Bearer sk_live_..."The rest
Section titled “The rest”Serve it over HTTPS only; the key is a bearer token and travels on every request. The endpoint inherits the API’s rate limit of 100 requests per second per IP. If the assistant does not need to reach a given instance, MCP_ENABLED=false removes the endpoint entirely, and a reverse proxy can restrict /api/mcp by source address if you want a second lock.
Cross-origin requests are rejected
Section titled “Cross-origin requests are rejected”The endpoint refuses any request carrying an Origin header from another host, with 403. MCP clients are not browsers and send no Origin, so this costs them nothing.
It matters because this endpoint also accepts the dashboard’s session cookie: without the check, a page you visit could drive your monitoring as you. The MCP specification requires the same check to prevent DNS rebinding.
Turning it off
Section titled “Turning it off”The endpoint is behind authentication, but if you would rather not serve it at all:
MCP_ENABLED=falseChecking it works
Section titled “Checking it works”curl -X POST https://warden.example.com/api/mcp \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'A working server answers with its capabilities and an Mcp-Session-Id header. unauthorized means the key is missing or wrong; a 404 means MCP_ENABLED is false.
HTTP check evidence
Section titled “HTTP check evidence”The viewer tool get_monitor_checks exposes paginated stored checks and the same 24-hour summary as the Checks tab. See HTTP diagnostics for trace coverage, retry attempts, phase timings and second-probe limitations.
What it cannot do
Section titled “What it cannot do”With a viewer key it only reads and runs immediate checks. With an editor key it can also create and organise monitors, and pause or resume them. It never offers monitor deletion, user or credential management, notification configuration, status-page configuration, incident editing, or autonomous remediation.
MCP responds when you ask an assistant to use it; it does not initiate a conversation when something breaks. For proactive delivery, configure notifications through Slack, webhooks or email.