文章目录

    • 1_调用公用MCP
    • 2_Stdio方式
    • 3_Stdio实现原理
    • 4_SSE方式
    • 5_自定义MCP客户端
    • 6_MCP Server权限控制

SpringAI 通过 SpringBoot 集成扩展了 MCP Java SDK ,提供了客户端和服务端 starter,让 AI 应用程序快速支持 MCP。

接下来直接演示。

1_调用公用MCP

在使用其他开发者提供好的 MCP 服务时,仅仅引入 MCP 的客户端依赖即可。

SpringAI 提供了如下两种 MCP Client 依赖,根据需求选择一种引入即可(常见第二种)。

仅支持 Stdio 的依赖

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

既支持 SSE(远程)也支持 Stdio(进程内)

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

以使用百度地图提供的 MCP 服务为例,演示如何使用 Stdio 的方式调用公用的 MCP Server。

首先,对 MCP 进行配置,Stdio 的配置方式有两种。

一种是直接配置全局配置文件中:

spring:ai:mcp:client:enabled: truename: spring-ai-mcp-clientversion: 1.0.0type: sync # 或 async 调用响应式流应用request-timeout: 60000stdio:connections:baidu-map:command: cmdargs:- "/c"- "npx"- "-y"- "@baidumap/mcp-server-baidu-map" # 第一次启动会安装此包env:BAIDU_MAP_API_KEY: xxx # 百度地图开放平台查看

另外一种是使用 Claude Desktop 格式的 JSON 文件单独存放(推荐)

{"mcpServers": {"baidu-map": {"command": "cmd","args": ["/c","npx","-y","@baidumap/mcp-server-baidu-map"],"env": {"BAIDU_MAP_API_KEY": "xxxx"}}}
}

然后在 application.yaml 中指定文件的位置

spring:ai:mcp:client:request-timeout: 60000stdio:servers-configuration: classpath:/mcp/stdio-server-config.json

通过 ToolCallbackProvider 绑定 ChatClient,ToolCallbackProvider 可以看做外部工具提供商。

@Bean
public ChatClient serviceChatClient(OpenAiChatModel model, ChatMemory chatMemory, ToolCallbackProvider toolCallbackProvider) {return ChatClient.builder(model).defaultSystem(new ClassPathResource("call.txt")).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build(), // CHAT MEMORYnew SimpleLoggerAdvisor()).defaultToolCallbacks(toolCallbackProvider)//关键.build();
}

如果想观察到调用 mcp 工具的日志信息进行调试,可以添加如下配置:

logging:level:io:modelcontextprotocol:client: debugspec: debug

测试结果:

img

2_Stdio方式

以查询天气为目标,定义 MCP-Server 工程,引入 Stdio 服务端依赖

<!-- mcp-server依赖-->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>

配置 application.yaml 属性:

spring:application:name: mcp-servermain:web-application-type: none # stdio 并不需要启动为web应用banner-mode: off # 关掉banner 下方有说明ai:mcp:server:name: mcp-server # 应用名称version: 1.0.0 # 版本
# 注意: 您必须禁用 banner 和控制台日志记录,以允许 Stdio 传输工作!!!

定义查询城市天气信息的工具类

@Service
public class WeatherService {@Tool(description = "根据城市名称获得天气信息")public String getWeather(@ToolParam(description = "城市名称") String cityName) {return cityName + "今日天气 晴";}
}

将工具绑定到 ToolCallbackProvider:

@SpringBootApplication
public class McpServerApplication {public static void main(String[] args) {SpringApplication.run(McpServerApplication.class, args);}@Beanpublic ToolCallbackProvider weatherTools(WeatherService weatherService){return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();}// 客户端使用 .defaultToolCallbacks(toolCallbackProvider) 添加
}

将项目打成 jar 包后,在客户端 mcp/stdio-server-config.json 文件的 mcpServers 属性中新增如下配置:

"mcp-server-weather": {"command": "java","args": ["-Dspring.ai.mcp.server.stdio=true","-Dlogging.pattern.console=","-jar","D:\\WorkSpace\\IdeaProject\\jar\\mcp-server-weather.jar"]
}

说明:

  • command:内部 java 程序直接调用,所以无需 cmd 命令。
  • args:以 stdio 的方式启动,清空控制台,否则 mcp-server 启动也会打印干扰信息。

img

3_Stdio实现原理

当客户端启动之后,会由 McpClientAutoConfiguration 初始化并启动传输连接获取 tool 列表。

传输连接的关键流程及类图如下:

在这里插入图片描述

在 StdioClientTransport 的 connect 方法中,会利用 ProcessBuilder 对象将配置文件中的命令、参数、环境等信息封装起来创建子进程并启动。

之后建立输入、输出流连接循环地读取或写入信息,源码中的大致实现如下:

// 准备指定的命令、参数、环境
List<String> fullCommand = new ArrayList<>();
fullCommand.add(params.getCommand());
fullCommand.addAll(params.getArgs());
ProcessBuilder processBuilder = this.getProcessBuilder();
processBuilder.command(fullCommand);
processBuilder.environment().putAll(params.getEnv());
// 启动子进程
this.process = processBuilder.start();
// 循环地从连接中读取数据
try (BufferedReader processReader = new BufferedReader(new InputStreamReader(procesString line;while (!isClosing && (line = processReader.readLine()) != null) {try {JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(this.objecif (!this.inboundSink.tryEmitNext(message).isSuccess()) {if (!isClosing) {logger.error("Failed to enqueue inbound message: {}", message);}break;}}}
}
// 如果有消息向连接中写入数据
try {String jsonMessage = objectMapper.writeValueAsString(message);// Escape any embedded newlines in the JSON message as per spec:// https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio// - Messages are delimited by newlines, and MUST NOT contain// embedded newlines.jsonMessage = jsonMessage.replace("\r\n", "\\n").replace("\n", "\\n").replace("\r", "\\n");var os = this.process.getOutputStream();synchronized (os) {os.write(jsonMessage.getBytes(StandardCharsets.UTF_8));os.write("\n".getBytes(StandardCharsets.UTF_8));os.flush();}s.next(message);
}

注意:这种方式传输信息时,服务端运行时打印的各种信息,如:banner、日志等都会成为干扰项。

4_SSE方式

最新版 MCP 协议已经弃用了这种方式,转为感知的 Streamable HTTP,但 SpringAI 目前还不支持 Streamable HTTP。

SSE 需要将 MCP Server 部署为 Web 服务。

以加法计算为例,定义 MCP-Server 工程,引入 SSE 服务端依赖

<!--支持webflux以及stdio的方式,还支持webflux的方式暴露服务,-->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<!---webmvc是基于mvc的依赖,同样支持stdio,还支持-webmvc的方式暴露服务-->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>

配置 application.yaml 属性:

spring:application:name: mcp-servermain:# (mvc,webflux依赖都引入则只能使用webflux,不然客户端会404)web-application-type: reactive # webflux的方式暴露服务,除此外还有mvc-servletai:mcp:server:name: mcp-server # 应用名称version: 1.0.0 # 版本type: async # 异步响应通信方式base-url:  # base-urlsse-endpoint: /sse # 切入点,client默认会向url/sse下发送请求
server:port: 8888

定义加法计算的工具类,这里让它返回错误的结果方便验证

@Service
public class CalculationService {@Tool(description = "两数加法计算")public Long getWeather(@ToolParam(description = "加数") Long addend,@ToolParam(description = "被加数") Long summand) {return addend * summand;}
}

将工具绑定到 ToolCallbackProvider:

@SpringBootApplication
public class McpServerApplication {public static void main(String[] args) {SpringApplication.run(McpServerApplication.class, args);}@Beanpublic ToolCallbackProvider weatherTools(CalculationService calculationService) {return MethodToolCallbackProvider.builder().toolObjects(calculationService).build();}// 客户端使用 .defaultToolCallbacks(toolCallbackProvider) 添加
}

直接启动 MCP Server 后,客户端新增如下配置即调用服务端提供的工具

spring:ai:mcp:client:sse: # 新增此处配置,与stdio同级,其他配置无需更改connections: # 配置多个连接addition-calculation: # 服务名称url: http://localhost:8888 # mcp-server 地址

验证:

img

5_自定义MCP客户端

在 Spring AI 中,MCP 客户端分为两类:

  • 同步客户端(Sync Client):默认类型,适用于传统的请求-响应模式,使用阻塞操作。
  • 异步客户端(Async Client):适用于响应式应用,使用非阻塞操作。可以通过配置项 spring.ai.mcp.client.type=async 启用。

Spring 提供了自动装配机制,开发者只需实现 McpSyncClientCustomizer 或 McpAsyncClientCustomizer 接口,就能对 MCP 客户端的各个方面进行个性化配置。

例如,控制请求超时、事件监听以及消息处理逻辑。

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.stereotype.Component;import java.time.Duration;
import java.util.List;/*** 自定义同步 MCP 客户端配置器* (兼容所有的传输方式: stdio、mvc、webflux 传输方式)*/
@Slf4j
@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {@Overridepublic void customize(String serverConfigurationName, McpClient.SyncSpec spec) {log.info("正在自定义 MCP 同步客户端配置: {}", serverConfigurationName);// 1. 配置请求超时时间spec.requestTimeout(Duration.ofSeconds(30));// 2. 工具变更监听spec.toolsChangeConsumer((List<Tool> tools) -> {log.info("工具列表发生变更,共 {} 个", tools.size());tools.forEach(tool -> log.debug("工具: {} - {}", tool.name(), tool.description()));handleToolsChange(serverConfigurationName, tools);});// 3. 资源变更监听spec.resourcesChangeConsumer((List<Resource> resources) -> {log.info("资源列表发生变更,共 {} 个", resources.size());resources.forEach(resource -> log.debug("资源: {} - {} ({})",resource.name(), resource.description(), resource.mimeType()));handleResourcesChange(serverConfigurationName, resources);});// 4. 提示变更监听spec.promptsChangeConsumer((List<Prompt> prompts) -> {log.info("提示列表发生变更,共 {} 个", prompts.size());prompts.forEach(prompt -> log.debug("提示: {} - {}", prompt.name(), prompt.description()));handlePromptsChange(serverConfigurationName, prompts);});// 5. 日志消息处理spec.loggingConsumer((McpSchema.LoggingMessageNotification logMsg) -> {switch (logMsg.level()) {case ERROR -> log.error("MCP 服务器错误: {}", logMsg.data());case WARNING -> log.warn("MCP 服务器警告: {}", logMsg.data());case INFO -> log.info("MCP 服务器信息: {}", logMsg.data());case DEBUG -> log.debug("MCP 服务器调试: {}", logMsg.data());default -> log.trace("MCP 服务器日志: {}", logMsg.data());}});log.info("MCP 同步客户端配置完成: {}", serverConfigurationName);// 2. 设置文件系统根目录访问权限// 3. 自定义采样处理器//  - 处理LLM生成请求//  - 客户端保持对模型访问、选择和权限的控制//  - 服务器无需API密钥即可利用AI能力}/** 工具变更处理逻辑 */private void handleToolsChange(String serverName, List<Tool> tools) {log.info("为服务器 {} 更新工具缓存", serverName);// 可扩展:更新缓存、通知下游服务等}/** 资源变更处理逻辑 */private void handleResourcesChange(String serverName, List<Resource> resources) {log.info("为服务器 {} 刷新资源索引", serverName);// 可扩展:刷新索引、更新界面等}/** 提示变更处理逻辑 */private void handlePromptsChange(String serverName, List<Prompt> prompts) {log.info("为服务器 {} 重新加载提示模板", serverName);// 可扩展:重新加载提示、推荐新模板等}
}

MCP 客户端 bean 会自动配置并且可以通过注入的方式调用:

@Autowired
private List<McpSyncClient> mcpSyncClients;  // For sync client
// OR
@Autowired
private List<McpAsyncClient> mcpAsyncClients;  // For async client

当启用工具回调(默认行为)时,所有 MCP 客户端注册的 MCP 工具都将作为 ToolCallbackProvider 实例提供:

@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();

6_MCP Server权限控制

系统提示词,由调用方传递用户数据:可以将用户信息传递到工具中,但是可以随意去查询其他用户的数据,不可取。

Stdio:

  • 环境变量传递授权信息,但是目前不支持动态修改环境变量。
  • 可以自己尝试实现(销毁原来的 Stdio、建立新的 Stdio,不推荐),由于多个用户存在 Stdio 可能会造成并发竞争因此不推荐。

SSE:

按照传统的 Web 鉴权实现即可,推荐,也不会多个用户竞争同一个进程“切换权限”造成并发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94228.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94228.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94228.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Start Here 读书笔记:第10章 Implementing REST services

REST 服务可用于实现两个应用之间的通讯&#xff0c;包括 Web 应用中的客户端和服务器之间&#xff0c;移动应用与后端服务之间&#xff0c;或两个后端服务之间。 10.1 使用 REST 服务在应用之间交换数据 REST端点是应用程序通过 Web 协议公开服务的方式&#xff0c;因此也称…

SYBASE ASE、Oracle、MySQL/MariaDB、SQL Server及PostgreSQL在邮件/短信发送功能上的全面横向对比报告

以下是对SYBASE ASE、Oracle、MySQL/MariaDB、SQL Server及PostgreSQL在邮件/短信发送功能上的全面横向对比报告&#xff08;截至2025年8月最新版本&#xff09;&#xff0c;涵盖技术实现、配置复杂度、适用场景及权威评测&#xff1a;​​一、邮件发送能力对比​​​​1. Orac…

服务器与客户端

目录 一、服务器&#xff08;Server&#xff09; 核心特点 常见类型 二、客户端&#xff08;Client&#xff09; 核心特点 常见类型 客户端与服务器的交互流程 补充&#xff1a;与 “对等网络&#xff08;P2P&#xff09;” 的区别 C/S模式 一、C/S 模式的核心原理 …

GaussDB 并发自治事务数达到最大值处理案例

1 业务背景自治事务&#xff08;Autonomous Transactions&#xff09;是一种高级特性&#xff0c;允许你在一个事务中执行另一个独立的事务。这种机制特别有用&#xff0c;尤其是在需要在一个事务中执行多个操作但又不想因为其中一个操作失败而影响整个事务的场景。2 业务影响在…

【传奇开心果系列】Flet分页自定义组件CustomPaginationComponent封装版自定义模板

Flet分页自定义组件CustomPaginationComponent封装版自定义模板一、效果展示GIF动图二、应用场景三、特色说明四、源码下载地址一、效果展示GIF动图 二、应用场景 图片浏览应用&#xff1a; 用户可以通过分页组件浏览多张图片&#xff0c;每点击一次“上一页”或“下一页”按钮…

数据安全——39页解读数字化转型大数据安全基础培训方案【附全文阅读】

适应人群为企业数据安全管理人员、IT 运维人员、数字化转型决策者、网络安全工程师及关注大数据安全的从业人员。主要内容围绕数字化转型中大数据安全展开,核心包括基础概念(信息、数据与大数据的定义及区别,大数据 4V 特点与来源);安全风险(企业面临的数据资产管理缺失、…

week3-[二维数组]小方块

week3-[二维数组]小方块 题目描述 如果四个数 a,b,c,da,b,c,da,b,c,d 可以分成两组&#xff0c;每组两个数&#xff0c;满足每组里面的两个数一样&#xff0c;那么称这四个数是好的。 比如&#xff0c;2,5,2,52,5,2,52,5,2,5 是好的&#xff0c;因它满足两组&#xff1a;222 与…

Swift 项目结构详解:构建可维护的大型应用

Swift 项目结构详解&#xff1a;构建可维护的大型应用一、基础结构&#xff08;推荐新手使用&#xff09;二、组件化结构&#xff08;企业级应用推荐&#xff09;层级架构&#xff1a;MVVM Coordinator路由实现&#xff08;Coordinator模式&#xff09;三、通用组件实现DI&…

【实时Linux实战系列】基于实时Linux的数字转换器设计

在现代电子系统中&#xff0c;数字转换器&#xff08;如模数转换器ADC和数模转换器DAC&#xff09;扮演着至关重要的角色。它们负责将模拟信号转换为数字信号&#xff0c;或将数字信号转换为模拟信号&#xff0c;从而实现信号的数字化处理和传输。在实时系统中&#xff0c;如工…

FastTracker:实时准确的视觉跟踪

摘要 https://arxiv.org/pdf/2508.14370 传统的多目标跟踪(MOT)系统主要设计用于行人跟踪&#xff0c;通常对其他物体类别的泛化能力有限。本文提出了一种能够处理多种物体类型的通用跟踪框架&#xff0c;特别强调在复杂交通场景中的车辆跟踪。所提出的1方法包含两个关键组件&a…

国产轻量级桌面GIS软件Snaplayers从入门到精通(20)

国产轻量级桌面GIS软件Snaplayers实操&#xff1a;打开图层并显示属性信息1、根据数据格式选择图层文件2、加载图层到地图中&#xff0c;并在左侧显示图层的属性表格3、属性表格分页显示Snaplayers研发团队承诺&#xff1a;国产轻量级桌面GIS软件Snaplayers永久免费并持续更新

快速入门flask应用(从入门到实战)

目录 前言&#xff1a; 了解一些网络通信的概念 什么是网络通信&#xff1a; 当我们访问一个网址的时候发生了什么&#xff1a; 1. 解析 URL&#xff1a;明确访问目标 2. DNS 域名解析&#xff1a;将 “名字” 转为 “地址” 3. 建立连接&#xff1a;TCP 三次握手&#x…

C++/QT 开发技能树详解

一、 编程语言 (C)1. C基础语法&#xff08;数据类型、模板、命名空间&#xff09;是什么&#xff1a; 这是构建C程序的基石。数据类型定义了变量存储的数据种类和大小&#xff1b;模板允许编写与数据类型无关的通用代码&#xff1b;命名空间用于避免大型项目中的名称冲突。如何…

Java多线程进阶-死锁与面试题解析

文章目录Java多线程进阶&#xff1a;死锁与面试题解析一、并发编程的噩梦——死锁1. 什么是死锁&#xff1f;四个缺一不可的条件2. 如何避免死锁&#xff1f;从破坏循环等待开始二、并发编程面试题全景解析1. 锁与同步机制2. CAS 与原子操作3. JUC 工具与线程池4. 线程安全集合…

ZYNQ启动流程——ZYNQ学习笔记11

ZYNQ SoC 的启动由片上的 BootROM 开始。片上 BootROM 是 ZYNQ 芯片上的一块非易失性存储器&#xff0c;它包含了 ZYNQ 所支持的配置器件的驱动&#xff0c; 而且里面的代码是不可修改的。 BootROM 中的代码首先会在片外的非易失性存储器中寻找一个头文件&#xff0c; 头文件里…

C++利用CerateProcess创建WPF进程并通过命名管道通讯

引言原因是我需要在C程序中调用另外一个WPF窗体打开或则关闭&#xff0c;进程之前通过通讯协议进行交互。由于使用不同语言开发&#xff0c;两者都比较复杂不方便重写&#xff0c;最方便的方法就是使用进程间通信&#xff0c;WPF窗体应用程序根据消息进行Show/Hide/Exit操作。函…

Seaborn数据可视化实战

1. Seaborn基础与实践&#xff1a;数据可视化的艺术 2. Seaborn入门&#xff1a;环境搭建与基础操作 3. Seaborn基础图表绘制入门 4. Seaborn数据可视化基础&#xff1a;从内置数据集到外部数据集的应用 5. Seaborn颜色与样式定制教程 6. Seaborn数据可视化入门&#xff1a;绘制…

BIM+写实数字孪生落地实战指南

&#x1f31f; 正文 在智慧城市与工业4.0的浪潮中&#xff0c;BIM与数字孪生的深度碰撞正在重塑建筑的生命周期。基于Revit&#xff08;RVT&#xff09;模型构建的超写实数字孪生体&#xff0c;不仅实现物理空间的毫米级镜像&#xff0c;更通过实时数据驱动&#xff0c;赋予建…

[Git] 如何拉取 GitHub 仓库的特定子目录

作为开发者&#xff0c;我们经常遇到只需要克隆大型仓库中某个子目录的场景。 Git 本身并不支持直接克隆子目录&#xff0c;但通过一些技巧可以实现类似效果。本文将介绍几种实用的方法&#xff0c;帮助获取目标代码。 为什么需要局部拉取&#xff1f; 节省时间和带宽&#xff…

修复Simulink到UE5丢包时被控船体的残影问题

提问 simulink 有一个和UE5协同的模块&#xff0c;叫做Simulation 3D Scence Configuration&#xff0c;还有一个发送来自simulink到UE5数据的模块叫做Simulation 3D Message。 现在遇到的问题是&#xff0c;这两个模块的优先级设置是正确的&#xff0c;且sample time都设置为0…