Devpost Submission — Watchtower (English, copy-paste ready)

Track: New Slack Agent / MCP requirement: MCP server integration (external Scrapling MCP)


Project name

Watchtower

Tagline (one-liner)

Watchtower — a Slack agent that detects competitor pricing and copy changes the day they happen, powered by MCP.

Inspiration

Every product team watches competitors, and almost every team does it the same way: someone opens the competitor's pricing page "when they remember to," skims it, and hopes they'd notice a change. When a rival silently raises a plan price, days or weeks can pass before anyone reacts — lost revenue on underpriced plans, stale battle cards, sales caught off guard. We wanted that entire chore to live where the team already works: a Slack DM that answers "what changed?" with an exact, line-level diff.

What it does

Watchtower is a Slack agent for competitive-intelligence monitoring:

  1. Watch — DM it "Watch this pricing page: ". The agent fetches the page through an external Scrapling MCP server (stealth fetch that gets past bot walls), and saves a normalized baseline snapshot.
  2. Detect — Later, ask "What changed?". It re-fetches the live page, diffs it against the baseline, and reports only material changes — with price/plan lines highlighted and tracking-ID / footer noise automatically filtered out.
  3. Explain — Instead of a raw diff, the agent summarizes the business meaning of the change in the DM.

Measured, real-world result (shown in the demo video): from a live 335-line baseline of Notion's pricing page, Watchtower pinpointed a single price movement — "Business plan: ¥2,990 → ¥3,150 (+5.4%)" — as a clean one-line finding, while correctly dismissing tracking-parameter churn as noise.

Measurable impact:

  • A manual daily competitor check (~10–15 min per page, easy to forget) becomes a ~2.5-minute on-demand agent run inside Slack: baseline capture completes in ~2 min, a full re-fetch + diff + summary in ~2.5 min.
  • Detection latency drops from "whenever someone looks" to same-day.
  • Signal quality is line-exact: 1 real change surfaced out of a 335-line page, 0 false positives from tracking IDs or footers in our recorded run.
  • Reliability is enforced by 30/30 automated tests (node --test), including regression tests for the diff engine.

How we built it

  • Slack layer: Bolt for JavaScript (Socket Mode), built from the official bolt-js-starter-agent (Claude Agent SDK variant). Assistant threads, DM chat tab, and app-home surfaces.
  • Agent layer: the Claude Agent SDK runs the reasoning loop and acts as an MCP client. This is the core of the submission: the agent connects to an external Scrapling MCP server (stdio) for robust stealth fetching — an ability the agent could not have natively — and optionally to the Slack MCP server for canvas-based watchlists.
  • Snapshot/diff engine: custom save_snapshot / compare_snapshot tools. Pages are normalized into line sets, stored as sha256-keyed JSON baselines, and diffed with price/plan-line highlighting and noise filtering.
  • Hardening: SSRF guard (assertPublicUrl) on every fetched URL, environment validation at startup, generic user-facing error messages, secrets kept out of the repo (.env gitignored).
  • Quality: 30/30 unit tests, plus code review and security review passes (0 critical / 0 high findings).

Challenges we ran into

  1. Stealth fetch broke the diff engine. One day the Scrapling stealth fetch started returning the entire page as a single 8,234-character line with escaped \n sequences instead of real newlines. Our line-set diff collapsed to "everything changed," burying the real price movement in noise. Fix: normalizeLines now splits on escaped newlines too (/\r?\n|\\r\\n|\\n/), restoring stable multi-line baselines — implemented test-first with a dedicated regression test.
  2. The agent was too helpful. During comparisons, the agent would sometimes re-save the baseline before diffing — silently destroying the very state it was supposed to compare against. We solved it with explicit tool-use constraints in the prompt design ("compare only, never overwrite the baseline").
  3. API credits ran dry mid-project. We switched the Claude Agent SDK from pay-per-use API-key auth to subscription OAuth token auth, with a safe fallback order when both are configured.

Accomplishments we're proud of

  • A real, end-to-end detection on a live production website (Notion's pricing page), not a mocked demo — captured on video.
  • A diff engine that survives hostile real-world HTML: escaped-newline payloads, tracking-ID churn, full-width currency characters (¥ vs ¥).
  • A fully reproducible demo-video pipeline (scene manifest → TTS narration → authenticated Slack screen recording → FFmpeg assembly → automated QA gate).

What we learned

  • MCP is the right seam: keeping fetching in a dedicated MCP server means the Slack agent never needs scraping code, and the fetcher can be swapped or upgraded independently.
  • Agent tool design is prompt design: a tool that can overwrite state will eventually be called at the wrong time unless the instructions make the safe path the default.
  • Line-set diffs beat text diffs for monitoring: normalize aggressively, filter noise explicitly, and highlight the domain signal (prices) instead of showing raw output.

What's next for Watchtower

  • Scheduled patrols: cron-driven re-checks that proactively post diffs to a channel (no one even has to ask).
  • Watchlists: multiple URLs per team, managed in a Slack canvas via the Slack MCP server.
  • Always-on hosting: OAuth/HTTP mode deployment so the agent responds 24/7 without a local process.
  • Richer targets: copy/messaging changes, new feature announcements, and job-page monitoring with the same baseline/diff engine.

Built with

Node.js · Bolt for JavaScript (Socket Mode) · Claude Agent SDK · MCP (Model Context Protocol) · Scrapling MCP server · Zod · node:test · FFmpeg + Playwright (demo pipeline)

How judges can test it (Slack sandbox)

  1. We've invited [email protected] and [email protected] to the sandbox; open a DM with Watchtower (or /invite @Watchtower in a channel, then @mention it).
  2. Send: Watch this pricing page: https://www.notion.com/pricing → the agent stealth-fetches the page via the Scrapling MCP server and saves a baseline (shows the captured prices).
  3. Send: What changed on https://www.notion.com/pricing ? → it re-fetches and diffs against the baseline (no change yet → "no material change", or a price diff if the page moved).
  4. The 3-minute demo video shows the full save → change-detected → compare flow end to end.

Note: the agent runs in Socket Mode; it responds while our app process is connected to the sandbox. The demo video is the canonical reference for full functionality.

Built With

Share this project:

Updates

Submission history