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

267 lines (266 loc) 11.5 kB
import { search } from './gdrive_search.js'; import { analyzePDF } from './gdrive_analyze_pdf.js'; import { readFile } from './gdrive_read_file.js'; import { gdrive_read_excel } from './gdrive_read_excel.js'; export const schema = { name: 'gdrive_intelligent_analysis', description: 'Intelligently find and analyze documents based on natural language queries. Handles complex analysis requests with automatic fallbacks and context synthesis.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Natural language query describing what you want to analyze (e.g., "analyze my business plan", "find financial projections", "summarize meeting notes")' }, analysisType: { type: 'string', description: 'Type of analysis needed', enum: ['comprehensive', 'summary', 'financial', 'strategic', 'technical'] }, includeRelatedDocs: { type: 'boolean', description: 'Whether to include related documents in the analysis (default: true)' }, maxDocuments: { type: 'number', description: 'Maximum number of documents to analyze (default: 5)' } }, required: ['query'] } }; export async function gdrive_intelligent_analysis(args) { try { const maxDocs = args.maxDocuments || 5; const includeRelated = args.includeRelatedDocs !== false; const analysisType = args.analysisType || 'comprehensive'; // Step 1: Intelligent document discovery const documents = await intelligentDocumentSearch(args.query, maxDocs); if (documents.length === 0) { return { content: [{ type: 'text', text: `🔍 No documents found matching "${args.query}". Try refining your search terms or check if the documents exist in your Google Drive.` }], isError: false }; } // Step 2: Analyze each document with progressive fallbacks const analyses = []; for (const doc of documents) { const analysis = await analyzeDocumentWithFallbacks(doc, analysisType, args.query); analyses.push(analysis); } // Step 3: Synthesize comprehensive response const synthesis = await synthesizeAnalysis(analyses, args.query, analysisType); return { content: [{ type: 'text', text: synthesis }], isError: false }; } catch (error) { return { content: [{ type: 'text', text: `Error during intelligent analysis: ${error.message}` }], isError: true }; } } async function intelligentDocumentSearch(query, maxDocs) { const searchStrategies = [ // Strategy 1: Exact query match { query, fileType: undefined }, // Strategy 2: Business document types ...(query.toLowerCase().includes('business') || query.toLowerCase().includes('plan') ? [ { query: 'business plan', fileType: 'pdf' }, { query: 'business', fileType: 'document' } ] : []), // Strategy 3: Financial document types ...(query.toLowerCase().includes('financial') || query.toLowerCase().includes('budget') ? [ { query: 'financial', fileType: 'spreadsheet' }, { query: 'budget', fileType: 'spreadsheet' } ] : []), // Strategy 4: Meeting and notes ...(query.toLowerCase().includes('meeting') || query.toLowerCase().includes('notes') ? [ { query: 'meeting', fileType: 'document' }, { query: 'notes', fileType: 'document' } ] : []), ]; const allDocuments = new Map(); for (const strategy of searchStrategies) { try { const searchResult = await search({ query: strategy.query, fileType: strategy.fileType, pageSize: maxDocs }); // Parse search results and add to collection const content = searchResult.content[0]?.text || ''; const lines = content.split('\n').slice(1); // Skip header for (const line of lines) { if (line.trim() && !line.includes('More results')) { const fileId = line.split(' ')[0]; const fileName = line.split(' ').slice(1).join(' ').split(' - ')[0]; if (fileId && fileName && !allDocuments.has(fileId)) { allDocuments.set(fileId, { fileId, fileName: fileName.trim(), relevanceScore: calculateRelevance(fileName, query) }); } } } } catch (error) { // Continue with other strategies if one fails continue; } } // Sort by relevance and return top results return Array.from(allDocuments.values()) .sort((a, b) => b.relevanceScore - a.relevanceScore) .slice(0, maxDocs); } function calculateRelevance(fileName, query) { const queryLower = query.toLowerCase(); const fileNameLower = fileName.toLowerCase(); let score = 0; // Exact match gets highest score if (fileNameLower.includes(queryLower)) score += 10; // Individual word matches const queryWords = queryLower.split(' '); for (const word of queryWords) { if (fileNameLower.includes(word)) score += 2; } // Boost for business document types if (fileNameLower.includes('business') || fileNameLower.includes('plan')) score += 5; if (fileNameLower.includes('financial') || fileNameLower.includes('budget')) score += 5; if (fileNameLower.includes('proposal') || fileNameLower.includes('contract')) score += 3; return score; } async function analyzeDocumentWithFallbacks(doc, analysisType, query) { const baseAnalysis = { fileId: doc.fileId, fileName: doc.fileName, analysisResult: '', analysisMethod: '', success: false }; // Strategy 1: AI PDF Analysis (if PDF) if (doc.fileName.toLowerCase().includes('.pdf')) { try { const pdfAnalysis = await analyzePDF({ fileId: doc.fileId, prompt: `Provide a ${analysisType} analysis of this document focusing on: ${query}. Include key insights, numbers, and strategic information.` }); if (!pdfAnalysis.isError) { return { ...baseAnalysis, analysisResult: pdfAnalysis.content[0]?.text || 'Analysis completed', analysisMethod: 'AI PDF Analysis', success: true }; } } catch (error) { // Continue to next strategy } } // Strategy 2: Excel/Spreadsheet Analysis if (doc.fileName.toLowerCase().includes('spreadsheet') || doc.fileName.toLowerCase().includes('.xlsx') || doc.fileName.toLowerCase().includes('financial')) { try { const excelAnalysis = await gdrive_read_excel({ fileId: doc.fileId }); if (!excelAnalysis.isError) { const content = excelAnalysis.content[0]?.text || ''; return { ...baseAnalysis, analysisResult: `## Spreadsheet Analysis: ${doc.fileName}\n\n${content}\n\n**Analysis**: This spreadsheet contains structured data relevant to your query about "${query}".`, analysisMethod: 'Structured Data Analysis', success: true }; } } catch (error) { // Continue to next strategy } } // Strategy 3: Document Content Analysis (with chunking for large docs) try { const docAnalysis = await readFile({ fileId: doc.fileId }); if (!docAnalysis.isError) { const content = docAnalysis.content[0]?.text || ''; const truncatedContent = content.length > 5000 ? content.substring(0, 5000) + '\n\n[Content truncated for analysis...]' : content; return { ...baseAnalysis, analysisResult: `## Document Analysis: ${doc.fileName}\n\n${truncatedContent}\n\n**Context**: This document contains information relevant to "${query}".`, analysisMethod: 'Direct Content Reading', success: true }; } } catch (error) { // Final fallback } // Strategy 4: Metadata-only analysis return { ...baseAnalysis, analysisResult: `## Document Found: ${doc.fileName}\n\n**Status**: Document identified but content analysis failed. File may require manual review or different access permissions.`, analysisMethod: 'Metadata Only', success: false }; } async function synthesizeAnalysis(analyses, originalQuery, analysisType) { const successfulAnalyses = analyses.filter(a => a.success); const failedAnalyses = analyses.filter(a => !a.success); let synthesis = `# 🎯 Intelligent Analysis Results\n\n`; synthesis += `**Query**: "${originalQuery}"\n`; synthesis += `**Analysis Type**: ${analysisType}\n`; synthesis += `**Documents Found**: ${analyses.length}\n`; synthesis += `**Successfully Analyzed**: ${successfulAnalyses.length}\n\n`; if (successfulAnalyses.length > 0) { synthesis += `## 📊 Comprehensive Analysis\n\n`; for (const analysis of successfulAnalyses) { synthesis += `### 📄 ${analysis.fileName}\n`; synthesis += `**Method**: ${analysis.analysisMethod}\n\n`; synthesis += `${analysis.analysisResult}\n\n`; synthesis += `---\n\n`; } // Add synthesis summary synthesis += `## 🔍 Key Insights Summary\n\n`; synthesis += `Based on the analysis of ${successfulAnalyses.length} document(s):\n\n`; if (analysisType === 'comprehensive' || analysisType === 'strategic') { synthesis += `- **Strategic Overview**: Multiple documents analyzed providing comprehensive coverage of "${originalQuery}"\n`; synthesis += `- **Data Sources**: Combined insights from ${successfulAnalyses.map(a => a.analysisMethod).join(', ')}\n`; synthesis += `- **Reliability**: Analysis based on actual document content with verified data\n\n`; } synthesis += `💡 **Recommendation**: Review the detailed analysis above for specific insights related to your query.\n\n`; } if (failedAnalyses.length > 0) { synthesis += `## ⚠️ Documents Requiring Manual Review\n\n`; for (const failed of failedAnalyses) { synthesis += `- **${failed.fileName}**: ${failed.analysisResult}\n`; } synthesis += `\n`; } synthesis += `---\n`; synthesis += `*Analysis completed using Google Drive MCP Intelligent Analysis*`; return synthesis; }