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
162 lines (161 loc) • 6.4 kB
JavaScript
import { google } from "googleapis";
export const schema = {
name: "gdrive_search",
description: "Search for files in Google Drive with advanced filtering options",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query - can be file name or content search",
},
pageToken: {
type: "string",
description: "Token for the next page of results",
optional: true,
},
pageSize: {
type: "number",
description: "Number of results per page (max 100)",
optional: true,
},
fileType: {
type: "string",
description: "Filter by file type: 'spreadsheet', 'document', 'presentation', 'pdf', 'image', 'video', 'audio', 'folder'",
optional: true,
},
modifiedAfter: {
type: "string",
description: "Only return files modified after this date (ISO format: YYYY-MM-DD)",
optional: true,
},
modifiedBefore: {
type: "string",
description: "Only return files modified before this date (ISO format: YYYY-MM-DD)",
optional: true,
},
},
required: ["query"],
},
};
export async function search(args) {
const drive = google.drive("v3");
const userQuery = args.query.trim();
let searchQuery = "trashed = false";
// Helper function to map file types to mime types
const getFileTypeMimeTypes = (fileType) => {
switch (fileType.toLowerCase()) {
case 'spreadsheet':
return [
'application/vnd.google-apps.spreadsheet',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel'
];
case 'document':
return [
'application/vnd.google-apps.document',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/msword'
];
case 'presentation':
return [
'application/vnd.google-apps.presentation',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.ms-powerpoint'
];
case 'pdf':
return ['application/pdf'];
case 'image':
return ['image/'];
case 'video':
return ['video/'];
case 'audio':
return ['audio/'];
case 'folder':
return ['application/vnd.google-apps.folder'];
default:
return [];
}
};
// Build search query components
const queryParts = [];
// Add name/content search if provided
if (userQuery) {
if (userQuery.includes("=") || userQuery.includes(":") || userQuery.startsWith("mimeType") || userQuery.startsWith("trashed")) {
// If the query looks like a Drive API query, use it directly (but still apply other filters)
searchQuery = userQuery;
}
else {
// Search by name and full text
const escapedQuery = userQuery.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
queryParts.push(`(name contains '${escapedQuery}' or fullText contains '${escapedQuery}')`);
}
}
// Add file type filter
if (args.fileType) {
const mimeTypes = getFileTypeMimeTypes(args.fileType);
if (mimeTypes.length > 0) {
if (args.fileType.toLowerCase() === 'image' || args.fileType.toLowerCase() === 'video' || args.fileType.toLowerCase() === 'audio') {
// For media types, use startsWith
const mimeTypeQueries = mimeTypes.map(mime => `mimeType contains '${mime}'`);
queryParts.push(`(${mimeTypeQueries.join(' or ')})`);
}
else {
// For specific types, use exact match
const mimeTypeQueries = mimeTypes.map(mime => `mimeType = '${mime}'`);
queryParts.push(`(${mimeTypeQueries.join(' or ')})`);
}
}
}
// Add date filters
if (args.modifiedAfter) {
queryParts.push(`modifiedTime > '${args.modifiedAfter}'`);
}
if (args.modifiedBefore) {
queryParts.push(`modifiedTime < '${args.modifiedBefore}'`);
}
// Combine all query parts
if (queryParts.length > 0) {
if (searchQuery === "trashed = false") {
searchQuery = `${queryParts.join(' and ')} and trashed = false`;
}
else {
// If we already have a complex query, combine it carefully
searchQuery = `(${searchQuery}) and ${queryParts.join(' and ')}`;
}
}
const res = await drive.files.list({
q: searchQuery,
pageSize: args.pageSize || 10,
pageToken: args.pageToken,
orderBy: "modifiedTime desc",
fields: "nextPageToken, files(id, name, mimeType, modifiedTime, size, parents)",
});
const files = res.data.files || [];
// Format the file list with more detailed information
const fileList = files
.map((file) => {
const modifiedTime = file.modifiedTime ? new Date(file.modifiedTime).toLocaleDateString() : 'Unknown';
const size = file.size ? `${Math.round(parseInt(file.size) / 1024)} KB` : '';
return `${file.id} ${file.name} - ${modifiedTime} ${size} (${file.mimeType})`;
})
.join("\n");
let response = `Found ${files.length} files:\n${fileList}`;
// Add search query info for debugging
if (args.fileType || args.modifiedAfter || args.modifiedBefore) {
response += `\n\n**Search Query:** ${searchQuery}`;
}
// Add pagination info if there are more results
if (res.data.nextPageToken) {
response += `\n\nMore results available. Use pageToken: ${res.data.nextPageToken}`;
}
return {
content: [
{
type: "text",
text: response,
},
],
isError: false,
};
}