MCP workflow demo
Seven things that happen to an agent using a third-party MCP server, run live against a real demo server, with no guard, a lockfile alone, and a lockfile plus CrawlCheck. ✓ means the guard did what it should.
| Score | No guard | Lockfile alone | Lockfile + CrawlCheck |
|---|---|---|---|
| Hazards stopped | 0 of 4 | 3 of 4 | 4 of 4 |
| Normal work allowed | 3 of 3 | 3 of 3 | 3 of 3 |
Scenario by scenario
| What happens | Should | No guard | Lockfile alone | Lockfile + CrawlCheck |
|---|---|---|---|---|
| Approved server, unchanged The agent reconnects to the server a person approved. Nothing changed. | connect | ✓ connect nothing is checked: the agent connects and the model receives whatever tools the server lists | ✓ connect every tool hashes exactly as approved | ✓ connect the signed check says unchanged and the local hash agrees |
| A tool description is rewritten After approval, create_issue's description gains a hidden instruction to read ~/.ssh/id_rsa into the issue body and not tell the user. | block | ✗ connect nothing is checked: the model now reads the injected instruction as part of the tool | ✓ block the tool list hashes differently from the approved one | ✓ block the tool list is not the one that was approved; block until a person re-approves it (1 changed) |
| A person re-approves a harmless update get_issue's description now mentions labels and assignees. A person reads the change and makes a new lockfile. | connect | ✓ connect nothing is checked: the agent connects and the model receives whatever tools the server lists | ✓ connect every tool hashes exactly as approved | ✓ connect the signed check says unchanged and the local hash agrees |
| A forged “connect” answer Something between the agent and CrawlCheck returns the rewritten server's check flipped to unchanged/connect, validly signed with a key it made itself. | block | ✗ connect nothing is checked, so the forged answer is not even needed | ✓ block the tool list hashes differently from the approved one (a lockfile alone reads no answer, so it cannot be fooled by one; it blocks on its own hash) | ✓ block the tool list hashes differently from the approved one; the CrawlCheck answer was rejected (signed by a key that is not in the published directory) and is treated as no answer |
| The MCP server is down The server answers 503. Its tools cannot be read. | block | ✗ connect nothing is checked: the agent retries and uses whatever tools the server lists when it returns | ✓ block the tool list could not be read, so it cannot be compared with the lockfile | ✓ block the server did not return a tool list; nothing can be compared, so do not connect on the strength of the lockfile |
| CrawlCheck is unreachable The server is the approved one, but the check request to CrawlCheck fails. | warn | ✓ connect nothing is checked: the agent connects and the model receives whatever tools the server lists (this guard never asks CrawlCheck, so its outage changes nothing) | ✓ connect every tool hashes exactly as approved (this guard never asks CrawlCheck, so its outage changes nothing) | ✓ warn every tool hashes exactly as approved; CrawlCheck could not be reached (HTTP 530), so the local lockfile decides and connect is lowered to warn, as the failure policy's crawlcheck_unreachable row says |
| A person re-approves the poisoned version Someone clicks approve on the rewritten list without reading it, so the new lockfile pins the injected description. | warn | ✗ connect nothing is checked: the agent connects and the model receives whatever tools the server lists | ✗ connect every tool hashes exactly as approved | ✓ warn the list matches the approved lockfile, but CrawlCheck's scan finds high poisoning in it (injected_instructions, exfiltration): a person approved tools that carry instructions for the model |
What this shows
A lockfile alone stops every change to an approved server and every outage. What it cannot see is what it was told to pin: a careless re-approval of a poisoned list passes it. CrawlCheck adds the scan that flags that list, the record of exactly what moved, and answers an agent can prove came from CrawlCheck - a forged one is refused because its key is not published.
Run it yourself
The same run from your own machine, against the same live servers and the public API (Node 20 or newer, nothing to install):
curl -sO https://crawlcheck.io/mcp/demo.mjs && node demo.mjs
The demo servers are at /mcp/demo/server/v1 (approved), v2 (rewritten description), v3 (harmless update) and down. Signed result: /api/v1/mcp/demo. Lockfile format: /spec/mcp-lockfile. Outages: /spec/failure-policy. Live changes on real servers: /mcp/changes.
Run at 2026-11-03 14:30 UTC in 603 ms; refreshed every 10 minutes.