Servidores MCP respaldados por una aplicación: por qué Hermes ahora comprueba antes si esa app está instalada

Instalas Blender. Activas dentro el add-on oficial de MCP. Después sigues un tutorial, dejas que Hermes instale un plugin de Blender y la instalación dice “hecho” — y, en efecto, get_objects_summary aparece en la lista de herramientas. Así que preguntas: “¿Qué objetos hay ahora mismo en mi escena?” La respuesta que vuelve es: “herramienta no conectada”.
Abres mcp.json. La dirección está bien. Reinicias Blender. Sigue sin aparecer nada. El problema real es que ese puerto nunca estuvo en mcp.json: Blender elige uno al arrancar y lo escribe en un archivo de runtime propio. La dirección que tecleaste a mano en la config llevaba obsoleta desde el primer día.
Tres cambios recientes cierran exactamente esta clase de “la config parece correcta, simplemente no conecta”: los plugins pueden declarar qué aplicación necesitan (#119049), el runtime solo confía en el archivo de endpoint que escribe la app (#119050), y el catálogo de plugins estrenó su primer caso real, Blender Lab (#119365).
Por qué “poner la dirección en la config” nunca iba a funcionar
Las apps locales con herramientas se comunican con Hermes a través de un puente en loopback. Para no pelearse por los puertos, una app de escritorio suele asignar uno dinámicamente al arrancar y luego escribe su dirección http actual, un token temporal y su pid de proceso en un archivo de runtime — en Blender, eso es el puente en el puerto 9876. De ahí se derivan dos consecuencias:
- Una URL escrita a mano acaba quedando obsoleta, y queda obsoleta en silencio. La config es sintácticamente perfecta; lo único muerto es la conexión.
- Una herramienta que no conecta tenía antes un único mensaje: “no conectada”. No había forma de saber si la app estaba cerrada, si el puerto se había movido o si la versión era demasiado antigua.
Así que el diseño se invirtió: no adivines — pregúntale a la aplicación.
Un plugin puede declarar qué aplicación necesita, en plugin.json
Los plugins portables (los que traen un plugin.json más archivos, sin código nativo) ganaron un bloque de declaración bajo extensions."com.nousresearch.hermes".servers. El nombre del server tiene que coincidir con una entrada del mcp.json del plugin:
"extensions": {
"com.nousresearch.hermes": {
"servers": {
"blender": {
"app": {
"darwin": {
"presence": "bundle",
"location": "/Applications/Blender.app",
"version": { "kind": "plist" }
},
"win32": {
"presence": "executable",
"location": "%ProgramFiles%/Blender Foundation/Blender/blender.exe",
"version": { "kind": "uninstall_registry", "display_name_prefix": "Blender" }
},
"linux": {
"presence": "executable",
"location": "/usr/bin/blender",
"version": { "kind": "none" }
}
},
"requires": { "app": true, "min_version": "5.1" },
"liveness": {
"kind": "server_json",
"path": "%LOCALAPPDATA%/Blender/server.json",
"fields": { "url": "http", "token": "token", "pid": "pid" }
}
}
}
}
}
El parser es estricto con cada línea de eso, algo que conviene saber antes de escribir uno:
appva indexado por sistema operativo y solo aceptadarwin,win32ylinux, con al menos un bloque obligatorio.locationdebe ser absoluta o empezar por~,%VAR%o$VAR; cualquier cosa que contenga..o un esquema de URL se rechaza de plano.presencetiene dos valores:bundle(un bundle de app de macOS) yexecutable.- Una fuente de versión (
version.kind) es una deplist(solo darwin),uninstall_registry(solo win32, y exige undisplay_name_prefix),pe_resource(win32) onone. Si definesrequires.min_version, cada bloque de OS tiene que poder leer una versión de verdad — un bloquenonefalla la validación. liveness.kindesserver_jsononone. Elegirserver_jsonrequierepath; las claves opcionales sonendpoint_path(por defecto/mcp) y las anulaciones de nombres de campopid_key,url_key,token_key(por defectopid,http,token).
Fíjate en la separación entre los dos bloques: app son datos (“dónde vive la aplicación”), requires es política (“si exijo que esté ahí”). La separación es deliberada.
La barrera de instalación: no instales lo que no puede funcionar
Antes, que faltara una aplicación era puramente un problema de runtime: el plugin aterrizaba en su directorio, sus herramientas se registraban, y solo descubrías el vacío cuando llamabas a una. Ahora el instalador comprueba cada server declarado antes del swap atómico y rechaza en cualquiera de estos casos:
| Rechazo | Detonante |
|---|---|
missing_app |
La aplicación declarada no está en esta máquina |
version_too_old |
La app está, pero es más antigua que requires.min_version |
unsupported_os |
Este host no está cubierto por ningún bloque de OS |
| Desajuste de plataforma | La lista platforms de una entrada del catálogo no coincide con la familia de OS del host (windows / macos se aceptan como alias de win32 / darwin) |
En las instalaciones desde catálogo, la comprobación ocurre antes de clonar o escribir el destino del plugin, así que nunca te quedas con un directorio a medio instalar. La validación también reporta el estado de disponibilidad como una fila informativa:
hermes plugins validate --install-deps
En runtime, solo el archivo que escribe la app es autoritativo
Esta es la parte más firme del cambio. Cada intento de conexión a un server declarado vuelve a leer primero ese archivo de runtime y usa el endpoint que hay dentro:
- El archivo de runtime es autoritativo. Un archivo ausente, un endpoint malformado o un
pidmuerto provocanLiveEndpointUnavailable— y Hermes no recurre a la URL configurada de forma estática. Hay un test que sostiene esa línea:test_missing_runtime_file_never_falls_back. - El token se registra antes de usarse. Un token temporal encontrado en el archivo de runtime se entrega al redactor antes de que Hermes construya con él una cabecera
Authorization, así no se filtrará cuando pegues logs o leas un transcript más adelante. - Los requisitos de aplicación también filtran la admisión de herramientas. Un server con requisitos de app no ofrece sus herramientas al conjunto disponible mientras el host no pueda satisfacerlos.
Cuando una herramienta no está, di por qué
La peor versión de esta experiencia no es “falta la herramienta”, sino “falta la herramienta y nadie va a decirte por qué”. Las herramientas declaradas pero no disponibles ahora devuelven un estado estructurado: el estado actual, los detalles de la aplicación, una acción concreta para el usuario y una política de reintento. El catálogo de herramientas diferidas y el resumen de búsqueda vacía también conservan una línea estable por cada server declarado que queda oculto.
Se distinguen cinco estados:
| Estado | Significado | A grandes rasgos, qué hacer |
|---|---|---|
missing_app |
No está instalada | Instala la app y luego instala/activa el plugin |
version_too_old |
Instalada, pero demasiado antigua | Actualiza la app |
app_not_running |
Instalada, sin ejecutarse | Ábrela y deja que el puente arranque |
endpoint_unavailable |
Falta el archivo de runtime o el endpoint no es válido | Reinicia el puente desde dentro de la app |
no_interactive_session |
Necesita una sesión gráfica | Ejecútalo en una sesión de escritorio, no en un servicio headless |
Fíjate también en que la política de reintento solo es after_user_action (haz lo de arriba y reintenta) o never_here (este host nunca lo va a satisfacer, por ejemplo porque el OS no está soportado). “Vuelve a intentarlo” y “deja de intentarlo” son respuestas distintas, y ahora la herramienta te da la correcta.
El caso real que ya está en el catálogo: Blender Lab
El catálogo oficial de plugins ya incluye una entrada blender, que envuelve este repositorio público: hermes-plugin-blender.
# instala el sha fijado de la entrada del catálogo, no la punta de una rama
hermes plugins install NousResearch/hermes-plugin-blender
hermes plugins enable blender
Lo que hace es sencillo: lanza el server MCP oficial de Blender Lab por stdio (código upstream fijado al commit 2cea8d56, v1.0.3), y ese server se conecta al puente del add-on de Blender en 127.0.0.1:9876. Viene con una skill de workflow original.
Tres cosas que conviene saber antes de apoyarte en él:
- Los prerrequisitos los pones tú. Blender 5.1+, el add-on oficial de MCP (instalado, activado, con acceso Online permitido y el puente arrancado), más Git y uv en la máquina del backend. Instalar el paquete no instala Blender, ni cambia sus preferencias, ni instala el add-on, ni abre una escena.
- Calienta el entorno de uv antes de la primera conexión. La primera compilación upstream tarda minutos; eso se midió en Windows, y un timeout normal de conexión MCP la interrumpirá:
uv tool run --from "git+https://projects.blender.org/lab/blender_mcp.git@2cea8d566dde07fbac28a61d698909d69724e853#subdirectory=mcp" blender-mcp --help
- Este wrapper todavía no usa el filtro de aplicación. Su README dice con claridad que la detección automática de aplicación, versión y liveness ni está implementada ni está verificada por el wrapper, y que los prerrequisitos son manuales. En otras palabras: el contrato
app/requires/livenessya está vivo en el core, y este plugin en concreto aún no lo ha adoptado — la secuencia habitual de “primero la capacidad, luego los plugins”.
Una cosa más que merece decirse en abierto: el propio Blender Lab advierte de que sus herramientas ejecutan Python arbitrario dentro de Blender, sin protección contra el borrado de archivos o la exfiltración de datos. Mantén el puente solo en loopback, úsalo en workflows de confianza y prefiere una escena desechable. Esto no es un sandbox.
Asimetrías entre plataformas y esquinas que siguen abiertas
- Las comprobaciones del host no son simétricas entre plataformas. Windows exige una sesión de proceso distinta de cero (la sesión de consola activa o una estación de ventana de proceso). Linux exige un ID de sesión de proceso legible y el directorio de runtime del usuario actual. macOS devuelve true — la librería estándar no tiene forma de preguntar por la sesión gráfica actual sin lanzar un subproceso.
- Las builds de la aplicación con autenticación desactivada puede que nunca escriban el archivo de runtime. El PR lo lista como cuestión abierta pendiente de verificación en hardware; una build así se quedaría en
endpoint_unavailablede forma permanente. - La frontera de activación no se movió. Cuando un plugin se carga a mitad de ejecución, sus handlers del lado del gateway (comandos de barra, callbacks de botón, transformaciones de entrada) se conectan de inmediato — pero las herramientas y las secciones del system prompt siguen esperando a la siguiente sesión (el invariante del prompt cache), y los servidores MCP portables necesitan un
mcp.reloadexplícito.
Para cerrar
Si tu plugin habla con software de escritorio, el problema nunca fue que la config estuviera mal escrita: fue que un humano no debería estar escribiendo esa dirección en absoluto. Ahora un plugin declara su dependencia de aplicación, el instalador rechaza antes de tocar tu profile, el runtime solo confía en el endpoint que publica la propia app, y una herramienta muerta por fin te dice qué eslabón de la cadena se rompió.
Para el panorama de plugins que lo rodea, mira el modelo de confianza del catálogo de plugins y el catálogo oficial de MCP remotos; si lo que te preocupa es la frontera de seguridad de herramientas locales como esta, seguridad del catálogo MCP es la lectura más cercana. Para la variante de skills dentro del repositorio, la guía de skills a nivel de proyecto cubre .hermes/skills/ y su filtro de confianza.