{
  "markdown": "# healthchecks-mcp\n\n<!-- badges: start -->\n\n[![CI](https://img.shields.io/github/actions/workflow/status/ni-c/healthchecks-mcp/ci.yml?branch=main&label=CI)](https://github.com/ni-c/healthchecks-mcp/actions/workflows/ci.yml)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ni-c/healthchecks-mcp/badge)](https://scorecard.dev/viewer/?uri=github.com/ni-c/healthchecks-mcp)\n<a href=\"https://socket.dev/npm/package/healthchecks-mcp\"><img src=\"https://socket.dev/api/badge/npm/package/healthchecks-mcp\" alt=\"Socket supply-chain report\" height=\"20\"></a>\n[![Glama score](https://glama.ai/mcp/servers/ni-c/healthchecks-mcp/badges/score.svg)](https://glama.ai/mcp/servers/ni-c/healthchecks-mcp)\n<br>\n[![npm version](https://img.shields.io/npm/v/healthchecks-mcp)](https://www.npmjs.com/package/healthchecks-mcp)\n[![container image](https://img.shields.io/badge/ghcr.io-ni--c%2Fhealthchecks--mcp-4f46e5?logo=docker&logoColor=white)](https://github.com/ni-c/healthchecks-mcp/pkgs/container/healthchecks-mcp)\n[![HTTP via mcp-hub](https://img.shields.io/badge/HTTP-via%20mcp--hub-4f46e5?logo=modelcontextprotocol&logoColor=white)](https://mcp-hub.ni-c.de)\n<br>\n[![docs](https://img.shields.io/badge/docs-healthchecks--mcp.ni--c.de-4f46e5?logo=readthedocs&logoColor=white)](https://healthchecks-mcp.ni-c.de)\n[![sponsor](https://img.shields.io/badge/sponsor-ni--c-ea4aaa?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/ni-c)\n<!-- badges: end -->\n\nA [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for\n[Healthchecks](https://healthchecks.io), the dead man's switch for cron jobs and\nscheduled tasks — it alerts you when a job stops checking in. Works against the\nhosted service and against a self-hosted instance alike.\n\nLets MCP clients like Claude Code, Claude Desktop or Codex see which scheduled\njobs are healthy, read the output the failing one reported, and create or adjust\nchecks — with the irreversible operations behind a confirmation token and the\nwrite tools switchable off entirely.\n\nFourteen tools is the ceiling, not the floor:\n`HEALTHCHECKS_ALLOW_TOOLS=essential` registers a curated seven instead, and a\nmodel picks the right tool far more reliably from seven than from fourteen — see\n[choosing which tools load](#choosing-which-tools-load).\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://healthchecks-mcp.ni-c.de/architecture-dark.svg\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://healthchecks-mcp.ni-c.de/architecture-light.svg\">\n  <img src=\"https://healthchecks-mcp.ni-c.de/architecture.svg\" alt=\"An MCP client talks to healthchecks-mcp over stdio; the server calls the Healthchecks Management API v3 over HTTPS.\" width=\"800\">\n</picture>\n\n![Listing the tools, narrowing them to the essential preset, and the startup abort a mistyped tool name produces](https://healthchecks-mcp.ni-c.de/demo.gif)\n\n## What makes it different\n\n**It reads the ping bodies.** `get_ping_body` returns what a job actually printed\nwhen it reported failure. Every other question — which check is down, since when,\nhow often — is one step away from that one, and it is the endpoint the other\nHealthchecks MCP servers leave out.\n\n**Read-only API keys work properly.** Healthchecks hands a read-only key a\ndifferent object: no `uuid`, no `ping_url`, no `channels` — a 40-character\n`unique_key` instead. This server addresses checks by either, and\n`get_api_key_info` tells you up front which kind of key you configured and which\ntools it cannot reach, rather than leaving you with a `401 missing api key` for a\nkey that was sent.\n\n**It never pings a check.** Pinging is how a job reports that it ran. A tool that\ncould ping would let a model make a dead job look alive, which is the one thing\nmonitoring must not allow — see [Not exposed, on purpose](#not-exposed-on-purpose).\n\n**It knows where this API is sharp.** `timeout` and `schedule` cannot be combined\nbecause the upstream silently discards one of them; tags are validated against\ntheir space separator and keywords against their comma; a new check is given\nevery integration unless you say otherwise, because the API's own default is a\ncheck that alerts nobody.\n\n## Requirements\n\n- Node.js ≥ 22\n- A **Healthchecks** project API key — Project Settings → API Access. Keys are per\n  project, not per account, and are exactly 32 characters long.\n\n## Configuration\n\n| Variable                    | Required | Description                                                                                           |\n| --------------------------- | -------- | ----------------------------------------------------------------------------------------------------- |\n| `HEALTHCHECKS_API_KEY`      | yes      | Project API key. A read-only key works for part of the tool surface — see below                       |\n| `HEALTHCHECKS_URL`          | no       | Site root of a self-hosted instance, e.g. `https://hc.example.net`. Default `https://healthchecks.io` |\n| `HEALTHCHECKS_READ_ONLY`    | no       | `true` registers only the read tools                                                                  |\n| `HEALTHCHECKS_ALLOW_TOOLS`  | no       | Comma-separated tool names, `list_*` prefixes, or `essential` for a curated preset                    |\n| `HEALTHCHECKS_DENY_TOOLS`   | no       | Same syntax; removed from whatever `HEALTHCHECKS_ALLOW_TOOLS` left                                    |\n| `HEALTHCHECKS_INSECURE_TLS` | no       | `true` accepts self-signed certificates (scoped to this connection)                                   |\n| `ELICITATION`               | no       | `false` replaces the approval dialog with the two-call token. **Not prefixed**                        |\n\n`HEALTHCHECKS_URL` is the site root, not the API root: `https://hc.example.net`,\nnot `https://hc.example.net/api/v3`. Both are accepted — the suffix is trimmed —\nbecause the API documentation spells every example the long way.\n\n> **Use `https://`.** Over plain http the API key travels unencrypted; the server\n> prints a warning unless the host is local. For self-signed certificates prefer a\n> proper internal CA over `HEALTHCHECKS_INSECURE_TLS`.\n\nWithout an API key the server still starts and lists its tools (so registries and\ninspectors can introspect it), but every call except `get_status` fails with setup\ninstructions instead of reaching the API.\n\n**Read-only keys.** Healthchecks gates three tools that only read — `list_pings`,\n`get_ping_body` and `list_integrations` — behind a read-write key anyway. With a\nread-only key those fail, along with all five write tools.\n\nThe failure does not look like a permission problem: the API answers\n`401 {\"error\": \"wrong api key\"}`, which reads as if the key were wrong or\nmissing. It is not — those three tools translate it into what actually happened.\n`get_api_key_info` reports which kind of key is configured, and\n`HEALTHCHECKS_DENY_TOOLS` is the tidy way to stop offering them at all.\n\n### Choosing which tools load\n\n`HEALTHCHECKS_ALLOW_TOOLS` and `HEALTHCHECKS_DENY_TOOLS` take comma-separated tool\nnames; a trailing `*` matches a whole family. `essential` is a curated preset —\n`list_checks`, `get_check`, `list_pings`, `list_flips`, `create_check`,\n`update_check` and `resume_check` — marked as such in the\n[tool reference](https://healthchecks-mcp.ni-c.de/reference/tools).\n\n```sh\nHEALTHCHECKS_ALLOW_TOOLS=essential\nHEALTHCHECKS_ALLOW_TOOLS=list_*,get_check\nHEALTHCHECKS_DENY_TOOLS=delete_check,pause_check\n```\n\nAn entry that matches no tool aborts startup and names it, so a typo cannot silently\nhide a tool — an absent tool is not something anyone traces back to an environment\nvariable. A filtered tool is never registered, so it is absent from `tools/list` and\nunknown to `tools/call` alike, exactly like a write tool under\n`HEALTHCHECKS_READ_ONLY`.\n\nIf you run several of these servers at once, [mcp-hub](https://mcp-hub.ni-c.de) is\nthe other answer — its `/hub` endpoint replaces every server's tools with six\nmeta-tools.\n\n## Installation\n\n### Claude Code\n\n```sh\nclaude mcp add healthchecks-mcp -- npx -y healthchecks-mcp\n```\n\n### Claude Desktop\n\n```json\n{\n  \"mcpServers\": {\n    \"healthchecks-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"healthchecks-mcp\"],\n      \"env\": {\n        \"HEALTHCHECKS_API_KEY\": \"…\"\n      }\n    }\n  }\n}\n```\n\n### Codex\n\n```toml\n[mcp_servers.healthchecks-mcp]\ncommand = \"npx\"\nargs = [\"-y\", \"healthchecks-mcp\"]\nenv = { HEALTHCHECKS_API_KEY = \"…\" }\n```\n\n### Docker\n\n```sh\ndocker run --rm -i \\\n  -e HEALTHCHECKS_API_KEY=… \\\n  ghcr.io/ni-c/healthchecks-mcp\n```\n\nAdd `-e HEALTHCHECKS_URL=https://hc.example.net` for a self-hosted instance.\n\n### Through mcp-hub\n\nA client that cannot spawn a local process — ChatGPT connectors, Claude on the web,\nCursor, LibreChat — reaches healthchecks-mcp through [mcp-hub](https://mcp-hub.ni-c.de): one\ncontainer serves many stdio MCP servers over Streamable HTTP, with an OAuth 2.1 login\nbehind a single password and long-lived tokens for the clients that cannot do OAuth. Its\n`/hub` endpoint puts every server behind six meta-tools, so one connector reaches all of\nthem without N×tool schemas in the model's context, and it speaks both protocol revisions\n— a question this server asks travels through it to the person at the far end.\n\nIts `/config/mcp.json` uses Claude Code's format, so the entry is the one you already\nhave:\n\n```json\n{\n  \"mcpServers\": {\n    \"healthchecks-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"healthchecks-mcp\"],\n      \"env\": {\n        \"HEALTHCHECKS_URL\": \"https://hc.example.net\",\n        \"HEALTHCHECKS_API_KEY\": \"…\",\n        \"HEALTHCHECKS_ALLOW_TOOLS\": \"essential\"\n      },\n      \"denyTools\": [\"delete_check,pause_check\"]\n    }\n  }\n}\n```\n\n`allowTools` and `denyTools` there are the hub's **own** per-server filter, which is not\nthe same thing as `*_ALLOW_TOOLS` in `env` — the difference, and the mistake it invites,\nare in the [client guide](https://healthchecks-mcp.ni-c.de/guide/clients#through-mcp-hub).\n\n## Tools\n\nRead tools are always registered. 🔑 marks the ones Healthchecks requires a\nread-write key for even though they only read; 👤 marks the ones that ask for a\nconfirmation token before acting.\n\n| Tool                   | Description                                                    |\n| ---------------------- | -------------------------------------------------------------- |\n| `list_checks`          | Checks in the project, with `tag`, `slug` and `status` filters |\n| `get_check`            | One check with every field, by UUID or `unique_key`            |\n| `list_pings` 🔑        | Recent pings of a check, newest first                          |\n| `get_ping_body` 🔑     | The body a job POSTed with one ping — its output               |\n| `list_flips`           | Up/down transitions of a check, with a time window             |\n| `list_integrations` 🔑 | Notification integrations and the UUIDs the write tools accept |\n| `list_badges`          | Status badge URLs, per tag and for the project                 |\n| `get_status`           | Whether the instance is reachable — needs no API key at all    |\n| `get_api_key_info`     | Which instance, which kind of key, and what that key cannot do |\n\nWrite tools are registered unless `HEALTHCHECKS_READ_ONLY=true`.\n\n| Tool              | Description                                                                                               |\n| ----------------- | --------------------------------------------------------------------------------------------------------- |\n| `create_check`    | Creates a check. Notifies every integration unless `channels` says otherwise                              |\n| `update_check`    | Changes the given fields. `channels` replaces the list rather than adding to it; an empty list is refused |\n| `pause_check`     | Stops the check expecting pings — and alerting. `resume_check` puts it back                               |\n| `resume_check`    | Puts a paused check back into the `new` state                                                             |\n| `delete_check` 👤 | Deletes a check. The UUID is not recoverable                                                              |\n\n### Structured output\n\nEvery tool declares an `outputSchema` and answers with `structuredContent`\nalongside the text block, so a client can use the result without parsing prose:\n\n```jsonc\n{\n  \"untrusted\": true,\n  \"source\": \"healthchecks\",\n  \"checks\": [{ \"id\": \"…\", \"name\": \"Nightly Backup\", \"status\": \"up\" }],\n  \"total_in_project\": 12,\n}\n```\n\nEvery tool that reports anything from the instance carries `untrusted: true`\nand `source: \"healthchecks\"` as fields — a check name, a description and above\nall a logged ping body are written by whoever pinged, and a ping URL sits in a\ncron job on every monitored host. `get_api_key_info` is without it, and\n`get_status` carries it **only** when the instance answered something other than\n`OK`: a plain `OK` is this server's own sentence, and a marker on everything is\na marker that means nothing.\n\nFields this server builds are described exactly; a check record is left open,\nbecause `normalizeCheck` passes through whatever a self-hosted release chose to\nadd and the SDK validates every result against its schema before it goes out.\n\n## Not exposed, on purpose\n\n- **Pinging.** The server never calls a ping URL. Pinging is how a job says it\n  ran; a tool that could ping would let a model — or text a model read — report\n  success for a job that never executed, and a monitoring system that can be\n  talked into a green status is worse than none.\n- **Ping keys.** They are not readable through the Management API, and this server\n  does not ask for one.\n- **Creating integrations.** The API has no endpoint for it; they are configured\n  in the web UI. `list_integrations` reads them.\n- **The `unique` upsert as a default.** `create_check` accepts it and says loudly\n  in its result when it was used, because it turns a create into a silent update\n  of a check that already exists.\n\n## Safety\n\n- **`delete_check` asks a person.** Where the client supports MCP elicitation it\n  raises a real dialog that the model cannot answer on its behalf; where it does\n  not, it falls back to a short-lived token bound to that exact check and that\n  exact operation, and says so rather than implying somebody approved.\n  `pause_check` is deliberately not asked about — `resume_check` puts it back and\n  nothing is lost in between, and a dialog in front of a reversible change is how\n  people learn to tick without reading. See\n  [Asking a person](https://healthchecks-mcp.ni-c.de/guide/approval).\n- **Confirmation prompts never quote content from Healthchecks** — a check's name\n  and description are free text this server does not control, and that text is\n  read by a model.\n- **Ping bodies and check descriptions are marked as untrusted data**, because\n  anything that can ping a check can write into them.\n- Error bodies are truncated, HTML error pages are dropped, and every response has\n  a byte ceiling enforced while it streams — the Management API paginates nothing.\n- `HEALTHCHECKS_READ_ONLY=true` does not register the write tools at all, and\n  `HEALTHCHECKS_DENY_TOOLS` cuts finer along the same line — a filtered tool is\n  never built, not refused at call time.\n- The API key is deleted from `process.env` once it has been read, and never\n  travels in a request body.\n\n## Documentation\n\nThe full guide, tool reference and security notes live at\n**[healthchecks-mcp.ni-c.de](https://healthchecks-mcp.ni-c.de)** (source in [`docs/`](docs/)).\n\n## Development\n\n```sh\nnpm install\nnpm run lint && npm run build && npm run test:coverage\n```\n\n## Releasing\n\n1. Add the CHANGELOG entry and bump `package.json`.\n2. `npm run lint && npm run build && npm run test:coverage`\n3. Commit, then push a signed tag: `git tag -s vX.Y.Z -m \"vX.Y.Z\" && git push origin main vX.Y.Z`\n\nThe release workflow publishes to npm (Trusted Publishing, with provenance), creates\nthe GitHub release from the CHANGELOG section and updates the MCP Registry entry.\n\n## Contributing\n\nIssues, discussions and pull requests are welcome — see\n[CONTRIBUTING.md](CONTRIBUTING.md). For vulnerabilities please use\n[private reporting](https://github.com/ni-c/healthchecks-mcp/security/advisories/new)\nrather than a public issue; the policy is in [SECURITY.md](SECURITY.md).\n\n## License\n\n[MIT](LICENSE) © Willi Thiel\n",
  "bytes": 16406,
  "sha": "0563e82c2a42b39c56afc1a75e6a1dacacfd9ee886438c95df65460bcfae2760",
  "repo_slug": "ni-c/healthchecks-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ni_c_healthchecks_mcp_17e81cbe/readme"
}