changedetection-mcp
# changedetection-mcp
An MCP (Model Context Protocol) server that connects AI assistants to a [changedetection.io](https://github.com/dgtlmoon/changedetection.io) instance through its API v1. It has 28 tools for watches, snapshot history and diffs, tags, notifications, search, and import. Writes are dry-run previews unless you confirm them and enable writes. It runs over stdio, or over stateless Streamable HTTP on localhost.
**Connects to:** changedetection.io (self-hosted or a hosted account), using your own API key.
By Issah Abubakari ([@si-essence](https://github.com/si-essence)). Also listed on [Bot Essentials](https://bot-essentials.netlify.app).
changedetection.io is Apache-2.0 software. Self-host it, or point this connector at your own hosted account. The connector does not grant any right to the changedetection.io name, and it does not imply endorsement.
This connector is MIT. It is a BYO-key client. It does not resell hosted access.
## Requirements
- Node.js 20 or newer (CI uses Node.js 22)
- npm
## Setup
From a clone of this repository:
```bash
npm ci
npm run ci
npm start
npm run start:http
```
`npm start` speaks stdio. `npm run start:http` serves Streamable HTTP at `http://127.0.0.1:3000/mcp` and `GET /health`. HTTP mode has no authentication. Keep it on localhost.
Create an API key in changedetection.io under Settings, API. The connector sends it as `x-api-key` and never prints it.
```json
{
"mcpServers": {
"changedetection": {
"command": "node",
"args": ["/path/to/changedetection-mcp/dist/index.js"],
"env": {
"CHANGEDETECTION_URL": "http://127.0.0.1:5000",
"CHANGEDETECTION_API_KEY": "...",
"CHANGEDETECTION_ALLOW_WRITES": "false",
"CHANGEDETECTION_CONTACT": "you@example.com"
}
}
}
}
```
Hosted accounts use the login origin as `CHANGEDETECTION_URL`. A trailing `/api/v1` is stripped.
Docker (HTTP, no authentication, localhost only):
```bash
docker build -t changedetection-mcp .
docker run -p 3000:3000 \
-e CHANGEDETECTION_URL=http://host.docker.internal:5000 \
-e CHANGEDETECTION_API_KEY=... \
changedetection-mcp
```
Do not put the API key in the image.
## Tools
| Tool | Call |
| --------------------------------------- | ---------------------------------------------------- |
| `changedetection_list_watches` | `GET /api/v1/watch` |
| `changedetection_get_watch` | `GET /api/v1/watch/{uuid}` |
| `changedetection_create_watch` | `POST /api/v1/watch` |
| `changedetection_update_watch` | `PUT /api/v1/watch/{uuid}` |
| `changedetection_delete_watch` | `DELETE /api/v1/watch/{uuid}` |
| `changedetection_recheck_watch` | `GET /api/v1/watch/{uuid}?recheck=1` |
| `changedetection_recheck_all` | `GET /api/v1/watch?recheck_all=1` |
| `changedetection_set_watch_state` | `GET /api/v1/watch/{uuid}` with `paused` and `muted` |
| `changedetection_list_history` | `GET /api/v1/watch/{uuid}/history` (timestamps only) |
| `changedetection_get_snapshot` | `GET /api/v1/watch/{uuid}/history/{timestamp}` |
| `changedetection_get_diff` | `GET /api/v1/watch/{uuid}/difference/{from}/{to}` |
| `changedetection_delete_history` | `DELETE /api/v1/watch/{uuid}/history` |
| `changedetection_get_favicon` | `GET /api/v1/watch/{uuid}/favicon` |
| `changedetection_list_tags` | `GET /api/v1/tags` |
| `changedetection_get_tag` | `GET /api/v1/tag/{uuid}` |
| `changedetection_create_tag` | `POST /api/v1/tag` |
| `changedetection_update_tag` | `PUT /api/v1/tag/{uuid}` |
| `changedetection_delete_tag` | `DELETE /api/v1/tag/{uuid}` |
| `changedetection_recheck_tag` | `GET /api/v1/tag/{uuid}?recheck=1` |
| `changedetection_set_tag_muted` | `GET /api/v1/tag/{uuid}?muted=` |
| `changedetection_list_notifications` | `GET /api/v1/notifications` |
| `changedetection_add_notifications` | `POST /api/v1/notifications` |
| `changedetection_replace_notifications` | `PUT /api/v1/notifications` |
| `changedetection_delete_notifications` | `DELETE /api/v1/notifications` |
| `changedetection_search` | `GET /api/v1/search` |
| `changedetection_import_watches` | `POST /api/v1/import` as `text/plain` |
| `changedetection_systeminfo` | `GET /api/v1/systeminfo` |
| `changedetection_full_spec` | `GET /api/v1/full-spec` |
Create and update accept restock fields (`processor: restock_diff`, `in_stock_only`, `follow_price_changes`, `price_change_threshold_percent`) and LLM fields (`llm_backend_profile`, `llm_intent`, `llm_change_summary`, `llm_change_summary_mode`). Get watch returns `processor_config_restock_diff` when the instance provides it.
`from_timestamp` may be `previous`. `to_timestamp` and snapshot `timestamp` may be `latest`.
`changedetection_list_history` returns `{ uuid, count, snapshots }`. Each snapshot is `{ timestamp, iso_time }`. Snapshots are sorted by unix time, oldest first. The upstream history map values are server filesystem paths. The parser discards those values and never copies them into the result. Nested `history` maps on `changedetection_get_watch` and other JSON tools become the same snapshot list. Keys such as `filename` and `datastore_path` are dropped when they hold a server path.
## Environment
| Variable | Default | Role |
| ------------------------------- | ------------------------- | ------------------------------------------------------------------------------------ |
| `CHANGEDETECTION_URL` | `https://example.invalid` | Instance origin. No userinfo, query, or hash. `/api/v1` suffix is stripped |
| `CHANGEDETECTION_API_KEY` | unset | Required for an upstream call. At least 8 characters. Never printed |
| `CHANGEDETECTION_ALLOW_WRITES` | off | The literal `true` enables writes. Any other value keeps writes as a blocked preview |
| `CHANGEDETECTION_CONTACT` | `contact@example.com` | Contact inside the User-Agent. Must not contain the API key |
| `CHANGEDETECTION_PER_MINUTE` | `30` | Local sliding-window cap |
| `CHANGEDETECTION_CACHE_TTL_SEC` | `60` | TTL for successful reads. `0` disables the cache |
| `CHANGEDETECTION_TIMEOUT_MS` | `20000` | Upstream timeout |
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http`. `--http` forces HTTP |
| `PORT` | `3000` | HTTP listen port |
Invalid values are reported by field name and issue code. The rejected value is not included.
## Safety
A write tool returns a dry-run preview when `confirm` is omitted. The preview is the method, path, query, and body that would be sent.
`confirm` must be the boolean `true`. `false` and the string `"true"` are rejected.
When `confirm` is `true` and `CHANGEDETECTION_ALLOW_WRITES` is not `true`, the tool still returns the preview, sets `blocked` to true, and does not call the instance.
Writes include create, update, delete, import, recheck, pause or mute, and notification changes. Recheck uses GET on the upstream API. This connector still treats it as a write.
Delete watch also deletes that watch's history. Replace notifications overwrites the whole list.
The API key is removed from tool results, error text, and logs, including its URL-encoded form. It is refused in the query string, the base URL, the User-Agent, and a JSON request body. Notification URLs that carry webhook tokens, URL userinfo, and `Authorization` / cookie headers are redacted in output.
HTTP 402 is returned as `[upstream]` with a quota or payment note. HTTP 429 is `[rate-limit]`. HTTP 404 is `[not-found]`.
HTTP mode has no authentication. Do not expose it beyond localhost.
## Attribution and terms
- **Upstream source.** [changedetection.io](https://github.com/dgtlmoon/changedetection.io) by dgtlmoon, API v1 at `/api/v1/`. Docs: https://changedetection.io/docs/api_v1/index.html
- **License of the software.** Apache-2.0. Hosted use is also covered by the [changedetection.io terms of service](https://changedetection.io/terms-of-service).
- **Attribution text.** The provider does not require a credit line beyond the Apache-2.0 NOTICE for the software itself.
- **What this connector does not grant.** A lookup does not transfer trademarks or a right to imply that the provider endorses this project. Do not resell hosted access.
- **User-Agent.** Requests send `changedetection-mcp/0.1.0 (<CHANGEDETECTION_CONTACT>)`.
## Running the tests
The tests use mocked upstream responses. They need no API key and make no network calls to changedetection.io.
```bash
npm ci
npm test # unit and tool tests (vitest)
npm run ci # typecheck, lint, tests, and build
```
At the time of this commit, `npm test` reports 53 passing tests.
`npm run live-check` runs every tool against a real instance set in `CHANGEDETECTION_URL` and `CHANGEDETECTION_API_KEY`. It creates and then deletes test objects, so point it at a test instance and set `CHANGEDETECTION_ALLOW_WRITES=true` only there.
## Author
Issah Abubakari ([@si-essence](https://github.com/si-essence)).
This connector is also listed on [Bot Essentials](https://bot-essentials.netlify.app).
## Licence
MIT. See [LICENSE](LICENSE).
TDQS
Scored across 28 tools
Most tools target clear resource/action combinations, so an agent can usually select correctly. Minor overlap exists between recheck_tag and recheck_all with a tag filter, and dedicated state/mute tools vs general update tools.
All tools use the changedetection_ prefix and snake_case, with a mostly predictable verb_noun pattern. A few exceptions like systeminfo and full_spec are noun-oriented rather than verb_noun.
The broad Changedetection API surface justifies many of the 28 tools, and most map to distinct operations. However, 28 is heavy for an MCP tool set, and some convenience variants could be consolidated.
The set covers the core domain well: watch CRUD, tag CRUD, notification management, history/snapshots/diffs, rechecks, search, import, and system inspection. No obvious lifecycle gaps stand out.