UNPKG

mcp-gdrive-enhanced-markov

Version:

MCP server for Google Drive and Sheets with write capabilities, AI-powered PDF analysis, SQLite document indexing, and advanced directory exploration tools

209 lines (208 loc) 7.55 kB
import fs from "fs"; import path from "path"; export const schema = { name: "local_analyze_pdf", description: "Analyze local PDF files using Gemini 2.5 Flash AI model", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Absolute path to the local PDF file to analyze", }, prompt: { type: "string", description: "Analysis instructions or questions about the PDF content", }, useThinking: { type: "boolean", description: "Use Gemini 2.5 Flash thinking mode for complex analysis (optional, default: false)", optional: true, }, maxReasoningTokens: { type: "number", description: "Maximum tokens for reasoning in thinking mode (optional, default: 5000)", optional: true, }, }, required: ["filePath", "prompt"], }, }; // OpenRouter configuration const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"; const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY; async function callOpenRouter(model, messages, maxReasoningTokens, retries = 3) { if (!OPENROUTER_API_KEY) { throw new Error("OPENROUTER_API_KEY environment variable is not set"); } const requestBody = { model, messages, max_tokens: 4096, temperature: 0.7, plugins: [ { id: "file-parser", pdf: { engine: "native" } } ] }; if (maxReasoningTokens && model.includes("thinking")) { requestBody.max_reasoning_tokens = maxReasoningTokens; } for (let attempt = 0; attempt < retries; attempt++) { try { const response = await fetch(`${OPENROUTER_BASE_URL}/chat/completions`, { method: "POST", headers: { "Authorization": `Bearer ${OPENROUTER_API_KEY}`, "Content-Type": "application/json", "HTTP-Referer": "https://github.com/markov-kernel/mcp-gdrive-enhanced", "X-Title": "Google Drive MCP PDF Analyzer", }, body: JSON.stringify(requestBody), }); if (!response.ok) { const error = await response.text(); console.error(`OpenRouter API error (attempt ${attempt + 1}/${retries}): ${response.status} - ${error}`); // If it's a 500 error and we have retries left, wait and retry if (response.status === 500 && attempt < retries - 1) { await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); continue; } throw new Error(`OpenRouter API error: ${response.status} - ${error}`); } const data = await response.json(); return data.choices[0].message.content; } catch (error) { if (attempt === retries - 1) throw error; console.error(`Request failed (attempt ${attempt + 1}/${retries}):`, error); await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); } } throw new Error("Failed to call OpenRouter API after all retries"); } export async function analyzeLocalPDF(args) { try { const validatedArgs = args; // Security validation - ensure absolute path if (!path.isAbsolute(validatedArgs.filePath)) { return { content: [ { type: "text", text: "Error: File path must be absolute", }, ], isError: true, }; } // Check if file exists if (!fs.existsSync(validatedArgs.filePath)) { return { content: [ { type: "text", text: `Error: File not found at path: ${validatedArgs.filePath}`, }, ], isError: true, }; } // Get file stats const stats = fs.statSync(validatedArgs.filePath); // Check if it's a file (not directory) if (!stats.isFile()) { return { content: [ { type: "text", text: `Error: Path is not a file: ${validatedArgs.filePath}`, }, ], isError: true, }; } // Check file extension const ext = path.extname(validatedArgs.filePath).toLowerCase(); if (ext !== '.pdf') { return { content: [ { type: "text", text: `Error: File is not a PDF. Extension: ${ext}`, }, ], isError: true, }; } // Check file size (limit to 20MB for API constraints) const MAX_PDF_SIZE = 20 * 1024 * 1024; // 20MB if (stats.size > MAX_PDF_SIZE) { return { content: [ { type: "text", text: `Error: PDF file size (${Math.round(stats.size / 1024 / 1024)}MB) exceeds 20MB limit`, }, ], isError: true, }; } // Read the PDF file const pdfBuffer = fs.readFileSync(validatedArgs.filePath); const pdfBase64 = pdfBuffer.toString("base64"); console.error(`PDF file size: ${Math.round(pdfBuffer.length / 1024)}KB`); // Choose the appropriate Gemini model const model = validatedArgs.useThinking ? "google/gemini-2.5-flash-preview-05-20:thinking" : "google/gemini-2.5-flash-preview-05-20"; const fileName = path.basename(validatedArgs.filePath); console.error(`Analyzing local PDF "${fileName}" with ${model}...`); // Call OpenRouter API const messages = [ { role: "user", content: [ { type: "text", text: validatedArgs.prompt, }, { type: "file", file: { filename: fileName, file_data: `data:application/pdf;base64,${pdfBase64}`, }, }, ], }, ]; const analysisResult = await callOpenRouter(model, messages, validatedArgs.maxReasoningTokens || 5000); return { content: [ { type: "text", text: `Analysis of "${fileName}":\n\n${analysisResult}`, }, ], isError: false, }; } catch (error) { console.error("Error analyzing local PDF:", error); return { content: [ { type: "text", text: `Error analyzing PDF: ${error.message || error}`, }, ], isError: true, }; } }