@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,240 @@
1
+ import { GoogleGenAI, ApiError, ThinkingLevel } from "@google/genai";
2
+ import { readFileSync, existsSync } from "node:fs";
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+ import { join } from "node:path";
6
+ import { GeminiResponseSchema } from "./types.js";
7
+ /**
8
+ * Load environment variables from .env file in current working directory.
9
+ * Only sets variables that are not already defined in process.env.
10
+ */
11
+ function loadEnvFile() {
12
+ const envPath = join(process.cwd(), ".env");
13
+ if (!existsSync(envPath))
14
+ return;
15
+ const content = readFileSync(envPath, "utf-8");
16
+ for (const line of content.split("\n")) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed || trimmed.startsWith("#"))
19
+ continue;
20
+ const eqIndex = trimmed.indexOf("=");
21
+ if (eqIndex === -1)
22
+ continue;
23
+ const key = trimmed.slice(0, eqIndex).trim();
24
+ const value = trimmed.slice(eqIndex + 1).trim().replace(/^["']|["']$/g, "");
25
+ if (!process.env[key]) {
26
+ process.env[key] = value;
27
+ }
28
+ }
29
+ }
30
+ const SYSTEM_INSTRUCTION = `You are a document analysis assistant. Analyze PDF documents and answer questions based on their content.
31
+ For each question, provide a clear, detailed answer based on the content of the PDF.
32
+ If the answer cannot be determined from the PDF, say so explicitly.
33
+ Always respond with accurate information from the document.`;
34
+ /** Gemini File API URI prefix */
35
+ const GEMINI_FILE_URI_PREFIX = "https://generativelanguage.googleapis.com/";
36
+ /**
37
+ * Creates and returns a configured GoogleGenAI client.
38
+ * Loads GEMINI_API_KEY from .env file if not already set in environment.
39
+ */
40
+ export function createGeminiClient() {
41
+ loadEnvFile();
42
+ const apiKey = process.env.GEMINI_API_KEY;
43
+ if (!apiKey) {
44
+ throw new Error("GEMINI_API_KEY not set. Get your key from https://aistudio.google.com/apikey");
45
+ }
46
+ return new GoogleGenAI({ apiKey });
47
+ }
48
+ /**
49
+ * Check if a string is a Gemini File API URI.
50
+ */
51
+ export function isGeminiFileUri(source) {
52
+ return source.startsWith(GEMINI_FILE_URI_PREFIX);
53
+ }
54
+ /**
55
+ * Check if a string is a URL (excluding Gemini File API URIs).
56
+ */
57
+ export function isUrl(source) {
58
+ if (isGeminiFileUri(source)) {
59
+ return false;
60
+ }
61
+ try {
62
+ const url = new URL(source);
63
+ return url.protocol === "http:" || url.protocol === "https:";
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /**
70
+ * Validates a local PDF file path.
71
+ * Throws descriptive errors for common issues.
72
+ */
73
+ export function validateLocalPath(pdfPath) {
74
+ const trimmedPath = pdfPath.trim();
75
+ if (!path.isAbsolute(trimmedPath)) {
76
+ throw new Error(`PDF path must be absolute: ${trimmedPath}`);
77
+ }
78
+ if (!fs.existsSync(trimmedPath)) {
79
+ throw new Error(`PDF file not found: ${trimmedPath}`);
80
+ }
81
+ const stats = fs.statSync(trimmedPath);
82
+ if (stats.isDirectory()) {
83
+ throw new Error(`Path is a directory, not a file: ${trimmedPath}`);
84
+ }
85
+ if (!trimmedPath.toLowerCase().endsWith(".pdf")) {
86
+ throw new Error(`File is not a PDF: ${trimmedPath}`);
87
+ }
88
+ }
89
+ /** Timeout for fetching PDFs from URLs (60 seconds) */
90
+ const FETCH_TIMEOUT_MS = 60_000;
91
+ /**
92
+ * Fetch PDF content from a URL with timeout.
93
+ */
94
+ async function fetchPdfFromUrl(url) {
95
+ const controller = new AbortController();
96
+ const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
97
+ let response;
98
+ try {
99
+ response = await fetch(url, { signal: controller.signal });
100
+ }
101
+ catch (error) {
102
+ if (error instanceof Error && error.name === "AbortError") {
103
+ throw new Error(`Request timed out after ${FETCH_TIMEOUT_MS / 1000}s`);
104
+ }
105
+ const message = error instanceof Error ? error.message : "Unknown error";
106
+ throw new Error(`Failed to fetch URL: ${message}`);
107
+ }
108
+ finally {
109
+ clearTimeout(timeoutId);
110
+ }
111
+ if (!response.ok) {
112
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
113
+ }
114
+ const contentType = response.headers.get("content-type");
115
+ if (contentType &&
116
+ !contentType.includes("application/pdf") &&
117
+ !contentType.includes("octet-stream")) {
118
+ throw new Error(`URL does not point to a PDF file. Content-Type: ${contentType}`);
119
+ }
120
+ const arrayBuffer = await response.arrayBuffer();
121
+ return Buffer.from(arrayBuffer);
122
+ }
123
+ /**
124
+ * Wait for a file to finish processing.
125
+ * Polls the file state until it's no longer PROCESSING.
126
+ */
127
+ async function waitForFileReady(client, fileName, maxAttempts = 10) {
128
+ for (let i = 0; i < maxAttempts; i++) {
129
+ const fileInfo = await client.files.get({ name: fileName });
130
+ if (fileInfo.state === "FAILED") {
131
+ throw new Error(`File processing failed: ${fileInfo.name}`);
132
+ }
133
+ if (fileInfo.state !== "PROCESSING") {
134
+ return; // File is ready (ACTIVE state)
135
+ }
136
+ await new Promise((resolve) => setTimeout(resolve, 2000));
137
+ }
138
+ throw new Error("File processing timed out");
139
+ }
140
+ /**
141
+ * Upload a PDF to the Gemini File API, or return the URI if already a Gemini file.
142
+ * Accepts local paths, web URLs, or Gemini File API URIs.
143
+ * Returns the Gemini file_uri for use in subsequent calls.
144
+ */
145
+ async function uploadPdf(client, source) {
146
+ // If already a Gemini file URI, return as-is (no upload needed)
147
+ if (isGeminiFileUri(source)) {
148
+ return source;
149
+ }
150
+ // Upload the file
151
+ let file;
152
+ if (isUrl(source)) {
153
+ const pdfBuffer = await fetchPdfFromUrl(source);
154
+ file = await client.files.upload({
155
+ file: new Blob([pdfBuffer], { type: "application/pdf" }),
156
+ config: { mimeType: "application/pdf" },
157
+ });
158
+ }
159
+ else {
160
+ validateLocalPath(source);
161
+ file = await client.files.upload({
162
+ file: source,
163
+ config: { mimeType: "application/pdf" },
164
+ });
165
+ }
166
+ if (!file.name || !file.uri) {
167
+ throw new Error("File upload failed: missing name or URI");
168
+ }
169
+ // Wait for file to be ready
170
+ await waitForFileReady(client, file.name);
171
+ return file.uri;
172
+ }
173
+ /**
174
+ * Build the user prompt with queries.
175
+ */
176
+ function buildUserPrompt(queries) {
177
+ const queriesText = queries.map((q, i) => `${i + 1}. ${q}`).join("\n");
178
+ return `Please analyze the attached PDF and answer these questions:\n\n${queriesText}`;
179
+ }
180
+ /**
181
+ * Analyzes a PDF document using Gemini and returns responses to the provided queries.
182
+ * Uses the File API for uploads and structured output for reliable JSON responses.
183
+ * Returns the Gemini file_uri so calling agents can reuse it for subsequent queries.
184
+ */
185
+ export async function analyzePdf(client, input) {
186
+ const fileUri = await uploadPdf(client, input.pdf_source);
187
+ const response = await client.models.generateContent({
188
+ model: "gemini-3-pro-preview",
189
+ contents: [
190
+ {
191
+ role: "user",
192
+ parts: [
193
+ { fileData: { fileUri, mimeType: "application/pdf" } },
194
+ { text: buildUserPrompt(input.queries) },
195
+ ],
196
+ },
197
+ ],
198
+ config: {
199
+ systemInstruction: SYSTEM_INSTRUCTION,
200
+ responseMimeType: "application/json",
201
+ responseSchema: GeminiResponseSchema,
202
+ thinkingConfig: { thinkingLevel: ThinkingLevel.HIGH },
203
+ },
204
+ });
205
+ const responseText = response.text || "{}";
206
+ const parsed = JSON.parse(responseText);
207
+ // Ensure all queries have answers (in case model missed some)
208
+ const responseMap = new Map(parsed.responses.map((r) => [r.query, r.answer]));
209
+ const responses = input.queries.map((query, i) => {
210
+ const existingAnswer = parsed.responses[i];
211
+ if (existingAnswer) {
212
+ return existingAnswer;
213
+ }
214
+ return {
215
+ query,
216
+ answer: responseMap.get(query) || "No answer found for this query.",
217
+ };
218
+ });
219
+ return {
220
+ pdf_source: input.pdf_source,
221
+ file_uri: fileUri,
222
+ responses,
223
+ };
224
+ }
225
+ /**
226
+ * Check if an error is an ApiError and return typed error info.
227
+ */
228
+ export function isApiError(error) {
229
+ return error instanceof ApiError;
230
+ }
231
+ /**
232
+ * Get error message from ApiError, preserving the actual API response.
233
+ */
234
+ export function getApiErrorMessage(error) {
235
+ return {
236
+ message: `Gemini API error (HTTP ${error.status})`,
237
+ details: error.message,
238
+ };
239
+ }
240
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { isGeminiFileUri, isUrl, validateLocalPath } from "./service.js";
3
+ import { AnalyzePdfInputSchema } from "./types.js";
4
+ describe("isGeminiFileUri", () => {
5
+ it("returns true for valid Gemini File API URIs", () => {
6
+ expect(isGeminiFileUri("https://generativelanguage.googleapis.com/v1beta/files/abc123")).toBe(true);
7
+ expect(isGeminiFileUri("https://generativelanguage.googleapis.com/v1/files/xyz789")).toBe(true);
8
+ });
9
+ it("returns false for regular URLs", () => {
10
+ expect(isGeminiFileUri("https://example.com/doc.pdf")).toBe(false);
11
+ expect(isGeminiFileUri("https://www.ti.com/lit/ds/symlink/tps62880-q1.pdf")).toBe(false);
12
+ });
13
+ it("returns false for local paths", () => {
14
+ expect(isGeminiFileUri("/Users/name/docs/report.pdf")).toBe(false);
15
+ expect(isGeminiFileUri("/tmp/test.pdf")).toBe(false);
16
+ });
17
+ it("returns false for http URLs", () => {
18
+ expect(isGeminiFileUri("http://example.com/doc.pdf")).toBe(false);
19
+ });
20
+ });
21
+ describe("isUrl", () => {
22
+ it("returns true for http URLs", () => {
23
+ expect(isUrl("http://example.com/doc.pdf")).toBe(true);
24
+ });
25
+ it("returns true for https URLs", () => {
26
+ expect(isUrl("https://example.com/doc.pdf")).toBe(true);
27
+ expect(isUrl("https://www.ti.com/lit/ds/symlink/tps62880-q1.pdf")).toBe(true);
28
+ });
29
+ it("returns false for Gemini File API URIs", () => {
30
+ expect(isUrl("https://generativelanguage.googleapis.com/v1beta/files/abc123")).toBe(false);
31
+ expect(isUrl("https://generativelanguage.googleapis.com/v1/files/xyz789")).toBe(false);
32
+ });
33
+ it("returns false for local paths", () => {
34
+ expect(isUrl("/Users/name/docs/report.pdf")).toBe(false);
35
+ expect(isUrl("/tmp/test.pdf")).toBe(false);
36
+ expect(isUrl("C:\\Users\\name\\docs\\report.pdf")).toBe(false);
37
+ });
38
+ it("returns false for relative paths", () => {
39
+ expect(isUrl("./test.pdf")).toBe(false);
40
+ expect(isUrl("../docs/test.pdf")).toBe(false);
41
+ });
42
+ it("returns false for invalid URLs", () => {
43
+ expect(isUrl("not-a-url")).toBe(false);
44
+ expect(isUrl("")).toBe(false);
45
+ });
46
+ });
47
+ describe("validateLocalPath", () => {
48
+ it("throws for relative paths", () => {
49
+ expect(() => validateLocalPath("./test.pdf")).toThrow("must be absolute");
50
+ expect(() => validateLocalPath("test.pdf")).toThrow("must be absolute");
51
+ });
52
+ it("throws for non-existent files", () => {
53
+ expect(() => validateLocalPath("/nonexistent/path/to/file.pdf")).toThrow("not found");
54
+ });
55
+ it("throws for non-PDF files", () => {
56
+ expect(() => validateLocalPath("/etc/passwd")).toThrow("not a PDF");
57
+ expect(() => validateLocalPath("/bin/ls")).toThrow("not a PDF");
58
+ });
59
+ it("throws for directories", () => {
60
+ expect(() => validateLocalPath("/tmp")).toThrow("directory");
61
+ });
62
+ it("trims whitespace from paths", () => {
63
+ expect(() => validateLocalPath(" ")).toThrow("must be absolute");
64
+ expect(() => validateLocalPath(" /nonexistent.pdf ")).toThrow("not found");
65
+ });
66
+ it("accepts valid PDF files", () => {
67
+ // Use a PDF that exists in our test fixtures
68
+ expect(() => validateLocalPath(process.cwd() + "/test/fixtures/m3000a.pdf")).not.toThrow();
69
+ });
70
+ });
71
+ describe("AnalyzePdfInputSchema", () => {
72
+ it("rejects empty queries array", () => {
73
+ const result = AnalyzePdfInputSchema.safeParse({
74
+ pdf_source: "/path/to/file.pdf",
75
+ queries: [],
76
+ });
77
+ expect(result.success).toBe(false);
78
+ });
79
+ it("rejects empty string queries", () => {
80
+ const result = AnalyzePdfInputSchema.safeParse({
81
+ pdf_source: "/path/to/file.pdf",
82
+ queries: [""],
83
+ });
84
+ expect(result.success).toBe(false);
85
+ });
86
+ it("rejects queries with empty strings mixed with valid ones", () => {
87
+ const result = AnalyzePdfInputSchema.safeParse({
88
+ pdf_source: "/path/to/file.pdf",
89
+ queries: ["valid query", ""],
90
+ });
91
+ expect(result.success).toBe(false);
92
+ });
93
+ it("accepts valid queries", () => {
94
+ const result = AnalyzePdfInputSchema.safeParse({
95
+ pdf_source: "/path/to/file.pdf",
96
+ queries: ["What is this document about?"],
97
+ });
98
+ expect(result.success).toBe(true);
99
+ });
100
+ it("accepts multiple valid queries", () => {
101
+ const result = AnalyzePdfInputSchema.safeParse({
102
+ pdf_source: "/path/to/file.pdf",
103
+ queries: ["First question?", "Second question?"],
104
+ });
105
+ expect(result.success).toBe(true);
106
+ });
107
+ });
108
+ //# sourceMappingURL=service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.test.js","sourceRoot":"","sources":["../src/service.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CACJ,eAAe,CAAC,+DAA+D,CAAC,CACjF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,CACJ,eAAe,CAAC,2DAA2D,CAAC,CAC7E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,eAAe,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,CAAC,eAAe,CAAC,mDAAmD,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,eAAe,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,eAAe,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3F,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC1E,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACpE,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,6CAA6C;QAC7C,MAAM,CAAC,GAAG,EAAE,CACV,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,2BAA2B,CAAC,CAC/D,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC;YAC7C,UAAU,EAAE,mBAAmB;YAC/B,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC;YAC7C,UAAU,EAAE,mBAAmB;YAC/B,OAAO,EAAE,CAAC,EAAE,CAAC;SACd,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC;YAC7C,UAAU,EAAE,mBAAmB;YAC/B,OAAO,EAAE,CAAC,aAAa,EAAE,EAAE,CAAC;SAC7B,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC;YAC7C,UAAU,EAAE,mBAAmB;YAC/B,OAAO,EAAE,CAAC,8BAA8B,CAAC;SAC1C,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC;YAC7C,UAAU,EAAE,mBAAmB;YAC/B,OAAO,EAAE,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;SACjD,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import { type Schema } from "@google/genai";
3
+ /** Schema for the analyze_pdf tool input */
4
+ export declare const AnalyzePdfInputSchema: z.ZodObject<{
5
+ pdf_source: z.ZodString;
6
+ queries: z.ZodArray<z.ZodString>;
7
+ }, z.core.$strip>;
8
+ /** Input type for the analyze_pdf tool */
9
+ export type AnalyzePdfInput = z.infer<typeof AnalyzePdfInputSchema>;
10
+ /** Response for a single query */
11
+ export interface QueryResponse {
12
+ query: string;
13
+ answer: string;
14
+ }
15
+ /** Response from the analyze_pdf tool */
16
+ export interface AnalyzePdfResponse {
17
+ pdf_source: string;
18
+ file_uri: string;
19
+ responses: QueryResponse[];
20
+ }
21
+ /**
22
+ * Gemini response schema for structured output.
23
+ * This ensures the model returns a predictable JSON structure.
24
+ */
25
+ export declare const GeminiResponseSchema: Schema;
26
+ /** Error response from the tool */
27
+ export interface ToolError {
28
+ error: string;
29
+ details?: string;
30
+ }
31
+ /** GitHub release asset information */
32
+ export interface GitHubAsset {
33
+ name: string;
34
+ browser_download_url: string;
35
+ size: number;
36
+ }
37
+ /** GitHub release information */
38
+ export interface GitHubRelease {
39
+ tag_name: string;
40
+ name: string;
41
+ prerelease: boolean;
42
+ draft: boolean;
43
+ assets: GitHubAsset[];
44
+ }
45
+ /** Platform and architecture identifier for binary downloads */
46
+ export type Platform = "darwin-arm64" | "darwin-x64" | "linux-arm64" | "linux-x64" | "windows-x64";
package/dist/types.js ADDED
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { Type } from "@google/genai";
3
+ /** Schema for the analyze_pdf tool input */
4
+ export const AnalyzePdfInputSchema = z.object({
5
+ pdf_source: z.string().describe("PDF source: absolute local file path or URL"),
6
+ queries: z.array(z.string().min(1)).min(1).describe("Array of questions to ask about the PDF"),
7
+ });
8
+ /**
9
+ * Gemini response schema for structured output.
10
+ * This ensures the model returns a predictable JSON structure.
11
+ */
12
+ export const GeminiResponseSchema = {
13
+ type: Type.OBJECT,
14
+ properties: {
15
+ responses: {
16
+ type: Type.ARRAY,
17
+ items: {
18
+ type: Type.OBJECT,
19
+ properties: {
20
+ query: { type: Type.STRING, description: "The original question" },
21
+ answer: { type: Type.STRING, description: "The answer based on PDF content" },
22
+ },
23
+ required: ["query", "answer"],
24
+ },
25
+ description: "Array of query-answer pairs",
26
+ },
27
+ },
28
+ required: ["responses"],
29
+ };
30
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAe,MAAM,eAAe,CAAC;AAElD,4CAA4C;AAC5C,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;IAC9E,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,yCAAyC,CAAC;CAC/F,CAAC,CAAC;AAkBH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAW;IAC1C,IAAI,EAAE,IAAI,CAAC,MAAM;IACjB,UAAU,EAAE;QACV,SAAS,EAAE;YACT,IAAI,EAAE,IAAI,CAAC,KAAK;YAChB,KAAK,EAAE;gBACL,IAAI,EAAE,IAAI,CAAC,MAAM;gBACjB,UAAU,EAAE;oBACV,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,uBAAuB,EAAE;oBAClE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE;iBAC9E;gBACD,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;aAC9B;YACD,WAAW,EAAE,6BAA6B;SAC3C;KACF;IACD,QAAQ,EAAE,CAAC,WAAW,CAAC;CACxB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Current version of the PDF Analyzer MCP Server */
2
+ export declare const VERSION: string;
3
+ /** GitHub repository for release downloads */
4
+ export declare const GITHUB_REPO = "IntelligentElectron/pdf-analyzer";
5
+ /** Binary name for the compiled executable */
6
+ export declare const BINARY_NAME = "pdf-analyzer";
7
+ /** Default installation directory for the binary */
8
+ export declare const DEFAULT_INSTALL_DIR: string;
@@ -0,0 +1,32 @@
1
+ import { createRequire } from "node:module";
2
+ /** Current version of the PDF Analyzer MCP Server */
3
+ export const VERSION = (() => {
4
+ // Bun compiled binary: use injected version
5
+ if (typeof BUILD_VERSION !== "undefined") {
6
+ return BUILD_VERSION;
7
+ }
8
+ // Node.js/npm: read from package.json
9
+ try {
10
+ const require = createRequire(import.meta.url);
11
+ const pkg = require("../package.json");
12
+ return pkg.version;
13
+ }
14
+ catch {
15
+ return "0.0.0-dev";
16
+ }
17
+ })();
18
+ /** GitHub repository for release downloads */
19
+ export const GITHUB_REPO = "IntelligentElectron/pdf-analyzer";
20
+ /** Binary name for the compiled executable */
21
+ export const BINARY_NAME = "pdf-analyzer";
22
+ /** Default installation directory for the binary */
23
+ export const DEFAULT_INSTALL_DIR = (() => {
24
+ if (process.platform === "win32") {
25
+ return `${process.env.LOCALAPPDATA || "C:\\Program Files"}\\pdf-analyzer`;
26
+ }
27
+ if (process.platform === "darwin") {
28
+ return `${process.env.HOME}/Library/Application Support/pdf-analyzer`;
29
+ }
30
+ return `${process.env.HOME}/.pdf-analyzer`;
31
+ })();
32
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAK5C,qDAAqD;AACrD,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE;IAC3B,4CAA4C;IAC5C,IAAI,OAAO,aAAa,KAAK,WAAW,EAAE,CAAC;QACzC,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,sCAAsC;IACtC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;QAC9D,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AAEL,8CAA8C;AAC9C,MAAM,CAAC,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAE9D,8CAA8C;AAC9C,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAE1C,oDAAoD;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE;IACvC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,mBAAmB,gBAAgB,CAAC;IAC5E,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,2CAA2C,CAAC;IACxE,CAAC;IACD,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,gBAAgB,CAAC;AAC7C,CAAC,CAAC,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@intelligentelectron/pdf-analyzer",
3
+ "version": "0.0.3",
4
+ "description": "MCP server for analyzing PDF documents using Gemini API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "pdf-analyzer": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "dev": "tsx src/index.ts",
15
+ "build": "tsc",
16
+ "type-check": "tsc --noEmit",
17
+ "lint": "eslint .",
18
+ "lint:fix": "eslint . --fix",
19
+ "format": "prettier --write \"src/**/*.ts\"",
20
+ "format:check": "prettier --check \"src/**/*.ts\"",
21
+ "test": "vitest run",
22
+ "test:watch": "vitest",
23
+ "compile:all": "npm run compile:darwin-arm64 && npm run compile:darwin-x64 && npm run compile:linux-arm64 && npm run compile:linux-x64 && npm run compile:windows-x64",
24
+ "compile:darwin-arm64": "bun build --compile --minify --target=bun-darwin-arm64 --define BUILD_VERSION=\"\\\"$(node -p 'require(\\\"./package.json\\\").version')\\\"\" src/index.ts --outfile=bin/pdf-analyzer-darwin-arm64",
25
+ "compile:darwin-x64": "bun build --compile --minify --target=bun-darwin-x64 --define BUILD_VERSION=\"\\\"$(node -p 'require(\\\"./package.json\\\").version')\\\"\" src/index.ts --outfile=bin/pdf-analyzer-darwin-x64",
26
+ "compile:linux-arm64": "bun build --compile --minify --target=bun-linux-arm64 --define BUILD_VERSION=\"\\\"$(node -p 'require(\\\"./package.json\\\").version')\\\"\" src/index.ts --outfile=bin/pdf-analyzer-linux-arm64",
27
+ "compile:linux-x64": "bun build --compile --minify --target=bun-linux-x64 --define BUILD_VERSION=\"\\\"$(node -p 'require(\\\"./package.json\\\").version')\\\"\" src/index.ts --outfile=bin/pdf-analyzer-linux-x64",
28
+ "compile:windows-x64": "bun build --compile --minify --target=bun-windows-x64 --define BUILD_VERSION=\"\\\"$(node -p 'require(\\\"./package.json\\\").version')\\\"\" src/index.ts --outfile=bin/pdf-analyzer-windows-x64.exe"
29
+ },
30
+ "dependencies": {
31
+ "@google/genai": "^1.37.0",
32
+ "@modelcontextprotocol/sdk": "^1.25.2",
33
+ "zod": "^4.1.12"
34
+ },
35
+ "devDependencies": {
36
+ "@eslint/js": "^9.39.2",
37
+ "@types/node": "^22.15.21",
38
+ "eslint": "^9.18.0",
39
+ "eslint-plugin-jsdoc": "^50.6.17",
40
+ "prettier": "^3.8.1",
41
+ "tsx": "^4.19.4",
42
+ "typescript": "^5.7.3",
43
+ "typescript-eslint": "^8.32.1",
44
+ "vitest": "^3.2.4"
45
+ },
46
+ "engines": {
47
+ "node": ">=20.0.0"
48
+ },
49
+ "license": "Apache-2.0",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/IntelligentElectron/pdf-analyzer.git"
53
+ },
54
+ "keywords": [
55
+ "mcp",
56
+ "pdf",
57
+ "gemini",
58
+ "ai",
59
+ "document-analysis"
60
+ ]
61
+ }