@sylphlab/tools-pdf
Version:
Core logic for MCP PDF tools (text extraction, etc.)
1 lines • 10.6 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/tools/getTextTool.ts","../src/tools/getTextTool.schema.ts"],"sourcesContent":["// src/index.ts for @sylphlab/mcp-pdf-core\n\n// Export the tool implementation, Zod schema, core function, and inferred types\nexport { getTextTool, extractPdfText } from './tools/getTextTool.js';\nexport { getTextToolInputSchema } from './tools/getTextTool.schema.js'; // Export schema from schema file\nexport type { GetTextToolInput, GetTextResultItem, GetTextInputItem } from './tools/getTextTool.js';\n","import { readFile } from 'node:fs/promises';\nimport { defineTool } from '@sylphlab/tools-core';\nimport { jsonPart, validateAndResolvePath } from '@sylphlab/tools-core';\nimport type { ToolExecuteOptions, Part } from '@sylphlab/tools-core';\nimport * as mupdfjs from 'mupdf/mupdfjs';\nimport { z } from 'zod';\nimport { type GetTextItemSchema, getTextToolInputSchema } from './getTextTool.schema.js';\n\n// --- Core Logic Function ---\n\n/**\n * Extracts text content from a PDF buffer using MuPDF.\n * @param pdfBuffer Buffer containing the PDF data.\n * @returns A promise resolving to the extracted text content.\n * @throws If PDF parsing or text extraction fails.\n */\nexport async function extractPdfText(pdfBuffer: Buffer): Promise<string> {\n // Ensure MuPDF is initialized (important if running in different contexts)\n // await mupdfjs.ready; // Assuming ready promise exists or handle initialization\n\n let doc: mupdfjs.PDFDocument | undefined;\n try {\n doc = mupdfjs.PDFDocument.openDocument(pdfBuffer, 'application/pdf');\n const numPages = doc.countPages();\n const pageTexts: string[] = [];\n\n for (let i = 0; i < numPages; i++) {\n let page: mupdfjs.PDFPage | undefined;\n try {\n page = doc.loadPage(i);\n pageTexts.push(page.getText());\n } finally {\n page?.destroy(); // Ensure page resources are freed\n }\n }\n return pageTexts.join('\\n').trim();\n } finally {\n doc?.destroy(); // Ensure document resources are freed\n }\n}\n\n// --- TypeScript Types ---\nexport type GetTextInputItem = z.infer<typeof GetTextItemSchema>;\nexport type GetTextToolInput = z.infer<typeof getTextToolInputSchema>;\n\n// --- Output Types ---\n// Interface for a single PDF text extraction result item\nexport interface GetTextResultItem {\n /** Optional ID from the input item. */\n id?: string;\n /** The input file path. */\n path: string;\n /** Whether the text extraction for this item was successful. */\n success: boolean;\n /** The extracted text content, if successful. */\n result?: string;\n /** Error message, if extraction failed for this item. */\n error?: string;\n /** Suggestion for fixing the error. */\n suggestion?: string;\n}\n\n// Zod Schema for the individual result\nconst GetTextResultItemSchema = z.object({\n id: z.string().optional(),\n path: z.string(), // Added path to result schema\n success: z.boolean(),\n result: z.string().optional(),\n error: z.string().optional(),\n suggestion: z.string().optional(),\n});\n\n// Define the output schema instance as a constant array\nconst GetTextToolOutputSchema = z.array(GetTextResultItemSchema);\n\n// --- Helper Function ---\n\n// Helper function to process a single PDF text extraction item\nasync function processSinglePdfGetText(\n item: GetTextInputItem,\n options: ToolExecuteOptions,\n): Promise<GetTextResultItem> {\n const { id, filePath: inputFilePath } = item;\n // Initialize result with path\n const resultItem: GetTextResultItem = { id, path: inputFilePath, success: false };\n const { workspaceRoot, allowOutsideWorkspace } = options;\n\n let resolvedPath: string | undefined;\n try {\n // --- Path Validation ---\n const validationResult = validateAndResolvePath(\n inputFilePath,\n workspaceRoot,\n allowOutsideWorkspace,\n );\n if (typeof validationResult !== 'string') {\n throw new Error(\n `Path validation failed: ${validationResult.error} ${validationResult.suggestion ?? ''}`,\n );\n }\n resolvedPath = validationResult;\n // --- End Path Validation ---\n\n const buffer = await readFile(resolvedPath);\n const extractedText = await extractPdfText(buffer); // Call the core function\n\n resultItem.success = true;\n resultItem.result = extractedText;\n resultItem.suggestion = 'Successfully extracted text from PDF.';\n } catch (e: unknown) {\n resultItem.success = false;\n const errorMsg = e instanceof Error ? e.message : String(e);\n resultItem.error = `Failed to get text from PDF '${inputFilePath}': ${errorMsg}`;\n\n // Provide suggestions based on error type\n if (errorMsg.includes('Path validation failed')) {\n // Suggestion is already part of the error message from validation\n resultItem.suggestion =\n errorMsg.split('Suggestion: ')[1] ?? 'Check path validity and workspace settings.';\n } else if (e && typeof e === 'object' && 'code' in e) {\n if (e.code === 'ENOENT') {\n resultItem.suggestion = 'Ensure the file path is correct and the file exists.';\n } else if (e.code === 'EACCES') {\n resultItem.suggestion = 'Check file read permissions.';\n } else {\n resultItem.suggestion =\n 'Ensure the file is a valid, uncorrupted PDF and check permissions.';\n }\n } else if (errorMsg.toLowerCase().includes('pdf')) {\n // Generic PDF error\n resultItem.suggestion = 'Ensure the file is a valid, uncorrupted PDF document.';\n } else {\n resultItem.suggestion = 'Check file path, permissions, and file validity.';\n }\n }\n return resultItem;\n}\n\n// --- Tool Definition using defineTool ---\nimport { BaseContextSchema } from '@sylphlab/tools-core'; // Import BaseContextSchema\n\nexport const getTextTool = defineTool({\n name: 'get-text',\n description: 'Extracts text content from one or more PDF files.',\n inputSchema: getTextToolInputSchema,\n contextSchema: BaseContextSchema, // Add context schema\n execute: async (\n // Use new signature with destructuring\n { context, args }: { context: ToolExecuteOptions; args: GetTextToolInput }\n ): Promise<Part[]> => {\n // Return Part[]\n\n // Zod validation (throw error on failure)\n const parsed = getTextToolInputSchema.safeParse(args); // Validate args\n if (!parsed.success) {\n const errorMessages = Object.entries(parsed.error.flatten().fieldErrors)\n .map(([field, messages]) => `${field}: ${messages.join(', ')}`)\n .join('; ');\n throw new Error(`Input validation failed: ${errorMessages}`);\n }\n\n // Add upfront check for workspaceRoot from context\n if (!context?.workspaceRoot) {\n throw new Error('Workspace root is not available in context.'); // Updated message\n }\n\n const { items } = parsed.data; // Get data from parsed args\n const results: GetTextResultItem[] = [];\n\n // Process requests sequentially (or parallelize with Promise.all)\n for (const item of items) {\n // Pass context to processSinglePdfGetText\n const result = await processSinglePdfGetText(item, context);\n results.push(result);\n }\n\n // Return the results wrapped in jsonPart\n return [jsonPart(results, GetTextToolOutputSchema)];\n },\n});\n\n// Export necessary types\n","import { z } from 'zod';\n\n// Schema for a single PDF text extraction item\nexport const GetTextItemSchema = z.object({\n id: z.string().optional(),\n filePath: z.string().min(1, 'filePath cannot be empty.'),\n // Add options like page range later if needed\n});\n\n// Main input schema: an array of PDF items\nexport const getTextToolInputSchema = z.object({\n items: z.array(GetTextItemSchema).min(1, 'At least one PDF item is required.'),\n // allowOutsideWorkspace is handled by ToolExecuteOptions\n});\n"],"mappings":"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","names":["import_tools_core","import_zod","import_tools_core"]}