From 4275f76104443bf3dce6e062cdf0f85a6e04a102 Mon Sep 17 00:00:00 2001 From: jade Date: Tue, 11 Aug 2026 10:18:36 +0900 Subject: [PATCH] feat: enforce underscore MCP tool names --- README.md | 10 +-- .../lib/config/AxhubHttpConfiguration.java | 21 ++++++ .../dap/lib/util/ToolScaffolder.java | 2 +- .../lib/validation/McpToolNameValidator.java | 4 +- .../config/AxhubHttpConfigurationTest.java | 31 +++++++++ .../lib/mcp/McpToolMethodRegistryTest.java | 8 +-- .../dap/lib/util/ToolScaffolderTest.java | 6 +- .../dap/lib/util/ToolSchemaResolverTest.java | 4 +- .../dap/lib/util/ToolSourceUpdaterTest.java | 6 +- .../validation/McpToolNameValidatorTest.java | 40 ++++++++--- .../mcc/manifest/ToolManifestServiceTest.java | 2 +- .../mcc/biz/cmm/usecase/BalanceUseCase.java | 2 +- .../cmm/usecase/BillingProcessUseCase.java | 2 +- .../mcc/biz/cmm/usecase/BondIssueUseCase.java | 2 +- .../ClaimSearchSchemaSampleUseCase.java | 2 +- .../biz/cmm/usecase/CommonUtilityUseCase.java | 4 +- .../cmm/usecase/ContractInquiryUseCase.java | 2 +- .../biz/cmm/usecase/CustomerInfoUseCase.java | 2 +- .../cmm/usecase/MetaCommonCodeUseCase.java | 2 +- .../mcc/biz/cmm/usecase/MetaTableUseCase.java | 2 +- .../cmm/usecase/TemplateUtilityUseCase.java | 2 +- .../mcc/biz/oth/usecase/Onnba3011UseCase.java | 2 +- .../smp/usecase/DailyQuoteToolUseCase.java | 2 +- .../smp/usecase/ExchangeRateToolUseCase.java | 2 +- .../smp/usecase/SampleHttpStatusUseCase.java | 2 +- .../biz/smp/usecase/TeamMemberUseCase.java | 2 +- .../biz/smp/usecase/WeatherToolUseCase.java | 2 +- .../biz/sol/usecase/SolReqDetailUseCase.java | 2 +- .../biz/sol/usecase/SolReqListUseCase.java | 2 +- .../cmm/converter/ClaimSearchConverter.java | 14 ++++ .../mcc/biz/cmm/dto/ClaimSearchRequest.java | 16 +++++ .../mcc/biz/cmm/dto/ClaimSearchResponse.java | 22 ++++++ .../biz/cmm/usecase/ClaimSearchUseCase.java | 29 ++++++++ .../usecase/impl/ClaimSearchUseCaseImpl.java | 68 +++++++++++++++++++ .../mcc/biz/sms/usecase/SmsToolUseCase.java | 2 +- .../infra/itrf/mci/ncla/MciNclaClient.java | 30 ++++++++ .../infra/itrf/mci/ncla/io/CLCNNB00001_I.java | 14 ++++ .../infra/itrf/mci/ncla/io/CLCNNB00001_O.java | 17 +++++ .../mock-responses/sms_cmm_claim_search.json | 5 ++ .../claim-search-resource-input-schema.json | 11 +++ .../claim-search-resource-output-schema.json | 16 +++++ .../cmm/usecase/ClaimSearchUseCaseTest.java | 16 +++++ 42 files changed, 380 insertions(+), 52 deletions(-) create mode 100644 dap-was-lib/src/main/java/io/shinhanlife/dap/lib/config/AxhubHttpConfiguration.java create mode 100644 dap-was-lib/src/test/java/io/shinhanlife/dap/lib/config/AxhubHttpConfigurationTest.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/converter/ClaimSearchConverter.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchRequest.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchResponse.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCase.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/MciNclaClient.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java create mode 100644 dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java create mode 100644 dap-was-sms/src/main/resources/mock-responses/sms_cmm_claim_search.json create mode 100644 dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json create mode 100644 dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json create mode 100644 dap-was-sms/src/test/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java diff --git a/README.md b/README.md index 1b485501..3bed7c26 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ UI에서 사용하는 Tailwind CSS와 Chart.js는 `dap-gateway/src/main/resource "jsonrpc": "2.0", "method": "tools/call", "params": { - "name": "oth.cmm.claim.search", + "name": "oth_cmm_claim_search", "arguments": { "claimNo": "CLM2026070100120" } @@ -170,8 +170,8 @@ Agent나 외부 클라이언트의 표준 MCP 진입은 Gateway를 사용합니 Tool 함수명은 아래 4단계 규칙을 사용합니다. ```text -pod.domain.service.action -예: oth.cmm.claim.search +pod_domain_service_action +예: oth_cmm_claim_search ``` - `pod`: Tool Pod 식별자 (`oth`, `sms` 등) @@ -234,7 +234,7 @@ https://dev-ichmci.shinhanlife.co.kr/ntl_mci/clc_rcv 배포 전에는 다음을 확인합니다. -- Tool 이름의 전역 중복 여부와 `pod.domain.service.action` 규칙(아래 확인 사항 반영 후) +- Tool 이름의 전역 중복 여부와 `pod_domain_service_action` 규칙(아래 확인 사항 반영 후) - Request/Response Schema 및 실제 예제 JSON - Tool Pod 단위 테스트와 Gateway 경유 호출 - MCI/EAI 오류 코드의 사용자용 응답 매핑 @@ -266,6 +266,6 @@ https://dev-ichmci.shinhanlife.co.kr/ntl_mci/clc_rcv Tool 관련 공통 기능은 `dap-was-*` 모듈명만 기준으로 동작합니다. - `validateMcpToolNames`는 `dap-was-*` Tool Pod를 탐색하여 이름 규칙과 전역 중복을 검사합니다. -- Tool Scaffold는 `dap-was-sms`처럼 선택한 Pod 이름을 Tool 함수명 첫 번째 구간에 반영합니다. 예: `sms.cmm.notification.send` +- Tool Scaffold는 `dap-was-sms`처럼 선택한 Pod 이름을 Tool 함수명 첫 번째 구간에 반영합니다. 예: `sms_cmm_notification_send` - Pod Scaffold와 Gateway Scaffold 화면/API의 모듈 목록도 `dap-was-*` 명칭으로 통일되어 있습니다. - Tool Source Update 기능은 `dap-was-*` 아래의 `*UseCase.java`를 검색합니다. \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/config/AxhubHttpConfiguration.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/config/AxhubHttpConfiguration.java new file mode 100644 index 00000000..d124b376 --- /dev/null +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/config/AxhubHttpConfiguration.java @@ -0,0 +1,21 @@ +package io.shinhanlife.dap.lib.config; + +import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestClient; + +/** + * Registers the temporary Glow HTTP compatibility component from the DAP library scan scope. + * The bean is only created when an official GlowHttpComponent has not already been supplied. + */ +@Configuration(proxyBeanMethods = false) +public class AxhubHttpConfiguration { + + @Bean + @ConditionalOnMissingBean(GlowHttpComponent.class) + public GlowHttpComponent glowHttpComponent(RestClient.Builder restClientBuilder) { + return new GlowHttpComponent(restClientBuilder); + } +} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java index 5c1cbb05..c73861ab 100644 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java @@ -940,7 +940,7 @@ public class ToolScaffolder { String[] words = normalizedName.split("\\s+"); String service = words[0]; String action = words.length == 1 ? "execute" : words[words.length - 1]; - return "%s.%s.%s.%s".formatted( + return "%s_%s_%s_%s".formatted( pod.toLowerCase(Locale.ROOT), group.toLowerCase(Locale.ROOT), service, diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/validation/McpToolNameValidator.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/validation/McpToolNameValidator.java index 89aea24d..d662c925 100644 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/validation/McpToolNameValidator.java +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/validation/McpToolNameValidator.java @@ -28,7 +28,7 @@ import java.util.regex.Pattern; public final class McpToolNameValidator { private static final Pattern TOOL_NAME_PATTERN = Pattern.compile("\\bname\\s*=\\s*\\\"([^\\\"]+)\\\""); - private static final Pattern TOOL_NAME_CONVENTION = Pattern.compile("^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$"); + private static final Pattern TOOL_NAME_CONVENTION = Pattern.compile("^[a-zA-Z0-9_-]{1,128}$"); private McpToolNameValidator() { } @@ -139,7 +139,7 @@ public final class McpToolNameValidator { } private static String buildInvalidNameMessage(List>> invalidNames) { - StringBuilder message = new StringBuilder("Invalid MCP tool name(s): expected pod.domain.service.action using lowercase letters, digits, or hyphens."); + StringBuilder message = new StringBuilder("Invalid MCP tool name(s): expected 1-128 characters using letters, digits, underscores, or hyphens."); for (Map.Entry> invalidName : invalidNames) { message.append("\n\n").append(invalidName.getKey()); invalidName.getValue().stream() diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/config/AxhubHttpConfigurationTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/config/AxhubHttpConfigurationTest.java new file mode 100644 index 00000000..4e7f10f5 --- /dev/null +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/config/AxhubHttpConfigurationTest.java @@ -0,0 +1,31 @@ +package io.shinhanlife.dap.lib.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.web.client.RestClient; + +class AxhubHttpConfigurationTest { + + @Test + void registersGlowHttpComponentFromDapLibConfiguration() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class)) { + assertThat(context.getBean(GlowHttpComponent.class)).isNotNull(); + } + } + + @Configuration + @Import(AxhubHttpConfiguration.class) + static class TestConfiguration { + + @Bean + RestClient.Builder restClientBuilder() { + return RestClient.builder(); + } + } +} \ No newline at end of file diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/mcp/McpToolMethodRegistryTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/mcp/McpToolMethodRegistryTest.java index 050d6b10..37b321be 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/mcp/McpToolMethodRegistryTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/mcp/McpToolMethodRegistryTest.java @@ -18,10 +18,10 @@ class McpToolMethodRegistryTest { registry.initialize(); - McpToolMethodRegistry.RegisteredTool tool = registry.find("oth.cmm.echo.search"); + McpToolMethodRegistry.RegisteredTool tool = registry.find("oth_cmm_echo_search"); assertNotNull(tool); assertEquals("execute", tool.method().getName()); - assertEquals("oth.cmm.echo.search", tool.annotation().name()); + assertEquals("oth_cmm_echo_search", tool.annotation().name()); } @Test @@ -42,14 +42,14 @@ class McpToolMethodRegistryTest { } static class EchoTool { - @McpTool(name = "oth.cmm.echo.search") + @McpTool(name = "oth_cmm_echo_search") public String execute(String request) { return request; } } static class DuplicateEchoTool { - @McpTool(name = "oth.cmm.echo.search") + @McpTool(name = "oth_cmm_echo_search") public String execute(String request) { return request; } diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java index 14f396ec..9e83b09f 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java @@ -25,7 +25,7 @@ class ToolScaffolderTest { String useCase = Files.readString(root.resolve("usecase/ClaimSearchUseCase.java")); String response = Files.readString(root.resolve("dto/ClaimSearchResponse.java")); - assertTrue(useCase.contains("name = \"oth.cmm.claim.search\"")); + assertTrue(useCase.contains("name = \"oth_cmm_claim_search\"")); assertTrue(useCase.contains("@ToolHint(register = true, categoryKey = \"cmm\", mappingId = \"CLM0001\")")); assertTrue(response.contains("private String resultCode;")); assertTrue(response.contains("private String resultMessage;")); @@ -41,7 +41,7 @@ class ToolScaffolderTest { "src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/NotificationSendUseCase.java"); String useCase = Files.readString(useCasePath); - assertTrue(useCase.contains("name = \"sms.cmm.notification.send\"")); + assertTrue(useCase.contains("name = \"sms_cmm_notification_send\"")); } @Test @@ -121,7 +121,7 @@ class ToolScaffolderTest { ToolScaffolder.scaffold("claim search", "CLM0001", "Claim search", "cmm", "MCI", moduleName, "tester", "2026.08.10", true, null, null, null, List.of(), outputFields); - Path mockResponse = root.resolve("dap-was-oth/src/main/resources/mock-responses/oth.cmm.claim.search.json"); + Path mockResponse = root.resolve("dap-was-oth/src/main/resources/mock-responses/oth_cmm_claim_search.json"); Path useCaseTest = root.resolve("dap-was-oth/src/test/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java"); assertTrue(Files.exists(mockResponse)); diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSchemaResolverTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSchemaResolverTest.java index 00a72f94..9e0cab83 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSchemaResolverTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSchemaResolverTest.java @@ -57,13 +57,13 @@ class ToolSchemaResolverTest { } static class AutomaticSchemaTool { - @McpTool(name = "oth.test.automatic.search") + @McpTool(name = "oth_test_automatic_search") void search(AutomaticRequest request) { } } static class AutomaticOutputSchemaTool { - @McpTool(name = "oth.test.output.search") + @McpTool(name = "oth_test_output_search") SimpleResponse search(AutomaticRequest request) { return null; } diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSourceUpdaterTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSourceUpdaterTest.java index 4c0bd514..7c89445b 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSourceUpdaterTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolSourceUpdaterTest.java @@ -21,16 +21,16 @@ class ToolSourceUpdaterTest { import org.springaicommunity.mcp.annotation.McpTool; import io.shinhanlife.dap.lib.annotation.ToolHint; interface SampleUseCase { - @McpTool(name = "oth.cmm.sample.search", description = "old") + @McpTool(name = "oth_cmm_sample_search", description = "old") @ToolHint(register = false, requiresApproval = false) void search(); } """); - ToolSourceUpdater.updateToolSource(temporaryRoot, "oth.cmm.sample.search", "customer", "new", true, true); + ToolSourceUpdater.updateToolSource(temporaryRoot, "oth_cmm_sample_search", "customer", "new", true, true); String updated = Files.readString(source); - assertTrue(updated.contains("@McpTool(name = \"oth.cmm.sample.search\", description = \"new\")")); + assertTrue(updated.contains("@McpTool(name = \"oth_cmm_sample_search\", description = \"new\")")); assertTrue(updated.contains("@ToolHint(register = true, requiresApproval = true")); assertTrue(updated.contains("categoryKey = \"customer\""), updated); } diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/validation/McpToolNameValidatorTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/validation/McpToolNameValidatorTest.java index 81ec6e16..2bb4b6ba 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/validation/McpToolNameValidatorTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/validation/McpToolNameValidatorTest.java @@ -31,35 +31,53 @@ class McpToolNameValidatorTest { @Test void rejectsDuplicateMcpToolNamesAcrossToolModules() throws IOException { - writeToolSource("dap-was-first", "FirstTool.java", "first", "oth.sms.notification.send"); - writeToolSource("dap-was-second", "SecondTool.java", "second", "oth.sms.notification.send"); + writeToolSource("dap-was-first", "FirstTool.java", "first", "oth_sms_notification_send"); + writeToolSource("dap-was-second", "SecondTool.java", "second", "oth_sms_notification_send"); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> McpToolNameValidator.assertUnique(temporaryRoot)); - assertTrue(exception.getMessage().contains("oth.sms.notification.send")); + assertTrue(exception.getMessage().contains("oth_sms_notification_send")); assertTrue(exception.getMessage().contains("dap-was-first")); assertTrue(exception.getMessage().contains("dap-was-second")); } @Test void validationRunnerRejectsDuplicateMcpToolNamesBeforePackaging() throws IOException { - writeToolSource("dap-was-first", "FirstTool.java", "first", "oth.sms.notification.send"); - writeToolSource("dap-was-second", "SecondTool.java", "second", "oth.sms.notification.send"); + writeToolSource("dap-was-first", "FirstTool.java", "first", "oth_sms_notification_send"); + writeToolSource("dap-was-second", "SecondTool.java", "second", "oth_sms_notification_send"); assertThrows(IllegalStateException.class, () -> McpToolNameValidationRunner.validate(temporaryRoot)); } @Test - void rejectsToolNameOutsidePodDomainServiceActionConvention() throws IOException { - writeToolSource("dap-was-first", "FirstTool.java", "first", "bond_issue"); + void rejectsToolNameOutsideConfiguredPattern() throws IOException { + writeToolSource("dap-was-first", "FirstTool.java", "first", "bond.issue"); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> McpToolNameValidator.assertUnique(temporaryRoot)); assertTrue(exception.getMessage().contains("Invalid MCP tool name(s)")); - assertTrue(exception.getMessage().contains("bond_issue")); + assertTrue(exception.getMessage().contains("bond.issue")); + } + @Test + void acceptsLettersDigitsUnderscoresAndDashesWithin128Characters() throws IOException { + String validName = "Tool_Name-" + "a".repeat(118); + writeToolSource("dap-was-first", "FirstTool.java", "first", validName); + + assertDoesNotThrow(() -> McpToolNameValidator.assertUnique(temporaryRoot)); + } + + @Test + void rejectsToolNameLongerThan128Characters() throws IOException { + String invalidName = "a".repeat(129); + writeToolSource("dap-was-first", "FirstTool.java", "first", invalidName); + + IllegalStateException exception = assertThrows(IllegalStateException.class, + () -> McpToolNameValidator.assertUnique(temporaryRoot)); + + assertTrue(exception.getMessage().contains(invalidName)); } @Test void acceptsCurrentProjectToolNames() { @@ -71,11 +89,11 @@ class McpToolNameValidatorTest { Path root = findProjectRoot(); assertToolName(root, "dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CustomerInfoUseCase.java", - "oth.cmm.customer.detail", "detail"); + "oth_cmm_customer_detail", "detail"); assertToolName(root, "dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BillingProcessUseCase.java", - "oth.cmm.billing.process", "process"); + "oth_cmm_billing_process", "process"); assertToolName(root, "dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BondIssueUseCase.java", - "oth.cmm.bond.issue", "issue"); + "oth_cmm_bond_issue", "issue"); } private void assertToolName(Path root, String relativePath, String expectedName, String legacyName) throws IOException { diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/mcc/manifest/ToolManifestServiceTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/mcc/manifest/ToolManifestServiceTest.java index 6efc38ec..cd29bc3a 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/mcc/manifest/ToolManifestServiceTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/mcc/manifest/ToolManifestServiceTest.java @@ -52,7 +52,7 @@ class ToolManifestServiceTest { void rejectsEntireManifestWhenToolNameDoesNotMatchConfiguredPrefix() { McpProperties properties = manifestProperties("insurance-processing", "processing."); ToolManifestService service = new ToolManifestService( - () -> List.of(tool("notification.sms.send", "1.0.0", 3000)), objectMapper, properties); + () -> List.of(tool("notification_sms_send", "1.0.0", 3000)), objectMapper, properties); IllegalStateException error = assertThrows(IllegalStateException.class, service::currentManifest); diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BalanceUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BalanceUseCase.java index 8e200f8c..85f00573 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BalanceUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BalanceUseCase.java @@ -6,7 +6,7 @@ import io.shinhanlife.dap.lib.annotation.ToolHint; import io.shinhanlife.dap.mcc.biz.cmm.dto.*; public interface BalanceUseCase { - @McpTool(name = "oth.cmm.balance.inquiry", title = "잔고 조회 툴", description = "고객의 계좌 잔액을 조회합니다.") + @McpTool(name = "oth_cmm_balance_inquiry", title = "잔고 조회 툴", description = "고객의 계좌 잔액을 조회합니다.") @ToolHint(register = false, categoryKey = "cmm", mappingId = "ACC_001") Object execute(BalanceRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BillingProcessUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BillingProcessUseCase.java index 8c856e1d..96270e8e 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BillingProcessUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BillingProcessUseCase.java @@ -7,7 +7,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*; public interface BillingProcessUseCase { Object getStatus(BillingStatusRequest req); - @McpTool(name = "oth.cmm.billing.process", title = "청구 프로세스 툴", description = "청구 처리") + @McpTool(name = "oth_cmm_billing_process", title = "청구 프로세스 툴", description = "청구 처리") @ToolHint(register = false, categoryKey = "cmm", mappingId = "BILL_002") Object processBilling(BillingProcessRequest data); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BondIssueUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BondIssueUseCase.java index 840bd479..eabfcfdc 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BondIssueUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/BondIssueUseCase.java @@ -8,7 +8,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*; public interface BondIssueUseCase { Object check(BondCheckRequest req); - @McpTool(name = "oth.cmm.bond.issue", title = "채권 발행 툴", description = "증권 발행 테스트1") + @McpTool(name = "oth_cmm_bond_issue", title = "채권 발행 툴", description = "증권 발행 테스트1") @ToolHint(register = false, categoryKey = "cmm", mappingId = "BOND_002") Object issue(BondIssueRequest data); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchSchemaSampleUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchSchemaSampleUseCase.java index 7eb8bfde..13c4a7de 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchSchemaSampleUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchSchemaSampleUseCase.java @@ -8,7 +8,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchResponse; public interface ClaimSearchSchemaSampleUseCase { - @McpTool(name = "oth.cmm.claim.search", title = "청구 조회 스키마 샘플", description = "Claim search Tool sample using input and output JSON Schema resources.", annotations = @McpTool.McpAnnotations(readOnlyHint = true, idempotentHint = true)) + @McpTool(name = "oth_cmm_claim_search", title = "청구 조회 스키마 샘플", description = "Claim search Tool sample using input and output JSON Schema resources.", annotations = @McpTool.McpAnnotations(readOnlyHint = true, idempotentHint = true)) @ToolHint(register = false, categoryKey = "cmm", inputSchemaResource = "classpath:tool-schemas/cmm/claim-search-resource-input-schema.json", outputSchemaResource = "classpath:tool-schemas/cmm/claim-search-resource-output-schema.json") diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CommonUtilityUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CommonUtilityUseCase.java index 74f85e62..47c0a56f 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CommonUtilityUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CommonUtilityUseCase.java @@ -8,11 +8,11 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*; public interface CommonUtilityUseCase { Object registerVacation(VacationRegisterRequest req); - @McpTool(name = "oth.cmm.leave.count", title = "공통 유틸리티 툴", description = "연차 갯수 조회") + @McpTool(name = "oth_cmm_leave_count", title = "공통 유틸리티 툴", description = "연차 갯수 조회") @ToolHint(register = false, categoryKey = "cmm", mappingId = "HR_VAC_02") Object getLeaveCount(LeaveCountRequest data); - @McpTool(name = "oth.cmm.secret.execute", title = "공통 유틸리티 툴", description = "비공개 툴 테스트") + @McpTool(name = "oth_cmm_secret_execute", title = "공통 유틸리티 툴", description = "비공개 툴 테스트") @ToolHint(register = false, categoryKey = "cmm", mappingId = "SECRET_001") Object secretTool(LeaveCountRequest data); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ContractInquiryUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ContractInquiryUseCase.java index 6e21fcb4..3031622e 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ContractInquiryUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ContractInquiryUseCase.java @@ -8,7 +8,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*; public interface ContractInquiryUseCase { Object getStatus(ContractStatusRequest req); - @McpTool(name = "oth.cmm.contract.detail", title = "계약 상세조회 툴", description = "계약상세 조회") + @McpTool(name = "oth_cmm_contract_detail", title = "계약 상세조회 툴", description = "계약상세 조회") @ToolHint(register = false, categoryKey = "cmm", mappingId = "CNTR_002") Object getDetail(ContractDetailRequest data); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CustomerInfoUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CustomerInfoUseCase.java index c70d5116..30409a3b 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CustomerInfoUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/CustomerInfoUseCase.java @@ -8,7 +8,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*; public interface CustomerInfoUseCase { Object getGrade(CustomerGradeRequest req); - @McpTool(name = "oth.cmm.customer.detail", title = "고객 상세조회 툴", description = "고객상세 정보 조회") + @McpTool(name = "oth_cmm_customer_detail", title = "고객 상세조회 툴", description = "고객상세 정보 조회") @ToolHint(register = false, categoryKey = "cmm", mappingId = "CRM_002") Object getDetail(CustomerDetailRequest data); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaCommonCodeUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaCommonCodeUseCase.java index 3829fe0a..a7b2ae08 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaCommonCodeUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaCommonCodeUseCase.java @@ -20,7 +20,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.MetaCommonCodeRequest; * */ public interface MetaCommonCodeUseCase { - @McpTool(name = "oth.cmm.common-code.lookup", title = "메타 공통코드 조회 툴", description = "메타 통합코드 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true)) + @McpTool(name = "oth_cmm_commonCode_lookup", title = "메타 공통코드 조회 툴", description = "메타 통합코드 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true)) @ToolHint(register = false, requiresApproval = false, categoryKey = "cmm", mappingId = "CLCNNB00001") Object execute(MetaCommonCodeRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaTableUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaTableUseCase.java index 03cc0dc1..9c809945 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaTableUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/MetaTableUseCase.java @@ -20,7 +20,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.MetaTableRequest; * */ public interface MetaTableUseCase { - @McpTool(name = "oth.cmm.meta.table", title = "메타 테이블 조회 툴", description = "메타 테이블 정보 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true)) + @McpTool(name = "oth_cmm_meta_table", title = "메타 테이블 조회 툴", description = "메타 테이블 정보 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true)) @ToolHint(register = false, requiresApproval = false, categoryKey = "cmm", mappingId = "CLCNNB00001") Object execute(MetaTableRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/TemplateUtilityUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/TemplateUtilityUseCase.java index 3b9d4da7..64e0b7e1 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/TemplateUtilityUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/TemplateUtilityUseCase.java @@ -8,7 +8,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*; import java.util.Map; public interface TemplateUtilityUseCase { - @McpTool(name = "oth.cmm.template.url", title = "템플릿 유틸리티 툴", description = "요청한 템플릿(엑셀, 워드 등) 파일(양식)을 다운로드 받을 수 있는 시스템 URL을 반환합니다. AI는 이 URL을 사용자에게 마크다운 링크 형태로 제공해야 합니다.") + @McpTool(name = "oth_cmm_template_url", title = "템플릿 유틸리티 툴", description = "요청한 템플릿(엑셀, 워드 등) 파일(양식)을 다운로드 받을 수 있는 시스템 URL을 반환합니다. AI는 이 URL을 사용자에게 마크다운 링크 형태로 제공해야 합니다.") @ToolHint(categoryKey = "cmm") Map getTemplateFileUrl(TemplateDownloadRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/oth/usecase/Onnba3011UseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/oth/usecase/Onnba3011UseCase.java index 7f8b3654..6af755c0 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/oth/usecase/Onnba3011UseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/oth/usecase/Onnba3011UseCase.java @@ -5,7 +5,7 @@ import io.shinhanlife.dap.lib.annotation.ToolHint; import io.shinhanlife.dap.mcc.biz.oth.dto.*; public interface Onnba3011UseCase { - @McpTool(name = "oth.oth.onnba3011.call", description = "Onnba3011 호출 툴") + @McpTool(name = "oth_oth_onnba3011_call", description = "Onnba3011 호출 툴") @ToolHint(categoryKey = "oth", register = true) Object execute(Onnba3011Request req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/DailyQuoteToolUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/DailyQuoteToolUseCase.java index ade68558..9ecc6c1a 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/DailyQuoteToolUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/DailyQuoteToolUseCase.java @@ -7,7 +7,7 @@ import io.shinhanlife.dap.mcc.biz.smp.dto.DailyQuoteRequest; import io.shinhanlife.dap.mcc.biz.smp.dto.DailyQuoteResponse; public interface DailyQuoteToolUseCase { - @McpTool(name = "oth.smp.quote.daily", title = "오늘의 명언 툴", description = "무작위로 영감을 주는 명언을 하나 가져옵니다.") + @McpTool(name = "oth_smp_quote_daily", title = "오늘의 명언 툴", description = "무작위로 영감을 주는 명언을 하나 가져옵니다.") @ToolHint(register = false, categoryKey = "smp", mappingId = "QUOTE_001") DailyQuoteResponse execute(DailyQuoteRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/ExchangeRateToolUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/ExchangeRateToolUseCase.java index 77cd0ab8..bf7c1972 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/ExchangeRateToolUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/ExchangeRateToolUseCase.java @@ -9,7 +9,7 @@ import io.shinhanlife.dap.mcc.biz.smp.dto.ExchangeRateRequest; import io.shinhanlife.dap.mcc.biz.smp.dto.ExchangeRateResponse; public interface ExchangeRateToolUseCase { - @McpTool(name = "oth.smp.exchange-rate.inquiry", title = "실시간 환율 조회 툴", description = "원하는 통화의 실시간 환율을 조회합니다. (예: USD, EUR, JPY)") + @McpTool(name = "oth_smp_exchangeRate_inquiry", title = "실시간 환율 조회 툴", description = "원하는 통화의 실시간 환율을 조회합니다. (예: USD, EUR, JPY)") @ToolHint(register = false, categoryKey = "smp", mappingId = "EXCHANGE_001") ExchangeRateResponse execute(ExchangeRateRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java index cb74912b..1eb10081 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/SampleHttpStatusUseCase.java @@ -7,7 +7,7 @@ import org.springaicommunity.mcp.annotation.McpTool; /** Sample Tool that demonstrates a configured HTTP API integration. */ public interface SampleHttpStatusUseCase { - @McpTool(name = "oth.smp.sample.http.status", + @McpTool(name = "oth_smp_sample_status", title = "Sample external HTTP API status", description = "Calls the configured sample HTTP API and returns its status.") @ToolHint(register = false, categoryKey = "smp", mappingId = "HTTP_SAMPLE_001") diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/TeamMemberUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/TeamMemberUseCase.java index a87cdb19..dd801934 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/TeamMemberUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/TeamMemberUseCase.java @@ -6,7 +6,7 @@ import io.shinhanlife.dap.lib.annotation.ToolHint; import io.shinhanlife.dap.mcc.biz.smp.dto.TeamMemberRequest; public interface TeamMemberUseCase { - @McpTool(name = "oth.smp.team.list", title = "신한라이프 MCP, TOOL 파트 구성원 조회", description = "신한라이프 MCP, TOOL 파트 구성원을 조회합니다.", annotations = @McpTool.McpAnnotations(openWorldHint = true)) + @McpTool(name = "oth_smp_team_list", title = "신한라이프 MCP, TOOL 파트 구성원 조회", description = "신한라이프 MCP, TOOL 파트 구성원을 조회합니다.", annotations = @McpTool.McpAnnotations(openWorldHint = true)) @ToolHint(register = false, requiresApproval = false, categoryKey = "smp", mappingId = "DIRECT0001") Object execute(TeamMemberRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/WeatherToolUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/WeatherToolUseCase.java index d46d76ca..d60b1e10 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/WeatherToolUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/smp/usecase/WeatherToolUseCase.java @@ -6,7 +6,7 @@ import io.shinhanlife.dap.lib.annotation.ToolHint; import io.shinhanlife.dap.mcc.biz.smp.dto.*; public interface WeatherToolUseCase { - @McpTool(name = "oth.smp.weather.inquiry", title = "날씨 조회 툴", description = "특정 도시의 현재 날씨, 온도, 풍속 정보를 조회합니다.") + @McpTool(name = "oth_smp_weather_inquiry", title = "날씨 조회 툴", description = "특정 도시의 현재 날씨, 온도, 풍속 정보를 조회합니다.") @ToolHint(register = false, categoryKey = "smp", mappingId = "WEATHER_001") WeatherResponse execute(WeatherRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqDetailUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqDetailUseCase.java index 3817d65e..853fff5f 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqDetailUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqDetailUseCase.java @@ -21,7 +21,7 @@ import io.shinhanlife.dap.mcc.biz.sol.dto.SolReqDetailRequest; */ public interface SolReqDetailUseCase { - @McpTool(name = "oth.sol.request.detail", title = "SolReqDetail 툴", description = "SOL 의뢰서 상세 조회", annotations = @McpTool.McpAnnotations(openWorldHint = true, readOnlyHint = true)) + @McpTool(name = "oth_sol_request_detail", title = "SolReqDetail 툴", description = "SOL 의뢰서 상세 조회", annotations = @McpTool.McpAnnotations(openWorldHint = true, readOnlyHint = true)) @ToolHint(register = false, requiresApproval = false, categoryKey = "sol", mappingId = "SOLG00000002") Object execute(SolReqDetailRequest req); } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqListUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqListUseCase.java index 2b14abe4..a3c6e2a0 100644 --- a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqListUseCase.java +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/sol/usecase/SolReqListUseCase.java @@ -6,7 +6,7 @@ import io.shinhanlife.dap.lib.annotation.ToolHint; import io.shinhanlife.dap.mcc.biz.sol.dto.SolReqListRequest; public interface SolReqListUseCase { - @McpTool(name = "oth.sol.request.list", title = "SolReqList 툴", description = "SOL 의뢰서 목록 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true)) + @McpTool(name = "oth_sol_request_list", title = "SolReqList 툴", description = "SOL 의뢰서 목록 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true)) @ToolHint(register = false, requiresApproval = false, categoryKey = "sol", mappingId = "SOLG00000001") Object execute(SolReqListRequest req); } diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/converter/ClaimSearchConverter.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/converter/ClaimSearchConverter.java new file mode 100644 index 00000000..ed5d79c8 --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/converter/ClaimSearchConverter.java @@ -0,0 +1,14 @@ +package io.shinhanlife.dap.mcc.biz.cmm.converter; + +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequest; +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchResponse; +import io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_I; +import io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_O; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface ClaimSearchConverter { + CLCNNB00001_I toLegacyRequest(ClaimSearchRequest request); + ClaimSearchRequest toRequest(CLCNNB00001_I mciRequest); + ClaimSearchResponse toResponse(CLCNNB00001_O mciRes); +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchRequest.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchRequest.java new file mode 100644 index 00000000..644d3146 --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchRequest.java @@ -0,0 +1,16 @@ +package io.shinhanlife.dap.mcc.biz.cmm.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ClaimSearchRequest { + @Schema(description = "보험금 청구번호", example = "CLM202608100001", requiredMode = Schema.RequiredMode.REQUIRED) + private String claimNo; + + @Schema(description = "보험 계약번호", example = "10023456789") + private String contractNo; + +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchResponse.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchResponse.java new file mode 100644 index 00000000..301d3f8a --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/ClaimSearchResponse.java @@ -0,0 +1,22 @@ +package io.shinhanlife.dap.mcc.biz.cmm.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ClaimSearchResponse { + private String resultCode; + + private String resultMessage; + @Schema(description = "청구 처리 상태 코드", example = "RECEIVED", requiredMode = Schema.RequiredMode.REQUIRED) + private String status; + + @Schema(description = "청구 처리 상태명", example = "접수", requiredMode = Schema.RequiredMode.REQUIRED) + private String statusLabel; + + @Schema(description = "승인 금액", example = "150000") + private Long approvedAmount; + +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCase.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCase.java new file mode 100644 index 00000000..500cc752 --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCase.java @@ -0,0 +1,29 @@ +package io.shinhanlife.dap.mcc.biz.cmm.usecase; + +import org.springaicommunity.mcp.annotation.McpTool; +import io.shinhanlife.dap.lib.annotation.ToolHint; +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequest; +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchResponse; + +/** + * @package io.shinhanlife.dap.mcc.biz.cmm.usecase + * @className ClaimSearchUseCase + * @description AX HUB 시스템 처리 클래스 + * @author jade + * @create 2026.08.10 + *
+ * ---------- 개정이력 ----------
+ * 수정일      수정자    수정내용
+ * ---------- -------- ---------------------------
+ * 2026.08.10  jade    최초생성
+ *
+ * 
+ */ +public interface ClaimSearchUseCase { + + @McpTool(name = "sms_cmm_claim_search", title = "청구번호 또는 계약번호로 보험금 청구 상태를 조회한다.", description = "청구번호 또는 계약번호로 보험금 청구 상태를 조회한다.") + @ToolHint(register = false, categoryKey = "cmm", mappingId = "CLCNNB00001", + inputSchemaResource = "classpath:tool-schemas/cmm/claim-search-resource-input-schema.json", + outputSchemaResource = "classpath:tool-schemas/cmm/claim-search-resource-output-schema.json") + ClaimSearchResponse execute(ClaimSearchRequest req); +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java new file mode 100644 index 00000000..8ce78e7b --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java @@ -0,0 +1,68 @@ +package io.shinhanlife.dap.mcc.biz.cmm.usecase.impl; + +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequest; +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchResponse; +import io.shinhanlife.dap.mcc.biz.cmm.usecase.ClaimSearchUseCase; +import io.shinhanlife.glow.communication.dto.Transfer; +import org.springframework.stereotype.Service; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import io.shinhanlife.dap.mcc.biz.cmm.converter.ClaimSearchConverter; +import io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_I; +import io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_O; +import io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.MciNclaClient; + + +/** + * @package io.shinhanlife.dap.mcc.biz.cmm.usecase.impl + * @className ClaimSearchUseCaseImpl + * @description AX HUB 시스템 처리 클래스 + * @author jade + * @create 2026.08.10 + *
+ * ---------- 개정이력 ----------
+ * 수정일      수정자    수정내용
+ * ---------- -------- ---------------------------
+ * 2026.08.10  jade    최초생성
+ *
+ * 
+ */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ClaimSearchUseCaseImpl implements ClaimSearchUseCase { + + private final MciNclaClient mci; + private final ClaimSearchConverter converter; + + @Override + public ClaimSearchResponse execute(ClaimSearchRequest req) { + log.info("[MCI Tool] {} 요청 수신.", "sms_cmm_claim_search"); + try { + // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO) + CLCNNB00001_I mciReq = converter.toLegacyRequest(req); + + Transfer resTransfer = mci.callTo( + "CLCNNB00001", + null, + mciReq, + CLCNNB00001_O.class + ); + ClaimSearchResponse response = new ClaimSearchResponse(); + if (resTransfer.getBody() != null) { + response = converter.toResponse(resTransfer.getBody()); + } + response.setResultCode("SUCCESS"); + response.setResultMessage(resTransfer.getBody() != null + ? "MCI call completed." + : "MCI call completed without a response body."); + return response; + } catch (Exception e) { + log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e); + ClaimSearchResponse response = new ClaimSearchResponse(); + response.setResultCode("ERROR"); + response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error"); + return response; + } + } +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/usecase/SmsToolUseCase.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/usecase/SmsToolUseCase.java index 9f80d439..ccfdf333 100644 --- a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/usecase/SmsToolUseCase.java +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/usecase/SmsToolUseCase.java @@ -5,7 +5,7 @@ import io.shinhanlife.dap.lib.annotation.ToolHint; import io.shinhanlife.dap.mcc.biz.sms.dto.*; public interface SmsToolUseCase { - @McpTool(name = "sms.sms.msg.send", title = "SMS 발송 툴", description = "SMS 발송 기능을 제공합니다.") + @McpTool(name = "sms_sms_msg_send", title = "SMS 발송 툴", description = "SMS 발송 기능을 제공합니다.") @ToolHint(register = false, categoryKey = "sms", mappingId = "SMS_SEND") Object sendSms(SmsSendRequest req); } diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/MciNclaClient.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/MciNclaClient.java new file mode 100644 index 00000000..5c81c4fa --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/MciNclaClient.java @@ -0,0 +1,30 @@ +package io.shinhanlife.dap.mcc.infra.itrf.mci.ncla; + +import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; +import io.shinhanlife.dap.lib.integration.mci.component.AxhubMciComponent; +import io.shinhanlife.glow.communication.dto.Transfer; + +/** + * @package io.shinhanlife.dap.mcc.infra.itrf.mci.ncla + * @className MciNclaClient + * @description AX HUB 시스템 처리 클래스 + * @author jade + * @create 2026.08.10 + *
+ * ---------- 개정이력 ----------
+ * 수정일      수정자    수정내용
+ * ---------- -------- ---------------------------
+ * 2026.08.10  jade    최초생성
+ *
+ * 
+ */ +@Component +@RequiredArgsConstructor +public class MciNclaClient { + private final AxhubMciComponent mci; + + public Transfer callTo(String interfaceId, String dummy, Object mciReq, Class resType) throws Exception { + return mci.callTo(interfaceId, dummy, mciReq, resType); + } +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java new file mode 100644 index 00000000..1c5a292f --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java @@ -0,0 +1,14 @@ +package io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.io; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class CLCNNB00001_I { + @Schema(description = "보험금 청구번호", example = "CLM202608100001", requiredMode = Schema.RequiredMode.REQUIRED) + private String claimNo; + + @Schema(description = "보험 계약번호", example = "10023456789") + private String contractNo; + +} diff --git a/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java new file mode 100644 index 00000000..99760997 --- /dev/null +++ b/dap-was-sms/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java @@ -0,0 +1,17 @@ +package io.shinhanlife.dap.mcc.infra.itrf.mci.ncla.io; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class CLCNNB00001_O { + @Schema(description = "청구 처리 상태 코드", example = "RECEIVED", requiredMode = Schema.RequiredMode.REQUIRED) + private String status; + + @Schema(description = "청구 처리 상태명", example = "접수", requiredMode = Schema.RequiredMode.REQUIRED) + private String statusLabel; + + @Schema(description = "승인 금액", example = "150000") + private Long approvedAmount; + +} diff --git a/dap-was-sms/src/main/resources/mock-responses/sms_cmm_claim_search.json b/dap-was-sms/src/main/resources/mock-responses/sms_cmm_claim_search.json new file mode 100644 index 00000000..9c4f9c03 --- /dev/null +++ b/dap-was-sms/src/main/resources/mock-responses/sms_cmm_claim_search.json @@ -0,0 +1,5 @@ +{ + "status" : "RECEIVED", + "statusLabel" : "접수", + "approvedAmount" : 150000 +} diff --git a/dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json b/dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json new file mode 100644 index 00000000..e6f63087 --- /dev/null +++ b/dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "additionalProperties": false, + "properties": { + "TODO_FIELD": { + "type": "string", + "description": "TODO: 파라미터 설명을 입력하세요." + } + }, + "required": [] +} diff --git a/dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json b/dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json new file mode 100644 index 00000000..9c280db8 --- /dev/null +++ b/dap-was-sms/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "type": "string", + "description": "처리 결과 상태 (SUCCESS / FAILURE)", + "enum": ["SUCCESS", "FAILURE"] + }, + "message": { + "type": "string", + "description": "처리 결과 메시지" + } + }, + "required": ["status"] +} diff --git a/dap-was-sms/src/test/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java b/dap-was-sms/src/test/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java new file mode 100644 index 00000000..37356b3e --- /dev/null +++ b/dap-was-sms/src/test/java/io/shinhanlife/dap/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java @@ -0,0 +1,16 @@ +package io.shinhanlife.dap.mcc.biz.cmm.usecase; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequest; +import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchResponse; +import org.junit.jupiter.api.Test; + +class ClaimSearchUseCaseTest { + + @Test + void createsToolRequestAndResponseDtos() { + assertNotNull(new ClaimSearchRequest()); + assertNotNull(new ClaimSearchResponse()); + } +}