mcp-google-drive-server

andresfrei/mcp-google-drive-server

3.2

If you are the rightful owner of mcp-google-drive-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

The Google Drive MCP Server is designed to manage multiple Google Drive accounts with read-only access, providing a robust and secure environment for file operations.

Tools
6
Resources
0
Prompts
0

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

  1. Crear proyecto en Google Cloud Console
  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:

{
  "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 descriptivo
  • description (string, opcional): Descripción de la cuenta
  • serviceAccountPath (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í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:

{
  "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:

{
  "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:

{
  "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:

{
  "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):

// 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:

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
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_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:

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/:

  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:

📊 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 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

# 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_KEY esté 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=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:
    deploy:
      resources:
        limits:
          memory: 256M