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: list returns all jobs from listJobs() with status, URL, and interval data.
  • Pause/Resume: action: pause or action: resume updates job status via setJobStatus.
  • Delete: action: delete removes 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, supports unified/hunks/summary outputs with line/word/section granularity, 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 scheduleOverdueCheck in src/watch/scheduler.ts—no persistent background process required.
  • Storage: Jobs persist in SQLite through src/watch/store.ts, while src/cache/store.js manages content caching for both tools.
  • Safety: SSRF protection via guardUrl and 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →