Skip to main content
Glama
olykov

@olykov/node-red-contrib-mcp-server-readonly

by olykov

@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 /authorize de todas formas. Migración: cambie el cliente del IdP a público con PKCE (un cliente aún confidencial falla el intercambio de tokens con invalid_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 en POST /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 nodos mcp-server pueden 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 en msg.payload son 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í con msg._mcpCallId intacto (del mensaje mcp-in de origen) y msg.payload con 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 un id.

Configuración de un nodo mcp-server

  • General: nombre, path (→ registra POST /mcp/<path>), la Server URL pú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 provider de 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 claim groups que el usuario de depuración recibe es configurable para que los controles de acceso se puedan probar también localmente), y el control de Access 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 access que 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

Server access

mcp-server, pestaña Auth

todas las herramientas de este servidor

Tool access

mcp-in

esa herramienta, adicionalmente

Read-only access

mcp-server, pestaña Admin

get_flow, adicionalmente

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]         → A

Perroioso 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 claim de 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 valueAccess 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

POST /mcp/docker

El endpoint MCP JSON-RPC (bearer para con token de acceso)

GET /mcp/docker/.well-known/oauth-protected-resource

Metadatos del recurso (RFC 9728), con la ruta insertada

GET /.well-known/oauth-protected-resource/mcp/docker

Metadatos del recurso (RFC 9728), en forma RFC 8414

GET /mcp/docker/.well-known/oauth-authorization-server

Metadatos del servidor de autorización (RFC 8414), con la ruta insertada

GET /.well-known/oauth-authorization-server/mcp/docker

Metadatos del servidor de autorización (RFC 8414), en forma RFC 8414

POST /mcp/docker/oauth/register

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 test

Licencia

ISC

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all MCP Connectors

Latest Blog Posts

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