文章目录
- [1. 项目概述](#1. 项目概述)
- [2. 环境准备](#2. 环境准备)
-
- [2.1 前置条件](#2.1 前置条件)
- [2.2 设置环境变量(可选)](#2.2 设置环境变量(可选))
- [3. 创建项目](#3. 创建项目)
-
- [3.1 目录结构](#3.1 目录结构)
- [4. pom.xml ------ 依赖管理](#4. pom.xml —— 依赖管理)
-
- [4.1 完整配置](#4.1 完整配置)
- [4.2 依赖关系图](#4.2 依赖关系图)
- [5. application.yml ------ 应用配置](#5. application.yml —— 应用配置)
-
- [5.1 关键配置项说明](#5.1 关键配置项说明)
- [5.2 MCP 工具自动发现流程](#5.2 MCP 工具自动发现流程)
- [6. 核心代码实现](#6. 核心代码实现)
-
- [6.1 启动类](#6.1 启动类)
- [6.2 McpClientToolCallbackProvider ------ MCP 工具提供器](#6.2 McpClientToolCallbackProvider —— MCP 工具提供器)
- [6.3 McpNode ------ MCP 图节点](#6.3 McpNode —— MCP 图节点)
- [6.4 GraphMcpConfig ------ StateGraph 配置](#6.4 GraphMcpConfig —— StateGraph 配置)
-
- [6.4.1 状态键策略](#6.4.1 状态键策略)
- [6.4.2 图结构](#6.4.2 图结构)
- [6.5 ReactAgentMcpConfig ------ ReactAgent 配置](#6.5 ReactAgentMcpConfig —— ReactAgent 配置)
-
- [6.5.1 ReactAgent 执行流程(ReAct 循环)](#6.5.1 ReactAgent 执行流程(ReAct 循环))
- [6.5.2 MemorySaver 的作用](#6.5.2 MemorySaver 的作用)
- [6.6 Controller 层](#6.6 Controller 层)
-
- [6.6.1 GraphMcpController](#6.6.1 GraphMcpController)
- [6.6.2 AgentMcpController](#6.6.2 AgentMcpController)
- [7. 两种 MCP 集成模式对比](#7. 两种 MCP 集成模式对比)
- [8. 编译与运行](#8. 编译与运行)
-
- [8.1 编译](#8.1 编译)
- [8.2 打包](#8.2 打包)
- [8.3 启动](#8.3 启动)
- [8.4 调用接口](#8.4 调用接口)
-
- [8.4.1 Graph 模式](#8.4.1 Graph 模式)
- [8.4.2 ReactAgent 模式](#8.4.2 ReactAgent 模式)
- [9. 扩展:多 MCP Server 按节点分配](#9. 扩展:多 MCP Server 按节点分配)
1. 项目概述
本项目演示两种在 Spring AI Alibaba 中集成 MCP(Model Context Protocol) 工具的方式:
| 模式 | 说明 | 端点 |
|---|---|---|
| StateGraph + MCP 节点 | 在 StateGraph 工作流节点中,通过 ChatClient 注入 MCP 工具 | /graph/mcp/call |
| ReactAgent + MCP 工具 | 在 ReactAgent 推理循环中,直接注册 MCP 工具参与 ReAct 过程 | /agent/mcp/call |
MCP Server 对接的是高德地图 (https://mcp.amap.com),提供地理编码、天气查询、路线规划等工具。
技术栈:
| 组件 | 版本 |
|---|---|
| Spring Boot | 3.5.11 |
| Spring AI BOM | 1.1.4 |
| Spring AI Alibaba BOM | 1.1.2.2 |
| JDK | 17 |
| 模型 | DashScope qwen-plus |
| MCP 传输 | streamable-http(SYNC) |
2. 环境准备
2.1 前置条件
2.2 设置环境变量(可选)
bash
# 不设置则使用 application.yml 中内置的默认值
export DASHSCOPE_API_KEY=sk-your-dashscope-key
export AMAP_API_KEY=your-amap-key
3. 创建项目
3.1 目录结构
spring-ai-alibaba-mcp-demo/
├── pom.xml
├── src/main/java/com/example/mcpdemo/
│ ├── McpDemoApplication.java # Spring Boot 启动类
│ ├── config/
│ │ ├── GraphMcpConfig.java # StateGraph + MCP 节点配置
│ │ └── ReactAgentMcpConfig.java # ReactAgent + MCP 工具配置
│ ├── graph/
│ │ ├── McpNode.java # MCP 图节点(NodeAction 实现)
│ │ └── McpClientToolCallbackProvider.java # MCP 工具回调提供器
│ └── controller/
│ ├── GraphMcpController.java # Graph 模式 REST 端点
│ └── AgentMcpController.java # ReactAgent 模式 REST 端点
└── src/main/resources/
└── application.yml # 应用配置
4. pom.xml ------ 依赖管理
4.1 完整配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.11</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-alibaba-mcp-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.1.4</spring-ai.version>
<spring-ai-alibaba.version>1.1.2.2</spring-ai-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring AI BOM:管理 ChatClient、MCP Client 等版本 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring AI Alibaba BOM:管理 Graph、Agent 等版本 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI MCP Client -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<!-- Spring AI Alibaba Graph Core(StateGraph 工作流引擎) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Spring AI Alibaba Agent Framework(ReactAgent) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- DashScope 模型 Starter(通义千问) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.2 依赖关系图
spring-boot-starter-parent (3.5.11)
│
├── spring-ai-bom (1.1.4) ───────── spring-ai-starter-mcp-client
│ └── MCP 客户端自动配置、ToolCallbackProvider
│
└── spring-ai-alibaba-bom (1.1.2.2)
├── spring-ai-alibaba-graph-core
│ └── StateGraph、CompiledGraph、KeyStrategy
├── spring-ai-alibaba-agent-framework
│ └── ReactAgent、MemorySaver
└── spring-ai-alibaba-starter-dashscope
└── DashScope ChatModel 自动配置
注意 :
spring-ai-alibaba-graph-core、spring-ai-alibaba-agent-framework、spring-ai-alibaba-starter-dashscope需要显式指定版本号 ,因为它们不在 Spring AI BOM 的管理范围内,而spring-ai-alibaba-bom的版本管理可能不完整。
5. application.yml ------ 应用配置
yaml
server:
port: 8087
spring:
application:
name: spring-ai-alibaba-mcp-demo
ai:
# DashScope 模型配置
dashscope:
api-key: ${DASHSCOPE_API_KEY:sk-3d56ec506a8943d0b39f58f306438260}
chat:
options:
model: qwen-plus
temperature: 0.7
# MCP 客户端配置
mcp:
client:
enabled: true
name: amap-maps-mcp-client
version: 1.0.0
request-timeout: 60s
type: SYNC # 同步客户端
streamable-http: # 使用 Streamable HTTP 传输
connections:
amap-maps: # 连接名(后续可据此过滤工具)
url: https://mcp.amap.com
endpoint: /mcp?key=${AMAP_API_KEY:0d29df51457fd82f93ac8db8c9b2be9b}
toolcallback:
enabled: true # 自动创建 ToolCallbackProvider Bean
5.1 关键配置项说明
| 配置项 | 说明 |
|---|---|
spring.ai.mcp.client.type |
SYNC 同步模式,适用于 WebMVC;若用 WebFlux 则选 ASYNC |
spring.ai.mcp.client.streamable-http.connections |
定义 MCP Server 连接,支持多个连接 |
spring.ai.mcp.client.toolcallback.enabled |
设为 true 后,Spring AI 自动将所有 MCP 连接发现的工具包装为 ToolCallbackProvider Bean |
endpoint 中带 ?key= |
高德 MCP Server 通过查询参数传递 API Key 进行认证 |
5.2 MCP 工具自动发现流程
application.yml 配置
│
▼
spring-ai-starter-mcp-client 自动配置
│
├── 为每个 connection 创建 McpSyncClient
├── client.initialize() 获取工具列表
│
▼
toolcallback.enabled: true
│
▼
SyncMcpToolCallbackProvider Bean
(实现 ToolCallbackProvider 接口)
│
▼
getToolCallbacks() → ToolCallback[]
└── 每个 MCP 工具被包装为 ToolCallback
6. 核心代码实现
6.1 启动类
java
package com.example.mcpdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class McpDemoApplication {
public static void main(String[] args) {
SpringApplication.run(McpDemoApplication.class, args);
}
}
@SpringBootApplication 会触发:
spring-ai-starter-mcp-client的MCP客户端自动配置spring-ai-alibaba-starter-dashscope的DashScope模型自动配置- 组件扫描(
@Component、@Configuration、@RestController)
6.2 McpClientToolCallbackProvider ------ MCP 工具提供器
java
package com.example.mcpdemo.graph;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
@Component
public class McpClientToolCallbackProvider {
private final ToolCallbackProvider toolCallbackProvider;
public McpClientToolCallbackProvider(ToolCallbackProvider toolCallbackProvider) {
this.toolCallbackProvider = toolCallbackProvider;
}
/**
* 获取所有 MCP 工具回调
*/
public Set<ToolCallback> getAllToolCallbacks() {
Set<ToolCallback> result = new HashSet<>();
ToolCallback[] callbacks = toolCallbackProvider.getToolCallbacks();
for (ToolCallback callback : callbacks) {
result.add(callback);
}
return result;
}
/**
* 根据连接名过滤工具(按需分配工具给不同节点)
*/
public Set<ToolCallback> findToolCallbacksByConnection(String connectionName) {
Set<ToolCallback> result = new HashSet<>();
ToolCallback[] callbacks = toolCallbackProvider.getToolCallbacks();
for (ToolCallback callback : callbacks) {
ToolDefinition definition = callback.getToolDefinition();
if (definition.name() != null
&& definition.name().toLowerCase().contains(connectionName.toLowerCase())) {
result.add(callback);
}
}
return result;
}
}
设计要点:
- 注入
ToolCallbackProvider:Spring AI自动配置创建的Bean,包含所有MCP连接发现的所有工具 getAllToolCallbacks():返回全部MCP工具,适用于简单场景findToolCallbacksByConnection(connectionName):按连接名过滤,适用于多 MCP Server 场景 ------不同Graph节点可分配不同的MCP工具集
6.3 McpNode ------ MCP 图节点
java
package com.example.mcpdemo.graph;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import reactor.core.publisher.Flux;
public class McpNode implements NodeAction {
private final ChatClient chatClient;
public McpNode(ChatClient.Builder chatClientBuilder, Set<ToolCallback> toolCallbacks) {
this.chatClient = chatClientBuilder
.defaultToolCallbacks(toolCallbacks.toArray(ToolCallback[]::new))
.build();
}
@Override
public Map<String, Object> apply(OverAllState state) {
String query = state.value("query", "");
// 流式调用 LLM,LLM 自动决定是否调用 MCP 工具
Flux<String> streamResult = chatClient.prompt(query).stream().content();
String result = streamResult.reduce("", (acc, item) -> acc + item).block();
HashMap<String, Object> resultMap = new HashMap<>();
resultMap.put("mcpcontent", result);
return resultMap;
}
}
执行流程:
StateGraph 调度到 McpNode
│
▼
从 OverAllState 读取 "query" 字段
│
▼
ChatClient.prompt(query).stream().content()
│
├── LLM 分析用户意图
├── 需要工具?→ 调用 defaultToolCallbacks 中的 MCP 工具
│ └── McpSyncClient.callTool() → MCP Server
├── 工具结果返回给 LLM
└── LLM 生成最终回复
│
▼
结果写入 OverAllState["mcpcontent"]
NodeAction是函数式接口,apply(OverAllState)的返回值会被合并回图状态。
6.4 GraphMcpConfig ------ StateGraph 配置
java
package com.example.mcpdemo.config;
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.KeyStrategyFactory;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.example.mcpdemo.graph.McpClientToolCallbackProvider;
import com.example.mcpdemo.graph.McpNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Set;
import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction.node_async;
@Configuration
public class GraphMcpConfig {
private static final Logger logger = LoggerFactory.getLogger(GraphMcpConfig.class);
@Bean("mcpGraph")
public StateGraph mcpGraph(ChatClient.Builder chatClientBuilder,
McpClientToolCallbackProvider mcpToolProvider)
throws GraphStateException {
// 1. 获取 MCP 工具
Set<ToolCallback> mcpToolCallbacks = mcpToolProvider.getAllToolCallbacks();
logger.info("MCP Graph: loaded {} tool callbacks", mcpToolCallbacks.size());
// 2. 定义状态键策略(ReplaceStrategy = 每次覆盖)
KeyStrategyFactory keyStrategyFactory = () -> {
HashMap<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("query", new ReplaceStrategy());
strategies.put("mcpcontent", new ReplaceStrategy());
return strategies;
};
// 3. 构建 StateGraph
StateGraph stateGraph = new StateGraph("mcp_graph", keyStrategyFactory)
.addNode("mcp", node_async(
new McpNode(chatClientBuilder, mcpToolCallbacks)))
.addEdge(StateGraph.START, "mcp")
.addEdge("mcp", StateGraph.END);
// 4. 打印 PlantUML 图结构
GraphRepresentation representation = stateGraph.getGraph(
GraphRepresentation.Type.PLANTUML, "MCP Flow");
logger.info("\n=== MCP Graph UML ===\n{}\n====================",
representation.content());
return stateGraph;
}
}
6.4.1 状态键策略
| 键 | 策略 | 说明 |
|---|---|---|
query |
ReplaceStrategy |
每次调用覆盖旧值 |
mcpcontent |
ReplaceStrategy |
每次调用覆盖旧结果 |
6.4.2 图结构
START ──→ [mcp] ──→ END
单节点线性工作流,mcp 节点内执行 LLM + MCP 工具调用。
6.5 ReactAgentMcpConfig ------ ReactAgent 配置
java
package com.example.mcpdemo.config;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.example.mcpdemo.graph.McpClientToolCallbackProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Set;
@Configuration
public class ReactAgentMcpConfig {
private static final Logger logger = LoggerFactory.getLogger(ReactAgentMcpConfig.class);
@Bean("mcpReactAgent")
public ReactAgent mcpReactAgent(ChatModel chatModel,
McpClientToolCallbackProvider mcpToolProvider) {
Set<ToolCallback> mcpToolCallbacks = mcpToolProvider.getAllToolCallbacks();
ToolCallback[] toolArray = mcpToolCallbacks.toArray(ToolCallback[]::new);
logger.info("ReactAgent MCP: loaded {} MCP tools", toolArray.length);
return ReactAgent.builder()
.name("mcp-react-agent")
.description("AI 助手,可调用高德地图 MCP 工具")
.model(chatModel)
.systemPrompt("你是一个有帮助的 AI 助手,可以调用高德地图提供的工具来查询地理位置、天气、路线等信息。")
.tools(toolArray) // 直接注入 MCP 工具
.saver(new MemorySaver()) // 内存检查点,支持多轮对话
.enableLogging(true) // 控制台打印工具调用过程
.build();
}
}
6.5.1 ReactAgent 执行流程(ReAct 循环)
用户输入: "查询北京的天气"
│
▼
[Think] LLM 分析:需要调用天气查询工具
│
▼
[Act] 调用 MCP 工具 ToolCallback
│ └── McpSyncClient.callTool("weather", {city: "北京"})
│
▼
[Observe] 工具返回: {temperature: 25°C, condition: "晴"}
│
▼
[Think] LLM 整合结果 → 生成自然语言回复
│
▼
输出: "北京当前天气晴朗,气温 25°C"
6.5.2 MemorySaver 的作用
java
// 多轮对话示例
RunnableConfig config = RunnableConfig.builder()
.threadId("user-123") // 同一 threadId 共享对话历史
.build();
agent.call("我叫张三", config); // 第1轮
agent.call("帮我查一下杭州天气", config); // 第2轮 → 记住"张三"
agent.call("我之前叫什么名字?", config); // 第3轮 → 能回答"张三"
6.6 Controller 层
6.6.1 GraphMcpController
java
@RestController
@RequestMapping("/graph/mcp")
public class GraphMcpController {
private final CompiledGraph compiledGraph;
public GraphMcpController(@Qualifier("mcpGraph") StateGraph stateGraph)
throws GraphStateException {
this.compiledGraph = stateGraph.compile(); // 编译 StateGraph
}
@GetMapping("/call")
public Map<String, Object> call(
@RequestParam(defaultValue = "北京时间现在几点钟") String query,
@RequestParam(defaultValue = "demo-user") String threadId) {
RunnableConfig config = RunnableConfig.builder()
.threadId(threadId).build();
Map<String, Object> inputs = new HashMap<>();
inputs.put("query", query);
Optional<OverAllState> result = compiledGraph.invoke(inputs, config);
return result.map(OverAllState::data).orElseGet(HashMap::new);
}
}
6.6.2 AgentMcpController
java
@RestController
@RequestMapping("/agent/mcp")
public class AgentMcpController {
private final ReactAgent agent;
public AgentMcpController(@Qualifier("mcpReactAgent") ReactAgent agent) {
this.agent = agent;
}
@GetMapping("/call")
public Map<String, Object> call(
@RequestParam(defaultValue = "查询北京市朝阳区的天气") String query,
@RequestParam(defaultValue = "demo-user") String threadId) {
RunnableConfig config = RunnableConfig.builder()
.threadId(threadId).build();
String result;
try {
result = agent.call(query, config).getText();
} catch (GraphRunnerException e) {
result = "Error: " + e.getMessage();
}
Map<String, Object> response = new HashMap<>();
response.put("query", query);
response.put("threadId", threadId);
response.put("result", result);
return response;
}
}
7. 两种 MCP 集成模式对比
┌──────────────────────────────────────────────────┐
│ StateGraph + MCP 节点 │
│ │
│ REST ──→ StateGraph ──→ [McpNode] │
│ │ │
│ ChatClient │
│ .defaultToolCallbacks(MCP工具) │
│ │ │
│ LLM + MCP工具 │
│ │ │
│ 返回结果 ←─────────────────────│
│ │
│ 适用场景: │
│ · 需要多节点编排(条件路由/并行/人机协同) │
│ · 不同节点分配不同的 MCP 工具集 │
│ · 需要图可视化(PlantUML/Mermaid) │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ ReactAgent + MCP 工具 │
│ │
│ REST ──→ ReactAgent │
│ │ │
│ ReAct 循环 (Think → Act → Observe) │
│ │ │
│ .tools(MCP工具数组) │
│ │ │
│ LLM 自主决定调用哪些工具 │
│ │ │
│ 返回文本结果 │
│ │
│ 适用场景: │
│ · 自由对话式 AI 助手 │
│ · LLM 自主推理 + 工具调用 │
│ · 多轮对话(MemorySaver 持久化上下文) │
└──────────────────────────────────────────────────┘
8. 编译与运行
8.1 编译
bash
cd spring-ai-alibaba-mcp-demo
mvn clean compile
8.2 打包
bash
mvn clean install -DskipTests
8.3 启动
bash
mvn spring-boot:run
启动日志关键输出:
MCP Graph: loaded 5 tool callbacks ← MCP Server 返回的工具数
Tool: amap-maps-mcp-client_geoCode ← 地理编码
Tool: amap-maps-mcp-client_regeoCode ← 逆地理编码
Tool: amap-maps-mcp-client_weather ← 天气查询
...
ReactAgent MCP: loaded 5 MCP tools ← ReactAgent 也加载了同样工具
=== MCP Graph UML === ← PlantUML 图输出
@startuml
...
@enduml
================================
8.4 调用接口
8.4.1 Graph 模式
bash
# 时间查询
curl "http://localhost:8087/graph/mcp/call?query=北京时间现在几点钟"
# 天气查询
curl "http://localhost:8087/graph/mcp/call?query=查询杭州市的天气"
# 地理编码
curl "http://localhost:8087/graph/mcp/call?query=北京市朝阳区的地理坐标是什么"
8.4.2 ReactAgent 模式
bash
# 天气查询
curl "http://localhost:8087/agent/mcp/call?query=查询北京市朝阳区的天气"
# 多轮对话(相同 threadId 保持上下文)
curl "http://localhost:8087/agent/mcp/call?query=我叫张三&threadid=user-001"
curl "http://localhost:8087/agent/mcp/call?query=帮我查上海天气&threadid=user-001"
curl "http://localhost:8087/agent/mcp/call?query=我是谁&threadid=user-001"
# → 返回: "你是张三"
9. 扩展:多 MCP Server 按节点分配
如果对接多个 MCP Server,可按节点分配不同的工具集:
yaml
spring.ai.mcp.client.streamable-http.connections:
amap-maps:
url: https://mcp.amap.com
endpoint: /mcp?key=${AMAP_API_KEY}
weather-service:
url: https://weather.example.com
endpoint: /mcp?key=${WEATHER_API_KEY}
java
// 节点1: 只用高德地图工具
Set<ToolCallback> amapTools = provider.findToolCallbacksByConnection("amap-maps");
// 节点2: 只用天气服务工具
Set<ToolCallback> weatherTools = provider.findToolCallbacksByConnection("weather-service");
StateGraph graph = new StateGraph("workflow", keyFactory)
.addNode("geo_node", node_async(new McpNode(chatBuilder, amapTools)))
.addNode("weather_node", node_async(new McpNode(chatBuilder, weatherTools)))
// ...