forked from kimhyungsik/ax_hub_mcp_tool
Delete all remaining Python scripts
This commit is contained in:
747
README.md
747
README.md
@@ -1,606 +1,271 @@
|
||||
# AX HUB MCP Tool
|
||||
# DAP WAS Tool Pods
|
||||
|
||||
신한라이프 업무 시스템과 AI Agent를 연결하는 MCP(Model Context Protocol) Gateway 및 Tool 서버 프로젝트입니다.
|
||||
신한라이프 업무 시스템과 MCP(Model Context Protocol) 클라이언트를 연결하는 독립형 Tool WAS 프로젝트입니다. 이 저장소에는 Gateway가 포함되어 있지 않습니다. 각 Tool Pod가 직접 MCP Streamable HTTP와 REST 실행 API를 제공하고, 업무 요청은 `UseCase → Converter → MCI/EAI 연동`으로 처리합니다.
|
||||
|
||||
Agent는 Gateway에서 Tool 목록과 입력 명세를 받고, Gateway는 권한과 정책을 확인한 뒤 Tool 서버로 요청을 전달합니다. 업무 Tool은 `DTO → UseCase → Converter → MCI/EAI Client` 구조로 레거시 시스템을 호출합니다.
|
||||
> 이 문서는 현재 `main`의 구현과 설정을 기준으로 합니다. 과거 `dap-gateway`, Chat API, SSE, 외부 Tool Registry/Heartbeat 관련 문서는 현재 저장소의 동작 범위가 아니므로 포함하지 않습니다.
|
||||
|
||||
## 전체 흐름
|
||||
## 구성
|
||||
|
||||
```text
|
||||
AI Agent / MCP Client
|
||||
│
|
||||
▼
|
||||
MCP Gateway (dap-gateway)
|
||||
├─ Tool 목록·스키마 제공
|
||||
├─ Tool 권한·승인·가드레일 확인
|
||||
├─ Redis Registry 및 실행 추적
|
||||
└─ 대상 Tool 서버로 라우팅
|
||||
│
|
||||
▼
|
||||
Tool Server (dap-was-sms / dap-was-oth)
|
||||
└─ BusinessToolController
|
||||
│
|
||||
▼
|
||||
UseCase → Converter → MCI/EAI Client → 레거시 시스템
|
||||
MCP Client
|
||||
├─ Streamable HTTP: /mcp
|
||||
└─ REST: POST /mcp/{tool-name}
|
||||
│
|
||||
▼
|
||||
Tool Pod (dap-was-oth 또는 dap-was-sms)
|
||||
│
|
||||
├─ LocalToolScanner: @McpTool / @McpFunction 메타데이터 생성
|
||||
├─ BusinessToolController: 입력 검증·DTO 변환·동적 실행
|
||||
├─ ToolManifestController: /tool-manifest 제공
|
||||
└─ UseCase → Converter → MCI/EAI Client → 대상 시스템
|
||||
```
|
||||
|
||||
## 현재 구조와 목표 구조
|
||||
## Gradle 모듈
|
||||
|
||||
현재는 Gateway와 두 개의 Tool 애플리케이션으로 구성됩니다.
|
||||
|
||||
```text
|
||||
현재: Gateway + SMS Tool Pod + OTH Tool Pod
|
||||
목표: Gateway + 고객 Pod + 영업 Pod + 지급/납입 Pod + 알림 Pod + 인사 Pod + 공통 Pod
|
||||
```
|
||||
|
||||
`dap-was-oth`에는 여러 업무 카테고리가 함께 있습니다. AA 협의 후에는 부서·업무 소유권 단위로 Tool 서버, 이미지, Pod, 배포 파이프라인을 분리합니다. 이 목표 구조는 향후 전환 방향이며 현재 구현 완료 상태가 아닙니다.
|
||||
|
||||
## Gradle 멀티모듈
|
||||
|
||||
| 모듈 | 역할 | 실행 포트 |
|
||||
| 모듈 | 역할 | 기본 포트 |
|
||||
|---|---|---:|
|
||||
| `dap-gateway` | MCP 진입점, Tool Registry, 라우팅, 권한·가드레일, Chat API | 8081 |
|
||||
| `dap-was-lib` | 공통 어노테이션, Controller, JSON Schema, MCI/EAI 지원, 보안·로깅 공통 기능 | 라이브러리 |
|
||||
| `dap-was-sms` | SMS/알림 Tool 서버 | 8082 |
|
||||
| `dap-was-oth` | 공통·업무·샘플·MCI Tool 서버 | 8084 |
|
||||
| `dap-was-lib` | MCP 어노테이션, 스캐너, 실행 Controller, Manifest, Schema, MCI/EAI/로깅/보안 공통 기능 | - |
|
||||
| `dap-was-oth` | 공통·기타·샘플·SOL 업무 Tool Pod | 8084 |
|
||||
| `dap-was-sms` | SMS/알림 업무 Pod | 8082 |
|
||||
|
||||
기술 기준은 Java 21, Spring Boot 4, Gradle, Spring AI MCP Server, Redis, MapStruct, MyBatis, Resilience4j입니다.
|
||||
기술 기준은 Java 21, Spring Boot 4.0.5, Gradle Wrapper 8.14.3, Spring AI MCP Server WebMVC, Redis, MapStruct, MyBatis, Resilience4j입니다.
|
||||
|
||||
## 환경 (Environment)
|
||||
## 현재 제공 API
|
||||
|
||||
| 항목 | 버전 / 기준 |
|
||||
아래 API는 각 Tool Pod가 직접 제공합니다. OTH Pod의 로컬 주소는 `http://localhost:8084`, SMS Pod는 `http://localhost:8082`입니다.
|
||||
|
||||
| 목적 | 메서드 | 경로 | 구현 |
|
||||
|---|---|---|---|
|
||||
| MCP Streamable HTTP 전송 | MCP 프로토콜 | `/mcp` | `ToolMcpServerConfiguration` |
|
||||
| Pod에서 스캔한 Tool 메타데이터 조회 | `GET` | `/mcp/api/v1/tools/local` | `BusinessToolController` |
|
||||
| 이름으로 Tool 직접 실행 | `POST` | `/mcp/{name}` | `BusinessToolController` |
|
||||
| Pod 소유 Manifest 조회 | `GET` | `/tool-manifest` | `ToolManifestController` |
|
||||
|
||||
`GET /tool-manifest`는 `If-None-Match` 요청 헤더를 지원하며, 내용이 바뀌지 않으면 `304 Not Modified`를 반환합니다. 응답에는 bundle ID, revision, Tool 목록, 입력 Schema, 실행 endpoint, annotation/meta 정보가 포함됩니다. Manifest는 `LocalToolScanner`의 전체 스캔 목록을 사용하므로 `visible = false` 또는 `register = false`인 항목도 포함될 수 있습니다.
|
||||
|
||||
`GET /mcp/api/v1/tools/local`은 Manifest 검증이나 MCP 세션을 열지 않고, 현재 Pod에서 스캔한 `ToolMetadata` 목록을 반환합니다.
|
||||
|
||||
### REST 실행 예시
|
||||
|
||||
Tool 이름은 `@McpFunction.name` 값입니다. 예를 들어 OTH Pod의 `oth.smp.weather.inquiry`는 다음처럼 호출합니다.
|
||||
|
||||
```powershell
|
||||
$headers = @{
|
||||
'trace-id' = 'trace-local-001'
|
||||
'request-id' = 'request-local-001'
|
||||
}
|
||||
|
||||
Invoke-RestMethod `
|
||||
-Method Post `
|
||||
-Uri 'http://localhost:8084/mcp/oth.smp.weather.inquiry' `
|
||||
-Headers $headers `
|
||||
-ContentType 'application/json' `
|
||||
-Body '{"city":"Seoul"}'
|
||||
```
|
||||
|
||||
Controller는 이름을 찾은 뒤 요청 JSON을 첫 번째 DTO 매개변수로 변환합니다. 입력 Schema 검증 실패는 `422 INVALID_PARAM`, 존재하지 않는 Tool은 `404 TOOL_NOT_FOUND`, 실행 예외는 `502 TOOL_ERROR` 응답입니다. `trace-id`와 `request-id`는 성공 응답 헤더로 다시 전달됩니다.
|
||||
|
||||
## Tool 검색과 MCP 노출 규칙
|
||||
|
||||
애플리케이션 기동 시 `LocalToolScanner`는 Spring Bean에서 `@McpTool`과 `@McpFunction` 메타데이터를 읽어 로컬 Tool 목록을 만듭니다. 기동 완료 후 `ToolPodMcpToolSynchronizer`는 이 목록 중 `visible = true`인 Tool만 MCP SDK 서버에 추가합니다.
|
||||
|
||||
| 속성 | 현재 구현에서의 의미 |
|
||||
|---|---|
|
||||
| Java | 21 |
|
||||
| Spring Boot | 4.0.5 |
|
||||
| Gradle Wrapper | 8.14.3 |
|
||||
| Spring AI BOM | 2.0.0 |
|
||||
| Spring AI MCP Server | `spring-ai-starter-mcp-server-webmvc` |
|
||||
| Redis Client | Lettuce 6.6.0.RELEASE |
|
||||
| Resilience | Resilience4j 2.2.0 |
|
||||
| MyBatis Spring Boot Starter | 3.0.3 |
|
||||
| MapStruct | 1.5.5.Final |
|
||||
| Lombok | 1.18.32 |
|
||||
| JSON Schema Validator | networknt 1.4.0 |
|
||||
| OpenAPI UI | springdoc 2.5.0 |
|
||||
| 컨테이너 실행 | Docker Compose |
|
||||
| `visible` | `false`이면 MCP SDK의 Tool 등록에서 제외됩니다. |
|
||||
| `register` | 스캐너 메타데이터의 `isRegistered` 값과 내부 `registeredTools` 목록에만 반영됩니다. 현재 저장소에는 외부 Registry 전송 구현이 없습니다. |
|
||||
| `namespace` | 비어 있지 않으면 Tool 이름 앞에 `{namespace}_`가 붙습니다. 기본 설정은 빈 문자열입니다. |
|
||||
| `enabled` | Manifest의 `_meta.enabled` 값으로 노출됩니다. 현재 synchronizer는 이 값으로 별도 필터링하지 않습니다. |
|
||||
| `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` | MCP Tool annotation과 Manifest annotation에 반영됩니다. |
|
||||
|
||||
프로젝트는 JDK 21을 기준으로 컴파일됩니다. IntelliJ에서는 Project SDK, Gradle JVM, Run Configuration JRE를 모두 JDK 21로 맞춰야 합니다.
|
||||
## 5분 빠른 시작
|
||||
`POST /mcp/{name}`의 동적 실행은 `visible` 및 `register` 값으로 차단하지 않습니다. 따라서 직접 호출을 막아야 하는 Tool은 네트워크 경계와 별도 인증·인가 정책으로 보호해야 합니다.
|
||||
|
||||
Docker와 JDK 21이 준비된 로컬 개발 환경 기준입니다.
|
||||
### 현재 Tool 선언 현황
|
||||
|
||||
```powershell
|
||||
# 1. Redis와 MCI Mock만 먼저 실행
|
||||
$env:ACTIVE_PROFILE = 'local'
|
||||
docker compose up -d redis mci-mock
|
||||
`dap-was-oth`에는 실제 `@McpFunction` 선언이 17개 있습니다. 도메인은 다음과 같습니다.
|
||||
|
||||
# 2. Gateway 실행 (새 PowerShell)
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
$env:OPENROUTER_API_KEY = '<개발용 비밀 저장소의 키>'
|
||||
.\gradlew.bat :dap-gateway:bootRun
|
||||
|
||||
# 3. OTH Tool 실행 (또 다른 PowerShell)
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
|
||||
$env:AXHUB_TOOL_URL = 'http://localhost:8084'
|
||||
.\gradlew.bat :dap-was-oth:bootRun
|
||||
```
|
||||
|
||||
Tool 서버가 기동된 뒤 아래 URL로 등록된 Tool 목록을 확인합니다.
|
||||
|
||||
```text
|
||||
http://localhost:8081/mcp/api/v1/tools/list
|
||||
```
|
||||
|
||||
SMS Tool도 함께 확인하려면 별도 PowerShell에서 아래 명령을 실행합니다.
|
||||
|
||||
```powershell
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
|
||||
$env:AXHUB_TOOL_URL = 'http://localhost:8082'
|
||||
.\gradlew.bat :dap-was-sms:bootRun
|
||||
```
|
||||
|
||||
전체 컨테이너 환경이 필요하면 개별 실행 대신 다음 한 줄을 사용합니다.
|
||||
|
||||
```powershell
|
||||
$env:ACTIVE_PROFILE = 'local'
|
||||
docker compose up -d --build
|
||||
```
|
||||
## Tool 등록과 실행
|
||||
|
||||
### 등록과 목록 제공
|
||||
|
||||
1. Tool 서버 기동 시 `ToolRegistryHeartbeatSender`가 `@McpTool`, `@McpFunction`을 스캔합니다.
|
||||
2. Tool 이름, 설명, 입력 JSON Schema, `categoryKey`, 연동 방식, 실행 URL을 메타데이터로 생성합니다.
|
||||
3. Gateway Redis Registry에 등록·Heartbeat 정보를 전송합니다.
|
||||
4. Agent와 관리 화면은 Gateway에서 Tool 목록과 명세를 조회합니다.
|
||||
|
||||
### 실행
|
||||
|
||||
1. Agent가 Gateway에 Tool 이름과 입력값을 보냅니다.
|
||||
2. Gateway가 Tool 존재 여부, 허용 Tool, 쓰기 승인, 가드레일을 확인합니다.
|
||||
3. Gateway가 Tool 서버의 `/mcp/{toolName}`으로 요청을 전달합니다.
|
||||
4. `BusinessToolController`가 Tool 메서드를 찾아 DTO로 변환하고 JSON Schema를 검증합니다.
|
||||
5. UseCase가 업무 흐름을 수행합니다.
|
||||
6. Converter가 업무 DTO를 인터페이스 ID 기반 MCI 요청 DTO로 변환합니다.
|
||||
7. MCI/EAI Client가 레거시를 호출하고 결과를 Tool 응답으로 반환합니다.
|
||||
|
||||
## 주요 URL
|
||||
|
||||
로컬에서 Gateway를 직접 실행할 때의 기준입니다. Docker Compose를 사용하면 Gateway 호스트 포트는 `8281`입니다.
|
||||
|
||||
| 용도 | 메서드 | URL |
|
||||
| 도메인 | 예시 Tool 이름 | 내용 |
|
||||
|---|---|---|
|
||||
| Tool 목록 | `GET` | `http://localhost:8081/mcp/api/v1/tools/list` |
|
||||
| Tool 실행 | `POST` | `http://localhost:8081/mcp/api/v1/tools/call` |
|
||||
| Tool Markdown 문서 | `GET` | `http://localhost:8081/mcp/api/v1/tools/docs/markdown` |
|
||||
| 카테고리별 SSE MCP 연결 | `GET` | `http://localhost:8081/mcp/sse/{categoryKey}` |
|
||||
| 카테고리별 MCP 호출 채널 | `POST` | `http://localhost:8081/mcp/custom/{categoryKey}` |
|
||||
| Chat 스트리밍 API | `POST` | `http://localhost:8081/api/chat` |
|
||||
| Scaffold API | `POST` | `http://localhost:8081/api/v1/scaffold/pod` 또는 `/tool` |
|
||||
| `cmm` | `oth.cmm.claim.search`, `oth.cmm.customer.detail`, `oth.cmm.meta.table` | 공통·고객·계약·청구·메타 기능 |
|
||||
| `smp` | `oth.smp.weather.inquiry`, `oth.smp.exchange-rate.inquiry` | 샘플·조회 기능 |
|
||||
| `sol` | `oth.sol.request.list`, `oth.sol.request.detail` | SOL 요청 조회 |
|
||||
|
||||
`/mcp/sse/{categoryKey}`는 SSE 연결을 여는 전송 경로이고, `/mcp/custom/{categoryKey}`는 같은 카테고리의 MCP 요청을 처리하는 호출 경로입니다. 두 URL은 역할이 다릅니다.
|
||||
`dap-was-oth`에는 `categoryKey = "oth"`인 `Onnba3011UseCase`도 있으나, 현재 `@McpFunction` 선언은 없습니다. `dap-was-sms`도 `@McpTool(routingType = "EAI", categoryKey = "notification")`은 선언되어 있지만, `SmsToolUseCase`/구현체에 `@McpFunction`이 없습니다. 따라서 현 상태에서 두 영역은 스캐너, `/tool-manifest`, MCP SDK에 노출되는 호출 가능 Tool을 만들지 않습니다. MCP Tool로 제공하려면 각 계약 메서드에 `@McpFunction`을 선언해야 합니다.
|
||||
|
||||
## Tool 개발 표준
|
||||
## Tool 개발 방식
|
||||
|
||||
| 구성 요소 | 책임 |
|
||||
|---|---|
|
||||
| `XxxRequest`, `XxxResponse` | Agent/Tool 관점의 입력·응답 DTO |
|
||||
| `XxxUseCase` | Tool 계약과 MCP 메타데이터 선언 |
|
||||
| `XxxUseCaseImpl` | 업무 흐름 조합과 Client 호출 |
|
||||
| `XxxConverter` | 업무 DTO와 레거시 인터페이스 DTO 사이 변환 |
|
||||
| `MciXxxClient` | Glow/MCI 또는 EAI 통신 호출 |
|
||||
| `INTERFACE_ID_I`, `INTERFACE_ID_O` | 인터페이스 ID 기준 MCI 요청·응답 DTO |
|
||||
|
||||
### Tool 선언
|
||||
|
||||
Tool 그룹에는 `@McpTool`, Agent가 호출하는 함수에는 `@McpFunction`을 사용합니다.
|
||||
Tool 그룹은 인터페이스에 `@McpTool`, Agent가 호출하는 메서드는 `@McpFunction`을 선언합니다. `@McpTool`은 Spring `@Component` 별칭이므로 Tool 인터페이스와 구현체는 Spring Bean으로 구성되어야 합니다.
|
||||
|
||||
```java
|
||||
@McpTool(routingType = "MCI", categoryKey = "claim")
|
||||
public interface ClaimInquiryUseCase {
|
||||
|
||||
@McpFunction(
|
||||
name = "claim_inquiry",
|
||||
displayName = "보험금 청구 조회",
|
||||
description = "청구 번호로 보험금 청구 상태를 조회합니다.",
|
||||
mappingId = "CLM00000001"
|
||||
name = "oth.claim.inquiry.detail",
|
||||
displayName = "청구 상세 조회",
|
||||
description = "청구 번호로 청구 상세를 조회합니다.",
|
||||
mappingId = "CLM00000001",
|
||||
readOnlyHint = true
|
||||
)
|
||||
ClaimInquiryResponse inquire(ClaimInquiryRequest request);
|
||||
}
|
||||
```
|
||||
|
||||
`categoryKey`는 Tool의 업무 그룹입니다. Gateway의 목록 필터링, Agent 권한, 동적 MCP 서버 구분에 사용하므로 합의된 업무 키를 사용합니다.
|
||||
|
||||
- `MCI`: 사내 MCI 인터페이스 호출
|
||||
- `EAI`: EAI 연동
|
||||
- `DIRECT`: 외부 HTTP 또는 내부 직접 연동
|
||||
|
||||
### 변환 원칙
|
||||
|
||||
UseCase 구현체는 Tool 요청을 레거시 요청과 섞어 쓰지 않습니다. Converter에서 변환한 뒤 Client에 전달합니다.
|
||||
구현체에는 업무 흐름만 두고, Tool DTO와 레거시 인터페이스 DTO의 변환은 Converter에 둡니다.
|
||||
|
||||
```java
|
||||
@Override
|
||||
public ClaimInquiryResponse inquire(ClaimInquiryRequest request) {
|
||||
CLM00000001_I mciRequest = converter.toMciRequest(request);
|
||||
CLM00000001_O mciResponse = mciClmClient.callClm00000001(mciRequest);
|
||||
return converter.toResponse(mciResponse);
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
class ClaimInquiryUseCaseImpl implements ClaimInquiryUseCase {
|
||||
private final ClaimInquiryConverter converter;
|
||||
private final MciClaimClient client;
|
||||
|
||||
@Override
|
||||
public ClaimInquiryResponse inquire(ClaimInquiryRequest request) {
|
||||
ClaimMciRequest legacyRequest = converter.toMciRequest(request);
|
||||
ClaimMciResponse legacyResponse = client.call(legacyRequest);
|
||||
return converter.toResponse(legacyResponse);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
MCI 입출력 객체는 업무 이름이 아니라 인터페이스 ID를 기준으로 둡니다.
|
||||
`@McpFunction.name`은 소문자 점 표기 형식으로 작성합니다. 현재 선언은 `oth.cmm.claim.search`, `oth.smp.weather.inquiry`처럼 `{pod}.{domain}.{service}.{action}` 패턴을 사용합니다. `validateMcpToolNames` Gradle 작업은 모든 Tool 모듈을 대상으로 이름 형식과 중복을 검사하며, 각 `bootJar` 전에 실행됩니다.
|
||||
|
||||
## Input/Output Schema
|
||||
|
||||
입력 Schema는 다음 우선순위로 결정됩니다.
|
||||
|
||||
1. `inputSchemaResource`에 지정한 classpath JSON Schema
|
||||
2. `inputSchema`에 인라인으로 지정한 JSON Schema
|
||||
3. 요청 DTO의 `@McpValidation`을 이용한 자동 생성 Schema
|
||||
|
||||
출력 검증은 선택 사항입니다. 다음 중 하나가 있을 때만 반환값을 검증합니다.
|
||||
|
||||
1. `outputSchemaResource`
|
||||
2. `outputSchema`
|
||||
3. 반환 DTO의 `@McpOutputSchema`와 `@McpValidation`
|
||||
|
||||
복잡한 Schema 리소스는 Tool 모듈에 둡니다. 현재 OTH의 청구 검색 예제는 다음 리소스를 사용합니다.
|
||||
|
||||
```text
|
||||
CLCNNB00001_I : CLCNNB00001 요청 DTO
|
||||
CLCNNB00001_O : CLCNNB00001 응답 DTO
|
||||
dap-was-oth/src/main/resources/tool-schemas/cmm/
|
||||
├─ claim-search-resource-input-schema.json
|
||||
└─ claim-search-resource-output-schema.json
|
||||
```
|
||||
|
||||
예를 들어 `Onnba3011Request`와 `CLCNNB00001_I`는 같은 업무 데이터를 담을 수 있지만 같은 객체가 아닙니다. 둘 사이의 변환 책임은 `Onnba3011Converter`에 둡니다.
|
||||
입력 검증에는 JSON Schema Draft 7이 사용됩니다. 출력 Schema 검증에 실패하면 `500 INVALID_TOOL_RESPONSE`을 반환합니다.
|
||||
|
||||
### 새 Tool 추가 체크리스트
|
||||
|
||||
새 업무 Tool을 추가할 때는 아래 순서로 확인합니다.
|
||||
|
||||
- [ ] 소속 모듈과 `categoryKey`를 업무 소유 조직 기준으로 결정한다.
|
||||
- [ ] Agent 입력·응답 DTO인 `XxxRequest`, `XxxResponse`를 만든다.
|
||||
- [ ] `XxxUseCase`에 `@McpTool`을 선언하고, 호출 메서드에 `@McpFunction`의 이름·설명·연동 ID를 선언한다.
|
||||
- [ ] `XxxUseCaseImpl`에서 업무 흐름만 조합한다.
|
||||
- [ ] `XxxConverter`에 업무 DTO ↔ 인터페이스 ID DTO 변환을 둔다.
|
||||
- [ ] `MciXxxClient`와 `INTERFACE_ID_I`, `INTERFACE_ID_O`를 인터페이스 ID 기준으로 만든다.
|
||||
- [ ] DTO에 Bean Validation을 선언하고, 중첩 DTO가 있으면 입력 Schema와 검증 대상에 포함되는지 확인한다.
|
||||
- [ ] 조회·변경 작업 특성에 따라 `readOnlyHint`, `requiresApproval`, `idempotentHint`를 설정한다.
|
||||
- [ ] 단위 테스트를 작성하고 `:dap-was-lib:test` 또는 대상 모듈 테스트를 실행한다.
|
||||
- [ ] Tool 서버 기동 후 `/mcp/api/v1/tools/list`에서 이름, 설명, category, JSON Schema가 맞는지 확인한다.
|
||||
- [ ] 요청·응답 로그에 개인정보나 인증값이 남지 않는지 확인한다.
|
||||
### 등록·노출 제어
|
||||
|
||||
| 속성 | 의미 |
|
||||
|---|---|
|
||||
| `register` | Redis Registry와 Gateway 카탈로그에 등록할지 여부 |
|
||||
| `visible` | Agent/클라이언트 목록에 표시할지 여부 |
|
||||
| `requiresApproval` | 쓰기·고위험 작업의 승인 요구 여부 |
|
||||
| `readOnlyHint`, `destructiveHint`, `idempotentHint` | Agent 호출 특성 힌트 |
|
||||
|
||||
`register = false`인 Tool은 자동 카탈로그 등록 대상이 아닙니다. 별도 실행 목적이 있는 경우에만 사용하고, 필요한 fallback 경로를 운영 설정으로 확인합니다.
|
||||
|
||||
## 빌드·테스트·로컬 실행
|
||||
## 로컬 실행
|
||||
|
||||
### 사전 조건
|
||||
|
||||
- JDK 21
|
||||
- Docker (Redis 또는 MCI mock을 사용할 경우)
|
||||
- Gradle Wrapper 사용 권장
|
||||
- 로컬 Redis 또는 Docker Compose 환경
|
||||
- 필요 시 MCI Mock 또는 사내 MCI/EAI 접근 환경
|
||||
|
||||
### 전체 빌드와 대표 검증
|
||||
로컬 프로필은 기본값이며, 두 Pod 모두 H2 메모리 DB와 P6Spy를 설정합니다. Pod URL은 `AXHUB_TOOL_URL` 환경 변수로 설정하며, 지정하지 않으면 해당 `server.port`의 localhost 주소를 사용합니다.
|
||||
|
||||
```powershell
|
||||
.\gradlew.bat clean build
|
||||
.\gradlew.bat :dap-was-lib:test
|
||||
.\gradlew.bat :dap-was-lib:compileJava
|
||||
```
|
||||
|
||||
### 애플리케이션 실행
|
||||
|
||||
각 애플리케이션은 별도 PowerShell에서 실행합니다.
|
||||
|
||||
```powershell
|
||||
# Gateway
|
||||
.\gradlew.bat :dap-gateway:bootRun
|
||||
|
||||
# SMS Tool
|
||||
.\gradlew.bat :dap-was-sms:bootRun
|
||||
|
||||
# 기타 업무 Tool
|
||||
.\gradlew.bat :dap-was-oth:bootRun
|
||||
```
|
||||
|
||||
기본 프로필은 `local`입니다. 개발 서버 설정이 필요하면 실행 환경에 프로필을 지정합니다.
|
||||
|
||||
```powershell
|
||||
$env:SPRING_PROFILES_ACTIVE = 'dev'
|
||||
.\gradlew.bat :dap-was-oth:bootRun
|
||||
```
|
||||
|
||||
## Docker Compose 실행
|
||||
|
||||
Docker Compose는 Redis, Gateway, MCI Mock, SMS Tool, OTH Tool을 함께 기동합니다.
|
||||
|
||||
```powershell
|
||||
$env:ACTIVE_PROFILE = 'local'
|
||||
docker compose up -d --build
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
| 서비스 | 컨테이너 포트 | 호스트 포트 |
|
||||
|---|---:|---:|
|
||||
| Redis | 6379 | 6379 |
|
||||
| Gateway | 8081 | 8281 |
|
||||
| MCI Mock | 8080 | 8089 |
|
||||
| SMS Tool | 8082 | 8282 |
|
||||
| OTH Tool | 8084 | 8284 |
|
||||
|
||||
컨테이너 내부와 PC 브라우저의 접속 주소는 다릅니다.
|
||||
|
||||
```text
|
||||
컨테이너 내부: http://gateway:8081, http://tool-oth:8084
|
||||
PC 브라우저: http://localhost:8281, http://localhost:8284
|
||||
```
|
||||
|
||||
## 환경 설정
|
||||
|
||||
### 프로필
|
||||
|
||||
기본 프로필은 `local`입니다. Tool 서버와 Gateway 모두 `local`, `dev` 프로필 파일을 사용합니다.
|
||||
|
||||
| 프로필 | 목적 | 주요 설정 파일 |
|
||||
|---|---|---|
|
||||
| `local` | PC 개발·MCI Mock·H2 메모리 DB 기반 실행 | `application-local.yml` |
|
||||
| `dev` | 개발 서버 배포 실행 | `application-dev.yml` |
|
||||
|
||||
프로필은 환경 변수로 지정합니다.
|
||||
|
||||
```powershell
|
||||
# 로컬 개발
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
|
||||
# 개발 서버 설정으로 실행
|
||||
$env:SPRING_PROFILES_ACTIVE = 'dev'
|
||||
```
|
||||
|
||||
### 환경별 연결 주소 비교
|
||||
|
||||
| 항목 | `local` 프로세스 실행 | Docker Compose | `dev` 프로필 |
|
||||
|---|---|---|---|
|
||||
| Gateway 접근 주소 | `http://localhost:8081` | 컨테이너 내부 `http://gateway:8081` | 배포 환경 Gateway URL |
|
||||
| SMS Tool 주소 | `http://localhost:8082` | `http://tool-sms:8082` | `PORT` 기본 8082 |
|
||||
| OTH Tool 주소 | `http://localhost:8084` | `http://tool-oth:8084` | `PORT` 기본 8084 |
|
||||
| Redis 주소 | 로컬 Redis 또는 `localhost:6379` | `redis:6379` | 운영/개발 Redis 설정 |
|
||||
| MCI/EAI 대상 | MCI Mock 또는 로컬 설정 | `mci-mock:8080` | 개발망 연동 설정 |
|
||||
| 브라우저 Gateway 접속 | `http://localhost:8081` | `http://localhost:8281` | 운영·개발 도메인 |
|
||||
|
||||
`local`에서 프로세스를 직접 실행하면 Tool의 `AXHUB_GATEWAY_URL`은 `localhost`를 사용합니다. Docker에서는 각 컨테이너가 서로 다른 네트워크 공간에 있으므로 반드시 Compose 서비스 이름을 사용합니다.
|
||||
### Gateway 환경 변수
|
||||
|
||||
| 변수 | 적용 대상 | 설명 | 로컬 기본값/예시 |
|
||||
|---|---|---|---|
|
||||
| `SPRING_PROFILES_ACTIVE` | Gateway, 모든 Tool | 활성 Spring 프로필 | `local` |
|
||||
| `OPENROUTER_API_KEY` | Gateway | Chat/LLM 호출 API 키 | 운영·개발 환경의 비밀 저장소에서 주입 |
|
||||
| `SPRING_DATA_REDIS_HOST` | Gateway, 모든 Tool | Redis 호스트 | Docker: `redis` |
|
||||
| `SPRING_DATA_REDIS_PORT` | Gateway, 모든 Tool | Redis 포트 | `6379` |
|
||||
| `MCP_GATEWAY_FALLBACK_DEFAULT_URL` | Gateway | Registry에 없는 Tool의 기본 fallback URL | Docker: `http://tool-oth:8084` |
|
||||
| `MCP_GATEWAY_FALLBACK_ROUTES_SMS` | Gateway | SMS 계열 fallback URL | Docker: `http://tool-sms:8082` |
|
||||
| `ACTIVE_PROFILE` | Docker Compose | Compose가 `SPRING_PROFILES_ACTIVE`에 전달할 프로필 | `local` |
|
||||
|
||||
`OPENROUTER_API_KEY` 같은 인증값은 `application.yml`, README, Git 커밋에 직접 넣지 않습니다. 개발·운영 환경의 Secret, CI/CD 변수 또는 안전한 환경 변수로 주입합니다.
|
||||
|
||||
### Tool 서버 환경 변수
|
||||
|
||||
| 변수 | 적용 대상 | 설명 | 로컬 기본값/예시 |
|
||||
|---|---|---|---|
|
||||
| `PORT` | `dap-was-sms`, `dap-was-oth` | 개발 프로필에서 Tool 서버 포트 변경 | SMS `8082`, OTH `8084` |
|
||||
| `AXHUB_GATEWAY_URL` | 모든 Tool | Tool 등록·Heartbeat 대상 Gateway 주소 | 로컬 `http://localhost:8081`, Docker `http://gateway:8081` |
|
||||
| `AXHUB_TOOL_URL` | 모든 Tool | Gateway가 해당 Tool Pod를 호출할 주소 | 로컬 `http://localhost:{server.port}` |
|
||||
| `GLOW_COMMUNICATION_MCI_HOMT` | Docker Tool 컨테이너 | MCI 대상 호스트 | 로컬 Compose는 `mci-mock` |
|
||||
| `GLOW_COMMUNICATION_MCI_PORT` | Docker Tool 컨테이너 | MCI 대상 포트 | 로컬 Compose는 `8080` |
|
||||
| `GLOW_COMMUNICATION_EAI_HOMT` | Docker Tool 컨테이너 | EAI 대상 호스트 | 로컬 Compose는 `mci-mock` |
|
||||
| `GLOW_COMMUNICATION_EAI_PORT` | Docker Tool 컨테이너 | EAI 대상 포트 | 로컬 Compose는 `8080` |
|
||||
|
||||
`AXHUB_TOOL_URL`은 반드시 Gateway가 실제로 접근 가능한 주소여야 합니다. PC에서 각각 실행할 때는 `localhost`를 사용하고, Docker 컨테이너 안에서는 `tool-sms`, `tool-oth` 같은 Compose 서비스 이름을 사용합니다.
|
||||
|
||||
### 로컬 실행용 권장 설정
|
||||
|
||||
아래는 키 값 없이 로컬 프로세스를 실행하는 예시입니다. Redis를 Docker로 먼저 기동하거나 전체 Docker Compose를 사용합니다.
|
||||
|
||||
```powershell
|
||||
# 선택 1: Redis만 기동
|
||||
# 선택: Redis와 WireMock 기반 MCI mock 기동
|
||||
$env:ACTIVE_PROFILE = 'local'
|
||||
docker compose up -d redis mci-mock
|
||||
|
||||
# 선택 2: 각 프로세스를 로컬에서 기동
|
||||
# OTH Tool Pod 실행
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
$env:OPENROUTER_API_KEY = '<개인 또는 개발용 비밀 저장소의 키>'
|
||||
.\gradlew.bat :dap-gateway:bootRun
|
||||
```
|
||||
|
||||
다른 PowerShell에서 Tool을 실행합니다.
|
||||
|
||||
```powershell
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
|
||||
$env:AXHUB_TOOL_URL = 'http://localhost:8084'
|
||||
.\gradlew.bat :dap-was-oth:bootRun
|
||||
|
||||
# SMS Tool Pod 실행 (별도 PowerShell)
|
||||
$env:SPRING_PROFILES_ACTIVE = 'local'
|
||||
$env:AXHUB_TOOL_URL = 'http://localhost:8082'
|
||||
.\gradlew.bat :dap-was-sms:bootRun
|
||||
```
|
||||
|
||||
### 권한 도메인 설정
|
||||
|
||||
`mcp.security.tenant-domains`는 테넌트 또는 호출 주체가 접근할 수 있는 `categoryKey`를 정의합니다. 운영 환경에서는 `ALL`을 무분별하게 사용하지 않고, Agent·조직별 허용 도메인을 최소 권한으로 설정합니다.
|
||||
|
||||
```yaml
|
||||
mcp:
|
||||
security:
|
||||
tenant-domains:
|
||||
claims-agent: claim, customer
|
||||
notification-agent: notification
|
||||
```
|
||||
|
||||
## 공통 기능과 운영 기준
|
||||
|
||||
### Redis
|
||||
|
||||
Redis는 Tool Registry의 등록 상태와 Heartbeat, Gateway 실행 추적 정보에 사용됩니다. 현재 Redis는 PII 원문 보관소가 아닙니다.
|
||||
|
||||
### 권한과 승인
|
||||
|
||||
Gateway는 Agent가 전달한 허용 Tool 목록, 서버 정책, 신뢰된 Claim 여부, 쓰기 승인 여부를 확인합니다. Tool마다 권한 로직을 중복 구현하지 말고 Gateway 공통 정책과 `@McpFunction` 메타데이터를 사용합니다.
|
||||
|
||||
### Trace ID와 Request ID
|
||||
|
||||
```text
|
||||
trace-id : 사용자 요청 전체에서 유지되는 상관관계 ID
|
||||
request-id : Gateway → Tool, Tool → MCI 등 HTTP 호출마다 새로 생성되는 ID
|
||||
```
|
||||
|
||||
Gateway와 Tool에는 관련 헤더 및 MDC 기반 로그 처리가 있습니다. 신규 HTTP Client도 공통 전파 정책을 따르며, Tool별로 임의의 헤더 이름을 추가하지 않습니다.
|
||||
|
||||
### 로그와 개인정보
|
||||
|
||||
Gateway에는 민감 키와 일부 형식을 마스킹하는 공통 기능이 있습니다. 마스킹은 원문을 Agent에서 분리하는 PII 토큰화와는 다릅니다.
|
||||
|
||||
- 요청·응답 전문을 로그에 남길 때는 반드시 마스킹합니다.
|
||||
- 운영 로그에 주민번호, 계좌번호, 전화번호, 이메일, 인증값을 남기지 않습니다.
|
||||
- Tool 서버의 신규 로그도 같은 마스킹 정책을 적용합니다.
|
||||
- 민감정보 복원 필요 여부는 Tool 개발자가 임의로 결정하지 않고 보안·AA 정책을 따릅니다.
|
||||
|
||||
## AA 협의 기반 향후 전환 과제
|
||||
|
||||
아래 항목은 현재 구현 완료 기능이 아니라 회의에서 합의한 목표 구조입니다.
|
||||
|
||||
### 부서별 Tool Pod와 저장소 경계
|
||||
|
||||
현재 `dap-was-oth`에 함께 있는 업무 Tool을 부서·업무 소유권 단위로 분리합니다. 각 Pod는 독립 이미지, 독립 배포, 독립 장애 범위를 갖도록 구성합니다. 실제 분리는 AA가 확정한 Tool 소유 부서와 운영 책임 매핑을 기준으로 수행합니다.
|
||||
|
||||
### Agent별 Tool 노출 수 제한
|
||||
|
||||
Agent에게 모든 Tool을 한 번에 제공하지 않고, 업무 도메인과 권한에 따라 약 10~20개 수준의 Tool 그룹을 제공합니다. `categoryKey`는 이를 위한 기초 메타데이터이며, 향후 Agent-Tool Group 정책으로 확장합니다.
|
||||
|
||||
### PII 토큰화
|
||||
|
||||
```text
|
||||
원문 개인정보
|
||||
→ PII Gateway가 Redis에 짧은 TTL로 보관
|
||||
→ Agent에는 PII 토큰 또는 안전한 식별자만 전달
|
||||
→ 인가된 Tool이 MCI 호출 직전에 필요한 항목만 복원
|
||||
```
|
||||
|
||||
이 전환 전까지는 현행 마스킹 기능을 PII 분리 구현으로 오해하지 않아야 합니다.
|
||||
|
||||
### MCP SDK와 Glow Framework
|
||||
|
||||
Gateway에는 Spring AI MCP Server 의존성이 포함되어 있습니다. MCP SDK/Glow 표준 적용 시에는 업무 Tool을 재작성하지 않고, 기존 `@McpTool`·`@McpFunction`과 UseCase를 표준 MCP Tool 명세·호출 콜백으로 연결하는 Adapter 계층을 공통 Core에 추가합니다.
|
||||
|
||||
```text
|
||||
MCP SDK 표준 tools/list, tools/call
|
||||
↓
|
||||
공통 Adapter
|
||||
↓
|
||||
기존 UseCase → Converter → MCI Client
|
||||
```
|
||||
|
||||
따라서 업무 DTO, Converter, MCI Client의 책임은 유지됩니다.
|
||||
|
||||
## 참고 소스
|
||||
|
||||
| 주제 | 대표 위치 |
|
||||
|---|---|
|
||||
| Gateway Tool API | `dap-gateway/.../presentation/McpRouterController.java` |
|
||||
| 동적 MCP SSE/호출 경로 | `dap-gateway/.../sync/DynamicMcpController.java` |
|
||||
| Tool 실행 Controller | `dap-was-lib/.../presentation/BusinessToolController.java` |
|
||||
| Tool 자동 등록 | `dap-was-lib/.../usecase/ToolRegistryHeartbeatSender.java` |
|
||||
| Tool 어노테이션 | `dap-was-lib/.../annotation/McpTool.java`, `McpFunction.java` |
|
||||
| Tool 예시 | `dap-was-oth/.../biz/oth`, `biz/sol`, `biz/smp` |
|
||||
| SMS Tool 예시 | `dap-was-sms/.../biz/sms` |
|
||||
| Docker 환경 | `docker-compose.yml` |
|
||||
|
||||
---
|
||||
|
||||
문서에 없는 업무·보안·배포 기준은 임의로 추가하지 말고 AA 및 플랫폼 운영 기준과 먼저 합의합니다.
|
||||
## Input/Output Schema 작성 가이드
|
||||
|
||||
Tool Schema는 Agent가 Tool을 정확히 호출하고, 반환값의 의미를 일관되게 해석하도록 하는 계약입니다. 인증 정보·사번·주민번호 등 민감정보(PII)는 Input/Output Schema와 Tool 응답에 포함하지 않습니다.
|
||||
|
||||
### Input Schema
|
||||
|
||||
Input Schema는 Agent가 Tool에 전달하는 파라미터의 이름, 타입, 필수 여부, 허용값, 형식 등을 정의합니다.
|
||||
|
||||
적용 우선순위는 다음과 같습니다.
|
||||
|
||||
1. `inputSchemaResource` — 복잡한 규칙을 담은 JSON Schema 리소스
|
||||
2. `inputSchema` — 어노테이션에 직접 선언한 JSON Schema
|
||||
3. 요청 DTO 필드의 `@McpValidation` — 자동 JSON Schema 생성
|
||||
|
||||
단순한 요청 DTO는 `@McpValidation`만으로 관리합니다.
|
||||
|
||||
```java
|
||||
public class ClaimSearchRequest {
|
||||
|
||||
@McpValidation(required = true, pattern = "^CLM[0-9]{13}$")
|
||||
private String claimNo;
|
||||
|
||||
@McpValidation(minimum = 1, maximum = 100)
|
||||
private Integer size;
|
||||
}
|
||||
```
|
||||
|
||||
### Output Schema
|
||||
|
||||
Output Schema는 Tool이 반환하는 결과의 타입과 의미를 정의합니다. `BusinessToolController`는 Tool 실행 후 반환값을 Output Schema 기준으로 검증합니다.
|
||||
|
||||
적용 우선순위는 다음과 같습니다.
|
||||
|
||||
1. `outputSchemaResource` — 조건부 필드·중첩 배열 등 복잡한 규칙을 담은 JSON Schema 리소스
|
||||
2. `outputSchema` — 어노테이션에 직접 선언한 JSON Schema
|
||||
3. 반환 DTO의 `@McpOutputSchema`와 필드 `@McpValidation` — 자동 JSON Schema 생성
|
||||
4. 위 설정이 모두 없으면 Output Schema 검증을 수행하지 않음
|
||||
|
||||
따라서 단순한 응답은 별도 `outputSchemaResource` 없이 반환 DTO에 `@McpOutputSchema`를 선언하면 됩니다. `null`이 정상 값일 수 있는 필드는 `nullable = true`를 반드시 지정합니다.
|
||||
|
||||
```java
|
||||
@McpOutputSchema
|
||||
public class ClaimSearchResponse {
|
||||
|
||||
@McpValidation(required = true, allowedValues = {"SUCCESS", "FAILURE"})
|
||||
private String resultCode;
|
||||
|
||||
@McpValidation(nullable = true, minimum = 0)
|
||||
private Long approvedAmount;
|
||||
}
|
||||
```
|
||||
|
||||
### 복잡한 Schema는 Tool 모듈별 리소스로 관리
|
||||
|
||||
조건부 응답, 중첩 DTO, 배열 정렬 기준처럼 어노테이션만으로 표현하기 어려운 규칙은 Tool Core가 아니라 각 Tool 모듈의 리소스에 JSON Schema로 둡니다.
|
||||
|
||||
```text
|
||||
src/main/resources/
|
||||
└─ tool-schemas/
|
||||
└─ {categoryKey}/
|
||||
├─ claim-search-resource-input-schema.json
|
||||
└─ claim-search-resource-output-schema.json
|
||||
```
|
||||
|
||||
예를 들어 `categoryKey`가 `cmm`이면 아래와 같이 선언합니다.
|
||||
|
||||
```java
|
||||
@McpFunction(
|
||||
name = "oth.cmm.claim.search",
|
||||
inputSchemaResource = "classpath:tool-schemas/cmm/claim-search-resource-input-schema.json",
|
||||
outputSchemaResource = "classpath:tool-schemas/cmm/claim-search-resource-output-schema.json"
|
||||
)
|
||||
public ClaimSearchResponse search(ClaimSearchRequest request) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
`inputSchemaResource`와 `outputSchemaResource`는 복잡한 경우에만 선언합니다. 단순한 Tool까지 JSON 파일을 별도 생성할 필요는 없습니다.
|
||||
|
||||
### Output 설계 규칙
|
||||
|
||||
- 코드와 표시용 라벨을 함께 반환합니다. 예: `status` + `statusLabel`
|
||||
- `null`이 정상인 값은 의미를 설명에 명시하고 DTO에는 `nullable = true`를 설정합니다.
|
||||
- 조건부 필드는 어떤 조건에서 값이 존재하는지 JSON Schema에 명시합니다.
|
||||
- 배열은 정렬 기준을 설명에 명시합니다. 예: `접수일 내림차순`
|
||||
- 목록 응답에는 추가 조회 여부를 나타내는 `hasMore`를 포함합니다.
|
||||
- 민감정보는 마스킹보다 **응답에서 제외**하는 것을 우선합니다.
|
||||
|
||||
### 실행 로그 및 확인
|
||||
|
||||
Tool 실행이 끝나면 아래 로그는 Schema 정의가 아니라 **검증을 통과한 실제 최종 응답값**을 출력합니다.
|
||||
|
||||
```text
|
||||
[Tool -> MCP Gateway] Output Schema Result: { ... }
|
||||
```
|
||||
|
||||
따라서 로그에도 실제 응답이 남으므로, 응답 DTO와 Output Schema에 민감정보가 포함되지 않도록 설계해야 합니다.
|
||||
|
||||
스키마 리소스와 DTO 기반 자동 Schema는 아래 테스트로 함께 검증할 수 있습니다.
|
||||
|
||||
```powershell
|
||||
.\gradlew.bat :dap-was-oth:test --tests "io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequestSchemaTest"
|
||||
```
|
||||
|
||||
### Tool Naming Convention
|
||||
|
||||
All Tool names use the four-level lowercase format `pod.domain.service.action`. Do not use underscores or CamelCase; use a hyphen (`-`) only when a single level has multiple words.
|
||||
|
||||
- `pod`: deployment Tool Pod/module (`dap-was-oth` → `oth`, `dap-was-sms` → `sms`)
|
||||
- `domain`: business-domain package (`cmm`, `smp`, `sol`, etc.)
|
||||
- `service`: business service or resource
|
||||
- `action`: the requested operation (`search`, `list`, `detail`, `issue`, `inquiry`, etc.)
|
||||
|
||||
```text
|
||||
oth.cmm.bond.issue
|
||||
oth.cmm.claim.search
|
||||
oth.sol.request.list
|
||||
oth.smp.weather.inquiry
|
||||
```
|
||||
|
||||
When Scaffold receives `dap-was-oth`, `cmm`, and `ClaimSearch`, it generates `oth.cmm.claim.search`. The `validateMcpToolNames` Gradle task rejects both a duplicate name and any name outside this format before packaging, including its source file and line number.
|
||||
|
||||
### Tool Test Console
|
||||
|
||||
각 Tool Pod는 공통 테스트 화면을 제공합니다.
|
||||
실행 후 OTH Pod에서 다음 URL로 현재 스캔된 메타데이터와 Manifest를 확인할 수 있습니다.
|
||||
|
||||
```text
|
||||
http://localhost:8084/mcp/api/v1/tools/local
|
||||
http://localhost:8084/tool-manifest
|
||||
http://localhost:8084/tool-test-console.html
|
||||
```
|
||||
|
||||
화면은 현재 Pod의 `/tool-manifest`에서 Tool 목록과 `inputSchema`를 읽습니다. Tool을 선택한 뒤 `Schema 샘플 채우기`로 요청 JSON을 만들고 실행할 수 있습니다. 업무에 맞게 보정한 요청은 `현재 요청 저장`으로 브라우저의 `localStorage`에 보관합니다.
|
||||
`tool-test-console.html`은 공통 라이브러리의 정적 리소스입니다. `/tool-manifest`에서 Tool과 입력 Schema를 읽어 요청 JSON을 만들고, 현재 Pod의 `/mcp/{toolName}`으로 호출합니다. 저장한 테스트 케이스는 브라우저 `localStorage`에 보관됩니다.
|
||||
|
||||
`Run saved cases`는 저장된 테스트 케이스를 순차 실행해 성공/실패, HTTP 상태, 소요 시간을 보여줍니다. 따라서 Tool이 수백 개여도 각 Tool마다 테스트 화면을 만들 필요 없이, 유효한 업무 테스트 데이터만 한 번 저장하면 이후에는 몇 번의 클릭으로 회귀 테스트할 수 있습니다.
|
||||
## 테스트와 빌드
|
||||
|
||||
- Tool 호출은 현재 Pod의 `/mcp/{toolName}`로 수행합니다.
|
||||
- 매 실행마다 `trace-id`, `request-id`를 새로 생성하여 응답과 함께 표시합니다.
|
||||
- 외부 MCI/EAI Tool은 샘플값 대신 개발계에서 허용된 테스트 데이터를 저장해서 사용해야 합니다.
|
||||
```powershell
|
||||
# 전체 테스트
|
||||
.\gradlew.bat test
|
||||
|
||||
# 공통 라이브러리 테스트
|
||||
.\gradlew.bat :dap-was-lib:test
|
||||
|
||||
# OTH Tool 테스트
|
||||
.\gradlew.bat :dap-was-oth:test
|
||||
|
||||
# Tool 이름 규칙 및 중복 검증
|
||||
.\gradlew.bat validateMcpToolNames
|
||||
|
||||
# 패키징 전 전체 빌드
|
||||
.\gradlew.bat clean build
|
||||
```
|
||||
|
||||
테스트는 공통 MCP Schema/Manifest/Header 처리, Glow MCI 파서, Tool 이름 검증과 OTH의 청구·SOL·MCI 변환을 다룹니다. SMS 모듈에는 현재 별도 테스트 소스가 없습니다.
|
||||
|
||||
## Docker Compose
|
||||
|
||||
현재 Compose 서비스와 호스트 포트는 다음과 같습니다.
|
||||
|
||||
| 서비스 | 컨테이너 포트 | 호스트 포트 |
|
||||
|---|---:|---:|
|
||||
| `redis` | 6379 | 6379 |
|
||||
| `mci-mock` (WireMock) | 8080 | 8089 |
|
||||
| `was-sms` | 8082 | 8282 |
|
||||
| `was-oth` | 8084 | 8284 |
|
||||
| `dozzle` | 8080 | 8288 |
|
||||
|
||||
Compose의 Pod URL은 컨테이너 DNS 이름을 사용합니다.
|
||||
|
||||
```text
|
||||
was-sms: http://was-sms:8082
|
||||
was-oth: http://was-oth:8084
|
||||
```
|
||||
|
||||
### 현재 Docker 이미지 빌드 제약
|
||||
|
||||
`docker-compose.yml`은 새 모듈 경로의 Dockerfile을 사용하지만, 두 Dockerfile 내부의 `COPY` 대상은 여전히 이전 경로인 `dap-tool-oth/build/libs`와 `dap-tool-sms/build/libs`입니다. 따라서 현재 모듈명으로 빌드한 jar를 이미지에 복사하지 못할 수 있습니다. 이 불일치를 수정하기 전에는 `docker compose up --build`를 정상 배포 절차로 간주하면 안 됩니다.
|
||||
|
||||
또한 Compose의 Gitea Runner 등록 토큰은 저장소에 직접 두지 말고 배포 환경의 Secret 또는 환경 변수로 주입해야 합니다.
|
||||
|
||||
## 설정
|
||||
|
||||
| 설정 | 위치/환경 변수 | 설명 |
|
||||
|---|---|---|
|
||||
| Pod 포트 | `server.port` 또는 `PORT` | SMS 8082, OTH 8084 |
|
||||
| Pod 외부 URL | `AXHUB_TOOL_URL` | 스캐너가 Tool endpoint를 만들 때 사용 |
|
||||
| MCP namespace | `mcp.namespace` | Tool 이름 앞에 `{namespace}_`를 붙임 |
|
||||
| Manifest bundle | `mcp.manifest.bundle-id` | SMS는 `tool-sms`, OTH는 `tool-oth` |
|
||||
| Manifest 이름 접두사 | `mcp.manifest.name-prefix` | 지정 시 모든 Manifest Tool 이름이 이 접두사로 시작해야 함 |
|
||||
| 활성 프로필 | `SPRING_PROFILES_ACTIVE` | 기본값 `local`, 선택값 `dev` |
|
||||
|
||||
`mcp.security.tenant-domains` 설정은 각 Pod의 YAML에 존재하지만, 현재 `McpProperties`와 `BusinessToolController`에는 이를 이용해 호출을 차단하는 로직이 없습니다. 문서상 권한 기능으로 간주하지 말고, 운영 노출 시 별도 인증·인가 계층을 적용해야 합니다.
|
||||
|
||||
## 보안과 운영 주의사항
|
||||
|
||||
- `BusinessToolController`는 요청 파라미터와 결과를 로그로 남깁니다. Tool 입력·응답에는 주민번호, 계좌번호, 전화번호, 인증값 등 민감정보를 포함하지 않도록 설계하고 공통 마스킹 적용 여부를 검토해야 합니다.
|
||||
- `employee-id` 헤더는 Controller가 수신하지만 현재 실행 로직에서 사용하지 않습니다. 이 헤더만으로 인증·인가가 수행된다고 가정하면 안 됩니다.
|
||||
- `mcp.security.tenant-domains`, `requiresApproval`, `register`는 현재 독립 WAS에서 실행 차단 정책을 구현하지 않습니다.
|
||||
- 외부 MCI/EAI 대상은 local/dev 설정과 실제 네트워크 정책을 별도로 점검해야 합니다.
|
||||
|
||||
## 참고 소스
|
||||
|
||||
| 주제 | 위치 |
|
||||
|---|---|
|
||||
| REST 실행 및 로컬 목록 | `dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/presentation/BusinessToolController.java` |
|
||||
| Manifest API | `dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/presentation/ToolManifestController.java` |
|
||||
| MCP Streamable HTTP | `dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/mcp/ToolMcpServerConfiguration.java` |
|
||||
| MCP Tool 동기화 | `dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/mcp/ToolPodMcpToolSynchronizer.java` |
|
||||
| Tool 스캔 | `dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/usecase/LocalToolScanner.java` |
|
||||
| Tool 어노테이션 | `dap-was-lib/src/main/java/io/shinhanlife/dap/lib/annotation/McpTool.java`, `McpFunction.java` |
|
||||
| OTH 업무 Tool | `dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/` |
|
||||
| SMS Tool | `dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/` |
|
||||
| 컨테이너 구성 | `docker-compose.yml`, `dap-was-oth/Dockerfile`, `dap-was-sms/Dockerfile` |
|
||||
|
||||
Reference in New Issue
Block a user