SILO-MCP
Servidor MCP que mueve archivos entre tu sistema de archivos y el almacenamiento en la nube.
Documentación
Silo MCP
Un servidor MCP autoalojado que mueve archivos entre tu sistema de archivos y el almacenamiento en la nube: nueve plataformas, una única superficie de herramientas. Sube, descarga, lista, busca, elimina, genera enlaces de uso compartido y copia archivos directamente de una nube a otra, sin darle a un LLM acceso directo al sistema de archivos ni un montón de credenciales específicas de cada plataforma que gestionar.
No es "local-first": los archivos viven en la nube y cada operación habla con una API remota del proveedor. Lo que permanece local es la parte que importa para la confianza: el servidor se ejecuta como un subproceso en tu máquina, tus credenciales están en el llavero del sistema operativo (nunca en la nube, en un archivo de configuración o en un argumento de llamada de herramienta), y el registro de auditoría de transferencias es un archivo SQLite local.
Almacenes de documentos: Dropbox · Google Drive · OneDrive · Box · Google Photos (solo subida) · Yandex Disk Almacenes de objetos: compatible con S3 (AWS S3, Cloudflare R2, MinIO) · Google Cloud Storage · Azure Blob Storage
¿Qué puedes hacer con Silo MCP?
- Copiar un archivo directamente de una nube a otra — "copia mi archivo
/photosde Dropbox a mi bucket de S3", "migra este archivo de Drive a OneDrive" — en una sola llamada de herramienta, sin descargar y luego subir manualmente. Lo único que una integración de almacenamiento de un solo proveedor estructuralmente no puede hacer. - Mover archivos dentro y fuera del almacenamiento en la nube — "sube
report.pdfa Dropbox", "descarganotes.txtde Google Drive para poder usarlo" — en las nueve plataformas con un mismo conjunto de herramientas. - Explorar y buscar tus almacenes de documentos por carpeta o consulta, y listar los buckets de almacenes de objetos por prefijo de clave.
- Generar enlaces compartibles a un archivo: URL prefirmadas/firmadas en los almacenes de objetos y Box (con caducidad), enlaces de uso compartido de la plataforma en el resto.
- Puente hacia otros servidores MCP — un
download_fileaquí produce una ruta local que puedes pasar directamente a otro servidor (por ejemplo, elmedia_pathsde un servidor de publicación en redes sociales). - Mantener un registro de auditoría local — cada subida, descarga, eliminación y enlace de uso compartido se registra en un registro SQLite local que puedes consultar con
list_transfers.
Todo se ejecuta a través de tu cliente MCP en lenguaje natural — consulta Ejemplos de flujos de trabajo.
Inicio rápido: pip install -e . → añade silo-mcp a la configuración de tu cliente MCP → silo-mcp-accounts add dropbox --label personal → pide a tu cliente que liste tus archivos de Dropbox. Pasos completos en Instalación y Añadir cuentas a continuación.
Contenido
- ¿Qué puedes hacer con Silo MCP?
- Por qué
- Arquitectura
- Requisitos
- Instalación
- Configurar tu cliente MCP
- Clientes compatibles
- Uso con Ollama
- Añadir cuentas
- Ejemplos de flujos de trabajo
- Herramientas
- Plataformas y herramientas compatibles
- Fricción de configuración y forma de las credenciales por plataforma
- Seguridad de rutas
- Notas específicas por plataforma
- Seguridad
- Seguridad
- Pruebas
- Solución de problemas
- Relacionados
- Licencia
Por qué
La mayoría de las configuraciones de "dale al LLM tu almacenamiento en la nube" caen en una de dos trampas: una integración de una sola plataforma que muere en cuanto cambias de proveedor, o un acceso local sin restricciones que permite que una conversación manipulada lea o suba cualquier cosa del disco. Silo MCP está construido contra ambas:
- Una superficie de herramientas, nueve plataformas.
upload_file,download_file,list_files,search_files,delete_file,create_share_linkse comportan igual independientemente de la plataforma a la que apuntes: los almacenes de objetos (bucket+clave) y los almacenes de documentos (basados en rutas) comparten un único contratoFileStore. - Confinado por diseño, no por convención.
local_pathpara subidas y el destino de las descargas son ambos argumentos de llamada de herramienta proporcionados por el LLM, por lo que ambos están estrictamente confinados a directorios raíz configurados: una conversación manipulada no puede pedirle a este servidor que lea una clave SSH ni escribir una descarga donde no debería. - Las mutaciones requieren
confirm=true.upload_file,delete_fileycreate_share_linkmutan estado remoto o crean un enlace con credencial de portador y requieren confirmación deliberada.download_file/list_files/search_filessolo escriben localmente, así que no requieren confirmación. - Multi-cuenta desde el principio. Cada herramienta acepta una etiqueta
accountopcional: ejecuta un Dropbox de trabajo y un Dropbox personal lado a lado sin reconfigurar nada.
Arquitectura
flowchart TB
subgraph Client["MCP Client"]
direction LR
CD["Claude Desktop / Code<br/>(stdio)"]
OL["Ollama bridge<br/>(streamable-http / SSE)"]
end
subgraph Silo["Silo MCP Server (server.py)"]
direction TB
Tools["Tool surface<br/>upload_file · download_file · list_files<br/>search_files · delete_file · create_share_link"]
Paths["paths.py<br/>upload/download root containment"]
Registry["stores/registry.py<br/>resolve(platform, account)"]
Accounts["accounts.py<br/>credential storage + OAuth refresh"]
History[("db.py<br/>SQLite transfer log")]
end
subgraph Backends["FileStore implementations"]
direction LR
Doc["Document stores<br/>Dropbox · Drive · OneDrive<br/>Box · Photos · Yandex Disk"]
Obj["Object stores<br/>S3-compatible · GCS · Azure Blob"]
end
FS[("Local filesystem<br/>upload/download roots")]
Cred[("OS credential store<br/>Windows / macOS / Linux keyring")]
CD --> Tools
OL --> Tools
Tools --> Paths --> FS
Tools --> Registry
Registry --> Doc
Registry --> Obj
Registry --> Accounts --> Cred
Tools --> History
Cada llamada de herramienta resuelve un par (platform, account) a una implementación FileStore y una credencial de accounts.py, comprueba cualquier ruta local contra las raíces de subida/descarga, ejecuta contra la API real de la plataforma y registra el resultado: la misma forma independientemente de cuál de los nueve backends esté al otro lado.
Requisitos
- Python 3.11+
- Un almacén de credenciales del sistema operativo que
keyringpueda usar (Administrador de credenciales de Windows, Llavero de macOS o un proveedor de Secret Service en Linux): las credenciales nunca se escriben en disco en texto plano ni se pasan a través de una llamada de herramienta MCP.
Instalación
git clone https://github.com/gouthamkallempudi/silo-mcp.git
cd silo-mcp
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -e .
Los SDK de almacenes de objetos son extras opcionales: instala solo lo que necesites:
pip install -e ".[s3]" # AWS S3 / Cloudflare R2 / MinIO
pip install -e ".[gcs]" # Google Cloud Storage
pip install -e ".[azure]" # Azure Blob Storage
pip install -e ".[all]" # all three
Una instalación básica (sin extras) cubre Dropbox/Drive/OneDrive/Box/Google Photos/Yandex Disk sin más dependencia que httpx: el servidor degrada con elegancia si un SDK de almacén de objetos no está instalado, en lugar de fallar al iniciar.
Configurar tu cliente MCP
Añade a la configuración MCP de tu cliente (por ejemplo, claude_desktop_config.json de Claude Desktop, o .mcp.json de Claude Code):
{
"mcpServers": {
"silo-mcp": {
"command": "silo-mcp"
}
}
}
silo-mcp debe resolverse en PATH dentro del entorno desde el que tu cliente lanza el servidor: si instalaste en un virtualenv, apunta command al ejecutable silo-mcp de ese venv directamente (por ejemplo, /path/to/silo-mcp/.venv/bin/silo-mcp) en lugar de depender de la activación del shell.
Opciones de transporte (stdio por defecto, HTTP para clientes de red)
Por defecto usa stdio, que es lo que todo cliente MCP de escritorio (Claude Desktop, Claude Code, Cursor, etc.) lanza como subproceso: no se abre ningún puerto de red. Para un cliente que no puede lanzar un subproceso local y necesita alcanzar el servidor por HTTP (consulta Uso con Ollama a continuación), configura:
SILO_MCP_TRANSPORT=streamable-http SILO_MCP_HOST=127.0.0.1 SILO_MCP_PORT=8000 silo-mcp
SILO_MCP_TRANSPORT también acepta sse (el transporte HTTP más antiguo, mantenido para clientes que aún no han migrado a streamable-http). SILO_MCP_HOST/SILO_MCP_PORT solo se leen para los dos transportes HTTP y por defecto son 127.0.0.1:8000.
[!WARNING] Este servidor no tiene autenticación integrada para el modo HTTP: no lo vincules a
0.0.0.0ni lo expongas más allá de localhost sin poner un proxy inverso con autenticación delante, ya que cada llamada de herramienta llega a tus cuentas de almacenamiento en la nube.
Clientes compatibles
Cualquier cliente MCP que pueda lanzar un servidor stdio local funciona: el servidor usa solo llamadas de herramienta MCP estándar, sin características específicas de cliente. Clientes verificados y con funcionamiento esperado:
| Cliente | Configuración | Notas |
|---|---|---|
| Claude Code | .mcp.json en el proyecto ({"mcpServers":{"silo-mcp":{"command":"silo-mcp"}}}) | Verificado de extremo a extremo sobre el protocolo MCP. |
| Claude Desktop | claude_desktop_config.json — mismo bloque mcpServers | stdio. |
| Cursor / VS Code (MCP) | Su mcp.json — mismo bloque mcpServers | stdio. |
| Ollama (a través de un puente) | Apunta el puente al transporte HTTP | Consulta Uso con Ollama. |
[!TIP] Si
silo-mcpno está en elPATHdel shell que lanza (común con virtualenvs), configuracommandal ejecutable del venv directamente, por ejemploC:\\path\\to\\silo-mcp\\.venv\\Scripts\\silo-mcp.exeen Windows o/path/to/.venv/bin/silo-mcpen otros sistemas.
Uso con Ollama
Ollama no habla MCP de forma nativa: necesita un cliente MCP en el bucle que convierta la llamada a herramientas de Ollama en llamadas de herramienta MCP, el mismo papel que Claude Desktop/Code juegan para Claude. Este servidor no incluye ese puente (se mantiene fuera del alcance para seguir siendo un paquete de servidor simple), pero cualquier cliente Ollama compatible con MCP funciona una vez que lo apuntas a streamable-http en lugar de stdio:
- Inicia el servidor en modo HTTP:
SILO_MCP_TRANSPORT=streamable-http silo-mcp. - Apunta tu cliente MCP del lado de Ollama a
http://127.0.0.1:8000/mcp. - Usa un modelo con capacidad de llamada a herramientas (por ejemplo,
llama3.1,qwen2.5): Ollama solo enruta llamadas a herramientas para modelos que admiten el campo de APItools.
Añadir cuentas
Las credenciales se añaden mediante una CLI con entrada oculta, nunca a través de una llamada de herramienta MCP, y se almacenan en el almacén de credenciales de tu sistema operativo:
silo-mcp-accounts add dropbox --label personal
silo-mcp-accounts add google_drive --label personal
silo-mcp-accounts add onedrive --label personal
silo-mcp-accounts add box --label personal
silo-mcp-accounts add google_photos --label personal
silo-mcp-accounts add yandex_disk --label personal
silo-mcp-accounts add s3 --label personal
silo-mcp-accounts add gcs --label personal
silo-mcp-accounts add azure_blob --label personal
silo-mcp-accounts list
Configuración OAuth por primera vez (Drive, Photos, OneDrive, Box)
Estas cuatro plataformas necesitan un refresh_token antes de que silo-mcp-accounts add las acepte — y obtener el primero requiere una autorización de navegador única, no solo un par client_id/secret. Registra una aplicación en la consola de desarrollador de cada plataforma y luego ejecuta:
silo-mcp-accounts oauth google_drive --label personal
silo-mcp-accounts oauth google_photos --label personal
silo-mcp-accounts oauth onedrive --label personal
silo-mcp-accounts oauth box --label personal
Esto abre tu navegador a la pantalla de consentimiento de la plataforma, escucha en http://localhost:8765/callback para la redirección, intercambia el código por un refresh_token y almacena la cuenta: no necesitas ejecutar add después.
Registro de aplicaciones, por plataforma:
- Google (Drive + Photos comparten una aplicación): https://console.cloud.google.com → nuevo proyecto → APIs & Services → habilita la Google Drive API y la Photos Library API → pantalla de consentimiento OAuth (External es suficiente para pruebas personales, añádate como usuario de prueba) → Credentials → Create OAuth client ID → tipo Desktop app. Los clientes de aplicación de escritorio aceptan cualquier redirección
http://localhost:<port>sin pre-registrarla, así que no se necesita configuración de URI de redirección. - OneDrive: Azure Portal → App registrations → New registration → plataforma Mobile and desktop applications → añade la URI de redirección
http://localhost:8765/callbackexactamente (Azure requiere coincidencia exacta). API permissions → Microsoft Graph → añadeFiles.ReadWriteyoffline_access(delegadas). Certificates & secrets → nuevo client secret. - Box: Box Developer Console → Create new app → Custom App → User Authentication (OAuth 2.0) → en Configuration, establece Redirect URI a
http://localhost:8765/callbackexactamente (Box también requiere coincidencia exacta) y marca los scopes que necesites (Read/write files).
Si usas --port para elegir un puerto local diferente, usa ese mismo puerto en la URI de redirección que registres.
Obtener un token de Yandex Disk
Yandex Disk usa un token estático como Dropbox: no tiene refresh_token, así que no forma parte del flujo de arranque silo-mcp-accounts oauth anterior. Su configuración de aplicación OAuth usa una concesión implícita: el token vuelve directamente en el navegador, sin paso de intercambio de código que scriptear.
- oauth.yandex.com → Create app (o reutiliza una existente).
- En Platforms, marca Web services y establece la URI de redirección a
https://oauth.yandex.com/verification_code— la página integrada de Yandex que simplemente muestra el token, sin necesidad de listener local para esta. - En Permissions, concede acceso a Disk:
cloud_api:disk.readycloud_api:disk.write(ocloud_api:disk.app_folderen lugar dedisk.writesi quieres limitarlo a una carpeta específica de la aplicación en lugar de todo el disco). - Guarda la aplicación y anota su ID.
- Visita
https://oauth.yandex.com/authorize?response_type=token&client_id=<your-app-id>en un navegador, aprueba el acceso: el token se muestra directamente en la página redirigida. silo-mcp-accounts add yandex_disk --label personal, pega el token.
Configuración masiva más rápida: importar archivo o variables de entorno
Ejecutar add una vez por plataforma se vuelve tedioso rápido cuando configuras
varias a la vez. Dos rutas más rápidas — ambas terminan igualmente en el llavero del sistema operativo,
nunca en un archivo o variable de entorno que este proyecto persista por sí mismo:
Archivo de importación — copia la plantilla, completa los valores reales, importa de una
sola vez. silo-accounts.example.yaml en la
raíz del repositorio tiene una entrada para las nueve plataformas con los nombres de campo exactos
que cada una necesita y un comentario sobre dónde obtener cada valor:
cp silo-accounts.example.yaml silo-accounts.yaml
# edit silo-accounts.yaml with real values, delete platforms you don't use
silo-mcp-accounts import silo-accounts.yaml
JSON también funciona (misma forma platform -> label -> fields), se detecta por la
extensión del archivo — cualquier cosa que no termine en .json se analiza como YAML.
silo-accounts.yaml/.json, credentials.yaml/.json, y cualquier
*.local.yaml/.json ya están en .gitignore, pero trátalo como un
respaldo, no como el plan — elimina el archivo inmediatamente después de importarlo.
Son secretos en texto plano en el disco mientras exista, esté en gitignore o no.
Variables de entorno — para configuración mediante scripts o CI donde un archivo no es práctico,
add lee SILO_MCP_CRED_<PLATFORM>_<FIELD> antes de solicitar entrada:
SILO_MCP_CRED_DROPBOX_ACCESS_TOKEN=sl.xxx silo-mcp-accounts add dropbox --label personal
Las variables de entorno están más expuestas que un archivo que eliminas (listados de procesos, historial de shell, volcados de memoria) — prefiere el archivo de importación para cualquier cosa más allá de una configuración rápida de pruebas automatizadas.
Flujos de trabajo de ejemplo
Una vez que el servidor está conectado, no llamas a las herramientas directamente — le pides a tu cliente MCP (Claude Desktop, Claude Code, etc.) en lenguaje natural y él elige la herramienta y los argumentos correctos. Estos ejemplos se probaron de extremo a extremo sobre el protocolo MCP real contra cuentas en vivo de Dropbox, Google Drive y Google Photos.
Dropbox / OneDrive / Yandex Disk (rutas reales):
- "Sube
report.pdfdesde mi carpeta de subidas a Dropbox." - "¿Qué hay en la raíz de mi Dropbox? Encuentra cualquier cosa llamada
invoice." - "Descarga
notes.txtde Dropbox y dame un enlace compartible." - "Elimina
old-draft.txtde mi Dropbox."
Google Drive / Box (direccionado por id — busca primero, luego actúa sobre el id):
- "Encuentra
budget.xlsxen mi Google Drive." → luego "Descarga ese." - "Comparte ese archivo con un enlace público."
Google Photos (solo subida):
- "Sube esta captura de pantalla a mi Google Photos."
Almacenes de objetos — S3 / GCS / Azure Blob (necesitan un bucket/contenedor):
- "Sube
backup.zipa mi bucket S3my-backups." - "Lista todo lo que esté bajo
logs/en el bucketmy-backups." - "Dame un enlace prefirmado de 1 hora para
backup.zipenmy-backups."
Copia entre nubes (de una plataforma directamente a otra):
- "Copia
report.pdfde mi Dropbox a mi Google Drive." - "Migra todo lo que acabo de encontrar en Drive a mi bucket S3
archive."
Entre servidores:
- "Toma mi foto de perfil de Google Drive para adjuntarla a una publicación." —
download_fileentrega una ruta local que otro servidor MCP puede usar.
El mapeo completo de frases a llamadas de herramientas:
| Tú dices | Qué se ejecuta |
|---|---|
| "¿Qué almacenamiento en la nube puedo usar aquí y qué cuentas están configuradas?" | list_supported_stores |
"Sube report.pdf desde mi carpeta de subidas a Dropbox." | upload_file(dropbox, …, confirm=true) — el cliente establece confirm después de que aceptes |
| "¿Qué hay en la raíz de mi Dropbox?" | list_files(dropbox) |
"Encuentra archivos llamados invoice en mi Dropbox." | search_files(dropbox, "invoice") |
"Descarga notes.txt de Dropbox para poder usarlo." | download_file(dropbox, "/notes.txt") |
| "Dame un enlace compartible para ese archivo." | create_share_link(dropbox, …, confirm=true) |
"Elimina old-draft.txt de Dropbox." | delete_file(dropbox, "/old-draft.txt", confirm=true) |
| "Pon esta imagen en mi Google Photos." | upload_file(google_photos, …, confirm=true) |
| "Copia mi currículum de Dropbox a un borrador de tweet." | download_file aquí → pasa la ruta local al media_paths de otro servidor MCP |
| "Muéstrame lo que he movido recientemente." | list_transfers |
Debido a que Google Drive y Box direccionan archivos por id, un flujo natural allí es de
dos pasos — "encuentra budget.xlsx en mi Drive" (search_files, devuelve el
id), luego "descarga ese" / "comparte ese" usando el id que el cliente
acaba de ver. El cliente maneja esa cadena por ti.
Las acciones mutadoras (upload_file, delete_file, create_share_link)
requieren confirm=true, así que un buen cliente te mostrará exactamente lo que está
a punto de hacer y solo continuará cuando lo apruebes — una eliminación o un enlace público
nunca ocurren en silencio a partir de una solicitud vaga.
Herramientas
| Herramienta | Puerta de confirmación | Descripción |
|---|---|---|
list_supported_stores() | — | Cada plataforma conocida, su tipo (documento vs. almacén de objetos), capacidades y cuentas configuradas. |
list_accounts(platform) | — | Cuentas configuradas, opcionalmente filtradas. |
upload_file(platform, local_path, remote_path, account, target, confirm) | ✅ | local_path debe estar dentro de la raíz de subida. |
download_file(platform, remote_path, local_filename, account, target) | — | Escribe en la raíz de descarga y devuelve la ruta local. |
list_files(platform, folder_or_prefix, account, target) | — | Listado de carpetas (almacenes de documentos) o listado por prefijo (almacenes de objetos). |
search_files(platform, query, account, target) | — | Indica errores claramente donde la plataforma no admite búsqueda. |
delete_file(platform, remote_path, account, target, confirm) | ✅ | |
create_share_link(platform, remote_path, account, target, expires_in_seconds, confirm) | ✅ | Un enlace que cualquiera puede usar para leer el archivo sin cuenta, donde se admita. Ver Enlaces compartibles. |
copy_file(from_platform, from_remote_path, to_platform, to_remote_path, from_account, to_account, from_target, to_target, confirm) | ✅ | Copia un archivo directamente de una nube a otra. Ver Copia entre nubes. |
list_transfers(platform, limit) | — | Registro de auditoría local de subidas, descargas, eliminaciones, enlaces compartibles y copias entre nubes. |
get_client_capabilities(platform) | — | Verifica qué admite una plataforma antes de llamarla. |
target es el nombre del bucket/contenedor para almacenes de objetos — se ignora para
almacenes de documentos.
Enlaces compartibles
expires_in_seconds se respeta de forma nativa en S3, GCS, Azure Blob y Box
(URLs prefirmadas/firmadas, o el unshared_at de Box). Dropbox, Google Drive,
OneDrive y Yandex Disk también crean un enlace, pero ninguna de sus APIs
admite caducidad en una cuenta personal/no Business, así que el parámetro se
acepta por consistencia de interfaz pero no se aplica allí. No disponible
en Google Photos (solo subida).
Protegido detrás de confirm=true aunque no mueve ni elimina datos —
la URL devuelta es en sí misma una credencial de portador, la misma preocupación
de exfiltración que tiene upload_file.
Copia entre nubes
copy_file mueve un archivo directamente de una plataforma a otra —
"copia mi /photos/id.png de Dropbox a mi bucket S3 backups", "migra
este archivo de Drive a OneDrive" — en una sola llamada de herramienta. Esto es lo único
que una integración de almacenamiento de un solo proveedor estructuralmente no puede hacer; es la
recompensa de poner cada backend detrás de una sola interfaz.
sequenceDiagram
actor User
participant Client as MCP Client<br/>(Claude)
participant Silo as Silo MCP
participant Src as Source cloud<br/>(Dropbox)
participant Dst as Destination cloud<br/>(Google Drive)
User->>Client: "Copy report.pdf from Dropbox to my Google Drive"
Client->>Silo: copy_file(from=dropbox, to=google_drive, confirm=false)
Silo-->>Client: dry run — will copy /report.pdf → google_drive:report.pdf
Client-->>User: About to copy Dropbox → Google Drive. Approve?
User->>Client: yes
Client->>Silo: copy_file(…, confirm=true)
Note over Silo: stream through a temp file<br/>the server controls
Silo->>Src: download /report.pdf
Src-->>Silo: bytes → temp file on disk
Silo->>Dst: upload from temp file
Dst-->>Silo: new file id + metadata
Note over Silo: delete temp file,<br/>log 'copy' to the audit trail
Silo-->>Client: copied ✓
Client-->>User: Done — report.pdf is now in your Google Drive
El LLM nunca toca los bytes del archivo ni una ruta intermedia — solo
emite una llamada copy_file y el servidor maneja la descarga → temporal →
subida → limpieza, condicionada a tu aprobación.
- Se transmite a través de un archivo local temporal que el servidor crea y elimina
— nunca manejas una descarga/subida intermedia, y la ruta temporal nunca es
un argumento proporcionado por el llamador (por lo que no está sujeta a la verificación
de contención de la raíz de subida como lo está
upload_file). from_remote_pathse direcciona como la plataforma origen espera para una descarga (una ruta para Dropbox/OneDrive/Yandex/almacenes de objetos; un id de archivo para Google Drive/Box — busca primero para obtenerlo).to_remote_pathtoma por defecto el nombre base del origen; pásalo explícitamente cuando el origen esté direccionado por id.from_target/to_targetson los nombres de bucket/contenedor cuando cualquiera de los extremos es un almacén de objetos.- Requiere
confirm=true— escribe en el destino. Se registra en el registro de auditoría comocopy, con ambos extremos capturados.
[!NOTE] La copia está limitada por los mismos topes de 5 GiB de descarga/subida, y la descarga del origen se transmite al disco, así que una copia grande entre nubes no almacenará todo el archivo en memoria.
Plataformas admitidas y herramientas
Qué operaciones admite cada plataforma y su modelo de autenticación. ✅
admitido · — no admitido. target = el nombre del bucket/contenedor que los almacenes de objetos
requieren.
| Plataforma | Tipo | Subida | Descarga | Listar | Buscar | Eliminar | Enlace compartible | Autenticación |
|---|---|---|---|---|---|---|---|---|
| Dropbox | documento | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Token estático |
| Google Drive | documento | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Refresco OAuth2 |
| OneDrive | documento | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Refresco OAuth2 |
| Box | documento | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (con caducidad) | Refresco OAuth2 |
| Google Photos | documento | ✅ | — | — | — | — | — | Refresco OAuth2 |
| Yandex Disk | documento | ✅ | ✅ | ✅ | — | ✅ | ✅ | Token estático |
| S3-compatible | objeto (necesita target) | ✅ | ✅ | ✅ | — | ✅ | ✅ (prefirmado) | Clave/secreto estático |
| Google Cloud Storage | objeto (necesita target) | ✅ | ✅ | ✅ | — | ✅ | ✅ (firmado) | JSON de cuenta de servicio |
| Azure Blob | objeto (necesita target) | ✅ | ✅ | ✅ | — | ✅ | ✅ (SAS) | Cadena de conexión |
[!NOTE] Google Photos es solo subida — Google eliminó el acceso a la API de lectura de biblioteca en marzo de 2025. Los almacenes de objetos y Yandex Disk no tienen búsqueda de contenido, solo listado por prefijo/ruta. Detalles en Notas específicas por plataforma.
Fricción de configuración y forma de credenciales por plataforma
Tabla de formas de credenciales
| Plataforma | Credencial | Modelo de autenticación | Configuración |
|---|---|---|---|
| Dropbox | access_token | Token estático | Consola de la aplicación → generar token. Sin baile de refresco. Ver alcances abajo — un error missing_scope significa regenerar, no reconfigurar. |
| Yandex Disk | access_token | Token estático, concesión implícita (sin paso de intercambio de código) | Ver Cómo obtener un token de Yandex Disk arriba. |
| Azure Blob | connection_string | Estático | Cuenta de almacenamiento → Claves de acceso. El más simple de los almacenes de objetos. |
| S3 | access_key_id, secret_access_key, region/endpoint_url opcional | Estático | Usuario IAM de AWS, o credenciales R2/MinIO + endpoint_url. |
| GCS | service_account_json | JWT (cuenta de servicio) | Consola de Google Cloud → crea una clave de cuenta de servicio, pega todo el JSON. |
| Google Drive | client_id, client_secret, refresh_token | OAuth2, refrescado en cada llamada | Pantalla de consentimiento OAuth de Google Cloud + una autorización única para obtener el token de refresco inicial. |
| Google Photos | igual que Drive | OAuth2, alcance photoslibrary.appendonly | Misma aplicación de Google Cloud; solo subida, ver abajo. |
| OneDrive | client_id, client_secret, refresh_token | OAuth2, refrescado en cada llamada | Registro de aplicación en Azure AD. |
| Box | client_id, client_secret, refresh_token | OAuth2, refrescado en cada llamada, el token rota | Aplicación de desarrollador de Box. Cada refresco emite un nuevo token de refresco automáticamente y este servidor lo persiste de vuelta al llavero — no reutilices uno antiguo manualmente. |
Dropbox, Yandex Disk, S3, GCS y Azure Blob usan credenciales estáticas leídas una vez. Google Drive, OneDrive, Box y Google Photos son todas OAuth2 de grandes empresas tecnológicas con tokens de acceso de corta duración (~1h) — este servidor llama al endpoint de token de cada plataforma de nuevo antes de cada solicitud de API en lugar de almacenar en caché, manteniendo la historia de corrección simple a costa de una solicitud extra rápida (~200ms) por llamada.
Permisos/alcances requeridos por plataforma
Los campos de credenciales anteriores solo te dan un token — el token también necesita el alcance correcto, o cada llamada fallará con un error de permisos sin importar cuán correctamente esté almacenado. Se configura en el lado de la plataforma, no aquí:
| Plataforma | Ámbitos requeridos | Dónde configurarlos |
|---|---|---|
| Dropbox | files.content.write, files.content.read, files.metadata.read, sharing.write (para create_share_link) | Consola de la aplicación → pestaña Permisos → marcar los ámbitos → hacer clic en Enviar. Los ámbitos se fijan en el momento de emitir el token: debes generar un token nuevo después de cambiar los ámbitos; un token ya emitido no los adquiere retroactivamente. |
| Yandex Disk | Acceso completo al disco (lectura/escritura) | Configuración de la aplicación en oauth.yandex.com → marcar el permiso de lectura/escritura del disco al crear la aplicación, antes de emitir el token. |
| Google Drive | https://www.googleapis.com/auth/drive | Solicitado automáticamente por silo-mcp-accounts oauth google_drive — no hay nada que configurar manualmente más allá de habilitar la API de Drive en el proyecto. |
| Google Photos | https://www.googleapis.com/auth/photoslibrary.appendonly | Igual que arriba, mediante silo-mcp-accounts oauth google_photos; habilitar la API de Photos Library en el proyecto. |
| OneDrive | Files.ReadWrite, offline_access (delegado) | Azure Portal → registro de la aplicación → Permisos de API → Microsoft Graph → agregar ambos, luego Conceder consentimiento de administrador si tu inquilino lo requiere. También se solicita automáticamente mediante silo-mcp-accounts oauth onedrive. |
| Box | "Leer y escribir todos los archivos y carpetas almacenados en Box" (o más restringido, según lo que necesites) | Consola de desarrollador → aplicación → pestaña Configuración → Ámbitos de la aplicación. |
| S3 | s3:GetObject, s3:PutObject, s3:ListBucket, s3:DeleteObject en el bucket de destino (además, la generación de URL prefirmadas no requiere permiso adicional — es una operación de firma local) | Política de IAM adjunta al usuario/rol cuyas access_key_id/secret_access_key estás usando. |
| GCS | Storage Object Admin (o Object Viewer + Object Creator para una concesión más restringida) en el bucket/proyecto | IAM & Admin → otorgar el rol a la cuenta de servicio antes de generar su clave. |
| Azure Blob | Acceso completo a la cuenta mediante la clave de cuenta de la cadena de conexión — no existe un concepto de ámbito separado | N/A — la propia cadena de conexión es el límite de permisos; usa una cadena de conexión restringida por SAS si quieres limitarla. |
[!TIP] Dropbox es el que más probablemente te muerda: un 401
missing_scopecon un token por lo demás correcto casi siempre significa que los ámbitos se cambiaron después de generar el token. Regenera el token, no solo lo vuelvas a guardar.
Seguridad de rutas
local_path para subidas y el nombre de archivo de destino para descargas son
argumentos de llamada a herramientas proporcionados por el LLM, por lo que ambos se limitan a
una raíz configurada cada uno, en ambas direcciones:
SILO_MCP_UPLOAD_ROOT(por defecto~/silo-mcp/uploads) — los archivos fuera de este directorio no se pueden subir. Esto importa mucho aquí: sin ello, una conversación manipulada podría pedir al servidor que suba un archivo local arbitrario (claves SSH,.env, almacenes de credenciales del navegador) a una cuenta en la nube — persistente, compartible, sin ningún artefacto visible en la plataforma que advierta a alguien de que algo salió de la máquina.SILO_MCP_DOWNLOAD_ROOT(por defecto~/silo-mcp/downloads) — las descargas no se pueden escribir fuera de este directorio mediante un nombre de archivo manipulado.
Las subidas están limitadas a 5 GiB (MAX_UPLOAD_BYTES) y las descargas a 5 GiB
(SILO_MCP_MAX_DOWNLOAD_BYTES, sobreescribible). Las descargas se transmiten a
disco y se abortan a mitad de camino si superan el límite, de modo que un archivo
inesperadamente grande no pueda agotar la memoria. Las credenciales se almacenan en el llavero del sistema operativo,
nunca en la base de datos SQLite; el flujo de autorización OAuth de una sola vez usa PKCE
y un valor state aleatorio (RFC 8252) para que la redirección de bucle local no pueda ser
falsificada.
Notas específicas de la plataforma
Asimetrías y limitaciones conocidas
- Google Photos es solo de subida. Google eliminó los ámbitos de lectura de la biblioteca
de la API de Photos Library en marzo de 2025 — una aplicación ahora solo puede gestionar
elementos multimedia que ella misma haya creado. Navegar o descargar la biblioteca
existente de un usuario requiere la "Picker API" interactiva (una sesión de interfaz web),
que una llamada de herramienta MCP sin interfaz no puede manejar.
list_files/download_file/search_filesdevuelven un error claro en lugar de fingir que funcionan. Tampoco hay ninguna capacidad de eliminación para Google Photos, ni en este proyecto ni en la API de Google — cualquier cosa subida es permanente hasta que se elimine manualmente a través de photos.google.com. - Google Drive y Box direccionan archivos por id, no por ruta, para
descarga/eliminación — el
uploadderemote_pathse usa como nombre de archivo (solo raíz de Drive / raíz de Box, sin direccionamiento de carpetas en v1); para descargar/eliminar necesitas el id delist_files/search_filesprimero. OneDrive, Dropbox y Yandex Disk usan rutas reales en todo momento, sin asimetría. - Los almacenes de objetos no admiten búsqueda — solo listado por prefijo mediante
list_files.search_filesdevuelve un error claro de "no compatible". Yandex Disk tampoco tiene un endpoint de búsqueda dedicado y se comporta de la misma manera (solo listado por ruta mediantelist_files). - Los endpoints de subida aquí son todos de una sola vez (Dropbox ≤150MB, OneDrive ≤4MB); la subida por fragmentos/reanudable para archivos grandes no está implementada en ninguna plataforma todavía.
Seguridad
upload_file, delete_file y create_share_link requieren
confirm=true — o mutan el estado remoto o entregan un enlace con credencial de portador. download_file/list_files/search_files no
tienen compuerta, ya que solo escriben localmente.
Estado de pruebas con cuentas reales
Dropbox, Google Drive y Google Photos se han verificado de extremo a extremo a través del protocolo MCP real — un cliente MCP inicia el servidor, realiza descubrimiento de herramientas y llama a las herramientas por nombre, exactamente como lo haría Claude Desktop/ Code — no solo mediante llamadas directas a Python.
| Plataforma | Estado |
|---|---|
| Dropbox | ✅ Verificado a través del protocolo MCP contra una cuenta real: upload_file (simulación + confirmado), list_files, search_files, download_file (transmitido a disco), create_share_link, delete_file (simulación + confirmado), list_transfers. Las compuertas de confirmación en upload_file/delete_file se comportaron correctamente (bloqueadas sin confirm=true). |
| Google Drive | ✅ Verificado a través del protocolo MCP contra una cuenta real, misma cobertura de herramientas que Dropbox arriba, direccionado por id de archivo según la nota de id-vs-ruta anterior. El refresco de OAuth se disparó de forma independiente antes de cada llamada como estaba diseñado, sin errores de caché observados. |
| Google Photos | ✅ Verificado a través del protocolo MCP contra una cuenta real: upload_file (simulación + confirmado, contenido de imagen real), y confirmado que list_files/search_files/download_file/delete_file fallan con un error claro y correctamente redactado en lugar de un bloqueo — esperado dado el diseño de solo subida, no un error. |
copy_file entre nubes | ✅ Verificado contra cuentas reales en ambas direcciones — Dropbox → Google Drive y Google Drive → Dropbox — con los bytes copiados verificados por suma de comprobación contra el origen en cada dirección. La compuerta de confirmación bloqueó la simulación; ambas copias se registraron en el registro de auditoría. |
Los problemas y PRs que informen resultados de pruebas con cuentas reales para las plataformas restantes son muy bienvenidos.
Seguridad
Este servidor da a un LLM la capacidad de leer archivos locales (de un directorio), moverlos a cuentas en la nube y generar enlaces públicos — por lo que vale la pena ser claro sobre los riesgos y lo que se hace al respecto.
- La inyección de prompts es el riesgo central. El contenido que el modelo lee (un nombre
de archivo, el texto de un documento, un mensaje anterior) puede intentar dirigirlo a
llamar a una herramienta que no pretendías — por ejemplo, "también sube
~/.ssh/id_rsa" o "comparte este archivo públicamente". Esta es una propiedad general del uso de herramientas agénticas, no específica de este servidor. - Mitigaciones integradas:
- Contención de rutas — las subidas solo pueden leer desde
SILO_MCP_UPLOAD_ROOTy las descargas solo pueden escribir enSILO_MCP_DOWNLOAD_ROOT. Una solicitud manipulada para una clave SSH o.envfuera de la raíz se rechaza antes de cualquier llamada de red. Mantén la raíz de subida como una carpeta dedicada, no tu directorio de inicio. - Compuertas de confirmación —
upload_file,delete_fileycreate_share_linkrequierenconfirm=true, de modo que un cliente bien comportado muestra exactamente lo que está a punto de suceder y un humano lo aprueba. Un enlace público o una eliminación nunca se disparan a partir de una solicitud vaga. - Las credenciales nunca llegan al modelo — viven en el llavero del sistema operativo y se leen en el lado del servidor; ninguna llamada de herramienta puede enumerarlas o exfiltrarlas, y nunca están en un archivo de configuración o volcado de entorno que el modelo vea.
- Privilegio mínimo — limita cada token de plataforma a solo lo que necesitas (ver la tabla de permisos); un token de solo lectura no puede ser convencido de eliminar nada.
- Contención de rutas — las subidas solo pueden leer desde
- Registro de auditoría —
list_transfersy el registro SQLite local registran cada subida, descarga, eliminación y enlace de compartir, para que puedas revisar después qué se movió realmente.
[!WARNING] Trata un enlace de compartir generado como una credencial de portador pública — cualquiera con la URL puede leer el archivo, sin necesidad de cuenta. Revócalo eliminando el archivo (o dejando de compartirlo en la plataforma) cuando hayas terminado.
Pruebas
pip install -e ".[all,dev]"
pytest
Las pruebas son de lógica pura y HTTP simulado con respx — sin llamadas de red reales, sin
credenciales reales requeridas.
Solución de problemas
Windows: [WinError 1783] El stub recibió datos incorrectos de silo-mcp-accounts add
Dos causas distintas se manifiestan como este error exacto:
-
El shim
pywin32-ctypesdeCredWrite.keyringprefiere este shim sobre elpywin32real incluso cuando ambos están instalados (ver elbackends/Windows.pydekeyring, que intentapywin32-ctypesprimero). Solución:pip uninstall pywin32-ctypes -yRequiere que
pywin32ya esté instalado (ya se incluye transitivamente aquí mediantemcp). Reinstalar/actualizarkeyringmás tarde puede traerpywin32-ctypesde vuelta como su dependencia — vuelve a ejecutar la desinstalación si esto reaparece después de una actualización. -
El límite duro de ~2560 bytes (~1280 caracteres) por secreto del Administrador de credenciales de Windows — un límite real del sistema operativo no relacionado con (1), confirmado al reproducir el error idéntico con
pywin32real y una cadena sobredimensionada simple. Algunos formatos de token de Dropbox y elservice_account_jsonde GCS (rutinariamente más de 2000 caracteres) superan esto fácilmente con credenciales completamente legítimas, no solo errores de pegado. Se maneja de forma transparente ahora —add_accountdivide cualquier secreto que supere el límite en múltiples entradas del llavero y lo reensambla al leer, en todas las plataformas (no solo Windows, para que el comportamiento no difiera silenciosamente por sistema operativo). No hay nada que hacer aquí; si aún encuentras un error, es probablemente demasiado grande (más de 200,000 caracteres) en lugar de este límite.
Relacionado
Proyecto hermano en el mismo estilo MCP autoalojado y con compuerta de confirmación: un
servidor MCP de publicación social (publica mensajes en lugar de mover archivos) —
diferente forma de capacidad y límite de confianza, componible a nivel de
cliente MCP en lugar de un código base compartido (download_file aquí puede
pasar una ruta local directamente al media_paths de ese servidor).