forked from kimhyungsik/ax_hub_mcp_tool
refactor: apply new Naming standard (uid, categoryKey, subToolName) across modules
This commit is contained in:
@@ -6,7 +6,8 @@ import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface McpFunction {
|
||||
String name();
|
||||
String name(); // 사람이 읽는 라벨 (예: "고객 조회 툴")
|
||||
String subToolName(); // MCP 서브툴 명칭 (예: "customer_search")
|
||||
String description();
|
||||
String prompt() default "";
|
||||
String mappingId() default "";
|
||||
|
||||
@@ -13,6 +13,6 @@ public @interface McpTool {
|
||||
@AliasFor(annotation = Component.class)
|
||||
String value() default "";
|
||||
|
||||
String group() default "COMMON";
|
||||
String categoryKey() default "common";
|
||||
String routingType() default "HTTP";
|
||||
}
|
||||
|
||||
@@ -28,11 +28,21 @@ import java.util.Map;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ToolMetadata {
|
||||
private String toolName;
|
||||
private String description;
|
||||
// 1. Tool 기본 정보
|
||||
private String uid; // UUID 형식의 고유 식별자
|
||||
private String semver; // 버전 (예: 1.0.0)
|
||||
private String name; // 사람이 읽는 라벨 (1-128자)
|
||||
private String subToolName; // MCP 서브툴 명칭 (64자 이하, 예: CustomerSearchTool)
|
||||
private String description; // 툴의 목적 및 설명 (LLM 프롬프트에 활용 가능)
|
||||
|
||||
// 2. 파라미터 스키마 (JSON Schema 형태의 Map)
|
||||
private Map<String, Object> parametersSchema;
|
||||
|
||||
// 2-0. 프론트엔드 UI용 함수별 프롬프트 매핑 (추가됨)
|
||||
private Map<String, String> actionPrompts;
|
||||
private String domainGroup;
|
||||
|
||||
// 2-1. 도메인 부서 그룹명 (category_key, 슬러그 형식)
|
||||
private String categoryKey;
|
||||
private String endpoint;
|
||||
private String podUrl;
|
||||
private String integrationType;
|
||||
|
||||
@@ -81,19 +81,23 @@ public class ToolRegistryHeartbeatSender {
|
||||
McpFunction functionAnnotation = AnnotationUtils.findAnnotation(method, McpFunction.class);
|
||||
if (functionAnnotation != null) {
|
||||
String baseName = functionAnnotation.name();
|
||||
String finalName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
|
||||
? mcpProperties.getNamespace() + "_" + baseName
|
||||
: baseName;
|
||||
String rawSubToolName = functionAnnotation.subToolName();
|
||||
String subToolName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
|
||||
? mcpProperties.getNamespace() + "_" + rawSubToolName
|
||||
: rawSubToolName;
|
||||
|
||||
boolean isRegister = functionAnnotation.register();
|
||||
if (!isRegister) {
|
||||
log.info(" [HeartbeatSender] '{}' 툴은 어노테이션 설정에 의해 외부 등록(Redis) 대상에서 제외되었습니다. (최종 이름: {})", baseName, finalName);
|
||||
log.info(" [HeartbeatSender] '{}' 툴은 어노테이션 설정에 의해 외부 등록(Redis) 대상에서 제외되었습니다. (최종 이름: {})", baseName, subToolName);
|
||||
}
|
||||
|
||||
ToolMetadata meta = new ToolMetadata();
|
||||
meta.setToolName(finalName);
|
||||
meta.setUid(java.util.UUID.nameUUIDFromBytes(subToolName.getBytes()).toString());
|
||||
meta.setName(baseName);
|
||||
meta.setSubToolName(subToolName);
|
||||
meta.setSemver("1.0.0");
|
||||
meta.setDescription(functionAnnotation.description());
|
||||
meta.setDomainGroup(toolAnnotation.group());
|
||||
meta.setCategoryKey(toolAnnotation.categoryKey());
|
||||
meta.setIntegrationType(toolAnnotation.routingType());
|
||||
meta.setMciServiceId(functionAnnotation.mappingId());
|
||||
meta.setPodUrl(podUrl);
|
||||
@@ -105,7 +109,7 @@ public class ToolRegistryHeartbeatSender {
|
||||
|
||||
Map<String, String> prompts = new HashMap<>();
|
||||
String promptText = functionAnnotation.prompt();
|
||||
prompts.put(finalName, promptText);
|
||||
prompts.put(subToolName, promptText);
|
||||
meta.setActionPrompts(prompts);
|
||||
|
||||
if (method.getParameterCount() > 0) {
|
||||
@@ -117,7 +121,7 @@ public class ToolRegistryHeartbeatSender {
|
||||
finalSchema.put("properties", schema);
|
||||
meta.setParametersSchema(finalSchema);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to generate schema for {}", finalName, e);
|
||||
log.error("Failed to generate schema for {}", subToolName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +129,7 @@ public class ToolRegistryHeartbeatSender {
|
||||
registeredTools.add(meta);
|
||||
}
|
||||
allScannedTools.add(meta);
|
||||
log.info(" [HeartbeatSender] 도구 메타데이터 생성: {} (isRegistered: {})", meta.getToolName(), isRegister);
|
||||
log.info(" [HeartbeatSender] 도구 메타데이터 생성: {} (isRegistered: {})", meta.getUid(), isRegister);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,15 +145,15 @@ public class ToolRegistryHeartbeatSender {
|
||||
.uri(gatewayUrl + "/mcp/api/v1/registry/heartbeat")
|
||||
.header("Content-Type", "application/json")
|
||||
.header("X-API-KEY", "SHINHAN_MCP_TEST_KEY_9999")
|
||||
.body(tool.getToolName())
|
||||
.body(tool.getUid())
|
||||
.retrieve()
|
||||
.toEntity(String.class);
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
log.info(" [HeartbeatSender] 하트비트 전송 성공: {}", tool.getToolName());
|
||||
log.info(" [HeartbeatSender] 하트비트 전송 성공: {}", tool.getUid());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(" [HeartbeatSender] 하트비트 전송 실패 ({}): {}. 재등록을 시도합니다.", tool.getToolName(), e.getMessage());
|
||||
log.warn(" [HeartbeatSender] 하트비트 전송 실패 ({}): {}. 재등록을 시도합니다.", tool.getUid(), e.getMessage());
|
||||
registerTool(tool);
|
||||
}
|
||||
}
|
||||
@@ -164,7 +168,7 @@ public class ToolRegistryHeartbeatSender {
|
||||
.body(tool)
|
||||
.retrieve()
|
||||
.toBodilessEntity();
|
||||
log.info(" [HeartbeatSender] 툴 재등록 성공: {}", tool.getToolName());
|
||||
log.info(" [HeartbeatSender] 툴 재등록 성공: {}", tool.getUid());
|
||||
} catch (Exception ex) {
|
||||
log.error(" [HeartbeatSender] 툴 등록 실패: {}", ex.getMessage());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user