springboot2.x基于Solon搭建MCP服务,附带源码和jar包
基于springboot框架搭建mcp服务,使用solon集成springboot快速集成mcp
看了一下网上搭建MCP服务基于springboot2.x版本的,找了几个发现写的都有遗漏,这里记录一下自己的搭建过程
流程:
1.基于solon框架集成springboot2.x版本,通过springboot启动solon服务
2.基于solon服务实现MCP的搭建访问
3.测试MCP服务访问,这里测试chatbox逻辑是通用的
注:
1.为什么基于solon搭建,springboot2.x不支持官方的ai,如果新项目可以直接使用solon开发;或者使用springboot3.x版本,新项目可以直接参考web2mcp方式
2.源码基于官方源码修改,添加个人注释,可以参考注释,整体比较简单的
3.mcp为对应源码的mcpserver包下内容
4.这里实现的是基于不影响之前开发的项目,可以通过远程调用方式访问
- springboot添加maven添加依赖
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-ai-mcp</artifactId>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-ai</artifactId>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-lib</artifactId>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web-servlet</artifactId>
</dependency>
- 修改启动类,添加必要的几个类
启动类HelloApp添加启动solon
@RestController
@SpringBootApplication
public class HelloApp {
public static void main(String[] args) {
if(Solon.app() != null) {
return;
}
SpringApplication.run(HelloApp.class, args);
}
}
配置类McpServerConfig复制即可
@Configuration
public class McpServerConfig {
@Value("${server.servlet.context-path:}")
private String contextPath;
@Autowired
private List<IMcpServerEndpoint> serverEndpoints;
@PostConstruct
public void start() {
/**
* Spring 注解支持
* */
ToolSchemaUtil.addBodyDetector(e -> e.isAnnotationPresent(RequestBody.class));
ToolSchemaUtil.addParamResolver((e,t)->{
RequestParam p1Anno = e.getAnnotation(RequestParam.class);
if (p1Anno != null) { //这个注解因为没有描述字段,所以变量名一定要很语义
Parameter p1 = (Parameter) e;
String name = Utils.annoAlias(p1Anno.name(), p1.getName());
return new ParamDesc(name, t.getGenericType(), p1Anno.required(), "");
}
return null;
});
System.setProperty("server.contextPath", contextPath);
Solon.start(McpServerConfig.class, new String[]{}, app -> {
app.enableScanning(false);
app.filter(new McpServerAuth());
});
//实现了IMcpServerEndpoint接口,会自动注入到solon容器中
springCom2Endpoint();
}
@PreDestroy
public void stop() {
if (Solon.app() != null) {
//停止 solon(根据配置,可支持两段式安全停止)
Solon.stopBlock(false, Solon.cfg().stopDelay());
}
}
//Spring 组件转为端点
protected void springCom2Endpoint() {
//提取实现容器里 IMcpServerEndpoint 接口的 bean ,并注册为服务端点
for (IMcpServerEndpoint serverEndpoint : serverEndpoints) {
Class<?> serverEndpointClz = AopUtils.getTargetClass(serverEndpoint);
McpServerEndpoint anno = AnnotationUtils.findAnnotation(serverEndpointClz, McpServerEndpoint.class);
if (anno == null) {
continue;
}
McpServerEndpointProvider serverEndpointProvider = McpServerEndpointProvider.builder()
.from(serverEndpointClz, anno)
.build();
serverEndpointProvider.addTool(new MethodToolProvider(serverEndpointClz, serverEndpoint));
serverEndpointProvider.addResource(new MethodResourceProvider(serverEndpointClz, serverEndpoint));
serverEndpointProvider.addPrompt(new MethodPromptProvider(serverEndpointClz, serverEndpoint));
serverEndpointProvider.postStart();
}
}
//其他app可以通过http访问到部署到mcp服务
@Bean
public FilterRegistrationBean mcpServerFilter() {
//通过 Servlet Filter 实现 http 能力对接
FilterRegistrationBean<SolonServletFilter> filter = new FilterRegistrationBean<>();
filter.setName("SolonFilter");
filter.addUrlPatterns("/mcp/*");
filter.setFilter(new SolonServletFilter());
return filter;
}
}
认证类McpServerAuth复制即可,有需要添加认证的可以添加
public class McpServerAuth implements Filter {
@Override
public void doFilter(Context ctx, FilterChain chain) throws Throwable {
//如何鉴权“按需”设定(path 的过滤,与端点路径对应起来)
if (ctx.pathNew().startsWith("/mcp/")
&& ctx.pathNew().endsWith("/message") == false) { //message 端点不需要签权
String authStr = ctx.param("user"); //或者 ctx.header(...)
if ("no".equals(authStr)) { //模拟 401 效果
ctx.status(401);
ctx.setHandled(true);
return;
}
//业务检测
}
chain.doFilter(ctx);
}
}
访问路径类ToolController需要实现IMcpServerEndpoint,在配置类中会注入到solon中,添加访问路径
@Service
@McpServerEndpoint(channel = McpChannel.STREAMABLE, mcpEndpoint = "/mcp/sse/demo1")
public class ToolController implements IMcpServerEndpoint {
@ToolMapping(description = "查询天气预报, 返回地点和度数")
public String getWeather(@Param(description = "城市位置") String location) {
//todo 这里可以通过注入 使用远程调用的方式访问服务,直接返回object对象即可
return "晴,14度";
}
}
配置文件可以指定端口号 这里指定的是
server:
servlet:
encoding:
force: true
charset: UTF-8
enabled: true
port: 3011
- 启动项目,连接测试
项目启动后,可以通过http://localhost:3011//mcp/sse/demo1连接到部署到mcp服务器
启用mcp,测试提示词访问是否能直接调用即可
4.注意事项
1.导入到包需要查看是否是solon的,别导入错了
import org.noear.solon.ai.annotation.PromptMapping;
import org.noear.solon.ai.annotation.ResourceMapping;
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.ai.mcp.McpChannel;
import org.noear.solon.ai.mcp.server.IMcpServerEndpoint;
import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
import org.noear.solon.annotation.Param;
import org.springframework.stereotype.Service;
2.开发阶段会多次重启mcp服务器,重启后在chatbox中mcp需要关闭再打开,重新建立连接,否则根据原有的会话id请求不通,后续可以采取缓存机制