How to Use Wigolo’s Diff and Watch Tools to Track Website Changes
Wigolo provides two MCP tools—diff for comparing page versions and watch for monitoring sites—that work together to detect changes through cached content and configurable polling intervals.
The KnockOutEZ/wigolo repository ships a lightweight, cache-aware website monitoring system built on the Model Context Protocol (MCP). By combining the diff tool’s multi-granularity comparison engine with the watch tool’s job scheduling, you can track website changes without running background daemons.
Understanding the Diff Tool
The diff tool, implemented in [src/tools/diff.ts](https://github.com/KnockOutEZ/wigolo/blob/main/src/tools/diff.ts), compares two page sources and returns structured change reports. It supports three output shapes and three comparison granularities to match your specific use case.
Input Resolution and Validation
The tool requires an object containing old and new fields. Each side accepts either a raw Markdown string (markdown) or a URL (url). When URLs are provided, the tool calls resolveSide (lines 23-64) to check the cache via getCachedContent. If the old URL is not cached, it returns a cache_miss envelope. Invalid URLs trigger an invalid_input envelope immediately.
Output Shapes and Granularity
Valid output shapes are unified, hunks, or summary. Valid granularities are line, word, or section. The tool validates these enums (lines 71-89) before invoking the computation engine. Unified output produces standard patches, hunks return arrays of change blocks, and summary provides a concise boolean result.
The Diff Engine
Actual computation happens in [src/cache/diff-engine.js](https://github.com/KnockOutEZ/wigolo/blob/main/src/cache/diff-engine.js) via computeDiffEnvelope. The engine performs LCS (Longest Common Subsequence) comparison at the line, word, or Markdown section level. Results are wrapped in a StageResult<DiffOutput> containing a changed flag and the diff content.
Truncation Safety
When pages exceed internal LCS table limits (line count or token count), the engine automatically marks results with truncated: true and falls back to summary shape. This prevents process crashes on extremely large pages (see tests in [tests/unit/tools/diff.test.ts](https://github.com/KnockOutEZ/wigolo/blob/main/tests/unit/tools/diff.test.ts) lines 23-68).
Understanding the Watch Tool
The watch tool in [src/tools/watch.ts](https://github.com/KnockOutEZ/wigolo/blob/main/src/tools/watch.ts) manages persistent monitoring jobs stored in SQLite. Unlike traditional monitoring systems, wigolo uses a lazy execution model with no background daemon.
Creating Watch Jobs
Use action: create to register URLs for monitoring. You must provide interval_seconds (minimum 60) and can optionally specify a CSS selector and notification mode (inline or webhook URL). The tool passes URLs through guardUrl for SSRF protection before persisting via createJob in [src/watch/store.ts](https://github.com/KnockOutEZ/wigolo/blob/main/src/watch/store.ts). Batch creation supports up to 1,000 URLs (lines 29-34).
Listing and Managing Jobs
- List:
action: listreturns all jobs fromlistJobs()with status, URL, and interval data. - Pause/Resume:
action: pauseoraction: resumeupdates job status viasetJobStatus. - Delete:
action: deleteremoves the job row entirely.
All management actions validate job_id presence and return invalid_input if the job is missing.
Running Checks
Manual checks use action: check with a job_id. The scheduler retrieves the job, fetches current content using the same pipeline as diff, runs the comparison, updates the content_hash, and returns a ChangeReport. The response includes the updated job record and a changes_since_last array showing what changed.
Lazy Execution Model
Wigolo does not run a background daemon. Jobs execute only when you explicitly call watch { action: 'check' } or when the server auto-dispatches scheduleOverdueCheck(router) (see [src/watch/scheduler.ts](https://github.com/KnockOutEZ/wigolo/blob/main/src/watch/scheduler.ts) lines 45-57) upon detecting overdue jobs during regular operation.
Practical Code Examples
One-Shot Diff Between Two URLs
Compare a cached version against a live version using unified line-level diff:
import { client } from 'wigolo';
await client.callTool({
name: 'diff',
arguments: {
old: { url: 'https://example.com/old-page' },
new: { url: 'https://example.com/new-page' },
output: 'unified',
granularity: 'line',
},
});
Result: Returns { ok: true, data: { changed: true, unified_diff: '--- a\n+++ b\n@@ …' } }.
Register a Watch Job
Poll a news site every 5 minutes with inline notifications:
await client.callTool({
name: 'watch',
arguments: {
action: 'create',
url: 'https://news.example.com',
interval_seconds: 300,
notification: 'inline',
},
});
List Active Jobs
Retrieve all monitoring jobs and their current status:
await client.callTool({
name: 'watch',
arguments: { action: 'list' }
});
Manually Trigger a Check
Force an immediate comparison for a specific job:
await client.callTool({
name: 'watch',
arguments: {
action: 'check',
job_id: 'job-1',
},
});
Pause or Delete a Job
Temporarily stop monitoring without removing the job:
await client.callTool({
name: 'watch',
arguments: {
action: 'pause',
job_id: 'job-1',
},
});
Permanently remove the job:
await client.callTool({
name: 'watch',
arguments: {
action: 'delete',
job_id: 'job-1',
},
});
Summary
- Diff Tool: Located in
src/tools/diff.ts, supportsunified/hunks/summaryoutputs withline/word/sectiongranularity, and safely handles large pages via truncation. - Watch Tool: Located in
src/tools/watch.ts, provides CRUD operations for monitoring jobs with 60-second minimum intervals and batch limits of 1,000 URLs. - Execution Model: Uses lazy scheduling via
scheduleOverdueCheckinsrc/watch/scheduler.ts—no persistent background process required. - Storage: Jobs persist in SQLite through
src/watch/store.ts, whilesrc/cache/store.jsmanages content caching for both tools. - Safety: SSRF protection via
guardUrland input validation with consistent error envelopes (invalid_input,cache_miss,diff_failed).
Frequently Asked Questions
What input formats does wigolo diff support?
The diff tool accepts two input types for each side of the comparison: a markdown string containing raw content, or a url string pointing to a web resource. When using URLs, the old version must already exist in the cache (fetched previously), while the new version is fetched live. Invalid URLs or uncached old URLs return structured error envelopes.
How does wigolo handle large pages during diffing?
The diff engine in src/cache/diff-engine.js implements truncation guards. When content exceeds LCS table capacity in terms of line count or token count, it automatically sets truncated: true in the result and falls back to summary output. This ensures the process remains stable even when comparing massive web pages.
Is there a background process running for watch jobs?
No. Wigolo uses a lazy execution model where watches only run when explicitly triggered via action: 'check' or when the server detects an overdue job and calls scheduleOverdueCheck. There is no daemon or cron process; the scheduler in src/watch/scheduler.ts coordinates checks on-demand.
What notification modes are available for watch jobs?
When creating a job with action: 'create', you can set notification to either inline (returns diff results directly in the MCP response) or provide a webhook URL to receive HTTP callbacks when changes are detected. The notification mode is stored with the job and used when generating ChangeReport results during check cycles.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →