@olykov/node-red-contrib-mcp-server-readonly
@olykov/node-red-contrib-mcp-server-readonly
Nodos de servidor genéricos del Model Context Protocol (MCP) para Node-RED: exponga cualquier flujo como una herramienta MCP detrás de un endpoint protegido por OAuth, con inspección opcional de solo lectura de flujos de administración de Node-RED. Sin acoplamiento con la automatización del hogar ni de otros dominios — este es un bloque de construcción básico para convertir flujos de Node-RED en herramientas MCP que asistentes de IA (Claude, Codex, etc.) pueden llamar.
Cambio incompatible en 0.5.0 — solo cliente público (PKCE). Los secretos de cliente y la lista de autorización de URI de redirección del lado del nodo desaparecen: el endpoint abierto de registro de clientes entregaba cualquier secreto configurado a quien lo pidiera, y las URI de redirección son validadas por el proveedor de identidad en
/authorizede todas formas. Migración: cambie el cliente del IdP a público con PKCE (un cliente aún confidencial falla el intercambio de tokens coninvalid_client), asegúrese de que las URL de callback del cliente MCP estén en la lista blanca del IdP, y si el nodo avisa sobre un secreto almacenado, abra su configuración, haga clic en Hecho e implemente para eliminarlo. Si los clientes MCP conectados antes de la actualización tienen la trampa el registro antiguo — elimine y vuelva a añadir el servidor en el cliente si el inicio de sesión se comporta mal.
Nodos
mcp-server(nodo de configuración) — aloja un endpoint MCP JSON-RPC independiente enPOST /mcp/<path>, descubrimiento de recursos protegidos OAuth 2.0 (RFC 9728), descubrimiento del servidor de autorización (RFC 8414) que funciona como proxy de un proveedor de identidad OIDC real, y un shim de registro dinámico de clientes, para que los clientes MCP que soportan OAuth (por ejemplo, Claude.ai) puedan autorregistrarse y autenticarse. Varios nodosmcp-serverpueden coexistir, cada uno con su propia ruta y su propia configuración de autenticación independiente.mcp-in— define una herramienta MCP (nombre, descripción, parámetros JSON-Schema y un control de acceso opcional por herramienta). Cuando un cliente MCP llama a la herramienta, el nodo emite un mensaje que transporta el los argumentos de la llamada; conecte el resto del flujo para hacer el trabajo real. Los argumentos enmsg.payloadson entrada del llamador no confiable — el esquema JSON es documentación para el modelo, no validación — por lo que el flujo debe validar infinitivos y escapar antes de usarlos en comandos de shell, rutas de archivo, URL o consultas.mcp-out— resuelve una llamada a la herramienta pendiente. Conecte el final de su flujo aquí conmsg._mcpCallIdintacto (del mensajemcp-inde origen) ymsg.payloadcon el resultado.
Una sola cadena mcp-in → ... → mcp-out es una herramienta MCP. Cree tantas cadenas como quiera
contra el mismo nodo mcp-server para exponer un conjunto de herramientas completo.
Herramientas de API de administración de solo lectura
Habilite Admin read-only API tools en un nodo mcp-server para exponer además una herramienta que utiliza
el HTTP API de administración propia de Node-RED, controlada por un reclamo JWT configurable (por defecto: groups
contiene admin):
get_flow— enumera todas las pestañas de flujo (id, etiqueta, número de nodos), o devuelve el JSON completo de una pestaña cuando se llama con unid.
Configuración de un nodo mcp-server
General: nombre,
path(→ registraPOST /mcp/<path>), laServer URLpública por la que se puede acceder a esta instancia de Node-RED, nombre/instrucciones opcionales del servidor mostrados al modelo, y un filtro de nombre de host opcional (ver más abajo).Auth: una URL de
Identity providerde OIDC (obligatoria — los endpoints se auto-descubren desde/.well-known/openid-configuration, con rutas de respaldo estilo PocketID; si se deja vacío se produce un documento de descubrimiento OAuth roto con endpoints de ruta relativa y sin funcione de autorización, por lo que el editor no permitirá desplegar sin ello), un id de cliente (el cliente del IdP debe ser público con PKCE — los secretos de cliente ya no se soportan, y las URI de redirección se configuran y validan solo en el IdP), ámbitos (scopes), y un token de depuración local opcional que omite el IdP por completo para pruebas locales (ponga cualquier URL de marcador en Identity provider y confíe en el token de depuración — nunca se contacta cuando el token de depuración coincide; el claimgroupsque el usuario de depuración recibe es configurable para que los controles de acceso se puedan probar también localmente), y el control deAccess claim/Server access(ver más abajo).Admin: habilitar/deshabilitar las herramientas de API de administración de solo lectura, token de administración (para la API de administración de Node-RED), puerto de la API de administración, y el control de
Read-only accessque restringe adicionalmente solo las herramientas de administración de solo lectura.
Control de acceso
Un nombre de claim, muchas listas de valores. Access claim en la pestaña Auth (por defecto groups») el nombre del único claim JWT contra el que se comparan todos los controles. Todos los demás campos de autorización son una lista **any-of** separada por comas de los valores de ese claim: media, ops` pasa si el claim contiene al menos uno de ellos. Una lista vacía no impone restricción.
Los claims anidados se las direcciones con una ruta de puntos, para proveedores que no ponen los roles en el nivel superior
del token: realm_access.roles lee los roles de dominio de Keycloak, y cualquier profundidad funciona. Una clave
que existe literalmente siempre gana, por lo que un claim con nombre que tiene puntos dentro de ella se resuelve a sí misma. Solo las cadenas y las listas de cadenas coinciden —apuntar el claim a un objeto contenedor no concede nada en lugar de coincidir accidentalmente.
Campo | Dónde | Restringe |
| mcp-server, pestaña Auth | todas las herramientas de este servidor |
| mcp-in | esa herramienta, adicionalmente |
| mcp-server, pestaña Admin |
|
Las listas se combinan con AND. Llegar a una herramienta significa superar la lista del servidor y la lista de la propia herramienta. Las herramientas de API de administración de solo lectura no son un caso especial — su campo es simplemente la lista de herramientas para get_flow.
Access claim: groups Server access: staff
tool A: (empty) tool B: media Admin access: admin
groups=[staff] → A
groups=[staff, media] → A, B
groups=[staff, admin] → A + get_flow
groups=[media] → nothing (server list not cleared)
groups=[guest] → nothing
Server access empty:
groups=[media] → A, B
groups=[guest] → APerroioso que un usuario con token válido sigue conectándose — initialize siempre tiene éxito — pero las tablas
en juego de acceso de un sistema de control de accesos en la herramientas de la herramienta
tools/list y de las instrucciones de initialize. Una llamada directa tools/call a uno de ellos es rechazada como
resultado de una herramienta MCP con isError: true y un mensaje explicativo (no un error de protocolo JSON-RPC crudo), de
forma que la razón llega al modelo que llama en lugar de perderse en un "Tool execution failed" genérico.
El eje del cliente: alcance (scope) requerido
Las listas anteriores responden a la pregunta ¿qué puede hacer este usuario?. El required scope responde una
pregunta — ¿para qué fue autorizado este cliente en nombre del usuario? — y ambos se verifican con AND.
No son intercambiables. Un grupo dice quién está en el teclado; un scope dice cuánto de la autoridad de esa persona fue delegada al software que tiene el token. Si se fusionan en un solo campo entonces solo se considerará uno: un cliente con un scope de solo lectura, manejado por alguien que puede escribir, escribiría. La concesión del cliente debe limitar los derechos del usuario, no ser ignorada.
El scope requerido se agrega automáticamente a scopes_supported, por lo que no hay nada que repetir en el campo de scopes, y se incluye en los enlace de WWW-Authenticate en un 401.
El claim de scope se lee como lo define OAuth
(RFC 6749 §3.3): una cadena
separada por espacios, o un array si su proveedor envía uno. El nombre del claim no es configurable porque es estándar; scp se lee como fallback para Microsoft Entra y Okta. El campo en sí es una lista any-of separada por comas. Vacío significa sin restricción, por lo que una instalación que no lo llene nunca no se ve afectada; un scope configurado que el token no tenga es rechazado, incluso cuando el token no tiene ningún claim de scope en absoluto.
Actualización (upgrade): el control de administración ya no tiene su propio campo de nombre de claim: se compara contra el
Access claimde la pestaña Auth como todo lo demás. Si estableció un nombre de claim diferente para las herramientas de administración, mueva ese valor a la pestaña Auth o ajuste la lista de administración en consecuencia. Un valor que literalmente contenga una coma ahora se lee como una lista en lugar de una cadena literal. Los campos de control también se renombraron (Required claim/Required value→Access claim/Server access/Admin access); las configuraciones subyacentes no variaron, por lo que los flujos existentes siguen funcionando sin cambios.
Protocolo
El endpoint habla la version 2024-11-05 del protocolo MCP sobre HTTP POST directo — cada solicitud es
un mensaje JSON-RPC, cada respuesta un cuerpo JSON. Se admiten initialize, tools/list, tools/call
y ping; no hay canal GET/SSE/streaming de y no hay mensajes iniciados por el servidor.
Este es el subconjunto que los clientes MCP con capacidad OAuth actuales (por ejemplo, Claude) utilizan realmente contra un
servidor solo de herramientas. La versión anunciada se fija deliberadamente (no es un eco de la propuesta del cliente).
Filtro de nombre de host
Desactivado por defecto. Cuando se habilita Only serve requests for this hostname, el nodo solo responde a las solicitudes cuyo encabezado Host coincide con el nombre de host en su Server URL. Esto permite que varios mcp-server compartan la misma path en una sola instancia de Node-RED, cada uno con de su solo host virtual — útil detrás de un proxy reverso que atiende varios hostnames para un solo backend de Node-RED. Déjelo desactivado para un solo servidor, o cuando un proxy reverso reescribe el encabezado Host.
Proxy reverso
Cada nodo mcp-server es su propio recurso OAuth — a diferencia de un único endpoint MCP compartido, cada la instancia registra su propia ruta de descubrimiento y de registro, bajo su path. Para un nodo con path: docker y Server URL: https://mcp.example.com, existen estas seis rutas:
Método y ruta | Propósito |
| El endpoint MCP JSON-RPC (bearer para con token de acceso) |
| Metadatos del recurso (RFC 9728), con la ruta insertada |
| Metadatos del recurso (RFC 9728), en forma RFC 8414 |
| Metadatos del servidor de autorización (RFC 8414), con la ruta insertada |
| Metadatos del servidor de autorización (RFC 8414), en forma RFC 8414 |
| Shim de registro de cliente dinámico |
Documentos de metadatos del ID de cliente (CIMD). MCP 2026-07-28 obsoleta el registro de cliente dinámico en favor de
CIMD, donde el id de un cliente es la URL HTTPS de un documento de metadatos que el propio cliente aloja. Este nodo anuncia client_id_metadata_document_supported reflejando lo que dice el documento de descubrimiento de su IdP — nunca se configura aquí, porque es el IdP quien resuelve el id de cliente, y este servidor no está en posición de prometer o soporte que el IdP no tenga. El descubrimiento se obtiene una vez y se cachea para toda la vida del nodo, por lo que habilitar o deshabilitar CIMD en el IdP se recuerda en el siguiente reinicio o despliegue de Node-RED — no en vivo.
El shim de DCR está deshabilitado por defecto y debe mantenerse así. Sirve para una situación: algún cliente que no puede usar CIMD hablando con un IdP que no puede hacer DCR en sí. Cuando está activo, este servidor se anuncia a sí mismo como servidor de autorización para que el endpoint de registro sea descubrible — lo que también significa que el iss que devuelve su IdP no coincidirá con el emisor que el cliente registró, y un cliente que use RFC 9207 (exigido por MCP 2026-07-28) se negará a terminar el flujo. Cuando está desactivado, los clientes son dirigidos directamente al IdP y deben usar CIMD o un ID de cliente pre-registrado. Un nodo configurado antes de que existiera este interruptor sigue con el shim activado, porque así es como funcionaba.
Ambos mecanismos siguen disponibles a propósito. Los clientes eligen según el orden de la especificación — pre-registrado, luego CIMD, luego DCR — de modo que un cliente sin soporte CIMD sigue usando el shim de registro exactamente como antes. Qué mecanismo usó cada cliente se puede leer en el registro: MCP CIMD client authenticated: <url> la primera vez que se ve un cliente CIMD después de un reinicio, y MCP DCR fallback para un cliente que se registró aunque el IdP anuncie CIMD. Juntas, las dos líneas dan cuenta de cada cliente que llega al servidor.
Los tokens de un cliente CIMD llevan esa URL de documento como audiencia en lugar de tu id de cliente pre-registrado, y se aceptan siempre que el IdP anuncie CIMD. Este nodo no mantiene una segunda lista de permitidos propia, por lo que la lista de documentos de metadatos aceptados del IdP es el límite: cualquier cliente CIMD que esté en ella puede llegar a este servidor, con la compuerta de claims como comprobación restante.
Se anuncian ambas formas well-known porque distintos clientes MCP sondean unas u otras — expón ambas. Dado que las rutas de cada instancia comparten las formas /mcp/<path> y /.well-known/*/mcp/<path>, un único conjunto de reglas comodín cubre todos los nodos mcp-server actuales y futuros (siempre que todos sean alcanzables a través del mismo dominio/upstream) — no se necesita ningún cambio en el proxy inverso al añadir un nuevo path. Ejemplo, usando Caddy mediante las etiquetas de caddy-docker-proxy:
labels:
caddy_1: mcp.example.com
caddy_1.reverse_proxy_0: /mcp/* "{{upstreams 1880}}"
caddy_1.reverse_proxy_1: /.well-known/oauth-protected-resource/mcp/* "{{upstreams 1880}}"
caddy_1.reverse_proxy_2: /.well-known/oauth-authorization-server/mcp/* "{{upstreams 1880}}"Node-RED en sí mismo responde con un 404 a cualquier ruta que no sea una ruta registrada real, por lo que el comodín no expone nada más allá de lo que cada nodo mcp-server desplegado ya registra. Si un path necesita ser accesible en un dominio diferente al de los demás, dale su propio bloque de sitio caddy_N (o combínalo con el filtrado por nombre de host anterior).
Qué debe soportar el proveedor de identidad (los mismos requisitos que lib/mcp-auth.js):
Un proveedor OIDC con discovery — los endpoints se leen de
‹issuerUrl›/.well-known/openid-configuration, con respaldo al diseño de rutas de PocketID si el discovery no está disponible.Tokens de acceso JWT firmados con una clave publicada en el JWKS del proveedor (los tokens se verifican localmente; no se admiten tokens de acceso opacos o de solo introspección).
Un cliente público con PKCE (S256), tipos de concesión
authorization_code+refresh_token, y las URI(s) de redirección del cliente MCP en la lista de permitidos (para Claude.ai:https://claude.ai/api/mcp/auth_callback). Las URI de redirección se configuran y validan solo en el proveedor de identidad — el nodo ya no mantiene su propia lista de permitidos, por lo que el soporte de comodines del IdP (p. ej., el de PocketID) funciona tal cual. Los secretos de cliente ya no se admiten: el endpoint abierto de registro de clientes entregaba cualquier secreto configurado a quien lo pidiera, por lo que nunca podía ser realmente secreto. Si todavía hay un secreto almacenado de una versión anterior, se ignora con un aviso — cambia el cliente del IdP a público, abre la configuración del nodo, haz clic en Done y despliega para eliminar el secreto almacenado y borrar el aviso.
Probado con Caddy (proxy inverso) + PocketID (proveedor de identidad) + Claude.ai y Hermes (clientes MCP). Cualquier proveedor OIDC que cumpla la especificación y emita tokens de acceso JWT, detrás de cualquier proxy inverso que reenvíe las rutas anteriores, debería funcionar de la misma manera.
Ejemplos
Consulta examples/ para nueve flujos listos para importar (Jellyfin, Calibre, Docker, Music Assistant, Radarr, iRobot/rest980, Overseerr, Sonarr, Spotify), cada uno con su propio nodo mcp-server (descripción del servidor prellenada, Server URL/Identity provider en blanco para que los rellenes) y herramientas mcp-in/mcp-out — una buena referencia para conectar tus propias herramientas.
Desarrollo
npm install
npm testLicencia
ISC
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for Argo RPG Platform — connects AI assistants to campaign data via OAuth2
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/olykov/node-red-contrib-mcp-server-readonly'
If you have feedback or need assistance with the MCP directory API, please join our Discord server