feat: Auto-prefix container namespaces to tool names to prevent conflicts
This commit is contained in:
@@ -11,6 +11,7 @@ import java.util.Map;
|
|||||||
@ConfigurationProperties(prefix = "mcp")
|
@ConfigurationProperties(prefix = "mcp")
|
||||||
public class McpProperties {
|
public class McpProperties {
|
||||||
|
|
||||||
|
private String namespace;
|
||||||
private Map<String, FunctionProp> functions;
|
private Map<String, FunctionProp> functions;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|||||||
@@ -64,11 +64,18 @@ public class BusinessToolController {
|
|||||||
for (Object bean : toolBeans.values()) {
|
for (Object bean : toolBeans.values()) {
|
||||||
for (Method method : bean.getClass().getDeclaredMethods()) {
|
for (Method method : bean.getClass().getDeclaredMethods()) {
|
||||||
McpFunction mcpFunc = method.getAnnotation(McpFunction.class);
|
McpFunction mcpFunc = method.getAnnotation(McpFunction.class);
|
||||||
if (mcpFunc != null && mcpFunc.name().equals(functionName)) {
|
if (mcpFunc != null) {
|
||||||
targetBean = bean;
|
String baseName = mcpFunc.name();
|
||||||
targetMethod = method;
|
String expectedName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
|
||||||
targetFunctionAnnotation = mcpFunc;
|
? mcpProperties.getNamespace() + "_" + baseName
|
||||||
break outerLoop;
|
: baseName;
|
||||||
|
|
||||||
|
if (expectedName.equals(functionName)) {
|
||||||
|
targetBean = bean;
|
||||||
|
targetMethod = method;
|
||||||
|
targetFunctionAnnotation = mcpFunc;
|
||||||
|
break outerLoop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,19 +58,24 @@ public class ToolRegistryHeartbeatSender {
|
|||||||
for (Method method : bean.getClass().getDeclaredMethods()) {
|
for (Method method : bean.getClass().getDeclaredMethods()) {
|
||||||
McpFunction functionAnnotation = method.getAnnotation(McpFunction.class);
|
McpFunction functionAnnotation = method.getAnnotation(McpFunction.class);
|
||||||
if (functionAnnotation != null) {
|
if (functionAnnotation != null) {
|
||||||
|
String baseName = functionAnnotation.name();
|
||||||
|
String finalName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty()
|
||||||
|
? mcpProperties.getNamespace() + "_" + baseName
|
||||||
|
: baseName;
|
||||||
|
|
||||||
McpProperties.FunctionProp prop = null;
|
McpProperties.FunctionProp prop = null;
|
||||||
if (mcpProperties.getFunctions() != null) {
|
if (mcpProperties.getFunctions() != null) {
|
||||||
prop = mcpProperties.getFunctions().get(functionAnnotation.name());
|
prop = mcpProperties.getFunctions().get(baseName);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isRegister = prop != null && prop.getRegister() != null ? prop.getRegister() : functionAnnotation.register();
|
boolean isRegister = prop != null && prop.getRegister() != null ? prop.getRegister() : functionAnnotation.register();
|
||||||
if (!isRegister) {
|
if (!isRegister) {
|
||||||
log.info(" [HeartbeatSender] '{}' 툴은 설정에 의해 외부 등록(Redis) 대상에서 제외되었습니다.", functionAnnotation.name());
|
log.info(" [HeartbeatSender] '{}' 툴은 설정에 의해 외부 등록(Redis) 대상에서 제외되었습니다. (최종 이름: {})", baseName, finalName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolMetadata meta = new ToolMetadata();
|
ToolMetadata meta = new ToolMetadata();
|
||||||
meta.setToolName(functionAnnotation.name());
|
meta.setToolName(finalName);
|
||||||
meta.setDescription(prop != null && prop.getDescription() != null ? prop.getDescription() : functionAnnotation.description());
|
meta.setDescription(prop != null && prop.getDescription() != null ? prop.getDescription() : functionAnnotation.description());
|
||||||
meta.setDomainGroup(toolAnnotation.group());
|
meta.setDomainGroup(toolAnnotation.group());
|
||||||
meta.setIntegrationType(toolAnnotation.routingType());
|
meta.setIntegrationType(toolAnnotation.routingType());
|
||||||
@@ -82,7 +87,7 @@ public class ToolRegistryHeartbeatSender {
|
|||||||
|
|
||||||
Map<String, String> prompts = new HashMap<>();
|
Map<String, String> prompts = new HashMap<>();
|
||||||
String promptText = prop != null && prop.getPrompt() != null ? prop.getPrompt() : functionAnnotation.prompt();
|
String promptText = prop != null && prop.getPrompt() != null ? prop.getPrompt() : functionAnnotation.prompt();
|
||||||
prompts.put(functionAnnotation.name(), promptText);
|
prompts.put(finalName, promptText);
|
||||||
meta.setActionPrompts(prompts);
|
meta.setActionPrompts(prompts);
|
||||||
|
|
||||||
if (method.getParameterCount() > 0) {
|
if (method.getParameterCount() > 0) {
|
||||||
@@ -94,7 +99,7 @@ public class ToolRegistryHeartbeatSender {
|
|||||||
finalSchema.put("properties", schema);
|
finalSchema.put("properties", schema);
|
||||||
meta.setParametersSchema(finalSchema);
|
meta.setParametersSchema(finalSchema);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to generate schema for {}", functionAnnotation.name(), e);
|
log.error("Failed to generate schema for {}", finalName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ spring.profiles.active=local
|
|||||||
|
|
||||||
# Suppress Kafka Connection Logs
|
# Suppress Kafka Connection Logs
|
||||||
logging.level.org.apache.kafka=ERROR
|
logging.level.org.apache.kafka=ERROR
|
||||||
|
|
||||||
|
# Auto Prefix Namespace
|
||||||
|
mcp.namespace=email
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ spring.profiles.active=local
|
|||||||
|
|
||||||
# Suppress Kafka Connection Logs
|
# Suppress Kafka Connection Logs
|
||||||
logging.level.org.apache.kafka=ERROR
|
logging.level.org.apache.kafka=ERROR
|
||||||
|
|
||||||
|
# Auto Prefix Namespace
|
||||||
|
mcp.namespace=other
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ spring.profiles.active=local
|
|||||||
|
|
||||||
# Suppress Kafka Connection Logs
|
# Suppress Kafka Connection Logs
|
||||||
logging.level.org.apache.kafka=ERROR
|
logging.level.org.apache.kafka=ERROR
|
||||||
|
|
||||||
|
# Auto Prefix Namespace
|
||||||
|
mcp.namespace=sms
|
||||||
|
|||||||
Reference in New Issue
Block a user