forked from kimhyungsik/ax_hub_mcp_tool
feat: expose tool timeout and retry metadata
All checks were successful
Deploy Tools / deploy (push) Successful in 1m27s
All checks were successful
Deploy Tools / deploy (push) Successful in 1m27s
This commit is contained in:
60
README.md
60
README.md
@@ -1,8 +1,8 @@
|
||||
# DAP WAS MCP Tool Pods
|
||||
# DASMT MCP Tool Pods
|
||||
|
||||
신한라이프 업무 기능을 MCP(Model Context Protocol) Tool로 제공하는 Java 멀티 모듈 프로젝트입니다. 각 업무 모듈은 독립 실행 가능한 Spring Boot 애플리케이션이며, MCP Streamable HTTP와 REST 실행 API를 함께 제공합니다.
|
||||
|
||||
이 저장소에는 DATMS(Gateway) 애플리케이션이 포함되어 있지 않습니다. DATMT는 Gateway로 Tool을 push 등록하지 않으며, 각 Pod가 `GET /tool-manifest`를 제공하면 DATMS가 이 Manifest를 pull하여 Tool 목록을 구성합니다. Tool 조회와 직접 실행은 각 Pod에서도 자체적으로 처리합니다.
|
||||
이 저장소에는 DATMS(Gateway) 애플리케이션이 포함되어 있지 않습니다. DASMT는 Gateway로 Tool을 push 등록하지 않으며, 각 Pod가 `GET /tool-manifest`를 제공하면 DATMS가 이 Manifest를 pull하여 Tool 목록을 구성합니다. Tool 조회와 직접 실행은 각 Pod에서도 자체적으로 처리합니다.
|
||||
|
||||
## 기술 기준
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- Spring Boot 3.5.11
|
||||
- MCP Java SDK 2.0.0
|
||||
- Spring AI Community MCP Annotations 0.9.0
|
||||
- Jackson 2.20.1
|
||||
- Jackson 2.19.4 (Spring Boot BOM)
|
||||
- MapStruct, MyBatis, Redis, Kafka, Resilience4j
|
||||
- JUnit 5
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
| `dat-was-lib` | MCP 서버, Tool 스캔·실행, Schema, Manifest, 보안, MCI/EAI/HTTP 연동 공통 기능 | - | - |
|
||||
| `dat-was-cus` | 고객·CRM·VOC·웹 콘텐츠 관리 Tool | 8084 | 51 |
|
||||
| `dat-was-sal` | 영업·청구·인수·동의·현장지원 Tool | 8082 | 52 |
|
||||
| `dat-was-pro` | 상품·계약·고객·GA 설계사 Tool | 8085 | 50 |
|
||||
| `dat-was-pro` | 상품 영역 Tool(개인고객 상세조회·모집수수료 공시) | 8085 | 2 |
|
||||
| `dat-was-sys` | IAM·시스템 상태·공지·점검·배포 Tool | 8086 | 51 |
|
||||
|
||||
업무 모듈에는 총 204개의 `@McpTool` 선언이 있습니다. 현재 업무 모듈의 `src/main/resources`에는 별도 `tool-definitions` YAML이 없으며, Tool 메타데이터는 어노테이션과 `@GrowToolHint`를 기준으로 생성됩니다.
|
||||
업무 모듈에는 총 156개의 `@McpTool` 선언이 있습니다. 현재 업무 모듈의 `src/main/resources`에는 별도 `tool-definitions` YAML이 없으며, Tool 메타데이터는 어노테이션과 `@GrowToolHint`를 기준으로 생성됩니다.
|
||||
|
||||
현재 업무 구현은 개발·연동 검증 단계입니다. `dat-was-sal`의 `cmm_claim_search`와 `cmm_memo_retriever`는 각각 MCI와 HTTP Client 흐름을 사용하며, 나머지 Tool은 외부 시스템을 변경하지 않는 모의 응답을 중심으로 구현되어 있습니다.
|
||||
|
||||
@@ -60,20 +60,20 @@ Tool Pod
|
||||
4. `ToolPodMcpToolSynchronizer`가 Tool을 MCP SDK 서버에 등록합니다.
|
||||
5. REST와 MCP 요청은 공통 `McpToolExecutionService`를 통해 실행됩니다.
|
||||
|
||||
DATMT 내부에는 `/registry/register`, `/registry/deregister` 호출이나 주기적인 Gateway heartbeat 전송이 없습니다. `ToolRegistryHeartbeatSender`라는 클래스명은 호환성을 위해 남아 있지만 현재 역할은 로컬 Tool 스캔과 메타데이터 생성뿐입니다.
|
||||
DASMT 내부에는 `/registry/register`, `/registry/deregister` 호출이나 주기적인 Gateway heartbeat 전송이 없습니다. `ToolRegistryHeartbeatSender`라는 클래스명은 호환성을 위해 남아 있지만 현재 역할은 로컬 Tool 스캔과 메타데이터 생성뿐입니다.
|
||||
|
||||
### DATMS 연동 방식
|
||||
|
||||
```text
|
||||
DATMS
|
||||
└─ GET {DATMT Pod URL}/tool-manifest
|
||||
└─ GET {DASMT Pod URL}/tool-manifest
|
||||
└─ bundleId + revision + tools[]
|
||||
└─ 각 Tool endpoint: {Pod URL}/mcp/{toolName}
|
||||
```
|
||||
|
||||
- `mcp.manifest.bundle-id`는 Manifest를 제공하는 Pod의 고유 식별자이며 필수입니다.
|
||||
- 현재 값은 `was-sal`, `was-cus`, `was-pro`, `was-sys`입니다.
|
||||
- DATMS에 설정한 bundle ID와 DATMT가 반환하는 `bundleId`가 일치해야 같은 Tool bundle로 관리됩니다.
|
||||
- DATMS에 설정한 bundle ID와 DASMT가 반환하는 `bundleId`가 일치해야 같은 Tool bundle로 관리됩니다.
|
||||
- `mcp.manifest.name-prefix`가 비어 있지 않으면 모든 Tool 이름이 해당 prefix로 시작해야 합니다.
|
||||
- Manifest 내용이 바뀌면 `revision`이 증가하며, `If-None-Match`가 일치하면 `304 Not Modified`를 반환합니다.
|
||||
|
||||
@@ -88,6 +88,7 @@ DATMS
|
||||
| 로컬 Tool 메타데이터 조회 | `GET` | `/mcp/api/v1/tools/local` |
|
||||
| Tool 직접 실행 | `POST` | `/mcp/{toolName}` |
|
||||
| Tool Manifest 조회 | `GET` | `/tool-manifest` |
|
||||
| 카테고리별 Tool Manifest 조회 | `GET` | `/tool-manifest/{categoryKey}` |
|
||||
|
||||
`GET /tool-manifest`는 `If-None-Match` 요청 헤더를 지원합니다. Manifest가 변경되지 않았으면 `304 Not Modified`를 반환합니다.
|
||||
|
||||
@@ -103,7 +104,7 @@ $headers = @{
|
||||
'X-Request-Id' = 'request-local-001'
|
||||
'X-Request-Time' = '2026-08-25T12:34:56+09:00'
|
||||
'X-Vrtl-Praf-No' = 'V100001'
|
||||
'X-App-Code' = 'DATMT'
|
||||
'X-App-Code' = 'DASMT'
|
||||
'X-Project-Code' = 'AXHUB'
|
||||
'X-User-Ip' = '10.0.0.10'
|
||||
'X-Caller-Ip' = '10.0.0.20'
|
||||
@@ -123,11 +124,11 @@ Invoke-RestMethod `
|
||||
|
||||
### 요청 헤더 계약
|
||||
|
||||
DATMS가 DATMT Tool Service를 호출할 때 사용하는 헤더는 다음과 같습니다. HTTP 헤더 이름은 대소문자를 구분하지 않지만, 문서와 구현에서는 아래 표기를 기준으로 사용합니다.
|
||||
DATMS가 DASMT Tool Service를 호출할 때 사용하는 헤더는 다음과 같습니다. HTTP 헤더 이름은 대소문자를 구분하지 않지만, 문서와 구현에서는 아래 표기를 기준으로 사용합니다.
|
||||
|
||||
| 헤더 | 필수 여부 | 용도 | 전달 동작 |
|
||||
|---|---|---|---|
|
||||
| `X-Tool-Server-API-Key` | 인증 설정 시 필수 | DATMS와 DATMT 사이의 Tool Server 인증 | `mcp.security.api-key` 또는 `api-keys`와 비교 |
|
||||
| `X-Tool-Server-API-Key` | 인증 설정 시 필수 | DATMS와 DASMT 사이의 Tool Server 인증 | `mcp.security.api-key` 또는 `api-keys`와 비교 |
|
||||
| `X-Guid` | 선택 | 업무 호출 상관관계 식별자 | 실행 로그, 성공 응답, 하위 HTTP 호출로 전달 |
|
||||
| `X-Praf-No` | 선택 | 실제 사용자 사번 | 세션 조회와 하위 HTTP 호출로 전달 |
|
||||
| `X-Request-Id` | 선택 | 요청 추적 식별자 | 실행 로그, 성공 응답, 하위 HTTP 호출로 전달 |
|
||||
@@ -142,7 +143,7 @@ DATMS가 DATMT Tool Service를 호출할 때 사용하는 헤더는 다음과
|
||||
| `X-Agent-Id` | 선택 | 호출 Agent 식별자 | 하위 HTTP 호출로 전달 |
|
||||
| `mcp-session-id` | 선택 | MCP 세션 식별자 | 성공 응답과 하위 HTTP 호출로 전달 |
|
||||
|
||||
REST 경로 `/mcp/{toolName}`은 Controller가 위 헤더를 직접 읽습니다. MCP Streamable HTTP 경로 `/mcp`는 `McpRequestHeaderFilter`가 동일한 헤더를 `McpRequestHeaderContext`에 저장한 뒤 Tool 실행과 하위 HTTP 호출에 전달합니다. 헤더가 없는 하위 HTTP 호출에는 `X-ANONYMOUS-REQ: AXHUB-TOOL`이 설정됩니다.
|
||||
`McpRequestHeaderFilter`는 URI에 `/mcp`가 포함된 요청에서 위 헤더를 `McpRequestHeaderContext`에 저장하므로 REST `/mcp/{toolName}`과 MCP Streamable HTTP `/mcp`, `/mcp/message`에 모두 적용됩니다. REST 경로는 `BusinessToolController`도 동일한 헤더를 직접 읽어 실행 서비스에 전달합니다. 헤더가 없는 하위 HTTP 호출에는 `X-ANONYMOUS-REQ: AXHUB-TOOL`이 설정됩니다.
|
||||
|
||||
기존 `guid`, `employee-no`, `virtual-employee-no` 헤더는 지원하지 않습니다.
|
||||
|
||||
@@ -260,13 +261,15 @@ http://localhost:8086/swagger-ui/index.html
|
||||
| `PORT` | Pod 수신 포트 | 모듈별 기본 포트 |
|
||||
| `TOOL_SERVER_API_KEY` | DATMS가 `X-Tool-Server-API-Key`로 전달할 공통 인증 Key | `tool-server-key` |
|
||||
| `AXHUB_TOOL_URL` | Manifest의 Tool endpoint 생성에 사용할 Pod 외부 URL | `http://localhost:${server.port}` |
|
||||
| `AXHUB_GATEWAY_URL` | 프로필 및 Compose 호환용 Gateway URL. 현재 DATMT의 push 등록에는 사용하지 않음 | `http://localhost:8081` |
|
||||
| `AXHUB_GATEWAY_URL` | 프로필 및 Compose 호환용 Gateway URL. 현재 DASMT의 push 등록에는 사용하지 않음 | `http://localhost:8081` |
|
||||
| `SPRING_DATA_REDIS_HOST` | Redis 호스트 | `localhost` |
|
||||
| `SPRING_DATA_REDIS_PORT` | Redis 포트 | `6379` |
|
||||
| `GLOW_COMMUNICATION_MCI_HOST` | MCI 대상 호스트 | 프로필별 설정 |
|
||||
| `GLOW_COMMUNICATION_MCI_PORT` | MCI 대상 포트 | 프로필별 설정 |
|
||||
|
||||
`mcp.security.api-key`는 단일 DATMS 공통 Key를, `mcp.security.api-keys`는 `API Key → tenant ID` 형태의 다중 Key를 지원합니다. 둘 중 하나라도 설정되어 있으면 올바른 `X-Tool-Server-API-Key`가 없는 `/rpc/**`, `/mcp/**` 요청은 `401 Unauthorized`가 됩니다. 두 설정이 모두 비어 있을 때만 익명 요청을 허용합니다. 현재 네 업무 Pod의 기본 `application.yml`은 단일 Key를 설정하므로 API Key 없이 Tool을 호출할 수 없습니다.
|
||||
`mcp.security.api-key`는 단일 DATMS 공통 Key를, `mcp.security.api-keys`는 `API Key → tenant ID` 형태의 다중 Key를 지원합니다. `ApiKeyInterceptor`는 Spring MVC가 처리하는 `/rpc/**`, `/mcp/**` 요청에 적용되므로 REST `/mcp/{toolName}`과 `/mcp/api/v1/tools/local`은 올바른 `X-Tool-Server-API-Key`가 없으면 `401 Unauthorized`가 됩니다. 두 설정이 모두 비어 있을 때는 해당 MVC 요청을 익명으로 허용합니다. 현재 네 업무 Pod의 기본 `application.yml`은 단일 Key를 설정합니다.
|
||||
|
||||
반면 `/mcp`와 `/mcp/message`는 별도 Servlet으로 등록되어 Spring MVC `HandlerInterceptor`를 통과하지 않습니다. 현재 구현만으로는 이 두 MCP Streamable HTTP 경로에 `ApiKeyInterceptor` 인증이 적용되지 않으므로, 운영 배포 전 Servlet Filter 또는 전용 MCP 인증 계층을 추가해야 합니다.
|
||||
|
||||
## Scaffold
|
||||
|
||||
@@ -335,7 +338,7 @@ mcp:
|
||||
|
||||
## Docker Compose
|
||||
|
||||
`docker-compose.yml`은 DATMT의 네 업무 Pod만 정의합니다.
|
||||
`docker-compose.yml`은 DASMT의 네 업무 Pod만 정의합니다.
|
||||
|
||||
| 서비스 | 컨테이너 포트 | 호스트 포트 |
|
||||
|---|---:|---:|
|
||||
@@ -355,14 +358,14 @@ Compose 파일의 용도와 현재 주의점은 다음과 같습니다.
|
||||
|
||||
| 파일 | 용도 | 현재 소스 기준 주의점 |
|
||||
|---|---|---|
|
||||
| `docker-compose.yml` | DATMT 네 Pod 단독 실행 | `gateway` 서비스가 없지만 push 등록이 제거되어 DATMT 시작에는 필요하지 않음 |
|
||||
| `docker-compose.local.yml` | DATMS와 DATMT의 로컬 통합 구성 | 두 저장소가 같은 상위 디렉터리에 있는 구조를 가정 |
|
||||
| `docker-compose.prod.yml` | DATMS와 DATMT의 개발 프로필 기반 OCI 구성 | 저장소의 runner 등록 토큰을 운영 Secret으로 분리해야 함 |
|
||||
| `docker-compose.yml` | DASMT 네 Pod 단독 실행 | `gateway` 서비스가 없지만 push 등록이 제거되어 DASMT 시작에는 필요하지 않음 |
|
||||
| `docker-compose.local.yml` | DATMS와 DASMT의 로컬 통합 구성 | 두 저장소가 같은 상위 디렉터리에 있는 구조를 가정 |
|
||||
| `docker-compose.prod.yml` | DATMS와 DASMT의 개발 프로필 기반 OCI 구성 | 저장소의 runner 등록 토큰을 운영 Secret으로 분리해야 함 |
|
||||
|
||||
`docker-compose.local.yml`과 `docker-compose.prod.yml`의 build context는 각각 `./dat-was-DATMS`, `./dat-was-datmt`입니다. 현재 파일 위치에서 사용할 때는 context 기준을 두 저장소의 상위 디렉터리로 맞춰야 합니다.
|
||||
`docker-compose.local.yml`과 `docker-compose.prod.yml`의 build context는 각각 `./dat-was-DATMS`, `./dat-was-dasmt`입니다. 현재 파일 위치에서 사용할 때는 context 기준을 두 저장소의 상위 디렉터리로 맞춰야 합니다.
|
||||
|
||||
```powershell
|
||||
# DATMT 저장소 디렉터리에서 실행
|
||||
# DASMT 저장소 디렉터리에서 실행
|
||||
docker compose --project-directory .. -f docker-compose.local.yml up --build
|
||||
```
|
||||
|
||||
@@ -370,7 +373,7 @@ docker compose --project-directory .. -f docker-compose.local.yml up --build
|
||||
|
||||
## OpenShift (Kubernetes) 배포
|
||||
|
||||
OpenShift 개발 환경용 Kustomize 매니페스트는 [`k8s/`](k8s/)에 있습니다. 대상은 DATMT의 네 Tool Pod뿐이며 DATMS(Gateway)의 Deployment·Service·Route는 이 저장소에서 만들지 않습니다.
|
||||
OpenShift 개발 환경용 Kustomize 매니페스트는 [`k8s/`](k8s/)에 있습니다. 대상은 DASMT의 네 Tool Pod뿐이며 DATMS(Gateway)의 Deployment·Service·Route는 이 저장소에서 만들지 않습니다.
|
||||
|
||||
```text
|
||||
k8s/
|
||||
@@ -389,7 +392,7 @@ k8s/
|
||||
| `was-pro` | 8085 | `http://was-pro:8085` |
|
||||
| `was-sys` | 8086 | `http://was-sys:8086` |
|
||||
|
||||
Gateway가 다른 namespace에 있으면 `was-sal.axhub-datmt-dev.svc`와 같은 FQDN을 사용하고, NetworkPolicy에서 Gateway namespace의 ingress를 별도로 허용해야 합니다.
|
||||
Gateway가 다른 namespace에 있으면 `was-sal.axhub-dasmt-dev.svc`와 같은 FQDN을 사용하고, NetworkPolicy에서 Gateway namespace의 ingress를 별도로 허용해야 합니다.
|
||||
|
||||
### 반영 전 설정
|
||||
|
||||
@@ -399,11 +402,11 @@ Gateway가 다른 namespace에 있으면 `was-sal.axhub-datmt-dev.svc`와 같은
|
||||
2. [`k8s/base/configmap.yaml`](k8s/base/configmap.yaml)의 `CHANGE_ME` MCI·EXTMCI·EAI 호스트
|
||||
3. 실제 Secret 값
|
||||
|
||||
`datmt-runtime-secrets` Secret은 Git에 저장하지 않고 OpenShift namespace에서 별도로 생성합니다. 최소한 `TOOL_SERVER_API_KEY`는 DATMS가 전달하는 `X-Tool-Server-API-Key`와 같은 값이어야 합니다. DB 계정·비밀번호, API Key, 인증서 비밀번호 등도 이 Secret으로 관리합니다.
|
||||
`dasmt-runtime-secrets` Secret은 Git에 저장하지 않고 OpenShift namespace에서 별도로 생성합니다. 최소한 `TOOL_SERVER_API_KEY`는 DATMS가 전달하는 `X-Tool-Server-API-Key`와 같은 값이어야 합니다. DB 계정·비밀번호, API Key, 인증서 비밀번호 등도 이 Secret으로 관리합니다.
|
||||
|
||||
```powershell
|
||||
# runtime-secrets.env는 저장소 밖에 보관합니다.
|
||||
oc -n axhub-datmt-dev create secret generic datmt-runtime-secrets `
|
||||
oc -n axhub-dasmt-dev create secret generic dasmt-runtime-secrets `
|
||||
--from-env-file=runtime-secrets.env
|
||||
```
|
||||
|
||||
@@ -419,7 +422,7 @@ Secret이 없으면 각 Deployment의 `envFrom.secretRef`를 해석할 수 없
|
||||
# OpenShift 로그인 및 project 선택 후
|
||||
oc kustomize k8s/overlays/dev
|
||||
oc apply -k k8s/overlays/dev
|
||||
oc get deployment,pod,svc -n axhub-datmt-dev
|
||||
oc get deployment,pod,svc -n axhub-dasmt-dev
|
||||
```
|
||||
|
||||
적용 전에는 `oc kustomize k8s/overlays/dev | oc apply --dry-run=client -f -`로 서버 측 스키마 검증을 수행합니다. 현재 매니페스트의 readiness/liveness probe는 TCP 포트 확인 방식입니다. Actuator health endpoint를 추가한 뒤에는 HTTP readiness/liveness probe로 변경하는 것을 권장합니다.
|
||||
@@ -430,8 +433,9 @@ oc get deployment,pod,svc -n axhub-datmt-dev
|
||||
|
||||
현재 구현을 운영 환경에 노출하기 전에 아래 항목을 반드시 점검해야 합니다.
|
||||
|
||||
- API Key 인터셉터는 `/rpc/**`, `/mcp/**`에 적용됩니다. 따라서 `/mcp`, `/mcp/{toolName}`, `/mcp/api/v1/tools/local`은 인증 대상입니다.
|
||||
- `/tool-manifest`는 위 인터셉터 경로 밖에 있어 현재 API Key 인증 대상이 아닙니다. 내부망·Ingress 정책 또는 별도 인증이 필요한지 운영 기준을 확인해야 합니다.
|
||||
- API Key 인터셉터는 Spring MVC의 `/rpc/**`, `/mcp/**` Handler에 적용됩니다. REST `/mcp/{toolName}`과 `/mcp/api/v1/tools/local`은 인증 대상입니다.
|
||||
- 별도 Servlet인 `/mcp`, `/mcp/message`는 MVC 인터셉터를 우회하므로 현재 API Key 인증 대상이 아닙니다. 운영 노출 전에 별도 인증을 추가해야 합니다.
|
||||
- `/tool-manifest`와 `/tool-manifest/{categoryKey}`는 위 인터셉터 경로 밖에 있어 현재 API Key 인증 대상이 아닙니다. 내부망·Ingress 정책 또는 별도 인증이 필요한지 운영 기준을 확인해야 합니다.
|
||||
- 네 업무 Pod의 기본 Key는 모두 `tool-server-key`입니다. 운영에서는 반드시 별도 Secret으로 교체하고 DATMS의 `X-Tool-Server-API-Key` 값과 일치시켜야 합니다.
|
||||
- 설정된 단일 Key와 다중 Key가 모두 없을 때만 익명 요청이 허용됩니다.
|
||||
- `mcp.security.tenant-domains`는 설정 객체에 바인딩되지만 Tool별 인가에 사용되지 않습니다.
|
||||
@@ -467,5 +471,5 @@ oc get deployment,pod,svc -n axhub-datmt-dev
|
||||
3. `validateMcpToolNames`와 모듈·전체 테스트를 실행합니다. Tool YAML 정의를 추가한 경우에는 V17 항목을 별도 검증합니다.
|
||||
4. 모듈 테스트와 전체 테스트를 실행합니다.
|
||||
5. 로컬 Pod에서 `/mcp/api/v1/tools/local`과 `/tool-manifest`를 확인합니다.
|
||||
6. DATMS의 bundle ID, Pod Manifest URL, Tool Server API Key가 DATMT 설정과 일치하는지 확인합니다.
|
||||
6. DATMS의 bundle ID, Pod Manifest URL, Tool Server API Key가 DASMT 설정과 일치하는지 확인합니다.
|
||||
7. REST와 MCP 양쪽에서 동일한 Tool 결과·요청 헤더 전달·오류 계약을 확인합니다.
|
||||
|
||||
Reference in New Issue
Block a user