Change Detection Store
An MCP server that answers one question well: 'has this JSON changed since I last saw it?'
README
Change Detection Store
An MCP server that answers one question well: "has this JSON changed since I last saw it?"
Why this exists
Say you want to track prices for a list of products, catch when an API response quietly changes shape, or watch stock levels across a few shops. The shape of the job is always the same: fetch the current state, compare it with what you saw last time, act only when something actually changed.
The fetching part is easy — a cron job, a script, or an LLM agent with a browsing tool. The remembering-and-comparing part is where projects get messy. Today you'd pick one of:
- A hand-rolled snapshot store. A table or a bucket of JSON files, plus comparison
code you rewrite for every project. Sooner or later it burns you, because the same data
serialized twice is rarely byte-identical (key order,
1.0vs1) — so you get "changes" that aren't. - A full monitoring product like changedetection.io or Visualping. Great when you want their fetcher, scheduler and notifications. Overkill when you already own the fetching side and only miss the memory.
- (For agents) pasting the previous value into the prompt and letting the model do the diffing. Works, but you pay tokens for it on every run and the comparison quality depends on the model's mood.
This project is the missing middle piece: a small service that only remembers and
compares. Your script or agent sends the current value; the store hashes it canonically,
writes only when the hash differs from last time, and answers changed: true/false.
History accumulates real changes only, so "what happened to this key over the last
month" is one call.
To be clear about the scope: it's a building block, not a monitoring product. It doesn't fetch, doesn't schedule, doesn't notify. It remembers and compares — that's the whole job, and it tries to do that one job properly: canonical hashing, concurrency-safe writes, a change history that cleans up after itself.
It speaks MCP, so you can add it to Claude as a custom connector and let the agent call the tools directly — but anything that can do OAuth and JSON-RPC can talk to it.
What a run looks like
sequenceDiagram
participant A as Your agent / script
participant S as Change Detection Store
participant D as DynamoDB
Note over A,S: every run starts the same way
A->>S: create_store("prices")
S-->>A: { created: false } — already exists, that's fine
A->>S: patch_items(30 items with fresh values)
S->>S: hash each value (RFC 8785 canonical JSON)
S->>D: write only what differs
S-->>A: 28 × changed:false, 2 × changed:true
Note over A: act on the 2 real changes
A->>S: get_item_history("prices", "product-8471")
S-->>A: today's price, last week's price, ...
Twenty-eight of those thirty writes cost nothing — no DynamoDB write, no history entry, no noise. That asymmetry is the point: polling is cheap, only change is expensive.
How the change detection works
Values are hashed as SHA-256 over RFC 8785 canonical JSON. In practice:
- key order doesn't matter —
{"a":1,"b":2}and{"b":2,"a":1}are the same value - number formatting doesn't matter —
1.0,1e0and1are the same - array order DOES matter —
[1,2]is not[2,1]; sort arrays client-side if your source returns them in random order
Every patch also takes an optional meta field. It's stored every time but never hashed —
put things like lastSeenAt in there, so a timestamp doesn't count as a "change".
Deletes are soft: data disappears immediately and DynamoDB TTL removes it physically within about 7 days. History entries expire after 30 days on their own.
Architecture
flowchart LR
C["Claude / your script"] -- "MCP + OAuth 2.0" --> L["Lambda<br/>(Function URL)"]
L --> D[("DynamoDB<br/>single table, PK+SK")]
L --> G["Cognito<br/>hosted login, group authz"]
L --> M["Secrets Manager<br/>client credentials"]
One Lambda serves everything: the MCP endpoint, the OAuth discovery/proxy endpoints and
the JWT gate. Storage is a single DynamoDB table (partition + sort key, no indexes) with
on-demand billing — idle cost is close to zero. There's a CloudWatch dashboard
(cds-health) with notes on how to read it, and an SNS topic for alarms.
Tools
create_store— idempotent; call it at the start of every run, it just sayscreated: true/falsepatch_item/patch_items— the core; batch takes up to 50 items and reports per keyget_item— returns{ found: false }for a key that has no value yet (that's normal, not an error)get_item_history— the change timeline of a key, newest firstlist_stores,list_items,delete_item,delete_store
Running it locally
Node 22 or newer.
npm install
npm test # unit tests + CDK template assertions
npm run test:integration # storage contract against DynamoDB Local (needs docker,
# or point CDS_DYNAMODB_ENDPOINT at a running instance)
npm run dev --workspace app # MCP server on http://localhost:3000/mcp, no auth, in-memory
Point MCP Inspector at the dev server to poke around.
Deploying
You need an AWS account and credentials in your shell. Everything lands in eu-central-1
(edit infra/bin/app.ts for another region).
npx cdk bootstrap aws://<account-id>/eu-central-1 # once per account/region
npm run deploy --workspace infra
npm run create-user --workspace scripts -- --email you@example.com
npm run connection-info --workspace scripts
The last command prints the MCP URL and the OAuth client id. In Claude: Settings → Connectors → Add custom connector → paste the URL, put the client id under Advanced settings, connect, sign in with the user you just created.
Access is a Cognito group (cds-allowed) — the create-user script puts people in it.
There's no self-signup, and the app client secret never leaves Secrets Manager.
Cost: close to nothing at low traffic. The KMS key is about a dollar a month; Lambda,
DynamoDB on-demand and Cognito sit in or near their free tiers. Subscribe your email to
the AlarmTopicArn stack output if you want to hear about problems.
Limitations, so you know what you're getting
- No notifications. Nothing pushes when a value changes — your client asks. If you need push, DynamoDB Streams is the natural extension point.
- No diffs. You get the current value and the history; comparing versions is on you.
- Values up to 64 KB after canonicalization, history kept 30 days, list pages of 100.
- Store names:
a-z0-9_-, 3–12 chars. Item keys also allowA-Zand|, up to 32 chars, case-sensitive (external ids likesource|ID6HfGmafit as-is). - One shared space: every user in the group sees all stores. No multi-tenancy.
- No web UI — MCP only.
- Concurrency is capped at 10 on purpose: it's a hard cost ceiling for an endpoint
that's public by nature. Raise
RESERVED_CONCURRENCYin the api construct if you need more. - Function URL quirk: the 401 challenge comes back as
x-amzn-Remapped-www-authenticate. MCP clients don't care (they use the well-known discovery endpoints), but your curl scripts might.
License
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.