feat: Add MCI Scaffolding support and fix bean collision
This commit is contained in:
@@ -21,7 +21,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "shinhan.integration")
|
||||
public class ShinhanIntegrationProperties {
|
||||
|
||||
|
||||
@@ -161,188 +161,270 @@ public class ToolScaffolder {
|
||||
|
||||
String toolName = baseName.isEmpty() ? baseName : Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1);
|
||||
|
||||
// Generate Service
|
||||
String serviceContent = """
|
||||
package %s.service;
|
||||
boolean isMci = "MCI".equalsIgnoreCase(routingType);
|
||||
String serviceContent;
|
||||
|
||||
import %s.annotation.McpFunction;
|
||||
import %s.annotation.McpTool;
|
||||
import %s.dto.%sReq;
|
||||
import %s.dto.%sRes;
|
||||
import %s.%s.converter.%sLegacyConverter;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
if (isMci) {
|
||||
serviceContent = """
|
||||
package %s.service;
|
||||
|
||||
/**
|
||||
* @package %s.service
|
||||
* @className %sService
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Service
|
||||
@McpTool(
|
||||
routingType = "%s",
|
||||
categoryKey = "%s"
|
||||
)
|
||||
public class %sService extends AbstractMcpToolService {
|
||||
import %s.annotation.McpFunction;
|
||||
import %s.annotation.McpTool;
|
||||
import %s.dto.%sReq;
|
||||
import %s.dto.%sRes;
|
||||
import io.shinhanlife.dap.common.integration.mci.component.AxhubMciComponent;
|
||||
import io.shinhanlife.glow.communication.dto.Transfer;
|
||||
import org.springframework.stereotype.Service;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
private final %sLegacyConverter converter = Mappers.getMapper(%sLegacyConverter.class);
|
||||
|
||||
@McpFunction(
|
||||
displayName = "%s 툴",
|
||||
name = "%s",
|
||||
description = "%s",
|
||||
prompt = "%s",
|
||||
mappingId = "%s",
|
||||
register = %s,
|
||||
requiresApproval = false
|
||||
/**
|
||||
* @package %s.service
|
||||
* @className %sService
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@McpTool(
|
||||
routingType = "%s",
|
||||
categoryKey = "%s"
|
||||
)
|
||||
public Object execute(%sReq req) {
|
||||
// %sLegacyReq legacyReq = converter.toLegacyReq(req);
|
||||
return executeLegacy("%s", "%s", req); // Or pass legacyReq
|
||||
public class %sService {
|
||||
|
||||
private final AxhubMciComponent mci;
|
||||
|
||||
@McpFunction(
|
||||
displayName = "%s 툴",
|
||||
name = "%s",
|
||||
description = "%s",
|
||||
prompt = "%s",
|
||||
mappingId = "%s",
|
||||
register = %s,
|
||||
requiresApproval = false,
|
||||
openWorldHint = true
|
||||
)
|
||||
public Object execute(%sReq req) {
|
||||
log.info("[MCI Tool] {} 요청 수신.", "%s");
|
||||
try {
|
||||
Transfer<Object> resTransfer = mci.callTo(
|
||||
"%s",
|
||||
null,
|
||||
req,
|
||||
Object.class
|
||||
);
|
||||
return resTransfer.getBody() != null ? resTransfer.getBody() : "{\"status\":\"SUCCESS\"}";
|
||||
} catch (Exception e) {
|
||||
log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e);
|
||||
return "{\"status\":\"ERROR\", \"message\":\"" + e.getMessage() + "\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".formatted(
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, shortName, baseName,
|
||||
BASE_PACKAGE,
|
||||
baseName,
|
||||
author,
|
||||
createDate,
|
||||
createDate, author,
|
||||
routingType, group.toLowerCase(),
|
||||
baseName,
|
||||
baseName, baseName,
|
||||
baseName, toolName, description, description + " 해줘.", interfaceId, register,
|
||||
baseName,
|
||||
baseName,
|
||||
routingType, interfaceId
|
||||
);
|
||||
""".formatted(
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
author,
|
||||
createDate,
|
||||
createDate, author,
|
||||
routingType, group.toLowerCase(),
|
||||
baseName,
|
||||
baseName, toolName, description, description + " 해줘.", interfaceId, register,
|
||||
baseName,
|
||||
baseName, interfaceId
|
||||
);
|
||||
} else {
|
||||
serviceContent = """
|
||||
package %s.service;
|
||||
|
||||
import %s.annotation.McpFunction;
|
||||
import %s.annotation.McpTool;
|
||||
import %s.dto.%sReq;
|
||||
import %s.dto.%sRes;
|
||||
import %s.%s.converter.%sLegacyConverter;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @package %s.service
|
||||
* @className %sService
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Service
|
||||
@McpTool(
|
||||
routingType = "%s",
|
||||
categoryKey = "%s"
|
||||
)
|
||||
public class %sService extends AbstractMcpToolService {
|
||||
|
||||
private final %sLegacyConverter converter = Mappers.getMapper(%sLegacyConverter.class);
|
||||
|
||||
@McpFunction(
|
||||
displayName = "%s 툴",
|
||||
name = "%s",
|
||||
description = "%s",
|
||||
prompt = "%s",
|
||||
mappingId = "%s",
|
||||
register = %s,
|
||||
requiresApproval = false
|
||||
)
|
||||
public Object execute(%sReq req) {
|
||||
// %sLegacyReq legacyReq = converter.toLegacyReq(req);
|
||||
return executeLegacy("%s", "%s", req); // Or pass legacyReq
|
||||
}
|
||||
}
|
||||
""".formatted(
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, shortName, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
author,
|
||||
createDate,
|
||||
createDate, author,
|
||||
routingType, group.toLowerCase(),
|
||||
baseName,
|
||||
baseName, toolName, description, description + " 해줘.", interfaceId, register,
|
||||
baseName,
|
||||
baseName,
|
||||
routingType, interfaceId
|
||||
);
|
||||
}
|
||||
|
||||
Files.writeString(serviceDir.resolve(baseName + "Service.java"), serviceContent);
|
||||
|
||||
if (!isMci) {
|
||||
String legacyReqContent = """
|
||||
package %s.%s.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
// Generate Legacy Req DTO
|
||||
String legacyReqContent = """
|
||||
package %s.%s.dto;
|
||||
/**
|
||||
* @package %s.%s.dto
|
||||
* @className %sLegacyReq
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
public class %sLegacyReq {
|
||||
// TODO: Add legacy request fields here
|
||||
}
|
||||
""".formatted(BASE_PACKAGE, shortName, BASE_PACKAGE, shortName, baseName, author, createDate, createDate, author, baseName);
|
||||
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyReq.java"), legacyReqContent);
|
||||
|
||||
import lombok.Data;
|
||||
String legacyResContent = """
|
||||
package %s.%s.dto;
|
||||
|
||||
/**
|
||||
* @package %s.%s.dto
|
||||
* @className %sLegacyReq
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
public class %sLegacyReq {
|
||||
// TODO: Add legacy request fields here
|
||||
}
|
||||
""".formatted(BASE_PACKAGE, shortName, BASE_PACKAGE, shortName, baseName, author, createDate, createDate, author, baseName);
|
||||
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyReq.java"), legacyReqContent);
|
||||
import lombok.Data;
|
||||
|
||||
// Generate Legacy Res DTO
|
||||
String legacyResContent = """
|
||||
package %s.%s.dto;
|
||||
/**
|
||||
* @package %s.%s.dto
|
||||
* @className %sLegacyRes
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
public class %sLegacyRes {
|
||||
// TODO: Add legacy response fields here
|
||||
}
|
||||
""".formatted(BASE_PACKAGE, shortName, BASE_PACKAGE, shortName, baseName, author, createDate, createDate, author, baseName);
|
||||
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyRes.java"), legacyResContent);
|
||||
|
||||
import lombok.Data;
|
||||
String converterContent = """
|
||||
package %s.%s.converter;
|
||||
|
||||
/**
|
||||
* @package %s.%s.dto
|
||||
* @className %sLegacyRes
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Data
|
||||
public class %sLegacyRes {
|
||||
// TODO: Add legacy response fields here
|
||||
}
|
||||
""".formatted(BASE_PACKAGE, shortName, BASE_PACKAGE, shortName, baseName, author, createDate, createDate, author, baseName);
|
||||
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyRes.java"), legacyResContent);
|
||||
import %s.dto.%sReq;
|
||||
import %s.dto.%sRes;
|
||||
import %s.%s.dto.%sLegacyReq;
|
||||
import %s.%s.dto.%sLegacyRes;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
// Generate Legacy Converter
|
||||
String converterContent = """
|
||||
package %s.%s.converter;
|
||||
/**
|
||||
* @package %s.%s.converter
|
||||
* @className %sLegacyConverter
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface %sLegacyConverter {
|
||||
|
||||
import %s.dto.%sReq;
|
||||
import %s.dto.%sRes;
|
||||
import %s.%s.dto.%sLegacyReq;
|
||||
import %s.%s.dto.%sLegacyRes;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @package %s.%s.converter
|
||||
* @className %sLegacyConverter
|
||||
* @description AX HUB 시스템 처리 클래스
|
||||
* @author %s
|
||||
* @create %s
|
||||
* <pre>
|
||||
* ---------- 개정이력 ----------
|
||||
* 수정일 수정자 수정내용
|
||||
* ---------- -------- ---------------------------
|
||||
* %s %s 최초생성
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface %sLegacyConverter {
|
||||
|
||||
// @Mapping(source = "sourceField", target = "targetField")
|
||||
%sLegacyReq toLegacyReq(%sReq req);
|
||||
|
||||
%sRes toRes(%sLegacyRes legacyRes);
|
||||
}
|
||||
""".formatted(
|
||||
BASE_PACKAGE, shortName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, shortName, baseName,
|
||||
BASE_PACKAGE, shortName, baseName,
|
||||
BASE_PACKAGE, shortName, baseName, author, createDate, createDate, author,
|
||||
baseName, baseName, baseName, baseName, baseName, baseName
|
||||
);
|
||||
Files.writeString(converterDir.resolve(baseName + "LegacyConverter.java"), converterContent);
|
||||
%sLegacyReq toLegacyReq(%sReq req);
|
||||
|
||||
%sRes toRes(%sLegacyRes legacyRes);
|
||||
}
|
||||
""".formatted(
|
||||
BASE_PACKAGE, shortName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, baseName,
|
||||
BASE_PACKAGE, shortName, baseName,
|
||||
BASE_PACKAGE, shortName, baseName,
|
||||
BASE_PACKAGE, shortName, baseName, author, createDate, createDate, author,
|
||||
baseName, baseName, baseName, baseName, baseName, baseName
|
||||
);
|
||||
Files.writeString(converterDir.resolve(baseName + "LegacyConverter.java"), converterContent);
|
||||
}
|
||||
|
||||
log.append("\n=========================================\n");
|
||||
log.append(" Scaffolding Complete!\n");
|
||||
log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n");
|
||||
log.append("=========================================\n");
|
||||
log.append("[Service] ").append(serviceDir.resolve(baseName + "Service.java")).append("\n");
|
||||
log.append("[Req DTO] ").append(dtoDir.resolve(baseName + "Req.java")).append("\n");
|
||||
log.append("[Res DTO] ").append(dtoDir.resolve(baseName + "Res.java")).append("\n");
|
||||
log.append("[Legacy Req DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyReq.java")).append("\n");
|
||||
log.append("[Legacy Res DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyRes.java")).append("\n");
|
||||
log.append("[Legacy Converter] ").append(converterDir.resolve(baseName + "LegacyConverter.java")).append("\n");
|
||||
|
||||
if (!isMci) {
|
||||
log.append("[Legacy Req DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyReq.java")).append("\n");
|
||||
log.append("[Legacy Res DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyRes.java")).append("\n");
|
||||
log.append("[Legacy Converter] ").append(converterDir.resolve(baseName + "LegacyConverter.java")).append("\n");
|
||||
}
|
||||
log.append("\n Tip: ").append(interfaceId).append(" 목업 데이터를 mock-responses.json에 추가하세요.\n");
|
||||
|
||||
return log.toString();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.shinhanlife.dap.biz.mcp.tool.annotation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class McpFunctionAnnotationsTest {
|
||||
|
||||
@Test
|
||||
void usesConservativeDefaultsForMcpBehaviorHints() throws NoSuchMethodException {
|
||||
Method method = SampleTool.class.getDeclaredMethod("execute");
|
||||
McpFunction annotation = method.getAnnotation(McpFunction.class);
|
||||
|
||||
assertFalse(annotation.readOnlyHint());
|
||||
assertFalse(annotation.destructiveHint());
|
||||
assertFalse(annotation.idempotentHint());
|
||||
assertFalse(annotation.openWorldHint());
|
||||
}
|
||||
|
||||
static class SampleTool {
|
||||
@McpFunction(displayName = "sample", name = "sample", description = "sample")
|
||||
void execute() {
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user