refs #743: validate advertised ADB MCP tools at startup
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package com.cloudhandson.vpdbackoffice.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Fails startup when an advertised ADB Agent Tool cannot actually be called. */
|
||||
@Component
|
||||
public class McpAgentToolStartupValidator implements SmartInitializingSingleton {
|
||||
|
||||
static final String TOOL_METADATA_SQL = """
|
||||
SELECT TOOL_NAME, STATUS
|
||||
FROM USER_AI_AGENT_TOOLS
|
||||
""";
|
||||
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(McpAgentToolStartupValidator.class);
|
||||
|
||||
private final McpToolCatalog toolCatalog;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public McpAgentToolStartupValidator(
|
||||
McpToolCatalog toolCatalog,
|
||||
JdbcTemplate jdbcTemplate
|
||||
) {
|
||||
this.toolCatalog = toolCatalog;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
validateAdvertisedAgentTools();
|
||||
}
|
||||
|
||||
void validateAdvertisedAgentTools() {
|
||||
Set<String> requiredTargets = new TreeSet<>();
|
||||
toolCatalog.tools().stream()
|
||||
.filter(McpToolDefinition::agentTool)
|
||||
.map(McpToolDefinition::targetName)
|
||||
.map(value -> value.toUpperCase(Locale.ROOT))
|
||||
.forEach(requiredTargets::add);
|
||||
|
||||
if (requiredTargets.isEmpty()) {
|
||||
log.info("MCP startup validation skipped: no ADB Agent Tool is advertised");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> registered = loadRegisteredTools();
|
||||
List<String> missing = new ArrayList<>();
|
||||
List<String> notEnabled = new ArrayList<>();
|
||||
for (String requiredTarget : requiredTargets) {
|
||||
if (!registered.containsKey(requiredTarget)) {
|
||||
missing.add(requiredTarget);
|
||||
continue;
|
||||
}
|
||||
String status = registered.get(requiredTarget);
|
||||
if (!"ENABLED".equals(status)) {
|
||||
notEnabled.add(requiredTarget + "=" + status);
|
||||
}
|
||||
}
|
||||
|
||||
if (!missing.isEmpty() || !notEnabled.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Advertised MCP Agent Tool validation failed: missing=" + missing
|
||||
+ ", notEnabled=" + notEnabled);
|
||||
}
|
||||
|
||||
log.info("Validated {} advertised ADB Agent Tool(s): {}",
|
||||
requiredTargets.size(), requiredTargets);
|
||||
}
|
||||
|
||||
private Map<String, String> loadRegisteredTools() {
|
||||
try {
|
||||
Map<String, String> registered = new TreeMap<>();
|
||||
for (Map<String, Object> row : jdbcTemplate.queryForList(TOOL_METADATA_SQL)) {
|
||||
String toolName = normalized(row, "TOOL_NAME");
|
||||
if (!toolName.isBlank()) {
|
||||
String status = normalized(row, "STATUS");
|
||||
registered.put(toolName, status.isBlank() ? "<NULL>" : status);
|
||||
}
|
||||
}
|
||||
return registered;
|
||||
} catch (DataAccessException exception) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to validate advertised MCP Agent Tools in USER_AI_AGENT_TOOLS",
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
private String normalized(Map<String, Object> row, String column) {
|
||||
Object value = row.get(column);
|
||||
if (value == null) {
|
||||
value = row.entrySet().stream()
|
||||
.filter(entry -> column.equalsIgnoreCase(entry.getKey()))
|
||||
.map(Map.Entry::getValue)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
return value == null ? "" : value.toString().trim().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user