Refactor: Move MCP tool annotations to interface level
This commit is contained in:
@@ -179,12 +179,35 @@ public class ToolScaffolder {
|
||||
String serviceInterfaceContent = """
|
||||
package %s.usecase;
|
||||
|
||||
import io.shinhanlife.dap.lib.annotation.McpFunction;
|
||||
import io.shinhanlife.dap.lib.annotation.McpTool;
|
||||
import %s.dto.%sRequest;
|
||||
|
||||
@McpTool(
|
||||
routingType = "%s",
|
||||
categoryKey = "%s"
|
||||
)
|
||||
public interface %sUseCase {
|
||||
@McpFunction(
|
||||
displayName = "%s 툴",
|
||||
name = "%s",
|
||||
description = "%s",
|
||||
prompt = "%s",
|
||||
mappingId = "%s",
|
||||
register = %s,
|
||||
requiresApproval = false,
|
||||
openWorldHint = true
|
||||
)
|
||||
Object execute(%sRequest req);
|
||||
}
|
||||
""".formatted(bizPackage, bizPackage, baseName, baseName, baseName);
|
||||
""".formatted(
|
||||
bizPackage,
|
||||
bizPackage, baseName,
|
||||
routingType, group.toLowerCase(),
|
||||
baseName,
|
||||
baseName, toolName, description, description + " 해줘.", interfaceId, register,
|
||||
baseName
|
||||
);
|
||||
|
||||
Files.writeString(usecaseDir.resolve(baseName + "UseCase.java"), serviceInterfaceContent);
|
||||
|
||||
@@ -194,8 +217,6 @@ public class ToolScaffolder {
|
||||
serviceImplContent = """
|
||||
package %s.usecase.impl;
|
||||
|
||||
import io.shinhanlife.dap.lib.annotation.McpFunction;
|
||||
import io.shinhanlife.dap.lib.annotation.McpTool;
|
||||
import %s.dto.%sRequest;
|
||||
import %s.dto.%sResponse;
|
||||
import %s.usecase.%sUseCase;
|
||||
@@ -225,26 +246,12 @@ public class ToolScaffolder {
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@McpTool(
|
||||
routingType = "%s",
|
||||
categoryKey = "%s"
|
||||
)
|
||||
public class %sUseCaseImpl implements %sUseCase {
|
||||
|
||||
private final AxhubMciComponent mci;
|
||||
private final %sLegacyConverter converter;
|
||||
|
||||
@Override
|
||||
@McpFunction(
|
||||
displayName = "%s 툴",
|
||||
name = "%s",
|
||||
description = "%s",
|
||||
prompt = "%s",
|
||||
mappingId = "%s",
|
||||
register = %s,
|
||||
requiresApproval = false,
|
||||
openWorldHint = true
|
||||
)
|
||||
public Object execute(%sRequest req) {
|
||||
log.info("[MCI Tool] {} 요청 수신.", "%s");
|
||||
try {
|
||||
@@ -269,18 +276,14 @@ public class ToolScaffolder {
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName,
|
||||
bizPackage, baseName,
|
||||
BASE_PACKAGE, group.toLowerCase(), interfaceId,
|
||||
bizPackage, baseName,
|
||||
author,
|
||||
createDate,
|
||||
createDate, author,
|
||||
routingType, group.toLowerCase(),
|
||||
baseName, baseName,
|
||||
baseName,
|
||||
baseName, toolName, description, description + " 해줘.", interfaceId, register,
|
||||
baseName,
|
||||
interfaceId,
|
||||
baseName, toolName,
|
||||
interfaceId,
|
||||
interfaceId
|
||||
);
|
||||
@@ -288,8 +291,6 @@ public class ToolScaffolder {
|
||||
serviceImplContent = """
|
||||
package %s.usecase.impl;
|
||||
|
||||
import io.shinhanlife.dap.lib.annotation.McpFunction;
|
||||
import io.shinhanlife.dap.lib.annotation.McpTool;
|
||||
import %s.dto.%sRequest;
|
||||
import %s.dto.%sResponse;
|
||||
import %s.usecase.%sUseCase;
|
||||
@@ -316,24 +317,11 @@ public class ToolScaffolder {
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@McpTool(
|
||||
routingType = "%s",
|
||||
categoryKey = "%s"
|
||||
)
|
||||
public class %sUseCaseImpl extends AbstractMcpToolUseCase implements %sUseCase {
|
||||
|
||||
private final %sLegacyConverter converter;
|
||||
|
||||
@Override
|
||||
@McpFunction(
|
||||
displayName = "%s 툴",
|
||||
name = "%s",
|
||||
description = "%s",
|
||||
prompt = "%s",
|
||||
mappingId = "%s",
|
||||
register = %s,
|
||||
requiresApproval = false
|
||||
)
|
||||
public Object execute(%sRequest req) {
|
||||
// %sLegacyRequest legacyRequest = converter.toLegacyRequest(req);
|
||||
return executeLegacy("%s", "%s", req); // Or pass legacyRequest
|
||||
@@ -348,10 +336,8 @@ public class ToolScaffolder {
|
||||
author,
|
||||
createDate,
|
||||
createDate, author,
|
||||
routingType, group.toLowerCase(),
|
||||
baseName, baseName,
|
||||
baseName,
|
||||
baseName, toolName, description, description + " 해줘.", interfaceId, register,
|
||||
baseName,
|
||||
baseName,
|
||||
routingType, interfaceId
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.stream.Stream;
|
||||
public class ToolSourceUpdater {
|
||||
|
||||
public static void updateToolSource(String toolName, String domainGroup, String description, boolean register, Boolean requiresApproval) throws Exception {
|
||||
// 1. Find all *Service.java files in axhub-tool-* directories
|
||||
// 1. Find all *UseCase.java files in dap-tool-* directories
|
||||
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
|
||||
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
|
||||
|
||||
@@ -36,8 +36,8 @@ public class ToolSourceUpdater {
|
||||
try (Stream<Path> paths = Files.walk(rootDir)) {
|
||||
javaFiles = paths
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(p -> p.toString().endsWith("Service.java"))
|
||||
.filter(p -> p.toString().contains("axhub-tool-"))
|
||||
.filter(p -> p.toString().endsWith("UseCase.java"))
|
||||
.filter(p -> p.toString().contains("dap-tool-") || p.toString().contains("axhub-tool-"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +94,8 @@ public class BusinessToolController {
|
||||
outerLoop:
|
||||
for (Object bean : allBeans.values()) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
for (Method method : targetClass.getDeclaredMethods()) {
|
||||
McpFunction mcpFunc = AnnotationUtils.findAnnotation(method, McpFunction.class);
|
||||
for (Method targetMethodOfClass : targetClass.getDeclaredMethods()) {
|
||||
McpFunction mcpFunc = AnnotationUtils.findAnnotation(targetMethodOfClass, McpFunction.class);
|
||||
if (mcpFunc != null) {
|
||||
String baseName = mcpFunc.name();
|
||||
String expectedName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
|
||||
@@ -104,7 +104,11 @@ public class BusinessToolController {
|
||||
|
||||
if (expectedName.equals(functionName) || baseName.equals(functionName)) {
|
||||
targetBean = bean;
|
||||
targetMethod = method;
|
||||
try {
|
||||
targetMethod = bean.getClass().getMethod(targetMethodOfClass.getName(), targetMethodOfClass.getParameterTypes());
|
||||
} catch (NoSuchMethodException e) {
|
||||
targetMethod = targetMethodOfClass;
|
||||
}
|
||||
targetFunctionAnnotation = mcpFunc;
|
||||
break outerLoop;
|
||||
}
|
||||
|
||||
@@ -76,11 +76,18 @@ public class ToolRegistryHeartbeatSender {
|
||||
Map<String, Object> allBeans = applicationContext.getBeansOfType(Object.class);
|
||||
for (Object bean : allBeans.values()) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
|
||||
// 클래스 또는 프록시(인터페이스)에서 @McpTool 스캔
|
||||
McpTool toolAnnotation = AnnotationUtils.findAnnotation(targetClass, McpTool.class);
|
||||
if (toolAnnotation == null) {
|
||||
toolAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), McpTool.class);
|
||||
}
|
||||
|
||||
for (Method method : targetClass.getDeclaredMethods()) {
|
||||
// 메서드, 수퍼클래스, 인터페이스를 모두 뒤져서 @McpFunction 스캔
|
||||
McpFunction functionAnnotation = AnnotationUtils.findAnnotation(method, McpFunction.class);
|
||||
if (functionAnnotation != null) {
|
||||
|
||||
if (functionAnnotation != null && toolAnnotation != null) {
|
||||
String baseName = functionAnnotation.displayName();
|
||||
String rawSubToolName = functionAnnotation.name();
|
||||
String subToolName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
|
||||
|
||||
Reference in New Issue
Block a user