durable-web-monitor-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@durable-web-monitor-mcpmonitor https://example.com/pricing for changes and alert me"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
durable-web-monitor-mcp
A small, security-conscious reference implementation for durable website-change monitoring with MCP.
It demonstrates a pattern that is useful for long-running assistants and automations:
baseline → detect → notify → acknowledge → durable inbox
The monitor supports two fetch modes:
direct HTTPS for ordinary pages;
isolated Playwright MCP for pages that need browser rendering.
Each browser fetch launches a fresh, non-persistent Playwright MCP subprocess with --isolated, then tears it down after the snapshot.
Why this exists
A reliable page monitor needs more than "fetch URL and compare strings". It should:
avoid alerting on the first observation;
normalize content before hashing;
persist baseline state across process restarts;
keep notifications until they are acknowledged;
separate "delivered" notifications from "reviewed" state;
use a disposable browser session for dynamic pages;
reject obviously unsafe network targets before fetching;
make the monitoring state inspectable through MCP.
This repository is deliberately generic. It does not contain private infrastructure, credentials, account identifiers, internal hostnames, or environment-specific deployment configuration.
Related MCP server: Doc Monitor MCP
Security model
This project is a reference implementation, not a network sandbox.
It rejects non-HTTPS URLs and hostnames that resolve to loopback, private, link-local, multicast, reserved, or unspecified IP addresses. Redirect targets are revalidated in the direct HTTP adapter.
Application-level URL checks cannot completely eliminate DNS rebinding or browser redirect risk. For untrusted URLs, run the monitor in a container/VM with restrictive egress rules and no access to metadata services or private networks. See SECURITY.md.
The browser adapter uses Playwright MCP's --sandbox and --isolated modes. Isolation prevents normal browser profile persistence between checks; neither option makes Playwright MCP a complete security boundary.
Requirements
Python 3.10+
Node.js 18+ and
npxonly if you use the browser adaptermcp>=2,<3Chromium available either through the pinned Playwright build or as a trusted system executable
Install
python -m venv .venv
source .venv/bin/activate
pip install -e .The package installs two commands:
dwm— CLI for checks and notification handling;dwm-mcp— stdio MCP server.
For the default browser-backed setup, install Chromium once for the Playwright build used by Playwright MCP v0.0.80:
npx -y playwright@1.63.0-alpha-2026-08-31 install chromiumIf your deployment manages Chromium separately, point the monitor at that trusted executable instead:
export DWM_BROWSER_EXECUTABLE_PATH=/path/to/chromiumWhen this variable is set, the browser adapter passes that path to Playwright MCP with --executable-path. The package never hard-codes a host-specific Chromium location.
Run as an MCP server
dwm-mcpThis starts the bundled MCP server over stdio without requiring the optional MCP SDK CLI extra.
The server exposes:
check_urllist_notificationsmark_notifications_reviewedmonitor_status
The SQLite database defaults to:
~/.local/share/durable-web-monitor-mcp/state.sqlite3Override it with DWM_STATE_PATH.
Example MCP flow
Call
check_urlwith a stable monitor name and URL.The first successful check stores a baseline and emits no notification.
Later content changes create durable notifications.
Call
list_notifications(status="new").After successfully presenting those notifications to the user, call
mark_notifications_reviewed(through_seq=...).
That last step matters: reading the inbox does not itself advance the review cursor.
Direct HTTPS example
{
"name": "example-docs",
"url": "https://example.com/docs",
"adapter": "direct",
"watch_terms": []
}An empty watch_terms list means any normalized content change is noteworthy.
Browser example
{
"name": "dynamic-status",
"url": "https://example.com/status",
"adapter": "browser",
"target": "body",
"watch_terms": ["incident", "degraded"]
}v0.1.0 deliberately pins the browser integration to the version tested for this release:
npx -y @playwright/mcp@0.0.80 --headless --sandbox --isolated --browser chromium --image-responses omit --codegen noneIf DWM_BROWSER_EXECUTABLE_PATH is set, --executable-path <value> is added to that command.
Playwright accessibility snapshots contain generated locator references such as [ref=e17]. The monitor removes those ephemeral references before hashing so locator churn does not create false change alerts.
Do not silently switch the package to @latest in production. Upgrade the pinned Playwright MCP and matching Playwright browser build deliberately, reinstall the browser binary if needed, and rerun the unit and browser integration tests first.
CLI
The same engine can be driven by cron, systemd timers, CI, or another scheduler:
dwm check \
--name example-docs \
--url https://example.com/docs \
--adapter direct
dwm inbox --status new
dwm ack --through-seq 12A scheduler is intentionally outside this package. Keeping scheduling separate makes the monitor usable with cron, systemd timers, GitHub Actions, container schedulers, or an agent platform without coupling the durable monitoring model to one runtime.
Normalization
Direct HTML fetches are reduced to visible text with scripts/styles/comments removed, HTML entities decoded, and whitespace collapsed.
Browser snapshots are normalized as text by removing generated Playwright locator references, trimming line endings, collapsing horizontal whitespace, and removing repeated blank lines.
The normalized text is SHA-256 hashed. The database stores hashes and bounded excerpts rather than complete page bodies.
Testing
Core tests use only the Python standard library:
python -m unittest discover -s tests -vThe Playwright MCP browser adapter is intentionally an integration boundary and is not launched by the unit tests.
Before publishing a release, also test installation in a fresh virtual environment, start the installed dwm-mcp entry point, and run real direct-HTTPS and isolated-browser checks against non-sensitive public pages. If using a system Chromium deployment, run the browser test with the same DWM_BROWSER_EXECUTABLE_PATH that production will use.
Design notes
See docs/ARCHITECTURE.md for the state machine, notification cursor model, browser-isolation rationale, and known limits.
License
Apache-2.0. See LICENSE.
Available Tools
4 toolscheck_urlC
Fetch one public HTTPS page and update its durable baseline.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| name | Yes | ||
| target | No | body | |
| adapter | No | direct | |
| watch_terms | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose two real traits: the fetch is outbound to a *public HTTPS* page (a network + reachability constraint) and the call mutates persistent state ('update its durable baseline'), so an agent knows it is not read-only. It stops there, saying nothing about permissions, what happens to the prior baseline, rate limits, or failure behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler or redundancy; the action and the state change come first. It is efficient, though the extreme terseness borders on under-specification rather than crispness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter mutation tool with no annotations, no output schema, and no schema descriptions, one sentence is not enough. The agent cannot determine what target, adapter, or watch_terms do, what 'baseline' means, or what the call returns or changes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Five parameters at 0% schema description coverage, and the description names none of them. 'Fetch one public HTTPS page' weakly constrains url, and 'durable baseline' hints at name, but target, adapter, and watch_terms are entirely opaque in both description and schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb pair (fetch + update) and resource (a public HTTPS page / its durable baseline), which is more specific than the sibling names list_notifications or mark_notifications_reviewed. The term 'durable baseline' is internal jargon that isn't defined, so the purpose is clear in shape but not fully in meaning, and it doesn't explicitly differentiate itself from monitor_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use, when-not-to-use, or alternative routing is given. With a sibling like monitor_status clearly adjacent in function, the description should say how check_url relates to it, but it offers nothing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_notificationsB
List durable notifications without advancing the review cursor.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| status | No | new |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It usefully discloses that the review cursor is not advanced, which is a key non-mutating behavior. However, it omits other relevant traits such as read-only safety, idempotency, and how defaults/status filtering affect results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with zero waste, front-loading the action and the key behavioral constraint. It is appropriately sized for a simple list tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with two undocumented parameters and no output schema, the description is incomplete. It covers purpose and one behavioral trait but omits parameter semantics and return value shape, leaving the agent without enough context to invoke it confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has two parameters (limit, status) with 0% description coverage, and the description does not mention either parameter. It adds no meaning beyond the schema's names and defaults, leaving the agent to infer semantics for 'status' and 'limit'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (List) and resource (durable notifications), and the clause 'without advancing the review cursor' distinguishes it from the sibling mark_notifications_reviewed. It is clear without opening the schema, though 'durable' is slightly underspecified.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It implies when to use this tool (when you want to view notifications without marking them reviewed), but does not explicitly name alternatives or state when-not-to-use. The contrast with the sibling is implicit rather than spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_notifications_reviewedA
Advance the durable notification cursor after successful handling.
| Name | Required | Description | Default |
|---|---|---|---|
| through_seq | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses the key behavioral trait that this is a cursor-advancing mutation and that it's meant to be done post-handling, which guards against premature calls. However, it doesn't state whether advancement is idempotent, reversible, or what effect skipping it has – meaningful gaps for a state mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence with no waste; the precondition is front-loaded in the trailing clause so the agent reads the critical 'when' immediately after the action.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-param mutation with no annotations and no output schema, the description is minimally viable: it conveys intent and timing but omits parameter meaning and behavioral specifics (idempotency, error effects) that an agent would need to invoke it confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate, yet it never mentions through_seq or explains that it marks all notifications up to and including that sequence number. The phrase 'durable notification cursor' hints at the concept but provides no semantics for the sole parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('advance') and resource ('durable notification cursor'), and adds the precondition 'after successful handling'. It's clear what it does, though 'durable notification cursor' is jargon that assumes familiarity with the notification model.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The clause 'after successful handling' tells the agent when to call it – following the successful processing of notifications obtained elsewhere (presumably list_notifications). It doesn't name the sibling explicitly or state exclusions, keeping it out of the top tier.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
monitor_statusC
Return bounded durable inbox state and storage location.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, no output schema, and no parameter schema, the description carries the full behavioral burden. "Bounded" hints at a size cap and "durable" hints at persistence, but neither is explained — no indication of what is returned, whether the call is read-only, or what 'storage location' means.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single compact sentence that is front-loaded with the object of the operation. It is efficient, though the terseness veers toward under-specification rather than tight writing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no annotations, the description should describe the returned state more concretely, and it only gestures at it via "bounded durable inbox state and storage location". For a simple zero-parameter read this is minimally adequate but leaves the agent unsure of what it will receive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate. Baseline 4 applies; no parameter-level gaps exist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a verb ("Return") and resources ("inbox state", "storage location"), so the basic purpose is inferable. However, "bounded durable inbox state" is opaque jargon, and nothing distinguishes this from sibling list_notifications or explains what an 'inbox' means in this server's context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no prerequisites, and no mention of alternatives such as list_notifications or check_url. The agent must guess whether this is a diagnostic read, a polling endpoint, or a complement to the notification tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
check_url - First observed
list_notifications - First observed
mark_notifications_reviewed - First observed
monitor_status
TDQS
Scored across 4 tools
Each tool targets a distinct action: check_url for fetching, list_notifications for retrieving, mark_notifications_reviewed for updating state, and monitor_status for reporting. However, list_notifications and monitor_status could overlap if an agent wants to check notifications without advancing the cursor, but the descriptions clarify their distinct purposes.
All tool names follow a consistent verb_noun pattern (check_url, list_notifications, mark_notifications_reviewed, monitor_status). This pattern is predictable and easy to understand.
With only 4 tools, the server is well-scoped and each tool appears to serve a specific purpose in the monitor's workflow. This count is appropriate for a focused durable web monitoring tool.
The tool set covers the core lifecycle: checking a URL, listing notifications, marking them reviewed, and checking status. However, there is no tool to configure or remove monitors, which could be a gap for managing multiple URLs or cleaning up baselines.
Maintenance
Related MCP Connectors
Watch a public web page for changes when your agent cannot stay running. Hourly checks, signed diffs
Evidence-bearing public webpage monitoring with hashes, diffs, signed webhooks, REST, and MCP.
AI-powered website change monitoring - manage monitors and alerts via MCP.
Give an AI agent eyes on the web: turn any feed, page, or stream into deduplicated change events.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMonitor any website and get AI-enriched change intelligence via MCP. Manage sources, search changes, and automate web monitoring from Claude, Harvey, or any MCP client.30 npmMIT
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to monitor web documentation for changes, perform semantic search with RAG, and analyze breaking changes in APIs.9-
- AlicenseAqualityDmaintenanceEnables monitoring web pages for changes using Playwright, storing snapshots in SQLite, and optionally analyzing changes with an LLM for business impact assessment.5MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to monitor web pages for content changes and receive alerts when changes occur.-