mcpbeat Sign in

Fettle MCP Server

answering

Fettle is answering right now. Last checked 6 min ago. 375 installs a week from npm. It exposes 49 tools. Last commit 14 Oct 2026.

Uptime monitoring for developers — monitors, incidents, heartbeats, maintenance, status pages

Installs per day peak 11 · avg 3 · +8% w/w
a month agotoday
Uptime history 53 days of history · worst day 98%
53 days agonow
100.0%
Uptime 24h
92 of 92 checks
49
Tools
read from the server
209 ms
Response time
average over 24h
375
Installs / week
npm and PyPI

What the code does

We read the source, 22 h ago · tools taken from the live server · rules ccca72095570

Capabilities

What this server is able to do. For an MCP server this is often the job itself — a terminal server runs commands because that is what it is for. Listed so you know what you are plugging in, not as an accusation.

Package points at a different repository fettlesh/monitoring-mcp
пакет fettle-mcp ссылается на darkweid/monitoring-mcp

Is this your server and something here is wrong? Tell us — corrections are free and do not require a plan.

This code can reach further than it looks

We found places where it runs commands, builds paths or queries from values it is given. None of that is a flaw by itself — it becomes one when the code changes, and code changes quietly between releases. We re-read it on every one.

Three servers free · no card

Connect this server

Endpoint below is the one we actually reach during checks — not the one copied from a README. Last verified 6 min ago.

run in your terminal
claude mcp add mcp --transport http https://api.fettle.sh/mcp
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "mcp": {
      "url": "https://api.fettle.sh/mcp"
    }
  }
}
~/.codex/config.toml
[mcp_servers.mcp]
url = "https://api.fettle.sh/mcp"
.cursor/mcp.json
{
  "mcpServers": {
    "mcp": {
      "url": "https://api.fettle.sh/mcp"
    }
  }
}
.vscode/mcp.json
{
  "mcpServers": {
    "mcp": {
      "url": "https://api.fettle.sh/mcp"
    }
  }
}

This one needs environment variables set before it will start: FETTLE_API_KEY (Fettle API key (Settings → API Keys at https://fettle.sh). Read scope lists and inspects; write scope is needed to create, change or pause anything.), FETTLE_API_URL (Base URL of the Fettle API. Only set this to point the server at something other than production.), FETTLE_TIMEOUT_MS (How long one tool call may take before it is abandoned.). The author declared them in the registry entry; get the values from the project itself.

Available tools 49

Read directly from the server with tools/list, grouped by what they act on. If a tool disappears, we record the date.

status
create_status_page_incident
Open an incident on a status page with a first message. This is public and emails the page's confirmed subscribers (details below): confirm the wording with the user before calling. Requires a key with the write scope. page_id (uuid, required) is the page's id from list_status_pages; an id that is not a status page of this organization is a 404. Required: title (1 to 255 characters) and body (the first message, 1 to 5000 characters). status is INVESTIGATING, IDENTIFIED, MONITORING or RESOLVED; RESOLVED closes the incident on the page. It defaults to INVESTIGATING. Any status but RESOLVED emails every confirmed subscriber of the page at once; RESOLVED records an already closed incident (a postmortem) and emails nobody. component_ids (optional array of component uuids from get_status_page) marks the affected components; an id that is not a component of this page is a 404. Returns the incident. Example: {"page_id": "<id from list_status_pages>", "title": "API errors", "body": "We are investigating elevated error rates."}
delete_status_page_incident
Remove an incident, with its timeline, from a status page; it stops showing at once and no tool restores it. Nothing is sent to subscribers. To close an incident instead, use post_status_page_update with RESOLVED. Requires a key with the write scope. page_id (uuid, required) is the page's id from list_status_pages; an id that is not a status page of this organization is a 404. incident_id (uuid, required) is from list_status_page_incidents.
get_status_page
Return one status page in the list_status_pages shape, including its groups and ungrouped_components; each component has an id (the component_ids of a status page incident are these), a monitor_id, display_name and position. page_id (uuid, required) is the page's id from list_status_pages; an id that is not a status page of this organization is a 404.
list_status_page_incidents
List the incidents on a status page, newest first, both those posted by a person and those Fettle opened itself: each with id, title, status, is_auto (opened by Fettle from a monitor rather than by a person), started_at, resolved_at, notified_at (when subscribers were first emailed; null if they were not), component_ids and updates (the timeline, oldest first, each with id, status, body, created_by and created_at). The result is a page: items plus total, page, size and pages. page_id (uuid, required) is the page's id from list_status_pages; an id that is not a status page of this organization is a 404. Optional: page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25).
list_status_pages
List the organization's status pages: each with id, slug, name, description, is_published (whether it is publicly visible now), history_days, created_at, groups (each with name, position and its components) and ungrouped_components (components outside any group). The result is a page: items plus total, page, size and pages. All arguments are optional. page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25) select the page. sort is created_at (the default, newest first) or name (alphabetical).
update_status_page_incident
Change a status page incident's title; nothing is sent to subscribers. To change its status, use post_status_page_update. Requires a key with the write scope. page_id (uuid, required) is the page's id from list_status_pages; an id that is not a status page of this organization is a 404. incident_id (uuid, required) is from list_status_page_incidents. title (1 to 255 characters, required). Returns the incident. Example: {"page_id": "<id from list_status_pages>", "incident_id": "<id from list_status_page_incidents>", "title": "API errors in eu-central"}
bulk
bulk_create_monitors
Create 1 to 100 monitors in one call: pass monitors, a list whose items take the same fields and follow the same rules as create_monitor. Requires a key with the write scope. All are created or none is: an invalid item, or a batch that would exceed the plan's limits, rejects the whole call and nothing is created. On success it returns monitors, one short row per created monitor (the list_monitors row shape) in request order. Errors name the failing item in one of two ways. A field that fails input validation is named by its path, for example monitors.3.target (index 3 is the fourth item; every such field error is reported at once). Any other item error keeps the status create_monitor would give (403 when its interval or regions break the plan or name an unknown region, 404 for an unknown channel id, 422 for its config or its type's region rules) and its reason starts with monitors[3] ("<item name>"): — only the first such item is reported. Read the index only from that leading monitors[N] (the pattern ^monitors\[(\d+)\] once any lead-in such as "Forbidden: " or "Invalid request: " is set aside), never from the name, which may itself contain quotes or brackets. A plan-limit 403 without a monitors[N] prefix is about the batch as a whole: together the items would exceed the plan's monitor count or its quota of monitors below 60 seconds. Items with is_active false are created paused and still count toward those limits. Fix the named item, or shrink the batch, and send the whole call again. Call get_plan_usage first to check the batch fits. Example: {"monitors": [{"name": "db.example.com exposed ports", "type": "TCP", "target": "db.example.com", "interval_seconds": 3600, "timeout_seconds": 10, "regions": ["eu-central"], "config": {"ports": [5432, 6379, 27017], "expected_state": "closed"}}]}
bulk_pause_monitors
Pause up to 100 monitors in one call, all or nothing: no checks run and each open incident closes without an alert. Use it to stop monitoring a whole project or group (find ids with list_monitors and its filters); to silence alerts around a deploy, prefer create_maintenance_window. Requires a key with the write scope. monitor_ids (array of 1 to 100 distinct monitor uuids, required); one id that is not a monitor of this organization is a 404 naming it and nothing changes; a repeated id is a 422. Pausing a paused monitor is not an error. Returns monitors: the short rows after the change (the list_monitors row shape), in request order. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"]}
bulk_resume_monitors
Resume up to 100 paused monitors in one call, all or nothing; each goes back to PENDING and is checked on the next tick. Requires a key with the write scope. monitor_ids (array of 1 to 100 distinct monitor uuids, required); an unknown id is a 404 naming it, a repeated id a 422. Resuming more monitors than the plan's active limit allows is a 403 and nothing changes (see get_plan_usage). A monitor that is already running is left as it is. Returns monitors: the short rows after the change (the list_monitors row shape), in request order. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"]}
bulk_update_monitor_tags
Add and/or remove tags across up to 300 monitors in one call — the way to retag a whole fleet without one request per monitor. monitor_ids (array of 1 to 300 distinct monitor uuids, required; a repeated id is a 422) names the monitors; every one must belong to the organization: an unknown id rejects the whole request with a 404 naming every id it could not find. add and remove (arrays of up to 10 tags each, optional) apply as (current tags | add) - remove per monitor. A tag listed in both add and remove is rejected with a 422, not silently resolved — the two lists must be disjoint. At least one of add or remove is required. A monitor that would end up with more than 10 tags rejects the whole request (422); nothing is changed when any check fails. Returns monitors_updated. Tags are trimmed, lowercased and deduplicated on write, each at most 50 characters of lowercase letters, digits and : _ . / - , starting with a letter or digit. Call list_tags first to reuse the organization's existing names instead of inventing near-duplicates. Requires a key with the write scope. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"], "add": ["env:prod"], "remove": ["env:staging"]}
bulk_update_monitors
Set the same values on up to 100 monitors in one call, all or nothing, for example move twenty PING monitors to other regions or a slower interval. Requires a key with the write scope. monitor_ids (array of 1 to 100 distinct monitor uuids, required; an unknown id is a 404 naming it, a repeated id a 422). changes (object, required) holds one or more of: interval_seconds (30 to 86400), timeout_seconds (1 to 120), regions (list of region codes; see list_regions), channel_ids (array of channel uuids from list_channels; replaces each monitor's binding, [] clears it, an unknown channel is a 404), confirmation_threshold (1 to 10) and notification_cooldown_seconds (60 to 86400). Omit a field to keep it; never send null. config, name and tags are not here: use update_monitor or bulk_update_monitor_tags. The plan's rules apply to the result: an interval below the plan minimum, too many fast-interval monitors, more regions than allowed or an unknown region is a 403. A value one monitor cannot take (an SSL or DNS monitor with more than one region; a port-list TCP monitor with several regions or an interval under 300) is a 422 that starts with monitors[<i>] ("<name>"). Returns monitors: short rows (id, name, type, target, status, is_active, tags, interval_seconds, ssl_expires_at, last_checked_at), so they show the interval but not the other changed settings; get_monitor gives a monitor's full settings. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"], "changes": {"regions": ["eu-central", "us-east"], "interval_seconds": 300}}
maintenance
create_maintenance_window
Create a maintenance window that silences alerts for a time. Requires a key with the write scope. Required: name (1 to 255 characters), scope, dtstart and duration_minutes. enabled (boolean, default true): false stores it without effect. During a window the covered monitors keep being checked, but no incident opens and no alert goes out; use a window rather than pausing monitors around a deploy or reboot. scope is ORG (every monitor of the organization, monitor_ids ignored) or MONITOR (only monitor_ids, a list of monitor uuids from list_monitors, at least one; an id that is not a monitor of this organization is a 404). dtstart is the first start as an ISO 8601 date-time in local wall-clock time of timezone, for example 2026-10-14T22:00:00; a UTC offset or Z is a 422. duration_minutes (integer, 1 to 40320) is how long each occurrence lasts. rrule (at most 1024 characters) repeats it, as a single iCal RRULE without the RRULE: prefix, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3; hourly, minutely and secondly frequencies are refused. timezone (IANA name such as Europe/Berlin, at most 64 characters) is the zone the schedule repeats in, so a weekly 03:00 stays 03:00 across daylight-saving changes. Call preview_maintenance_window first to check a recurring schedule. Omit rrule for a one-time window. When timezone is omitted the organization's timezone is used. The plan caps how many windows an organization may have (403 past it or when the plan has none; see max_maintenance_windows in get_plan_usage). Other errors: 422 for an invalid argument or schedule, naming it. Returns the window. Example: {"name": "Deploy", "scope": "MONITOR", "monitor_ids": ["<id from list_monitors>"], "dtstart": "2026-10-14T22:00:00", "duration_minutes": 30}
delete_maintenance_window
Delete a maintenance window; alerts for its monitors resume at once. This cannot be undone — to suspend it, use update_maintenance_window with enabled false. Requires a key with the write scope. window_id (uuid, required) is the window's id from list_maintenance_windows; an id that is not a window of this organization is a 404.
get_maintenance_window
Return one maintenance window, in the list_maintenance_windows shape plus next_occurrences (the next three, each with start and end). window_id (uuid, required) is the window's id from list_maintenance_windows; an id that is not a window of this organization is a 404.
list_maintenance_windows
List the organization's maintenance windows: each with id, name, enabled, scope (ORG or MONITOR), dtstart, duration_minutes, rrule, timezone, monitor_ids and created_at (get_maintenance_window adds the upcoming occurrences). The result is a page: items plus total, page, size and pages. All arguments are optional. page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25) select the page. q (string, up to 255 characters) matches a case-insensitive substring of the name. sort is created_at (the default, newest first) or name (alphabetical).
update_maintenance_window
Change a maintenance window; only the fields you pass change (omit a field to keep it; a recurring window cannot be turned into a one-time one — delete it and create a new one). monitor_ids replaces the list. Requires a key with the write scope. window_id (uuid, required) is the window's id from list_maintenance_windows; an id that is not a window of this organization is a 404. Fields: name, enabled, scope, dtstart, duration_minutes, rrule, timezone, monitor_ids. During a window the covered monitors keep being checked, but no incident opens and no alert goes out; use a window rather than pausing monitors around a deploy or reboot. scope is ORG (every monitor of the organization, monitor_ids ignored) or MONITOR (only monitor_ids, a list of monitor uuids from list_monitors, at least one; an id that is not a monitor of this organization is a 404). dtstart is the first start as an ISO 8601 date-time in local wall-clock time of timezone, for example 2026-10-14T22:00:00; a UTC offset or Z is a 422. duration_minutes (integer, 1 to 40320) is how long each occurrence lasts. rrule (at most 1024 characters) repeats it, as a single iCal RRULE without the RRULE: prefix, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3; hourly, minutely and secondly frequencies are refused. timezone (IANA name such as Europe/Berlin, at most 64 characters) is the zone the schedule repeats in, so a weekly 03:00 stays 03:00 across daylight-saving changes. Call preview_maintenance_window first to check a recurring schedule. Other errors: 422 for an invalid argument, 406 for a resulting schedule that is invalid (for example a bad rrule), each with the reason. Returns the window. Example: {"window_id": "<id from list_maintenance_windows>", "enabled": false}
heartbeat
create_heartbeat
Create a heartbeat monitor and get back ping_url, the URL a scheduled job should call (GET or POST) each time it succeeds. Requires a key with the write scope; the plan caps how many heartbeats an organization may have (403 past it, see get_plan_usage). Required: name (1 to 255 characters), period_seconds and grace_seconds. It is never reported late before its first successful ping (a /fail call still marks it DOWN). After that, an alert fires period_seconds + grace_seconds after the last ping (a success ping, or a call to ping_url + "/start" when the job begins, restarts that clock; ping_url + "/fail" marks it DOWN at once). To ping every N seconds and be alerted after T seconds of silence, set period_seconds = N and grace_seconds = T - N (T at least N; period_seconds 60 to 2592000, grace_seconds 0 to 2592000, and grace may exceed the period). notification_cooldown_seconds (integer, 60 to 86400, optional; 1800 when omitted) only holds back a duplicate first alert for the same incident within that window. Repeat reminders while the incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (see get_plan_usage; 0 means no reminders), not this value; the recovery alert is never held back by it. channel_ids (array of channel uuids, optional) binds notification channels; omit it or pass an empty list to alert every verified, active channel; an id that is not an active channel of this organization is a 404; list_channels gives the ids. tags is an optional list of flat labels (for example env:prod, service:api) used to group and filter heartbeats, at most 10 per heartbeat. Tags are trimmed, lowercased and deduplicated on write, each at most 50 characters of lowercase letters, digits and : _ . / - , starting with a letter or digit. Call list_tags first to reuse the organization's existing names instead of inventing near-duplicates. Other errors: 422 for an invalid argument, naming it. Example: {"name": "nightly backup", "period_seconds": 86400, "grace_seconds": 3600}
delete_heartbeat
Delete a heartbeat for good; its ping_url stops working (404). This cannot be undone — to stop alerts for a while, use pause_heartbeat instead. Requires a key with the write scope. heartbeat_id (uuid, required) is the heartbeat's id from list_heartbeats; an id that is not a heartbeat of this organization (or a deleted one) is a 404.
get_heartbeat
Return one heartbeat: name, state, period_seconds, grace_seconds, ping_url, last_ping_at, last_success_at, last_start_at, last_duration_ms, overdue_at (when it will be reported late), notification_cooldown_seconds, is_active, channel_ids and tags. heartbeat_id (uuid, required) is the heartbeat's id from list_heartbeats; an id that is not a heartbeat of this organization (or a deleted one) is a 404.
update_heartbeat
Change a heartbeat; only the fields you pass change (omit a field to keep it). Requires a key with the write scope. heartbeat_id (uuid, required) is the heartbeat's id from list_heartbeats; an id that is not a heartbeat of this organization (or a deleted one) is a 404. name (1 to 255 characters). period_seconds (60 to 2592000) and grace_seconds (0 to 2592000): an alert fires period_seconds + grace_seconds after the last ping. A new period or grace applies from the next ping; the current deadline (overdue_at) is not recomputed. notification_cooldown_seconds (60 to 86400) only holds back a duplicate first alert. channel_ids (array of channel uuids) replaces the binding; [] clears it, which alerts every verified, active channel; an id that is not an active channel of this organization is a 404 (list_channels gives the ids). tags replaces the whole tag list, at most 10; [] clears it. Tags are trimmed, lowercased and deduplicated on write, each at most 50 characters of lowercase letters, digits and : _ . / - , starting with a letter or digit. Call list_tags first to reuse the organization's existing names instead of inventing near-duplicates. Other errors: 422 for an invalid argument, naming it. Returns the heartbeat. Example: {"heartbeat_id": "<id from list_heartbeats>", "grace_seconds": 7200}
monitor
create_monitor
Create a monitor that checks a URL, host or port on a schedule and return it (the get_monitor shape). Requires a key with the write scope. Required: name (1 to 255 characters), type (HTTP, PING, SSL, TCP or DNS), target, interval_seconds, timeout_seconds and regions. target (up to 2048 characters): for HTTP an http:// or https:// URL (https:// is added when the scheme is missing; port, path and query are kept); for PING, SSL, TCP and DNS a bare public hostname or IP address with no :port and no path (the TCP or SSL port goes in config; host:port is a 422, while a full URL such as https://host:8443/x is cut down to its host and its port is lost). Hostnames must be ASCII (punycode for internationalized domains). localhost, *.localhost and IP addresses that are not public (private, loopback, link-local, CGNAT 100.64.0.0/10, reserved or unspecified) are rejected with a 422. type and target cannot be changed later. config is a JSON object whose shape depends on type. HTTP takes method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET), expected_status_codes (a non-empty list of codes 100 to 599, default [200]), keyword (case-sensitive substring the response body must contain, at most 1024 characters; not allowed with HEAD), follow_redirects (default true), verify_ssl (default true; false skips TLS verification, which also means that monitor can no longer report a TLS_INVALID failure), request_headers (an object of header name to value: at most 20 headers; a name is at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~; a value is at most 1024 bytes of printable ASCII, space or tab; the whole object at most 8192 bytes as JSON; two names differing only in case, and the reserved names Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP and any Proxy-* name, are a 422) and request_body (a string of at most 8192 bytes as UTF-8; requires POST, PUT, PATCH or DELETE), all optional. Reads never return request_header values: each one comes back as the literal __FETTLE_UNCHANGED__, and writing that value back in an update keeps the stored header unchanged (on create it is a 422). PING takes packet_count (1 to 20, default 4), optional. SSL takes warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443), both optional. TCP takes either port (one port, 1 to 65535, expected_state "open" by default or "closed") or ports — a list of 1 to 20 distinct ports that must all be closed: ports requires expected_state "closed", exactly one region and interval_seconds of at least 300, and a check fails when any listed port accepts a connection (the monitor goes DOWN once confirmation_threshold failures confirm it). Passing both port and ports, or neither, is a 422. expected_state values are lowercase. DNS requires record_type (A, AAAA, CNAME, MX, TXT or NS; no default) and takes optional expected_value (at most 2048 characters): when set, one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); when absent, any answer passes. For MX the answers are the mail host names only, without priority (mail.example.com, not 10 mail.example.com). A and AAAA both look up every address of the host, IPv4 and IPv6 alike, and any one of them may match; write an IPv6 address in its shortest form. Omit config for HTTP, PING or SSL to use their defaults; omitting it on a TCP or DNS monitor is a 422. regions is a list of one or more distinct region codes (get_plan_usage lists them in limits.allowed_regions, for example eu-central; an unknown code is a 403) — SSL and DNS monitors must target exactly one region (422 otherwise). interval_seconds (integer, 30 to 86400) is the time between checks; timeout_seconds (integer, 1 to 120) is how long one check may take. confirmation_threshold (integer, 1 to 10; 3 when not set on create) is how many checks in a row must fail in every region before the monitor goes DOWN and an incident opens. notification_cooldown_seconds (integer, 60 to 86400; 1800 when not set on create) only holds back a duplicate first alert for the same incident within that window. Repeat reminders while the incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (see get_plan_usage; 0 means no reminders), not this value; it never holds back the recovery alert. Plan limits apply: monitor count, minimum interval_seconds, how many monitors may use an interval below 60 seconds, and max_regions per monitor. Breaking one is a 403 that names the limit (more regions than the plan allows is a 403 too; SSL/DNS with more than one region is a 422). get_plan_usage shows the limits and current usage. is_active (boolean, default true): false creates it paused — no check runs until resume_monitor; a paused monitor still counts toward the plan's monitor limits. channel_ids (array of channel uuids) binds notification channels; omit it or pass an empty list to alert every verified, active channel of the organization; an id that is not an active channel of this organization is a 404. list_channels gives the ids. tags is an optional list of flat labels (for example env:prod, service:api) used to group and filter monitors, at most 10 per monitor. Tags are trimmed, lowercased and deduplicated on write, each at most 50 characters of lowercase letters, digits and : _ . / - , starting with a letter or digit. Call list_tags first to reuse the organization's existing names instead of inventing near-duplicates. Other errors: 422 for an invalid argument, naming it. To create many monitors at once, use bulk_create_monitors. Example: {"name": "API", "type": "HTTP", "target": "https://api.example.com/health", "interval_seconds": 300, "timeout_seconds": 10, "regions": ["eu-central"], "config": {"expected_status_codes": [200]}, "tags": ["env:prod"]}
delete_monitor
Soft-delete a monitor: it stops being checked and no longer appears in the organization's monitor list. Prefer pause_monitor unless the user explicitly asked for deletion. Requires the write scope. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404.
get_monitor
Read one monitor's full configuration and current state: type, target, interval_seconds, timeout_seconds, regions, config, status, is_active, confirmation_threshold, notification_cooldown_seconds, channel_ids (empty means every verified, active channel is alerted), tags, badge_enabled, last_checked_at, next_run_at, and ssl_expires_at and ssl_cert_info for SSL monitors. For uptime figures use get_uptime_summary. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404.
update_monitor
Change an existing monitor's settings and return it (the get_monitor shape); its type and target cannot change (sending either is a 422). Requires a key with the write scope. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404. Every other field is optional: name (1 to 255 characters), interval_seconds, timeout_seconds, regions, config, confirmation_threshold, notification_cooldown_seconds, channel_ids, badge_enabled and tags. Only the fields you pass are modified: omit a field to leave it unchanged, and never send null. At least one field besides monitor_id is required (422 otherwise). config is the exception to partial updates: passing it replaces the monitor's entire config, not just the keys you include — any field of that type's schema you leave out is reset to its default. Passing only port on a TCP monitor whose expected_state is closed silently resets expected_state to open unless you include it too. config is a JSON object whose shape depends on type. HTTP takes method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET), expected_status_codes (a non-empty list of codes 100 to 599, default [200]), keyword (case-sensitive substring the response body must contain, at most 1024 characters; not allowed with HEAD), follow_redirects (default true), verify_ssl (default true; false skips TLS verification, which also means that monitor can no longer report a TLS_INVALID failure), request_headers (an object of header name to value: at most 20 headers; a name is at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~; a value is at most 1024 bytes of printable ASCII, space or tab; the whole object at most 8192 bytes as JSON; two names differing only in case, and the reserved names Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP and any Proxy-* name, are a 422) and request_body (a string of at most 8192 bytes as UTF-8; requires POST, PUT, PATCH or DELETE), all optional. Reads never return request_header values: each one comes back as the literal __FETTLE_UNCHANGED__, and writing that value back in an update keeps the stored header unchanged (on create it is a 422). PING takes packet_count (1 to 20, default 4), optional. SSL takes warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443), both optional. TCP takes either port (one port, 1 to 65535, expected_state "open" by default or "closed") or ports — a list of 1 to 20 distinct ports that must all be closed: ports requires expected_state "closed", exactly one region and interval_seconds of at least 300, and a check fails when any listed port accepts a connection (the monitor goes DOWN once confirmation_threshold failures confirm it). Passing both port and ports, or neither, is a 422. expected_state values are lowercase. DNS requires record_type (A, AAAA, CNAME, MX, TXT or NS; no default) and takes optional expected_value (at most 2048 characters): when set, one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); when absent, any answer passes. For MX the answers are the mail host names only, without priority (mail.example.com, not 10 mail.example.com). A and AAAA both look up every address of the host, IPv4 and IPv6 alike, and any one of them may match; write an IPv6 address in its shortest form. The ports rules above are checked against the monitor after the change, so changing only the interval or the regions of a ports monitor can be rejected too. A monitor that checks a list of ports cannot switch to a single port (422): create a new monitor instead. regions is a list of one or more distinct region codes (get_plan_usage lists them in limits.allowed_regions, for example eu-central; an unknown code is a 403) — SSL and DNS monitors must target exactly one region (422 otherwise). interval_seconds (integer, 30 to 86400) is the time between checks; timeout_seconds (integer, 1 to 120) is how long one check may take. confirmation_threshold (integer, 1 to 10; 3 when not set on create) is how many checks in a row must fail in every region before the monitor goes DOWN and an incident opens. notification_cooldown_seconds (integer, 60 to 86400; 1800 when not set on create) only holds back a duplicate first alert for the same incident within that window. Repeat reminders while the incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (see get_plan_usage; 0 means no reminders), not this value; it never holds back the recovery alert. Plan limits apply: minimum interval_seconds, how many monitors may use an interval below 60 seconds (only moving a monitor from 60 seconds or more to below 60 takes a slot), and max_regions per monitor. Breaking one is a 403 that names the limit. get_plan_usage shows the limits and current usage. Use pause_monitor and resume_monitor, not this tool, to stop or restart checks. channel_ids (array of channel uuids), when passed, replaces the whole binding; an empty list clears it, after which every verified, active channel is alerted; omit it to leave the binding unchanged; an id that is not an active channel of this organization is a 404; list_channels gives the ids. badge_enabled (boolean) turns the monitor's public status badge on or off; a monitor on a published status page has a badge even when it is false. tags, when passed, replaces the monitor's whole tag list (at most 10); pass an empty list to clear it. Tags are trimmed, lowercased and deduplicated on write, each at most 50 characters of lowercase letters, digits and : _ . / - , starting with a letter or digit. Call list_tags first to reuse the organization's existing names instead of inventing near-duplicates. Example: {"monitor_id": "<id from list_monitors>", "interval_seconds": 600}
pause
pause_heartbeat
Pause a heartbeat: it is no longer reported late, pings to its ping_url are accepted but ignored, and its open incident closes without an alert. Use it when the job is stopped on purpose, for example a runner taken out of service. Requires a key with the write scope. heartbeat_id (uuid, required) is the heartbeat's id from list_heartbeats; an id that is not a heartbeat of this organization (or a deleted one) is a 404. Pausing a paused heartbeat succeeds without change. Returns the heartbeat.
pause_monitor
Stop checking a monitor without deleting it — use before planned downtime. Requires a key with the write scope. The monitor's status becomes PAUSED, and an open incident on it is closed without a recovery alert. Pausing a paused monitor changes nothing. Returns the monitor (the get_monitor shape). monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404.
resume
resume_heartbeat
Resume a paused heartbeat. It goes back to PENDING with no deadline, so it is not reported late until its next successful ping restarts the clock. Resuming one that is not paused resets it to PENDING the same way. Requires a key with the write scope. heartbeat_id (uuid, required) is the heartbeat's id from list_heartbeats; an id that is not a heartbeat of this organization (or a deleted one) is a 404. Returns the heartbeat.
resume_monitor
Resume checks on a paused monitor. Requires a key with the write scope. Its status goes back to PENDING and it is checked on the scheduler's next tick; resuming an active monitor is a no-op success. A 403 means the organization already has as many active monitors as its plan allows: pause or delete another one first. Returns the monitor (the get_monitor shape). monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404.
uptime
get_uptime_daily
Return a monitor's uptime for each UTC day, as monitor_id and days: an array ordered oldest first and ending today, one entry per day with date, uptime_percentage, total_checks, up_checks, down_checks, degraded_checks and maintenance_checks. A degraded check counts as up, and a day with no checks (or only checks inside a maintenance window) has a null uptime_percentage, not 100. Use it to find the day an outage or degradation began; get_uptime_summary gives the 24h, 7d and 30d totals. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404. days (optional integer, 1 to 365, default 30) is how many days to return; it is capped at the plan's history retention (limits.history_retention_days in get_plan_usage), so fewer days can come back than asked for.
get_uptime_summary
Uptime percentage, average response time and incident counts for one monitor over the 24-hour, 7-day and 30-day windows at once. Use this to answer 'how reliable has this been'. Returns monitor_id and windows keyed 24h, 7d and 30d, each with uptime_percentage (DEGRADED checks count as up; null when there were no checks), total_checks, up_checks, down_checks, degraded_checks, incident_count and avg_response_time_ms. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404.
acknowledge
acknowledge_incident
Acknowledge an incident so repeat notifications stop while someone works on it; the recovery alert still goes out. Requires a key with the write scope. incident_id (uuid, required) is the incident's id from list_incidents; an id that is not an incident of this organization is a 404. Acknowledging twice keeps the first acknowledgement, and acknowledging a resolved incident succeeds without effect on alerts. Returns the incident.
active
list_active_maintenance
List the enabled maintenance windows that are in effect right now, as a plain array (not a page) of the list_maintenance_windows shape. Use it to tell whether alerts are silenced at the moment; an ORG-scope window covers every monitor. No arguments.
channels
list_channels
List the organization's notification channels (up to 100): each with id, type (TELEGRAM, WEBHOOK, SLACK or DISCORD), label, is_verified, is_active and destination_hint (a chat id, a masked address, or for a webhook only its scheme and host). Use the ids in channel_ids of monitors and heartbeats. An inactive or unknown id cannot be bound (404), and only a verified, active channel is alerted: one that is not verified yet can be bound but receives nothing until it is. Email is not listed: every alert also goes to the account email of the user who created the monitor or heartbeat. Channels are added in the dashboard. No arguments.
exposed
get_exposed_ports_preset
List ports that should not be reachable from the internet (databases, queues, admin panels): ports, each as port plus the service that usually listens on it. Takes no arguments. To watch a server, create ONE TCP monitor on its public hostname or IP with config {"ports": [the ports you want], "expected_state": "closed"}, 1 to 20 distinct ports, exactly one region and an interval_seconds of at least 300 (3600 is plenty): it counts as one monitor, and a check fails when any listed port accepts a connection, naming every open port.
heartbeats
list_heartbeats
List heartbeat (cron / dead-man's-switch) monitors: each with id, name, state (PENDING, UP, DOWN or PAUSED; a new heartbeat is PENDING until a successful ping makes it UP or a /fail ping makes it DOWN), period_seconds, grace_seconds, ping_url, last_ping_at, last_success_at, overdue_at, tags and channel_ids. The result is a page: items plus total, page, size and pages. All arguments are optional. page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25) select the page. q (string, up to 255 characters) matches a case-insensitive substring of the name. sort is status (the default: DOWN, then PENDING, then UP, then PAUSED, newest first within each), name (alphabetical) or created_at (newest first). tag takes a JSON array of up to 10 tags, lowercased before matching; with tag_match all (the default) a heartbeat must carry every tag given, with tag_match any at least one.
incident
get_incident
Return one incident: id, monitor_id or heartbeat_id (exactly one is set), started_at, resolved_at, cause, cause_detail, last_notified_at, acknowledged_at, acknowledged_by, renotify_count and notification_logs (the alert attempts: each with id, channel_type, destination, event TRIGGERED, RESOLVED or SSL_EXPIRY, sent_at, status PENDING, SENT or FAILED, and error). destination is an email address, a Telegram chat id, or for a webhook only scheme://host/… (the full URL is never returned). incident_id (uuid, required) is the incident's id from list_incidents; an id that is not an incident of this organization is a 404.
incidents
list_incidents
List the organization's incidents — both monitor and heartbeat incidents, newest first. Each item has id, monitor_id or heartbeat_id (exactly one is set), started_at, resolved_at (null while open), cause, cause_detail (the raw check error), acknowledged_at and notification_logs: the alerts sent for it, oldest first, each with id, channel_type (EMAIL, TELEGRAM, WEBHOOK, SLACK or DISCORD), destination (where it went; may be masked), event (TRIGGERED for the first alert and every reminder, RESOLVED for the recovery), sent_at, status (PENDING while a webhook, Slack or Discord delivery is still being tried, then SENT or FAILED) and error (the last delivery error; null when SENT). The result is a page: items plus total, page, size and pages. All arguments are optional. page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25) select the page. monitor_id (uuid) keeps one monitor's incidents. status is open or resolved (lowercase); omit it for both. cause keeps one failure cause: TIMEOUT, DNS_FAILURE, CONNECTION_REFUSED, TLS_INVALID, HTTP_4XX, HTTP_5XX, HTTP_UNEXPECTED_STATUS, KEYWORD_MISSING, TOO_MANY_REDIRECTS, TARGET_BLOCKED, PORT_STATE_MISMATCH, INVALID_TARGET, HEARTBEAT_MISSED, HEARTBEAT_FAILED or UNKNOWN. period_days (integer, 1 to 365) keeps incidents active within the last N days; an open incident always qualifies, so with status open it changes nothing. An incident resolves by itself when its monitor or heartbeat recovers. A value outside these is a 422 naming the argument. Example: {"status": "open", "size": 50}
monitors
list_monitors
List the monitors in the organization the API key belongs to, one short row each: id, name, type, target, status, is_active, tags, interval_seconds, ssl_expires_at, last_checked_at. The result is a page: items plus total, page, size and pages. Use this first to find a monitor's id; get_monitor returns its full configuration. All arguments are optional. page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25) select the page; keep paging until you have seen total rows. q (string, up to 255 characters) matches a case-insensitive substring of the name or the target. status and type each take a JSON array of uppercase values and keep monitors matching any of them: status is PENDING (no verdict yet), UP, DEGRADED, DOWN or PAUSED, so status ["DOWN", "DEGRADED"] answers "what is broken right now"; type is HTTP, PING, SSL, TCP or DNS. tag takes an array of up to 10 tags (each up to 50 characters), lowercased before matching; with tag_match all (the default) a monitor must carry every tag, with tag_match any at least one. sort is one of status (the default: problems first — PENDING, DOWN, DEGRADED, UP, PAUSED — then by name), up_first (healthy first), name (alphabetical), newest, oldest (by creation) or ssl_expiry (the soonest-expiring certificates first, and monitors with no known expiry — non-SSL types, or not checked yet — last). A value outside these is a 422 naming the argument. Example: {"status": ["DOWN", "DEGRADED"], "size": 100}
now
check_now
Re-arm a monitor to run on the scheduler's next tick instead of waiting out its interval. Returns the monitor record, not a check result — call get_monitor a moment later to see the outcome. A repeat call for the same monitor within 30 seconds is a silent no-op that returns it unchanged, and so is a call on a paused monitor. Across monitors, calls are limited to 10 per minute keyed on the calling address (not the API key or the organization), answering 429 with the seconds to wait once exceeded — pace yourself if checking many monitors in a row. Requires a key with the write scope. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404.
plan
get_plan_usage
Show the organization's plan, its limits and how much of them is used now. Takes no arguments. Returns plan (one of FREE, PLUS, PRO, MAX, ENTERPRISE), limits and usage. limits includes max_monitors, min_interval_seconds, max_fast_monitors (how many monitors may use an interval below fast_interval_threshold_seconds, which is 60), max_regions (regions per monitor), allowed_regions (the region codes to choose from), max_heartbeats, api_rate_limit_per_minute (API requests per minute for the whole organization), renotify_interval_seconds and renotify_max_count (how often and how many times an open, unacknowledged incident is alerted again; 0 means never); null means no limit. usage has monitors (paused ones included), fast_monitors and heartbeats. Call it before creating several monitors to check they fit.
post
post_status_page_update
Add a message to a status page incident and set its status; status RESOLVED closes it. This is public and emails the page's confirmed subscribers (details below): confirm the wording with the user before calling. Requires a key with the write scope. page_id (uuid, required) is the page's id from list_status_pages; an id that is not a status page of this organization is a 404. incident_id (uuid, required) is the incident's id from list_status_page_incidents (not from list_incidents); an unknown one is a 404. Required: status and body (1 to 5000 characters). status is INVESTIGATING, IDENTIFIED, MONITORING or RESOLVED; RESOLVED closes the incident on the page. Every confirmed subscriber is emailed the update, except that an incident subscribers were never told about stays silent when this update resolves it. A resolved incident only accepts further RESOLVED updates; any other status is a 409. Returns the incident. Example: {"page_id": "<id from list_status_pages>", "incident_id": "<id from list_status_page_incidents>", "status": "RESOLVED", "body": "Fixed; error rates are back to normal."}
preview
preview_maintenance_window
Return the next three occurrences (start and end) a schedule would produce, without saving anything, as an array. Use it before create_maintenance_window for a recurring window. Requires a key with the write scope. Required: dtstart (ISO 8601 date-time in local wall-clock time of timezone; a UTC offset or Z is a 422) and duration_minutes (integer, 1 to 40320). rrule (single iCal RRULE without the RRULE: prefix, at most 1024 characters; hourly, minutely and secondly frequencies are refused) and timezone (IANA name, at most 64 characters; the organization's timezone when omitted). An invalid schedule is a 422 with the reason. Example: {"dtstart": "2026-10-18T03:00:00", "duration_minutes": 60, "rrule": "FREQ=WEEKLY;BYDAY=SU", "timezone": "Europe/Berlin"}
regions
list_regions
List every check region of the platform as regions, each with code (what the regions argument takes), display_name, city, country_code, group and group_display_name. The plan decides which of them an organization may use: get_plan_usage shows limits.allowed_regions and how many one monitor may use (max_regions). No arguments.
rename
rename_tag
Rename a tag on every monitor and heartbeat that carries it; an item that already has the new name keeps one copy. Requires a key with the write scope. Required: from (the existing tag) and to (the new name). Tags are trimmed, lowercased and deduplicated on write, each at most 50 characters of lowercase letters, digits and : _ . / - , starting with a letter or digit. Call list_tags first to reuse the organization's existing names instead of inventing near-duplicates. Returns from, to and how many monitors_updated and heartbeats_updated changed; a tag nothing carries is not an error, both counts are 0. Example: {"from": "prod", "to": "env:prod"}
response
get_response_time_by_region
Return a monitor's average response time over time, one series per region: monitor_id, window, bucket_seconds and regions, each with region, display_name and points. Every region the monitor is configured for has a series, even one with no checks yet, followed by any region since removed from the monitor that still had checks in the window. points are oldest first, the same buckets in every series, each with bucket_start, avg_response_time_ms (null when the bucket had no successful check) and sample_count; they start at the window's start, or at the bucket the monitor was created in when it is younger than the window. Use it to see where a monitor is slow and since when. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404. window (optional) is 24h (the default; up to 24 hourly buckets), 7d (up to 28 six-hourly buckets) or 30d (up to 30 daily buckets); any other value is a 422.
results
list_check_results
List individual check results of a monitor, newest first: each with id, region, checked_at, status (UP, DEGRADED, DOWN or ERROR), response_time_ms, status_code, error_message, details and maintenance_window_id (set when the check ran inside a window). Use it to find out why a monitor is DOWN: which regions fail, with what code or error. The result is a page: items plus total, page, size and pages; total counts only the results that match the filters. monitor_id (uuid, required) is the monitor's id from list_monitors; an id that is not a monitor of this organization (or a deleted one) is a 404. All other arguments are optional. page (integer, 1 or more, default 1) and size (integer, 1 to 100, default 25) pick the page. region (string, up to 64 characters) keeps only results of that exact region code. status (UP, DEGRADED, DOWN or ERROR) keeps only that status. period_days (integer, 1 to 365) keeps only results of the last that many days; without it every stored result is eligible.
tag
delete_tag
Remove a tag from every monitor and heartbeat that carries it. This cannot be undone. Requires a key with the write scope. tag (string, required) is the tag to remove, as list_tags shows it. Returns from (the tag) and how many monitors_updated and heartbeats_updated changed; a tag nothing carries is not an error, both counts are 0.
tags
list_tags
List every tag used in the organization, with how many monitors and how many heartbeats carry each one, most-used first: items, each with tag, monitor_count and heartbeat_count. Call this before tagging something new so you reuse the names already in use rather than inventing a near-duplicate. kind (optional) is monitor, heartbeat or all (the default): monitor or heartbeat lists only the tags that kind of item uses; both counts are reported either way.
target
check_target
Run one real check of a target from one region, chosen by Fettle, and report status (up, down, error or unknown), region, response_time_ms, status_code, error and failure_kind. unknown means no definite answer came back, not a failure. Use it before create_monitor to catch a typo or an unreachable host; a down result does not stop you from creating the monitor. Requires a key with the write scope and is rate limited per caller address (a 429 whose retry_after says how many seconds to wait). Required: type (HTTP, PING, SSL, TCP or DNS) and target, as in create_monitor. config (optional object) as in create_monitor; TCP and DNS need it. A target that is not public is a 422. Example: {"type": "HTTP", "target": "https://api.example.com/health"}

Endpoints

URLTransportStateLatencyChecked
https://api.fettle.sh/mcp streamable-http answering 186 ms 6 min ago

Alternatives to Fettle

same job, measured the same way
Vantaj
by vantaj

Vantaj uptime monitoring via MCP - manage monitors, heartbeats, incidents, and status pages.

13 tools answering
Uptimely
by getuptimely

Uptime monitoring: monitors, incidents, alerts, maintenance, on-call, status pages and telemetry.

answering
StatOSS
by statoss

Status pages and uptime monitoring: read status, monitors and incidents; open incidents.

answering
openstatus
by openstatus

Manage monitors, status pages, incidents and maintenances in your openstatus workspace.

answering
Openuptime
by openuptime

Manage uptime monitors, incidents and status pages from any MCP client.

answering
SolidPing
by fclairamb

Self-hostable uptime monitoring: create checks, read incidents, manage status pages.

answering
SutramX
by sutramx

SutramX uptime monitoring: monitors, check results, incidents, status pages and uptime/SLO reports.

643 installs/wk local only
Tá Rodando
by com-tarodando

Uptime monitoring and public status page: HTTP monitors, down/up alerts and incidents.

answering

Fettle — questions

Answers built from our own checks of this server.

What can Fettle do?
It exposes 49 tools, read directly from the server on our last check. Among them: acknowledge_incident, bulk_create_monitors, bulk_pause_monitors, bulk_resume_monitors, bulk_update_monitors, bulk_update_monitor_tags and 43 more. The full list with descriptions is on this page — we take it from the server itself via tools/list, not from a README. How MCP servers expose tools in the first place →
What is Fettle mostly used for?
Its tools cluster around status, bulk and maintenance. That is what this server is built to work with — the grouping comes from the actual tool names, not from a category we assigned.
Is Fettle working right now?
We send a real MCP handshake every 15 minutes. Over the last 24 hours 92 of 92 checks got a reply (100.0%), average response time 209 ms. The bar chart above shows every period we have measured.
How do I connect Fettle?
Copy the ready config from this page — we generate it for Claude Code, Claude Desktop, Codex, Cursor and VS Code, each with the file path that client actually reads. It is a remote server, so there is nothing to install — the client connects to the address.
Does Fettle need an API key?
No. Fettle completed a full MCP handshake with us as an anonymous client and listed its tools without asking for anything. All 49 of them are readable on this page. This is what we observed, not what the docs claim.
How fast is Fettle?
It answers our handshake in 209 ms on average, which is faster than 60% of all working MCP servers we measure. The comparison comes from our own checks across the whole registry, every 15 minutes.
How many people use Fettle?
The npm package fettle-mcp was installed 375 times in the last week. Week over week that is +8%. We show installs rather than GitHub stars on purpose: a star is a bookmark, an install is someone actually running it.
Is Fettle open source?
Yes — it is published under the MIT licence, written in TypeScript and 0 stars on GitHub. The source link is on this page, so you can read exactly what it does with your data before you connect it.