@exactpdf/mcp 0.2.6 → 0.2.7

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 CHANGED
@@ -56,11 +56,12 @@ For a local checkout of this monorepo:
56
56
  | `exactpdf_extract_text` | POST /api/v1/extract-text | 1 |
57
57
  | `exactpdf_pdf_structured_markdown` | POST /api/v1/pdf-structured-markdown | 1 |
58
58
  | `exactpdf_estimate_speech_cost` | local estimate | 0 |
59
- | `exactpdf_pdf_to_speech` | POST /api/v1/pdf-to-speech | 10 |
60
- | `exactpdf_pdf_to_audiobook` | POST /api/v1/pdf-to-audiobook | 10 |
61
- | `exactpdf_generate_audiobook` | POST /api/v1/generate-audiobook | 10 |
62
- | `exactpdf_translate_and_speak` | POST /api/v1/translate-and-speak | 20 |
63
- | `exactpdf_presentation_narration` | POST /api/v1/presentation-narration | 10 |
59
+ | `exactpdf_voice_preview` | POST /api/v1/voice-preview | 0 |
60
+ | `exactpdf_pdf_to_speech` | POST /api/v1/pdf-to-speech | 1/min |
61
+ | `exactpdf_pdf_to_audiobook` | POST /api/v1/pdf-to-audiobook | 1/min |
62
+ | `exactpdf_generate_audiobook` | POST /api/v1/generate-audiobook | 1/min |
63
+ | `exactpdf_translate_and_speak` | POST /api/v1/translate-and-speak | 3/min |
64
+ | `exactpdf_presentation_narration` | POST /api/v1/presentation-narration | 1/min |
64
65
  | `exactpdf_job_status` | GET /api/v1/jobs/:id | 0 |
65
66
  | `exactpdf_get_speech_job` | GET /api/v1/speech-jobs/:id | 0 |
66
67
  | `exactpdf_download_audio` | GET /api/v1/speech-jobs/:id/download | 0 |
@@ -69,9 +70,10 @@ Async audiobook flow:
69
70
 
70
71
  ```text
71
72
  1. exactpdf_estimate_speech_cost(path="/abs/book.pdf", mode="audiobook")
72
- 2. exactpdf_generate_audiobook(path="/abs/book.pdf", voice_style="audiobook")
73
- 3. exactpdf_get_speech_job(job_id="...", download=false)
74
- 4. exactpdf_download_audio(job_id="...") after status is succeeded
73
+ 2. exactpdf_voice_preview(text="Read the first paragraph in an audiobook tone", voice_style="audiobook")
74
+ 3. exactpdf_generate_audiobook(path="/abs/book.pdf", voice_style="audiobook")
75
+ 4. exactpdf_get_speech_job(job_id="...", download=false)
76
+ 5. exactpdf_download_audio(job_id="...") after status is succeeded
75
77
  ```
76
78
 
77
79
  `exactpdf_pdf_to_speech`, `exactpdf_pdf_to_audiobook`, and `exactpdf_generate_audiobook` accept optional `callback_url` and `webhook_secret`. ExactPDF signs webhook bodies with `x-exactpdf-signature: sha256=<hmac>` over `timestamp.body`.
package/dist/run.js CHANGED
@@ -24,8 +24,8 @@ function requireKey() {
24
24
  }
25
25
  return k;
26
26
  }
27
- const server = new McpServer({ name: 'exactpdf', version: '0.2.6' }, {
28
- instructions: 'ExactPDF API tools: exactpdf_account + exactpdf_pdf_info (free); merge, split, rotate, compress, images→PDF, extract-text, pdf-structured-markdown (1 credit each on success); async pdf-to-speech/pdf-to-audiobook/generate-audiobook (10 credits), translate-and-speak (20 credits), presentation narration (10 credits), plus speech job polling/download. Set EXACTPDF_API_KEY.',
27
+ const server = new McpServer({ name: 'exactpdf', version: '0.2.7' }, {
28
+ instructions: 'ExactPDF API tools: exactpdf_account + exactpdf_pdf_info (free); merge, split, rotate, compress, images→PDF, extract-text, pdf-structured-markdown (1 credit each on success); voice-preview (free), async pdf-to-speech/pdf-to-audiobook/generate-audiobook (10 credits), translate-and-speak (20 credits), presentation narration (10 credits), plus speech job polling/download. Set EXACTPDF_API_KEY.',
29
29
  });
30
30
  async function saveBinaryFromResponse(res, prefix, fallbackExt) {
31
31
  const buf = Buffer.from(await res.arrayBuffer());
@@ -793,7 +793,8 @@ server.registerTool('exactpdf_estimate_speech_cost', {
793
793
  }
794
794
  const estimatedMinutes = minutes ?? Math.max(1, Math.ceil(estimatedChars / 900));
795
795
  const selectedMode = target_language ? 'translate' : (mode ?? 'audiobook');
796
- const credits = selectedMode === 'translate' ? 20 : 10;
796
+ const creditsPerMinute = selectedMode === 'translate' ? 3 : 1;
797
+ const credits = Math.max(1, estimatedMinutes * creditsPerMinute);
797
798
  return {
798
799
  content: [
799
800
  {
@@ -805,14 +806,66 @@ server.registerTool('exactpdf_estimate_speech_cost', {
805
806
  file_bytes: fileBytes,
806
807
  characters: estimatedChars || null,
807
808
  generated_minutes: estimatedMinutes,
809
+ credits_per_minute: creditsPerMinute,
808
810
  current_credit_cost: credits,
809
- note: 'ExactPDF currently charges fixed job credits for async speech jobs: 10 for speech/audiobook/presentation, 20 for translate-and-speak. Minute-based pricing is planned.',
811
+ note: 'ExactPDF charges async speech jobs by estimated generated minutes: 1 credit/minute for standard speech, 3 credits/minute for translate-and-speak. Path-based estimates are rough until the API extracts PDF text.',
810
812
  },
811
813
  }, null, 2),
812
814
  },
813
815
  ],
814
816
  };
815
817
  });
818
+ server.registerTool('exactpdf_voice_preview', {
819
+ description: 'Generate a short server-side voice preview before submitting a paid PDF speech job. Saves MP3/WAV locally. Does not consume credits.',
820
+ inputSchema: z.object({
821
+ text: z.string().optional().describe('Preview text, capped server-side at 600 characters.'),
822
+ output_format: z.enum(['mp3', 'wav']).optional().describe('Audio preview format. Default: mp3.'),
823
+ voice_style: z
824
+ .enum(['professional', 'audiobook', 'educational', 'presenter', 'conversational'])
825
+ .optional()
826
+ .describe('Narration style. Default: professional.'),
827
+ voice_id: z.string().optional().describe('Optional Google Cloud voice name, e.g. en-US-Standard-H.'),
828
+ language: z.string().optional().describe('Language hint such as en, hi, es, fr. Default: en.'),
829
+ speed: z.number().min(0.5).max(2).optional().describe('Speech speed multiplier, 0.5-2.0.'),
830
+ }),
831
+ }, async ({ text, output_format, voice_style, voice_id, language, speed }) => {
832
+ const key = requireKey();
833
+ const res = await fetch(`${BASE}/api/v1/voice-preview`, {
834
+ method: 'POST',
835
+ headers: {
836
+ Authorization: `Bearer ${key}`,
837
+ Accept: '*/*',
838
+ 'Content-Type': 'application/json',
839
+ },
840
+ body: JSON.stringify({
841
+ ...(text ? { text } : {}),
842
+ ...(output_format ? { output_format } : {}),
843
+ ...(voice_style ? { voice_style } : {}),
844
+ ...(voice_id ? { voice_id } : {}),
845
+ ...(language ? { language } : {}),
846
+ ...(typeof speed === 'number' ? { speed } : {}),
847
+ }),
848
+ });
849
+ if (!res.ok) {
850
+ const raw = await res.text();
851
+ return {
852
+ content: [{ type: 'text', text: `voice-preview failed HTTP ${res.status}\n${raw.slice(0, 8000)}` }],
853
+ isError: true,
854
+ };
855
+ }
856
+ const ext = extensionFromContentType(res.headers.get('content-type'), output_format ?? 'mp3');
857
+ const outPath = await saveBinaryFromResponse(res, 'exactpdf-voice-preview', ext);
858
+ return {
859
+ content: [
860
+ {
861
+ type: 'text',
862
+ text: `Voice preview saved: ${outPath}\n` +
863
+ `Credits used: ${res.headers.get('x-credits-used') ?? '0'}\n` +
864
+ `Provider: ${res.headers.get('x-exactpdf-provider') ?? 'unknown'}`,
865
+ },
866
+ ],
867
+ };
868
+ });
816
869
  server.registerTool('exactpdf_pdf_to_speech', {
817
870
  description: 'Create an async PDF→speech job via /api/v1/pdf-to-speech (10 credits). Alias of the production audiobook pipeline.',
818
871
  inputSchema: speechJobSchema,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exactpdf/mcp",
3
- "version": "0.2.6",
4
- "description": "MCP server for ExactPDF — PDF tools, async PDF to audiobook jobs, polling, and API credits.",
3
+ "version": "0.2.7",
4
+ "description": "MCP server for ExactPDF — PDF tools, voice previews, async PDF speech/audiobook jobs, polling, and API credits.",
5
5
  "mcpName": "com.exactpdf/mcp",
6
6
  "type": "module",
7
7
  "main": "./dist/run.js",
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, metadata, text, Markdown, async speech/audiobook, multilingual speech, and presentation narration jobs.",
6
- "version": "0.2.6",
5
+ "description": "Agent-facing PDF API: merge, split, rotate, compress, images, metadata, text, Markdown, voice previews, async speech/audiobook, multilingual speech, and presentation narration jobs.",
6
+ "version": "0.2.7",
7
7
  "websiteUrl": "https://exactpdf.com/docs/api",
8
8
  "packages": [
9
9
  {
10
10
  "registryType": "npm",
11
11
  "identifier": "@exactpdf/mcp",
12
- "version": "0.2.6",
12
+ "version": "0.2.7",
13
13
  "transport": {
14
14
  "type": "stdio"
15
15
  }