fix(core): use AnnotationUtils and AopUtils for bulletproof proxy method scanning

This commit is contained in:
jade
2026-07-09 11:34:23 +09:00
parent 1db259dbc9
commit 367743b461
2 changed files with 23 additions and 13 deletions

View File

@@ -26,6 +26,8 @@ import io.shinhanlife.axhub.biz.mcp.tool.config.McpProperties;
import java.lang.reflect.Method;
import io.shinhanlife.axhub.biz.mcp.tool.util.JsonSchemaGenerator;
import org.springframework.util.ClassUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import lombok.extern.slf4j.Slf4j;
@@ -71,15 +73,12 @@ public class BusinessToolController {
// 1. 대상 Bean 및 Method 찾기 (ApplicationContext 활용)
Map<String, Object> arguments = params != null ? (Map<String, Object>) params.get("arguments") : null;
Object targetBean = null;
Method targetMethod = null;
McpFunction targetFunctionAnnotation = null;
Map<String, Object> toolBeans = applicationContext.getBeansWithAnnotation(McpTool.class);
outerLoop:
for (Object bean : toolBeans.values()) {
Class<?> userClass = ClassUtils.getUserClass(bean);
for (Method method : userClass.getDeclaredMethods()) {
McpFunction mcpFunc = method.getAnnotation(McpFunction.class);
Class<?> targetClass = AopUtils.getTargetClass(bean);
for (Method method : targetClass.getDeclaredMethods()) {
McpFunction mcpFunc = AnnotationUtils.findAnnotation(method, McpFunction.class);
if (mcpFunc != null) {
String baseName = mcpFunc.name();
String expectedName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
@@ -97,15 +96,26 @@ public class BusinessToolController {
}
if (targetBean == null || targetMethod == null) {
log.error("[Tool] 실행할 함수(Method)를 찾을 수 없습니다: {}", functionName);
List<String> availableFunctions = new ArrayList<>();
for (Object bean : toolBeans.values()) {
Class<?> targetCls = AopUtils.getTargetClass(bean);
for (Method m : targetCls.getDeclaredMethods()) {
McpFunction func = AnnotationUtils.findAnnotation(m, McpFunction.class);
if (func != null) {
String baseName = func.name();
String expName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
? mcpProperties.getNamespace() + "_" + baseName : baseName;
availableFunctions.add(expName + " (in " + targetCls.getSimpleName() + ")");
}
}
}
log.error("[Tool] 실행할 함수(Method)를 찾을 수 없습니다: {}. 현재 스캔된 툴 메서드 목록: {}", functionName, availableFunctions);
Map<String, Object> error = new HashMap<>();
error.put("jsonrpc", "2.0");
error.put("error", Map.of("code", -32601, "message", "실행할 함수를 찾을 수 없습니다: " + functionName));
error.put("id", payload != null ? payload.get("id") : null);
return error;
}
// --- 비공개 툴(register=false)은 외부 노출(Redis)만 제외하고, 직접 실행은 허용하도록 변경 ---
// (기존 차단 로직 제거됨)
// 2. 파라미터 유효성 검증 (JSON Schema)

View File

@@ -68,11 +68,11 @@ public class ToolRegistryHeartbeatSender {
private void scanAndBuildMetadata() {
Map<String, Object> toolBeans = applicationContext.getBeansWithAnnotation(McpTool.class);
for (Object bean : toolBeans.values()) {
Class<?> userClass = ClassUtils.getUserClass(bean);
McpTool toolAnnotation = userClass.getAnnotation(McpTool.class);
Class<?> targetClass = org.springframework.aop.support.AopUtils.getTargetClass(bean);
McpTool toolAnnotation = org.springframework.core.annotation.AnnotationUtils.findAnnotation(targetClass, McpTool.class);
for (Method method : userClass.getDeclaredMethods()) {
McpFunction functionAnnotation = method.getAnnotation(McpFunction.class);
for (Method method : targetClass.getDeclaredMethods()) {
McpFunction functionAnnotation = org.springframework.core.annotation.AnnotationUtils.findAnnotation(method, McpFunction.class);
if (functionAnnotation != null) {
String baseName = functionAnnotation.name();
String finalName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()