diff --git a/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/annotation/McpValidation.java b/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/annotation/McpValidation.java
new file mode 100644
index 00000000..2b7d975d
--- /dev/null
+++ b/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/annotation/McpValidation.java
@@ -0,0 +1,30 @@
+package io.shinhanlife.dap.lib.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @package io.shinhanlife.dap.lib.annotation
+ * @className McpValidation
+ * @description Declares JSON Schema validation constraints for MCP tool input fields
+ * @author 0986406
+ * @create 2026.07.27
+ *
+ * ---------- revision history ----------
+ * date author description
+ * ---------- --------- ---------------------------
+ * 2026.07.27 0986406 initial creation
+ *
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface McpValidation {
+ boolean required() default false;
+ String pattern() default "";
+ long minimum() default Long.MIN_VALUE;
+ String[] allowedValues() default {};
+}
diff --git a/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/util/JsonSchemaGenerator.java b/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/util/JsonSchemaGenerator.java
index 587e0a1e..46d303b2 100644
--- a/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/util/JsonSchemaGenerator.java
+++ b/dap-tool-core/src/main/java/io/shinhanlife/dap/lib/util/JsonSchemaGenerator.java
@@ -3,6 +3,7 @@ package io.shinhanlife.dap.lib.util;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
@@ -58,6 +59,20 @@ public class JsonSchemaGenerator {
if ((jsonProp != null && jsonProp.required()) || (paramAnnotation != null && paramAnnotation.required())) {
requiredList.add(field.getName());
}
+
+ McpValidation validation = field.getAnnotation(McpValidation.class);
+ if (validation != null && validation.required() && !requiredList.contains(field.getName())) {
+ requiredList.add(field.getName());
+ }
+ if (validation != null && !validation.pattern().isEmpty()) {
+ fieldSchema.put("pattern", validation.pattern());
+ }
+ if (validation != null && validation.minimum() != Long.MIN_VALUE) {
+ fieldSchema.put("minimum", validation.minimum());
+ }
+ if (validation != null && validation.allowedValues().length > 0) {
+ fieldSchema.put("enum", List.of(validation.allowedValues()));
+ }
properties.put(field.getName(), fieldSchema);
}
diff --git a/dap-tool-core/src/test/java/io/shinhanlife/dap/lib/util/JsonSchemaGeneratorTest.java b/dap-tool-core/src/test/java/io/shinhanlife/dap/lib/util/JsonSchemaGeneratorTest.java
new file mode 100644
index 00000000..f5340464
--- /dev/null
+++ b/dap-tool-core/src/test/java/io/shinhanlife/dap/lib/util/JsonSchemaGeneratorTest.java
@@ -0,0 +1,57 @@
+package io.shinhanlife.dap.lib.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @package io.shinhanlife.dap.lib.util
+ * @className JsonSchemaGeneratorTest
+ * @description JSON Schema constraint generation test
+ * @author 0986406
+ * @create 2026.07.27
+ *
+ * ---------- revision history ----------
+ * date author description
+ * ---------- --------- ---------------------------
+ * 2026.07.27 0986406 initial creation
+ *
+ */
+class JsonSchemaGeneratorTest {
+
+ @Test
+ void includesMcpParameterConstraintsInGeneratedSchema() {
+ Map schema = JsonSchemaGenerator.generateSchema(ValidatedRequest.class);
+ Map> properties = properties(schema);
+
+ assertTrue(((List) schema.get("required")).contains("phoneNumber"));
+ assertTrue(((List) schema.get("required")).contains("approvalStatus"));
+ assertEquals("^01[0-9]{8,9}$", properties.get("phoneNumber").get("pattern"));
+ assertEquals(1L, properties.get("amount").get("minimum"));
+ assertEquals(List.of("APPROVE", "REJECT"), properties.get("approvalStatus").get("enum"));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map> properties(Map schema) {
+ return (Map>) schema.get("properties");
+ }
+
+ private static class ValidatedRequest {
+ @McpParameter(description = "recipient phone number", required = true)
+ @McpValidation(pattern = "^01[0-9]{8,9}$")
+ private String phoneNumber;
+
+ @McpParameter(description = "issue amount", required = true)
+ @McpValidation(minimum = 1)
+ private Long amount;
+
+ @McpParameter(description = "approval result")
+ @McpValidation(required = true, allowedValues = {"APPROVE", "REJECT"})
+ private String approvalStatus;
+ }
+}
diff --git a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BalanceRequest.java b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BalanceRequest.java
index e8fb92db..a16c0c7f 100644
--- a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BalanceRequest.java
+++ b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BalanceRequest.java
@@ -16,10 +16,12 @@ package io.shinhanlife.dap.mcc.biz.cmm.dto;
*
*/
import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
import lombok.Data;
@Data
public class BalanceRequest {
@McpParameter(description = "고객의 계좌번호 (- 제외)", required = true)
+ @McpValidation(pattern = "\\S")
private String accountNumber;
}
diff --git a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BillingProcessRequest.java b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BillingProcessRequest.java
index a788451e..795537ae 100644
--- a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BillingProcessRequest.java
+++ b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BillingProcessRequest.java
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
import lombok.Getter;
import lombok.Setter;
@@ -30,8 +31,10 @@ import lombok.Setter;
public class BillingProcessRequest {
@McpParameter(description = "처리할 청구 접수 번호", required = true)
+ @McpValidation(pattern = "\\S")
private String billingId;
@McpParameter(description = "심사 승인 여부 (예: APPROVE, REJECT)")
+ @McpValidation(required = true, allowedValues = {"APPROVE", "REJECT"})
private String approvalStatus;
-}
\ No newline at end of file
+}
diff --git a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BondIssueRequest.java b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BondIssueRequest.java
index e310fdb2..aa6d8248 100644
--- a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BondIssueRequest.java
+++ b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/BondIssueRequest.java
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
import lombok.Getter;
import lombok.Setter;
@@ -30,8 +31,10 @@ import lombok.Setter;
public class BondIssueRequest {
@McpParameter(description = "발행할 디지털 증권 금액", required = true)
+ @McpValidation(minimum = 1)
private Long amount;
@McpParameter(description = "발행 대상 계좌 번호", required = true)
+ @McpValidation(pattern = "\\S")
private String targetAccount;
-}
\ No newline at end of file
+}
diff --git a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/VacationRegisterRequest.java b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/VacationRegisterRequest.java
index 5fabdb0f..bde48595 100644
--- a/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/VacationRegisterRequest.java
+++ b/dap-tool-oth/src/main/java/io/shinhanlife/dap/mcc/biz/cmm/dto/VacationRegisterRequest.java
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
import lombok.Getter;
import lombok.Setter;
@@ -30,8 +31,10 @@ import lombok.Setter;
public class VacationRegisterRequest {
@McpParameter(description = "연차를 등록할 사원 번호", required = true)
+ @McpValidation(pattern = "\\S")
private String employeeId;
@McpParameter(description = "휴가 일자 (YYYY-MM-DD 형식)", required = true)
+ @McpValidation(pattern = "^\\d{4}-\\d{2}-\\d{2}$")
private String date;
-}
\ No newline at end of file
+}
diff --git a/dap-tool-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/dto/SmsSendRequest.java b/dap-tool-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/dto/SmsSendRequest.java
index 8912d901..b7cea0df 100644
--- a/dap-tool-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/dto/SmsSendRequest.java
+++ b/dap-tool-sms/src/main/java/io/shinhanlife/dap/mcc/biz/sms/dto/SmsSendRequest.java
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import io.shinhanlife.dap.lib.annotation.McpParameter;
+import io.shinhanlife.dap.lib.annotation.McpValidation;
import lombok.Getter;
import lombok.Setter;
@@ -30,10 +31,12 @@ import lombok.Setter;
public class SmsSendRequest {
@McpParameter(description = "수신자 전화번호", required = true)
+ @McpValidation(pattern = "^01[0-9]-?\\d{3,4}-?\\d{4}$")
private String phoneNumber;
@McpParameter(description = "전송할 메시지 내용", required = true)
+ @McpValidation(pattern = "\\S")
private String message;
-}
\ No newline at end of file
+}