12 KiB
AX HUB MCP Server
Agent Builder와 Tool Service 사이의 stateless MCP 실행 계층이다. Agent Builder가 tools/call에 명시한 단일 Tool을 JSON-RPC 2.0과 inputSchema로 검증하고, 서버가 관리하는 metadata에 따라 Tool Service를 호출한다.
이 서버는 Tool을 추천하거나 사용자 의도를 판단하지 않는다. 업무 규칙은 Tool Service가, Tool 선택과 사용자·Agent별 노출 정책은 Agent Builder가 소유한다.
기술 기준
- Java 21, Spring Boot 3.5.11, Spring MVC
- MCP Java SDK 2.0.0의
mcp-json-jackson2: protocol 상수·표준 result 모델·JSON Schema 검증에만 사용 - Spring Data Redis: 선택적 공유 cache
- Spring AI MCP Starter/transport: 사용하지 않음
SDK 적용 경계는 MCP Java SDK 선택적 도입 설계를 따른다.
빠른 시작
필수 조건은 JDK 21이다. 기본 profile은 local이며 Redis 없이 Tool Service 매니페스트를 먼저 조회하고, 최초 조회 실패 시 config/local-core-tools-manifest-sample-v1.json을 fallback으로 사용한다.
.\gradlew.bat check
.\gradlew.bat bootRun
빌드는 외부 저장소에서 코드 스타일 도구를 내려받지 않는다. 폐쇄망에서 검사 하나 때문에 빌드 전체가 시작되지 못하는 상황을 만들지 않기 위해서다. 서식 검사는 저장소 안의 테스트가 소유한다.
Java 포맷은 .idea/codeStyles/Project.xml의 IntelliJ IDEA 코드 스타일로 고정한다. 이 파일은 저장소에 포함되어 있어 IDE에서 자동으로 적용된다. Java 소스의 줄바꿈은 운영체제와 무관하게 LF이며 .gitattributes가 commit 시점에 이를 강제한다.
두 가지가 보장하는 범위가 다르다.
| 무엇이 | 보장하는 것 | 조건 |
|---|---|---|
CodeStyleContractTest |
LF 줄바꿈, 탭 없음, 후행 공백 없음, 파일 끝 개행, 미사용 import 없음 | 항상 (test에 포함) |
| IntelliJ formatter | 4칸 들여쓰기, 줄바꿈 스타일, 단순 lambda·다중 표현식 분리 | IDEA_FORMATTER 설정 시에만 |
IDEA_FORMATTER가 없으면 IntelliJ formatter 단계는 경고를 남기고 건너뛴다. IntelliJ가 없는 CI나 폐쇄망 빌드에서 빌드가 깨지지 않게 하기 위한 것이며, 그 환경에서는 들여쓰기와 줄바꿈이 검증되지 않는다는 뜻이다. 도구 없이 판정할 수 있는 규칙은 그때도 계속 검사된다.
포맷터로 코드를 실제로 정리하려면 경로를 지정하고 ideaFormat을 실행한다. CodeStyleContractTest는 검사만 하고 고쳐 주지 않는다.
$env:IDEA_FORMATTER='C:/Program Files/JetBrains/IntelliJ IDEA 2026.1.4/bin/format.bat'
.\gradlew.bat ideaFormat
줄 폭 160자는 코드 스타일의 권장값이며 기존 코드에 소급 적용되지 않는다. 강제 대상이 아니다.
local Tool 파일을 바꾸려면 다음 환경변수에 Spring resource 경로를 지정한다.
$env:MCP_LOCAL_TOOL_REGISTRY_FILE='file:C:/path/local-tools.json'
공개 계약
- 공개 endpoint:
POST https://{global.mcpHost}{deployments.<key>.publicPath} - 예:
https://mcp-dev.apps.example.internal/mcp/processing-critical - OpenShift Route는 공개 path로 MCP Service만 선택하고, 컨테이너가 같은 path를 직접 처리
- Method:
initialize,notifications/initialized,tools/list,tools/call - Response: 항상 단일
application/jsonJSON-RPC response Accept: application/json, text/event-stream: 호환 목적으로 수용하지만 SSE 경로는 제공하지 않음- 공개 endpoint의
GET:405 Method Not Allowed MCP-Protocol-Version:initialize이후 필수, 현재2025-11-25Mcp-Session-Id: initialize lifecycle 추적용 correlation 값이며 서버 세션이 아님
정확한 요청·응답과 오류 의미는 Agent Builder-MCP 현재 계약 v0.3이 정본이다. Agent Builder는 공개 URL마다 별도 MCP로 등록하고 initialize한다. URL 사이에 lifecycle correlation이나 Tool 목록을 공유하지 않는다.
Tool metadata와 실행
| 환경 | Tool 원천 | Redis |
|---|---|---|
local |
local JSON fixture | 사용 안 함 |
운영(ocp) |
이 배포가 보는 Tool Service 매니페스트를 주기적으로 pull | 성공 snapshot 공유와 warm start에만 사용 |
요청 경로의 tools/list와 tools/call은 in-memory snapshot만 읽는다. 운영 refresh는 bundle별 last-good을 유지하고, 모든 bundle에 사용 가능한 성공본이 있을 때만 aggregate를 교체한다. 조회 실패만으로 Tool을 제거하지 않으며 정상 매니페스트에서 삭제가 확인될 때만 반영한다. 코드는 bundle N개 병합을 지원하지만 운영 배포의 bundle은 항상 하나다(ADR-0007).
Tool 실행 주소는 local _meta.endpoint 또는 운영 baseEndpoint 설정에서만 정한다. Agent Builder의 arguments와 Tool Service 매니페스트는 호출 대상을 바꿀 수 없다.
운영 매니페스트와 장애 처리의 wire 계약은 Tool Service-MCP bundle 조회 계약 v0.2를 따른다.
Correlation과 로그
호출자가 보내는 헤더는 다섯 개이며 모두 선택값이다. 값은 그대로 Tool Service 요청 헤더로 bypass한다.
| 헤더 | 의미 | 없을 때 |
|---|---|---|
guid |
요청 하나를 끝까지 따라가는 상관 값(UUID) | 서버가 생성 |
x-request-id |
개별 HTTP 요청 ID | 서버가 생성 |
mcp-session-id |
initialize lifecycle 상관 값 | 전달하지 않음 |
employee-no |
암호화된 사원번호 | 전달하지 않음 |
virtual-employee-no |
암호화된 가상사원번호(상담사 등 비사원) | 전달하지 않음 |
employee-no와 virtual-employee-no는 MCP가 복호화하지 않는 불투명 값이다. 형식이나 의미를 해석하지 않고, 개행이 섞여 downstream 헤더가 조작되는 것만 막은 뒤 그대로 전달한다.
MDC는 사용하지 않는다. 로그에는 guid와 x-request-id만 남기며 사원 식별자는 암호문이라도 기록하지 않는다. request/response body와 credential도 남기지 않는다.
Authorization은 mcp.tool-client.forward-authorization 설정이 켜진 경우에만 전달한다. MCP는 이 값을 해석하지 않는다.
인증과 권한
이 서버는 인증도 인가도 하지 않는다. 요청자 신원을 검증하지 않고, Tool 실행 권한을 판단하지 않으며, 사원 식별자를 복호화하지 않는다. 결정과 근거는 ADR-0006이다.
| 책임 | 주체 |
|---|---|
| 외부 호출자를 Agent Builder로 제한 | OpenShift Route IP allowlist |
| MCP Pod 직접 접근 제한 | 플랫폼 NetworkPolicy |
| 사용자 인증과 Tool 실행 권한 | Agent Builder |
| 사원 식별자 복호화(KMS)와 업무 권한 | Tool Service |
⚠️ Route IP allowlist와 NetworkPolicy는 선택 사항이 아니다. 외부 요청은 Route가 Agent Builder의 고정 egress CIDR만 받고, backend 요청은 ingress controller 또는 허용된 Agent Builder namespace에서만 MCP Pod에 도달한다. 실제 CIDR을 넣지 않은 배포는 운영에 사용할 수 없다. HelmDeploymentContractTest가 두 경계가 Chart에서 빠지지 않도록 고정한다.
운영 설정
운영 설정은 Helm Chart가 만드는 ConfigMap이 담당한다. identity와 bundle 설정을 환경변수로 나열하지 않는 이유는 항목이 흩어질수록 인덱스 실수가 조용한 오라우팅이 되기 때문이다.
identity는 {배포 이름}-{global.env}로 template이 조립한다. 현재 Redis cache 구현이 이 값을 사용하지만, Redis key namespace와 공유 정책은 아직 확정되지 않았으므로 extension-points.md에서 합의한다.
- 업무 포트:
SERVER_PORT(기본 8080) - management 포트:
MANAGEMENT_SERVER_PORT(운영 기본 9090) - 상태:
/actuator/health/liveness,/actuator/health/readiness - bundle 진단: management 포트의
/actuator/toolBundles
readiness는 첫 Tool discovery 시도가 끝나고 usable in-memory snapshot이 있을 때만 UP이다. 원천 장애 중에도 기존 memory 또는 Redis last-good이 있으면 서비스를 유지하고, 아무 성공본도 없으면 트래픽을 받지 않는다.
MCP 배포 하나는 Tool Service 하나만 본다(ADR-0007). 대상을 늘리는 방법은 bundle 목록을 늘리는 것이 아니라 배포를 하나 더 만드는 것이다. 외부에서는 같은 host의 고유 path로 각 배포를 노출하고 컨테이너가 그 path를 그대로 처리한다(ADR-0009). 배포는 업무 × 중요도 등급으로 나뉘며, 등급이 replica 수와 PodDisruptionBudget을 정한다.
배포 정의는 Helm Chart 하나뿐이다. 배포 토폴로지는 values.yaml이, 환경 차이는 values-{dev,test,prod}.yaml이 소유한다. 설치할 배포 하나는 --set으로 고른다.
helm upgrade --install processing-critical-mcp deploy/helm/mcp-server -f deploy/helm/mcp-server/values-dev.yaml --set deploymentKey=processing-critical -n <namespace>
MCP Server와 Tool Service는 같은 namespace에 배포한다. 그래서 values에는 Tool Service의 이름만 적고 주소는 template이 조립한다. 환경마다 URL을 반복해 적지 않으므로 오타로 엉뚱한 곳을 호출할 수 없다.
deployments:
processing-critical:
name: processing-critical-mcp
service: processing-critical-tools # ← 이름만. 주소는 template이 만든다
namePrefix: "processing." # ← 업무 단위. 등급을 넣지 않는다
tier: critical
publicPath: /mcp/processing-critical # ← 같은 환경 host 안에서 유일
배포가 10개든 20개든 파일 수는 늘지 않는다. 자세한 사용법은 deploy/README.md에 있다.
평문 manifest가 필요하면 helm template으로 만든다. 별도 YAML을 저장소에 두지 않는다 — 두 벌은 반드시 어긋난다.
빌드·이미지·배포 실행 방식은 사내 표준 CI/CD가 담당하며 이 저장소가 정하지 않는다. 배포 시 알아야 할 앱 제약은 deploy/README.md에 정리했다.
문서 R&R
| 문서 | 책임 |
|---|---|
| README | 프로젝트 진입점과 실행 방법 |
| architecture.md | 현재 코드 구조, 요청 흐름, 내부 책임과 장애 동작 |
| Agent Builder-MCP contracts | Agent Builder와의 HTTP/JSON-RPC wire 계약 |
| Tool Service-MCP contracts | 매니페스트와 Tool 실행 wire 계약 |
| decisions | 결정 이유와 대안 이력 |
| extension-points.md | 아직 미합의인 항목과 운영 보완 작업 |
| codex-workflow.md | 저장소 작업 규칙과 공개 정책 |
Superseded/Rejected 문서는 이력일 뿐 현재 구현 근거가 아니다. 코드나 공개 계약을 변경할 때는 가까운 테스트와 해당 현재 계약을 함께 수정한다. 변경을 마치기 전에 실행할 검증 명령은 AGENTS.md의 완료 기준이 정본이다.