@exactpdf/mcp 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -4
- package/dist/run.js +135 -39
- package/llms-install.md +67 -0
- package/package.json +9 -4
- package/server.json +4 -4
package/README.md
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
Model Context Protocol (stdio) server for **ExactPDF** — production PDF tools for **Cursor**, **Claude Desktop**, **Codex**, and any MCP client.
|
|
6
6
|
|
|
7
|
-
Use it when an agent needs to inspect, merge, split, rotate, compress, extract text, export structured Markdown for RAG, or build images-to-PDF workflows without writing fragile PDF plumbing.
|
|
7
|
+
Use it when an agent needs to inspect, merge, split, rotate, compress to upload limits, extract text, export PDF JSON, export structured Markdown for RAG, or build images-to-PDF workflows without writing fragile PDF plumbing.
|
|
8
8
|
|
|
9
9
|
**Design principle:** every tool should work on first run, explain credit cost before risky work, return a local output path for files, and expose precise HTTP/API errors when something fails.
|
|
10
10
|
|
|
11
11
|
Developer quickstart: [https://exactpdf.com/developers](https://exactpdf.com/developers)
|
|
12
12
|
|
|
13
|
-
## Setup
|
|
13
|
+
## One-minute Setup
|
|
14
|
+
|
|
15
|
+
ExactPDF MCP is free to install. Hosted ExactPDF API workflows include 20 free test credits, then pay-as-you-go credits when the workflow is worth automating.
|
|
14
16
|
|
|
15
17
|
1. Create an API key at [max.exactpdf.com/account](https://max.exactpdf.com/account). New accounts get **20 free test credits**. Buy one-time credit packs only after the workflow works; no subscription is required for API/MCP.
|
|
16
18
|
2. Export:
|
|
@@ -48,6 +50,161 @@ For a local checkout of this monorepo:
|
|
|
48
50
|
"args": ["/path/to/ExactPDF/packages/exactpdf-mcp/dist/run.js"]
|
|
49
51
|
```
|
|
50
52
|
|
|
53
|
+
## Copy-paste Configs
|
|
54
|
+
|
|
55
|
+
Use this bare config when your client lets you set environment variables separately:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"exactpdf": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["-y", "@exactpdf/mcp"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use this config when the client supports env values inside JSON:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"exactpdf": {
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["-y", "@exactpdf/mcp"],
|
|
76
|
+
"env": {
|
|
77
|
+
"EXACTPDF_API_KEY": "sk_live_..."
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Claude Desktop
|
|
85
|
+
|
|
86
|
+
Add to `claude_desktop_config.json`, then restart Claude Desktop:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"exactpdf": {
|
|
92
|
+
"command": "npx",
|
|
93
|
+
"args": ["-y", "@exactpdf/mcp"],
|
|
94
|
+
"env": {
|
|
95
|
+
"EXACTPDF_API_KEY": "sk_live_..."
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Claude Code
|
|
103
|
+
|
|
104
|
+
Add the server from your project terminal:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
claude mcp add exactpdf -- npx -y @exactpdf/mcp
|
|
108
|
+
export EXACTPDF_API_KEY="sk_live_..."
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If your Claude Code setup stores env values in its MCP config, use the JSON block above instead.
|
|
112
|
+
|
|
113
|
+
### Cursor
|
|
114
|
+
|
|
115
|
+
Add to `.cursor/mcp.json` or Cursor Settings -> MCP:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"exactpdf": {
|
|
121
|
+
"command": "npx",
|
|
122
|
+
"args": ["-y", "@exactpdf/mcp"],
|
|
123
|
+
"env": {
|
|
124
|
+
"EXACTPDF_API_KEY": "sk_live_..."
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Windsurf
|
|
132
|
+
|
|
133
|
+
Add ExactPDF as a custom MCP server:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"mcpServers": {
|
|
138
|
+
"exactpdf": {
|
|
139
|
+
"command": "npx",
|
|
140
|
+
"args": ["-y", "@exactpdf/mcp"],
|
|
141
|
+
"env": {
|
|
142
|
+
"EXACTPDF_API_KEY": "sk_live_..."
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### VS Code / Cline
|
|
150
|
+
|
|
151
|
+
Add to your Cline MCP settings:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"mcpServers": {
|
|
156
|
+
"exactpdf": {
|
|
157
|
+
"command": "npx",
|
|
158
|
+
"args": ["-y", "@exactpdf/mcp"],
|
|
159
|
+
"env": {
|
|
160
|
+
"EXACTPDF_API_KEY": "sk_live_..."
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Five Agent Recipes
|
|
168
|
+
|
|
169
|
+
Paste one of these after the MCP server is connected. Use absolute local file paths.
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
Merge these 3 PDFs into one file:
|
|
173
|
+
/Users/me/Desktop/cover.pdf
|
|
174
|
+
/Users/me/Desktop/form.pdf
|
|
175
|
+
/Users/me/Desktop/id.pdf
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
Compress /Users/me/Desktop/application.pdf under 500KB for a government upload.
|
|
180
|
+
Use ExactPDF MCP and save the output next to the original.
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
Convert /Users/me/Desktop/manual.pdf to structured Markdown for RAG.
|
|
185
|
+
Keep headings and page references.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```text
|
|
189
|
+
Extract metadata and per-page JSON from /Users/me/Desktop/contract.pdf.
|
|
190
|
+
Show me the page count, title, author, and first-page text.
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
Turn these images into one PDF:
|
|
195
|
+
/Users/me/Desktop/photo-1.jpg
|
|
196
|
+
/Users/me/Desktop/photo-2.jpg
|
|
197
|
+
/Users/me/Desktop/photo-3.jpg
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## 30-Second Demo
|
|
201
|
+
|
|
202
|
+

|
|
203
|
+
|
|
204
|
+
Video: https://exactpdf.com/demos/exactpdf-mcp-demo.mp4
|
|
205
|
+
|
|
206
|
+
Install ExactPDF MCP, paste one config block, ask your agent to fix a PDF, and get a local output file.
|
|
207
|
+
|
|
51
208
|
## Tools
|
|
52
209
|
|
|
53
210
|
Safe first run:
|
|
@@ -82,10 +239,18 @@ Credit model:
|
|
|
82
239
|
| `exactpdf_split_pdf` | POST /api/v1/split | 1 |
|
|
83
240
|
| `exactpdf_rotate_pdf` | POST /api/v1/rotate | 1 |
|
|
84
241
|
| `exactpdf_compress_pdf` | POST /api/v1/compress | 1 |
|
|
242
|
+
| `exactpdf_compress_pdf_to_target` | POST /api/v1/compress + target_bytes | 1 |
|
|
243
|
+
| `exactpdf_compress_pdf_to_100kb` | POST /api/v1/compress + 102400 target_bytes | 1 |
|
|
244
|
+
| `exactpdf_compress_pdf_to_200kb` | POST /api/v1/compress + 204800 target_bytes | 1 |
|
|
245
|
+
| `exactpdf_compress_pdf_to_500kb` | POST /api/v1/compress + 512000 target_bytes | 1 |
|
|
246
|
+
| `exactpdf_compress_pdf_to_1mb` | POST /api/v1/compress + 1048576 target_bytes | 1 |
|
|
247
|
+
| `exactpdf_pdf_to_images` | planned /api/v1/pdf-to-images | backend required |
|
|
85
248
|
| `exactpdf_images_to_pdf` | POST /api/v1/images-to-pdf | 1 |
|
|
86
249
|
| `exactpdf_images_to_pdf_and_compress` | POST /api/v1/images-to-pdf-compress | 1 |
|
|
87
250
|
| `exactpdf_extract_text` | POST /api/v1/extract-text | 1 |
|
|
251
|
+
| `exactpdf_pdf_to_json` | POST /api/v1/pdf-to-json | 1 |
|
|
88
252
|
| `exactpdf_pdf_structured_markdown` | POST /api/v1/pdf-structured-markdown | 1 |
|
|
253
|
+
| `exactpdf_ocr_pdf` | planned /api/v1/ocr | backend required |
|
|
89
254
|
| `exactpdf_estimate_speech_cost` | local estimate | 0 |
|
|
90
255
|
| `exactpdf_voice_preview` | POST /api/v1/voice-preview | 0 |
|
|
91
256
|
| `exactpdf_pdf_to_speech` | POST /api/v1/pdf-to-speech | disabled / hardening |
|
|
@@ -102,8 +267,10 @@ Credit model:
|
|
|
102
267
|
| Tool family | What the agent provides | What ExactPDF returns |
|
|
103
268
|
| --- | --- | --- |
|
|
104
269
|
| Account / inspect | API key, local PDF path | JSON balance, page count, metadata |
|
|
105
|
-
| PDF operations | Absolute local PDF paths | Saved PDF/ZIP path plus remaining credits |
|
|
106
|
-
|
|
|
270
|
+
| PDF operations | Absolute local PDF paths | Saved PDF/ZIP path plus remaining credits and measured size headers |
|
|
271
|
+
| Target-size compression | Absolute local PDF path, target bytes or preset | Saved PDF plus `X-Target-Reached`, original bytes, output bytes |
|
|
272
|
+
| Text / JSON / Markdown | Absolute local PDF path | JSON text/pages/metadata or Markdown, page count, structured content |
|
|
273
|
+
| Backend-required PDF images/OCR | Desired path/options | Clear backend-required message plus live browser tool URL |
|
|
107
274
|
| Voice preview | Short text, voice style/language | Saved MP3/WAV preview, no credit charge |
|
|
108
275
|
| Audio planning | Short text, character count, rough PDF size | Voice preview or estimated minutes/credits |
|
|
109
276
|
| Legacy job polling/download | `job_id` | progress/errors/result URL or saved audio/ZIP for jobs created before the hardening gate |
|
package/dist/run.js
CHANGED
|
@@ -28,8 +28,15 @@ function requireKey() {
|
|
|
28
28
|
return k;
|
|
29
29
|
}
|
|
30
30
|
const BACKGROUND_AUDIO_HARDENING_MESSAGE = 'Background PDF speech, audiobook, translate-and-speak, and presentation narration jobs are currently disabled for paid workloads while ExactPDF finishes dedicated worker hardening and queue-completion smoke. Use exactpdf_voice_preview for short free voice samples and exactpdf_estimate_speech_cost for planning only. Do not promise MP3/audiobook delivery through MCP until the API returns enabled status again.';
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const BACKEND_REQUIRED_TOOL_MESSAGE = 'This MCP tool is intentionally exposed as backend-required, not live. ExactPDF has the browser workflow today, but the API/MCP endpoint needs a dedicated server renderer/OCR worker before agents can run it as infrastructure. Do not present this as a completed API workflow yet.';
|
|
32
|
+
const TARGET_SIZE_PRESETS = {
|
|
33
|
+
'100kb': 100 * 1024,
|
|
34
|
+
'200kb': 200 * 1024,
|
|
35
|
+
'500kb': 500 * 1024,
|
|
36
|
+
'1mb': 1024 * 1024,
|
|
37
|
+
};
|
|
38
|
+
const server = new McpServer({ name: 'exactpdf', version: '0.2.15' }, {
|
|
39
|
+
instructions: 'ExactPDF turns local PDFs into production outputs for agents: inspect metadata, merge/split/rotate/compress, target-size best-effort compression, create portal-ready merge/images workflows, extract text, export PDF JSON, export structured Markdown for RAG, and create short voice previews. Start with exactpdf_first_run_checklist, then exactpdf_account and exactpdf_pdf_info before paid work. New users can create a key with free test credits at https://max.exactpdf.com/account; developer quickstart: https://exactpdf.com/developers. Free tools: first-run checklist, account, pdf-info, voice-preview, and speech cost estimate. Standard document tools cost 1 credit on success, including launch portal-ready workflows. PDF-to-images and OCR are visible as backend-required placeholders until server renderer/OCR worker endpoints ship. Background PDF speech/audiobook/translation/presentation jobs are in production hardening and must be treated as planning-only until worker smoke passes. Use absolute local file paths; binary outputs are saved to EXACTPDF_API_OUTPUT_DIR or the OS temp directory.',
|
|
33
40
|
});
|
|
34
41
|
async function saveBinaryFromResponse(res, prefix, fallbackExt) {
|
|
35
42
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
@@ -61,6 +68,40 @@ function targetStatus(res) {
|
|
|
61
68
|
const mode = res.headers.get('x-compression-mode') ?? 'best_effort';
|
|
62
69
|
return `Target bytes: ${target}\nTarget reached: ${reached}\nOriginal bytes: ${original}\nOutput bytes: ${output}\nCompression mode: ${mode}`;
|
|
63
70
|
}
|
|
71
|
+
async function compressPdfToTarget(path, target_bytes) {
|
|
72
|
+
const key = requireKey();
|
|
73
|
+
const form = new FormData();
|
|
74
|
+
form.append('file', new Blob([await readFile(path)], { type: 'application/pdf' }), basename(path));
|
|
75
|
+
if (typeof target_bytes === 'number')
|
|
76
|
+
form.append('target_bytes', String(target_bytes));
|
|
77
|
+
const res = await fetch(`${BASE}/api/v1/compress`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
80
|
+
body: form,
|
|
81
|
+
});
|
|
82
|
+
const credits = res.headers.get('x-credits-remaining') ?? '?';
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
const errBody = await res.text();
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: 'text',
|
|
89
|
+
text: `Compress failed HTTP ${res.status}. Credits: ${credits}\n${errBody.slice(0, 4000)}`,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
isError: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const outPath = await saveBinaryFromResponse(res, 'exactpdf-compressed', 'pdf');
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `Compressed → ${outPath}\nCredits remaining: ${credits}\n${targetStatus(res)}`,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
64
105
|
server.registerTool('exactpdf_first_run_checklist', {
|
|
65
106
|
description: 'Return the safest first-run sequence for ExactPDF MCP clients, including API key setup, free probe tools, credit rules, output directory behavior, and docs links. Free; no API key required.',
|
|
66
107
|
inputSchema: z.object({}),
|
|
@@ -305,46 +346,67 @@ server.registerTool('exactpdf_rotate_pdf', {
|
|
|
305
346
|
};
|
|
306
347
|
});
|
|
307
348
|
server.registerTool('exactpdf_compress_pdf', {
|
|
308
|
-
description: 'Compress/repack a local PDF for smaller delivery. Costs 1 credit on success.
|
|
349
|
+
description: 'Compress/repack a local PDF for smaller delivery. Costs 1 credit on success. Optional target_bytes returns measured target headers. Best-effort, not guaranteed exact-size compression.',
|
|
309
350
|
inputSchema: z.object({
|
|
310
351
|
path: z.string().describe('Absolute path to a PDF file'),
|
|
352
|
+
target_bytes: z.number().int().positive().optional().describe('Optional upload limit such as 102400 for 100KB, 204800 for 200KB, 512000 for 500KB, or 1048576 for 1MB.'),
|
|
311
353
|
}),
|
|
312
|
-
}, async ({ path }) =>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
354
|
+
}, async ({ path, target_bytes }) => compressPdfToTarget(path, target_bytes));
|
|
355
|
+
server.registerTool('exactpdf_compress_pdf_to_target', {
|
|
356
|
+
description: 'Best-effort compress/repack a local PDF toward a target size such as 100KB, 200KB, 500KB, or 1MB. Costs 1 credit on success and reports whether target_bytes was reached.',
|
|
357
|
+
inputSchema: z.object({
|
|
358
|
+
path: z.string().describe('Absolute path to a PDF file'),
|
|
359
|
+
target_bytes: z.number().int().positive().describe('Upload limit in bytes, e.g. 102400 for 100KB or 1048576 for 1MB.'),
|
|
360
|
+
}),
|
|
361
|
+
}, async ({ path, target_bytes }) => compressPdfToTarget(path, target_bytes));
|
|
362
|
+
for (const [label, targetBytes] of Object.entries(TARGET_SIZE_PRESETS)) {
|
|
363
|
+
server.registerTool(`exactpdf_compress_pdf_to_${label}`, {
|
|
364
|
+
description: `Best-effort compress/repack a local PDF toward ${label.toUpperCase()}. Costs 1 credit on success and reports whether the target was reached.`,
|
|
365
|
+
inputSchema: z.object({
|
|
366
|
+
path: z.string().describe('Absolute path to a PDF file'),
|
|
367
|
+
}),
|
|
368
|
+
}, async ({ path }) => compressPdfToTarget(path, targetBytes));
|
|
369
|
+
}
|
|
370
|
+
server.registerTool('exactpdf_pdf_to_images', {
|
|
371
|
+
description: 'Backend-required placeholder for PDF-to-images API/MCP. Use the live browser tool at https://exactpdf.com/tools/pdf-to-images until the server renderer endpoint ships.',
|
|
372
|
+
inputSchema: z.object({
|
|
373
|
+
path: z.string().optional().describe('Absolute path to a PDF file. Not processed yet because the API endpoint is not live.'),
|
|
374
|
+
output_format: z.enum(['jpg', 'png']).optional().describe('Planned output format.'),
|
|
375
|
+
dpi: z.number().int().positive().optional().describe('Planned render DPI.'),
|
|
376
|
+
page_range: z.string().optional().describe('Planned page range, e.g. "1-3".'),
|
|
377
|
+
}),
|
|
378
|
+
}, async () => ({
|
|
379
|
+
content: [{
|
|
380
|
+
type: 'text',
|
|
381
|
+
text: JSON.stringify({
|
|
382
|
+
ok: false,
|
|
383
|
+
status: 'backend_required',
|
|
384
|
+
web_tool: `${BASE}/tools/pdf-to-images`,
|
|
385
|
+
planned_endpoint: `${BASE}/api/v1/pdf-to-images`,
|
|
386
|
+
note: BACKEND_REQUIRED_TOOL_MESSAGE,
|
|
387
|
+
}, null, 2),
|
|
388
|
+
}],
|
|
389
|
+
isError: true,
|
|
390
|
+
}));
|
|
391
|
+
server.registerTool('exactpdf_ocr_pdf', {
|
|
392
|
+
description: 'Backend-required placeholder for OCR API/MCP. Use the live browser OCR page at https://exactpdf.com/tools/ocr-pdf until the server OCR worker endpoint ships.',
|
|
393
|
+
inputSchema: z.object({
|
|
394
|
+
path: z.string().optional().describe('Absolute path to a PDF file. Not processed yet because the API endpoint is not live.'),
|
|
395
|
+
language: z.string().optional().describe('Planned OCR language hint such as eng, hin, spa.'),
|
|
396
|
+
}),
|
|
397
|
+
}, async () => ({
|
|
398
|
+
content: [{
|
|
399
|
+
type: 'text',
|
|
400
|
+
text: JSON.stringify({
|
|
401
|
+
ok: false,
|
|
402
|
+
status: 'backend_required',
|
|
403
|
+
web_tool: `${BASE}/tools/ocr-pdf`,
|
|
404
|
+
planned_endpoint: `${BASE}/api/v1/ocr`,
|
|
405
|
+
note: BACKEND_REQUIRED_TOOL_MESSAGE,
|
|
406
|
+
}, null, 2),
|
|
407
|
+
}],
|
|
408
|
+
isError: true,
|
|
409
|
+
}));
|
|
348
410
|
server.registerTool('exactpdf_images_to_pdf_and_compress', {
|
|
349
411
|
description: 'Portal-ready workflow: convert local PNG/JPEG images into one ordered PDF, then best-effort repack/compress for an upload limit. Costs 1 launch credit on success. Returns measured output size and whether target_bytes was reached.',
|
|
350
412
|
inputSchema: z.object({
|
|
@@ -535,6 +597,40 @@ server.registerTool('exactpdf_pdf_structured_markdown', {
|
|
|
535
597
|
content: [{ type: 'text', text: `Credits remaining: ${credits}\n${raw.slice(0, 120_000)}` }],
|
|
536
598
|
};
|
|
537
599
|
});
|
|
600
|
+
server.registerTool('exactpdf_pdf_to_json', {
|
|
601
|
+
description: 'Extract text-layer PDF structure as JSON: metadata, per-page text, full text, page count, and extraction notes. Costs 1 credit on success. Scanned PDFs need OCR first.',
|
|
602
|
+
inputSchema: z.object({
|
|
603
|
+
path: z.string().describe('Absolute path to a PDF file'),
|
|
604
|
+
}),
|
|
605
|
+
}, async ({ path }) => {
|
|
606
|
+
const key = requireKey();
|
|
607
|
+
const form = new FormData();
|
|
608
|
+
form.append('file', new Blob([await readFile(path)], { type: 'application/pdf' }), basename(path));
|
|
609
|
+
const res = await fetch(`${BASE}/api/v1/pdf-to-json`, {
|
|
610
|
+
method: 'POST',
|
|
611
|
+
headers: {
|
|
612
|
+
Authorization: `Bearer ${key}`,
|
|
613
|
+
Accept: 'application/json',
|
|
614
|
+
},
|
|
615
|
+
body: form,
|
|
616
|
+
});
|
|
617
|
+
const credits = res.headers.get('x-credits-remaining') ?? '?';
|
|
618
|
+
const raw = await res.text();
|
|
619
|
+
if (!res.ok) {
|
|
620
|
+
return {
|
|
621
|
+
content: [
|
|
622
|
+
{
|
|
623
|
+
type: 'text',
|
|
624
|
+
text: `pdf-to-json failed HTTP ${res.status}. Credits: ${credits}\n${raw.slice(0, 8000)}`,
|
|
625
|
+
},
|
|
626
|
+
],
|
|
627
|
+
isError: true,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
content: [{ type: 'text', text: `Credits remaining: ${credits}\n${raw.slice(0, 120_000)}` }],
|
|
632
|
+
};
|
|
633
|
+
});
|
|
538
634
|
server.registerTool('exactpdf_pdf_to_audiobook', {
|
|
539
635
|
description: 'Planning-only placeholder while background audio workers are hardened. Do not use for paid workloads; use exactpdf_voice_preview and exactpdf_estimate_speech_cost instead.',
|
|
540
636
|
inputSchema: z.object({
|
package/llms-install.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# ExactPDF MCP Installation for Agents
|
|
2
|
+
|
|
3
|
+
Use this file when an AI coding assistant or MCP marketplace needs deterministic setup instructions for `@exactpdf/mcp`.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 18 or newer.
|
|
8
|
+
- No repository clone is required.
|
|
9
|
+
- Optional: `EXACTPDF_API_KEY` for hosted ExactPDF API workflows.
|
|
10
|
+
|
|
11
|
+
## Install Command
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y @exactpdf/mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## MCP Client Config
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"exactpdf": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@exactpdf/mcp"],
|
|
25
|
+
"env": {
|
|
26
|
+
"EXACTPDF_API_KEY": "sk_live_or_test_key"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If the user does not have an API key yet, keep the server installed and direct them to:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
https://max.exactpdf.com/account
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
New users get free test credits. The server can still show first-run guidance without a key.
|
|
40
|
+
|
|
41
|
+
## First Verification Prompt
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
Use ExactPDF MCP. Run the first-run checklist, then check my ExactPDF account.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## First PDF Workflow Prompt
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
Use ExactPDF MCP to inspect this PDF, then compress it under 500KB and save the result next to the original file:
|
|
51
|
+
/absolute/path/to/document.pdf
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Safety and Billing Notes
|
|
55
|
+
|
|
56
|
+
- ExactPDF MCP is free to install.
|
|
57
|
+
- Hosted ExactPDF API workflows include free test credits, then pay-as-you-go credits.
|
|
58
|
+
- Standard document workflows consume credits only on successful output.
|
|
59
|
+
- Background PDF speech, audiobook, translate-and-speak, and presentation narration are disabled while dedicated worker hardening is in progress.
|
|
60
|
+
- OCR and PDF-to-images are listed as backend-required placeholders until dedicated API workers are enabled.
|
|
61
|
+
|
|
62
|
+
## Common Troubleshooting
|
|
63
|
+
|
|
64
|
+
- If `npx` is not found, install Node.js 18+ and restart the MCP client.
|
|
65
|
+
- If the server starts but tools return `401`, set `EXACTPDF_API_KEY`.
|
|
66
|
+
- Use absolute file paths for local PDFs and images.
|
|
67
|
+
- If a PDF is too large for the API workflow, use ExactPDF browser tools or Max workflows from `https://exactpdf.com`.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exactpdf/mcp",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "MCP server for ExactPDF — agent PDF workflows, structured Markdown/RAG, merge/split
|
|
3
|
+
"version": "0.2.15",
|
|
4
|
+
"description": "MCP server for ExactPDF — agent PDF workflows, target-size compression, structured Markdown/RAG, PDF JSON, merge/split, images-to-PDF, voice previews, and API credits.",
|
|
5
5
|
"mcpName": "com.exactpdf/mcp",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/run.js",
|
|
@@ -14,19 +14,20 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
16
|
"README.md",
|
|
17
|
+
"llms-install.md",
|
|
17
18
|
"server.json"
|
|
18
19
|
],
|
|
19
20
|
"publishConfig": {
|
|
20
21
|
"access": "public"
|
|
21
22
|
},
|
|
22
|
-
"homepage": "https://exactpdf.com/
|
|
23
|
+
"homepage": "https://exactpdf.com/mcp",
|
|
23
24
|
"bugs": {
|
|
24
25
|
"url": "https://exactpdf.com/docs/api",
|
|
25
26
|
"email": "support@exactpdf.com"
|
|
26
27
|
},
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/
|
|
30
|
+
"url": "git+https://github.com/smmallya/ExactPDF.git",
|
|
30
31
|
"directory": "packages/exactpdf-mcp"
|
|
31
32
|
},
|
|
32
33
|
"scripts": {
|
|
@@ -48,7 +49,11 @@
|
|
|
48
49
|
"pdf-mcp",
|
|
49
50
|
"pdf-compression",
|
|
50
51
|
"pdf-to-markdown",
|
|
52
|
+
"pdf-to-json",
|
|
53
|
+
"pdf-to-images",
|
|
54
|
+
"target-size-compression",
|
|
51
55
|
"rag",
|
|
56
|
+
"ocr",
|
|
52
57
|
"images-to-pdf",
|
|
53
58
|
"merge-pdf",
|
|
54
59
|
"split-pdf"
|
package/server.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "com.exactpdf/mcp",
|
|
4
4
|
"title": "ExactPDF",
|
|
5
|
-
"description": "Agent-facing PDF API: merge, split, rotate, compress, images-to-PDF, metadata, text extraction, structured Markdown/RAG, voice previews, and API credit checks.
|
|
6
|
-
"version": "0.2.
|
|
7
|
-
"websiteUrl": "https://exactpdf.com/
|
|
5
|
+
"description": "Agent-facing PDF API: merge, split, rotate, compress, target-size compression, images-to-PDF, metadata, text extraction, PDF JSON, structured Markdown/RAG, voice previews, and API credit checks. PDF-to-images/OCR require backend workers; background audio jobs are hardening-only until worker smoke passes.",
|
|
6
|
+
"version": "0.2.15",
|
|
7
|
+
"websiteUrl": "https://exactpdf.com/mcp",
|
|
8
8
|
"packages": [
|
|
9
9
|
{
|
|
10
10
|
"registryType": "npm",
|
|
11
11
|
"identifier": "@exactpdf/mcp",
|
|
12
|
-
"version": "0.2.
|
|
12
|
+
"version": "0.2.15",
|
|
13
13
|
"transport": {
|
|
14
14
|
"type": "stdio"
|
|
15
15
|
}
|