architecture.md를 현재 코드에 맞춘다
문서가 3de052a 시점 상태로 복구되면서 그 뒤 main의 코드 커밋 일곱 개가
반영되지 않았다. 계약 테스트가 보는 범위 밖이라 194건이 통과해도
드러나지 않는다. 코드와 대조해 확인한 아홉 건을 고친다.
사실이 틀린 것:
- 갱신 모델. ToolRegistryRefreshScheduler에는 @Scheduled가 없고
ApplicationReadyEvent 하나만 있다. 갱신은 주기 실행이 아니라 요청
시점 TTL 만료로 일어난다. 설정 키도 refresh-interval-seconds가 아니라
refresh-ttl-seconds다.
- 1:1 배포 전제. ADR-0007은 Superseded이고 코드는 route당 N개 Tool
Service를 병합한다. ADR-0013 기준으로 다시 쓴다.
- route table이 없다는 서술. McpController가 /mcp/{routeKey}를 받고
PortalToolRegistryClient가 bundlesByRoute를, ToolRegistryService가
route별 snapshot을 들고 있다. route 매핑은 애플리케이션 안에 있다.
- 실행 흐름 1번의 publicPath. route key는 URI에서만 결정된다.
빠진 것:
- 클래스 표에 McpRouteKeyValidator, PortalToolRegistryClient,
RedisPortalRegistryCache, AgentRoutingHintsProperties,
LocalFixtureProperties를 추가한다. 앞의 셋은 Portal 모드의 핵심
경로인데 표에 없었다.
- Agent routing hint 절을 새로 쓴다. 9905d52가 들여온 기능이 문서에
전혀 없었다.
- Tool 호출 retry 절을 새로 쓴다. 6526e73의 retrySafeByAnnotation이
destructiveHint를 항상 금지하고 annotations 미선언 Tool은 재시도하지
않는다는 사실이 없었다.
- 스키마 정책 두 클래스를 표에 넣고, 검사 지점이 ToolMetadata 생성자
하나라는 것을 실행 흐름 9번에 적는다.
인용한 식별자 열여섯 개가 코드에 실재하는지, 상대링크가 깨지지 않는지
확인했다. retry 기본값도 application.yml과 대조했다. 194개 테스트 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ MCP는 Agent Builder가 `tools/call`에 명시한 단일 Tool을 실행한다. T
|
||||
|
||||
## 전체 실행 흐름
|
||||
|
||||
1. Agent Builder가 배포별 공개 URL `https://{host}{publicPath}`을 호출한다. OpenShift Route는 host와 path로 MCP Service만 선택하고 원래 path를 컨테이너에 전달한다([ADR-0009](decisions/ADR-0009-container-handles-public-mcp-path.md)). 요청 body·header·Tool 이름은 이 선택에 관여하지 않는다.
|
||||
1. Agent Builder가 route별 공개 URL `https://{host}/mcp/{routeKey}`를 호출한다. OpenShift Route는 host와 path로 MCP Service만 선택하고 원래 path를 컨테이너에 전달한다([ADR-0009](decisions/ADR-0009-container-handles-public-mcp-path.md)). route key는 이 URI에서만 결정하며 설정 기본값으로 보정하지 않는다([ADR-0013](decisions/ADR-0013-portal-owns-route-and-endpoint-registry.md)). Portal 모드에서 route가 없는 `/mcp` 호출은 `route key is required`로 거부한다. 요청 body·header·Tool 이름은 route 선택에 관여하지 않는다.
|
||||
2. 컨테이너의 `mcp.endpoint-path` 전용 `McpExchangeFilter`가 header를 검증/추출하고 요청 전체 deadline을 포함한 `McpRequestContext`를 만든다. 호출자 헤더 다섯 개(`guid`, `x-request-id`, `mcp-session-id`, `employee-no`, `virtual-employee-no`)는 모두 선택값이며, 응답과 downstream Tool 호출에 그대로 전파한다. `guid`는 요청 하나의 end-to-end 상관 값, `x-request-id`는 개별 HTTP 요청 식별자다.
|
||||
3. filter는 크기가 제한된 repeatable request body에서 `method`만 관찰용으로 읽고 `mcp_http_request_received` 로그를 남긴다. body, header 값, credential은 로그에 저장하지 않는다.
|
||||
4. `McpProtocolVersionValidator`가 `initialize`를 제외한 요청의 `MCP-Protocol-Version`을 supported versions와 대조한다. 누락·불일치는 Controller 진입 전 HTTP 400으로 종료한다.
|
||||
@@ -30,7 +30,7 @@ MCP는 Agent Builder가 `tools/call`에 명시한 단일 Tool을 실행한다. T
|
||||
6. `McpMethodHandlerRegistry`가 method를 명시적 handler에 연결한다.
|
||||
7. `tools/list`는 `ToolRegistryService`의 in-memory snapshot에서 실행 metadata를 얻는다. 요청 경로는 Redis를 호출하지 않으므로 Redis 장애·지연이 응답에 영향을 주지 않으며, snapshot이 비어 있는 기동 직후에만 Tool catalog provider를 한 번 조회한다. 이후 `ToolsListHandler`가 MCP SDK의 `Tool`과 `ListToolsResult`로 변환한다. local 기본 구성은 Tool Service 매니페스트를 먼저 조회하고, 최초 실패 시 bundle별 local manifest sample을 cold-start fallback으로 사용한다. 운영은 이 배포가 보는 Tool Service 매니페스트의 사용 가능한 성공본만 원천으로 사용한다.
|
||||
8. `tools/call`은 `ToolsCallHandler`가 표준 MCP의 `params.name`과 object인 `params.arguments`를 검증하고 추출한다.
|
||||
9. `ToolExecutionService`가 표준 Tool name으로 metadata를 확정하고 argument schema를 검증한다. `ToolRoutingService`는 snapshot에 저장된 정확한 Tool endpoint와 metadata timeout으로 HTTP 요청을 만든다. Agent Builder가 보낸 `arguments` 객체는 JSON raw body로 전달하며 MCP가 Tool을 대체 선택하지 않는다.
|
||||
9. `ToolExecutionService`가 표준 Tool name으로 metadata를 확정하고 argument schema를 검증한다. `inputSchema` 자체의 안전성은 실행 시점이 아니라 `ToolMetadata` 생성 시점에 이미 검사됐다. 매니페스트 파싱·local 파일 로딩·Redis snapshot 역직렬화가 모두 같은 생성자를 지나므로 검사 지점은 하나다. `ToolRoutingService`는 snapshot에 저장된 정확한 Tool endpoint와 metadata timeout으로 HTTP 요청을 만든다. Agent Builder가 보낸 `arguments` 객체는 JSON raw body로 전달하며 MCP가 Tool을 대체 선택하지 않는다.
|
||||
10. `arguments`의 어떤 field도 outbound URL 선택에 사용하지 않는다. Portal registry는 Tool Server의 `serviceDomain`과 `manifestPath`만 제공하고, Tool별 실행 endpoint는 Tool Server manifest의 top-level `endpoint` 또는 `_meta.endpoint`에서 가져온다. manifest endpoint가 절대 HTTP(S) URL이면 Tool Server가 제공한 실행 주소 원천으로 허용하고, 상대 경로이면 Portal registry의 `serviceDomain` 뒤에 붙인다.
|
||||
11. `HttpToolClient`가 JDK 공유 HTTP client의 connection pool을 사용해 correlation 헤더와 함께 POST를 실행한다. arguments는 JSON body로 전달하며 Tool read timeout은 metadata timeout과 요청 전체 deadline의 남은 시간 이하로 제한한다. Authorization 전달은 설정으로 통제한다.
|
||||
12. Tool 응답은 요청 payload와 분리해 `response.data`만 사용한다. plain text는 그대로, JSON object/array는 compact JSON string으로 MCP SDK `CallToolResult`/`TextContent`의 `result.content[0].text`에 넣고 outer JSON serializer가 escaping을 처리한다. 호출 소요 시간(ms)은 `result.content[0]._meta.searchTime`으로 반환하고, 정상 결과에도 `isError: false`를 명시한다. Tool 실행·timeout·권한 오류는 JSON-RPC error가 아니라 `isError: true` result로 변환한다. JSON-RPC envelope/params/method 및 서버 구성 오류는 최상위 JSON-RPC `error`로 반환한다.
|
||||
@@ -44,6 +44,7 @@ MCP는 Agent Builder가 `tools/call`에 명시한 단일 Tool을 실행한다. T
|
||||
|---|---|---|
|
||||
| `McpController` | `transport/http` | `mcp.endpoint-path`의 단일 공개 endpoint, parser/handler 연결, notification 202와 initialize UUID header 선택 |
|
||||
| `McpRequestContextFactory` | `transport/http` | 호출자 헤더 5종 추출. correlation 값 형식 검증, 사원 식별자는 해석하지 않고 주입 위험 문자만 차단 |
|
||||
| `McpRouteKeyValidator` | `transport/http` | `/mcp/{routeKey}`의 route가 현재 서버가 아는 route인지 확인하는 transport 전용 port. memory snapshot만 읽고 registry를 직접 참조하지 않아 패키지 경계를 유지 |
|
||||
| `McpRequestContextHolder` | `context` | 요청 수명 ThreadLocal 저장; 세션 저장소가 아님 |
|
||||
| `JsonRpcRequestParser` | `jsonrpc` | JSON-RPC envelope shape 검증과 내부 request 정규화 |
|
||||
| `McpMethodHandlerRegistry` | `method` | `Handler` 전략과 method dispatch를 한 경계에서 관리 |
|
||||
@@ -52,8 +53,12 @@ MCP는 Agent Builder가 `tools/call`에 명시한 단일 Tool을 실행한다. T
|
||||
| `LocalFileToolRegistryClient` | `registry` | 매니페스트 조회를 끈 local profile에서 legacy JSON/manifest fixture를 읽어 테스트 Tool 목록을 제공 |
|
||||
| `ToolBundleDiscovery` | `registry` | 구현상 N개 Tool Service 매니페스트를 병렬 조회·검증하고 bundle별 last-good 상태를 유지. 최초 원격 조회 실패 시에만 설정된 local manifest fallback을 사용하며, 운영 배포는 1개 Bundle만 사용 |
|
||||
| `ToolBundleRegistryClient` | `registry` | 구현상 모든 bundle의 사용 가능한 성공본을 중복·총량 검증 후 하나의 snapshot으로 병합. 운영 배포에서는 단일 Bundle 결과를 채택 |
|
||||
| `PortalToolRegistryClient` | `registry` | Portal registry를 route → Tool Service 목록(`bundlesByRoute`)으로 유지하고 route별 manifest를 조회. 한 route의 실패는 `fetchRouteToolsSafely()`로 격리해 다른 route 수집을 막지 않음 |
|
||||
| `RedisPortalRegistryCache` | `registry` | Portal Registry API 장애 시 endpoint registry JSON을 읽는 선택적 Redis fallback. route별 Tool snapshot key와 분리 |
|
||||
| `ToolSchemaReferencePolicy` | `registry` | `inputSchema`가 문서 밖을 가리키는 `$ref`·`$dynamicRef`와 미지원 dialect를 거부([ADR-0011](decisions/ADR-0011-tool-input-schema-stays-in-document.md)) |
|
||||
| `ToolSchemaPatternPolicy` | `registry` | `pattern` 정규식의 길이·무한 수량자·중첩 반복을 제한하고 `maxLength` 동반을 요구. `patternProperties`는 거부([ADR-0012](decisions/ADR-0012-tool-input-schema-pattern-budget.md)) |
|
||||
| `RedisToolRegistryCache` | `registry` | best-effort Redis snapshot, 실제 read/write 실패를 cache miss로 격리 |
|
||||
| `ToolRegistryRefreshScheduler` | `registry` | 기동 preload와 주기 refresh; 실패 시 애플리케이션 생존 |
|
||||
| `ToolRegistryRefreshScheduler` | `registry` | `ApplicationReadyEvent`에서 warm start와 원천 preload를 한 번 실행. 주기 실행은 하지 않으며 이름과 달리 scheduler가 아니다. 실패해도 애플리케이션은 생존 |
|
||||
| `ToolArgumentValidator` | `execute` | 기존 required/type 오류 계약을 보존하고 MCP SDK JSON Schema 2020-12 검증 적용 |
|
||||
| `ToolExecutionService` | `execute` | 이름 기반 metadata 해석, argument validation, 단일 Tool 실행, HTTP 경계 로그와 오류 mapping |
|
||||
| `ToolRoutingService` | `execute` | 단일 POST endpoint와 timeout 확정, 기본 URI 검증 |
|
||||
@@ -65,6 +70,8 @@ MCP는 Agent Builder가 `tools/call`에 명시한 단일 Tool을 실행한다. T
|
||||
| `McpProtocolVersionValidator` | `transport/http` | `initialize` 이후 HTTP `MCP-Protocol-Version`의 지원 여부 검증; 서버 상태를 저장하지 않음 |
|
||||
| `TraceLogger` | `observability` | context의 guid/requestId를 직접 포함하는 최소 key=value 경계 로그. 사원 식별자는 기록하지 않음 |
|
||||
| `McpExceptionHandler` | `transport/http` | JSON parse, JSON-RPC, 예상 밖 오류의 표준 response 변환 |
|
||||
| `AgentRoutingHintsProperties` | `config` | initialize 응답 `_meta`에 넣을 Tool Server routing manifest 조회 경로 설정(기본 `/tool-service-manifest`) |
|
||||
| `LocalFixtureProperties` | `config` | 실제 Tool Server가 없는 local·dev 환경의 임시 manifest·응답 파일 위치. 기본 비활성이며 실서버 확보 후 제거 대상 |
|
||||
|
||||
Spring Boot 3.5가 관리하는 Jackson 2 databind 모델과 annotation API는 `com.fasterxml.jackson.*` namespace를 사용한다. Registry 응답의 unknown field 무시는 회귀 테스트로 검증한다.
|
||||
|
||||
@@ -145,6 +152,18 @@ Tool 호출 직전마다 `remainingMillis()`로 남은 예산을 계산해 read
|
||||
중복 실행 방지는 Tool Service의 책임이다. retry에서 같은 `guid`를 재사용해 멱등성 키로 삼을지는
|
||||
[미합의 항목](extension-points.md)이며, 합의 전에는 MCP가 이를 보장한다고 가정하지 않는다.
|
||||
|
||||
## Tool 호출 retry
|
||||
|
||||
MCP는 Tool 호출 실패를 제한적으로 재시도한다. 세 조건이 모두 참일 때만 재시도하며, 하나라도 거짓이면 단발 호출이다(`ToolRoutingService:64`).
|
||||
|
||||
1. `mcp.tool-client.retry.enabled`가 참이고 `max-attempts`가 2 이상이다(기본 `true`, `2`).
|
||||
2. HTTP status가 `retry-on-http-status` 목록에 있다(기본 408, 429).
|
||||
3. Tool이 annotations로 안전하다고 선언했다.
|
||||
|
||||
3번은 `ToolMetadata.retrySafeByAnnotation()`이 공개 정의의 annotations로 판단한다. `destructiveHint`가 참이면 **항상 금지**하고, 그렇지 않은 경우에만 `readOnlyHint` 또는 `idempotentHint`가 참이면 허용한다. annotations가 없으면 허용하지 않는다. 즉 **선언하지 않은 Tool은 재시도하지 않는다.**
|
||||
|
||||
재시도는 같은 요청 deadline 안에서 일어나므로 `remainingMillis()` 예산을 넘지 못한다. 멱등성 자체는 Tool Service의 책임이며, MCP는 Tool이 선언한 annotations를 신뢰할 뿐 검증하지 않는다([ADR-0004](decisions/ADR-0004-execution-guardrails.md)).
|
||||
|
||||
## Protocol version 협상과 검증
|
||||
|
||||
- 서버는 `mcp.protocol.supported-versions`와 `mcp.protocol.preferred-version`으로 지원 버전을 명시적으로 관리한다. preferred version은 반드시 supported versions에 포함되어야 한다.
|
||||
@@ -159,6 +178,12 @@ Tool 호출 직전마다 `remainingMillis()`로 남은 예산을 계산해 read
|
||||
- Agent Builder는 MCP 2025-11-25 lifecycle에 따라 `notifications/initialized`를 보낸다. 서버는 이를 저장하거나 이후 요청의 readiness gate로 사용하지 않는다.
|
||||
- `InitializedNotificationHandler`는 id 없는 notification을 HTTP 202으로 수용한다. 이는 Tool 실행 준비 상태를 메모리에 세우는 동작이 아니므로 replica 간 affinity가 필요 없다.
|
||||
|
||||
## Agent routing hint
|
||||
|
||||
`mcp.agent-routing-hints.enabled`가 켜져 있고 요청에 route가 있으면, `initialize` 응답의 `_meta`에 `toolServers` 배열을 실어 보낸다. `InitializeHandler`가 해당 route의 Tool Server에서 `mcp.agent-routing-hints.manifest-path`(기본 `/tool-service-manifest`)를 조회해 받은 JSON을 **변환 없이 그대로** 감싼다.
|
||||
|
||||
기능이 꺼져 있거나 route가 없으면 `_meta`를 붙이지 않고 표준 `initialize` 응답만 반환한다. 이 값은 Agent Builder에 주는 힌트이며 MCP의 Tool 실행 경로는 이를 읽지 않는다.
|
||||
|
||||
## Tool metadata 갱신 장애 시나리오
|
||||
|
||||
요청 경로는 memory만 읽으므로 Redis 상태가 등장하지 않는다.
|
||||
@@ -198,7 +223,7 @@ Redis는 요청 경로의 의존성이 아닌 선택적인 warm-start cache다.
|
||||
|
||||
로컬 검증에서는 `mcp.portal.registry-url`을 `file:./config/local-toolserver-info-sample-v1.json` 같은 Spring resource location으로 지정할 수 있다. 이 경우 MCP는 기동 preload와 주기 endpoint refresh에서 Portal HTTP API를 호출하지 않고 프로젝트 안의 registry JSON을 읽는다. 파일에서 확보한 endpoint 목록 이후의 Tool Server `tool-manifest` 주기 조회, route별 in-memory snapshot 갱신, Redis fallback 규칙은 Portal API를 사용할 때와 동일하다.
|
||||
|
||||
Portal Registry를 사용하는 구성에서는 포털을 route별 Tool Server 목록의 원천으로만 사용한다. MCP는 기동 preload 때 포털 registry API를 먼저 호출해 `serviceDomain`과 `manifestPath`를 확보한 뒤 Tool Server `tool-manifest`를 조회한다. 이후에는 `mcp.registry.refresh-interval-seconds` 주기로 저장된 Tool Server 목록에 대해 manifest만 다시 조회하고, `mcp.portal.refresh-interval-seconds` 주기로 포털 registry만 별도로 갱신한다. 포털 `registryRevision`은 포털 응답 JSON 변경 로그와 Tool Server 목록 변경 진단에 사용하며, Tool Server 내부 tool/schema/revision/endpoint 변경 감지는 MCP의 manifest 주기 조회 결과를 route별 in-memory snapshot에 다시 병합하면서 처리한다. 요청 경로의 `tools/list`와 `tools/call`은 계속 in-memory snapshot만 읽는다. Portal API 조회가 실패하면 이미 확보한 in-memory Tool Server snapshot을 유지하며, cold start처럼 memory가 비어 있을 때만 `mcp.redis.portal-registry-key`의 Redis registry JSON을 fallback으로 읽는다. 이 Portal registry fallback은 route 목록과 Tool Server 목록 확보용이고, route별 Tool snapshot Redis key는 이미 알고 있는 route의 마지막 Tool 목록 fallback에만 사용한다. Redis fallback도 실패하면 Tool Server 원천을 확보하지 못한 것으로 처리하고 다음 주기에서 재시도한다.
|
||||
Portal Registry를 사용하는 구성에서는 포털을 route별 Tool Server 목록의 원천으로만 사용한다. MCP는 기동 preload 때 포털 registry API를 먼저 호출해 `serviceDomain`과 `manifestPath`를 확보한 뒤 Tool Server `tool-manifest`를 조회한다. 이후 갱신은 주기 실행이 아니라 **요청 시점 TTL 만료**로 일어난다. 요청이 들어오면 `ToolRegistryService`가 `mcp.portal.refresh-ttl-seconds`가 지났을 때만 포털 registry를, `mcp.registry.refresh-ttl-seconds`가 지났을 때만 해당 route의 manifest를 다시 조회한다. 아직 snapshot이 없는 route는 TTL과 무관하게 조회하며, 포털 endpoint 목록 변경이 그 route의 마지막 manifest 조회보다 나중이면 TTL이 남아 있어도 manifest를 다시 읽는다. 요청이 없으면 갱신도 일어나지 않는다. 포털 `registryRevision`은 포털 응답 JSON 변경 로그와 Tool Server 목록 변경 진단에 사용하며, Tool Server 내부 tool/schema/revision/endpoint 변경 감지는 MCP의 manifest 주기 조회 결과를 route별 in-memory snapshot에 다시 병합하면서 처리한다. 요청 경로의 `tools/list`와 `tools/call`은 계속 in-memory snapshot만 읽는다. Portal API 조회가 실패하면 이미 확보한 in-memory Tool Server snapshot을 유지하며, cold start처럼 memory가 비어 있을 때만 `mcp.redis.portal-registry-key`의 Redis registry JSON을 fallback으로 읽는다. 이 Portal registry fallback은 route 목록과 Tool Server 목록 확보용이고, route별 Tool snapshot Redis key는 이미 알고 있는 route의 마지막 Tool 목록 fallback에만 사용한다. Redis fallback도 실패하면 Tool Server 원천을 확보하지 못한 것으로 처리하고 다음 주기에서 재시도한다.
|
||||
|
||||
노출 대상 Tool은 그 파일이 정의한다. 목록을 이 문서에 옮겨 적지 않는다. 파일의 공개 필드는 그대로 보존하고 `_meta`와 `endpoint` 실행 정보만 제거해 `tools/list`에 내보낸다. fallback도 원격 매니페스트와 같이 top-level `endpoint` 또는 `_meta.endpoint`를 내부 실행 endpoint로 사용한다.
|
||||
|
||||
@@ -206,9 +231,15 @@ Portal Registry를 사용하는 구성에서는 포털을 route별 Tool Server
|
||||
|
||||
운영 profile에서는 `ToolBundleDiscovery`와 `ToolBundleRegistryClient`만 metadata 원천으로 활성화한다. MCP 배포별 `mcp.bundles`가 Tool Service의 매니페스트와 실행 주소를 선언한다. 운영 Helm 설정에는 fallback 파일을 넣지 않는다. Tool Service는 표준 `name`을 소유하고, MCP는 자기 Bundle 안에서 형식·설정된 `namePrefix`·중복을 검증하되 이름을 재작성하지 않는다. 서로 다른 MCP 배포 간 이름의 전역 유일성은 Tool Service·플랫폼의 변경 절차가 보장한다. Redis는 선택적인 공유 last-good cache일 뿐 Tool 목록의 원천이 아니다.
|
||||
|
||||
**`mcp.bundles`는 N개를 지원하지만 운영 배포에서는 항상 한 항목이다.** MCP 배포 하나가 Tool Service 하나만 보기로 했기 때문이다([ADR-0007](decisions/ADR-0007-one-mcp-per-tool-service.md)). 대상을 늘리는 방법은 이 목록을 늘리는 것이 아니라 MCP 배포를 하나 더 만드는 것이다. 그래야 등급이 다른 Tool Service의 조회 실패가 서로의 카탈로그 갱신을 막지 않는다. 다중 bundle 병합 코드는 유지하되 Helm Chart가 1개로 잠그고 `HelmDeploymentContractTest`가 그 사실을 검사한다.
|
||||
**MCP 배포 하나가 N개 route를 서비스하고, route 하나에 N개 Tool Service가 붙는다**([ADR-0013](decisions/ADR-0013-portal-owns-route-and-endpoint-registry.md)). 카탈로그 병합 단위는 route다. 이전의 1:1 전제([ADR-0007](decisions/ADR-0007-one-mcp-per-tool-service.md), `Superseded`)는 매핑이 배포 시점에 확정된다는 가정 위에 있었으나, 매핑의 원천이 Portal로 옮겨지면서 성립하지 않는다.
|
||||
|
||||
각 배포는 같은 환경 host의 고유 `publicPath`를 가진 OpenShift Route로 노출된다([ADR-0009](decisions/ADR-0009-container-handles-public-mcp-path.md)). Route는 path로 Service만 선택하고 컨테이너가 같은 값을 `mcp.endpoint-path`로 직접 처리한다. Java 애플리케이션에는 route table이나 다중 Registry를 추가하지 않는다. Deployment·snapshot·readiness·connection pool은 path별로 분리되고, 공유되는 장애 지점은 OpenShift ingress와 DNS다.
|
||||
등급별 물리 격리는 이 구조에서 얻지 못한다. 무엇이 남는지는 ADR-0013의 격리 표가 정본이며, 요약하면 route별 snapshot 보관과 route 간 갱신은 격리되지만 프로세스 자원·배포·재기동은 전 route가 공유한다.
|
||||
|
||||
`mcp.bundles` 기반 1:1 구성은 코드에 남아 있어 local 검증과 1:1 배포에서 유효하고 `HelmDeploymentContractTest`가 그 계약을 검사한다. 내부망 운영 대상인지는 ADR-0013이 정하지 않는다.
|
||||
|
||||
Route는 path로 Service만 선택하고 컨테이너가 `mcp.endpoint-path`와 그 아래 `{routeKey}`를 직접 처리한다([ADR-0009](decisions/ADR-0009-container-handles-public-mcp-path.md)의 결정 4는 ADR-0013이 대체한다). `McpController`는 `${mcp.endpoint-path}`와 `${mcp.endpoint-path}/{routeKey}` 두 패턴을 받는다.
|
||||
|
||||
route 매핑은 애플리케이션 안에 있다. `PortalToolRegistryClient`가 Portal registry를 route → Tool Service 목록으로 유지하고, `ToolRegistryService`가 route별 snapshot을 들고, `McpRouteKeyValidator`가 등록되지 않은 route를 controller 진입 전에 거부한다. snapshot과 Redis key는 route별로 분리되지만 Deployment·readiness·connection pool은 전 route가 공유하며, 공유되는 장애 지점은 프로세스 자체와 OpenShift ingress·DNS다.
|
||||
|
||||
운영 상태는 외부 ingress가 아니라 management port(기본 9090)의 `GET /actuator/toolBundles`로 확인한다.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user