@sylphx/pdf-reader-mcp 1.2.0 → 1.3.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/dist/index.js CHANGED
@@ -1,57 +1,536 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
- import { zodToJsonSchema } from 'zod-to-json-schema';
6
- // Import the aggregated tool definitions
7
- import { allToolDefinitions } from './handlers/index.js';
8
- // Removed incorrect import left over from partial diff
9
- // --- Tool Names (Constants) ---
10
- // Removed tool name constants, names are now in the definitions
11
- // --- Server Setup ---
12
- const server = new Server({
13
- name: 'pdf-reader-mcp',
14
- version: '1.2.0',
15
- description: 'MCP Server for reading PDF files and extracting text, metadata, images, and page information.',
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ErrorCode as ErrorCode5,
9
+ ListToolsRequestSchema,
10
+ McpError as McpError5
11
+ } from "@modelcontextprotocol/sdk/types.js";
12
+ import { zodToJsonSchema } from "zod-to-json-schema";
13
+
14
+ // src/handlers/readPdf.ts
15
+ import { ErrorCode as ErrorCode4, McpError as McpError4 } from "@modelcontextprotocol/sdk/types.js";
16
+ import { z as z2 } from "zod";
17
+
18
+ // src/pdf/extractor.ts
19
+ import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
20
+ import { PNG } from "pngjs";
21
+ var encodePixelsToPNG = (pixelData, width, height, channels) => {
22
+ const png = new PNG({ width, height });
23
+ if (channels === 4) {
24
+ png.data = Buffer.from(pixelData);
25
+ } else if (channels === 3) {
26
+ for (let i = 0;i < width * height; i++) {
27
+ const srcIdx = i * 3;
28
+ const dstIdx = i * 4;
29
+ png.data[dstIdx] = pixelData[srcIdx] ?? 0;
30
+ png.data[dstIdx + 1] = pixelData[srcIdx + 1] ?? 0;
31
+ png.data[dstIdx + 2] = pixelData[srcIdx + 2] ?? 0;
32
+ png.data[dstIdx + 3] = 255;
33
+ }
34
+ } else if (channels === 1) {
35
+ for (let i = 0;i < width * height; i++) {
36
+ const gray = pixelData[i] ?? 0;
37
+ const dstIdx = i * 4;
38
+ png.data[dstIdx] = gray;
39
+ png.data[dstIdx + 1] = gray;
40
+ png.data[dstIdx + 2] = gray;
41
+ png.data[dstIdx + 3] = 255;
42
+ }
43
+ }
44
+ const pngBuffer = PNG.sync.write(png);
45
+ return pngBuffer.toString("base64");
46
+ };
47
+ var extractMetadataAndPageCount = async (pdfDocument, includeMetadata, includePageCount) => {
48
+ const output = {};
49
+ if (includePageCount) {
50
+ output.num_pages = pdfDocument.numPages;
51
+ }
52
+ if (includeMetadata) {
53
+ try {
54
+ const pdfMetadata = await pdfDocument.getMetadata();
55
+ const infoData = pdfMetadata.info;
56
+ if (infoData !== undefined) {
57
+ output.info = infoData;
58
+ }
59
+ const metadataObj = pdfMetadata.metadata;
60
+ if (typeof metadataObj.getAll === "function") {
61
+ output.metadata = metadataObj.getAll();
62
+ } else {
63
+ const metadataRecord = {};
64
+ for (const key in metadataObj) {
65
+ if (Object.hasOwn(metadataObj, key)) {
66
+ metadataRecord[key] = metadataObj[key];
67
+ }
68
+ }
69
+ output.metadata = metadataRecord;
70
+ }
71
+ } catch (metaError) {
72
+ console.warn(`[PDF Reader MCP] Error extracting metadata: ${metaError instanceof Error ? metaError.message : String(metaError)}`);
73
+ }
74
+ }
75
+ return output;
76
+ };
77
+ var buildWarnings = (invalidPages, totalPages) => {
78
+ if (invalidPages.length === 0) {
79
+ return [];
80
+ }
81
+ return [
82
+ `Requested page numbers ${invalidPages.join(", ")} exceed total pages (${String(totalPages)}).`
83
+ ];
84
+ };
85
+ var extractPageContent = async (pdfDocument, pageNum, includeImages, sourceDescription) => {
86
+ const contentItems = [];
87
+ try {
88
+ const page = await pdfDocument.getPage(pageNum);
89
+ const textContent = await page.getTextContent();
90
+ const textByY = new Map;
91
+ for (const item of textContent.items) {
92
+ const textItem = item;
93
+ const yCoord = textItem.transform[5];
94
+ if (yCoord === undefined)
95
+ continue;
96
+ const y = Math.round(yCoord);
97
+ if (!textByY.has(y)) {
98
+ textByY.set(y, []);
99
+ }
100
+ textByY.get(y)?.push(textItem.str);
101
+ }
102
+ for (const [y, textParts] of textByY.entries()) {
103
+ const textContent2 = textParts.join("");
104
+ if (textContent2.trim()) {
105
+ contentItems.push({
106
+ type: "text",
107
+ yPosition: y,
108
+ textContent: textContent2
109
+ });
110
+ }
111
+ }
112
+ if (includeImages) {
113
+ const operatorList = await page.getOperatorList();
114
+ const imageIndices = [];
115
+ for (let i = 0;i < operatorList.fnArray.length; i++) {
116
+ const op = operatorList.fnArray[i];
117
+ if (op === OPS.paintImageXObject || op === OPS.paintXObject) {
118
+ imageIndices.push(i);
119
+ }
120
+ }
121
+ const imagePromises = imageIndices.map((imgIndex, arrayIndex) => new Promise((resolve) => {
122
+ const argsArray = operatorList.argsArray[imgIndex];
123
+ if (!argsArray || argsArray.length === 0) {
124
+ resolve(null);
125
+ return;
126
+ }
127
+ const imageName = argsArray[0];
128
+ let yPosition = 0;
129
+ if (argsArray.length > 1 && Array.isArray(argsArray[1])) {
130
+ const transform = argsArray[1];
131
+ const yCoord = transform[5];
132
+ if (yCoord !== undefined) {
133
+ yPosition = Math.round(yCoord);
134
+ }
135
+ }
136
+ const processImageData = (imageData) => {
137
+ if (!imageData || typeof imageData !== "object") {
138
+ return null;
139
+ }
140
+ const img = imageData;
141
+ if (!img.data || !img.width || !img.height) {
142
+ return null;
143
+ }
144
+ const channels = img.kind === 1 ? 1 : img.kind === 3 ? 4 : 3;
145
+ const format = img.kind === 1 ? "grayscale" : img.kind === 3 ? "rgba" : "rgb";
146
+ const pngBase64 = encodePixelsToPNG(img.data, img.width, img.height, channels);
147
+ return {
148
+ type: "image",
149
+ yPosition,
150
+ imageData: {
151
+ page: pageNum,
152
+ index: arrayIndex,
153
+ width: img.width,
154
+ height: img.height,
155
+ format,
156
+ data: pngBase64
157
+ }
158
+ };
159
+ };
160
+ if (imageName.startsWith("g_")) {
161
+ try {
162
+ const imageData = page.commonObjs.get(imageName);
163
+ if (imageData) {
164
+ const result = processImageData(imageData);
165
+ resolve(result);
166
+ return;
167
+ }
168
+ } catch (error) {
169
+ const message = error instanceof Error ? error.message : String(error);
170
+ console.warn(`[PDF Reader MCP] Error getting image from commonObjs ${imageName}: ${message}`);
171
+ }
172
+ }
173
+ try {
174
+ const imageData = page.objs.get(imageName);
175
+ if (imageData !== undefined) {
176
+ const result = processImageData(imageData);
177
+ resolve(result);
178
+ return;
179
+ }
180
+ } catch (error) {
181
+ const message = error instanceof Error ? error.message : String(error);
182
+ console.warn(`[PDF Reader MCP] Sync image get failed for ${imageName}, trying async: ${message}`);
183
+ }
184
+ let resolved = false;
185
+ const timeout = setTimeout(() => {
186
+ if (!resolved) {
187
+ resolved = true;
188
+ console.warn(`[PDF Reader MCP] Image extraction timeout for ${imageName} on page ${String(pageNum)}`);
189
+ resolve(null);
190
+ }
191
+ }, 1e4);
192
+ page.objs.get(imageName, (imageData) => {
193
+ if (!resolved) {
194
+ resolved = true;
195
+ clearTimeout(timeout);
196
+ const result = processImageData(imageData);
197
+ resolve(result);
198
+ }
199
+ });
200
+ }));
201
+ const resolvedImages = await Promise.all(imagePromises);
202
+ contentItems.push(...resolvedImages.filter((item) => item !== null));
203
+ }
204
+ } catch (error) {
205
+ const message = error instanceof Error ? error.message : String(error);
206
+ console.warn(`[PDF Reader MCP] Error extracting page content for page ${String(pageNum)} in ${sourceDescription}: ${message}`);
207
+ return [
208
+ {
209
+ type: "text",
210
+ yPosition: 0,
211
+ textContent: `Error processing page: ${message}`
212
+ }
213
+ ];
214
+ }
215
+ return contentItems.sort((a, b) => b.yPosition - a.yPosition);
216
+ };
217
+
218
+ // src/pdf/loader.ts
219
+ import fs from "node:fs/promises";
220
+ import { ErrorCode as ErrorCode2, McpError as McpError2 } from "@modelcontextprotocol/sdk/types.js";
221
+ import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
222
+
223
+ // src/utils/pathUtils.ts
224
+ import path from "node:path";
225
+ import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
226
+ var PROJECT_ROOT = process.cwd();
227
+ var resolvePath = (userPath) => {
228
+ if (typeof userPath !== "string") {
229
+ throw new McpError(ErrorCode.InvalidParams, "Path must be a string.");
230
+ }
231
+ const normalizedUserPath = path.normalize(userPath);
232
+ if (path.isAbsolute(normalizedUserPath)) {
233
+ return normalizedUserPath;
234
+ }
235
+ return path.resolve(PROJECT_ROOT, normalizedUserPath);
236
+ };
237
+
238
+ // src/pdf/loader.ts
239
+ var loadPdfDocument = async (source, sourceDescription) => {
240
+ let pdfDataSource;
241
+ try {
242
+ if (source.path) {
243
+ const safePath = resolvePath(source.path);
244
+ const buffer = await fs.readFile(safePath);
245
+ pdfDataSource = new Uint8Array(buffer);
246
+ } else if (source.url) {
247
+ pdfDataSource = { url: source.url };
248
+ } else {
249
+ throw new McpError2(ErrorCode2.InvalidParams, `Source ${sourceDescription} missing 'path' or 'url'.`);
250
+ }
251
+ } catch (err) {
252
+ if (err instanceof McpError2) {
253
+ throw err;
254
+ }
255
+ const message = err instanceof Error ? err.message : String(err);
256
+ const errorCode = ErrorCode2.InvalidRequest;
257
+ if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT" && source.path) {
258
+ throw new McpError2(errorCode, `File not found at '${source.path}'.`, {
259
+ cause: err instanceof Error ? err : undefined
260
+ });
261
+ }
262
+ throw new McpError2(errorCode, `Failed to prepare PDF source ${sourceDescription}. Reason: ${message}`, { cause: err instanceof Error ? err : undefined });
263
+ }
264
+ const loadingTask = getDocument(pdfDataSource);
265
+ try {
266
+ return await loadingTask.promise;
267
+ } catch (err) {
268
+ console.error(`[PDF Reader MCP] PDF.js loading error for ${sourceDescription}:`, err);
269
+ const message = err instanceof Error ? err.message : String(err);
270
+ throw new McpError2(ErrorCode2.InvalidRequest, `Failed to load PDF document from ${sourceDescription}. Reason: ${message || "Unknown loading error"}`, { cause: err instanceof Error ? err : undefined });
271
+ }
272
+ };
273
+
274
+ // src/pdf/parser.ts
275
+ import { ErrorCode as ErrorCode3, McpError as McpError3 } from "@modelcontextprotocol/sdk/types.js";
276
+ var MAX_RANGE_SIZE = 1e4;
277
+ var parseRangePart = (part, pages) => {
278
+ const trimmedPart = part.trim();
279
+ if (trimmedPart.includes("-")) {
280
+ const splitResult = trimmedPart.split("-");
281
+ const startStr = splitResult[0] || "";
282
+ const endStr = splitResult[1];
283
+ const start = parseInt(startStr, 10);
284
+ const end = endStr === "" || endStr === undefined ? Infinity : parseInt(endStr, 10);
285
+ if (Number.isNaN(start) || Number.isNaN(end) || start <= 0 || start > end) {
286
+ throw new Error(`Invalid page range values: ${trimmedPart}`);
287
+ }
288
+ const practicalEnd = Math.min(end, start + MAX_RANGE_SIZE);
289
+ for (let i = start;i <= practicalEnd; i++) {
290
+ pages.add(i);
291
+ }
292
+ if (end === Infinity && practicalEnd === start + MAX_RANGE_SIZE) {
293
+ console.warn(`[PDF Reader MCP] Open-ended range starting at ${String(start)} was truncated at page ${String(practicalEnd)}.`);
294
+ }
295
+ } else {
296
+ const page = parseInt(trimmedPart, 10);
297
+ if (Number.isNaN(page) || page <= 0) {
298
+ throw new Error(`Invalid page number: ${trimmedPart}`);
299
+ }
300
+ pages.add(page);
301
+ }
302
+ };
303
+ var parsePageRanges = (ranges) => {
304
+ const pages = new Set;
305
+ const parts = ranges.split(",");
306
+ for (const part of parts) {
307
+ parseRangePart(part, pages);
308
+ }
309
+ if (pages.size === 0) {
310
+ throw new Error("Page range string resulted in zero valid pages.");
311
+ }
312
+ return Array.from(pages).sort((a, b) => a - b);
313
+ };
314
+ var getTargetPages = (sourcePages, sourceDescription) => {
315
+ if (!sourcePages) {
316
+ return;
317
+ }
318
+ try {
319
+ if (typeof sourcePages === "string") {
320
+ return parsePageRanges(sourcePages);
321
+ }
322
+ if (sourcePages.some((p) => !Number.isInteger(p) || p <= 0)) {
323
+ throw new Error("Page numbers in array must be positive integers.");
324
+ }
325
+ const uniquePages = [...new Set(sourcePages)].sort((a, b) => a - b);
326
+ if (uniquePages.length === 0) {
327
+ throw new Error("Page specification resulted in an empty set of pages.");
328
+ }
329
+ return uniquePages;
330
+ } catch (error) {
331
+ const message = error instanceof Error ? error.message : String(error);
332
+ throw new McpError3(ErrorCode3.InvalidParams, `Invalid page specification for source ${sourceDescription}: ${message}`);
333
+ }
334
+ };
335
+ var determinePagesToProcess = (targetPages, totalPages, includeFullText) => {
336
+ if (targetPages) {
337
+ const pagesToProcess = targetPages.filter((p) => p <= totalPages);
338
+ const invalidPages = targetPages.filter((p) => p > totalPages);
339
+ return { pagesToProcess, invalidPages };
340
+ }
341
+ if (includeFullText) {
342
+ const pagesToProcess = Array.from({ length: totalPages }, (_, i) => i + 1);
343
+ return { pagesToProcess, invalidPages: [] };
344
+ }
345
+ return { pagesToProcess: [], invalidPages: [] };
346
+ };
347
+
348
+ // src/schemas/readPdf.ts
349
+ import { z } from "zod";
350
+ var pageSpecifierSchema = z.union([
351
+ z.array(z.number().int().min(1)).min(1).describe("Array of page numbers (1-based)"),
352
+ z.string().min(1).refine((val) => /^[0-9,-]+$/.test(val.replace(/\s/g, "")), {
353
+ message: "Page string must contain only numbers, commas, and hyphens."
354
+ }).describe('Page range string (e.g., "1-5,10,15-20")')
355
+ ]);
356
+ var pdfSourceSchema = z.object({
357
+ path: z.string().min(1).optional().describe("Path to the local PDF file (absolute or relative to cwd)."),
358
+ url: z.string().url().optional().describe("URL of the PDF file."),
359
+ pages: pageSpecifierSchema.optional().describe("Extract text only from specific pages (1-based) or ranges for this source. If provided, 'include_full_text' is ignored for this source.")
360
+ }).strict().refine((data) => !!(data.path && !data.url) || !!(!data.path && data.url), {
361
+ message: "Each source must have either 'path' or 'url', but not both."
362
+ });
363
+ var readPdfArgsSchema = z.object({
364
+ sources: z.array(pdfSourceSchema).min(1).describe("An array of PDF sources to process, each can optionally specify pages."),
365
+ include_full_text: z.boolean().optional().default(false).describe("Include the full text content of each PDF (only if 'pages' is not specified for that source)."),
366
+ include_metadata: z.boolean().optional().default(true).describe("Include metadata and info objects for each PDF."),
367
+ include_page_count: z.boolean().optional().default(true).describe("Include the total number of pages for each PDF."),
368
+ include_images: z.boolean().optional().default(false).describe("Extract and include embedded images from the PDF pages as base64-encoded data.")
369
+ }).strict();
370
+
371
+ // src/handlers/readPdf.ts
372
+ var processSingleSource = async (source, options) => {
373
+ const sourceDescription = source.path ?? source.url ?? "unknown source";
374
+ let individualResult = { source: sourceDescription, success: false };
375
+ try {
376
+ const targetPages = getTargetPages(source.pages, sourceDescription);
377
+ const { pages: _pages, ...loadArgs } = source;
378
+ const pdfDocument = await loadPdfDocument(loadArgs, sourceDescription);
379
+ const totalPages = pdfDocument.numPages;
380
+ const metadataOutput = await extractMetadataAndPageCount(pdfDocument, options.includeMetadata, options.includePageCount);
381
+ const output = { ...metadataOutput };
382
+ const { pagesToProcess, invalidPages } = determinePagesToProcess(targetPages, totalPages, options.includeFullText);
383
+ const warnings = buildWarnings(invalidPages, totalPages);
384
+ if (warnings.length > 0) {
385
+ output.warnings = warnings;
386
+ }
387
+ if (pagesToProcess.length > 0) {
388
+ const pageContents = await Promise.all(pagesToProcess.map((pageNum) => extractPageContent(pdfDocument, pageNum, options.includeImages, sourceDescription)));
389
+ output.page_contents = pageContents.map((items, idx) => ({
390
+ page: pagesToProcess[idx],
391
+ items
392
+ }));
393
+ const extractedPageTexts = pageContents.map((items, idx) => ({
394
+ page: pagesToProcess[idx],
395
+ text: items.filter((item) => item.type === "text").map((item) => item.textContent).join("")
396
+ }));
397
+ if (targetPages) {
398
+ output.page_texts = extractedPageTexts;
399
+ } else {
400
+ output.full_text = extractedPageTexts.map((p) => p.text).join(`
401
+
402
+ `);
403
+ }
404
+ if (options.includeImages) {
405
+ const extractedImages = pageContents.flatMap((items) => items.filter((item) => item.type === "image" && item.imageData)).map((item) => item.imageData).filter((img) => img !== undefined);
406
+ if (extractedImages.length > 0) {
407
+ output.images = extractedImages;
408
+ }
409
+ }
410
+ }
411
+ individualResult = { ...individualResult, data: output, success: true };
412
+ } catch (error) {
413
+ let errorMessage = `Failed to process PDF from ${sourceDescription}.`;
414
+ if (error instanceof McpError4) {
415
+ errorMessage = error.message;
416
+ } else if (error instanceof Error) {
417
+ errorMessage += ` Reason: ${error.message}`;
418
+ } else {
419
+ errorMessage += ` Unknown error: ${JSON.stringify(error)}`;
420
+ }
421
+ individualResult.error = errorMessage;
422
+ individualResult.success = false;
423
+ individualResult.data = undefined;
424
+ }
425
+ return individualResult;
426
+ };
427
+ var handleReadPdfFunc = async (args) => {
428
+ let parsedArgs;
429
+ try {
430
+ parsedArgs = readPdfArgsSchema.parse(args);
431
+ } catch (error) {
432
+ if (error instanceof z2.ZodError) {
433
+ throw new McpError4(ErrorCode4.InvalidParams, `Invalid arguments: ${error.issues.map((e) => `${e.path.join(".")} (${e.message})`).join(", ")}`);
434
+ }
435
+ const message = error instanceof Error ? error.message : String(error);
436
+ throw new McpError4(ErrorCode4.InvalidParams, `Argument validation failed: ${message}`);
437
+ }
438
+ const { sources, include_full_text, include_metadata, include_page_count, include_images } = parsedArgs;
439
+ const results = await Promise.all(sources.map((source) => processSingleSource(source, {
440
+ includeFullText: include_full_text,
441
+ includeMetadata: include_metadata,
442
+ includePageCount: include_page_count,
443
+ includeImages: include_images
444
+ })));
445
+ const content = [];
446
+ const resultsForJson = results.map((result) => {
447
+ if (result.data) {
448
+ const { images, page_contents, ...dataWithoutBinaryContent } = result.data;
449
+ if (images) {
450
+ const imageInfo = images.map((img) => ({
451
+ page: img.page,
452
+ index: img.index,
453
+ width: img.width,
454
+ height: img.height,
455
+ format: img.format
456
+ }));
457
+ return { ...result, data: { ...dataWithoutBinaryContent, image_info: imageInfo } };
458
+ }
459
+ return { ...result, data: dataWithoutBinaryContent };
460
+ }
461
+ return result;
462
+ });
463
+ content.push({
464
+ type: "text",
465
+ text: JSON.stringify({ results: resultsForJson }, null, 2)
466
+ });
467
+ for (const result of results) {
468
+ if (!result.success || !result.data?.page_contents)
469
+ continue;
470
+ for (const pageContent of result.data.page_contents) {
471
+ for (const item of pageContent.items) {
472
+ if (item.type === "text" && item.textContent) {
473
+ content.push({
474
+ type: "text",
475
+ text: item.textContent
476
+ });
477
+ } else if (item.type === "image" && item.imageData) {
478
+ content.push({
479
+ type: "image",
480
+ data: item.imageData.data,
481
+ mimeType: "image/png"
482
+ });
483
+ }
484
+ }
485
+ }
486
+ }
487
+ return { content };
488
+ };
489
+ var readPdfToolDefinition = {
490
+ name: "read_pdf",
491
+ description: "Reads content/metadata/images from one or more PDFs (local/URL). Each source can specify pages to extract.",
492
+ schema: readPdfArgsSchema,
493
+ handler: handleReadPdfFunc
494
+ };
495
+
496
+ // src/handlers/index.ts
497
+ var allToolDefinitions = [readPdfToolDefinition];
498
+
499
+ // src/index.ts
500
+ var server = new Server({
501
+ name: "pdf-reader-mcp",
502
+ version: "1.3.0",
503
+ description: "MCP Server for reading PDF files and extracting text, metadata, images, and page information."
16
504
  }, {
17
- capabilities: { tools: {} },
505
+ capabilities: { tools: {} }
18
506
  });
19
- // Helper function to convert Zod schema to JSON schema for MCP
20
- // Use 'unknown' instead of 'any' for better type safety, although casting is still needed for the SDK
21
- const generateInputSchema = (schema) => {
22
- // Need to cast as 'unknown' then 'object' because zodToJsonSchema might return slightly incompatible types for MCP SDK
23
- return zodToJsonSchema(schema, { target: 'openApi3' });
507
+ var generateInputSchema = (schema) => {
508
+ return zodToJsonSchema(schema, { target: "openApi3" });
24
509
  };
25
510
  server.setRequestHandler(ListToolsRequestSchema, () => {
26
- // Removed unnecessary async
27
- // Removed log
28
- // Map the aggregated definitions to the format expected by the SDK
29
- const availableTools = allToolDefinitions.map((def) => ({
30
- name: def.name,
31
- description: def.description,
32
- inputSchema: generateInputSchema(def.schema), // Generate JSON schema from Zod schema
33
- }));
34
- return { tools: availableTools };
511
+ const availableTools = allToolDefinitions.map((def) => ({
512
+ name: def.name,
513
+ description: def.description,
514
+ inputSchema: generateInputSchema(def.schema)
515
+ }));
516
+ return { tools: availableTools };
35
517
  });
36
518
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
37
- // Use imported handlers
38
- // Find the tool definition by name and call its handler
39
- const toolDefinition = allToolDefinitions.find((def) => def.name === request.params.name);
40
- if (!toolDefinition) {
41
- throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
42
- }
43
- // Call the handler associated with the found definition
44
- // The handler itself will perform Zod validation on the arguments
45
- return toolDefinition.handler(request.params.arguments);
519
+ const toolDefinition = allToolDefinitions.find((def) => def.name === request.params.name);
520
+ if (!toolDefinition) {
521
+ throw new McpError5(ErrorCode5.MethodNotFound, `Unknown tool: ${request.params.name}`);
522
+ }
523
+ return toolDefinition.handler(request.params.arguments);
46
524
  });
47
- // --- Server Start ---
48
525
  async function main() {
49
- const transport = new StdioServerTransport();
50
- await server.connect(transport);
51
- console.error('[PDF Reader MCP] Server running on stdio');
526
+ const transport = new StdioServerTransport;
527
+ await server.connect(transport);
528
+ if (process.env.DEBUG_MCP) {
529
+ console.error("[PDF Reader MCP] Server running on stdio");
530
+ console.error("[PDF Reader MCP] Project root:", process.cwd());
531
+ }
52
532
  }
53
533
  main().catch((error) => {
54
- // Specify 'unknown' type for catch variable
55
- console.error('[PDF Reader MCP] Server error:', error);
56
- process.exit(1);
534
+ console.error("[PDF Reader MCP] Server error:", error);
535
+ process.exit(1);
57
536
  });