Skip to main content
Glama

Google Drive MCP Server

by andresfrei
README.md22.2 kB
# Google Drive MCP Server Servidor MCP (Model Context Protocol) modernizado para gestión de múltiples cuentas de Google Drive con acceso de solo lectura. ## 🎯 Características - ✅ **StreamableHTTP Transport**: Arquitectura stateless HTTP moderna (reemplaza SSE deprecado) - ✅ **MCP SDK v1.19.1**: Usando McpServer high-level API con validación automática - ✅ **Multi-cliente**: Múltiples clientes pueden conectarse simultáneamente (stateless) - ✅ **Puerto configurable**: Ideal para VPS con múltiples servicios MCP - ✅ **Multi-cuenta**: Gestiona múltiples cuentas de Google Drive simultáneamente - ✅ **7 Herramientas MCP**: Incluyendo listado recursivo de carpetas - ✅ **Arquitectura modular**: Tools organizadas en módulos independientes - ✅ **Autenticación segura**: Query parameter o header API key - ✅ **Operaciones de archivos**: Listar, buscar, recursivo y obtener contenido - ✅ **Soporta Google Workspace**: Docs, Sheets, Slides - ✅ **Archivos de texto**: TXT, Markdown - ✅ **Logging estructurado**: Winston con múltiples niveles - ✅ **Validación robusta**: Zod schemas en todas las herramientas - ✅ **Path alias @/**: Imports absolutos desde `src/` - ✅ **Oxlint + Prettier**: Linting ultrarrápido y formato consistente - ✅ **Docker-ready**: Configuración lista para deployment en VPS ## 📁 Estructura del Proyecto ``` src/ config/ config-loader.ts # Gestión de configuración de Drives types.ts # Tipos y esquemas Zod services/ drive-service.ts # Servicio de Google Drive API (incluye recursivo) utils/ logger.ts # Sistema de logging con Winston mcp/ auth.ts # Autenticación de requests MCP server.ts # Configuración del servidor MCP (33 líneas) tools/ # 🆕 Herramientas modularizadas index.ts # Exportador central list-drives.ts # Listar cuentas configuradas add-drive.ts # Agregar cuenta remove-drive.ts # Eliminar cuenta list-files.ts # Listar archivos con filtros list-files-recursive.ts # 🆕 Listado recursivo get-file-content.ts # Obtener contenido search-files.ts # Buscar por nombre index.ts # Entry point del servidor tests/ test-mcp-client.ts # Test de conexión general test-recursive.ts # Test de listado recursivo test-drive.ts # Test de API de Drive README.md # Documentación de tests ``` ## 🚀 Instalación y Deployment ### Desarrollo Local ```bash # Clonar repositorio git clone https://github.com/andresfrei/mcp-google-drive-server.git cd mcp-google-drive-server # Instalar dependencias pnpm install # Configurar environment cp .env.example .env nano .env # Desarrollo (con hot reload) pnpm dev # El servidor estará disponible en http://localhost:3001 ``` ### Producción con Docker ```bash # Build y run con docker-compose docker-compose up -d # Ver logs docker-compose logs -f mcp-drive # Detener docker-compose down ``` ### VPS con Múltiples MCPs Para ejecutar varios servidores MCP en el mismo VPS, configura diferentes puertos: ```bash # MCP Drive en puerto 3001 MCP_DRIVE_PORT=3001 docker-compose up -d # En otro directorio, otro MCP en puerto 3002 cd ../otro-mcp && MCP_OTRO_PORT=3002 docker-compose up -d ``` ## ⚙️ Configuración ### 1. Service Account de Google 1. Crear proyecto en [Google Cloud Console](https://console.cloud.google.com) 2. Habilitar Google Drive API 3. Crear Service Account y descargar JSON 4. Compartir carpetas/archivos de Drive con el email del Service Account 5. Guardar JSON en `credentials/` ### 2. Archivo de Configuración El servidor usa `drives-config.json` para gestionar cuentas: ```json { "drives": { "personal": { "name": "Drive Personal", "description": "Mi Drive personal", "serviceAccountPath": "./credentials/personal-sa.json" }, "work": { "name": "Drive Trabajo", "description": "Cuenta corporativa", "serviceAccountPath": "./credentials/work-sa.json" } } } ``` **Nota**: El archivo se crea automáticamente vacío si no existe. Usa la herramienta `add_drive` para agregar cuentas. ### 3. Variables de Entorno ```env # Configuración del servidor HTTP MCP_DRIVE_PORT=3001 # Puerto del servidor (default: 3001) MCP_DRIVE_HOST=0.0.0.0 # Host de escucha (0.0.0.0 para Docker) # Configuración de Drives DRIVES_CONFIG_PATH=./drives-config.json # Nivel de logging (debug, info, warn, error) LOG_LEVEL=info # API key para autenticación de requests MCP (opcional) MCP_API_KEY=tu_api_key_seguro ``` ## 🛠️ Herramientas MCP El servidor expone **7 herramientas** vía protocolo MCP: ### Gestión de Drives #### `list_drives` Lista todas las cuentas de Google Drive configuradas. **Parámetros**: Ninguno **Respuesta**: ```json [ { "id": "personal", "name": "Drive Personal", "description": "Mi Drive personal" } ] ``` #### `add_drive` Agrega una nueva cuenta de Google Drive a la configuración. **Parámetros**: - `driveId` (string, requerido): ID único (ej: 'personal', 'work') - `name` (string, requerido): Nombre descriptivo - `description` (string, opcional): Descripción de la cuenta - `serviceAccountPath` (string, requerido): Ruta al archivo JSON de Service Account **Ejemplo**: ```json { "driveId": "personal", "name": "Drive Personal", "description": "Mi cuenta personal", "serviceAccountPath": "./credentials/personal-sa.json" } ``` #### `remove_drive` Elimina una cuenta de Drive de la configuración. **Parámetros**: - `driveId` (string, requerido): ID del Drive a eliminar ### Operaciones de Archivos #### `list_files` Lista archivos de Google Drive con filtros opcionales. **Parámetros**: - `driveId` (string, opcional): ID del Drive (usa el primero si se omite) - `folderId` (string, opcional): ID de carpeta específica - `modifiedAfter` (string, opcional): Fecha ISO 8601 - `modifiedBefore` (string, opcional): Fecha ISO 8601 - `mimeType` (string, opcional): Tipo MIME específico - `pageSize` (number, opcional): Límite de resultados (default: 100) **Respuesta**: ```json { "totalFiles": 5, "files": [ { "id": "1abc...", "name": "Documento.docx", "mimeType": "application/vnd.google-apps.document", "modifiedTime": "2024-10-16T10:30:00Z", "size": "12345", "webViewLink": "https://drive.google.com/...", "parents": ["0BwwA4oUTeiV1TGRPeTVjaWRDY1E"] } ] } ``` #### `get_file_content` Obtiene el contenido de un archivo de Google Drive. **Soporta**: - Google Docs → exporta como texto plano - Google Sheets → exporta como CSV - Archivos de texto (.txt, .md) → descarga directa **Parámetros**: - `fileId` (string, requerido): ID del archivo - `driveId` (string, opcional): ID del Drive **Respuesta**: ```json { "fileId": "1abc...", "fileName": "Documento.txt", "mimeType": "text/plain", "content": "Contenido del archivo...", "extractedAt": "2024-10-16T10:30:00Z" } ``` #### `search_files` Busca archivos por nombre en un Drive específico. **Parámetros**: - `driveId` (string, requerido): ID del Drive donde buscar - `query` (string, requerido): Texto a buscar en nombres de archivo **Respuesta**: ```json { "totalFiles": 3, "files": [ { "id": "1abc...", "name": "Presupuesto 2024.xlsx", "mimeType": "application/vnd.google-apps.spreadsheet", "modifiedTime": "2024-10-16T10:30:00Z", "webViewLink": "https://drive.google.com/..." } ] } ``` #### `list_files_recursive` 🆕 Lista recursivamente todos los archivos y subcarpetas dentro de una carpeta con **filtros opcionales por fecha y tipo**, ideal para escaneos diarios de documentos modificados. **Parámetros**: - `folderId` (string, requerido): ID de la carpeta raíz desde donde iniciar - `driveId` (string, opcional): ID del Drive (usa el primero si se omite) - `maxDepth` (number, opcional): Profundidad máxima de recursión (default: 10) - `modifiedAfter` (string, opcional): Filtrar archivos modificados después de esta fecha (formato RFC 3339: `2024-10-17T08:00:00` o `2024-10-17T08:00:00Z` para UTC) - `mimeType` (string, opcional): Filtrar por tipo MIME específico (ej: `application/vnd.google-apps.document` para Google Docs, `application/pdf` para PDFs) **Respuesta**: ```json { "totalItems": 42, "filters": { "modifiedAfter": "2024-10-17T08:00:00", "mimeType": "application/vnd.google-apps.document" }, "items": [ { "id": "1abc...", "name": "Reporte Mensual.docx", "mimeType": "application/vnd.google-apps.document", "modifiedTime": "2024-10-17T10:30:00Z", "size": "12345", "webViewLink": "https://drive.google.com/...", "parents": ["0BwwA4oUTeiV1TGRPeTVjaWRDY1E"], "depth": 0, "path": "/CONTABILIDAD/Reporte Mensual.docx" }, { "id": "2def...", "name": "Presupuesto.docx", "mimeType": "application/vnd.google-apps.document", "modifiedTime": "2024-10-17T14:20:00Z", "size": "470883", "webViewLink": "https://drive.google.com/...", "parents": ["1abc..."], "depth": 2, "path": "/CONTABILIDAD/DOCUMENTOS/Presupuesto.docx" } ] } ``` **Características**: - ✅ **Filtros opcionales**: Por fecha de modificación y tipo MIME - ✅ **Exploración completa**: Las carpetas siempre se recorren, filtros aplican solo a archivos - ✅ **Búsqueda recursiva**: DFS (Depth-First Search) en toda la jerarquía - ✅ **Metadatos completos**: ID, nombre, ruta completa, fecha, tipo, tamaño - ✅ **Campo `depth`**: Nivel de anidación (0 = raíz) - ✅ **Campo `path`**: Ruta completa desde carpeta inicial - ✅ **Protección**: Límite `maxDepth` previene recursión infinita - ✅ **Optimizado**: Doble query para carpetas + archivos filtrados - ✅ **Google Drive API**: Límite de 1000 items por nivel **Caso de uso típico** (escaneo diario): ```typescript // Obtener todos los Google Docs modificados hoy después de las 8 AM const result = await client.callTool({ name: "list_files_recursive", arguments: { folderId: "carpeta-raiz-id", modifiedAfter: "2024-10-17T08:00:00", mimeType: "application/vnd.google-apps.document", maxDepth: 5, }, }); // Resultado: Solo Docs modificados hoy, con rutas completas para procesamiento LLM ``` ## 🌐 Endpoints HTTP El servidor expone los siguientes endpoints: ### Health Check ```bash GET http://localhost:3001/health # Respuesta { "status": "healthy", "timestamp": "2024-10-16T10:30:00.000Z" } ``` ### Conexión MCP (StreamableHTTP) ```bash GET http://localhost:3001/mcp?apiKey=tu-api-key-aqui # Establece conexión StreamableHTTP para comunicación MCP # Autenticación vía query parameter (recomendado) o header X-API-Key ``` **Nota sobre SSE**: El transporte SSE (Server-Sent Events) está deprecado en MCP SDK v1.19+. Use StreamableHTTP. ## 🔌 Conectar desde Cliente ### Opción 1: StreamableHTTP (Recomendado) 🆕 **Transport moderno stateless para cualquier aplicación:** ```typescript import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; // Conectar con autenticación por query parameter const transport = new StreamableHTTPClientTransport( new URL("http://localhost:3001/mcp?apiKey=tu-api-key-aqui") ); const client = new Client( { name: "my-app", version: "1.0.0", }, { capabilities: {}, } ); await client.connect(transport); // Listar herramientas disponibles (7 tools) const tools = await client.listTools(); console.log(`Tools disponibles: ${tools.tools.length}`); // Ejecutar herramienta const result = await client.callTool({ name: "list_drives", arguments: {}, }); console.log(result); // Listar recursivamente una carpeta const recursiveResult = await client.callTool({ name: "list_files_recursive", arguments: { folderId: "1AdO2achPP4Kgz4AGmKw2C4wKF49Ce-KC", driveId: "comnet-manuales", maxDepth: 5, }, }); await client.close(); ``` ### Opción 2: Cliente NestJS (Orquestador) **Recomendado para aplicaciones que necesitan múltiples MCPs:** ```typescript // src/mcp/mcp.config.ts (NestJS) import { registerAs } from "@nestjs/config"; export default registerAs("mcp", () => ({ servers: { googleDrive: { name: "google-drive-local", transport: { type: "streamableHttp", // 🆕 Cambio de "sse" a "streamableHttp" // Desarrollo: http://localhost:3001/mcp?apiKey=... // Producción Docker: http://mcp-drive:3001/mcp?apiKey=... url: process.env.MCP_DRIVE_URL || `http://localhost:3001/mcp?apiKey=${process.env.MCP_DRIVE_API_KEY}`, }, timeout: 30000, }, }, })); // src/mcp/mcp.service.ts import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const transport = new StreamableHTTPClientTransport( new URL(config.transport.url) ); await client.connect(transport); ``` 📚 **Ver guía completa**: [`docs/NESTJS-CLIENT.md`](./docs/NESTJS-CLIENT.md) ### Características del Cliente - ✅ **CORS habilitado**: Funciona desde cualquier dominio - ✅ **API Key dual**: Via query parameter `?apiKey=...` (recomendado) o header `X-API-Key` - ✅ **Stateless**: No mantiene sesiones, ideal para Docker/Kubernetes - ✅ **Multi-cliente**: Múltiples clientes pueden conectarse simultáneamente - ✅ **Retry automático**: SDK maneja reconexiones ## 🔒 Seguridad - **Solo lectura**: Service Account con scope `drive.readonly` - **Autenticación opcional**: Soporta API key via header `X-API-Key` - **CORS configurado**: Permite conexiones desde clientes externos - **Validación robusta**: Esquemas Zod para todos los inputs - **Logging seguro**: No expone credenciales en logs ### Autenticación con API Key **1. Configurar API key en servidor:** ```env # .env MCP_API_KEY=tu_api_key_super_secreto_aqui ``` **2. Enviar desde cliente:** ```typescript // Opción 1: Query parameter (recomendado para StreamableHTTP) const transport = new StreamableHTTPClientTransport( new URL("http://localhost:3001/mcp?apiKey=tu_api_key_super_secreto_aqui") ); // Opción 2: Header (alternativa) const transport = new StreamableHTTPClientTransport( new URL("http://localhost:3001/mcp"), { headers: { "X-API-Key": "tu_api_key_super_secreto_aqui", }, } ); ``` **3. Comportamiento:** - ✅ Si `MCP_API_KEY` NO está configurado → **Acceso libre** (desarrollo) - 🔒 Si `MCP_API_KEY` está configurado → **Requiere header** `X-API-Key` ### Seguridad en Producción (VPS) ⚠️ **Importante**: Este servidor usa HTTP sin cifrado. Para producción: 1. **Reverse Proxy con SSL** (nginx/traefik) 2. **Firewall**: Restringir acceso por IP 3. **Rate Limiting**: Prevenir abuso 4. **API Key**: Habilitar autenticación Ejemplo nginx con SSL: ```nginx upstream mcp_drive { server localhost:3001; } server { listen 443 ssl http2; server_name mcp-drive.tudominio.com; ssl_certificate /etc/letsencrypt/live/tudominio.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/tudominio.com/privkey.pem; # Solo permitir IP del orquestador allow 192.168.1.100; deny all; location / { proxy_pass http://mcp_drive; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-API-Key $http_x_api_key; # Pasar API key proxy_buffering off; proxy_cache off; proxy_read_timeout 86400; } } ``` ### CORS en Producción Por defecto, CORS permite cualquier origen (`*`). Para producción, restringe dominios: ```typescript // src/index.ts app.use((req, res, next) => { res.setHeader( "Access-Control-Allow-Origin", "https://tu-app.com" // Solo tu dominio ); // ... resto }); ``` ## 📊 Logging El servidor genera logs estructurados en JSON: - **Consola**: Formato colorizado para desarrollo - **error.log**: Solo errores críticos - **combined.log**: Todos los niveles Configurar nivel via `LOG_LEVEL` env variable. ## 🐳 Docker ### Docker Compose (Recomendado) ```bash # Levantar servicio docker-compose up -d # Ver logs en tiempo real docker-compose logs -f # Detener servicio docker-compose down # Reconstruir después de cambios docker-compose up -d --build ``` ### Docker Manual ```bash # Build docker build -t mcp-drive-server . # Run docker run -d \ --name mcp-drive \ -p 3001:3000 \ -e PORT=3000 \ -e HOST=0.0.0.0 \ -e LOG_LEVEL=info \ -v $(pwd)/drives-config.json:/app/config/drives-config.json \ -v $(pwd)/keys:/app/keys:ro \ -v $(pwd)/logs:/app/logs \ mcp-drive-server # Ver logs docker logs -f mcp-drive ``` ### Múltiples Instancias en VPS ```bash # Instancia 1 - Drive Personal (puerto 3001) docker run -d --name mcp-drive-personal \ -p 3001:3000 \ -v $(pwd)/config-personal:/app/config \ mcp-drive-server # Instancia 2 - Drive Trabajo (puerto 3002) docker run -d --name mcp-drive-work \ -p 3002:3000 \ -v $(pwd)/config-work:/app/config \ mcp-drive-server ``` ## 🧪 Desarrollo y Testing ```bash # Desarrollo local con hot reload pnpm dev # Servidor en http://localhost:3001 # Linting con oxlint (ultrarrápido) pnpm lint pnpm lint:fix # Formateo con Prettier pnpm format pnpm format:check # Verificación completa (lint + format) pnpm check # Build para producción pnpm build # Ejecutar versión compilada pnpm start # Tests pnpm test:client # Test de conexión y tools básicas pnpm test:recursive # Test de listado recursivo pnpm test:drive # Test de Google Drive API pnpm test:all # Ejecutar todos los tests # Ver logs de Docker docker-compose logs -f # Reiniciar contenedor docker-compose restart # Reconstruir imagen docker-compose up -d --build ``` ### Tests Disponibles El proyecto incluye 3 tests completos en la carpeta `tests/`: 1. **test-mcp-client.ts**: Conexión general y herramientas básicas 2. **test-recursive.ts**: Validación de listado recursivo (166 items en estructura COMNET) 3. **test-drive.ts**: Pruebas directas con Google Drive API 📚 **Ver documentación completa**: [`tests/README.md`](./tests/README.md) ## 📊 Monitoreo ```bash # Health check curl http://localhost:3001/health # Test de conexión MCP pnpm test:client # Logs en tiempo real docker-compose logs -f mcp-drive # Stats de recursos docker stats mcp-drive # Inspeccionar contenedor docker inspect mcp-drive ``` ## 🏗️ Arquitectura Técnica ### Transport Layer - **StreamableHTTP**: Transport moderno stateless (HTTP-based) - **Deprecado**: SSE (Server-Sent Events) - removido en v2.0.0 - **Ventajas**: Sin estado, escalable, compatible con proxies/load balancers ### MCP SDK - **Version**: 1.19.1 - **API**: McpServer high-level (reemplaza Server low-level) - **Validación**: Zod schemas automáticos en inputSchema/outputSchema - **Registro**: `server.registerTool(name, config, handler)` ### Herramientas Modularizadas ```typescript // src/mcp/tools/list-drives.ts export const listDrivesTool = { name: "list_drives", config: { title, description, inputSchema, outputSchema }, handler: async (params) => { /* ... */ }, }; // src/mcp/server.ts (33 líneas) const toolList = Object.values(tools); toolList.forEach((tool) => { server.registerTool(tool.name, tool.config, tool.handler); }); ``` ### Path Aliases ```typescript // tsconfig.json "baseUrl": ".", "paths": { "@/*": ["src/*"] } // En código import { logger } from "@/utils/logger.js"; import { googleDriveService } from "@/services/drive-service.js"; ``` **Nota**: Los imports usan extensión `.js` aunque los archivos sean `.ts` (requisito de TypeScript ESM con `"module": "NodeNext"`) ## 📝 Tipos MIME Soportados ### Google Workspace - `application/vnd.google-apps.document` - Google Docs - `application/vnd.google-apps.spreadsheet` - Google Sheets - `application/vnd.google-apps.presentation` - Google Slides ### Archivos de Texto - `text/plain` - Texto plano - `text/markdown` - Markdown ### Formatos de Exportación - `text/plain` - Docs exportados - `text/csv` - Sheets exportados ## 🤝 Contribuir 1. Fork el repositorio 2. Crea una rama para tu feature (`git checkout -b feature/amazing-feature`) 3. Commit tus cambios (`git commit -m 'Add amazing feature'`) 4. Push a la rama (`git push origin feature/amazing-feature`) 5. Abre un Pull Request ## 📄 Licencia Este proyecto está bajo licencia MIT. ## 🆘 Troubleshooting ### Servidor no inicia ```bash # Verificar que el puerto no esté en uso netstat -ano | findstr :3000 # Windows lsof -i :3000 # Linux/Mac # Cambiar puerto si está ocupado PORT=3001 pnpm dev ``` ### Error: "Service account file not found" - Verifica que el archivo JSON existe en la ruta especificada - En Docker, asegúrate de montar el volumen correctamente: ```bash -v $(pwd)/keys:/app/keys:ro ``` ### Error: "Unauthorized: Invalid API key" - Verifica que `MCP_API_KEY` esté configurado en el servidor - El API key debe enviarse en el header `X-API-Key` (no en `_meta.apiKey`) - Formato correcto: ```typescript headers: { "X-API-Key": "tu-api-key" } ``` ### No se pueden leer archivos - Verifica que el Service Account tenga acceso (compartido con su email) - Confirma que el scope sea `drive.readonly` - Revisa permisos de la carpeta/archivo en Drive ### Cliente no puede conectar (VPS) ```bash # Verificar que el puerto esté expuesto docker ps | grep mcp-drive # Verificar firewall sudo ufw status sudo ufw allow 3001/tcp # Test de conectividad curl http://vps-ip:3001/health ``` ### Logs no aparecen - Configura `LOG_LEVEL=debug` para ver más detalles - Verifica permisos de escritura en la carpeta del proyecto - En Docker: `docker-compose logs -f mcp-drive` ### Alto uso de memoria - Ajusta límites en `docker-compose.yml`: ```yaml deploy: resources: limits: memory: 256M ```

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/andresfrei/mcp-drive'

If you have feedback or need assistance with the MCP directory API, please join our Discord server