手搓MCP客户端动态调用多MCP服务,调用哪个你说了算!

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

前两天,有个粉丝朋友咨询MCP服务如何动态调用,动态加载MCP服务的链接?我们都知道MCP客户端可以配置多个MCP服务的地址:

properties 复制代码
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:xxxx
spring.ai.mcp.client.sse.connections.server2.url=http://localhost:xxxx
spring.ai.mcp.client.sse.connections.server3.url=http://localhost:xxxx

这样的MCP服务会自动加载到项目中,类似合成一个服务。一个请求进来,MCP客户端会在所有的服务中寻找合适的方法。

但是,业务中总会有各种不同的场景。例如,粉丝朋友的需求。SpringBoot一直坚持约定大于配置的方式,Spring AI也不例外,既然提供配置文件的配置,那么JavaBean的配置一样可行。指定调用只要需要找到调用的时机,自然可以控制。

本篇就带大家一起解决这份粉丝朋友的问题。

02 MCP动态调用

2.1 代码追踪

在研究动态调用之前,我们需要知道配置用在了哪里?

我们直接在.properties.yml配置文件中,

按住Ctrl,然后用鼠标点击url,就会跳转到下面的文件:

从方法中可以看到:

最终的配置会保存在SseParameters中,最后放到Map集合中。使用或者获取服务链接的时候,肯定也会通过getConnections()来获取,这个也是突破问题的关键。

2.2 探寻使用位置

上面找到了配置文件对用的Java类:

org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties

我们可以采用上面相同的方法,看看哪里调用了getConnections(),由于我们的源码没有全部下载下来,所以很大程度上可能找不到。所以,断点提示就变成了我们唯一的出路。

断点启动之后,我们看到连接数为0,也就是还没有加载MCP服务的链接呢。如下图:

我们断点继续向下执行(快捷键:F8),中间会经历漫长的Bean的创建过程。我们的目标就是看看是哪一个Bean实例化的时候会获取连接。

我们就需要注意几个注解:@Bean@Component等,终于实例化出来了,找到了关键类:

org.springframework.ai.mcp.client.autoconfigure.McpClientAutoConfiguration

具体如图:

我们可以看到namedTransports里面就包含了我们MCP服务,最终放到mcpSyncClients集合中,如下图:

至此,Spring 启动随后完成。

2.3 修改点

我们可以看到mcpSyncClients集合上有@Bean标签,所以会被Spring管理,所以可以直接从Spring容器中,通过注入的方式获取到。

但是修改点有在哪里呢?我们先看看MCP调用的案例:

从构建ChatClient到发送请求参数,就只有ChatModelToolCallbackProvider两个参数。而ChatMode表示采用的大模型的类型,显然和MCP服务没有关系,就只有ToolCallbackProvider这个类了。

org.springframework.ai.tool.ToolCallbackProvider的实现有四个:

通过注入的ToolCallbackProvider,我们断点可以看到它的实现是:

org.springframework.ai.mcp.SyncMcpToolCallbackProvider

从代码的构造启可以看到,参数就是我们上面注入的mcpSyncClients

2.4 动态调用

我们只要根据MCP服务的名称区分不同的McpSyncClient即可。

我们从截图可以看到:

小编提供了两个MCP服务,分别是csdn-mcp-server(CSDN文章浏览服务)和gzh-mcp-server(种种好推荐服务)。小编没有传递参数,默认会调用gzh-mcp-server

我们看看返回的结果:

请求是一个与csdn-mcp-server匹配度比较高的请求,最终只调用了gzh-mcp-server。所以我们就可以控制调用的MCP服务了。

03 动态加载

动态调用的问题,我们已经解决了。如果这些服务链接想从数据库读取,又该如何处理呢?

最直接的想法,就是直接将配置文件对应的Bean自己new出来交给Spring管理。

3.1 配置McpSseClientProperties

Bean配置

java 复制代码
@Bean
public McpSseClientProperties mcpSseClientProperties() {
    McpSseClientProperties mcpSseClientProperties = new McpSseClientProperties();
    Map<String, SseParameters> connections = mcpSseClientProperties.getConnections();
    connections.put("server1", new SseParameters("http://localhost:8080", null));
    connections.put("server2", new SseParameters("http://localhost:8082", null));
    return mcpSseClientProperties;
}

启动结果

McpSseClientProperties发现了2个,实际只需要一个。也就是说,框架自动实例化McpSseClientProperties和我们的冲突了。

这种方式行不通!!!

既然已经存在,那就直接添加连接呢?因为Spring默认是单例。

3.2 动态加载链接

容器启动后加载链接

测试

已经加载成功了。

04 小结

到这里粉丝朋友的问题就全部解决了。同时,也分享给大家,希望对大家有帮助!

相关推荐
源码技术栈3 分钟前
智慧工地微服务架构+Java+Spring Cloud +Uni-App +MySql开发,在微信公众号、小程序、H5、移动端
java·ai·saas·智慧工地·智慧工地项目·可视化大屏·智慧工地系统
老华带你飞7 分钟前
健身房预约|基于springboot 健身房预约小程序系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·小程序
paopaokaka_luck7 分钟前
基于SpringBoot+Uniapp的自习室预约小程序(腾讯地图API、Echarts图形化分析、二维码识别)
vue.js·spring boot·后端·spring·echarts
树洞RoBot8 分钟前
Spring框架深度解析:从核心原理到企业级实战
后端
..过云雨9 分钟前
15-2.【Linux系统编程】进程信号 - 信号保存(信号处理流程的三种状态:未决、阻塞、递达,信号保存由未决表完成、sigset_t信号集类型及相关函数)
linux·c++·后端·信号处理
帅得不敢出门10 分钟前
MTK Android11 APP调用OTA升级
android·java·开发语言·framework
李拾叁的摸鱼日常14 分钟前
ThreadLocal 内存泄漏深度解析:原因、避坑指南与业务最佳实践
java·面试
Kiri霧15 分钟前
Go Defer语句详解
java·服务器·golang
Q_Q51100828516 分钟前
基于Java的加油站销售积分管理系统的设计与实
java·开发语言
亿.618 分钟前
2025鹏城杯 Web
java·安全·web·ctf·鹏城杯