08-08 Spring AI MCP 调用实现原理
✅Spring AI MCP 调用实现原理
通过上一节,我们可以看到 chatclient 调用 mcp server 这个过程非常简洁,是不是很好奇是如何做到的,我们接下来看下源码。
如何注入mcp?
我们先从init方法的getToolCallbacks进入,看下做了什么工作:
1 |
|

发现他这边确实是对McpSyncClient做了一些转换处理,包装成了 SyncMcpToolCallback,这个东西说白了就是将概念的工具转换成了具有可执行能力的工具,SyncMcpToolCallback 具备了工具的定义、元数据、执行逻辑。
我们进入 SyncMcpToolCallback 看下他是如何去执行调度的。下面是这个类的调用方法,我加上了中文注释,核心就是接收大模型发过来的工具参数,最终还是调用mcpClient.callTool方法。
1 |
|
在Spring AI 1.1.0-M4版本中,这部分的逻辑变化较大:
SyncMcpToolCallbackProvider 首先去看他的builder方法的调用方。

他的初始化其实是在McpToolCallbackAutoConfiguration之中的。也就是当Spring.ai.mcp.client.type=SYNC的时候触发初始化。

在这里我们构建了SyncMcpToolCallbackProvider,但是他入参中的McpSyncClient又是从何而来呢?他其实同样也有一个autoconfiguration。核心就在于这个NamedClientMcpTransport。

NamedClientMcpTransport是一个record,但是从他的调用类,我们可以看到,包含了stdio,sse,以及streamable。

我们选择进入Stdio的自动配置类,可以看到他其实就是读取的配置就的properties,来构建我们的transport通道的。sse和streamable也是同理。

我们了解了mcptransport是如何构建了之后,这时候我们再回到McpClientAutoConfiguration
在这段代码里,他还对mcpclient进行了初始化操作,并加入到了List

而在 Spring AI 1.1.0之前的版本,他是通过event机制来触发初始化的,这边也为大家介绍一下:

我们可以看到他实现了一个事件listener:McpToolsChangedEvent

而这个事件又是在 McpSyncToolsChangeEventEmmiter 中发布的,我们继续跟进:

可以看到这个类又是从 McpClientAutoConfiguration 中new出来的。

结果很显然了,就是自动注入,设置了 spring.ai.mcp.client 的 type = SYNC(异步就是ASYNC)就可以触发自动注入的逻辑。
如何调用mcp server?
理解了 Spring AI 是如何将mcp client和mcp server进行绑定的,我们再来看下 chatclient 是如何调用mcp server的。我们从call方法进入。

跟进到他的默认实现类 DefaultChatClient :

发现 call 方法中有一个 buildAdvisorChain 方法,他的含义就是将你在chatclient中设置的advisor构建成一个链,而ChatModelCallAdvisor和ChatModelStreamAdvisor则位于这个链的末端,也就是最后执行调用大模型。

我们为了演示方便,没有设置任何 advisor,我们以非流式为例,进入 ChatModelCallAdvisor。

继续跟进到 OpenAiChatModel,这也是我们之前设置的model类型:

进入 internalCall 方法,我把这块代码贴出来,并增加了中文注释,方便你看懂:
1 | public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatResponse) { |
整体流程就是,先调用一次大模型,然后大模型会返回结果,根据模型返回结果判断是否含有 tool_calls 字段,如果有则表明需要调用工具,接着判断工具的参数 returnDirect,是否是仅仅调用工具不需要总结,还是需要获取到工具执行结果后,再次总结输出。这个地方做了递归处理,直到模型不再输出tool_calls,也就是不需要再调用工具,则判断停止。
conversationHistory 则是拼接的上下文信息,包括用户的原始问题,大模型返回的 tool_calls,工具返回的结果,将这些信息一起拼接成新的 prompt 传递给下次模型调用。

其中 tool_calls 判断逻辑如下:


我们再回到 DefaultToolCallingManager 的 executeToolCalls 方法中,进入到 executeToolCall 方法,这就是他具体执行工具地方:

这边直接展示他的核心代码,就是这个 toolCallback.call 方法,这个 toolCallback 是不是很眼熟,我们在之前的注入中,是不是就是注入的这个 toolCallback。



到这里,我们的注入和调用流程是不是都串联起来了,又回到了 SyncMcpToolCallback 的 call 方法了。
原来 chatclient 就是利用默认的 ChatModelAdvisor 来执行工具调用的,先调用大模型,获取结果,判断结果是否包含 tool_calls 字段,如果有,就调用注入的 SyncMcpToolCallback 来 call 相应的工具,最终根据你的参数,判断是直接 returnDirect 还是再次抛给大模型总结输出。
