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
# 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
# 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:
# 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
- Crear proyecto en Google Cloud Console
- Habilitar Google Drive API
- Crear Service Account y descargar JSON
- Compartir carpetas/archivos de Drive con el email del Service Account
- Guardar JSON en
credentials/
2. Archivo de Configuración
El servidor usa drives-config.json para gestionar cuentas:
{
"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
# 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:
[
{
"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 descriptivodescription(string, opcional): Descripción de la cuentaserviceAccountPath(string, requerido): Ruta al archivo JSON de Service Account
Ejemplo:
{
"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íficamodifiedAfter(string, opcional): Fecha ISO 8601modifiedBefore(string, opcional): Fecha ISO 8601mimeType(string, opcional): Tipo MIME específicopageSize(number, opcional): Límite de resultados (default: 100)
Respuesta:
{
"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 archivodriveId(string, opcional): ID del Drive
Respuesta:
{
"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 buscarquery(string, requerido): Texto a buscar en nombres de archivo
Respuesta:
{
"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 iniciardriveId(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:00o2024-10-17T08:00:00Zpara UTC)mimeType(string, opcional): Filtrar por tipo MIME específico (ej:application/vnd.google-apps.documentpara Google Docs,application/pdfpara PDFs)
Respuesta:
{
"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
maxDepthpreviene 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):
// 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
GET http://localhost:3001/health
# Respuesta
{
"status": "healthy",
"timestamp": "2024-10-16T10:30:00.000Z"
}
Conexión MCP (StreamableHTTP)
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:
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:
// 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
Características del Cliente
- ✅ CORS habilitado: Funciona desde cualquier dominio
- ✅ API Key dual: Via query parameter
?apiKey=...(recomendado) o headerX-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
MCP_API_KEY=tu_api_key_super_secreto_aqui
2. Enviar desde cliente:
// 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_KEYNO está configurado → Acceso libre (desarrollo) - 🔒 Si
MCP_API_KEYestá configurado → Requiere headerX-API-Key
Seguridad en Producción (VPS)
⚠️ Importante: Este servidor usa HTTP sin cifrado. Para producción:
- Reverse Proxy con SSL (nginx/traefik)
- Firewall: Restringir acceso por IP
- Rate Limiting: Prevenir abuso
- API Key: Habilitar autenticación
Ejemplo nginx con SSL:
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:
// 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)
# 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
# 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
# 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
# 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/:
- test-mcp-client.ts: Conexión general y herramientas básicas
- test-recursive.ts: Validación de listado recursivo (166 items en estructura COMNET)
- test-drive.ts: Pruebas directas con Google Drive API
📚 Ver documentación completa: tests/README.md
📊 Monitoreo
# 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
// 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
// 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 Docsapplication/vnd.google-apps.spreadsheet- Google Sheetsapplication/vnd.google-apps.presentation- Google Slides
Archivos de Texto
text/plain- Texto planotext/markdown- Markdown
Formatos de Exportación
text/plain- Docs exportadostext/csv- Sheets exportados
🤝 Contribuir
- Fork el repositorio
- Crea una rama para tu feature (
git checkout -b feature/amazing-feature) - Commit tus cambios (
git commit -m 'Add amazing feature') - Push a la rama (
git push origin feature/amazing-feature) - Abre un Pull Request
📄 Licencia
Este proyecto está bajo licencia MIT.
🆘 Troubleshooting
Servidor no inicia
# 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:
-v $(pwd)/keys:/app/keys:ro
Error: "Unauthorized: Invalid API key"
- Verifica que
MCP_API_KEYesté configurado en el servidor - El API key debe enviarse en el header
X-API-Key(no en_meta.apiKey) - Formato correcto:
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)
# 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=debugpara 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:deploy: resources: limits: memory: 256M