You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
6.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package xyz.wbsite.mcp.server.registrar;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import xyz.wbsite.mcp.server.annotation.P;
import xyz.wbsite.mcp.server.annotation.Tool;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* MCP服务器注册器
*
* @author wangbing
*/
@Component
public class McpServerRegistrar {
@Resource
private ApplicationContext applicationContext;
@Resource
private WebFluxSseServerTransportProvider transport;
@PostConstruct
public void registerMcpServers() {
// 创建具有自定义配置的服务器
McpSyncServer syncServer = McpServer.sync(transport)
.serverInfo("Mcp Server", "1.0.0")
.capabilities(McpSchema.ServerCapabilities.builder()
.resources(true, true) // 启用资源支持
.tools(true) // 启用工具支持
.prompts(true) // 启用提示支持
.logging() // 启用日志支持
.build())
.build();
// 查找方法级别的Tool注解
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanNames) {
// 跳过自身 Bean避免循环依赖
if (beanName.equals("mcpServerRegistrar")) {
continue;
}
Object bean = applicationContext.getBean(beanName);
for (Method method : bean.getClass().getMethods()) {
if (method.isAnnotationPresent(Tool.class)) {
Tool tool = method.getAnnotation(Tool.class);
// 处理参数
ObjectNode schema = new ObjectNode(JsonNodeFactory.instance);
schema.put("type", "object");
ArrayNode required = schema.putArray("required");
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
this.putProperties(schema, parameter);
required.add(parameter.getName());
}
McpServerFeatures.SyncToolSpecification syncToolSpecification = buildSyncToolSpecification(bean, method, tool.value(), schema.toPrettyString());
syncServer.addTool(syncToolSpecification);
}
}
}
// 发送日志通知
syncServer.loggingNotification(McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.INFO)
.logger("Mcp Server Log")
.data("The server has been initialized")
.build());
}
private void putProperties(ObjectNode schema, Parameter parameter) {
// 处理参数
String typeName = jsonTypeMapper(parameter);
ObjectNode properties = (ObjectNode) schema.get("properties");
if (properties == null) {
properties = schema.putObject("properties");
}
ObjectNode paramSchema = properties.putObject(parameter.getName());
paramSchema.put("type", typeName);
if (parameter.isAnnotationPresent(P.class)) {
P p = parameter.getAnnotation(P.class);
paramSchema.put("description", p.value());
}
}
private String jsonTypeMapper(Parameter parameter) {
Class<?> paramType = parameter.getType();
Type genericType = parameter.getParameterizedType();
// 基础类型判断
if (paramType.isPrimitive()) {
if (paramType == boolean.class) {
return "boolean";
} else if (paramType == char.class) {
return "string";
} else {
return "number";
}
} else if (Number.class.isAssignableFrom(paramType)) {
return "number";
} else if (paramType == String.class) {
return "string";
} else if (paramType == Boolean.class) {
return "boolean";
}
// 复合类型判断
if (paramType.isArray() || Collection.class.isAssignableFrom(paramType)) {
return "array";
} else if (Map.class.isAssignableFrom(paramType)) {
return "object";
}
// 泛型类型处理
if (genericType instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) genericType).getRawType();
if (rawType == List.class || rawType == Set.class) {
return "array";
}
}
// 默认视为对象
return "object";
}
private McpServerFeatures.SyncToolSpecification buildSyncToolSpecification(Object instance, Method method, String toolDesc, String schema) {
// 定义Tool
McpSchema.Tool tool = new McpSchema.Tool(
method.getName(),
toolDesc,
schema
);
// 定义同步工具
return new McpServerFeatures.SyncToolSpecification(
tool,
(exchange, arguments) -> {
//执行函数
try {
Parameter[] parameters = method.getParameters();
Object[] argArr = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
argArr[i] = arguments.get(parameters[i].getName());
}
Object obj = method.invoke(instance, argArr);
String str = obj == null ? "" : JSONUtil.toJsonStr(obj);
return new McpSchema.CallToolResult(Collections.singletonList(new McpSchema.TextContent(str)), false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
);
}
}

Powered by TurnKey Linux.