@kolmopdf/mcp-server 1.2.1 → 1.2.2
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 +3 -1
- package/dist/index.cjs +91 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +77 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/config.ts","../src/context.ts","../src/tools/check-balance.ts","../src/tools/convert.ts","../src/pages.ts","../src/progress.ts","../src/polling.ts","../src/sniff.ts","../src/tools/estimate-cost.ts","../src/tools/get-task-status.ts","../src/tools/parse-pdf.ts","../src/extract.ts","../src/tools/translate-pdf.ts"],"sourcesContent":["/**\n * @kolmopdf/mcp-server — stdio MCP server bootstrap (DEVELOPMENT.md §5.2).\n *\n * - Registers over the stdio transport.\n * - Does NOT validate the API key at startup; the key is read lazily so the\n * server boots even with no network / no key. The first authenticated tool\n * call surfaces a missing key as an MCP error (invalid_api_key).\n */\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { KolmoPdfClient } from \"./client.js\";\nimport { loadConfig } from \"./config.js\";\nimport { type McpSuccessResult, type ToolContext, jsonResult } from \"./context.js\";\nimport { KolmoPdfError, toMcpErrorResult } from \"./errors.js\";\nimport {\n checkBalanceDescription,\n checkBalanceHandler,\n checkBalanceInputSchema,\n checkBalanceName,\n} from \"./tools/check-balance.js\";\nimport {\n convertDescription,\n convertHandler,\n convertInputSchema,\n convertName,\n} from \"./tools/convert.js\";\nimport {\n estimateCostDescription,\n estimateCostHandler,\n estimateCostInputSchema,\n estimateCostName,\n} from \"./tools/estimate-cost.js\";\nimport {\n getTaskStatusDescription,\n getTaskStatusHandler,\n getTaskStatusInputSchema,\n getTaskStatusName,\n} from \"./tools/get-task-status.js\";\nimport {\n parsePdfDescription,\n parsePdfHandler,\n parsePdfInputSchema,\n parsePdfName,\n} from \"./tools/parse-pdf.js\";\nimport {\n translatePdfDescription,\n translatePdfHandler,\n translatePdfInputSchema,\n translatePdfName,\n} from \"./tools/translate-pdf.js\";\n\nconst VERSION = \"1.1.0\";\n\n/** Build the per-call tool context with a lazily-constructed API client. */\nfunction buildContext(): ToolContext {\n const config = loadConfig();\n return {\n config,\n getClient(): KolmoPdfClient {\n if (!config.apiKey) {\n throw new KolmoPdfError(\"invalid_api_key\");\n }\n return new KolmoPdfClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n httpTimeoutMs: config.httpTimeoutMs,\n uploadTimeoutMs: config.uploadTimeoutMs,\n });\n },\n };\n}\n\n/** Wrap a typed handler so all thrown errors become MCP error results (§5.13). */\nfunction guard<A>(\n handler: (args: A, ctx: ToolContext) => Promise<McpSuccessResult>,\n): (args: unknown) => Promise<CallToolResult> {\n return async (args: unknown) => {\n try {\n return (await handler(args as A, buildContext())) as CallToolResult;\n } catch (err) {\n return toMcpErrorResult(err) as CallToolResult;\n }\n };\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer({ name: \"kolmopdf\", version: VERSION });\n\n server.registerTool(\n parsePdfName,\n { description: parsePdfDescription, inputSchema: parsePdfInputSchema.shape },\n guard(parsePdfHandler),\n );\n server.registerTool(\n translatePdfName,\n { description: translatePdfDescription, inputSchema: translatePdfInputSchema.shape },\n guard(translatePdfHandler),\n );\n server.registerTool(\n convertName,\n { description: convertDescription, inputSchema: convertInputSchema.shape },\n guard(convertHandler),\n );\n server.registerTool(\n estimateCostName,\n { description: estimateCostDescription, inputSchema: estimateCostInputSchema.shape },\n guard(estimateCostHandler),\n );\n server.registerTool(\n checkBalanceName,\n { description: checkBalanceDescription, inputSchema: checkBalanceInputSchema.shape },\n guard(checkBalanceHandler),\n );\n server.registerTool(\n getTaskStatusName,\n { description: getTaskStatusDescription, inputSchema: getTaskStatusInputSchema.shape },\n guard(getTaskStatusHandler),\n );\n\n return server;\n}\n\nasync function main(): Promise<void> {\n // Surface --version without booting the transport (TESTING_AND_USAGE.md §9).\n if (process.argv.includes(\"--version\") || process.argv.includes(\"-v\")) {\n process.stdout.write(`${VERSION}\\n`);\n return;\n }\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\n// `jsonResult` is part of the public surface used by tool handlers (M2+).\nexport { jsonResult };\n\nmain().catch((err) => {\n const detail = err instanceof Error ? err.stack : String(err);\n process.stderr.write(`[kolmopdf-mcp] fatal: ${detail}\\n`);\n process.exit(1);\n});\n","import { randomUUID } from \"node:crypto\";\nimport type { Writable } from \"node:stream\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { KolmoPdfError, errorFromApiBody } from \"./errors.js\";\n\nexport interface KolmoPdfClientOptions {\n apiKey: string;\n baseUrl: string;\n httpTimeoutMs: number;\n uploadTimeoutMs: number;\n}\n\nexport interface ParseForm {\n table_mode?: \"markdown\" | \"image\";\n formula_format?: \"dollar\" | \"bracket\";\n enable_translation?: boolean;\n target_language?: string;\n output_options?: string[];\n images_as_url?: boolean;\n skip_rotation_detection?: boolean;\n enable_cross_page_merge?: boolean;\n /** Comma features or `none`. Server default outline,summary when omitted. */\n enrichment?: string;\n}\n\nexport interface TranslateForm {\n source_language?: string;\n target_language?: string;\n layout_modes?: Array<\"translated_only\" | \"side_by_side\">;\n enable_image_translation?: boolean;\n enable_table_translation?: boolean;\n}\n\nexport interface ConvertForm {\n target_format?: string;\n}\n\n/** Normalized client status (maps v1 + legacy). */\nexport type TaskStatus =\n | \"queued\"\n | \"processing\"\n | \"succeeded\"\n | \"failed\"\n | \"cancelled\"\n | \"pending\"\n | \"waiting\"\n | \"completed\"\n | string;\n\nexport interface SubmitResult {\n /** Public job id (v1) or legacy task id */\n task_id: string;\n status: TaskStatus | string;\n points_deducted: number;\n remaining_points: number;\n queue_info?: { position: number; ahead_tasks: number };\n}\n\nexport interface JobResultMeta {\n task_id: string;\n download_url?: string;\n filename?: string | null;\n kind?: string | null;\n content_type?: string | null;\n sha256?: string | null;\n bytes?: number | null;\n files?: Array<{ name: string; kind: string }> | null;\n}\n\nexport interface StatusResult {\n success: boolean;\n status: TaskStatus | string;\n message?: string;\n queue_info?: { position: number; ahead_tasks: number };\n error_code?: string;\n result?: JobResultMeta;\n}\n\nexport interface DownloadMeta {\n contentType: string | null;\n isZip: boolean;\n bytesWritten: number;\n /** Absolute path written when destPath is provided */\n destPath?: string;\n}\n\nexport interface BalanceResult {\n success: boolean;\n points: number;\n api_key: string;\n}\n\nexport type FileInput = Buffer | NodeJS.ReadableStream;\n\nfunction normalizeStatus(status: string | undefined): string {\n if (!status) return \"processing\";\n if (status === \"completed\") return \"succeeded\";\n if (status === \"pending\" || status === \"waiting\") return \"queued\";\n return status;\n}\n\nexport class KolmoPdfClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly httpTimeoutMs: number;\n private readonly uploadTimeoutMs: number;\n\n constructor(opts: KolmoPdfClientOptions) {\n this.apiKey = opts.apiKey;\n this.baseUrl = opts.baseUrl;\n this.httpTimeoutMs = opts.httpTimeoutMs;\n this.uploadTimeoutMs = opts.uploadTimeoutMs;\n }\n\n private get jobsBase(): string {\n return `${this.baseUrl}/api/v1/jobs`;\n }\n\n private headers(): Record<string, string> {\n return {\n \"X-API-Key\": this.apiKey,\n Authorization: `Bearer ${this.apiKey}`,\n };\n }\n\n private async jsonRequest(url: string, init: RequestInit): Promise<Record<string, unknown>> {\n const res = await fetch(url, init);\n let body: Record<string, unknown> = {};\n const text = await res.text();\n try {\n body = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n if (!res.ok) {\n throw new KolmoPdfError(\"api_task_error\", {\n message: `HTTP ${res.status}: non-JSON body`,\n httpStatus: res.status,\n });\n }\n }\n\n // v1 create returns 202 without success:true; treat 2xx as ok unless success===false\n if (!res.ok || body.success === false) {\n const errObj = body.error as { code?: string; message?: string } | undefined;\n throw errorFromApiBody(\n {\n error_code: (body.error_code as string) || errObj?.code,\n message: (body.message as string) || errObj?.message,\n points_required: body.points_required as number | undefined,\n current_points: body.current_points as number | undefined,\n },\n res.status,\n );\n }\n return body;\n }\n\n private async buildFileForm(file: FileInput, filename: string): Promise<FormData> {\n const form = new FormData();\n let blob: Blob;\n if (Buffer.isBuffer(file)) {\n blob = new Blob([file]);\n } else {\n const chunks: Buffer[] = [];\n for await (const chunk of file) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n blob = new Blob([Buffer.concat(chunks)]);\n }\n form.append(\"file\", blob, filename);\n return form;\n }\n\n private normalizeSubmit(body: Record<string, unknown>): SubmitResult {\n const id = String(body.id ?? body.task_id ?? body.legacy_task_id ?? \"\");\n if (!id) {\n throw new KolmoPdfError(\"task_creation_failed\", { message: \"No job id in create response\" });\n }\n const queue = body.queue as { ahead?: number; position?: number } | null | undefined;\n return {\n task_id: id,\n status: normalizeStatus(String(body.status ?? \"queued\")),\n points_deducted: Number(body.points_deducted ?? 0),\n remaining_points: Number(body.remaining_points ?? 0),\n queue_info:\n queue && typeof queue.ahead === \"number\"\n ? { position: queue.position ?? 0, ahead_tasks: queue.ahead }\n : undefined,\n };\n }\n\n async parse(file: FileInput, form: ParseForm, filename: string): Promise<SubmitResult> {\n const fd = await this.buildFileForm(file, filename);\n if (form.table_mode) fd.append(\"table_mode\", form.table_mode);\n if (form.formula_format) fd.append(\"formula_format\", form.formula_format);\n if (form.enable_translation !== undefined)\n fd.append(\"enable_translation\", String(form.enable_translation));\n if (form.target_language) fd.append(\"target_language\", form.target_language);\n if (form.output_options?.length) fd.append(\"output_options\", form.output_options.join(\",\"));\n if (form.images_as_url !== undefined) fd.append(\"images_as_url\", String(form.images_as_url));\n if (form.skip_rotation_detection !== undefined)\n fd.append(\"skip_rotation_detection\", String(form.skip_rotation_detection));\n if (form.enable_cross_page_merge !== undefined)\n fd.append(\"enable_cross_page_merge\", String(form.enable_cross_page_merge));\n if (form.enrichment !== undefined) fd.append(\"enrichment\", form.enrichment);\n\n const body = await this.jsonRequest(`${this.jobsBase}/parse`, {\n method: \"POST\",\n headers: { ...this.headers(), \"Idempotency-Key\": randomUUID() },\n body: fd,\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n return this.normalizeSubmit(body);\n }\n\n async translatePdf(\n file: FileInput,\n form: TranslateForm,\n filename: string,\n ): Promise<SubmitResult> {\n const fd = await this.buildFileForm(file, filename);\n if (form.source_language) fd.append(\"sourceLanguage\", form.source_language);\n if (form.target_language) fd.append(\"targetLanguage\", form.target_language);\n if (form.layout_modes?.length) fd.append(\"layoutModes\", form.layout_modes.join(\",\"));\n if (form.enable_image_translation !== undefined)\n fd.append(\"enableImageTranslation\", String(form.enable_image_translation));\n if (form.enable_table_translation !== undefined)\n fd.append(\"enableTableTranslation\", String(form.enable_table_translation));\n\n const body = await this.jsonRequest(`${this.jobsBase}/translate-pdf`, {\n method: \"POST\",\n headers: { ...this.headers(), \"Idempotency-Key\": randomUUID() },\n body: fd,\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n return this.normalizeSubmit(body);\n }\n\n async convert(file: FileInput, form: ConvertForm, filename: string): Promise<SubmitResult> {\n const fd = await this.buildFileForm(file, filename);\n if (form.target_format) fd.append(\"targetFormat\", form.target_format);\n\n const body = await this.jsonRequest(`${this.jobsBase}/convert`, {\n method: \"POST\",\n headers: { ...this.headers(), \"Idempotency-Key\": randomUUID() },\n body: fd,\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n return this.normalizeSubmit(body);\n }\n\n async getStatus(taskId: string): Promise<StatusResult> {\n const body = await this.jsonRequest(`${this.jobsBase}/${encodeURIComponent(taskId)}`, {\n method: \"GET\",\n headers: this.headers(),\n signal: AbortSignal.timeout(this.httpTimeoutMs),\n });\n\n const status = normalizeStatus(String(body.status ?? \"processing\"));\n const err = body.error as { code?: string; message?: string } | null | undefined;\n const queue = body.queue as { ahead?: number; position?: number } | null | undefined;\n const result = body.result as JobResultMeta | null | undefined;\n\n const ok = status === \"succeeded\" || status === \"completed\";\n return {\n success: ok,\n status,\n message: (body.message as string) || err?.message,\n error_code: err?.code,\n queue_info:\n queue && typeof queue.ahead === \"number\"\n ? { position: queue.position ?? 0, ahead_tasks: queue.ahead }\n : undefined,\n result: result\n ? {\n task_id: taskId,\n download_url: result.download_url,\n filename: result.filename ?? null,\n kind: result.kind ?? null,\n content_type: result.content_type ?? null,\n sha256: result.sha256 ?? null,\n bytes: result.bytes ?? null,\n files: result.files ?? null,\n }\n : undefined,\n };\n }\n\n /** SSE stream for a job. Caller must abort/cancel the response body. */\n async openEvents(taskId: string, signal?: AbortSignal): Promise<Response> {\n const res = await fetch(`${this.jobsBase}/${encodeURIComponent(taskId)}/events`, {\n method: \"GET\",\n headers: {\n ...this.headers(),\n Accept: \"text/event-stream\",\n },\n ...(signal === undefined ? {} : { signal }),\n });\n if (!res.ok) {\n throw new KolmoPdfError(\"api_task_error\", {\n message: `SSE failed with HTTP ${res.status}`,\n httpStatus: res.status,\n });\n }\n return res;\n }\n\n /**\n * Stream download to a Writable, or to destPath (preferred — allows ZIP sniff after write).\n */\n async download(\n taskId: string,\n dest: Writable,\n opts?: { destPath?: string },\n ): Promise<DownloadMeta> {\n const res = await fetch(`${this.jobsBase}/${encodeURIComponent(taskId)}/download`, {\n method: \"GET\",\n headers: this.headers(),\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n if (!res.ok) {\n throw new KolmoPdfError(\"api_task_error\", {\n message: `Download failed with HTTP ${res.status}`,\n httpStatus: res.status,\n });\n }\n const contentType = res.headers.get(\"content-type\");\n let isZip =\n !!contentType &&\n (contentType.includes(\"zip\") ||\n contentType.includes(\"application/octet-stream\") ||\n contentType.includes(\"application/x-zip\"));\n const body = res.body;\n if (!body) {\n throw new KolmoPdfError(\"api_task_error\", { message: \"Empty download response body\" });\n }\n\n const reader = body.getReader();\n let bytesWritten = 0;\n const firstChunks: Buffer[] = [];\n let sniffed = false;\n\n async function* generate() {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const buf = Buffer.from(value);\n bytesWritten += buf.byteLength;\n if (!sniffed) {\n firstChunks.push(buf);\n const head = Buffer.concat(firstChunks);\n if (head.byteLength >= 4) {\n // ZIP local file header magic \"PK\\x03\\x04\"\n if (\n head[0] === 0x50 &&\n head[1] === 0x4b &&\n (head[2] === 0x03 || head[2] === 0x05 || head[2] === 0x07)\n ) {\n isZip = true;\n } else if (!contentType?.includes(\"zip\")) {\n isZip = false;\n }\n sniffed = true;\n }\n }\n yield buf;\n }\n }\n\n const readable = Readable.from(generate());\n await pipeline(readable, dest);\n return { contentType, isZip, bytesWritten, destPath: opts?.destPath };\n }\n\n async getBalance(): Promise<BalanceResult> {\n const body = await this.jsonRequest(`${this.baseUrl}/api/v1/balance`, {\n method: \"GET\",\n headers: this.headers(),\n signal: AbortSignal.timeout(this.httpTimeoutMs),\n });\n return {\n success: body.success !== false,\n points: Number(body.points ?? 0),\n api_key: String(body.api_key ?? \"\"),\n };\n }\n}\n","/**\n * Unified KolmoPDF error model and MCP error-result formatting.\n *\n * Implements the error-code mapping table in DEVELOPMENT.md §8 and the\n * MCP tool error envelope in §5.13.\n */\n\nexport type ErrorSource = \"api\" | \"client\";\n\nexport interface ErrorSpec {\n /** Default human-readable message. */\n message: string;\n /** Actionable remediation hint surfaced to the LLM / user. */\n remediation: string;\n /** Typical HTTP status; null for client-side codes. */\n httpStatus: number | null;\n source: ErrorSource;\n}\n\n/** Canonical mapping of every error_code we may surface (DEVELOPMENT.md §8). */\nexport const ERROR_SPECS: Record<string, ErrorSpec> = {\n // --- API codes ---\n invalid_api_key: {\n message: \"API key is missing or invalid.\",\n remediation: \"Create a key at https://www.kolmopdf.com/api-keys (requires Plus/Pro).\",\n httpStatus: 401,\n source: \"api\",\n },\n insufficient_points: {\n message: \"Not enough credits.\",\n remediation: \"Top up at https://www.kolmopdf.com/subscription.\",\n httpStatus: 402,\n source: \"api\",\n },\n points_deduction_failed: {\n message: \"Credit deduction failed.\",\n remediation: \"Retry; if it persists contact support.\",\n httpStatus: 402,\n source: \"api\",\n },\n no_file_found: {\n message: \"Request missing file field.\",\n remediation: \"(internal) MCP server bug, please report.\",\n httpStatus: 400,\n source: \"api\",\n },\n parse_file_too_large: {\n message: \"PDF exceeds 300MB.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n parse_page_limit_exceeded: {\n message: \"PDF exceeds 800 pages.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n parse_file_not_pdf: {\n message: \"File is not a valid PDF.\",\n remediation: \"Upload a .pdf file.\",\n httpStatus: 400,\n source: \"api\",\n },\n translate_pdf_file_too_large: {\n message: \"PDF exceeds 300MB.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n translate_pdf_file_not_pdf: {\n message: \"File is not a valid PDF.\",\n remediation: \"Upload a .pdf file.\",\n httpStatus: 400,\n source: \"api\",\n },\n translate_pdf_page_limit_exceeded: {\n message: \"PDF exceeds 800 pages.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n convert_file_too_large: {\n message: \"File exceeds 300MB.\",\n remediation: \"Reduce file size.\",\n httpStatus: 400,\n source: \"api\",\n },\n convert_file_type_unsupported: {\n message: \"File must be .md / .markdown / .zip.\",\n remediation: \"Convert source to markdown first.\",\n httpStatus: 400,\n source: \"api\",\n },\n convert_target_format_unsupported: {\n message: \"Target format unsupported.\",\n remediation: \"Use word/docx/html/pdf/latex/tex.\",\n httpStatus: 400,\n source: \"api\",\n },\n file_upload_failed: {\n message: \"Upload to storage failed.\",\n remediation: \"Check network and retry.\",\n httpStatus: 500,\n source: \"api\",\n },\n task_creation_failed: {\n message: \"Task creation failed.\",\n remediation: \"Retry.\",\n httpStatus: 500,\n source: \"api\",\n },\n parse_error: {\n message: \"Parsing failed.\",\n remediation: \"Retry; if it persists, split and try again.\",\n httpStatus: 500,\n source: \"api\",\n },\n parse_file_invalid: {\n message: \"PDF is malformed.\",\n remediation: \"Re-export the PDF.\",\n httpStatus: 500,\n source: \"api\",\n },\n parse_timeout: {\n message: \"Server-side timeout.\",\n remediation: \"Split into smaller PDFs.\",\n httpStatus: 500,\n source: \"api\",\n },\n api_task_error: {\n message: \"Generic task error.\",\n remediation: \"Retry; if it persists contact support.\",\n httpStatus: 500,\n source: \"api\",\n },\n // --- client codes ---\n client_polling_timeout: {\n message: \"Local polling exceeded KOLMOPDF_MAX_POLL_MINUTES.\",\n remediation: \"Task may still be running. Use kolmopdf_get_task_status with task_id.\",\n httpStatus: null,\n source: \"client\",\n },\n client_network_error: {\n message: \"Network error after retries.\",\n remediation: \"Check network.\",\n httpStatus: null,\n source: \"client\",\n },\n client_local_validation: {\n message: \"Local pre-check failed (page count / file size).\",\n remediation: \"See message for the specific limit that was exceeded.\",\n httpStatus: null,\n source: \"client\",\n },\n client_extract_failed: {\n message: \"ZIP extraction failed.\",\n remediation: \"Check disk permissions on output dir.\",\n httpStatus: null,\n source: \"client\",\n },\n} as const;\n\nconst UNKNOWN_SPEC: ErrorSpec = {\n message: \"Unknown error.\",\n remediation: \"Retry; if it persists contact https://www.kolmopdf.com/contact.\",\n httpStatus: null,\n source: \"client\",\n};\n\nexport interface KolmoPdfErrorOptions {\n /** Override the default message from the spec. */\n message?: string;\n /** Override the default HTTP status from the spec. */\n httpStatus?: number | null;\n pointsRequired?: number;\n currentPoints?: number;\n /** Override the default remediation hint. */\n remediation?: string;\n}\n\n/** Structured error thrown across the MCP server; carries a stable error_code. */\nexport class KolmoPdfError extends Error {\n readonly errorCode: string;\n readonly httpStatus: number | null;\n readonly remediation: string;\n readonly pointsRequired: number | undefined;\n readonly currentPoints: number | undefined;\n readonly source: ErrorSource;\n\n constructor(errorCode: string, opts: KolmoPdfErrorOptions = {}) {\n const spec = ERROR_SPECS[errorCode] ?? UNKNOWN_SPEC;\n super(opts.message ?? spec.message);\n this.name = \"KolmoPdfError\";\n this.errorCode = errorCode;\n this.httpStatus = opts.httpStatus !== undefined ? opts.httpStatus : spec.httpStatus;\n this.remediation = opts.remediation ?? spec.remediation;\n this.pointsRequired = opts.pointsRequired;\n this.currentPoints = opts.currentPoints;\n this.source = spec.source;\n }\n}\n\n/** Shape of the JSON payload embedded in an MCP error result (DEVELOPMENT.md §5.13). */\nexport interface McpErrorPayload {\n error_code: string;\n message: string;\n http_status: number | null;\n points_required?: number;\n current_points?: number;\n remediation: string;\n}\n\n/** MCP tool result envelope for an error (matches MCP SDK `CallToolResult`). */\nexport interface McpErrorResult {\n isError: true;\n content: Array<{ type: \"text\"; text: string }>;\n}\n\n/** Convert a KolmoPdfError (or any error) into the MCP error result envelope. */\nexport function toMcpErrorResult(err: unknown): McpErrorResult {\n const kerr =\n err instanceof KolmoPdfError\n ? err\n : new KolmoPdfError(\"api_task_error\", {\n message: err instanceof Error ? err.message : String(err),\n });\n\n const payload: McpErrorPayload = {\n error_code: kerr.errorCode,\n message: kerr.message,\n http_status: kerr.httpStatus,\n remediation: kerr.remediation,\n };\n if (kerr.pointsRequired !== undefined) payload.points_required = kerr.pointsRequired;\n if (kerr.currentPoints !== undefined) payload.current_points = kerr.currentPoints;\n\n return {\n isError: true,\n content: [{ type: \"text\", text: JSON.stringify(payload) }],\n };\n}\n\n/** Whether an API failure with this code is auto-refunded server-side (§8). */\nexport function isAutoRefunded(errorCode: string): boolean {\n return (\n errorCode === \"task_creation_failed\" ||\n errorCode === \"parse_error\" ||\n errorCode === \"parse_file_invalid\" ||\n errorCode === \"parse_timeout\"\n );\n}\n\n/** Map a raw API JSON failure body to a KolmoPdfError. */\nexport function errorFromApiBody(\n body: {\n error_code?: string;\n message?: string;\n points_required?: number;\n current_points?: number;\n },\n httpStatus?: number,\n): KolmoPdfError {\n const code = body.error_code ?? \"api_task_error\";\n return new KolmoPdfError(code, {\n message: body.message,\n httpStatus: httpStatus ?? null,\n pointsRequired: body.points_required,\n currentPoints: body.current_points,\n });\n}\n","/**\n * Environment-variable loader for the KolmoPDF MCP server.\n *\n * Per DEVELOPMENT.md §4 and §5.2, the API key is NOT validated at startup —\n * it is read lazily so the server can boot in offline / no-key environments.\n * The first authenticated tool call surfaces a missing key as an MCP error.\n */\n\nexport interface KolmoPdfConfig {\n /** Resolved at call time; may be undefined until the user sets it. */\n apiKey: string | undefined;\n baseUrl: string;\n outputDir: string;\n pollIntervalMs: number;\n maxPollMinutes: number;\n httpTimeoutMs: number;\n uploadTimeoutMs: number;\n}\n\nconst DEFAULTS = {\n baseUrl: \"https://www.kolmopdf.com\",\n outputDir: \"./kolmopdf-output\",\n pollIntervalMs: 2000,\n maxPollMinutes: 30,\n httpTimeoutMs: 60_000,\n uploadTimeoutMs: 600_000,\n} as const;\n\nfunction intFromEnv(value: string | undefined, fallback: number): number {\n if (value === undefined || value.trim() === \"\") return fallback;\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Build a config object from the current process environment.\n * Reads on every call so live env changes are picked up.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): KolmoPdfConfig {\n const apiKeyRaw = env.KOLMOPDF_API_KEY?.trim();\n return {\n apiKey: apiKeyRaw && apiKeyRaw.length > 0 ? apiKeyRaw : undefined,\n baseUrl: trimTrailingSlash(env.KOLMOPDF_BASE_URL?.trim() || DEFAULTS.baseUrl),\n outputDir: env.KOLMOPDF_OUTPUT_DIR?.trim() || DEFAULTS.outputDir,\n pollIntervalMs: intFromEnv(env.KOLMOPDF_POLL_INTERVAL_MS, DEFAULTS.pollIntervalMs),\n maxPollMinutes: intFromEnv(env.KOLMOPDF_MAX_POLL_MINUTES, DEFAULTS.maxPollMinutes),\n httpTimeoutMs: intFromEnv(env.KOLMOPDF_HTTP_TIMEOUT_MS, DEFAULTS.httpTimeoutMs),\n uploadTimeoutMs: intFromEnv(env.KOLMOPDF_UPLOAD_TIMEOUT_MS, DEFAULTS.uploadTimeoutMs),\n };\n}\n\n/** Mask an API key for display: first 6 + \"***\" + last 4 (DEVELOPMENT.md §5.8). */\nexport function maskApiKey(apiKey: string): string {\n if (apiKey.length <= 10) return \"***\";\n return `${apiKey.slice(0, 6)}***${apiKey.slice(-4)}`;\n}\n\nexport const configDefaults = DEFAULTS;\n","/**\n * Shared per-call context handed to every tool handler.\n */\nimport type { KolmoPdfClient } from \"./client.js\";\nimport type { KolmoPdfConfig } from \"./config.js\";\nimport type { ProgressReporter } from \"./progress.js\";\n\nexport interface ToolContext {\n config: KolmoPdfConfig;\n /** Lazily constructed; throws invalid_api_key when the key is absent. */\n getClient(): KolmoPdfClient;\n progress?: ProgressReporter;\n}\n\n/** Standard MCP success result envelope (subset of the SDK `CallToolResult`). */\nexport interface McpSuccessResult {\n content: Array<{ type: \"text\"; text: string }>;\n structuredContent?: Record<string, unknown>;\n}\n\n/** Wrap a JSON-serializable object into the MCP success envelope. */\nexport function jsonResult(data: Record<string, unknown>): McpSuccessResult {\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n structuredContent: data,\n };\n}\n","import { z } from \"zod\";\nimport { maskApiKey } from \"../config.js\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\n\nexport const checkBalanceName = \"kolmopdf_check_balance\";\n\nexport const checkBalanceDescription =\n \"Show the current KolmoPDF credit balance for the configured API key.\";\n\nexport const checkBalanceInputSchema = z.object({});\n\nexport type CheckBalanceInput = z.infer<typeof checkBalanceInputSchema>;\n\nexport interface CheckBalanceOutput {\n points: number;\n api_key_masked: string;\n}\n\nexport async function checkBalanceHandler(\n _args: CheckBalanceInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const balance = await client.getBalance();\n\n const output: CheckBalanceOutput = {\n points: balance.points,\n api_key_masked: maskApiKey(ctx.config.apiKey || \"\"),\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { createWriteStream, mkdirSync } from \"node:fs\";\nimport { readFile, rename } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { KolmoPdfError } from \"../errors.js\";\nimport { MAX_FILE_BYTES, readFileSize } from \"../pages.js\";\nimport { pollUntilComplete } from \"../polling.js\";\nimport { extensionForKind, sniffFile } from \"../sniff.js\";\n\nexport const convertName = \"kolmopdf_convert_markdown\";\n\nexport const convertDescription =\n \"Convert a Markdown file (or a ZIP of markdown + images) to DOCX, HTML, PDF, \" +\n \"or LaTeX via KolmoPDF.\";\n\nexport const convertInputSchema = z.object({\n file_path: z\n .string()\n .describe(\"Path to a .md/.markdown file or .zip containing markdown + images.\"),\n target_format: z.enum([\"word\", \"docx\", \"html\", \"pdf\", \"latex\", \"tex\"]).optional().default(\"word\"),\n output_subdir: z.string().optional(),\n});\n\nexport type ConvertInput = z.infer<typeof convertInputSchema>;\n\nexport interface ConvertOutput {\n task_id: string;\n points_deducted: number;\n remaining_points: number;\n output: {\n output_path: string;\n target_format: string;\n kind: string;\n };\n}\n\nexport function formatToExtension(targetFormat: string): string {\n switch (targetFormat) {\n case \"word\":\n case \"docx\":\n return \".docx\";\n case \"html\":\n return \".html\";\n case \"pdf\":\n return \".pdf\";\n case \"latex\":\n case \"tex\":\n return \".tex\";\n default:\n return \".out\";\n }\n}\n\nexport function normalizeFormat(targetFormat: string): string {\n switch (targetFormat) {\n case \"word\":\n case \"docx\":\n return \"docx\";\n case \"latex\":\n case \"tex\":\n return \"tex\";\n default:\n return targetFormat;\n }\n}\nexport async function convertHandler(\n args: ConvertInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const filePath = resolve(args.file_path);\n const filename = basename(filePath);\n\n const fileSize = await readFileSize(filePath);\n if (fileSize > MAX_FILE_BYTES) {\n throw new KolmoPdfError(\"convert_file_too_large\");\n }\n\n const ext = filePath.toLowerCase();\n if (!ext.endsWith(\".md\") && !ext.endsWith(\".markdown\") && !ext.endsWith(\".zip\")) {\n throw new KolmoPdfError(\"convert_file_type_unsupported\");\n }\n\n await ctx.progress?.report(\"[uploading] Sending file for conversion...\");\n\n const fileBuffer = await readFile(filePath);\n const submitResult = await client.convert(\n fileBuffer,\n {\n target_format: args.target_format,\n },\n filename,\n );\n\n const taskId = submitResult.task_id;\n await ctx.progress?.report(`[submitted] Task ${taskId} created`);\n\n await pollUntilComplete({\n client,\n taskId,\n options: {\n pollIntervalMs: ctx.config.pollIntervalMs,\n maxPollMinutes: ctx.config.maxPollMinutes,\n },\n progress: ctx.progress,\n });\n\n await ctx.progress?.report(\"[downloading] Fetching converted file...\");\n\n const subdir = args.output_subdir || taskId;\n const outputRoot = resolve(ctx.config.outputDir, subdir);\n mkdirSync(outputRoot, { recursive: true });\n\n const tempPath = join(outputRoot, \"download.bin\");\n const ws = createWriteStream(tempPath);\n await client.download(taskId, ws, { destPath: tempPath });\n const kind = await sniffFile(tempPath);\n const outputPath = join(outputRoot, `result${extensionForKind(kind)}`);\n await rename(tempPath, outputPath);\n\n const output: ConvertOutput = {\n task_id: taskId,\n points_deducted: submitResult.points_deducted,\n remaining_points: submitResult.remaining_points,\n output: {\n output_path: outputPath,\n target_format: normalizeFormat(args.target_format),\n kind,\n },\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { PDFDocument } from \"pdf-lib\";\n\nexport const MAX_PAGES = 800;\nexport const MAX_FILE_BYTES = 300 * 1024 * 1024;\n\nexport async function readPageCount(filePath: string): Promise<number> {\n const data = await readFile(filePath);\n const doc = await PDFDocument.load(data, { ignoreEncryption: true });\n return doc.getPageCount();\n}\n\nexport async function readFileSize(filePath: string): Promise<number> {\n const s = await stat(filePath);\n return s.size;\n}\n","/**\n * MCP progress-notification helper (DEVELOPMENT.md §5.11).\n *\n * `progress` must be monotonically increasing; `total` is omitted because the\n * API does not provide a precise percentage.\n */\n\n/** Minimal shape of the MCP request context used to emit notifications. */\nexport interface ProgressSink {\n /** Present only when the client supplied a progressToken in request `_meta`. */\n progressToken?: string | number;\n notify(notification: {\n method: \"notifications/progress\";\n params: {\n progressToken: string | number;\n progress: number;\n message?: string;\n };\n }): Promise<void>;\n}\n\n/** Stateful emitter that guarantees a monotonically increasing counter. */\nexport class ProgressReporter {\n private counter = 0;\n\n constructor(private readonly sink: ProgressSink | undefined) {}\n\n async report(message: string): Promise<void> {\n if (!this.sink || this.sink.progressToken === undefined) return;\n this.counter += 1;\n await this.sink.notify({\n method: \"notifications/progress\",\n params: {\n progressToken: this.sink.progressToken,\n progress: this.counter,\n message,\n },\n });\n }\n}\n\n/** Build a human-readable status line, e.g. \"[waiting] 3 tasks ahead\". */\nexport function humanizeStatus(status: string, aheadTasks?: number): string {\n if (status === \"waiting\" && typeof aheadTasks === \"number\") {\n return `[waiting] ${aheadTasks} tasks ahead`;\n }\n return `[${status}]`;\n}\n","import type { KolmoPdfClient, StatusResult } from \"./client.js\";\nimport { KolmoPdfError } from \"./errors.js\";\nimport { type ProgressReporter, humanizeStatus } from \"./progress.js\";\n\nexport interface PollOptions {\n pollIntervalMs: number;\n maxPollMinutes: number;\n signal?: AbortSignal;\n}\n\n/** v1 success; legacy `completed` still accepted */\nexport const TERMINAL_OK = new Set([\"succeeded\", \"completed\"]);\nexport const TERMINAL_FAIL = new Set([\"failed\", \"cancelled\"]);\nexport const IN_FLIGHT_STATUSES = new Set([\"queued\", \"pending\", \"waiting\", \"processing\"]);\n\nexport const RETRY_POLICY = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n factor: 2,\n} as const;\n\nexport function backoffDelayMs(attempt: number): number {\n return RETRY_POLICY.baseDelayMs * RETRY_POLICY.factor ** (attempt - 1);\n}\n\nexport function isRetryable(err: { httpStatus?: number | null; code?: string }): boolean {\n const transientCodes = [\"ECONNRESET\", \"ETIMEDOUT\", \"ECONNREFUSED\", \"EAI_AGAIN\"];\n if (err.code && transientCodes.includes(err.code)) return true;\n if (typeof err.httpStatus === \"number\" && err.httpStatus >= 500) return true;\n return false;\n}\n\nexport interface PollContext {\n client: KolmoPdfClient;\n taskId: string;\n options: PollOptions;\n progress?: ProgressReporter;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function fetchStatusWithRetry(client: KolmoPdfClient, taskId: string): Promise<StatusResult> {\n for (let attempt = 1; attempt <= RETRY_POLICY.maxAttempts; attempt++) {\n try {\n return await client.getStatus(taskId);\n } catch (err) {\n const retryable =\n err instanceof KolmoPdfError\n ? isRetryable(err)\n : isRetryable({ code: (err as NodeJS.ErrnoException).code });\n if (!retryable || attempt === RETRY_POLICY.maxAttempts) throw err;\n await sleep(backoffDelayMs(attempt));\n }\n }\n throw new KolmoPdfError(\"client_network_error\");\n}\n\nfunction nextSseFrame(buf: string): { frame: string; rest: string } | null {\n const lf = buf.indexOf(\"\\n\\n\");\n const crlf = buf.indexOf(\"\\r\\n\\r\\n\");\n if (lf < 0 && crlf < 0) return null;\n if (crlf >= 0 && (lf < 0 || crlf < lf)) {\n return { frame: buf.slice(0, crlf), rest: buf.slice(crlf + 4) };\n }\n return { frame: buf.slice(0, lf), rest: buf.slice(lf + 2) };\n}\n\nfunction eventNameFromFrame(raw: string): string {\n let eventName = \"message\";\n for (const line of raw.split(/\\r?\\n/)) {\n if (line.startsWith(\"event:\")) eventName = line.slice(6).trim();\n }\n return eventName;\n}\n\nasync function waitViaSse(ctx: PollContext, deadline: number): Promise<StatusResult | null> {\n const { client, taskId, progress, options } = ctx;\n const remaining = Math.max(1_000, deadline - Date.now());\n const timeout = AbortSignal.timeout(remaining);\n const parent = options.signal;\n const combined = parent === undefined ? timeout : AbortSignal.any([parent, timeout]);\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n try {\n const res = await client.openEvents(taskId, combined);\n const body = res.body;\n if (!body) return null;\n reader = body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n\n const handleEvent = async (eventName: string): Promise<StatusResult | \"continue\"> => {\n if (eventName === \"job.succeeded\") {\n const status = await fetchStatusWithRetry(client, taskId);\n if (TERMINAL_OK.has(String(status.status || \"\"))) {\n await progress?.report(`[completed] Task ${taskId} done`);\n return status;\n }\n return \"continue\";\n }\n if (eventName === \"job.failed\" || eventName === \"job.cancelled\") {\n const failed = await fetchStatusWithRetry(client, taskId);\n throw new KolmoPdfError(failed.error_code || eventName.slice(\"job.\".length), {\n message: failed.message || \"Task failed\",\n });\n }\n if (eventName === \"job.progress\" || eventName === \"job.snapshot\") {\n await progress?.report(humanizeStatus(\"processing\"));\n }\n return \"continue\";\n };\n\n while (!timeout.aborted) {\n if (parent?.aborted === true) throw new KolmoPdfError(\"client_polling_timeout\");\n const { done, value } = await reader.read();\n if (done) {\n buf += decoder.decode();\n const last = nextSseFrame(`${buf}\\n\\n`);\n if (last) {\n const result = await handleEvent(eventNameFromFrame(last.frame));\n if (result !== \"continue\") return result;\n }\n break;\n }\n buf += decoder.decode(value, { stream: true });\n let next = nextSseFrame(buf);\n while (next) {\n buf = next.rest;\n const result = await handleEvent(eventNameFromFrame(next.frame));\n if (result !== \"continue\") return result;\n next = nextSseFrame(buf);\n }\n }\n return null;\n } catch (err) {\n if (err instanceof KolmoPdfError) {\n const code = err.errorCode;\n if (\n code !== \"api_task_error\" &&\n code !== \"client_network_error\" &&\n code !== \"client_polling_timeout\"\n ) {\n throw err;\n }\n }\n return null;\n } finally {\n try {\n await reader?.cancel();\n } catch {\n /* ignore */\n }\n }\n}\n\nexport async function pollUntilComplete(ctx: PollContext): Promise<StatusResult> {\n const { client, taskId, options, progress } = ctx;\n const deadline = Date.now() + options.maxPollMinutes * 60_000;\n\n const viaSse = await waitViaSse(ctx, deadline);\n if (viaSse && TERMINAL_OK.has(String(viaSse.status || \"\"))) return viaSse;\n if (viaSse && TERMINAL_FAIL.has(String(viaSse.status || \"\"))) {\n throw new KolmoPdfError(viaSse.error_code || \"api_task_error\", {\n message: viaSse.message || \"Task failed\",\n });\n }\n\n while (true) {\n if (options.signal?.aborted === true) {\n throw new KolmoPdfError(\"client_polling_timeout\");\n }\n if (Date.now() > deadline) {\n throw new KolmoPdfError(\"client_polling_timeout\");\n }\n\n const result = await fetchStatusWithRetry(client, taskId);\n const status = String(result.status || \"\");\n\n if (TERMINAL_OK.has(status)) {\n await progress?.report(`[completed] Task ${taskId} done`);\n return result;\n }\n\n if (TERMINAL_FAIL.has(status)) {\n throw new KolmoPdfError(result.error_code || \"api_task_error\", {\n message: result.message || \"Task failed\",\n });\n }\n\n const aheadTasks = result.queue_info?.ahead_tasks;\n await progress?.report(humanizeStatus(result.status as string, aheadTasks));\n await sleep(options.pollIntervalMs);\n }\n}\n","import { open, rename } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport type SniffKind = \"zip\" | \"pdf\" | \"markdown\" | \"docx\" | \"html\" | \"latex\" | \"binary\";\n\nconst EXT: Record<SniffKind, string> = {\n zip: \".zip\",\n pdf: \".pdf\",\n markdown: \".md\",\n docx: \".docx\",\n html: \".html\",\n latex: \".tex\",\n binary: \".bin\",\n};\n\nexport function extensionForKind(kind: SniffKind): string {\n return EXT[kind];\n}\n\nexport function sniffBytes(buf: Uint8Array): SniffKind {\n if (\n buf.length >= 4 &&\n buf[0] === 0x50 &&\n buf[1] === 0x4b &&\n (buf[2] === 0x03 || buf[2] === 0x05 || buf[2] === 0x07)\n ) {\n const hay = Buffer.from(buf.subarray(0, Math.min(buf.length, 65536))).toString(\"latin1\");\n if (\n hay.includes(\"word/document.xml\") ||\n hay.includes(\"wordprocessingml.document\") ||\n (hay.includes(\"[Content_Types].xml\") && hay.toLowerCase().includes(\"word/\"))\n ) {\n return \"docx\";\n }\n return \"zip\";\n }\n if (buf.length >= 4 && buf[0] === 0x25 && buf[1] === 0x50 && buf[2] === 0x44 && buf[3] === 0x46) {\n return \"pdf\";\n }\n const head = Buffer.from(buf.subarray(0, Math.min(buf.length, 800))).toString(\"utf8\");\n const trimmed = head.trimStart().toLowerCase();\n if (trimmed.startsWith(\"<!doctype html\") || trimmed.startsWith(\"<html\")) return \"html\";\n if (trimmed.startsWith(\"\\\\documentclass\") || trimmed.startsWith(\"\\\\begin{document}\"))\n return \"latex\";\n if (head.trimStart().startsWith(\"#\") || head.includes(\"\\n# \") || head.includes(\"\\n```\"))\n return \"markdown\";\n return \"binary\";\n}\n\nexport async function sniffFile(filePath: string): Promise<SniffKind> {\n const handle = await open(filePath, \"r\");\n try {\n const bytes = Buffer.alloc(65536);\n const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);\n return sniffBytes(bytes.subarray(0, bytesRead));\n } finally {\n await handle.close();\n }\n}\n\nexport async function renameBySniff(\n tempPath: string,\n destDir: string,\n stem: string,\n): Promise<{\n path: string;\n kind: SniffKind;\n}> {\n const kind = await sniffFile(tempPath);\n const path = join(destDir, `${stem}${EXT[kind]}`);\n if (path !== tempPath) {\n await rename(tempPath, path);\n }\n return { path, kind };\n}\n\nexport function replaceExt(filename: string, ext: string): string {\n const e = ext.startsWith(\".\") ? ext : `.${ext}`;\n const base = filename.replace(/\\.[^.]+$/, \"\") || \"result\";\n return `${base}${e}`;\n}\n","import { resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { readPageCount } from \"../pages.js\";\n\nexport const estimateCostName = \"kolmopdf_estimate_cost\";\n\nexport const estimateCostDescription =\n \"Estimate the credit cost of a KolmoPDF operation before running it. \" +\n \"Reads page count locally and checks the current balance. Does not spend credits.\";\n\nexport const estimateCostInputSchema = z.object({\n file_path: z.string(),\n operation: z.enum([\"parse\", \"parse_translate\", \"translate\", \"convert\"]),\n options: z\n .object({\n images_as_url: z.boolean().optional(),\n })\n .optional(),\n});\n\nexport type EstimateCostInput = z.infer<typeof estimateCostInputSchema>;\n\nexport type Operation = EstimateCostInput[\"operation\"];\n\nexport interface EstimateCostOutput {\n pages: number | null;\n estimated_credits: number;\n current_balance: number;\n sufficient: boolean;\n shortfall: number;\n recommendation: string;\n}\n\nexport function estimateCredits(operation: Operation, pages: number): number {\n switch (operation) {\n case \"parse\":\n return pages * 2;\n case \"parse_translate\":\n return pages * 3;\n case \"translate\":\n return pages * 2;\n case \"convert\":\n return 1;\n }\n}\n\nexport function buildRecommendation(shortfall: number): string {\n return shortfall > 0\n ? `Need top-up at https://www.kolmopdf.com/subscription (short by ${shortfall} credits).`\n : \"Sufficient\";\n}\n\nexport async function estimateCostHandler(\n args: EstimateCostInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n\n let pages: number | null = null;\n if (args.operation !== \"convert\") {\n const filePath = resolve(args.file_path);\n pages = await readPageCount(filePath);\n }\n\n const estimatedCredits = estimateCredits(args.operation, pages ?? 1);\n const balance = await client.getBalance();\n const currentBalance = balance.points;\n const shortfall = Math.max(0, estimatedCredits - currentBalance);\n\n const output: EstimateCostOutput = {\n pages,\n estimated_credits: estimatedCredits,\n current_balance: currentBalance,\n sufficient: shortfall === 0,\n shortfall,\n recommendation: buildRecommendation(shortfall),\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\n\nexport const getTaskStatusName = \"kolmopdf_get_task_status\";\n\nexport const getTaskStatusDescription =\n \"Advanced/debug tool. Returns raw status for a KolmoPDF task. Use only when \" +\n \"explicitly asked to inspect a task by ID, or when troubleshooting a stuck task.\";\n\nexport const getTaskStatusInputSchema = z.object({\n task_id: z.string(),\n});\n\nexport type GetTaskStatusInput = z.infer<typeof getTaskStatusInputSchema>;\n\nexport async function getTaskStatusHandler(\n args: GetTaskStatusInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const status = await client.getStatus(args.task_id);\n return jsonResult(status as unknown as Record<string, unknown>);\n}\n","import { createWriteStream, mkdirSync } from \"node:fs\";\nimport { readFile, rename } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { KolmoPdfError } from \"../errors.js\";\nimport { extractZip } from \"../extract.js\";\nimport { MAX_FILE_BYTES, MAX_PAGES, readFileSize, readPageCount } from \"../pages.js\";\nimport { pollUntilComplete } from \"../polling.js\";\nimport { sniffFile } from \"../sniff.js\";\n\nexport const parsePdfName = \"kolmopdf_parse_pdf\";\n\nexport const parsePdfDescription =\n \"Parse a local PDF into Markdown via KolmoPDF. Handles formulas, tables, \" +\n \"multi-column layouts, and code blocks. Optionally translates while parsing. \" +\n \"Server may attach outline.md/summary.md sidecars (ZIP download).\";\n\nexport const parsePdfInputSchema = z.object({\n file_path: z.string().describe(\"Absolute or cwd-relative path to a local PDF file.\"),\n table_mode: z.enum([\"markdown\", \"image\"]).optional(),\n formula_format: z.enum([\"dollar\", \"bracket\"]).optional(),\n enable_translation: z.boolean().optional(),\n target_language: z.enum([\"zh\", \"en\", \"ja\", \"ko\", \"fr\", \"de\", \"es\", \"ru\"]).optional(),\n output_options: z.array(z.enum([\"original\", \"translated\", \"bilingual\"])).optional(),\n images_as_url: z.boolean().optional(),\n skip_rotation_detection: z.boolean().optional(),\n enable_cross_page_merge: z.boolean().optional(),\n enrichment: z\n .string()\n .optional()\n .describe(\n \"Parse-time AI sidecars. Omit for server default outline,summary. Use 'none' to disable. Examples: outline,summary,verification\",\n ),\n output_subdir: z\n .string()\n .optional()\n .describe(\"Subdirectory name under KOLMOPDF_OUTPUT_DIR. Defaults to <task_id>.\"),\n});\n\nexport type ParsePdfInput = z.infer<typeof parsePdfInputSchema>;\n\nexport interface ParsePdfOutput {\n task_id: string;\n pages_parsed: number;\n points_deducted: number;\n remaining_points: number;\n output: {\n type: \"zip_extracted\" | \"markdown_file\";\n markdown_path: string;\n images_dir: string | null;\n output_root: string;\n };\n preview: string;\n}\n\nexport async function parsePdfHandler(\n args: ParsePdfInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const filePath = resolve(args.file_path);\n const filename = basename(filePath);\n\n const fileSize = await readFileSize(filePath);\n if (fileSize > MAX_FILE_BYTES) {\n throw new KolmoPdfError(\"parse_file_too_large\");\n }\n\n const pageCount = await readPageCount(filePath);\n if (pageCount > MAX_PAGES) {\n throw new KolmoPdfError(\"parse_page_limit_exceeded\");\n }\n\n await ctx.progress?.report(\"[uploading] Sending PDF to KolmoPDF...\");\n\n const fileBuffer = await readFile(filePath);\n const submitResult = await client.parse(\n fileBuffer,\n {\n table_mode: args.table_mode,\n formula_format: args.formula_format,\n enable_translation: args.enable_translation,\n target_language: args.target_language,\n output_options: args.output_options,\n images_as_url: args.images_as_url,\n skip_rotation_detection: args.skip_rotation_detection,\n enable_cross_page_merge: args.enable_cross_page_merge,\n enrichment: args.enrichment,\n },\n filename,\n );\n\n const taskId = submitResult.task_id;\n await ctx.progress?.report(`[submitted] Task ${taskId} created`);\n\n await pollUntilComplete({\n client,\n taskId,\n options: {\n pollIntervalMs: ctx.config.pollIntervalMs,\n maxPollMinutes: ctx.config.maxPollMinutes,\n },\n progress: ctx.progress,\n });\n\n await ctx.progress?.report(\"[downloading] Fetching result...\");\n\n const subdir = args.output_subdir || taskId;\n const outputRoot = resolve(ctx.config.outputDir, subdir);\n mkdirSync(outputRoot, { recursive: true });\n\n // Always download to a temp name first — server may return ZIP even when images_as_url\n // (enrichment sidecars force a multi-file bundle). Trust magic bytes, not Content-Type.\n const downloadPath = join(outputRoot, \"download.bin\");\n const ws = createWriteStream(downloadPath);\n await client.download(taskId, ws, { destPath: downloadPath });\n\n let markdownPath: string;\n let imagesDir: string | null = null;\n let outputType: \"zip_extracted\" | \"markdown_file\";\n\n const kind = await sniffFile(downloadPath);\n if (kind === \"zip\") {\n const zipPath = join(outputRoot, \"result.zip\");\n await rename(downloadPath, zipPath);\n const extracted = await extractZip(zipPath, outputRoot);\n markdownPath = extracted.markdownPath || join(outputRoot, \"result.md\");\n imagesDir = extracted.imagesDir;\n outputType = \"zip_extracted\";\n } else {\n markdownPath = join(outputRoot, \"result.md\");\n await rename(downloadPath, markdownPath);\n outputType = \"markdown_file\";\n }\n\n const mdContent = await readFile(markdownPath, \"utf-8\").catch(() => \"\");\n const preview = mdContent.slice(0, 500);\n\n const ptsPerPage = args.enable_translation ? 3 : 2;\n const pagesParsed = Math.round(submitResult.points_deducted / ptsPerPage);\n\n const output: ParsePdfOutput = {\n task_id: taskId,\n pages_parsed: pagesParsed,\n points_deducted: submitResult.points_deducted,\n remaining_points: submitResult.remaining_points,\n output: {\n type: outputType,\n markdown_path: markdownPath,\n images_dir: imagesDir,\n output_root: outputRoot,\n },\n preview,\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { createWriteStream, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { type Entry, type ZipFile, open as yauzlOpen } from \"yauzl\";\n\nexport interface ExtractResult {\n markdownPath: string | null;\n imagesDir: string | null;\n outputRoot: string;\n files: string[];\n}\n\n/** Prefer primary document MD over enrichment sidecars (outline/summary/etc.). */\nexport function pickPrimaryMarkdownPath(\n candidates: Array<{ path: string; entryName: string; size: number }>,\n): string | null {\n if (candidates.length === 0) return null;\n const scored = candidates.map((c) => {\n const base = (c.entryName.split(\"/\").pop() || c.entryName).toLowerCase();\n let score = c.size;\n if (\n /^(outline|summary|verification_report|enrichment_meta|tables_changelog|tables_normalized)(\\.|$)/i.test(\n base,\n ) ||\n /outline|summary|verification|enrichment|tables_/.test(base)\n ) {\n score -= 1e12;\n }\n if (base === \"readme.md\") score -= 1e9;\n if (/translated|bilingual/.test(base)) score -= 1e6;\n return { path: c.path, score };\n });\n scored.sort((a, b) => b.score - a.score);\n return scored[0]?.path ?? null;\n}\n\nexport async function extractZip(zipPath: string, destDir: string): Promise<ExtractResult> {\n mkdirSync(destDir, { recursive: true });\n\n const zipFile = await openZip(zipPath);\n const files: string[] = [];\n const mdCandidates: Array<{ path: string; entryName: string; size: number }> = [];\n let imagesDir: string | null = null;\n\n for await (const entry of iterEntries(zipFile)) {\n const entryPath = join(destDir, entry.fileName);\n\n if (entry.fileName.endsWith(\"/\")) {\n mkdirSync(entryPath, { recursive: true });\n if (entry.fileName.includes(\"images\")) {\n imagesDir = entryPath;\n }\n continue;\n }\n\n mkdirSync(dirname(entryPath), { recursive: true });\n const readStream = await openReadStream(zipFile, entry);\n const writeStream = createWriteStream(entryPath);\n await pipeline(readStream, writeStream);\n files.push(entryPath);\n\n if (/\\.md$/i.test(entry.fileName)) {\n let size = entry.uncompressedSize || 0;\n try {\n size = readFileSync(entryPath).byteLength;\n } catch {\n /* keep zip header size */\n }\n mdCandidates.push({ path: entryPath, entryName: entry.fileName, size });\n }\n if (!imagesDir && /images\\//i.test(entry.fileName)) {\n const prefix = entry.fileName.split(\"images/\")[0] ?? \"\";\n imagesDir = join(destDir, prefix, \"images\");\n }\n }\n\n const markdownPath = pickPrimaryMarkdownPath(mdCandidates);\n\n return { markdownPath, imagesDir, outputRoot: destDir, files };\n}\n\nfunction openZip(path: string): Promise<ZipFile> {\n return new Promise((resolve, reject) => {\n yauzlOpen(path, { lazyEntries: true }, (err, zf) => {\n if (err || !zf) return reject(err ?? new Error(\"Failed to open zip\"));\n resolve(zf);\n });\n });\n}\n\nasync function* iterEntries(zipFile: ZipFile): AsyncGenerator<Entry> {\n let resolve: ((entry: Entry | null) => void) | null = null;\n const queue: (Entry | null)[] = [];\n\n zipFile.on(\"entry\", (entry: Entry) => {\n if (resolve) {\n const r = resolve;\n resolve = null;\n r(entry);\n } else {\n queue.push(entry);\n }\n });\n zipFile.on(\"end\", () => {\n if (resolve) {\n const r = resolve;\n resolve = null;\n r(null);\n } else {\n queue.push(null);\n }\n });\n\n zipFile.readEntry();\n while (true) {\n const entry =\n queue.length > 0\n ? (queue.shift() as Entry | null)\n : await new Promise<Entry | null>((r) => {\n resolve = r;\n });\n if (entry === null) break;\n yield entry;\n zipFile.readEntry();\n }\n}\n\nfunction openReadStream(zipFile: ZipFile, entry: Entry): Promise<NodeJS.ReadableStream> {\n return new Promise((resolve, reject) => {\n zipFile.openReadStream(entry, (err, stream) => {\n if (err || !stream) return reject(err ?? new Error(\"Failed to open entry stream\"));\n resolve(stream);\n });\n });\n}\n","import { createWriteStream, mkdirSync } from \"node:fs\";\nimport { readFile, rename } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { KolmoPdfError } from \"../errors.js\";\nimport { extractZip } from \"../extract.js\";\nimport { MAX_FILE_BYTES, MAX_PAGES, readFileSize, readPageCount } from \"../pages.js\";\nimport { pollUntilComplete } from \"../polling.js\";\nimport { extensionForKind, sniffFile } from \"../sniff.js\";\n\nexport const translatePdfName = \"kolmopdf_translate_pdf\";\n\nexport const translatePdfDescription =\n \"Translate a PDF while preserving its original layout via KolmoPDF. \" +\n \"Produces a translated PDF, or a ZIP of PDFs when multiple layout modes are requested.\";\n\nexport const translatePdfInputSchema = z.object({\n file_path: z.string(),\n source_language: z.string().optional().default(\"en\"),\n target_language: z.string().optional().default(\"zh\"),\n layout_modes: z\n .array(z.enum([\"translated_only\", \"side_by_side\"]))\n .optional()\n .default([\"translated_only\"]),\n enable_image_translation: z.boolean().optional().default(false),\n enable_table_translation: z.boolean().optional().default(false),\n output_subdir: z.string().optional(),\n});\n\nexport type TranslatePdfInput = z.infer<typeof translatePdfInputSchema>;\n\nexport interface TranslatePdfOutput {\n task_id: string;\n pages_translated: number;\n points_deducted: number;\n remaining_points: number;\n output: {\n kind: string;\n translated_pdf_path: string;\n archive_path?: string;\n };\n}\n\nexport async function translatePdfHandler(\n args: TranslatePdfInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const filePath = resolve(args.file_path);\n const filename = basename(filePath);\n const fileSize = await readFileSize(filePath);\n if (fileSize > MAX_FILE_BYTES) {\n throw new KolmoPdfError(\"translate_pdf_file_too_large\");\n }\n\n const pageCount = await readPageCount(filePath);\n if (pageCount > MAX_PAGES) {\n throw new KolmoPdfError(\"translate_pdf_page_limit_exceeded\");\n }\n\n await ctx.progress?.report(\"[uploading] Sending PDF for translation...\");\n\n const fileBuffer = await readFile(filePath);\n const submitResult = await client.translatePdf(\n fileBuffer,\n {\n source_language: args.source_language,\n target_language: args.target_language,\n layout_modes: args.layout_modes,\n enable_image_translation: args.enable_image_translation,\n enable_table_translation: args.enable_table_translation,\n },\n filename,\n );\n\n const taskId = submitResult.task_id;\n await ctx.progress?.report(`[submitted] Task ${taskId} created`);\n\n await pollUntilComplete({\n client,\n taskId,\n options: {\n pollIntervalMs: ctx.config.pollIntervalMs,\n maxPollMinutes: ctx.config.maxPollMinutes,\n },\n progress: ctx.progress,\n });\n\n await ctx.progress?.report(\"[downloading] Fetching translated result...\");\n\n const subdir = args.output_subdir || taskId;\n const outputRoot = resolve(ctx.config.outputDir, subdir);\n mkdirSync(outputRoot, { recursive: true });\n\n const tempPath = join(outputRoot, \"download.bin\");\n const ws = createWriteStream(tempPath);\n await client.download(taskId, ws, { destPath: tempPath });\n\n const kind = await sniffFile(tempPath);\n let translatedPdfPath = join(outputRoot, `translated${extensionForKind(kind)}`);\n let archivePath: string | undefined;\n await rename(tempPath, translatedPdfPath);\n\n if (kind === \"zip\") {\n archivePath = translatedPdfPath;\n const extracted = await extractZip(archivePath, outputRoot);\n const pdfs = extracted.files.filter((f) => f.toLowerCase().endsWith(\".pdf\"));\n if (pdfs[0]) translatedPdfPath = pdfs[0];\n }\n\n const pagesTranslated = Math.round(submitResult.points_deducted / 2);\n\n const output: TranslatePdfOutput = {\n task_id: taskId,\n pages_translated: pagesTranslated,\n points_deducted: submitResult.points_deducted,\n remaining_points: submitResult.remaining_points,\n output: {\n kind,\n translated_pdf_path: translatedPdfPath,\n ...(archivePath ? { archive_path: archivePath } : {}),\n },\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n"],"mappings":"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","names":["import_promises","import_node_path","import_zod","import_promises","resolve","import_promises","import_node_path","import_zod","import_zod","import_node_fs","import_promises","import_node_path","import_zod","import_node_fs","import_node_path","import_promises","resolve","yauzlOpen","import_node_fs","import_promises","import_node_path","import_zod"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/config.ts","../src/context.ts","../src/tools/check-balance.ts","../src/tools/convert.ts","../src/output.ts","../src/pages.ts","../src/progress.ts","../src/polling.ts","../src/sniff.ts","../src/tools/estimate-cost.ts","../src/tools/get-task-status.ts","../src/tools/parse-pdf.ts","../src/extract.ts","../src/tools/translate-pdf.ts"],"sourcesContent":["/**\n * @kolmopdf/mcp-server — stdio MCP server bootstrap (DEVELOPMENT.md §5.2).\n *\n * - Registers over the stdio transport.\n * - Does NOT validate the API key at startup; the key is read lazily so the\n * server boots even with no network / no key. The first authenticated tool\n * call surfaces a missing key as an MCP error (invalid_api_key).\n */\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { KolmoPdfClient } from \"./client.js\";\nimport { loadConfig } from \"./config.js\";\nimport { type McpSuccessResult, type ToolContext, jsonResult } from \"./context.js\";\nimport { KolmoPdfError, toMcpErrorResult } from \"./errors.js\";\nimport {\n checkBalanceDescription,\n checkBalanceHandler,\n checkBalanceInputSchema,\n checkBalanceName,\n} from \"./tools/check-balance.js\";\nimport {\n convertDescription,\n convertHandler,\n convertInputSchema,\n convertName,\n} from \"./tools/convert.js\";\nimport {\n estimateCostDescription,\n estimateCostHandler,\n estimateCostInputSchema,\n estimateCostName,\n} from \"./tools/estimate-cost.js\";\nimport {\n getTaskStatusDescription,\n getTaskStatusHandler,\n getTaskStatusInputSchema,\n getTaskStatusName,\n} from \"./tools/get-task-status.js\";\nimport {\n parsePdfDescription,\n parsePdfHandler,\n parsePdfInputSchema,\n parsePdfName,\n} from \"./tools/parse-pdf.js\";\nimport {\n translatePdfDescription,\n translatePdfHandler,\n translatePdfInputSchema,\n translatePdfName,\n} from \"./tools/translate-pdf.js\";\n\ndeclare const __KOLMOPDF_VERSION__: string;\nconst VERSION = __KOLMOPDF_VERSION__;\n\n/** Build the per-call tool context with a lazily-constructed API client. */\nfunction buildContext(): ToolContext {\n const config = loadConfig();\n return {\n config,\n getClient(): KolmoPdfClient {\n if (!config.apiKey) {\n throw new KolmoPdfError(\"invalid_api_key\");\n }\n return new KolmoPdfClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n httpTimeoutMs: config.httpTimeoutMs,\n uploadTimeoutMs: config.uploadTimeoutMs,\n });\n },\n };\n}\n\n/** Wrap a typed handler so all thrown errors become MCP error results (§5.13). */\nfunction guard<A>(\n handler: (args: A, ctx: ToolContext) => Promise<McpSuccessResult>,\n): (args: unknown) => Promise<CallToolResult> {\n return async (args: unknown) => {\n try {\n return (await handler(args as A, buildContext())) as CallToolResult;\n } catch (err) {\n return toMcpErrorResult(err) as CallToolResult;\n }\n };\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer({ name: \"kolmopdf\", version: VERSION });\n\n server.registerTool(\n parsePdfName,\n { description: parsePdfDescription, inputSchema: parsePdfInputSchema.shape },\n guard(parsePdfHandler),\n );\n server.registerTool(\n translatePdfName,\n { description: translatePdfDescription, inputSchema: translatePdfInputSchema.shape },\n guard(translatePdfHandler),\n );\n server.registerTool(\n convertName,\n { description: convertDescription, inputSchema: convertInputSchema.shape },\n guard(convertHandler),\n );\n server.registerTool(\n estimateCostName,\n { description: estimateCostDescription, inputSchema: estimateCostInputSchema.shape },\n guard(estimateCostHandler),\n );\n server.registerTool(\n checkBalanceName,\n { description: checkBalanceDescription, inputSchema: checkBalanceInputSchema.shape },\n guard(checkBalanceHandler),\n );\n server.registerTool(\n getTaskStatusName,\n { description: getTaskStatusDescription, inputSchema: getTaskStatusInputSchema.shape },\n guard(getTaskStatusHandler),\n );\n\n return server;\n}\n\nasync function main(): Promise<void> {\n // Surface --version without booting the transport (TESTING_AND_USAGE.md §9).\n if (process.argv.includes(\"--version\") || process.argv.includes(\"-v\")) {\n process.stdout.write(`${VERSION}\\n`);\n return;\n }\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\n// `jsonResult` is part of the public surface used by tool handlers (M2+).\nexport { jsonResult };\n\nmain().catch((err) => {\n const detail = err instanceof Error ? err.stack : String(err);\n process.stderr.write(`[kolmopdf-mcp] fatal: ${detail}\\n`);\n process.exit(1);\n});\n","import { randomUUID } from \"node:crypto\";\nimport type { Writable } from \"node:stream\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { KolmoPdfError, errorFromApiBody } from \"./errors.js\";\n\nexport interface KolmoPdfClientOptions {\n apiKey: string;\n baseUrl: string;\n httpTimeoutMs: number;\n uploadTimeoutMs: number;\n}\n\nexport interface ParseForm {\n table_mode?: \"markdown\" | \"image\";\n formula_format?: \"dollar\" | \"bracket\";\n enable_translation?: boolean;\n target_language?: string;\n output_options?: string[];\n images_as_url?: boolean;\n skip_rotation_detection?: boolean;\n enable_cross_page_merge?: boolean;\n /** Comma features or `none`. Server default outline,summary when omitted. */\n enrichment?: string;\n}\n\nexport interface TranslateForm {\n source_language?: string;\n target_language?: string;\n layout_modes?: Array<\"translated_only\" | \"side_by_side\">;\n enable_image_translation?: boolean;\n enable_table_translation?: boolean;\n}\n\nexport interface ConvertForm {\n target_format?: string;\n}\n\n/** Normalized client status (maps v1 + legacy). */\nexport type TaskStatus =\n | \"queued\"\n | \"processing\"\n | \"succeeded\"\n | \"failed\"\n | \"cancelled\"\n | \"pending\"\n | \"waiting\"\n | \"completed\"\n | string;\n\nexport interface SubmitResult {\n /** Public job id (v1) or legacy task id */\n task_id: string;\n status: TaskStatus | string;\n points_deducted: number;\n remaining_points: number;\n queue_info?: { position: number; ahead_tasks: number };\n}\n\nexport interface JobResultMeta {\n task_id: string;\n download_url?: string;\n filename?: string | null;\n kind?: string | null;\n content_type?: string | null;\n sha256?: string | null;\n bytes?: number | null;\n files?: Array<{ name: string; kind: string }> | null;\n}\n\nexport interface StatusResult {\n success: boolean;\n status: TaskStatus | string;\n message?: string;\n queue_info?: { position: number; ahead_tasks: number };\n error_code?: string;\n result?: JobResultMeta;\n}\n\nexport interface DownloadMeta {\n contentType: string | null;\n isZip: boolean;\n bytesWritten: number;\n /** Absolute path written when destPath is provided */\n destPath?: string;\n}\n\nexport interface BalanceResult {\n success: boolean;\n points: number;\n api_key: string;\n}\n\nexport type FileInput = Buffer | NodeJS.ReadableStream;\n\nfunction normalizeStatus(status: string | undefined): string {\n if (!status) return \"processing\";\n if (status === \"completed\") return \"succeeded\";\n if (status === \"pending\" || status === \"waiting\") return \"queued\";\n return status;\n}\n\nexport class KolmoPdfClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly httpTimeoutMs: number;\n private readonly uploadTimeoutMs: number;\n\n constructor(opts: KolmoPdfClientOptions) {\n this.apiKey = opts.apiKey;\n this.baseUrl = opts.baseUrl;\n this.httpTimeoutMs = opts.httpTimeoutMs;\n this.uploadTimeoutMs = opts.uploadTimeoutMs;\n }\n\n private get jobsBase(): string {\n return `${this.baseUrl}/api/v1/jobs`;\n }\n\n private headers(): Record<string, string> {\n return {\n \"X-API-Key\": this.apiKey,\n Authorization: `Bearer ${this.apiKey}`,\n };\n }\n\n private async jsonRequest(url: string, init: RequestInit): Promise<Record<string, unknown>> {\n const res = await fetch(url, init);\n let body: Record<string, unknown> = {};\n const text = await res.text();\n try {\n body = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {\n if (!res.ok) {\n throw new KolmoPdfError(\"api_task_error\", {\n message: `HTTP ${res.status}: non-JSON body`,\n httpStatus: res.status,\n });\n }\n }\n\n // v1 create returns 202 without success:true; treat 2xx as ok unless success===false\n if (!res.ok || body.success === false) {\n const errObj = body.error as { code?: string; message?: string } | undefined;\n throw errorFromApiBody(\n {\n error_code: (body.error_code as string) || errObj?.code,\n message: (body.message as string) || errObj?.message,\n points_required: body.points_required as number | undefined,\n current_points: body.current_points as number | undefined,\n },\n res.status,\n );\n }\n return body;\n }\n\n private async buildFileForm(file: FileInput, filename: string): Promise<FormData> {\n const form = new FormData();\n let blob: Blob;\n if (Buffer.isBuffer(file)) {\n blob = new Blob([file]);\n } else {\n const chunks: Buffer[] = [];\n for await (const chunk of file) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n blob = new Blob([Buffer.concat(chunks)]);\n }\n form.append(\"file\", blob, filename);\n return form;\n }\n\n private normalizeSubmit(body: Record<string, unknown>): SubmitResult {\n const id = String(body.id ?? body.task_id ?? body.legacy_task_id ?? \"\");\n if (!id) {\n throw new KolmoPdfError(\"task_creation_failed\", { message: \"No job id in create response\" });\n }\n const queue = body.queue as { ahead?: number; position?: number } | null | undefined;\n return {\n task_id: id,\n status: normalizeStatus(String(body.status ?? \"queued\")),\n points_deducted: Number(body.points_deducted ?? 0),\n remaining_points: Number(body.remaining_points ?? 0),\n queue_info:\n queue && typeof queue.ahead === \"number\"\n ? { position: queue.position ?? 0, ahead_tasks: queue.ahead }\n : undefined,\n };\n }\n\n async parse(file: FileInput, form: ParseForm, filename: string): Promise<SubmitResult> {\n const fd = await this.buildFileForm(file, filename);\n if (form.table_mode) fd.append(\"table_mode\", form.table_mode);\n if (form.formula_format) fd.append(\"formula_format\", form.formula_format);\n if (form.enable_translation !== undefined)\n fd.append(\"enable_translation\", String(form.enable_translation));\n if (form.target_language) fd.append(\"target_language\", form.target_language);\n if (form.output_options?.length) fd.append(\"output_options\", form.output_options.join(\",\"));\n if (form.images_as_url !== undefined) fd.append(\"images_as_url\", String(form.images_as_url));\n if (form.skip_rotation_detection !== undefined)\n fd.append(\"skip_rotation_detection\", String(form.skip_rotation_detection));\n if (form.enable_cross_page_merge !== undefined)\n fd.append(\"enable_cross_page_merge\", String(form.enable_cross_page_merge));\n if (form.enrichment !== undefined) fd.append(\"enrichment\", form.enrichment);\n\n const body = await this.jsonRequest(`${this.jobsBase}/parse`, {\n method: \"POST\",\n headers: { ...this.headers(), \"Idempotency-Key\": randomUUID() },\n body: fd,\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n return this.normalizeSubmit(body);\n }\n\n async translatePdf(\n file: FileInput,\n form: TranslateForm,\n filename: string,\n ): Promise<SubmitResult> {\n const fd = await this.buildFileForm(file, filename);\n if (form.source_language) fd.append(\"sourceLanguage\", form.source_language);\n if (form.target_language) fd.append(\"targetLanguage\", form.target_language);\n if (form.layout_modes?.length) fd.append(\"layoutModes\", form.layout_modes.join(\",\"));\n if (form.enable_image_translation !== undefined)\n fd.append(\"enableImageTranslation\", String(form.enable_image_translation));\n if (form.enable_table_translation !== undefined)\n fd.append(\"enableTableTranslation\", String(form.enable_table_translation));\n\n const body = await this.jsonRequest(`${this.jobsBase}/translate-pdf`, {\n method: \"POST\",\n headers: { ...this.headers(), \"Idempotency-Key\": randomUUID() },\n body: fd,\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n return this.normalizeSubmit(body);\n }\n\n async convert(file: FileInput, form: ConvertForm, filename: string): Promise<SubmitResult> {\n const fd = await this.buildFileForm(file, filename);\n if (form.target_format) fd.append(\"targetFormat\", form.target_format);\n\n const body = await this.jsonRequest(`${this.jobsBase}/convert`, {\n method: \"POST\",\n headers: { ...this.headers(), \"Idempotency-Key\": randomUUID() },\n body: fd,\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n return this.normalizeSubmit(body);\n }\n\n async getStatus(taskId: string): Promise<StatusResult> {\n const body = await this.jsonRequest(`${this.jobsBase}/${encodeURIComponent(taskId)}`, {\n method: \"GET\",\n headers: this.headers(),\n signal: AbortSignal.timeout(this.httpTimeoutMs),\n });\n\n const status = normalizeStatus(String(body.status ?? \"processing\"));\n const err = body.error as { code?: string; message?: string } | null | undefined;\n const queue = body.queue as { ahead?: number; position?: number } | null | undefined;\n const result = body.result as JobResultMeta | null | undefined;\n\n const ok = status === \"succeeded\" || status === \"completed\";\n return {\n success: ok,\n status,\n message: (body.message as string) || err?.message,\n error_code: err?.code,\n queue_info:\n queue && typeof queue.ahead === \"number\"\n ? { position: queue.position ?? 0, ahead_tasks: queue.ahead }\n : undefined,\n result: result\n ? {\n task_id: taskId,\n download_url: result.download_url,\n filename: result.filename ?? null,\n kind: result.kind ?? null,\n content_type: result.content_type ?? null,\n sha256: result.sha256 ?? null,\n bytes: result.bytes ?? null,\n files: result.files ?? null,\n }\n : undefined,\n };\n }\n\n /** SSE stream for a job. Caller must abort/cancel the response body. */\n async openEvents(taskId: string, signal?: AbortSignal): Promise<Response> {\n const res = await fetch(`${this.jobsBase}/${encodeURIComponent(taskId)}/events`, {\n method: \"GET\",\n headers: {\n ...this.headers(),\n Accept: \"text/event-stream\",\n },\n ...(signal === undefined ? {} : { signal }),\n });\n if (!res.ok) {\n throw new KolmoPdfError(\"api_task_error\", {\n message: `SSE failed with HTTP ${res.status}`,\n httpStatus: res.status,\n });\n }\n return res;\n }\n\n /**\n * Stream download to a Writable, or to destPath (preferred — allows ZIP sniff after write).\n */\n async download(\n taskId: string,\n dest: Writable,\n opts?: { destPath?: string },\n ): Promise<DownloadMeta> {\n const res = await fetch(`${this.jobsBase}/${encodeURIComponent(taskId)}/download`, {\n method: \"GET\",\n headers: this.headers(),\n signal: AbortSignal.timeout(this.uploadTimeoutMs),\n });\n if (!res.ok) {\n throw new KolmoPdfError(\"api_task_error\", {\n message: `Download failed with HTTP ${res.status}`,\n httpStatus: res.status,\n });\n }\n const contentType = res.headers.get(\"content-type\");\n let isZip =\n !!contentType &&\n (contentType.includes(\"zip\") ||\n contentType.includes(\"application/octet-stream\") ||\n contentType.includes(\"application/x-zip\"));\n const body = res.body;\n if (!body) {\n throw new KolmoPdfError(\"api_task_error\", { message: \"Empty download response body\" });\n }\n\n const reader = body.getReader();\n let bytesWritten = 0;\n const firstChunks: Buffer[] = [];\n let sniffed = false;\n\n async function* generate() {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const buf = Buffer.from(value);\n bytesWritten += buf.byteLength;\n if (!sniffed) {\n firstChunks.push(buf);\n const head = Buffer.concat(firstChunks);\n if (head.byteLength >= 4) {\n // ZIP local file header magic \"PK\\x03\\x04\"\n if (\n head[0] === 0x50 &&\n head[1] === 0x4b &&\n (head[2] === 0x03 || head[2] === 0x05 || head[2] === 0x07)\n ) {\n isZip = true;\n } else if (!contentType?.includes(\"zip\")) {\n isZip = false;\n }\n sniffed = true;\n }\n }\n yield buf;\n }\n }\n\n const readable = Readable.from(generate());\n await pipeline(readable, dest);\n return { contentType, isZip, bytesWritten, destPath: opts?.destPath };\n }\n\n async getBalance(): Promise<BalanceResult> {\n const body = await this.jsonRequest(`${this.baseUrl}/api/v1/balance`, {\n method: \"GET\",\n headers: this.headers(),\n signal: AbortSignal.timeout(this.httpTimeoutMs),\n });\n return {\n success: body.success !== false,\n points: Number(body.points ?? 0),\n api_key: String(body.api_key ?? \"\"),\n };\n }\n}\n","/**\n * Unified KolmoPDF error model and MCP error-result formatting.\n *\n * Implements the error-code mapping table in DEVELOPMENT.md §8 and the\n * MCP tool error envelope in §5.13.\n */\n\nexport type ErrorSource = \"api\" | \"client\";\n\nexport interface ErrorSpec {\n /** Default human-readable message. */\n message: string;\n /** Actionable remediation hint surfaced to the LLM / user. */\n remediation: string;\n /** Typical HTTP status; null for client-side codes. */\n httpStatus: number | null;\n source: ErrorSource;\n}\n\n/** Canonical mapping of every error_code we may surface (DEVELOPMENT.md §8). */\nexport const ERROR_SPECS: Record<string, ErrorSpec> = {\n // --- API codes ---\n invalid_api_key: {\n message: \"API key is missing or invalid.\",\n remediation: \"Create a key at https://www.kolmopdf.com/api-keys. Every account, including PAYG, can create one API key.\",\n httpStatus: 401,\n source: \"api\",\n },\n insufficient_points: {\n message: \"Not enough credits.\",\n remediation: \"Buy one-time credits at https://www.kolmopdf.com/credits — no subscription required. Credits are shared with the web account. Check the API key spending limit separately. Never start a purchase without the user's confirmation.\",\n httpStatus: 402,\n source: \"api\",\n },\n points_deduction_failed: {\n message: \"Credit deduction failed.\",\n remediation: \"Check your balance and API key limit. Buy one-time credits at https://www.kolmopdf.com/credits; do not retry a paid operation or purchase automatically.\",\n httpStatus: 402,\n source: \"api\",\n },\n no_file_found: {\n message: \"Request missing file field.\",\n remediation: \"(internal) MCP server bug, please report.\",\n httpStatus: 400,\n source: \"api\",\n },\n parse_file_too_large: {\n message: \"PDF exceeds 300MB.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n parse_page_limit_exceeded: {\n message: \"PDF exceeds 800 pages.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n parse_file_not_pdf: {\n message: \"File is not a valid PDF.\",\n remediation: \"Upload a .pdf file.\",\n httpStatus: 400,\n source: \"api\",\n },\n translate_pdf_file_too_large: {\n message: \"PDF exceeds 300MB.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n translate_pdf_file_not_pdf: {\n message: \"File is not a valid PDF.\",\n remediation: \"Upload a .pdf file.\",\n httpStatus: 400,\n source: \"api\",\n },\n translate_pdf_page_limit_exceeded: {\n message: \"PDF exceeds 800 pages.\",\n remediation: \"Split the PDF locally.\",\n httpStatus: 400,\n source: \"api\",\n },\n convert_file_too_large: {\n message: \"File exceeds 300MB.\",\n remediation: \"Reduce file size.\",\n httpStatus: 400,\n source: \"api\",\n },\n convert_file_type_unsupported: {\n message: \"File must be .md / .markdown / .zip.\",\n remediation: \"Convert source to markdown first.\",\n httpStatus: 400,\n source: \"api\",\n },\n convert_target_format_unsupported: {\n message: \"Target format unsupported.\",\n remediation: \"Use word/docx/html/pdf/latex/tex.\",\n httpStatus: 400,\n source: \"api\",\n },\n file_upload_failed: {\n message: \"Upload to storage failed.\",\n remediation: \"Check network and retry.\",\n httpStatus: 500,\n source: \"api\",\n },\n task_creation_failed: {\n message: \"Task creation failed.\",\n remediation: \"Retry.\",\n httpStatus: 500,\n source: \"api\",\n },\n parse_error: {\n message: \"Parsing failed.\",\n remediation: \"Retry; if it persists, split and try again.\",\n httpStatus: 500,\n source: \"api\",\n },\n parse_file_invalid: {\n message: \"PDF is malformed.\",\n remediation: \"Re-export the PDF.\",\n httpStatus: 500,\n source: \"api\",\n },\n parse_timeout: {\n message: \"Server-side timeout.\",\n remediation: \"Split into smaller PDFs.\",\n httpStatus: 500,\n source: \"api\",\n },\n api_task_error: {\n message: \"Generic task error.\",\n remediation: \"Retry; if it persists contact support.\",\n httpStatus: 500,\n source: \"api\",\n },\n // --- client codes ---\n client_polling_timeout: {\n message: \"Local polling exceeded KOLMOPDF_MAX_POLL_MINUTES.\",\n remediation: \"Task may still be running. Use kolmopdf_get_task_status with task_id.\",\n httpStatus: null,\n source: \"client\",\n },\n client_network_error: {\n message: \"Network error after retries.\",\n remediation: \"Check network.\",\n httpStatus: null,\n source: \"client\",\n },\n client_local_validation: {\n message: \"Local pre-check failed (page count / file size).\",\n remediation: \"See message for the specific limit that was exceeded.\",\n httpStatus: null,\n source: \"client\",\n },\n client_extract_failed: {\n message: \"ZIP extraction failed.\",\n remediation: \"Check disk permissions on output dir.\",\n httpStatus: null,\n source: \"client\",\n },\n} as const;\n\nconst UNKNOWN_SPEC: ErrorSpec = {\n message: \"Unknown error.\",\n remediation: \"Retry; if it persists contact https://www.kolmopdf.com/contact.\",\n httpStatus: null,\n source: \"client\",\n};\n\nexport interface KolmoPdfErrorOptions {\n /** Override the default message from the spec. */\n message?: string;\n /** Override the default HTTP status from the spec. */\n httpStatus?: number | null;\n pointsRequired?: number;\n currentPoints?: number;\n /** Override the default remediation hint. */\n remediation?: string;\n}\n\n/** Structured error thrown across the MCP server; carries a stable error_code. */\nexport class KolmoPdfError extends Error {\n readonly errorCode: string;\n readonly httpStatus: number | null;\n readonly remediation: string;\n readonly pointsRequired: number | undefined;\n readonly currentPoints: number | undefined;\n readonly source: ErrorSource;\n\n constructor(errorCode: string, opts: KolmoPdfErrorOptions = {}) {\n const spec = ERROR_SPECS[errorCode] ?? UNKNOWN_SPEC;\n super(opts.message ?? spec.message);\n this.name = \"KolmoPdfError\";\n this.errorCode = errorCode;\n this.httpStatus = opts.httpStatus !== undefined ? opts.httpStatus : spec.httpStatus;\n this.remediation = opts.remediation ?? spec.remediation;\n this.pointsRequired = opts.pointsRequired;\n this.currentPoints = opts.currentPoints;\n this.source = spec.source;\n }\n}\n\n/** Shape of the JSON payload embedded in an MCP error result (DEVELOPMENT.md §5.13). */\nexport interface McpErrorPayload {\n error_code: string;\n message: string;\n http_status: number | null;\n points_required?: number;\n current_points?: number;\n remediation: string;\n}\n\n/** MCP tool result envelope for an error (matches MCP SDK `CallToolResult`). */\nexport interface McpErrorResult {\n isError: true;\n content: Array<{ type: \"text\"; text: string }>;\n}\n\n/** Convert a KolmoPdfError (or any error) into the MCP error result envelope. */\nexport function toMcpErrorResult(err: unknown): McpErrorResult {\n const kerr =\n err instanceof KolmoPdfError\n ? err\n : new KolmoPdfError(\"api_task_error\", {\n message: err instanceof Error ? err.message : String(err),\n });\n\n const payload: McpErrorPayload = {\n error_code: kerr.errorCode,\n message: kerr.message,\n http_status: kerr.httpStatus,\n remediation: kerr.remediation,\n };\n if (kerr.pointsRequired !== undefined) payload.points_required = kerr.pointsRequired;\n if (kerr.currentPoints !== undefined) payload.current_points = kerr.currentPoints;\n\n return {\n isError: true,\n content: [{ type: \"text\", text: JSON.stringify(payload) }],\n };\n}\n\n/** Whether an API failure with this code is auto-refunded server-side (§8). */\nexport function isAutoRefunded(errorCode: string): boolean {\n return (\n errorCode === \"task_creation_failed\" ||\n errorCode === \"parse_error\" ||\n errorCode === \"parse_file_invalid\" ||\n errorCode === \"parse_timeout\"\n );\n}\n\n/** Map a raw API JSON failure body to a KolmoPdfError. */\nexport function errorFromApiBody(\n body: {\n error_code?: string;\n message?: string;\n points_required?: number;\n current_points?: number;\n },\n httpStatus?: number,\n): KolmoPdfError {\n const code = body.error_code ?? \"api_task_error\";\n return new KolmoPdfError(code, {\n message: body.message,\n httpStatus: httpStatus ?? null,\n pointsRequired: body.points_required,\n currentPoints: body.current_points,\n });\n}\n","/**\n * Environment-variable loader for the KolmoPDF MCP server.\n *\n * Per DEVELOPMENT.md §4 and §5.2, the API key is NOT validated at startup —\n * it is read lazily so the server can boot in offline / no-key environments.\n * The first authenticated tool call surfaces a missing key as an MCP error.\n */\n\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\n\nexport interface KolmoPdfConfig {\n /** Resolved at call time; may be undefined until the user sets it. */\n apiKey: string | undefined;\n baseUrl: string;\n outputDir: string;\n pollIntervalMs: number;\n maxPollMinutes: number;\n httpTimeoutMs: number;\n uploadTimeoutMs: number;\n}\n\nconst DEFAULTS = {\n baseUrl: \"https://www.kolmopdf.com\",\n outputDir: resolve(homedir(), \"kolmopdf-output\"),\n pollIntervalMs: 2000,\n maxPollMinutes: 30,\n httpTimeoutMs: 60_000,\n uploadTimeoutMs: 600_000,\n} as const;\n\nfunction intFromEnv(value: string | undefined, fallback: number): number {\n if (value === undefined || value.trim() === \"\") return fallback;\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/** Treat an empty or unexpanded plugin placeholder as a missing API key. */\nexport function normalizeApiKey(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed || /^\\$\\{KOLMOPDF_API_KEY(?::-[^}]*)?\\}$/.test(trimmed)) return undefined;\n return trimmed;\n}\n\n/**\n * Build a config object from the current process environment.\n * Reads on every call so live env changes are picked up.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): KolmoPdfConfig {\n return {\n apiKey: normalizeApiKey(env.KOLMOPDF_API_KEY),\n baseUrl: trimTrailingSlash(env.KOLMOPDF_BASE_URL?.trim() || DEFAULTS.baseUrl),\n outputDir: resolve(env.KOLMOPDF_OUTPUT_DIR?.trim() || DEFAULTS.outputDir),\n pollIntervalMs: intFromEnv(env.KOLMOPDF_POLL_INTERVAL_MS, DEFAULTS.pollIntervalMs),\n maxPollMinutes: intFromEnv(env.KOLMOPDF_MAX_POLL_MINUTES, DEFAULTS.maxPollMinutes),\n httpTimeoutMs: intFromEnv(env.KOLMOPDF_HTTP_TIMEOUT_MS, DEFAULTS.httpTimeoutMs),\n uploadTimeoutMs: intFromEnv(env.KOLMOPDF_UPLOAD_TIMEOUT_MS, DEFAULTS.uploadTimeoutMs),\n };\n}\n\n/** Mask an API key for display: first 6 + \"***\" + last 4 (DEVELOPMENT.md §5.8). */\nexport function maskApiKey(apiKey: string): string {\n if (apiKey.length <= 10) return \"***\";\n return `${apiKey.slice(0, 6)}***${apiKey.slice(-4)}`;\n}\n\nexport const configDefaults = DEFAULTS;\n","/**\n * Shared per-call context handed to every tool handler.\n */\nimport type { KolmoPdfClient } from \"./client.js\";\nimport type { KolmoPdfConfig } from \"./config.js\";\nimport type { ProgressReporter } from \"./progress.js\";\n\nexport interface ToolContext {\n config: KolmoPdfConfig;\n /** Lazily constructed; throws invalid_api_key when the key is absent. */\n getClient(): KolmoPdfClient;\n progress?: ProgressReporter;\n}\n\n/** Standard MCP success result envelope (subset of the SDK `CallToolResult`). */\nexport interface McpSuccessResult {\n content: Array<{ type: \"text\"; text: string }>;\n structuredContent?: Record<string, unknown>;\n}\n\n/** Wrap a JSON-serializable object into the MCP success envelope. */\nexport function jsonResult(data: Record<string, unknown>): McpSuccessResult {\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n structuredContent: data,\n };\n}\n","import { z } from \"zod\";\nimport { maskApiKey } from \"../config.js\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\n\nexport const checkBalanceName = \"kolmopdf_check_balance\";\n\nexport const checkBalanceDescription =\n \"Show the current KolmoPDF credit balance for the configured API key.\";\n\nexport const checkBalanceInputSchema = z.object({});\n\nexport type CheckBalanceInput = z.infer<typeof checkBalanceInputSchema>;\n\nexport interface CheckBalanceOutput {\n points: number;\n api_key_masked: string;\n}\n\nexport async function checkBalanceHandler(\n _args: CheckBalanceInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const balance = await client.getBalance();\n\n const output: CheckBalanceOutput = {\n points: balance.points,\n api_key_masked: maskApiKey(ctx.config.apiKey || \"\"),\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { createWriteStream, mkdirSync } from \"node:fs\";\nimport { readFile, rename } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { KolmoPdfError } from \"../errors.js\";\nimport { resolveOutputRoot } from \"../output.js\";\nimport { MAX_FILE_BYTES, readFileSize } from \"../pages.js\";\nimport { pollUntilComplete } from \"../polling.js\";\nimport { type SniffKind, extensionForKind, sniffFile } from \"../sniff.js\";\n\nexport const convertName = \"kolmopdf_convert_markdown\";\n\nexport const convertDescription =\n \"Convert a Markdown file (or a ZIP of markdown + images) to DOCX, HTML, PDF, \" +\n \"or LaTeX via KolmoPDF.\";\n\nexport const convertInputSchema = z.object({\n file_path: z\n .string()\n .describe(\"Path to a .md/.markdown file or .zip containing markdown + images.\"),\n target_format: z.enum([\"word\", \"docx\", \"html\", \"pdf\", \"latex\", \"tex\"]).optional().default(\"word\"),\n output_subdir: z.string().optional(),\n});\n\nexport type ConvertInput = z.infer<typeof convertInputSchema>;\n\nexport interface ConvertOutput {\n task_id: string;\n points_deducted: number;\n remaining_points: number;\n output: {\n output_path: string;\n target_format: string;\n kind: string;\n };\n}\n\nexport function formatToExtension(targetFormat: string): string {\n switch (targetFormat) {\n case \"word\":\n case \"docx\":\n return \".docx\";\n case \"html\":\n return \".html\";\n case \"pdf\":\n return \".pdf\";\n case \"latex\":\n case \"tex\":\n return \".tex\";\n default:\n return \".out\";\n }\n}\n\nexport function normalizeFormat(targetFormat: string): string {\n switch (targetFormat) {\n case \"word\":\n case \"docx\":\n return \"docx\";\n case \"latex\":\n case \"tex\":\n return \"tex\";\n default:\n return targetFormat;\n }\n}\n\nexport function resolveConvertKind(sniffedKind: SniffKind, targetFormat: string): SniffKind {\n return normalizeFormat(targetFormat) === \"docx\" && sniffedKind === \"zip\" ? \"docx\" : sniffedKind;\n}\n\nexport async function convertHandler(\n args: ConvertInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const filePath = resolve(args.file_path);\n const filename = basename(filePath);\n\n const fileSize = await readFileSize(filePath);\n if (fileSize > MAX_FILE_BYTES) {\n throw new KolmoPdfError(\"convert_file_too_large\");\n }\n\n const ext = filePath.toLowerCase();\n if (!ext.endsWith(\".md\") && !ext.endsWith(\".markdown\") && !ext.endsWith(\".zip\")) {\n throw new KolmoPdfError(\"convert_file_type_unsupported\");\n }\n\n await ctx.progress?.report(\"[uploading] Sending file for conversion...\");\n\n const fileBuffer = await readFile(filePath);\n const submitResult = await client.convert(\n fileBuffer,\n {\n target_format: args.target_format,\n },\n filename,\n );\n\n const taskId = submitResult.task_id;\n await ctx.progress?.report(`[submitted] Task ${taskId} created`);\n\n await pollUntilComplete({\n client,\n taskId,\n options: {\n pollIntervalMs: ctx.config.pollIntervalMs,\n maxPollMinutes: ctx.config.maxPollMinutes,\n },\n progress: ctx.progress,\n });\n\n await ctx.progress?.report(\"[downloading] Fetching converted file...\");\n\n const subdir = args.output_subdir || taskId;\n const outputRoot = resolveOutputRoot(ctx.config.outputDir, subdir);\n mkdirSync(outputRoot, { recursive: true });\n\n const tempPath = join(outputRoot, \"download.bin\");\n const ws = createWriteStream(tempPath);\n await client.download(taskId, ws, { destPath: tempPath });\n const sniffedKind = await sniffFile(tempPath);\n const kind = resolveConvertKind(sniffedKind, args.target_format);\n const outputPath = join(outputRoot, `result${extensionForKind(kind)}`);\n await rename(tempPath, outputPath);\n\n const output: ConvertOutput = {\n task_id: taskId,\n points_deducted: submitResult.points_deducted,\n remaining_points: submitResult.remaining_points,\n output: {\n output_path: outputPath,\n target_format: normalizeFormat(args.target_format),\n kind,\n },\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { isAbsolute, relative, resolve } from \"node:path\";\nimport { KolmoPdfError } from \"./errors.js\";\n\n/** Resolve an output subdirectory without allowing it to escape the configured root. */\nexport function resolveOutputRoot(baseDir: string, subdir: string): string {\n const root = resolve(baseDir);\n const candidate = resolve(root, subdir);\n const rel = relative(root, candidate);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new KolmoPdfError(\"client_local_validation\", {\n message: \"output_subdir must stay inside KOLMOPDF_OUTPUT_DIR.\",\n });\n }\n return candidate;\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { PDFDocument } from \"pdf-lib\";\n\nexport const MAX_PAGES = 800;\nexport const MAX_FILE_BYTES = 300 * 1024 * 1024;\n\nexport async function readPageCount(filePath: string): Promise<number> {\n const data = await readFile(filePath);\n const doc = await PDFDocument.load(data, { ignoreEncryption: true });\n return doc.getPageCount();\n}\n\nexport async function readFileSize(filePath: string): Promise<number> {\n const s = await stat(filePath);\n return s.size;\n}\n","/**\n * MCP progress-notification helper (DEVELOPMENT.md §5.11).\n *\n * `progress` must be monotonically increasing; `total` is omitted because the\n * API does not provide a precise percentage.\n */\n\n/** Minimal shape of the MCP request context used to emit notifications. */\nexport interface ProgressSink {\n /** Present only when the client supplied a progressToken in request `_meta`. */\n progressToken?: string | number;\n notify(notification: {\n method: \"notifications/progress\";\n params: {\n progressToken: string | number;\n progress: number;\n message?: string;\n };\n }): Promise<void>;\n}\n\n/** Stateful emitter that guarantees a monotonically increasing counter. */\nexport class ProgressReporter {\n private counter = 0;\n\n constructor(private readonly sink: ProgressSink | undefined) {}\n\n async report(message: string): Promise<void> {\n if (!this.sink || this.sink.progressToken === undefined) return;\n this.counter += 1;\n await this.sink.notify({\n method: \"notifications/progress\",\n params: {\n progressToken: this.sink.progressToken,\n progress: this.counter,\n message,\n },\n });\n }\n}\n\n/** Build a human-readable status line, e.g. \"[waiting] 3 tasks ahead\". */\nexport function humanizeStatus(status: string, aheadTasks?: number): string {\n if (status === \"waiting\" && typeof aheadTasks === \"number\") {\n return `[waiting] ${aheadTasks} tasks ahead`;\n }\n return `[${status}]`;\n}\n","import type { KolmoPdfClient, StatusResult } from \"./client.js\";\nimport { KolmoPdfError } from \"./errors.js\";\nimport { type ProgressReporter, humanizeStatus } from \"./progress.js\";\n\nexport interface PollOptions {\n pollIntervalMs: number;\n maxPollMinutes: number;\n signal?: AbortSignal;\n}\n\n/** v1 success; legacy `completed` still accepted */\nexport const TERMINAL_OK = new Set([\"succeeded\", \"completed\"]);\nexport const TERMINAL_FAIL = new Set([\"failed\", \"cancelled\"]);\nexport const IN_FLIGHT_STATUSES = new Set([\"queued\", \"pending\", \"waiting\", \"processing\"]);\n\nexport const RETRY_POLICY = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n factor: 2,\n} as const;\n\nexport function backoffDelayMs(attempt: number): number {\n return RETRY_POLICY.baseDelayMs * RETRY_POLICY.factor ** (attempt - 1);\n}\n\nexport function isRetryable(err: { httpStatus?: number | null; code?: string }): boolean {\n const transientCodes = [\"ECONNRESET\", \"ETIMEDOUT\", \"ECONNREFUSED\", \"EAI_AGAIN\"];\n if (err.code && transientCodes.includes(err.code)) return true;\n if (typeof err.httpStatus === \"number\" && err.httpStatus >= 500) return true;\n return false;\n}\n\nexport interface PollContext {\n client: KolmoPdfClient;\n taskId: string;\n options: PollOptions;\n progress?: ProgressReporter;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function fetchStatusWithRetry(client: KolmoPdfClient, taskId: string): Promise<StatusResult> {\n for (let attempt = 1; attempt <= RETRY_POLICY.maxAttempts; attempt++) {\n try {\n return await client.getStatus(taskId);\n } catch (err) {\n const retryable =\n err instanceof KolmoPdfError\n ? isRetryable(err)\n : isRetryable({ code: (err as NodeJS.ErrnoException).code });\n if (!retryable || attempt === RETRY_POLICY.maxAttempts) throw err;\n await sleep(backoffDelayMs(attempt));\n }\n }\n throw new KolmoPdfError(\"client_network_error\");\n}\n\nfunction nextSseFrame(buf: string): { frame: string; rest: string } | null {\n const lf = buf.indexOf(\"\\n\\n\");\n const crlf = buf.indexOf(\"\\r\\n\\r\\n\");\n if (lf < 0 && crlf < 0) return null;\n if (crlf >= 0 && (lf < 0 || crlf < lf)) {\n return { frame: buf.slice(0, crlf), rest: buf.slice(crlf + 4) };\n }\n return { frame: buf.slice(0, lf), rest: buf.slice(lf + 2) };\n}\n\nfunction eventNameFromFrame(raw: string): string {\n let eventName = \"message\";\n for (const line of raw.split(/\\r?\\n/)) {\n if (line.startsWith(\"event:\")) eventName = line.slice(6).trim();\n }\n return eventName;\n}\n\nasync function waitViaSse(ctx: PollContext, deadline: number): Promise<StatusResult | null> {\n const { client, taskId, progress, options } = ctx;\n const remaining = Math.max(1_000, deadline - Date.now());\n const timeout = AbortSignal.timeout(remaining);\n const parent = options.signal;\n const combined = parent === undefined ? timeout : AbortSignal.any([parent, timeout]);\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n try {\n const res = await client.openEvents(taskId, combined);\n const body = res.body;\n if (!body) return null;\n reader = body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n\n const handleEvent = async (eventName: string): Promise<StatusResult | \"continue\"> => {\n if (eventName === \"job.succeeded\") {\n const status = await fetchStatusWithRetry(client, taskId);\n if (TERMINAL_OK.has(String(status.status || \"\"))) {\n await progress?.report(`[completed] Task ${taskId} done`);\n return status;\n }\n return \"continue\";\n }\n if (eventName === \"job.failed\" || eventName === \"job.cancelled\") {\n const failed = await fetchStatusWithRetry(client, taskId);\n throw new KolmoPdfError(failed.error_code || eventName.slice(\"job.\".length), {\n message: failed.message || \"Task failed\",\n });\n }\n if (eventName === \"job.progress\" || eventName === \"job.snapshot\") {\n await progress?.report(humanizeStatus(\"processing\"));\n }\n return \"continue\";\n };\n\n while (!timeout.aborted) {\n if (parent?.aborted === true) throw new KolmoPdfError(\"client_polling_timeout\");\n const { done, value } = await reader.read();\n if (done) {\n buf += decoder.decode();\n const last = nextSseFrame(`${buf}\\n\\n`);\n if (last) {\n const result = await handleEvent(eventNameFromFrame(last.frame));\n if (result !== \"continue\") return result;\n }\n break;\n }\n buf += decoder.decode(value, { stream: true });\n let next = nextSseFrame(buf);\n while (next) {\n buf = next.rest;\n const result = await handleEvent(eventNameFromFrame(next.frame));\n if (result !== \"continue\") return result;\n next = nextSseFrame(buf);\n }\n }\n return null;\n } catch (err) {\n if (err instanceof KolmoPdfError) {\n const code = err.errorCode;\n if (\n code !== \"api_task_error\" &&\n code !== \"client_network_error\" &&\n code !== \"client_polling_timeout\"\n ) {\n throw err;\n }\n }\n return null;\n } finally {\n try {\n await reader?.cancel();\n } catch {\n /* ignore */\n }\n }\n}\n\nexport async function pollUntilComplete(ctx: PollContext): Promise<StatusResult> {\n const { client, taskId, options, progress } = ctx;\n const deadline = Date.now() + options.maxPollMinutes * 60_000;\n\n const viaSse = await waitViaSse(ctx, deadline);\n if (viaSse && TERMINAL_OK.has(String(viaSse.status || \"\"))) return viaSse;\n if (viaSse && TERMINAL_FAIL.has(String(viaSse.status || \"\"))) {\n throw new KolmoPdfError(viaSse.error_code || \"api_task_error\", {\n message: viaSse.message || \"Task failed\",\n });\n }\n\n while (true) {\n if (options.signal?.aborted === true) {\n throw new KolmoPdfError(\"client_polling_timeout\");\n }\n if (Date.now() > deadline) {\n throw new KolmoPdfError(\"client_polling_timeout\");\n }\n\n const result = await fetchStatusWithRetry(client, taskId);\n const status = String(result.status || \"\");\n\n if (TERMINAL_OK.has(status)) {\n await progress?.report(`[completed] Task ${taskId} done`);\n return result;\n }\n\n if (TERMINAL_FAIL.has(status)) {\n throw new KolmoPdfError(result.error_code || \"api_task_error\", {\n message: result.message || \"Task failed\",\n });\n }\n\n const aheadTasks = result.queue_info?.ahead_tasks;\n await progress?.report(humanizeStatus(result.status as string, aheadTasks));\n await sleep(options.pollIntervalMs);\n }\n}\n","import { open, rename } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport type SniffKind = \"zip\" | \"pdf\" | \"markdown\" | \"docx\" | \"html\" | \"latex\" | \"binary\";\n\nconst EXT: Record<SniffKind, string> = {\n zip: \".zip\",\n pdf: \".pdf\",\n markdown: \".md\",\n docx: \".docx\",\n html: \".html\",\n latex: \".tex\",\n binary: \".bin\",\n};\n\nexport function extensionForKind(kind: SniffKind): string {\n return EXT[kind];\n}\n\nexport function sniffBytes(buf: Uint8Array): SniffKind {\n if (\n buf.length >= 4 &&\n buf[0] === 0x50 &&\n buf[1] === 0x4b &&\n (buf[2] === 0x03 || buf[2] === 0x05 || buf[2] === 0x07)\n ) {\n const hay = Buffer.from(buf.subarray(0, Math.min(buf.length, 65536))).toString(\"latin1\");\n if (\n hay.includes(\"word/document.xml\") ||\n hay.includes(\"wordprocessingml.document\") ||\n (hay.includes(\"[Content_Types].xml\") && hay.toLowerCase().includes(\"word/\"))\n ) {\n return \"docx\";\n }\n return \"zip\";\n }\n if (buf.length >= 4 && buf[0] === 0x25 && buf[1] === 0x50 && buf[2] === 0x44 && buf[3] === 0x46) {\n return \"pdf\";\n }\n const head = Buffer.from(buf.subarray(0, Math.min(buf.length, 800))).toString(\"utf8\");\n const trimmed = head.trimStart().toLowerCase();\n if (trimmed.startsWith(\"<!doctype html\") || trimmed.startsWith(\"<html\")) return \"html\";\n if (trimmed.startsWith(\"\\\\documentclass\") || trimmed.startsWith(\"\\\\begin{document}\"))\n return \"latex\";\n if (head.trimStart().startsWith(\"#\") || head.includes(\"\\n# \") || head.includes(\"\\n```\"))\n return \"markdown\";\n return \"binary\";\n}\n\nexport async function sniffFile(filePath: string): Promise<SniffKind> {\n const handle = await open(filePath, \"r\");\n try {\n const bytes = Buffer.alloc(65536);\n const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);\n return sniffBytes(bytes.subarray(0, bytesRead));\n } finally {\n await handle.close();\n }\n}\n\nexport async function renameBySniff(\n tempPath: string,\n destDir: string,\n stem: string,\n): Promise<{\n path: string;\n kind: SniffKind;\n}> {\n const kind = await sniffFile(tempPath);\n const path = join(destDir, `${stem}${EXT[kind]}`);\n if (path !== tempPath) {\n await rename(tempPath, path);\n }\n return { path, kind };\n}\n\nexport function replaceExt(filename: string, ext: string): string {\n const e = ext.startsWith(\".\") ? ext : `.${ext}`;\n const base = filename.replace(/\\.[^.]+$/, \"\") || \"result\";\n return `${base}${e}`;\n}\n","import { resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { readPageCount } from \"../pages.js\";\n\nexport const estimateCostName = \"kolmopdf_estimate_cost\";\n\nexport const estimateCostDescription =\n \"Estimate the credit cost of a KolmoPDF operation before running it. \" +\n \"Reads page count locally and checks the current balance. Does not spend credits.\";\n\nexport const estimateCostInputSchema = z.object({\n file_path: z.string(),\n operation: z.enum([\"parse\", \"parse_translate\", \"translate\", \"convert\"]),\n options: z\n .object({\n images_as_url: z.boolean().optional(),\n })\n .optional(),\n});\n\nexport type EstimateCostInput = z.infer<typeof estimateCostInputSchema>;\n\nexport type Operation = EstimateCostInput[\"operation\"];\n\nexport interface EstimateCostOutput {\n pages: number | null;\n estimated_credits: number;\n current_balance: number;\n sufficient: boolean;\n shortfall: number;\n recommendation: string;\n}\n\nexport function estimateCredits(operation: Operation, pages: number): number {\n switch (operation) {\n case \"parse\":\n return pages * 2;\n case \"parse_translate\":\n return pages * 3;\n case \"translate\":\n return pages * 2;\n case \"convert\":\n return 1;\n }\n}\n\nexport function buildRecommendation(shortfall: number): string {\n return shortfall > 0\n ? `Need top-up at https://www.kolmopdf.com/subscription (short by ${shortfall} credits).`\n : \"Sufficient\";\n}\n\nexport async function estimateCostHandler(\n args: EstimateCostInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n\n let pages: number | null = null;\n if (args.operation !== \"convert\") {\n const filePath = resolve(args.file_path);\n pages = await readPageCount(filePath);\n }\n\n const estimatedCredits = estimateCredits(args.operation, pages ?? 1);\n const balance = await client.getBalance();\n const currentBalance = balance.points;\n const shortfall = Math.max(0, estimatedCredits - currentBalance);\n\n const output: EstimateCostOutput = {\n pages,\n estimated_credits: estimatedCredits,\n current_balance: currentBalance,\n sufficient: shortfall === 0,\n shortfall,\n recommendation: buildRecommendation(shortfall),\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\n\nexport const getTaskStatusName = \"kolmopdf_get_task_status\";\n\nexport const getTaskStatusDescription =\n \"Advanced/debug tool. Returns raw status for a KolmoPDF task. Use only when \" +\n \"explicitly asked to inspect a task by ID, or when troubleshooting a stuck task.\";\n\nexport const getTaskStatusInputSchema = z.object({\n task_id: z.string(),\n});\n\nexport type GetTaskStatusInput = z.infer<typeof getTaskStatusInputSchema>;\n\nexport async function getTaskStatusHandler(\n args: GetTaskStatusInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const status = await client.getStatus(args.task_id);\n return jsonResult(status as unknown as Record<string, unknown>);\n}\n","import { createWriteStream, mkdirSync } from \"node:fs\";\nimport { readFile, rename } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { KolmoPdfError } from \"../errors.js\";\nimport { extractZip } from \"../extract.js\";\nimport { resolveOutputRoot } from \"../output.js\";\nimport { MAX_FILE_BYTES, MAX_PAGES, readFileSize, readPageCount } from \"../pages.js\";\nimport { pollUntilComplete } from \"../polling.js\";\nimport { sniffFile } from \"../sniff.js\";\n\nexport const parsePdfName = \"kolmopdf_parse_pdf\";\n\nexport const parsePdfDescription =\n \"Parse a local PDF into Markdown via KolmoPDF. Handles formulas, tables, \" +\n \"multi-column layouts, and code blocks. Optionally translates while parsing. \" +\n \"Server may attach outline.md/summary.md sidecars (ZIP download).\";\n\nexport const parsePdfInputSchema = z.object({\n file_path: z.string().describe(\"Absolute or cwd-relative path to a local PDF file.\"),\n table_mode: z.enum([\"markdown\", \"image\"]).optional(),\n formula_format: z.enum([\"dollar\", \"bracket\"]).optional(),\n enable_translation: z.boolean().optional(),\n target_language: z.enum([\"zh\", \"en\", \"ja\", \"ko\", \"fr\", \"de\", \"es\", \"ru\"]).optional(),\n output_options: z.array(z.enum([\"original\", \"translated\", \"bilingual\"])).optional(),\n images_as_url: z.boolean().optional(),\n skip_rotation_detection: z.boolean().optional(),\n enable_cross_page_merge: z.boolean().optional(),\n enrichment: z\n .string()\n .optional()\n .describe(\n \"Parse-time AI sidecars. Omit for server default outline,summary. Use 'none' to disable. Examples: outline,summary,verification\",\n ),\n output_subdir: z\n .string()\n .optional()\n .describe(\"Subdirectory name under KOLMOPDF_OUTPUT_DIR. Defaults to <task_id>.\"),\n});\n\nexport type ParsePdfInput = z.infer<typeof parsePdfInputSchema>;\n\nexport interface ParsePdfOutput {\n task_id: string;\n pages_parsed: number;\n points_deducted: number;\n remaining_points: number;\n output: {\n type: \"zip_extracted\" | \"markdown_file\";\n markdown_path: string;\n images_dir: string | null;\n output_root: string;\n };\n preview: string;\n}\n\nexport async function parsePdfHandler(\n args: ParsePdfInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const filePath = resolve(args.file_path);\n const filename = basename(filePath);\n\n const fileSize = await readFileSize(filePath);\n if (fileSize > MAX_FILE_BYTES) {\n throw new KolmoPdfError(\"parse_file_too_large\");\n }\n\n const pageCount = await readPageCount(filePath);\n if (pageCount > MAX_PAGES) {\n throw new KolmoPdfError(\"parse_page_limit_exceeded\");\n }\n\n await ctx.progress?.report(\"[uploading] Sending PDF to KolmoPDF...\");\n\n const fileBuffer = await readFile(filePath);\n const submitResult = await client.parse(\n fileBuffer,\n {\n table_mode: args.table_mode,\n formula_format: args.formula_format,\n enable_translation: args.enable_translation,\n target_language: args.target_language,\n output_options: args.output_options,\n images_as_url: args.images_as_url,\n skip_rotation_detection: args.skip_rotation_detection,\n enable_cross_page_merge: args.enable_cross_page_merge,\n enrichment: args.enrichment,\n },\n filename,\n );\n\n const taskId = submitResult.task_id;\n await ctx.progress?.report(`[submitted] Task ${taskId} created`);\n\n await pollUntilComplete({\n client,\n taskId,\n options: {\n pollIntervalMs: ctx.config.pollIntervalMs,\n maxPollMinutes: ctx.config.maxPollMinutes,\n },\n progress: ctx.progress,\n });\n\n await ctx.progress?.report(\"[downloading] Fetching result...\");\n\n const subdir = args.output_subdir || taskId;\n const outputRoot = resolveOutputRoot(ctx.config.outputDir, subdir);\n mkdirSync(outputRoot, { recursive: true });\n\n // Always download to a temp name first — server may return ZIP even when images_as_url\n // (enrichment sidecars force a multi-file bundle). Trust magic bytes, not Content-Type.\n const downloadPath = join(outputRoot, \"download.bin\");\n const ws = createWriteStream(downloadPath);\n await client.download(taskId, ws, { destPath: downloadPath });\n\n let markdownPath: string;\n let imagesDir: string | null = null;\n let outputType: \"zip_extracted\" | \"markdown_file\";\n\n const kind = await sniffFile(downloadPath);\n if (kind === \"zip\") {\n const zipPath = join(outputRoot, \"result.zip\");\n await rename(downloadPath, zipPath);\n const extracted = await extractZip(zipPath, outputRoot);\n markdownPath = extracted.markdownPath || join(outputRoot, \"result.md\");\n imagesDir = extracted.imagesDir;\n outputType = \"zip_extracted\";\n } else {\n markdownPath = join(outputRoot, \"result.md\");\n await rename(downloadPath, markdownPath);\n outputType = \"markdown_file\";\n }\n\n const mdContent = await readFile(markdownPath, \"utf-8\").catch(() => \"\");\n const preview = mdContent.slice(0, 500);\n\n const ptsPerPage = args.enable_translation ? 3 : 2;\n const pagesParsed = Math.round(submitResult.points_deducted / ptsPerPage);\n\n const output: ParsePdfOutput = {\n task_id: taskId,\n pages_parsed: pagesParsed,\n points_deducted: submitResult.points_deducted,\n remaining_points: submitResult.remaining_points,\n output: {\n type: outputType,\n markdown_path: markdownPath,\n images_dir: imagesDir,\n output_root: outputRoot,\n },\n preview,\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n","import { createWriteStream, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { type Entry, type ZipFile, open as yauzlOpen } from \"yauzl\";\nimport { KolmoPdfError } from \"./errors.js\";\n\nexport interface ExtractResult {\n markdownPath: string | null;\n imagesDir: string | null;\n outputRoot: string;\n files: string[];\n}\n\n/** Prefer primary document MD over enrichment sidecars (outline/summary/etc.). */\nexport function pickPrimaryMarkdownPath(\n candidates: Array<{ path: string; entryName: string; size: number }>,\n): string | null {\n if (candidates.length === 0) return null;\n const scored = candidates.map((c) => {\n const base = (c.entryName.split(\"/\").pop() || c.entryName).toLowerCase();\n let score = c.size;\n if (\n /^(outline|summary|verification_report|enrichment_meta|tables_changelog|tables_normalized)(\\.|$)/i.test(\n base,\n ) ||\n /outline|summary|verification|enrichment|tables_/.test(base)\n ) {\n score -= 1e12;\n }\n if (base === \"readme.md\") score -= 1e9;\n if (/translated|bilingual/.test(base)) score -= 1e6;\n return { path: c.path, score };\n });\n scored.sort((a, b) => b.score - a.score);\n return scored[0]?.path ?? null;\n}\n\nexport function safeZipEntryPath(destDir: string, entryName: string): string {\n const normalized = entryName.replace(/\\\\/g, \"/\");\n const segments = normalized.split(\"/\").filter((segment) => segment && segment !== \".\");\n if (\n normalized.includes(\"\\0\") ||\n normalized.startsWith(\"/\") ||\n /^[A-Za-z]:/.test(normalized) ||\n segments.includes(\"..\")\n ) {\n throw new KolmoPdfError(\"client_extract_failed\", {\n message: `Unsafe ZIP entry path: ${entryName}`,\n });\n }\n\n const root = resolve(destDir);\n const entryPath = resolve(root, ...segments);\n const rel = relative(root, entryPath);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new KolmoPdfError(\"client_extract_failed\", {\n message: `Unsafe ZIP entry path: ${entryName}`,\n });\n }\n return entryPath;\n}\n\nexport async function extractZip(zipPath: string, destDir: string): Promise<ExtractResult> {\n mkdirSync(destDir, { recursive: true });\n\n const zipFile = await openZip(zipPath);\n const files: string[] = [];\n const mdCandidates: Array<{ path: string; entryName: string; size: number }> = [];\n let imagesDir: string | null = null;\n\n for await (const entry of iterEntries(zipFile)) {\n const entryPath = safeZipEntryPath(destDir, entry.fileName);\n\n if (entry.fileName.endsWith(\"/\")) {\n mkdirSync(entryPath, { recursive: true });\n if (entry.fileName.includes(\"images\")) {\n imagesDir = entryPath;\n }\n continue;\n }\n\n mkdirSync(dirname(entryPath), { recursive: true });\n const readStream = await openReadStream(zipFile, entry);\n const writeStream = createWriteStream(entryPath);\n await pipeline(readStream, writeStream);\n files.push(entryPath);\n\n if (/\\.md$/i.test(entry.fileName)) {\n let size = entry.uncompressedSize || 0;\n try {\n size = readFileSync(entryPath).byteLength;\n } catch {\n /* keep zip header size */\n }\n mdCandidates.push({ path: entryPath, entryName: entry.fileName, size });\n }\n if (!imagesDir && /images\\//i.test(entry.fileName)) {\n const prefix = entry.fileName.split(\"images/\")[0] ?? \"\";\n imagesDir = join(destDir, prefix, \"images\");\n }\n }\n\n const markdownPath = pickPrimaryMarkdownPath(mdCandidates);\n\n return { markdownPath, imagesDir, outputRoot: destDir, files };\n}\n\nfunction openZip(path: string): Promise<ZipFile> {\n return new Promise((resolve, reject) => {\n yauzlOpen(path, { lazyEntries: true }, (err, zf) => {\n if (err || !zf) return reject(err ?? new Error(\"Failed to open zip\"));\n resolve(zf);\n });\n });\n}\n\nasync function* iterEntries(zipFile: ZipFile): AsyncGenerator<Entry> {\n let resolve: ((entry: Entry | null) => void) | null = null;\n const queue: (Entry | null)[] = [];\n\n zipFile.on(\"entry\", (entry: Entry) => {\n if (resolve) {\n const r = resolve;\n resolve = null;\n r(entry);\n } else {\n queue.push(entry);\n }\n });\n zipFile.on(\"end\", () => {\n if (resolve) {\n const r = resolve;\n resolve = null;\n r(null);\n } else {\n queue.push(null);\n }\n });\n\n zipFile.readEntry();\n while (true) {\n const entry =\n queue.length > 0\n ? (queue.shift() as Entry | null)\n : await new Promise<Entry | null>((r) => {\n resolve = r;\n });\n if (entry === null) break;\n yield entry;\n zipFile.readEntry();\n }\n}\n\nfunction openReadStream(zipFile: ZipFile, entry: Entry): Promise<NodeJS.ReadableStream> {\n return new Promise((resolve, reject) => {\n zipFile.openReadStream(entry, (err, stream) => {\n if (err || !stream) return reject(err ?? new Error(\"Failed to open entry stream\"));\n resolve(stream);\n });\n });\n}\n","import { createWriteStream, mkdirSync } from \"node:fs\";\nimport { readFile, rename } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport type { McpSuccessResult, ToolContext } from \"../context.js\";\nimport { jsonResult } from \"../context.js\";\nimport { KolmoPdfError } from \"../errors.js\";\nimport { extractZip } from \"../extract.js\";\nimport { resolveOutputRoot } from \"../output.js\";\nimport { MAX_FILE_BYTES, MAX_PAGES, readFileSize, readPageCount } from \"../pages.js\";\nimport { pollUntilComplete } from \"../polling.js\";\nimport { extensionForKind, sniffFile } from \"../sniff.js\";\n\nexport const translatePdfName = \"kolmopdf_translate_pdf\";\n\nexport const translatePdfDescription =\n \"Translate a PDF while preserving its original layout via KolmoPDF. \" +\n \"Produces a translated PDF, or a ZIP of PDFs when multiple layout modes are requested.\";\n\nexport const translatePdfInputSchema = z.object({\n file_path: z.string(),\n source_language: z.string().optional().default(\"en\"),\n target_language: z.string().optional().default(\"zh\"),\n layout_modes: z\n .array(z.enum([\"translated_only\", \"side_by_side\"]))\n .optional()\n .default([\"translated_only\"]),\n enable_image_translation: z.boolean().optional().default(false),\n enable_table_translation: z.boolean().optional().default(false),\n output_subdir: z.string().optional(),\n});\n\nexport type TranslatePdfInput = z.infer<typeof translatePdfInputSchema>;\n\nexport interface TranslatePdfOutput {\n task_id: string;\n pages_translated: number;\n points_deducted: number;\n remaining_points: number;\n output: {\n kind: string;\n translated_pdf_path: string;\n archive_path?: string;\n };\n}\n\nexport async function translatePdfHandler(\n args: TranslatePdfInput,\n ctx: ToolContext,\n): Promise<McpSuccessResult> {\n const client = ctx.getClient();\n const filePath = resolve(args.file_path);\n const filename = basename(filePath);\n const fileSize = await readFileSize(filePath);\n if (fileSize > MAX_FILE_BYTES) {\n throw new KolmoPdfError(\"translate_pdf_file_too_large\");\n }\n\n const pageCount = await readPageCount(filePath);\n if (pageCount > MAX_PAGES) {\n throw new KolmoPdfError(\"translate_pdf_page_limit_exceeded\");\n }\n\n await ctx.progress?.report(\"[uploading] Sending PDF for translation...\");\n\n const fileBuffer = await readFile(filePath);\n const submitResult = await client.translatePdf(\n fileBuffer,\n {\n source_language: args.source_language,\n target_language: args.target_language,\n layout_modes: args.layout_modes,\n enable_image_translation: args.enable_image_translation,\n enable_table_translation: args.enable_table_translation,\n },\n filename,\n );\n\n const taskId = submitResult.task_id;\n await ctx.progress?.report(`[submitted] Task ${taskId} created`);\n\n await pollUntilComplete({\n client,\n taskId,\n options: {\n pollIntervalMs: ctx.config.pollIntervalMs,\n maxPollMinutes: ctx.config.maxPollMinutes,\n },\n progress: ctx.progress,\n });\n\n await ctx.progress?.report(\"[downloading] Fetching translated result...\");\n\n const subdir = args.output_subdir || taskId;\n const outputRoot = resolveOutputRoot(ctx.config.outputDir, subdir);\n mkdirSync(outputRoot, { recursive: true });\n\n const tempPath = join(outputRoot, \"download.bin\");\n const ws = createWriteStream(tempPath);\n await client.download(taskId, ws, { destPath: tempPath });\n\n const kind = await sniffFile(tempPath);\n let translatedPdfPath = join(outputRoot, `translated${extensionForKind(kind)}`);\n let archivePath: string | undefined;\n await rename(tempPath, translatedPdfPath);\n\n if (kind === \"zip\") {\n archivePath = translatedPdfPath;\n const extracted = await extractZip(archivePath, outputRoot);\n const pdfs = extracted.files.filter((f) => f.toLowerCase().endsWith(\".pdf\"));\n if (pdfs[0]) translatedPdfPath = pdfs[0];\n }\n\n const pagesTranslated = Math.round(submitResult.points_deducted / 2);\n\n const output: TranslatePdfOutput = {\n task_id: taskId,\n pages_translated: pagesTranslated,\n points_deducted: submitResult.points_deducted,\n remaining_points: submitResult.remaining_points,\n output: {\n kind,\n translated_pdf_path: translatedPdfPath,\n ...(archivePath ? { archive_path: archivePath } : {}),\n },\n };\n\n return jsonResult(output as unknown as Record<string, unknown>);\n}\n"],"mappings":"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","names":["import_promises","import_node_path","import_zod","import_node_path","import_promises","resolve","import_promises","import_node_path","import_node_path","import_zod","import_zod","import_node_fs","import_promises","import_node_path","import_zod","import_node_fs","import_node_path","import_promises","resolve","yauzlOpen","import_node_fs","import_promises","import_node_path","import_zod"]}
|