@intelligentelectron/pdf-analyzer 0.0.3

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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Auto-updater for pdf-analyzer server.
3
+ *
4
+ * Checks GitHub Releases for newer versions and self-updates on startup.
5
+ * Can be disabled via PDF_MCP_NO_UPDATE=1 environment variable.
6
+ */
7
+ /** Result of an update check. */
8
+ export interface UpdateCheckResult {
9
+ updateAvailable: boolean;
10
+ currentVersion: string;
11
+ latestVersion: string | null;
12
+ downloadUrl: string | null;
13
+ error?: string;
14
+ }
15
+ /** Result of an update operation. */
16
+ export interface UpdateResult {
17
+ success: boolean;
18
+ previousVersion: string;
19
+ newVersion: string | null;
20
+ error?: string;
21
+ }
22
+ /**
23
+ * Check if an update is available.
24
+ */
25
+ export declare const checkForUpdate: () => Promise<UpdateCheckResult>;
26
+ /**
27
+ * Perform the update by downloading and replacing the current binary.
28
+ */
29
+ export declare const performUpdate: (downloadUrl: string, newVersion: string) => Promise<UpdateResult>;
30
+ /**
31
+ * Re-execute the current process with the same arguments.
32
+ */
33
+ export declare const reexec: () => never;
34
+ /**
35
+ * Check for updates and apply if available.
36
+ * This is the main entry point for auto-updates on startup.
37
+ */
38
+ export declare const autoUpdate: () => Promise<boolean>;
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Auto-updater for pdf-analyzer server.
3
+ *
4
+ * Checks GitHub Releases for newer versions and self-updates on startup.
5
+ * Can be disabled via PDF_MCP_NO_UPDATE=1 environment variable.
6
+ */
7
+ import { createWriteStream, chmodSync, renameSync, unlinkSync, existsSync, readdirSync, } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join, dirname, basename } from "node:path";
10
+ import { spawn } from "node:child_process";
11
+ import { VERSION, GITHUB_REPO, BINARY_NAME } from "../version.js";
12
+ /**
13
+ * Get the platform-specific binary name.
14
+ */
15
+ const getPlatformBinaryName = () => {
16
+ const platform = process.platform;
17
+ const arch = process.arch;
18
+ if (platform === "darwin") {
19
+ return arch === "arm64" ? `${BINARY_NAME}-darwin-arm64` : `${BINARY_NAME}-darwin-x64`;
20
+ }
21
+ else if (platform === "linux") {
22
+ return arch === "arm64" ? `${BINARY_NAME}-linux-arm64` : `${BINARY_NAME}-linux-x64`;
23
+ }
24
+ else if (platform === "win32") {
25
+ return `${BINARY_NAME}-windows-x64.exe`;
26
+ }
27
+ throw new Error(`Unsupported platform: ${platform}-${arch}`);
28
+ };
29
+ /**
30
+ * Parse a version string into comparable parts.
31
+ */
32
+ const parseVersion = (version) => {
33
+ const cleaned = version.replace(/^v/, "");
34
+ return cleaned.split(".").map((part) => parseInt(part, 10) || 0);
35
+ };
36
+ /**
37
+ * Compare two version strings. Returns:
38
+ * - Negative if a < b
39
+ * - Zero if a === b
40
+ * - Positive if a > b
41
+ */
42
+ const compareVersions = (a, b) => {
43
+ const partsA = parseVersion(a);
44
+ const partsB = parseVersion(b);
45
+ const maxLen = Math.max(partsA.length, partsB.length);
46
+ for (let i = 0; i < maxLen; i++) {
47
+ const numA = partsA[i] || 0;
48
+ const numB = partsB[i] || 0;
49
+ if (numA !== numB) {
50
+ return numA - numB;
51
+ }
52
+ }
53
+ return 0;
54
+ };
55
+ /**
56
+ * Fetch the latest release from GitHub.
57
+ */
58
+ const fetchLatestRelease = async () => {
59
+ const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
60
+ const response = await fetch(url, {
61
+ headers: {
62
+ Accept: "application/vnd.github.v3+json",
63
+ "User-Agent": `${BINARY_NAME}/${VERSION}`,
64
+ },
65
+ });
66
+ if (!response.ok) {
67
+ if (response.status === 404) {
68
+ throw new Error("No releases found");
69
+ }
70
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
71
+ }
72
+ return response.json();
73
+ };
74
+ /**
75
+ * Check if an update is available.
76
+ */
77
+ export const checkForUpdate = async () => {
78
+ try {
79
+ const release = await fetchLatestRelease();
80
+ const latestVersion = release.tag_name.replace(/^v/, "");
81
+ const updateAvailable = compareVersions(latestVersion, VERSION) > 0;
82
+ let downloadUrl = null;
83
+ if (updateAvailable) {
84
+ const binaryName = getPlatformBinaryName();
85
+ const asset = release.assets.find((a) => a.name === binaryName);
86
+ if (asset) {
87
+ downloadUrl = asset.browser_download_url;
88
+ }
89
+ }
90
+ return {
91
+ updateAvailable,
92
+ currentVersion: VERSION,
93
+ latestVersion,
94
+ downloadUrl,
95
+ };
96
+ }
97
+ catch (error) {
98
+ return {
99
+ updateAvailable: false,
100
+ currentVersion: VERSION,
101
+ latestVersion: null,
102
+ downloadUrl: null,
103
+ error: error instanceof Error ? error.message : String(error),
104
+ };
105
+ }
106
+ };
107
+ /**
108
+ * Download a file from a URL to a local path.
109
+ */
110
+ const downloadFile = async (url, destPath) => {
111
+ const response = await fetch(url, {
112
+ headers: {
113
+ "User-Agent": `${BINARY_NAME}/${VERSION}`,
114
+ },
115
+ });
116
+ if (!response.ok) {
117
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
118
+ }
119
+ const fileStream = createWriteStream(destPath);
120
+ return new Promise((resolve, reject) => {
121
+ if (!response.body) {
122
+ reject(new Error("No response body"));
123
+ return;
124
+ }
125
+ const reader = response.body.getReader();
126
+ const pump = async () => {
127
+ const { done, value } = await reader.read();
128
+ if (done) {
129
+ fileStream.end();
130
+ return;
131
+ }
132
+ fileStream.write(Buffer.from(value));
133
+ return pump();
134
+ };
135
+ fileStream.on("finish", resolve);
136
+ fileStream.on("error", reject);
137
+ pump().catch(reject);
138
+ });
139
+ };
140
+ /**
141
+ * Get the path to the current executable.
142
+ */
143
+ const getCurrentExecutablePath = () => {
144
+ // For Bun-compiled binaries, process.execPath points to the binary itself
145
+ // For Node.js, process.argv[1] is the script path
146
+ if (process.execPath.includes("node") || process.execPath.includes("bun")) {
147
+ // Running via node/bun interpreter - use argv[1]
148
+ return process.argv[1];
149
+ }
150
+ // Compiled binary - use execPath
151
+ return process.execPath;
152
+ };
153
+ /**
154
+ * Generate a unique backup path with timestamp to avoid conflicts with locked files.
155
+ * On Windows, previous backup files may still be locked by the old process.
156
+ */
157
+ const getBackupPath = (currentPath) => {
158
+ return `${currentPath}.backup.${Date.now()}`;
159
+ };
160
+ /**
161
+ * Clean up old backup files from previous updates (best effort).
162
+ * On Windows, backup files may remain if the old process was still running.
163
+ * This is non-fatal - if files are locked, they'll be cleaned up next time.
164
+ */
165
+ const cleanupOldBackups = (currentPath) => {
166
+ const dir = dirname(currentPath);
167
+ const base = basename(currentPath);
168
+ try {
169
+ const files = readdirSync(dir);
170
+ for (const file of files) {
171
+ if (file.startsWith(`${base}.backup.`)) {
172
+ try {
173
+ unlinkSync(join(dir, file));
174
+ }
175
+ catch {
176
+ // Ignore - file may still be locked
177
+ }
178
+ }
179
+ }
180
+ }
181
+ catch {
182
+ // Ignore directory read errors
183
+ }
184
+ };
185
+ /**
186
+ * Perform the update by downloading and replacing the current binary.
187
+ */
188
+ export const performUpdate = async (downloadUrl, newVersion) => {
189
+ const currentPath = getCurrentExecutablePath();
190
+ const tempPath = join(tmpdir(), `${BINARY_NAME}-update-${Date.now()}`);
191
+ // Use timestamped backup to avoid conflicts with locked files from previous updates
192
+ const backupPath = getBackupPath(currentPath);
193
+ // Clean up old backups from previous updates (best effort, non-fatal)
194
+ cleanupOldBackups(currentPath);
195
+ try {
196
+ // Download new binary to temp location
197
+ await downloadFile(downloadUrl, tempPath);
198
+ // Make executable
199
+ if (process.platform !== "win32") {
200
+ chmodSync(tempPath, 0o755);
201
+ }
202
+ // Backup current binary
203
+ if (existsSync(currentPath)) {
204
+ renameSync(currentPath, backupPath);
205
+ }
206
+ // Move new binary into place
207
+ renameSync(tempPath, currentPath);
208
+ // Remove backup (non-fatal on Windows due to file locking)
209
+ if (existsSync(backupPath)) {
210
+ try {
211
+ unlinkSync(backupPath);
212
+ }
213
+ catch {
214
+ // On Windows, the old executable may still be locked.
215
+ // This is fine - it will be cleaned up on next update.
216
+ }
217
+ }
218
+ return {
219
+ success: true,
220
+ previousVersion: VERSION,
221
+ newVersion,
222
+ };
223
+ }
224
+ catch (error) {
225
+ // Attempt to restore backup
226
+ if (existsSync(backupPath) && !existsSync(currentPath)) {
227
+ try {
228
+ renameSync(backupPath, currentPath);
229
+ }
230
+ catch {
231
+ // Ignore restore errors
232
+ }
233
+ }
234
+ // Clean up temp file
235
+ if (existsSync(tempPath)) {
236
+ try {
237
+ unlinkSync(tempPath);
238
+ }
239
+ catch {
240
+ // Ignore cleanup errors
241
+ }
242
+ }
243
+ return {
244
+ success: false,
245
+ previousVersion: VERSION,
246
+ newVersion: null,
247
+ error: error instanceof Error ? error.message : String(error),
248
+ };
249
+ }
250
+ };
251
+ /**
252
+ * Re-execute the current process with the same arguments.
253
+ */
254
+ export const reexec = () => {
255
+ const execPath = getCurrentExecutablePath();
256
+ const args = process.argv.slice(2);
257
+ // Spawn the new process
258
+ const child = spawn(execPath, args, {
259
+ stdio: "inherit",
260
+ detached: false,
261
+ });
262
+ // Exit this process when child exits
263
+ child.on("exit", (code) => {
264
+ process.exit(code ?? 0);
265
+ });
266
+ child.on("error", (err) => {
267
+ console.error("Failed to restart:", err.message);
268
+ process.exit(1);
269
+ });
270
+ // This line is reached but we've set up handlers to exit
271
+ // TypeScript needs the never return type satisfied
272
+ throw new Error("Process should have been replaced");
273
+ };
274
+ /**
275
+ * Check for updates and apply if available.
276
+ * This is the main entry point for auto-updates on startup.
277
+ */
278
+ export const autoUpdate = async () => {
279
+ // Check if updates are disabled
280
+ if (process.env.PDF_MCP_NO_UPDATE === "1") {
281
+ return false;
282
+ }
283
+ const check = await checkForUpdate();
284
+ if (check.error) {
285
+ // Silently continue if update check fails
286
+ return false;
287
+ }
288
+ if (!check.updateAvailable || !check.downloadUrl || !check.latestVersion) {
289
+ return false;
290
+ }
291
+ // Log update to stderr (MCP uses stdio, so stdout is reserved)
292
+ console.error(`[pdf-analyzer] Updating from ${VERSION} to ${check.latestVersion}...`);
293
+ const result = await performUpdate(check.downloadUrl, check.latestVersion);
294
+ if (!result.success) {
295
+ console.error(`[pdf-analyzer] Update failed: ${result.error}`);
296
+ return false;
297
+ }
298
+ console.error(`[pdf-analyzer] Update complete. Restarting...`);
299
+ return true;
300
+ };
301
+ //# sourceMappingURL=updater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updater.js","sourceRoot":"","sources":["../../src/cli/updater.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PDF Analyzer MCP Server Entry Point
4
+ *
5
+ * Run with: npx tsx src/index.ts
6
+ * Or after build: node dist/index.js
7
+ *
8
+ * CLI flags:
9
+ * --version, -v Print version and exit
10
+ * --update Check for updates and apply if available
11
+ * --uninstall Remove pdf-analyzer from the system
12
+ * --no-update Skip auto-update check on startup
13
+ * --help, -h Show help
14
+ *
15
+ * Environment variables:
16
+ * GEMINI_API_KEY Required. Your Gemini API key.
17
+ * PDF_MCP_NO_UPDATE=1 Disable auto-updates
18
+ */
19
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PDF Analyzer MCP Server Entry Point
4
+ *
5
+ * Run with: npx tsx src/index.ts
6
+ * Or after build: node dist/index.js
7
+ *
8
+ * CLI flags:
9
+ * --version, -v Print version and exit
10
+ * --update Check for updates and apply if available
11
+ * --uninstall Remove pdf-analyzer from the system
12
+ * --no-update Skip auto-update check on startup
13
+ * --help, -h Show help
14
+ *
15
+ * Environment variables:
16
+ * GEMINI_API_KEY Required. Your Gemini API key.
17
+ * PDF_MCP_NO_UPDATE=1 Disable auto-updates
18
+ */
19
+ import { autoUpdate, reexec } from "./cli/updater.js";
20
+ import { printVersion, printHelp, handleUpdateCommand, handleUninstallCommand, } from "./cli/commands.js";
21
+ import { runServer } from "./server.js";
22
+ /**
23
+ * Main entry point for the PDF Analyzer MCP server.
24
+ */
25
+ const main = async () => {
26
+ const args = process.argv.slice(2);
27
+ // Handle --version / -v
28
+ if (args.includes("--version") || args.includes("-v")) {
29
+ printVersion();
30
+ return;
31
+ }
32
+ // Handle --help / -h
33
+ if (args.includes("--help") || args.includes("-h")) {
34
+ printHelp();
35
+ return;
36
+ }
37
+ // Handle --update
38
+ if (args.includes("--update")) {
39
+ await handleUpdateCommand();
40
+ return;
41
+ }
42
+ // Handle --uninstall
43
+ if (args.includes("--uninstall")) {
44
+ await handleUninstallCommand();
45
+ return;
46
+ }
47
+ // Auto-update check on startup (unless --no-update or env var)
48
+ const skipUpdate = args.includes("--no-update") || process.env.PDF_MCP_NO_UPDATE === "1";
49
+ if (!skipUpdate) {
50
+ const updated = await autoUpdate();
51
+ if (updated) {
52
+ reexec();
53
+ }
54
+ }
55
+ await runServer();
56
+ };
57
+ main().catch((error) => {
58
+ console.error("Server error:", error);
59
+ process.exit(1);
60
+ });
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;GAEG;AACH,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,wBAAwB;IACxB,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,YAAY,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IAED,qBAAqB;IACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,kBAAkB;IAClB,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,MAAM,mBAAmB,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,qBAAqB;IACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACjC,MAAM,sBAAsB,EAAE,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,+DAA+D;IAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,GAAG,CAAC;IAEzF,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,MAAM,UAAU,EAAE,CAAC;QACnC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IAED,MAAM,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC;AAEF,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * PDF Analyzer MCP Server
3
+ *
4
+ * Model Context Protocol server for analyzing PDF documents using Gemini API.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ /**
8
+ * Create and configure the MCP server.
9
+ */
10
+ export declare const createServer: () => McpServer;
11
+ /**
12
+ * Run the MCP server with stdio transport.
13
+ */
14
+ export declare const runServer: () => Promise<void>;
package/dist/server.js ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * PDF Analyzer MCP Server
3
+ *
4
+ * Model Context Protocol server for analyzing PDF documents using Gemini API.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { z } from "zod";
9
+ import { VERSION } from "./version.js";
10
+ import { createGeminiClient, analyzePdf, isApiError, getApiErrorMessage } from "./service.js";
11
+ // =============================================================================
12
+ // Server Instructions
13
+ // =============================================================================
14
+ const SERVER_INSTRUCTIONS = `
15
+ # PDF Analyzer MCP Server
16
+
17
+ Analyzes PDF documents using Gemini's vision capabilities.
18
+
19
+ ## Tool: analyze_pdf
20
+
21
+ Pass an absolute file path or URL and a list of queries. The server reads the PDF,
22
+ sends it to Gemini with your queries, and returns structured responses.
23
+
24
+ ## Caching Strategy
25
+
26
+ The response includes a \`file_uri\` (Gemini File API URI) that you should reuse for subsequent
27
+ queries on the same document. This avoids re-uploading and is cached by Gemini for 48 hours.
28
+
29
+ **Input types accepted:**
30
+ - Local file path: \`/Users/name/docs/report.pdf\`
31
+ - Web URL: \`https://example.com/doc.pdf\`
32
+ - Gemini file URI: \`https://generativelanguage.googleapis.com/v1beta/files/abc123\` (from previous response)
33
+
34
+ **Workflow for multiple queries on same document:**
35
+ 1. First call: pass local path or URL → receive \`file_uri\` in response
36
+ 2. Subsequent calls: pass the \`file_uri\` as \`pdf_source\` → no re-upload, faster response
37
+
38
+ ## Usage Tips
39
+
40
+ - Ask specific, focused queries for best results
41
+ - For multi-page PDFs, reference page numbers in queries when relevant
42
+ - Reuse the returned \`file_uri\` for follow-up questions on the same document
43
+
44
+ ## Example
45
+
46
+ \`\`\`json
47
+ {
48
+ "pdf_source": "/path/to/document.pdf",
49
+ "queries": [
50
+ "What is the main topic of this document?",
51
+ "List all the key findings mentioned",
52
+ "What recommendations are made in the conclusion?"
53
+ ]
54
+ }
55
+ \`\`\`
56
+
57
+ ## Error Handling
58
+
59
+ Common errors and solutions:
60
+ - Missing GEMINI_API_KEY: Set the environment variable with your API key
61
+ - PDF not found: Verify the path is absolute and file exists
62
+ - URL fetch failed: Check that the URL is accessible and points to a valid PDF
63
+
64
+ ## Environment Variables
65
+
66
+ - GEMINI_API_KEY: Required. Get your key from https://aistudio.google.com/apikey
67
+ - PDF_MCP_NO_UPDATE: Set to "1" to disable auto-updates
68
+ `.trim();
69
+ // =============================================================================
70
+ // Helper Functions
71
+ // =============================================================================
72
+ /**
73
+ * Format a result as MCP tool response content.
74
+ */
75
+ const formatResult = (result) => {
76
+ const text = JSON.stringify(result, null, 2);
77
+ return {
78
+ content: [{ type: "text", text }],
79
+ };
80
+ };
81
+ /**
82
+ * Format an error as MCP tool response content.
83
+ */
84
+ const formatError = (error, details) => {
85
+ const result = details ? { error, details } : { error };
86
+ return {
87
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
88
+ isError: true,
89
+ };
90
+ };
91
+ // =============================================================================
92
+ // Server Setup
93
+ // =============================================================================
94
+ /**
95
+ * Create and configure the MCP server.
96
+ */
97
+ export const createServer = () => {
98
+ const server = new McpServer({
99
+ name: "pdf-analyzer",
100
+ version: VERSION,
101
+ }, {
102
+ capabilities: {
103
+ tools: {},
104
+ },
105
+ instructions: SERVER_INSTRUCTIONS,
106
+ });
107
+ // -------------------------------------------------------------------------
108
+ // Tool: analyze_pdf
109
+ // -------------------------------------------------------------------------
110
+ server.registerTool("analyze_pdf", {
111
+ description: "Analyze a PDF document using Gemini AI. Provide an absolute file path, URL, or Gemini file URI (from a previous response) and a list of questions to ask about the PDF content. Returns a file_uri that can be reused for subsequent queries on the same document (cached by Gemini for 48 hours).",
112
+ inputSchema: {
113
+ pdf_source: z
114
+ .string()
115
+ .describe("PDF source: absolute local file path (e.g., /Users/name/docs/report.pdf), URL (e.g., https://example.com/doc.pdf), or Gemini file URI from a previous response (e.g., https://generativelanguage.googleapis.com/v1beta/files/abc123)"),
116
+ queries: z.array(z.string().min(1)).min(1).describe("Array of questions to ask about the PDF"),
117
+ },
118
+ }, async ({ pdf_source, queries }) => {
119
+ try {
120
+ const client = createGeminiClient();
121
+ const result = await analyzePdf(client, { pdf_source, queries });
122
+ return formatResult(result);
123
+ }
124
+ catch (error) {
125
+ // Handle typed Gemini API errors
126
+ if (isApiError(error)) {
127
+ const { message, details } = getApiErrorMessage(error);
128
+ return formatError(message, details);
129
+ }
130
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
131
+ // Provide helpful context for common errors
132
+ if (message.includes("GEMINI_API_KEY")) {
133
+ return formatError(message, "Set the GEMINI_API_KEY environment variable in your MCP client configuration.");
134
+ }
135
+ if (message.includes("not found")) {
136
+ return formatError(message, "Ensure the path is absolute and the file exists.");
137
+ }
138
+ if (message.includes("Failed to fetch PDF from URL")) {
139
+ return formatError(message, "Check that the URL is accessible and points to a valid PDF file.");
140
+ }
141
+ return formatError(message);
142
+ }
143
+ });
144
+ return server;
145
+ };
146
+ /**
147
+ * Run the MCP server with stdio transport.
148
+ */
149
+ export const runServer = async () => {
150
+ const server = createServer();
151
+ const transport = new StdioServerTransport();
152
+ await server.connect(transport);
153
+ };
154
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { GoogleGenAI, ApiError } from "@google/genai";
2
+ import type { AnalyzePdfInput, AnalyzePdfResponse } from "./types.js";
3
+ /**
4
+ * Creates and returns a configured GoogleGenAI client.
5
+ * Loads GEMINI_API_KEY from .env file if not already set in environment.
6
+ */
7
+ export declare function createGeminiClient(): GoogleGenAI;
8
+ /**
9
+ * Check if a string is a Gemini File API URI.
10
+ */
11
+ export declare function isGeminiFileUri(source: string): boolean;
12
+ /**
13
+ * Check if a string is a URL (excluding Gemini File API URIs).
14
+ */
15
+ export declare function isUrl(source: string): boolean;
16
+ /**
17
+ * Validates a local PDF file path.
18
+ * Throws descriptive errors for common issues.
19
+ */
20
+ export declare function validateLocalPath(pdfPath: string): void;
21
+ /**
22
+ * Analyzes a PDF document using Gemini and returns responses to the provided queries.
23
+ * Uses the File API for uploads and structured output for reliable JSON responses.
24
+ * Returns the Gemini file_uri so calling agents can reuse it for subsequent queries.
25
+ */
26
+ export declare function analyzePdf(client: GoogleGenAI, input: AnalyzePdfInput): Promise<AnalyzePdfResponse>;
27
+ /**
28
+ * Check if an error is an ApiError and return typed error info.
29
+ */
30
+ export declare function isApiError(error: unknown): error is ApiError;
31
+ /**
32
+ * Get error message from ApiError, preserving the actual API response.
33
+ */
34
+ export declare function getApiErrorMessage(error: ApiError): {
35
+ message: string;
36
+ details?: string;
37
+ };