- Add Auto-Tester Dashboard (tester.html) in gateway for batch testing tools - Add standalone Tool Test Console (tool-test-console.html) in core - Fix Tailwind CSS Preflight conflicts in console UI - Update console payload schema resolution to support both gateway and pod modes - Strip JSON-RPC metadata from tool payload output in console - Add unified navigation headers across all static HTML pages
AX HUB MCP Tool
신한라이프 업무 시스템과 AI Agent를 연결하는 MCP(Model Context Protocol) Gateway 및 Tool 서버 프로젝트입니다.
Agent는 Gateway에서 Tool 목록과 입력 명세를 받고, Gateway는 권한과 정책을 확인한 뒤 Tool 서버로 요청을 전달합니다. 업무 Tool은 DTO → UseCase → Converter → MCI/EAI Client 구조로 레거시 시스템을 호출합니다.
전체 흐름
AI Agent / MCP Client
│
▼
MCP Gateway (dap-gateway)
├─ Tool 목록·스키마 제공
├─ Tool 권한·승인·가드레일 확인
├─ Redis Registry 및 실행 추적
└─ 대상 Tool 서버로 라우팅
│
▼
Tool Server (dap-tool-sms / dap-tool-oth)
└─ BusinessToolController
│
▼
UseCase → Converter → MCI/EAI Client → 레거시 시스템
현재 구조와 목표 구조
현재는 Gateway와 두 개의 Tool 애플리케이션으로 구성됩니다.
현재: Gateway + SMS Tool Pod + OTH Tool Pod
목표: Gateway + 고객 Pod + 영업 Pod + 지급/납입 Pod + 알림 Pod + 인사 Pod + 공통 Pod
dap-tool-oth에는 여러 업무 카테고리가 함께 있습니다. AA 협의 후에는 부서·업무 소유권 단위로 Tool 서버, 이미지, Pod, 배포 파이프라인을 분리합니다. 이 목표 구조는 향후 전환 방향이며 현재 구현 완료 상태가 아닙니다.
Gradle 멀티모듈
| 모듈 | 역할 | 실행 포트 |
|---|---|---|
dap-gateway |
MCP 진입점, Tool Registry, 라우팅, 권한·가드레일, Chat API | 8081 |
dap-tool-core |
공통 어노테이션, Controller, JSON Schema, MCI/EAI 지원, 보안·로깅 공통 기능 | 라이브러리 |
dap-tool-sms |
SMS/알림 Tool 서버 | 8082 |
dap-tool-oth |
공통·업무·샘플·MCI Tool 서버 | 8084 |
기술 기준은 Java 21, Spring Boot 4, Gradle, Spring AI MCP Server, Redis, MapStruct, MyBatis, Resilience4j입니다.
환경 (Environment)
| 항목 | 버전 / 기준 |
|---|---|
| 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 |
프로젝트는 JDK 21을 기준으로 컴파일됩니다. IntelliJ에서는 Project SDK, Gradle JVM, Run Configuration JRE를 모두 JDK 21로 맞춰야 합니다.
5분 빠른 시작
Docker와 JDK 21이 준비된 로컬 개발 환경 기준입니다.
# 1. Redis와 MCI Mock만 먼저 실행
$env:ACTIVE_PROFILE = 'local'
docker compose up -d redis mci-mock
# 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-tool-oth:bootRun
Tool 서버가 기동된 뒤 아래 URL로 등록된 Tool 목록을 확인합니다.
http://localhost:8081/mcp/api/v1/tools/list
SMS Tool도 함께 확인하려면 별도 PowerShell에서 아래 명령을 실행합니다.
$env:SPRING_PROFILES_ACTIVE = 'local'
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
$env:AXHUB_TOOL_URL = 'http://localhost:8082'
.\gradlew.bat :dap-tool-sms:bootRun
전체 컨테이너 환경이 필요하면 개별 실행 대신 다음 한 줄을 사용합니다.
$env:ACTIVE_PROFILE = 'local'
docker compose up -d --build
Tool 등록과 실행
등록과 목록 제공
- Tool 서버 기동 시
ToolRegistryHeartbeatSender가@McpTool,@McpFunction을 스캔합니다. - Tool 이름, 설명, 입력 JSON Schema,
categoryKey, 연동 방식, 실행 URL을 메타데이터로 생성합니다. - Gateway Redis Registry에 등록·Heartbeat 정보를 전송합니다.
- Agent와 관리 화면은 Gateway에서 Tool 목록과 명세를 조회합니다.
실행
- Agent가 Gateway에 Tool 이름과 입력값을 보냅니다.
- Gateway가 Tool 존재 여부, 허용 Tool, 쓰기 승인, 가드레일을 확인합니다.
- Gateway가 Tool 서버의
/mcp/{toolName}으로 요청을 전달합니다. BusinessToolController가 Tool 메서드를 찾아 DTO로 변환하고 JSON Schema를 검증합니다.- UseCase가 업무 흐름을 수행합니다.
- Converter가 업무 DTO를 인터페이스 ID 기반 MCI 요청 DTO로 변환합니다.
- MCI/EAI Client가 레거시를 호출하고 결과를 Tool 응답으로 반환합니다.
주요 URL
로컬에서 Gateway를 직접 실행할 때의 기준입니다. Docker Compose를 사용하면 Gateway 호스트 포트는 8281입니다.
| 용도 | 메서드 | URL |
|---|---|---|
| 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 |
/mcp/sse/{categoryKey}는 SSE 연결을 여는 전송 경로이고, /mcp/custom/{categoryKey}는 같은 카테고리의 MCP 요청을 처리하는 호출 경로입니다. 두 URL은 역할이 다릅니다.
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을 사용합니다.
@McpTool(routingType = "MCI", categoryKey = "claim")
public interface ClaimInquiryUseCase {
@McpFunction(
name = "claim_inquiry",
displayName = "보험금 청구 조회",
description = "청구 번호로 보험금 청구 상태를 조회합니다.",
mappingId = "CLM00000001"
)
ClaimInquiryResponse inquire(ClaimInquiryRequest request);
}
categoryKey는 Tool의 업무 그룹입니다. Gateway의 목록 필터링, Agent 권한, 동적 MCP 서버 구분에 사용하므로 합의된 업무 키를 사용합니다.
MCI: 사내 MCI 인터페이스 호출EAI: EAI 연동DIRECT: 외부 HTTP 또는 내부 직접 연동
변환 원칙
UseCase 구현체는 Tool 요청을 레거시 요청과 섞어 쓰지 않습니다. Converter에서 변환한 뒤 Client에 전달합니다.
@Override
public ClaimInquiryResponse inquire(ClaimInquiryRequest request) {
CLM00000001_I mciRequest = converter.toMciRequest(request);
CLM00000001_O mciResponse = mciClmClient.callClm00000001(mciRequest);
return converter.toResponse(mciResponse);
}
MCI 입출력 객체는 업무 이름이 아니라 인터페이스 ID를 기준으로 둡니다.
CLCNNB00001_I : CLCNNB00001 요청 DTO
CLCNNB00001_O : CLCNNB00001 응답 DTO
예를 들어 Onnba3011Request와 CLCNNB00001_I는 같은 업무 데이터를 담을 수 있지만 같은 객체가 아닙니다. 둘 사이의 변환 책임은 Onnba3011Converter에 둡니다.
새 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-tool-core: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
- Gradle Wrapper 사용 권장
- 로컬 Redis 또는 Docker Compose 환경
- 필요 시 MCI Mock 또는 사내 MCI/EAI 접근 환경
전체 빌드와 대표 검증
.\gradlew.bat clean build
.\gradlew.bat :dap-tool-core:test
.\gradlew.bat :dap-tool-core:compileJava
애플리케이션 실행
각 애플리케이션은 별도 PowerShell에서 실행합니다.
# Gateway
.\gradlew.bat :dap-gateway:bootRun
# SMS Tool
.\gradlew.bat :dap-tool-sms:bootRun
# 기타 업무 Tool
.\gradlew.bat :dap-tool-oth:bootRun
기본 프로필은 local입니다. 개발 서버 설정이 필요하면 실행 환경에 프로필을 지정합니다.
$env:SPRING_PROFILES_ACTIVE = 'dev'
.\gradlew.bat :dap-tool-oth:bootRun
Docker Compose 실행
Docker Compose는 Redis, Gateway, MCI Mock, SMS Tool, OTH Tool을 함께 기동합니다.
$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 브라우저의 접속 주소는 다릅니다.
컨테이너 내부: 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 |
프로필은 환경 변수로 지정합니다.
# 로컬 개발
$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-tool-sms, dap-tool-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를 사용합니다.
# 선택 1: Redis만 기동
$env:ACTIVE_PROFILE = 'local'
docker compose up -d redis mci-mock
# 선택 2: 각 프로세스를 로컬에서 기동
$env:SPRING_PROFILES_ACTIVE = 'local'
$env:OPENROUTER_API_KEY = '<개인 또는 개발용 비밀 저장소의 키>'
.\gradlew.bat :dap-gateway:bootRun
다른 PowerShell에서 Tool을 실행합니다.
$env:SPRING_PROFILES_ACTIVE = 'local'
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
$env:AXHUB_TOOL_URL = 'http://localhost:8084'
.\gradlew.bat :dap-tool-oth:bootRun
권한 도메인 설정
mcp.security.tenant-domains는 테넌트 또는 호출 주체가 접근할 수 있는 categoryKey를 정의합니다. 운영 환경에서는 ALL을 무분별하게 사용하지 않고, Agent·조직별 허용 도메인을 최소 권한으로 설정합니다.
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
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-tool-oth에 함께 있는 업무 Tool을 부서·업무 소유권 단위로 분리합니다. 각 Pod는 독립 이미지, 독립 배포, 독립 장애 범위를 갖도록 구성합니다. 실제 분리는 AA가 확정한 Tool 소유 부서와 운영 책임 매핑을 기준으로 수행합니다.
Agent별 Tool 노출 수 제한
Agent에게 모든 Tool을 한 번에 제공하지 않고, 업무 도메인과 권한에 따라 약 10~20개 수준의 Tool 그룹을 제공합니다. categoryKey는 이를 위한 기초 메타데이터이며, 향후 Agent-Tool Group 정책으로 확장합니다.
PII 토큰화
원문 개인정보
→ 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에 추가합니다.
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-tool-core/.../presentation/BusinessToolController.java |
| Tool 자동 등록 | dap-tool-core/.../usecase/ToolRegistryHeartbeatSender.java |
| Tool 어노테이션 | dap-tool-core/.../annotation/McpTool.java, McpFunction.java |
| Tool 예시 | dap-tool-oth/.../biz/oth, biz/sol, biz/smp |
| SMS Tool 예시 | dap-tool-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에 전달하는 파라미터의 이름, 타입, 필수 여부, 허용값, 형식 등을 정의합니다.
적용 우선순위는 다음과 같습니다.
inputSchemaResource— 복잡한 규칙을 담은 JSON Schema 리소스inputSchema— 어노테이션에 직접 선언한 JSON Schema- 요청 DTO 필드의
@McpValidation— 자동 JSON Schema 생성
단순한 요청 DTO는 @McpValidation만으로 관리합니다.
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 기준으로 검증합니다.
적용 우선순위는 다음과 같습니다.
outputSchemaResource— 조건부 필드·중첩 배열 등 복잡한 규칙을 담은 JSON Schema 리소스outputSchema— 어노테이션에 직접 선언한 JSON Schema- 반환 DTO의
@McpOutputSchema와 필드@McpValidation— 자동 JSON Schema 생성 - 위 설정이 모두 없으면 Output Schema 검증을 수행하지 않음
따라서 단순한 응답은 별도 outputSchemaResource 없이 반환 DTO에 @McpOutputSchema를 선언하면 됩니다. null이 정상 값일 수 있는 필드는 nullable = true를 반드시 지정합니다.
@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로 둡니다.
src/main/resources/
└─ tool-schemas/
└─ {categoryKey}/
├─ claim-search-resource-input-schema.json
└─ claim-search-resource-output-schema.json
예를 들어 categoryKey가 cmm이면 아래와 같이 선언합니다.
@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 정의가 아니라 검증을 통과한 실제 최종 응답값을 출력합니다.
[Tool -> MCP Gateway] Output Schema Result: { ... }
따라서 로그에도 실제 응답이 남으므로, 응답 DTO와 Output Schema에 민감정보가 포함되지 않도록 설계해야 합니다.
스키마 리소스와 DTO 기반 자동 Schema는 아래 테스트로 함께 검증할 수 있습니다.
.\gradlew.bat :dap-tool-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-tool-oth→oth,dap-tool-sms→sms)domain: business-domain package (cmm,smp,sol, etc.)service: business service or resourceaction: the requested operation (search,list,detail,issue,inquiry, etc.)
oth.cmm.bond.issue
oth.cmm.claim.search
oth.sol.request.list
oth.smp.weather.inquiry
When Scaffold receives dap-tool-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는 공통 테스트 화면을 제공합니다.
http://localhost:8084/tool-test-console.html
화면은 현재 Pod의 /tool-manifest에서 Tool 목록과 inputSchema를 읽습니다. Tool을 선택한 뒤 Schema 샘플 채우기로 요청 JSON을 만들고 실행할 수 있습니다. 업무에 맞게 보정한 요청은 현재 요청 저장으로 브라우저의 localStorage에 보관합니다.
Run saved cases는 저장된 테스트 케이스를 순차 실행해 성공/실패, HTTP 상태, 소요 시간을 보여줍니다. 따라서 Tool이 수백 개여도 각 Tool마다 테스트 화면을 만들 필요 없이, 유효한 업무 테스트 데이터만 한 번 저장하면 이후에는 몇 번의 클릭으로 회귀 테스트할 수 있습니다.
- Tool 호출은 현재 Pod의
/mcp/{toolName}로 수행합니다. - 매 실행마다
trace-id,request-id를 새로 생성하여 응답과 함께 표시합니다. - 외부 MCI/EAI Tool은 샘플값 대신 개발계에서 허용된 테스트 데이터를 저장해서 사용해야 합니다.