@olykov/node-red-contrib-mcp-server-readonly
@olykov/node-red-contrib-mcp-server-readonly
Node-RED용 범용 Model Context Protocol(MCP) 서버 노드입니다. 모든 플로우를 OAuth로 보호된 엔드포인트 뒤의 MCP 도구로 노출하고, 선택적으로 읽기 전용 Node-RED 관리 플로우 검사를 제공합니다. 홈 오토메이션이나 다른 도메인에 결합되지 않은, Node-RED 플로우를 AI 어시스턴트(Claude, Codex 등)가 호출할 수 있는 MCP 도구로 바꿔주는 순수한 빌딩 블록입니다.
0.5.0의 주요 변경 사항 — 공개(PKCE) 클라이언트 전용. 클라이언트 시크릿과 노드 측 리다이렉트 URI 허용 목록이 제거되었습니다. 공개 클라이언트 등록 엔드포인트가 모든 호출자에게 설정된 시크릿을 노출했고, 리다이렉트 URI는 어차피
/authorize에서 ID 공급자가 검증하기 때문입니다. 마이그레이션: IdP 클라이언트를 PKCE를 사용하는 공개 클라이언트로 전환하고(여전히 기밀 클라이언트면invalid_client로 토큰 교환에 실패), MCP 클라이언트 콜백 URL이 IdP에 허용 목록으로 등록되어 있는지 확인하며, 노드가 저장된 시크릿에 대해 경고하면 해당 구성을 열고 완료를 클릭한 다음 배포하여 삭제하세요. 업그레이드 전에 연결된 MCP 클라이언트가 이전 등록을 캐시했을 수 있으므로, 로그인이 제대로 되지 않으면 클라이언트에서 서버를 제거했다가 다시 추가하세요.
노드
mcp-server(구성 노드) —POST /mcp/<path>에 독립형 MCP JSON-RPC 엔드포인트, OAuth 2.0 보호 리소스 검색(RFC 9728), 실제 OIDC ID 공급자를 프록시하는 권한 부여 서버 검색(RFC 8414), 동적 클라이언트 등록 셸을 호스팅하여 OAuth 인식 MCP 클라이언트(예: Claude.ai)가 스스로 등록하고 인증할 수 있게 합니다. 여러mcp-server노드가 각자의 경로와 독립적인 인증 구성을 가질 수 있습니다.mcp-in— 하나의 MCP 도구(이름, 설명, JSON-Schema 매개변수, 선택적 도구별 접근 게이트)를 정의합니다. MCP 클라이언트가 도구를 호출하면 노드는 호출 인수를 담은 메시지를 내보내며, 실제 작업을 수행하려면 나머지 플로우를 연결하세요.msg.payload의 인수는 신뢰할 수 없는 호출자 입력입니다. JSON 스키마는 모델을 위한 문서일 뿐 검증이 아니므로, 플로우는 이를 검증하고 이스케이프한 후 셸 명령, 파일 경로, URL 또는 쿼리에 사용해야 합니다.mcp-out— 대기 중인 도구 호출을 해결합니다. (원래mcp-in메시지의)msg._mcpCallId를 유지하고msg.payload에 결과를 설정한 채 플로우 끝을 여기에 연결하세요.
하나의 mcp-in → ... → mcp-out 체인이 하나의 MCP 도구입니다. 동일한 mcp-server 노드에 대해 원하는 만큼 체인을 만들어 전체 도구 모음을 노출하세요.
관리자 읽기 전용 API 도구
mcp-server 노드에서 관리자 읽기 전용 API 도구를 활성화하면 Node-RED 자체 관리 HTTP API에서 작동하는 도구를 하나 더 노출하며, 구성 가능한 JWT 클레임(기본값: groups에 admin 포함)으로 게이트됩니다.
get_flow— 모든 플로우 탭(id, 레이블, 노드 수)을 나열하거나,id와 함께 호출하면 해당 탭의 전체 JSON을 반환합니다.
mcp-server 노드 구성
일반: 이름,
path(→POST /mcp/<path>등록), 이 Node-RED 인스턴스에 접근 가능한 공개Server URL, 모델에 표시할 선택적 서버 이름/지침, 선택적 호스트 이름 필터(아래 참조).인증: OIDC
Identity provider발급자 URL(필수 —/.well-known/openid-configuration에서 엔드포인트를 자동 검색하며 PocketID 스타일 대체 경로도 지원합니다. 비워 두면 상대 경로 엔드포인트와 작동하는 인증이 없는 손상된 OAuth 검색 문서가 생성되므로 편집기에서 배포를 허용하지 않습니다), 클라이언트 ID(IdP 클라이언트는 PKCE를 사용하는 공개여야 하며, 클라이언트 시크릿은 더 이상 지원되지 않고 리다이렉트 URI는 IdP에서만 구성 및 검증됩니다), 범위, 토큰 대상 그룹, IdP를 완전히 우회하는 선택적 로컬 디버그 토큰(Identity provider에 아무 자리 표시자 URL을 넣고 디버그 토큰에 의존하세요. 디버그 토큰이 일치하면 연락하지 않습니다. 디버그 사용자가 얻는groups클레임은 구성 가능하므로 접근 게이트도 로컬에서 테스트할 수 있습니다), 그리고Access claim/Server access게이트(아래 참조).관리자: 관리자 읽기 전용 API 도구 활성화/비활성화, 관리자 토큰(Node-RED Admin API용), 관리자 API 포트, 그리고 읽기 전용 관리자 도구만 추가로 제한하는
Read-only access게이트.
접근 제어
하나의 클레임 이름, 여러 값 목록. 인증 탭의 Access claim(기본값 groups)은 모든 게이트가 일치하는 단일 JWT 클레임을 지정합니다. 다른 모든 권한 부여 필드는 해당 클레임 값의 쉼표로 구분된 any-of 목록입니다. media, ops는 클레임에 그중 하나 이상이 포함되면 통과합니다. 빈 목록은 제한을 두지 않습니다.
중첩 클레임은 점으로 구분된 경로로 지정합니다. 공급자가 토큰 최상위에 역할을 두지 않는 경우를 위함입니다. realm_access.roles는 Keycloak 영역 역할을 읽으며, 깊이 제한은 없습니다. 말 그대로 존재하는 키가 항상 우선하므로, 점이 포함된 실제 클레임 이름도 그 자체로 해석됩니다. 문자열과 문자열 배열만 일치합니다. 클레임이 컨테이너 객체를 가리키면 우연히 일치하는 대신 아무것도 부여하지 않습니다.
필드 | 위치 | 제한 대상 |
| mcp-server, 인증 탭 | 이 서버의 모든 도구 |
| mcp-in | 해당 도구 하나에 추가로 |
| mcp-server, 관리자 탭 |
|
목록은 AND로 결합됩니다. 도구에 도달하려면 서버 목록 및 해당 도구 자체 목록을 모두 통과해야 합니다. 관리자 읽기 전용 API 도구도 예외가 아닙니다. 해당 필드는 단지 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유효한 토큰을 가진 모든 사람은 여전히 연결할 수 있지만(initialize는 항상 성공), 호출자가 도달할 수 없는 도구는 tools/list 및 initialize 지침에서 숨겨집니다. 해당 도구 중 하나에 대한 직접적인 tools/call은 원시 JSON-RPC 프로토콜 오류가 아닌 설명 메시지와 함께 isError: true인 MCP 도구 결과로 거부되므로, 그 이유가 "도구 실행 실패"라는 일반적인 오류로 축소되지 않고 호출 모델에 도달합니다.
클라이언트 축: 필수 범위
위 목록은 사용자가 무엇을 할 수 있는지에 대한 답입니다. Required scope는 클라이언트가 사용자를 대신해 무엇을 하도록 승인되었는지라는 다른 질문에 답하며, 둘은 AND로 확인됩니다.
이 둘은 서로 바꿔 쓸 수 없습니다. 그룹은 키보드 앞에 누가 있는지를 말하고, 범위는 그 사람의 권한 중 얼마나 많은 부분이 토큰을 가진 소프트웨어에 위임되었는지를 말합니다. 이 둘을 하나의 필드로 합치면 하나만 고려됩니다. 쓰기 권한이 있는 사람이 조종하는 읽기 전용 범위가 부여된 클라이언트는 쓰기를 수행할 것입니다. 클라이언트의 권한 부여는 사용자의 권리를 제한해야지 무시해서는 안 됩니다.
필수 범위는 scopes_supported에 자동으로 추가되므로 scopes 필드에 반복할 필요가 없으며, 401 시 WWW-Authenticate 챌린지에 이름이 지정됩니다.
범위 클레임은 OAuth가 정의한 대로 읽힙니다(RFC 6749 §3.3). 공백으로 구분된 문자열이거나, 공급자가 배열을 보내는 경우 배열입니다. 클레임 이름은 표준화되어 있으므로 구성할 수 없습니다. scp는 Microsoft Entra 및 Okta용 대체 항목으로 읽힙니다. 필드 자체는 쉼표로 구분된 any-of 목록입니다. 비어 있으면 제약이 없으므로 이 필드를 채우지 않은 설치는 영향을 받지 않습니다. 토큰이 보유하지 않은 구성된 범위는 토큰에 범위 클레임이 전혀 없는 경우를 포함하여 거부됩니다.
업그레이드: 관리자 게이트에는 더 이상 자체 클레임 이름 필드가 없습니다. 다른 모든 것과 마찬가지로 인증 탭의
Access claim과 일치합니다. 관리자 도구에 대해 다른 클레임 이름을 설정한 경우 해당 값을 인증 탭으로 이동하거나 관리자 목록을 그에 맞게 조정하세요. 말 그대로 쉼표를 포함하는 값은 이제 리터럴 문자열이 아닌 목록으로 읽힙니다. 게이트 필드 이름도 변경되었습니다(Required claim/Required value→Access claim/Server access/Admin access). 기본 설정은 변경되지 않았으므로 기존 플로우는 수정 없이 계속 작동합니다.
프로토콜
엔드포인트는 일반 HTTP POST를 통해 MCP 프로토콜 버전 2024-11-05을 사용합니다. 모든 요청은 하나의 JSON-RPC 메시지이고 모든 응답은 하나의 JSON 본문입니다. initialize, tools/list, tools/call 및 ping이 지원됩니다. SSE/스트리밍 GET 채널이나 서버 시작 메시지는 없습니다. 이것이 오늘날 OAuth 지원 MCP 클라이언트(예: Claude)가 도구 전용 서버에 실제로 사용하는 하위 집합입니다. 광고되는 버전은 클라이언트의 제안을 그대로 따르지 않고 의도적으로 고정됩니다.
호스트 이름 필터링
기본적으로 꺼져 있습니다. Only serve requests for this hostname을 활성화하면 노드는 Host 헤더가 Server URL의 호스트 이름과 일치하는 요청에만 응답합니다. 이렇게 하면 여러 mcp-server 노드가 하나의 Node-RED 인스턴스에서 동일한 path를 공유하고 각자 자신의 가상 호스트에만 응답할 수 있습니다. 하나의 Node-RED 백엔드에 여러 호스트 이름을 제공하는 역방향 프록시 뒤에서 유용합니다. 단일 서버이거나 역방향 프록시가 Host 헤더를 다시 쓰는 경우에는 끄세요.
역방향 프록시
각 mcp-server 노드는 자체 OAuth 리소스입니다. 단일 공유 MCP 엔드포인트와 달리 모든 인스턴스는 path로 범위가 지정된 자체 검색 및 등록 경로를 등록합니다. path: docker 및 Server URL: https://mcp.example.com인 노드의 경우 다음 6개의 경로가 있습니다.
메서드 및 경로 | 용도 |
| JSON-RPC MCP 엔드포인트(베어러 토큰으로 보호) |
| 리소스 메타데이터(RFC 9728), 경로 삽입 형식 |
| 리소스 메타데이터(RFC 9728), RFC 8414 형식 |
| 인증 서버 메타데이터(RFC 8414), 경로 삽입 형식 |
| 인증 서버 메타데이터(RFC 8414), RFC 8414 형식 |
| 동적 클라이언트 등록 셸 |
클라이언트 ID 메타데이터 문서(CIMD). MCP 2026-07-28은 동적 클라이언트 등록을 더 이상 사용하지 않고 CIMD를 권장합니다. CIMD에서 클라이언트 ID는 클라이언트가 직접 호스팅하는 메타데이터 문서의 HTTPS URL입니다. 이 노드는 IdP의 검색 문서가 말하는 내용을 미러링하여 client_id_metadata_document_supported를 광고합니다. IdP가 클라이언트 ID를 확인하는데 이 서버가 IdP가 지원하지 않는 지원을 약속할 수 없으므로 여기서 구성되지 않습니다. 검색은 한 번 가져와 노드 수명 동안 캐시되므로 IdP에서 CIMD를 활성화/비활성화하면 실시간이 아닌 다음 Node-RED 재시작 또는 배포 시 반영됩니다.
DCR 셸은 기본적으로 꺼져 있으며 꺼진 채로 두어야 합니다. CIMD를 사용할 수 없고 IdP가 DCR을 직접 수행할 수 없는 클라이언트라는 한 가지 상황에만 존재합니다. 켜면 이 서버가 자신을 권한 부여 서버로 광고하여 등록 엔드포인트를 검색 가능하게 만듭니다. 즉, IdP가 반환하는 iss가 클라이언트가 기록한 발급자와 일치하지 않게 되어 RFC 9207(MCP 2026-07-28 필수)을 적용하는 클라이언트는 흐름 완료를 거부합니다. 끄면 클라이언트는 IdP로 직접 전송되며 CIMD 또는 사전 등록된 클라이언트 ID를 사용해야 합니다. 이 스위치가 생기기 전에 구성된 노드는 지금까지 그래 왔듯 셸을 계속 켭니다.
두 메커니즘 모두 의도적으로 계속 제공됩니다. 클라이언트는 스펙의 순서(사전 등록 → CIMD → DCR)대로 선택하므로, CIMD를 지원하지 않는 클라이언트는 이전과 똑같이 등록 shim을 계속 사용합니다. 각 클라이언트가 어떤 메커니즘을 택했는지는 로그에서 확인할 수 있습니다. 재시작 후 CIMD 클라이언트가 처음 보이면 MCP CIMD client authenticated: <url>이 기록되고, IdP가 CIMD를 광고하는데도 등록한 클라이언트는 MCP DCR fallback이 기록됩니다. 이 두 줄이면 서버에 도달하는 모든 클라이언트가 설명됩니다.
CIMD 클라이언트의 토큰은 사전 등록된 클라이언트 ID가 아니라 해당 문서 URL을 audience(대상)로 전달하며, IdP가 CIMD를 광고하는 한 수락됩니다. 이 노드는 자체 allowlist를 따로 유지하지 않으므로 IdP의 허용된 메타데이터 문서 목록이 경계가 됩니다. 그 목록에 있는 CIMD 클라이언트는 claim gate라는 남은 검사만 거치면 이 서버에 도달할 수 있습니다.
서로 다른 MCP 클라이언트가 서로 다른 형식을 탐색하므로 두 well-known 형식을 모두 광고합니다 — 둘 다 노출하세요. 모든 인스턴스의 라우트가 /mcp/<path> 및 /.well-known/*/mcp/<path> 형태를 공유하므로, 한 벌의 와일드카드 규칙으로 현재와 미래의 모든 mcp-server 노드를 처리할 수 있습니다 (모두 동일한 도메인/업스트림을 통해 접근 가능한 경우). 새 path를 추가할 때 리버스 프록시를 변경할 필요가 없습니다. 예를 들어, Caddy를 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 자체는 실제로 등록된 라우트가 아닌 경로에 404를 반환하므로, 와일드카드 규칙이 각각 배포된 mcp-server 노드가 이미 등록한 것 이상을 노출하지 않습니다. 특정 path를 다른 경로들과 다른 도메인에서 접근 가능하게 해야 한다면, 해당 path에 자체 caddy_N 사이트 블록을 지정하세요 (또는 위의 호스트 이름 필터링과 결합하세요).
ID 공급자가 지원해야 하는 사항 (lib/mcp-auth.js와 동일한 요구 사항):
Discovery를 지원하는 OIDC 공급자 — 엔드포인트는
‹issuerUrl›/.well-known/openid-configuration에서 읽어 오며, discovery를 사용할 수 없으면 PocketID의 경로 레이아웃으로 폴백합니다.JWT 액세스 토큰 — 공급자의 JWKS에 게시된 키로 서명 (토큰은 로컬에서 검증됩니다. opaque/introspection 전용 액세스 토큰은 지원되지 않습니다.)
공개 클라이언트(public client) — PKCE(S256) 사용, grant types
authorization_code+refresh_token, MCP 클라이언트의 redirect URI가 허용 목록에 등록되어 있어야 합니다 (Claude.ai의 경우:https://claude.ai/api/mcp/auth_callback). Redirect URI는 ID 공급자에서만 구성·검증됩니다. 이 노드는 더 이상 자체 allowlist를 유지하지 않으므로, IdP의 와일드카드 지원(예: PocketID)이 그대로 작동합니다. 클라이언트 시크릿은 더 이상 지원되지 않습니다. 공개 client-registration 엔드포인트는 구성된 시크릿을 모든 호출자에게 넘겨주었기 때문에 실제로 비밀이 될 수 없었습니다. 이전 버전에서 저장된 시크릿이 아직 남아 있으면 경고와 함께 무시됩니다. IdP 클라이언트를 public으로 전환한 다음 노드의 config를 열고 Done을 클릭하고 배포하면 저장된 시크릿이 삭제되고 경고가 사라집니다.
Caddy(리버스 프록시) + PocketID(ID 공급자) + Claude.ai 및 Hermes(MCP 클라이언트) 조합으로 테스트되었습니다. 위 라우트를 전달하는 리버스 프록시 뒤에서 JWT 액세스 토큰을 발급하는 스펙 준수 OIDC 공급자라면 동일하게 작동할 것입니다.
예제
바로 가져와 사용할 수 있는 9가지 플로우는 examples/에서 확인하세요 (Jellyfin, Calibre, Docker, Music Assistant, Radarr, iRobot/rest980, Overseerr, Sonarr, Spotify). 각 플로우에는 자체 mcp-server 노드(서버 설명이 미리 채워져 있고, Server URL/Identity provider는 직접 입력할 수 있도록 비워져 있음)와 mcp-in/mcp-out 도구가 포함되어 있습니다 — 나만의 도구를 연결할 때 좋은 참고 자료입니다.
개발
npm install
npm test라이선스
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