cloudisp-mcp
OfficialSupports sourcing the CloudISP API key from 1Password via op://<Vault>/<Item>/credential secret references; when configured this way the server is launched through op run, so the credential is resolved at runtime and never written to disk.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cloudisp-mcpbuscá al abonado Juan Pérez y mostrame su perfil de internet"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
cloudisp-mcp
Servidor MCP de CloudISP para agentes de IA: permite que Claude Code, Codex, pi, omp, opencode u otro agente consulten (y, si lo habilitás, operen) tu plataforma CloudISP en lenguaje natural — abonados, diagnóstico, ONU y WiFi, OLTs y routers, reportes, órdenes de trabajo y el estado de la red.
Es un proceso local sin dependencias: habla con la API pública de CloudISP
(/api/v1/public) usando tu API key y nunca abre conexiones a bases de
datos.
Requisitos
macOS o Linux.
Node.js 18 o superior y
git.Opcional: 1Password CLI (
op) si querés guardar la clave en 1Password en vez de en un archivo.
Related MCP server: sipflow
Instalación rápida
curl -fsSL https://raw.githubusercontent.com/CloudISP/cloudisp-mcp/main/install.sh | bash -s -- prodEl instalador:
Clona este repo en
~/.local/share/cloudisp-mcp(o$XDG_DATA_HOME/cloudisp-mcp).Crea
~/.config/cloudisp-mcp/prod.enva partir del ejemplo solo si no existe (permisos 600).Registra el servidor
cloudispen cada agente que encuentre instalado: Claude Code, Codex, pi, omp y opencode. Los que no estén se omiten.Enlaza las skills en
~/.agents/skills,~/.claude/skillsy~/.omp/agent/skills.
Después editá ~/.config/cloudisp-mcp/prod.env y pegá tu clave.
Actualizar: volvé a correr el mismo comando. Hace git pull y vuelve a
registrar sin duplicar nada; tu archivo .env no se toca.
Obtener una API key
Las claves las emite CloudISP para tu plataforma. Por defecto se entregan con
scopes de solo lectura (:read). Las herramientas nuevas piden además
devices:read (lecturas de equipos y tareas), reports:read y knowledge:read;
las de escritura piden devices:operate, subscribers:write,
work-orders:write y work-orders:assign, y un empleado responsable asociado a
la clave. Cada plataforma hija tiene además una política (read o
technician, con tope diario de escrituras) que el admin aplica del lado del
servidor. Pedila a contacto@digitalnoa.com.ar.
Configuración
Cada entorno es un archivo ~/.config/cloudisp-mcp/<entorno>.env:
Variable | Obligatoria | Descripción |
| sí | URL de tu CloudISP. Debe ser |
| sí | Tu clave, o una referencia |
| no |
|
| no |
|
| no | Carpeta de los archivos que arman |
Con una referencia op:// el servidor arranca vía op run, así que la clave
nunca queda en disco.
Otro entorno: creá ~/.config/cloudisp-mcp/<entorno>.env y corré
install.sh <entorno> (por ejemplo bash -s -- prod local). Se registra como
cloudisp-<entorno>.
Registro manual por agente
Si preferís no usar el instalador, cloná el repo y registrá el lanzador
bin/cloudisp-mcp <entorno> a mano (reemplazá ~ por la ruta absoluta de tu
home donde el formato no la expanda).
Claude Code
claude mcp add --scope user cloudisp -- ~/.local/share/cloudisp-mcp/bin/cloudisp-mcp prodCodex
codex mcp add cloudisp -- ~/.local/share/cloudisp-mcp/bin/cloudisp-mcp prodomp — ~/.omp/agent/mcp.json
{ "mcpServers": { "cloudisp": { "type": "stdio", "command": "/home/vos/.local/share/cloudisp-mcp/bin/cloudisp-mcp", "args": ["prod"], "enabled": true, "timeout": 150000 } } }pi — requiere el adaptador MCP (pi install npm:pi-mcp-adapter); ~/.pi/agent/mcp.json
{ "mcpServers": { "cloudisp": { "command": "/home/vos/.local/share/cloudisp-mcp/bin/cloudisp-mcp", "args": ["prod"] } } }opencode — ~/.config/opencode/opencode.json
{ "mcp": { "cloudisp": { "type": "local", "command": ["/home/vos/.local/share/cloudisp-mcp/bin/cloudisp-mcp", "prod"], "enabled": true, "timeout": 150000 } } }Skills: enlazá cada carpeta de skills/ en ~/.claude/skills (Claude Code),
~/.agents/skills (Codex, pi, opencode) y ~/.omp/agent/skills (omp).
Hermes — ~/.hermes/profiles/<perfil>/config.yaml; el servidor en modo
untrusted hace que cada herramienta sin readOnlyHint pida aprobación humana:
mcp_servers:
cloudisp:
command: /home/hermes/.local/share/cloudisp-mcp/bin/cloudisp-mcp
args: [prod]
trust: untrusted
timeout: 150Las herramientas de equipo esperan hasta 40 s por el resultado (110 s el reinicio, la sincronización, el reaprovisionamiento, el cambio de ONU y el restablecimiento de fábrica; el cambio de WiFi hace una lectura y una escritura: hasta ~85 s), así que el timeout del cliente tiene que ser de 150 s como mínimo.
Herramientas
Las lecturas llevan readOnlyHint: true (también las de equipo, que no
modifican nada). Las escrituras llevan readOnlyHint: false, su descripción
empieza con ESCRITURA (requiere confirmación del operador): y exigen
idempotency_key. Quién pide la escritura no es un argumento del modelo: ver
Identidad del operador.
Herramienta | Endpoint | Scope | Tipo |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
| sin pedido a la API: arma un HTML autocontenido con KPIs, tablas y gráficos SVG | — | lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| lectura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura, destructiva |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura |
|
|
| escritura, destructiva |
|
|
| escritura |
|
|
| escritura |
Todas las herramientas que reciben un abonado aceptan subscriber_id o
subscriber_query; con subscriber_query se exige exactamente un abonado
(si no, ambiguous_subscriber con los candidatos y no se hace ninguna
escritura).
Las herramientas de equipo (onu_read, onu_diagnose, olt_read,
router_read y las escrituras de ONU) despachan una tarea y esperan a su
resultado (consulta cada 2 s), que devuelven con la forma de GET /tasks/{id}.
El presupuesto es de 40 s para las lecturas y las escrituras rápidas, y de
110 s para reboot_onu, sync_onu, reprovision_onu, swap_onu y
factory_reset_onu. Si no terminó, responden {"status":"pending","task_run_id":N}
y se la sigue con get_task y wait_seconds (0 a 110: espera a que termine en
vez de pedirle al usuario que vuelva a mirar). change_subscriber_wifi,
change_wifi_radio y onu_read con onu.wifi_password_check primero leen el
WiFi (onu.wifi_read) y usan el task_run_id de esa lectura
(based_on_task_run_id); si la lectura falla o no termina, no hacen nada más.
optimize_wifi con mode: "preview" solo calcula el plan (result.plan) y
nunca cambia el equipo; con mode: "apply" se le devuelve ese plan sin
modificar. Va en una herramienta de escritura (pide confirmación y aprobación
como las demás) aunque la vista previa no escriba nada. factory_reset_onu
exige confirm: "factory_reset". locate_new_onu busca la ONU nueva por MAC en
cada OLT de la plataforma del abonado (una tarea por OLT) y junta los
resultados en matches; su network_onu_id y serial son los de swap_onu.
live_traffic vuelve a pedir la muestra cada 2 s (hasta 5 veces) mientras el
router arranca la observación, y responde download_bps y upload_bps.
Resultados. Las respuestas esperadas del dominio no son errores de la
herramienta: un 4xx con código de la API (validation_failed, not_found,
platform_policy_denied, actor_forbidden, daily_write_limit_reached,
illegal_transition, …), un 503 source_unavailable, los rechazos propios del
MCP (ambiguous_*, validación, writes_disabled, wifi_read_pending) y una
tarea de equipo fallida vuelven con isError: false y sin clave error
de primer nivel (así no abren el breaker de Hermes):
{"ok": false, "outcome": "<código>", "message": "<mensaje de la API>", "details": {}, "hint": "<instrucción corta para el modelo>"}details (detalles de la API o candidates) solo aparece si existe; la tarea
fallida es {"ok": false, "outcome": "task_failed", "error_code": "...", "message": "..."}.
Siguen siendo errores (isError: true, clave error) los fallos de transporte,
timeouts, 401, 5xx distintos de source_unavailable y respuestas no JSON.
Una tarea que terminó (succeeded) pero cuyo resultado trae error (por ejemplo
onu_offline) o restart_observed: false no es un éxito: vuelve como
{"ok": false, "outcome": "device_error", "error": "onu_offline", "task_run_id": N, "details": {…}}
(error es restart_not_observed cuando el equipo nunca volvió del reinicio).
Sigue sin ser un error de la herramienta (isError: false).
Lo mismo vale para outcome: "failed" o provisioned: false del estado de la
tarea (campos outcome, reason, provisioned, verification.pppoe_user_on_onu,
verification.credentials_pushed, verification.ppp_session_up): vuelve como
device_error con error = reason (o not_provisioned).
sync_onu y swap_onu nunca rotan la contraseña PPPoE
por su cuenta: solo envían rotate_pppoe_password: true cuando el operador lo
confirmó explícitamente. Si la contraseña guardada es inválida la API responde
409 pppoe_pass_invalid (outcome del mismo nombre, con instrucción de pedir la
confirmación). Si la respuesta de despacho trae credential_rotated: true, se
copia al resultado para avisar al operador.
Horas. La API habla en UTC. Antes de entregarlas al agente, toda cadena que
sea una fecha (ISO 8601 con zona, o YYYY-MM-DD HH:MM:SS sin zona, que es UTC) se
convierte a America/Argentina/Salta con -03:00
(2026-10-14T13:00:00+00:00 → 2026-10-14T10:00:00-03:00). Lo que no es una fecha
(fechas sin hora, seriales, texto) no se toca.
router_read solo ejecuta routeros.health_check, routeros.test_command
(command: /system/identity/print, /system/resource/print, /interface/print
o /ip/address/print) y routeros.interface_monitor_traffic.
olt_read solo ejecuta olt.health_check, olt.system_info_read,
olt.mac_table_read y olt.poll (la API rechaza olt.unprovisioned_onus_read).
Estas herramientas validan antes de llamar a la API y, si el valor no sirve,
responden validation_failed sin hacer ningún pedido:
device_metrics:window(15m,1h,6h,24h,7d,30d),resolution(auto,raw,trend_1h) y de 1 a 8metric_idsde 64 caracteres hexadecimales en minúscula. Los ids son opacos y salen del catálogo (la llamada sinmetric_ids, que también aceptasearch,family_id,template_id,state,selected,cursorylimitde 1 a 500); un nombre comocpuno es un id.list_onus(state:online,offline,unknown,absent),list_invoices(status:draft,issued,void) ynetwork_alerts(status:open,acknowledged,closed,active,all;severity:critical,warning,info).list_onusconserial/mac(exactos; el MAC acepta cualquier separador y mayúsculas) oserial_prefix(3 a 64 caracteres) /mac_prefix(4 a 12 dígitos hexadecimales): sirve para pasar de la etiqueta de una ONU al abonado y al PON. Cada fila traeoptical(rx_dbm,band,measured_at,measured_by;nullsi no hay lectura en los últimos 3 días, nunca0);bandusa las bandas del admin:okdesde −25 dBm,warninghasta −28 ycriticalpor debajo.network_alertslista solo las alertas activas (abiertas o reconocidas) salvo que se pida otrostatus;get_alerttrae la evidencia, las transiciones y quién la reconoció o cerró. El nombre del abonado afectado solo viene si la clave tienesubscribers:read.get_invoice:invoice_identero positivo.list_debtors:currency(ARS,USD) yqueryde 2 o más caracteres.list_cutoffs:status(cut_off,active) yhistory(true/false). Los importes de estas herramientas son centavos por moneda y nunca se suman entre monedas.find_subscriber: hace falta al menos uno dequery,document(solo dígitos),external_client_id(solo dígitos) ostatus(true/false); se combinan con AND.#Nes una búsqueda exacta y no se combina con los filtros.querybusca en nombre, apellido, documento, usuario PPPoE, MAC, teléfono, dirección, localidad, email e id legado, sin acentos ni mayúsculas, y cada palabra debe coincidir; el resultado trae el nombre del perfil, del nodo y de la OLT, más el plan contratado enget_subscriber(service).list_internet_profileslistaactivesalvo que se pidadeprecatedosoft_deleted.export_report:fromytovan juntos (YYYY-MM-DD) y viajan comofilter_period=<from>,<to>; el resto de los filtros del reporte van enfilters, igual que enrun_report.locate_new_onu:olt_id(opcional) acota la búsqueda a una sola OLT.find_work_orders(status,priorityyassigned_topositivo) yupdate_work_order_status(status:in_progress,completedocancelled; para cancelar,reasonobligatorio de hasta 500 caracteres).
Con open_work_order, disconnect_subscriber usa la idempotency_key como
external_ref de la orden. La API acepta 100 caracteres como máximo: una clave
más larga se reemplaza por idem-<sha256 de la clave>, que es siempre el mismo
para la misma clave (el reintento sigue siendo idempotente).
Las escrituras mandan Idempotency-Key (reintentar con la misma clave no repite
la acción).
Identidad del operador
Toda herramienta acepta un argumento oculto _operator
({id, name, channel, conversation, profile}) que no figura en ningún
inputSchema: lo agrega el runtime del agente (el plugin de Hermes, ver abajo),
nunca el modelo, y se quita antes de validar. Con él, todos los pedidos a la
API (lecturas, polling y escrituras) llevan:
Header | Valor |
|
|
| nombre visible, sin acentos, hasta 120 caracteres (si falta, el |
|
|
| referencia de la conversación, hasta 128 caracteres imprimibles (el plugin de Hermes manda el id de sesión del agente, que cambia con |
| nombre de la herramienta |
El admin los guarda en api_request_log y en safe_facts.agent de cada cambio.
Con CLOUDISP_REQUIRE_OPERATOR=1 una escritura sin _operator válido vuelve como
{"ok": false, "outcome": "operator_required", "hint": "No se pudo identificar al operador; la acción no se ejecutó."}
sin tocar la API (el admin hace lo mismo con 422 operator_required para las
claves con requires_operator). Un CLI sin plugin no tiene _operator: dejá
CLOUDISP_REQUIRE_OPERATOR sin definir y las escrituras salen solo con el header
de la herramienta.
Archivos: informes y gráficos
export_report (key, format pdf|csv, from + to, platform_id, filters) y
build_report guardan el archivo en CLOUDISP_ARTIFACT_DIR y responden
{"ok": true, "path": "...", "media": "MEDIA:<path>", "bytes": N}; el agente lo
manda al chat incluyendo la línea media tal cual en su respuesta (Hermes la
convierte en un adjunto). build_report recibe title, subtitle y hasta 20
sections (heading, text, kpis, table de hasta 200 filas, chart de
tipo bar, line o donut) y genera un HTML en español con el logo, la paleta
de la marca, CSS de impresión y los gráficos como SVG en línea, sin dependencias
ni pedidos externos. Un valor null se dibuja como hueco y se rotula
«no disponible»; nunca como 0. No hay PNG: si un cliente pide gráficos como
imagen en el chat, hace falta sumar un rasterizador.
Guía
Cada herramienta lleva guide: {category, label_es, example_es} (lib/guide.mjs).
npm run guide genera a partir de ellas hermes-plugin/cloudisp/guia.md (lo que
contesta /guia, por categoría y sin nombres internos) y
hermes-plugin/cloudisp/write_tools.json (las escrituras y la etiqueta de su
tarjeta de aprobación). Los tests fallan si alguno de los dos está desactualizado.
assign_installationse reemplazó porassign_work_order(acepta cualquier tipo de orden), y el parámetroqueryde las herramientas por abonado pasó asubscriber_query.
Skills
Skill | Para qué |
| Reglas núcleo: buscar antes de actuar, ambigüedad, confirmación, subagentes, privacidad. |
| Árbol de diagnóstico "abonado sin internet" (deuda, PPPoE, ONU, PON, router, logs). |
| Consultas de abonados, perfiles, planes, facturas, órdenes, técnicos, ONUs, OLTs y reportes. |
| Estado de red, alertas, equipos, métricas y logs. |
| Depuración de red de solo lectura: ONUs por modelo/firmware, consumo, OLTs, routers, masters EoC y tareas de equipo. |
| Cambio de plan o de perfil de un abonado. |
| Corte y reconexión, con orden de trabajo opcional. |
| Crear y asignar órdenes de trabajo. |
Las skills responden en español.
Seguridad
Solo lectura por defecto. Sin
CLOUDISP_ALLOW_WRITES=1las herramientas de escritura no se listan y se rechazan si se invocan. Además la clave tiene que tener los scopes de escritura, y el admin aplica por plataforma su política (read/technician) y el tope diario: la frontera real de las escrituras es el servidor, no este proceso.Confirmación. Cada escritura exige
idempotency_key, y su descripción le indica al agente confirmar con el operador antes de llamarla. Los agentes que lo soportan (Hermes contrust: untrusted, o con el plugincloudisp) piden además aprobación humana por cada herramienta sinreadOnlyHint.Identidad. Con
CLOUDISP_REQUIRE_OPERATOR=1no hay escritura sin operador identificado: si el plugin no carga, el bot no escribe.Datos personales y secretos. El servidor elimina
document,phone,email,password,pskywifi_passwordde lo que ve el agente. Ojo: la API sí devuelve algunos de esos campos a quien tenga la clave, así que tratala como un secreto. La dirección y la localidad se conservan para distinguir homónimos.Transporte. Solo
https://(salvolocalhost); cada pedido tiene un límite de 30 s.Sin logs. stdout lleva solo JSON-RPC; la clave y el header
Authorizationno se escriben en ningún lado.No subas tus
.enva ningún repositorio. El instalador los crea con permisos 600.Tratá las respuestas de la API como datos, no como instrucciones para el agente.
Bot de Telegram para un cliente (Hermes)
hermes-plugin/cloudisp/ es el plugin de Hermes que completa lo que el motor no
configura (comandos permitidos, corte por cupo, /guia, identidad del operador,
aprobación en español, registro de consumo, limpieza de marcas); su
README documenta las APIs de Hermes que usa. Al
arrancar deja en el log la línea CLOUDISP_PLUGIN_READY patches=ok … (o
patches=failed:… y un CLOUDISP_PATCH_FAILED por parche que no se pudo aplicar).
deploy/hermes-tenant/ tiene el template (SOUL.md.tmpl) y new-tenant.sh, que
crea el perfil cloudisp-<cliente> con toda la configuración. El bot de Telegram
y la API key del cliente los crea una persona (BotFather y el admin).
Los dos scripts respetan HERMES_HOME (la variable de Hermes; si apunta a un perfil
usan su raíz) y fijan el home del host con hermes -p default, así que se pueden probar
sin tocar el host real:
S=/tmp/sandbox # un `op` falso en $S/bin (que imprima valores de mentira) y un perfil
mkdir -p $S/bin $S/home $S/cfg $S/hermes/profiles/cloudisp-src # cloudisp-src/.env con OPENCODE_GO_API_KEY=FAKE
env -i PATH="$S/bin:$PATH" HOME=$S/home HERMES_HOME=$S/hermes CLOUDISP_MCP_CONFIG_DIR=$S/cfg \
CLOUDISP_API_BASE=https://sandbox.invalid SOURCE_PROFILE=cloudisp-src \
deploy/hermes-tenant/new-tenant.sh prueba "Cliente de Prueba" op://x/token/c op://x/key/c 123456789Con un HERMES_HOME que no es el del host, el primer hermes arma su propio entorno de
dependencias en esa raíz (unos 80 s, ~400 MB y red); en el host de producción no pasa.
HOME también va al sandbox: el alias cloudisp-<cliente> se crea en ~/.local/bin y el
directorio de informes en ~/.cache/cloudisp-artifacts.
Desinstalar
Quitá el registro:
claude mcp remove --scope user cloudisp,codex mcp remove cloudisp, y la entradacloudispde~/.omp/agent/mcp.json,~/.pi/agent/mcp.jsony~/.config/opencode/opencode.json.Borrá los symlinks
cloudisp-*en~/.claude/skills,~/.agents/skillsy~/.omp/agent/skills.Borrá
~/.local/share/cloudisp-mcpy, si ya no la necesitás,~/.config/cloudisp-mcp.
Desarrollo
npm test # servidor, librerías, guía y skills
npm run guide # regenera guia.md y write_tools.json
cd hermes-plugin && python -m pytest cloudisp/tests # plugin (necesita pytest y pyyaml)Licencia MIT.
This server cannot be deployed
Maintenance
Related MCP Connectors
Live outage status, internet dependencies and pay-per-call market intelligence for AI agents.
- HAVNOAuthapp.havnre
Read-only AI access to HAVN properties, leads, tasks, files, media, and analytics.
- mcp-serverOAuthcom.make
Give your AI agents the tools to build, manage, and run automation workflows.
Build and manage AI-native customer support agents from Claude or any MCP client.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to administrate WHMCS installations through the External API, providing ~50 tools for clients, billing, orders, services, domains, support, and aggregators with safety features and governance.29 npm3ISC
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to access curated SIP/VoIP documentation, troubleshoot traces, and analyze telecom configurations with 20+ read-only tools.MIT
- AlicenseNot gradedqualityCmaintenanceEnables a telecom support assistant to look up customers, check balances and orders, suspend lost SIMs, open tickets, and analyze SIP traces to diagnose call failures.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to query and manage a web hosting billing platform through its External API — clients, tickets, invoices, domains, orders, products, and system data. It defaults to read-only with per-domain opt-in for writes, two-step confirmation for billing and destructive actions, and automatic redaction of secrets, card data, and PII.MIT