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
JavaScript
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;
}