differino-mcp
# differino-mcp
MCP (Model Context Protocol) server for [Differino](https://www.differino.com) -- compare PDF, DOCX, and TXT documents from any AI agent.
## Setup
### 1. Install
```bash
pnpm add -g differino-mcp
```
### 2. Get an API Key
1. Sign up at [differino.com](https://www.differino.com/signup)
2. Go to **Settings → API Key**
3. Click **Generate Key** and copy it
### 3. Add to Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"differino": {
"command": "pnpm",
"args": ["dlx", "differino-mcp"],
"env": {
"DIFFERINO_API_KEY": "dfn_your_api_key_here"
}
}
}
}
```
### Local development
From the repository root:
```bash
pnpm install
pnpm --dir mcp build
```
| Variable | Required | Description |
|----------|----------|-------------|
| `DIFFERINO_API_KEY` | Yes | Your API key -- generate at Settings → API Key |
| `DIFFERINO_URL` | No | Base URL (default: `https://www.differino.com`) |
## Tools
### compare_documents
Compare two local files and get a diff summary plus the visual document-review manifest when available. This is the only tool that consumes a comparison (your free comparison or 1 credit).
**Parameters:**
- `file_a_path` (required) -- Absolute path to the original document
- `file_b_path` (required) -- Absolute path to the modified document
- `comparison_mode` (optional) -- `visual` for full document review, or `text` for fast text-only comparison (default: `visual`)
The tool uploads both files, waits for extraction and comparison, and returns a summary with added/removed/modified block counts plus a link to the full comparison. Successful responses include `creditsRemaining` and `freeComparisonRemaining`. If the server is still working when the internal wait expires, the response has `status: "processing"`; poll `get_comparison` with the returned `id` until `status` is `ready`.
Files up to 45 MB each are supported. Small pairs (up to ~4 MB combined) go through the multipart endpoint; larger files are uploaded automatically via signed upload URLs (`POST /api/v1/uploads` -> direct PUT to storage -> `POST /api/v1/uploads/complete` -> compare by version ids). This is transparent: you always just call `compare_documents`.
Responses include visual artifact metadata when available:
- `comparisonMode`: `visual` or `text`
- `visual.status`: `ready`, `pending`, `missing`, or `disabled`
- `visual.manifest`: the document-review contract when `visual.status` is `ready`
- `pages[]`: side-by-side page pairs with image URLs and dimensions
- `pages[].regions[]`: red/green-ready change regions with `leftBox`, `rightBox`, snippets, change type, salience metadata, and a stable `groupId`
- `regions[].groupId`: shared identity for paired change fragments, including changes that cross page boundaries
- `regions[].salience`: `subtle`, `visible`, `structural`, or `technical`, plus `salienceReason` when available
- `totalChanges` and `changedPageCount`: summary fields for compact controls and optional review navigation; paired fragments with the same `groupId` count as one change
- `visual.diffUrl`: signed URL for the visual diff JSON when available
Agents should treat `visual.manifest` as the primary surface for professional document review: render the two documents continuously, draw exact inline highlights when coordinates are present, use `groupId` for selection and synchronized scrolling, and derive center-gutter/minimap markers from the region coordinates. If `visual.status` is not `ready`, agents should use the text diff summary and comparison URL instead of assuming there are no visual changes.
Example:
```json
{
"name": "compare_documents",
"arguments": {
"file_a_path": "/docs/contract_v1.pdf",
"file_b_path": "/docs/contract_v2.pdf",
"comparison_mode": "visual"
}
}
```
### get_comparison
Check the status and results of a comparison by its UUID. Returns the same visual artifact contract as `compare_documents`, with fresh signed URLs. Use it to poll comparisons that returned `status: "processing"`. Free (does not consume credits).
**Parameters:**
- `comparison_id` (required) -- The comparison UUID
### list_comparisons
List recent comparisons in your workspace, newest first. Returns `id`, `status`, `comparisonMode`, `file_a`/`file_b` name and type, `summary` counters, `created_at`, and `completed_at` for each comparison. Useful to recover a comparison id or check recent activity. Free.
**Parameters:**
- `limit` (optional) -- Maximum results, 1 to 50 (default: 10)
- `status` (optional) -- Filter by `pending`, `processing`, `ready`, or `failed`
Example:
```json
{
"name": "list_comparisons",
"arguments": { "limit": 5, "status": "ready" }
}
```
### export_comparison_pdf
Export a finished comparison as a PDF report and get a temporary download URL (valid for about 5 minutes). With `wait: true` (default) the tool polls the export job every 2 seconds for up to 3 minutes and returns the `downloadUrl`. Free workspaces get a watermarked PDF; workspaces with credits export without watermark. Free (exports do not consume credits).
**Parameters:**
- `comparison_id` (required) -- The comparison UUID (must have `status: "ready"`)
- `include_unchanged` (optional) -- Include unchanged content in the report (default: `false`)
- `locale` (optional) -- Report language: `en`, `es`, `zh`, `fr`, `de`, or `hi` (default: `en`)
- `wait` (optional) -- Poll until the export completes (default: `true`)
Example:
```json
{
"name": "export_comparison_pdf",
"arguments": {
"comparison_id": "8f14e45f-...",
"locale": "es",
"include_unchanged": false
}
}
```
### summarize_visual_diff
Compact, page-by-page summary of the visual diff: one entry per change group (fragments that share a `groupId` are deduplicated), with change type, title, salience, and optional text snippets. No image URLs and no pixel coordinates, so it is the cheapest way for an agent to understand what changed and where. Requires a visual comparison with `visual.status: "ready"`. Free.
**Parameters:**
- `comparison_id` (required) -- The comparison UUID
- `min_salience` (optional) -- Only include changes at or above this prominence level; order is `technical` < `subtle` < `visible` < `structural`
- `include_snippets` (optional) -- Include `snippetA`/`snippetB` excerpts (default: `true`)
- `max_changes_per_page` (optional) -- Cap per page, extra changes are counted as omitted (default: 20)
Example:
```json
{
"name": "summarize_visual_diff",
"arguments": {
"comparison_id": "8f14e45f-...",
"min_salience": "visible",
"max_changes_per_page": 10
}
}
```
### get_text_diff
Fetch the block-level text diff of a finished comparison. Returns the diff summary counters plus the diff blocks: `added` and `removed` blocks carry the affected text, `modified` blocks also carry word-level `spans` marking exactly which words changed. By default only changed blocks are returned. Best when you need to quote exact text changes. Free.
**Parameters:**
- `comparison_id` (required) -- The comparison UUID (must have `status: "ready"`)
- `only_changed` (optional) -- Skip `equal` blocks (default: `true`)
- `max_blocks` (optional) -- Maximum blocks returned (default: 100)
Example:
```json
{
"name": "get_text_diff",
"arguments": { "comparison_id": "8f14e45f-...", "max_blocks": 50 }
}
```
### get_balance
Get the account status of the workspace behind the API key: credit balance, whether the lifetime free comparison is still available, effective plan, and workspace id. Useful before `compare_documents` or after a `[NO_CREDITS]` error. Free.
**Parameters:** none
Example response:
```json
{
"credits": 42,
"freeComparisonRemaining": 0,
"plan": "pro",
"workspaceId": "3b2f...",
"buyUrl": "https://www.differino.com/billing?pack=comparino"
}
```
## REST API
The MCP server uses the Differino REST API under the hood. You can also call it directly:
```bash
# Compare two SMALL files (multipart body limited to ~4.5 MB total on Vercel)
curl -X POST https://www.differino.com/api/v1/compare \
-H "Authorization: Bearer dfn_your_api_key" \
-H "Idempotency-Key: my-unique-key-001" \
-F "file_a=@original.pdf" \
-F "file_b=@modified.pdf" \
-F "comparison_mode=visual" \
-F "accuracy_mode=balanced"
# Check comparison status
curl https://www.differino.com/api/v1/comparisons/{id} \
-H "Authorization: Bearer dfn_your_api_key"
# List recent comparisons
curl "https://www.differino.com/api/v1/comparisons?limit=10&status=ready" \
-H "Authorization: Bearer dfn_your_api_key"
# Account status: credits, free comparison, plan
curl https://www.differino.com/api/v1/me \
-H "Authorization: Bearer dfn_your_api_key"
# Export a comparison as PDF, then poll for the download URL
curl -X POST https://www.differino.com/api/v1/comparisons/{id}/export \
-H "Authorization: Bearer dfn_your_api_key" \
-H "Content-Type: application/json" \
-d '{"format": "pdf", "includeUnchanged": false, "locale": "en"}'
curl "https://www.differino.com/api/v1/comparisons/{id}/export?jobId={jobId}" \
-H "Authorization: Bearer dfn_your_api_key"
```
### Large files: the signed-upload flow
Multipart `POST /api/v1/compare` is limited to about 4.5 MB of TOTAL request body (Vercel limit); above that it returns `413 PAYLOAD_TOO_LARGE`. For files up to 45 MB each, upload directly to storage:
```bash
# 1. Init: declare both files, get signed upload URLs + version ids
curl -X POST https://www.differino.com/api/v1/uploads \
-H "Authorization: Bearer dfn_your_api_key" \
-H "Content-Type: application/json" \
-d '{"fileA": {"name": "original.pdf", "type": "application/pdf", "size": 10485760},
"fileB": {"name": "modified.pdf", "type": "application/pdf", "size": 11534336}}'
# 2. PUT each file body to its uploadUrl (URLs are valid for 2 hours)
curl -X PUT "{fileA.uploadUrl}" \
-H "Content-Type: application/pdf" \
--data-binary @original.pdf
curl -X PUT "{fileB.uploadUrl}" \
-H "Content-Type: application/pdf" \
--data-binary @modified.pdf
# 3. Complete: verify the stored objects and queue extraction
curl -X POST https://www.differino.com/api/v1/uploads/complete \
-H "Authorization: Bearer dfn_your_api_key" \
-H "Content-Type: application/json" \
-d '{"versionIds": ["{fileA.versionId}", "{fileB.versionId}"]}'
# 4. Compare the uploaded versions (JSON body instead of multipart)
curl -X POST https://www.differino.com/api/v1/compare \
-H "Authorization: Bearer dfn_your_api_key" \
-H "Content-Type: application/json" \
-d '{"versionAId": "{fileA.versionId}", "versionBId": "{fileB.versionId}", "comparisonMode": "visual"}'
```
Steps 1-3 are free; only step 4 consumes the free comparison or a credit.
### Idempotency
`POST /api/v1/compare` accepts an optional `Idempotency-Key` header (up to 200 characters). Repeating a key within your workspace returns the existing comparison (`idempotentReplay: true`) without charging again. Use it to make retries safe.
### Rate limits
All limits are per workspace. Exceeding one returns `429` with `code: "RATE_LIMITED"`, a `retryAfter` field (seconds), and a `Retry-After` header.
| Endpoint | Limit |
|----------|-------|
| `POST /api/v1/compare` | 10 / minute |
| `POST /api/v1/comparisons/{id}/export` | 20 / hour, plus at most 5 pending/processing export jobs (`429 EXPORT_QUEUE_FULL`) |
| `GET` endpoints (list, get, export status, me) | 120 / minute (shared) |
| `POST /api/v1/uploads` + `/uploads/complete` | 30 / minute (shared) |
### accuracy_mode
`POST /api/v1/compare` accepts an optional `accuracy_mode` form field:
- `fast` -- quickest results, best for plain text and simple layouts (default for `comparison_mode=text`)
- `balanced` -- good accuracy/speed trade-off (default for `comparison_mode=visual`)
- `thorough` -- maximum accuracy for complex layouts, slower
### Error codes
| HTTP | `code` | Meaning |
|------|--------|---------|
| 400 | | Invalid request: missing files, unsupported file type, file too large, or invalid parameters |
| 400 | `INVALID_FILE` | File content does not match its extension (magic-byte check), or upload verification failed |
| 400 | `FILE_TOO_LARGE` | Declared file size exceeds the 45 MB per-file limit |
| 400 | `VERSION_FAILED` / `VERSION_NOT_PENDING` | Referenced version cannot be used (processing failed, or already completed) |
| 401 | | Missing or invalid API key |
| 402 | `NO_CREDITS` | No free comparison left and no credits remaining; the response includes `buyUrl` (https://www.differino.com/billing?pack=comparino) |
| 404 | `VERSION_NOT_FOUND` | Comparison, version, or export job not found in your workspace |
| 413 | `PAYLOAD_TOO_LARGE` | Multipart body exceeds ~4.5 MB total; use the signed-upload flow |
| 429 | `RATE_LIMITED` | Rate limit exceeded; retry after `retryAfter` seconds |
| 429 | `EXPORT_QUEUE_FULL` | More than 5 export jobs pending/processing in your workspace |
When present, the `code` field is machine-readable; the MCP server prefixes it to error messages (for example `[NO_CREDITS] No credits remaining...`) and appends `retryAfter` / `buyUrl` details when the API provides them.
## Supported Formats
- PDF (.pdf)
- Word (.docx)
- Plain Text (.txt)
## Credits
Every account gets 1 free comparison (lifetime, not monthly). After that, each comparison costs 1 credit. Checking results, listing comparisons, exporting PDFs, and reading diffs are always free.
Buy credit packs at [differino.com/pricing](https://www.differino.com/pricing).
## About this repository
This repo contains the source of the `differino-mcp` npm package: a thin MCP client for the [Differino](https://www.differino.com) REST API. The Differino comparison engine, web app, and infrastructure are proprietary and not part of this repository. Using the API consumes Differino credits; see [pricing](https://www.differino.com/pricing).
## License
MIT (this MCP client only).
TDQS
Scored across 7 tools
Each tool has a clearly distinct purpose: starting comparisons, retrieving status, listing, exporting, summarizing visual diffs, fetching text diffs, and checking account balance. No two tools overlap in scope, and the descriptions make the differences explicit.
All tool names follow a consistent verb_noun snake_case pattern (e.g., compare_documents, get_comparison, list_comparisons). The verbs are action-oriented and predictable, making it easy to infer what each tool does.
Seven tools is well-scoped for a document comparison server. Each tool addresses a specific part of the workflow (initiate, retrieve, list, export, summarize, text diff, account), with no redundant or unnecessary entries.
The tool surface covers the full lifecycle: creating comparisons, polling for status, retrieving results in both text and visual forms, exporting reports, listing history, and managing account credits. There are no obvious missing operations for the stated purpose.