DiffiCat
Compare a website before and after a migration: missing pages, content changes, visual diffs.
Install
One-Click Install
Add this to your `claude_desktop_config.json` file:
{
"mcpServers": {
"difficat": {
"url": "https://app.difficat.com/mcp"
}
}
}Remote endpoints
https://app.difficat.com/mcpstreamable-httpWhat it can do
Tool inventory
Tools (7)
🟢discover_pages(sourceUrl, targetUrl, sitemapUrl, maxPages)
List the pages of Site A (sitemap, robots.txt, or crawl), mapped onto Site B when given. Paid in USDC (the 402 challenge names the network): 0.05 USD per call.
Input Schema
{
"type": "object",
"properties": {
"sourceUrl": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"description": "Site A (full URL)."
},
"targetUrl": {
"description": "Site B; pages are mapped onto it when given.",
"type": "string",
"minLength": 1,
"maxLength": 2048
},
"sitemapUrl": {
"description": "Sitemap to read instead of the fallbacks.",
"type": "string",
"minLength": 1,
"maxLength": 2048
},
"maxPages": {
"description": "Stop after this many pages.",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": [
"sourceUrl"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}🟢capture_screenshot(url, auth, viewportWidth)
Full-page PNG of one URL. Returns the image plus a jobId/jobToken for get_screenshot. Paid in USDC (the 402 challenge names the network): 0.02 USD per call.
Input Schema
{
"type": "object",
"properties": {
"url": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"description": "Page to capture (full URL)."
},
"auth": {
"description": "HTTP basic auth for the page.",
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 512
},
"password": {
"type": "string",
"maxLength": 512
}
},
"required": [
"username",
"password"
]
},
"viewportWidth": {
"description": "Capture width in px.",
"type": "integer",
"minimum": 320,
"maximum": 2560
}
},
"required": [
"url"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}🟢start_comparison(sourceUrl, targetUrl, sourceAuth, targetAuth, urls, ...)
Compare Site A against Site B on page existence, content and visual regression. Charged upfront in USDC (the 402 challenge names the network): 0.02 USD per page for the first 1000 pages and 0.01 USD per page beyond. maxPages is optional: a ceiling, not a reservation. A job is priced on the pages it compares (the urls list, or the pages discovery finds, at most maxPages), the challenge names the count, and there is no page limit. Poll get_comparison, then get_report (paged with offset and limit).
Input Schema
{
"type": "object",
"properties": {
"sourceUrl": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"description": "Site A, the reference site (full URL)."
},
"targetUrl": {
"description": "Site B, the migrated site. Omit to capture screenshots of Site A only.",
"type": "string",
"minLength": 1,
"maxLength": 2048
},
"sourceAuth": {
"description": "HTTP basic auth for Site A.",
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 512
},
"password": {
"type": "string",
"maxLength": 512
}
},
"required": [
"username",
"password"
]
},
"targetAuth": {
"description": "HTTP basic auth for Site B.",
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 512
},
"password": {
"type": "string",
"maxLength": 512
}
},
"required": [
"username",
"password"
]
},
"urls": {
"description": "Explicit page pairs to compare; skips discovery and is priced per pair.",
"minItems": 1,
"type": "array",
"items": {
"type": "object",
"properties": {
"source": {
"type": "string",
"minLength": 1,
"maxLength": 2048
},
"target": {
"type": "string",
"minLength": 1,
"maxLength": 2048
}
},
"required": [
"source",
"target"
]
}
},
"maxPages": {
"description": "Optional ceiling on the pages compared and charged for. The job is priced on the pages it compares: the urls list, or the pages discovery finds, at most this many. There is no page limit.",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"threshold": {
"description": "Visual diff tolerance as a fraction, 0 (exact) to 1.",
"type": "number",
"minimum": 0,
"maximum": 1
},
"sitemapUrl": {
"description": "Sitemap to discover pages from.",
"type": "string",
"minLength": 1,
"maxLength": 2048
},
"viewportWidth": {
"description": "Capture width in px.",
"type": "integer",
"minimum": 320,
"maximum": 2560
}
},
"required": [
"sourceUrl"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}🟢get_comparison(jobId, jobToken)
Status and progress of a job. Free.
Input Schema
{
"type": "object",
"properties": {
"jobId": {
"type": "string",
"pattern": "^web-[a-z0-9-]+$"
},
"jobToken": {
"type": "string",
"minLength": 20,
"maxLength": 200,
"description": "The jobToken returned when the job was created."
}
},
"required": [
"jobId",
"jobToken"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}🔴cancel_comparison(jobId, jobToken)
Stop a running job; the job reports cancelled within a minute, with the pages compared so far in its report. Free.
Input Schema
{
"type": "object",
"properties": {
"jobId": {
"type": "string",
"pattern": "^web-[a-z0-9-]+$"
},
"jobToken": {
"type": "string",
"minLength": 20,
"maxLength": 200,
"description": "The jobToken returned when the job was created."
}
},
"required": [
"jobId",
"jobToken"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}🟢get_report(jobId, jobToken, offset, limit)
The finished report: summary over every page, a slice of the per-page results (offset and limit, default 1000 a call; nextOffset points at the next slice), and the plain-text report over that slice. Free.
Input Schema
{
"type": "object",
"properties": {
"jobId": {
"type": "string",
"pattern": "^web-[a-z0-9-]+$"
},
"jobToken": {
"type": "string",
"minLength": 20,
"maxLength": 200,
"description": "The jobToken returned when the job was created."
},
"offset": {
"description": "Index of the first page result to return (default 0).",
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"limit": {
"description": "Page results to return (default 1000, at most 5000).",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": [
"jobId",
"jobToken"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}🟢get_screenshot(jobId, jobToken, filename)
A screenshot PNG named in the report or returned by capture_screenshot. Free.
Input Schema
{
"type": "object",
"properties": {
"jobId": {
"type": "string",
"pattern": "^web-[a-z0-9-]+$"
},
"jobToken": {
"type": "string",
"minLength": 20,
"maxLength": 200,
"description": "The jobToken returned when the job was created."
},
"filename": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]+\\.png$",
"description": "A screenshot filename from the report."
}
},
"required": [
"jobId",
"jobToken",
"filename"
],
"$schema": "http://json-schema.org/draft-07/schema#"
}Community
Evidence