Files
dap-was-dapms/docs/contracts/portal-mcp/protocol-v0.1-registry.md
koseokmin 4e4c341f5d Portal registry 결정을 문서로 확정하고 ADR 상태를 코드에 맞춘다
ADR-0007은 MCP 배포 하나가 Tool Service 하나만 보게 했으나 구현은
이미 Portal이 route와 endpoint를 소유하는 구조다. 결정 문서가 없어
ADR-0007이 Accepted로 남은 채 코드와 정반대되는 내용을 현재 설계
근거처럼 제시하고 있었다.

ADR-0013이 그 경로를 확정하고 ADR-0007 전체와 ADR-0009 결정 4를
대체한다. ADR-0010은 Tool 실행 주소의 소유자가 설정이 아니라 Tool
Service 매니페스트라는 6078852의 결정을 사후 기록한다. 두 ADR이
정본으로 인용하는 Portal-MCP 계약 v0.1도 함께 넣는다.

ADR-0013은 main 현재 코드에 맞춰 세 곳을 고쳤다. route 간 갱신 격리
부재는 6653030이 해소해 격리 표로 옮겼고, 제거된 mcp.portal.route-key
항목은 뺐다. 남은 위험 둘(Portal 모드 warm start 미동작, readiness의
route별 상태 미노출)은 코드에서 유효함을 확인해 남긴다.

ADR-0010이 기록하는 대로 endpoint 검증에는 도메인 허용목록이 없고
NetworkPolicy도 Ingress만 선언한다. 매니페스트 원천의 신뢰성이 곧
outbound 대상의 신뢰성이다. McpProperties와 application.yml의 주석이
이 결정과 반대로 남아 있다는 사실도 ADR에 적었다. 코드는 바꾸지
않는다.

192개 테스트 통과. 문서 상대링크 깨짐 없음.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 17:23:38 +09:00

6.2 KiB

Portal-MCP Tool Server Registry 계약 v0.1

  • 상태: Implemented (MCP 서버 측 구현 완료, 포털 측 합의 대기)
  • 기준일: 2026-08-22
  • 조회 위치: mcp.portal.registry-url — HTTP(S) Portal API 또는 file:/classpath: 로컬 리소스
  • 활성 조건: mcp.portal.enabled=true
  • 구현: PortalToolRegistryClient

1. 계약 범위와 원칙

포털은 route별로 어떤 Tool Server가 있는가만 알려 준다. Tool 목록과 Tool 실행 주소는 포털이 아니라 각 Tool Server의 매니페스트에서 온다(Tool Service-MCP Bundle 조회 계약 v0.2, ADR-0010).

원칙 내용
MCP가 가져온다 포털은 registry를 제공만 한다. MCP에 push하지 않는다
포털은 route와 Tool Server만 소유 serviceDomainmanifestPath까지다. Tool 목록·실행 endpoint는 매니페스트가 정한다
registry는 전체 상태 응답은 그 시점 route 전체다. 증분 없음
route 단위 격리 한 route의 manifest 조회 실패가 다른 route의 snapshot을 지우지 않는다
요청 경로는 조회하지 않는다 tools/list·tools/call과 route key 검증은 in-memory snapshot만 본다

2. 응답 형태

두 가지를 모두 받는다. routes가 배열이면 집계형으로, 아니면 단일 route로 해석한다.

집계형 — 한 번의 호출로 모든 route를 받는다. 운영에서 사용한다.

{
  "registryRevision": "portal-registry-2026-08-22-01",
  "routes": [
    {
      "routeKey": "cus",
      "toolServices": [
        {
          "serviceKey": "was-cus",
          "serviceDomain": "https://tool-cus.devjun.net",
          "manifestPath": "/tool-manifest",
          "namePrefix": "",
          "status": "ACTIVE"
        }
      ]
    }
  ]
}

단일 route형routes 없이 최상위에 routeKeytoolServices를 둔다.

{
  "routeKey": "cus",
  "toolServices": [ { "serviceKey": "was-cus", "serviceDomain": "https://tool-cus.devjun.net", "manifestPath": "/tool-manifest", "status": "ACTIVE" } ]
}

3. 필드

필드 위치 필수 MCP 처리
routes[] 최상위 선택 배열이면 집계형. 없으면 최상위를 단일 route로 읽는다
routeKey route 필수 정규화 후 route 식별자. 요청 URL /mcp/{routeKey}와 대조한다
toolServices[] route 필수 이 route가 보는 Tool Server 목록
serviceKey service 필수 bundle id로 사용. 매니페스트의 bundleId와 일치해야 한다
serviceDomain service 필수 Tool Server 주소. 후행 /는 제거한다. 매니페스트의 상대 endpoint를 절대 URL로 바꾸는 기준이 된다
manifestPath service 필수 매니페스트 경로. serviceDomain + manifestPath가 조회 주소다
status service 선택 기본 ACTIVE. 대소문자 무시하고 ACTIVE가 아니면 그 서비스를 건너뛴다
namePrefix service 선택 기본 "". Tool 이름 접두사 검증에 사용한다
registryRevision 최상위 선택 변경 진단·로그용. 호출 대상 결정에는 쓰지 않는다

MCP가 읽지 않는 필드가 있다. 현재 구현은 executeBasePath, displayName, toolEndpoints를 무시한다. 응답에 있어도 오류가 아니지만 동작에 영향을 주지 않으므로, 포털이 이를 근거로 실행 주소를 통제할 수 있다고 가정하면 안 된다.

4. 실패 동작

상황 MCP 동작
registry 조회 실패 이미 확보한 in-memory endpoint 목록 유지. memory가 비어 있으면 mcp.redis.portal-registry-key의 Redis fallback을 읽는다
Redis fallback도 실패 원천 미확보로 처리하고 다음 주기에 재시도
route에 ACTIVE 서비스가 하나도 없음 Portal registry has no active Tool Service로 그 route 조회 실패
한 Tool Server의 매니페스트에 사용 가능한 성공본이 없음 그 route 전체를 실패 처리. 부분 목록을 채택하지 않는다
route 간 Tool name 중복 또는 maxToolsTotal 초과 같은 이유로 실패 처리
registry에서 사라진 route 다음 갱신에 in-memory snapshot에서도 제거

route 단위 격리와 catalog 교체 규칙은 Tool Service 계약 v0.2 §7과 같은 원칙을 따른다. 조회는 route마다 독립이지만, 한 route 안에서는 전부 아니면 전무다.

5. 갱신 주기

  • mcp.portal.refresh-interval-seconds (기본 300초): 포털 registry만 다시 읽는다.
  • mcp.registry.refresh-interval-seconds: 이미 확보한 Tool Server 목록의 매니페스트만 다시 읽는다.

기동 preload는 포털 registry를 먼저 호출한 뒤 매니페스트를 조회한다. 두 주기는 독립이다.

6. 로컬 검증

registry-urlfile: 또는 classpath: 리소스를 지정하면 포털 서버 없이 같은 계약으로 읽는다. 이후 매니페스트 조회·route별 snapshot 갱신·Redis fallback 규칙은 HTTP API를 쓸 때와 동일하다.

mcp:
  portal:
    enabled: true
    registry-url: file:./config/local-toolserver-info-sample-v1.json
    refresh-interval-seconds: 15

7. 저장소의 샘플 파일

두 샘플이 있고, 현재 서로 다르다. 이 계약을 정본으로 삼고 맞춰야 한다.

파일 용도 이 계약과의 차이
config/local-toolserver-info-sample-v1.json 로컬 검증용 MCP가 읽지 않는 displayName을 포함
deploy/portal-registry.json 배포 참고용 MCP가 읽지 않는 executeBasePath를 포함하고 registryRevision이 없다

8. 열린 항목

  • 포털 API의 인증 방식은 이 계약이 정하지 않는다. MCP는 인증·인가를 하지 않으므로(ADR-0006) 네트워크 경계에서 통제한다.
  • registryRevision의 형식을 문자열로 고정할지 정하지 않았다. 현재 구현은 값을 로그·진단에만 쓰므로 형식에 의존하지 않는다.
  • 즉시 refresh 알림: 주기 반영으로 부족하다는 운영 근거가 생길 때 검토한다.