使用spring-ai创建1个MCP Server很容易,下面演示MCP(stdio模式)的写法:

一、添加依赖项

1 <dependency>
2 <groupId>org.springframework.ai</groupId>
3 <artifactId>spring-ai-starter-mcp-server</artifactId>
4 <version>1.0.0</version>
5 </dependency>

二、创建1个MCP Server服务

 1 import org.springframework.ai.tool.annotation.Tool;
2 import org.springframework.ai.tool.annotation.ToolParam;
3 import org.springframework.stereotype.Service;
4
5 /**
6 * @author 菩提树下的杨过
7 */
8 @Service
9 public class OrderService {
10
11
12 @Tool(name = "queryOrderStatus",
13 description = "根据订单号查询订单状态")
14 public String queryOrderStatus(@ToolParam(required = true, description = "订单号,格式为8位数字,比如:25070601") String orderNo) {
15 return switch (orderNo) {
16 case "25070601" -> "订单号:" + orderNo + ",订单状态:已发货";
17 case "25070602" -> "订单号:" + orderNo + ",订单状态:已完成";
18 case "25070603" -> "订单号:" + orderNo + ",订单状态:已取消";
19 default -> "订单号:" + orderNo + ",订单状态:未知";
20 };
21 }
22 }

可以看到,几乎跟常规的后端Service开发没区别,只是多了@Tool@ToolParam注解,是给MCP Host和大模型使用的。

三、暴露MCP服务

MCP Server必须对外暴露出来,才能被调用方发现。

 1 import com.cnblogs.yjmyzz.mcp.server.OrderService;
2 import org.springframework.ai.tool.ToolCallbackProvider;
3 import org.springframework.ai.tool.method.MethodToolCallbackProvider;
4 import org.springframework.boot.SpringApplication;
5 import org.springframework.boot.autoconfigure.SpringBootApplication;
6 import org.springframework.context.annotation.Bean;
7
8 @SpringBootApplication
9 public class SpringAiApplication {
10
11 public static void main(String[] args) {
12 SpringApplication.run(SpringAiApplication.class, args);
13 }
14
15 @Bean
16 public ToolCallbackProvider orderTools(OrderService orderService) {
17 return MethodToolCallbackProvider.builder().toolObjects(orderService).build();
18 }
19
20 }

编译一下,会在本地生成jar包,类似D:\code\spring-ai-sample\target\spring-ai-0.0.1-SNAPSHOT.jar

四、调整yaml

spring:
main:
banner-mode: off logging:
pattern:
console:

这一步非常重要,MCP的stdio模式,client与server通过标准输入输出(Standard Input/Output )进行通讯(通俗来说,就是控制台的输入/输出)。如果不关闭启动banner以及控制台的输出,会影响 MCP Client调用时的结果解析。

五、编写MCP Client测试

MCP Server写好了,如何验证功能正常呢,写个Client来调用一下

package com.cnblogs.yjmyzz.mcp.client;

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import org.junit.Test; import java.util.Map; /**
* @author 菩提树下的杨过
*/
public class McpClientSample { @Test
public void testMcpClientSample() { ServerParameters stdioParams = ServerParameters.builder("java")
.args("-jar", "D:\\code\\spring-ai-sample\\target\\spring-ai-0.0.1-SNAPSHOT.jar")
.build(); StdioClientTransport stdioTransport = new StdioClientTransport(stdioParams); //创建MCP Client
McpSyncClient mcpClient = McpClient.sync(stdioTransport).build(); //这一步初始化执行完后,其实就是把jar中的Spring boot应用启动了(MCP Server就能对外提供服务了)
mcpClient.initialize(); McpSchema.ListToolsResult toolsList = mcpClient.listTools(); //打印工具信息(即:orderService的queryOrderStatus方法信息)
System.out.println(toolsList); //调用MCP Server
McpSchema.CallToolResult orderStatus = mcpClient.callTool(
//这里我们只有1个工具,所以直接取第一个
new McpSchema.CallToolRequest(toolsList.tools().getFirst().name(),
Map.of("orderNo", "25070601")));
//打印调用结果
System.out.println(orderStatus); //关闭MCP Client
//这里一定要记得关闭,否则MCP Server的Spring boot应用不会退出(大家可以这里打个断点,然后http://localhost:8080/api/hello 访问一下,应该能访问通)
mcpClient.closeGracefully();
}
}

正常的话,应该会看到以下输出:

18:59:41.289 [pool-1-thread-1] INFO io.modelcontextprotocol.client.McpAsyncClient -- Server response with Protocol: 2024-11-05, Capabilities: ServerCapabilities[completions=CompletionCapabilities[], experimental=null, logging=LoggingCapabilities[], prompts=PromptCapabilities[listChanged=true], resources=ResourceCapabilities[subscribe=false, listChanged=true], tools=ToolCapabilities[listChanged=true]], Info: Implementation[name=mcp-server, version=1.0.0] and Instructions null
ListToolsResult[tools=[Tool[name=queryOrderStatus, description=根据订单号查询订单状态, inputSchema=JsonSchema[type=object, properties={orderNo={type=string, description=订单号,格式为8位数字,比如:25070601}}, required=[orderNo], additionalProperties=false, defs=null, definitions=null]]], nextCursor=null]
CallToolResult[content=[TextContent[audience=null, priority=null, text="订单号:25070601,订单状态:已发货"]], isError=false]
18:59:41.615 [parallel-6] WARN io.modelcontextprotocol.client.transport.StdioClientTransport -- Process terminated with code 1

正好4行:

第1行,表示初始化启动成功

第2行,列出了当前应用暴露的所有工具(即:MCP Server方法)

第3行,是调用工具queryOrderStatus的结果

第4行,表示关闭Spring Boot应用(MCP Server)

如果运行失败,强烈建议先看看任务管理器里,是否有java应用占用了默认端口8080,导致MCP Server的Spring boot应用启动失败

看到这里,可能有朋友会懵,这跟大模型有什么关系 ?这不就是常规的后端开发,跟写个业务接口或SOA服务一样么?其实我刚开始接触时,也是这么认为的。

tips:前面提到了client与server是通过控制台I/O来通讯的,上述的测试过程其实也可以完全在控制台手动输入json来进行

上述测试代码其实就是发出了几条json,类似:

{"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"yjmyzz client","version":"0.0.1"}},"jsonrpc":"2.0","id":0}

{"method":"notifications/initialized","jsonrpc":"2.0"}

{"method":"ping","jsonrpc":"2.0","id":1}

{"method":"tools/list","jsonrpc":"2.0","id":1}

... 后面就不一一列举了

可以先用mvn spring-boot:run,把server启动起来,然后一条条把上面的json输入到终端里,就相当于纯手动测试了

当然,实际应用中,更通用的做法,是找1个现成的MCP Host,来承载MCP client

六、使用MCP Host

6.1 下载MCP Host

建议大家使用免费开源的 Cherry Studio,网址:https://www.cherry-ai.com/download,它能对接市面上的各大主流模型

以DeepSeek为例,我们添加api 密钥

然后发起个对话,看看deepseek是否能正常工作

显然,这个订单号deepSeek是不知道的。

6.2 添加MCP Server

参考下图:

右上角,启动保存时,如果失败,也强烈建议查看任务管理器,是否端口占用(或有其它java进程占用)。启动保存后,本质上就把这个Spring Boot应用给run起来了,跟部署到tomcat其实是类似的。

6.3 会话中启用MCP Server

再回到聊天界面,把刚才这个新加的MCP Server启用

再次来问这个订单的状态

发现大模型似乎变聪明了

静下来想想,发生了啥?

  • 模型还是那个模型,deepseek并没有改变。
  • 本地的mcp server springboot应用,也并不知道deepseek是何方大神

那么,这2个角色是怎么勾搭上的?显然是cherry studio这个媒婆(mcp host)牵线搭桥了,这个细节后面有机会咱们再分析。

文中代码:https://github.com/yjmyzz/spring-ai-sample/tree/day03

spring-ai 学习系列(3)-MCP(stdio)的更多相关文章

  1. Spring Boot 学习系列(10)—SpringBoot+JSP的使

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 解决问题 随着spring boot 框架的逐步使用,我们期望对于一些已有的系统进行改造,做成通用的脚手架, ...

  2. Spring Boot 学习系列(03)—jar or war,做出你的选择

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 两种打包方式 采用Spring Boot框架来构建项目,我们对项目的打包有两种方式可供选择,一种仍保持原有的 ...

  3. Spring Boot 学习系列(序)—Spring Boot

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Spring Boot? Spring Boot 是由pivotal团队提供的一个基于Spring的全新框架 ...

  4. Spring Boot 学习系列(06)—采用log4j2记录日志

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 为什么选择log4j2 log4j2相比于log4j1.x和logback来说,具有更快的执行速度.同时也支 ...

  5. Spring Boot 学习系列(05)—自定义视图解析规则

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 自定义视图解析 在默认情况下Spring Boot 的MVC框架使用的视图解析ViewResolver类是C ...

  6. Spring Boot 学习系列(09)—自定义Bean的顺序加载

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Bean 的顺序加载 有些场景中,我们希望编写的Bean能够按照指定的顺序进行加载.比如,有UserServ ...

  7. Spring Boot 学习系列(08)—自定义servlet、filter及listener

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 传统的filter及listener配置 在传统的Java web项目中,servlet.filter和li ...

  8. Spring Boot 学习系列(07)—properties文件读取

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 传统的properties读取方式 一般的,我们都可以自定义一个xxx.properties文件,然后在工程 ...

  9. Spring Boot 学习系列(04)—分而治之,多module打包

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 明确功能,各司其职 在一个结构清晰的项目中,一个没有module划分的结构显然不是最佳实践.有人会说可以在同 ...

  10. Spring Boot 学习系列(01)—从0到1,只需两分钟

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 快速构建 如果我们想基于spring mvc 提供一个简单的API查询服务,传统的方式,首先需要我们引入sp ...

随机推荐

  1. df -h命令卡住 怎么办

    df -h命令卡住 命令行输入df -h却发现一直卡在那里,有可能是挂载出了问题. 这种问题,大概率是由于 mount 的目录被删除了,但是没有提前执行 umount 操作,因此报错! 解决方法: 1 ...

  2. windows oracle11gR2安装使用

    安装 plsql安装 https://blog.csdn.net/li66934791/article/details/83856225 配置tns # tnsnames.ora Network Co ...

  3. Java三大特性 封装、继承、多态

    封装 概念: 封装指的是将类的某些信息隐藏在类内部,不允许外部程序直接访问,只能通过该类提供的方法来实现对隐藏信息的操作和访问. 封装实现的步骤: 1.修改属性的可见性来显示属性的访问,一般设为pri ...

  4. 【工具】Zotero|使用Zotero向Word/WPS中插入引用文献(2023年)

    版本:Word 2021,Zotero 6.0.30 版本:WPS 教育版,HUST 前言:两年前我找网上插入文献的方式,网上的博客提示让我去官网下个插件然后才能装,非常麻烦,导致我对Zotero都产 ...

  5. 【记录】Excel 2021|(一)VBA操作Excel入门,在姓和名之间打空格,如“ZhangSanSan“改成“Zhang SanSan“

    版本:Excel 2021. 打开VBA界面:快捷键Alt+F11.(或视图-宏). 录制宏,再看看宏的代码,就可以基本了解常规的Excel操作在VBA里是什么德行. 比方说,我需要对一个选区进行修改 ...

  6. java处理http请求之Apache httpClient入门教程

    说明 本文示例代码基于 4.5.13 版本 转载请注明出处:https://www.cnblogs.com/qnlcy/p/15378446.html 一.项目介绍 Apache 提供用来做http请 ...

  7. Java实现minio上传文件加解密操作

    一.背景与需求 在云存储场景中,数据安全是核心需求之一.MinIO作为高性能对象存储服务,支持通过客户端加密(CSE)在数据上传前完成加密,确保即使存储服务器被攻破,攻击者也无法获取明文数据.本文将详 ...

  8. 鸿蒙Next复杂列表性能优化:让滑动体验如丝般顺滑

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  9. RPC实战与核心原理之安全体系

    安全体系:如何建立可靠的安全体系? 回顾 异步化".调用方利用异步化机制实现并行调用多个服务,以缩短整个调用时间:而服务提供方则可以利用异步化把业务逻辑放到自定义线程池里面去执行,以提升单机 ...

  10. VcXsrv鼠标不显示问题解决方法

    问题描述 在windows10上通过WSL2安装了ubuntu22.04的系统,在通过VcXsrv使用界面显示的时候,显示界面不显示鼠标指针. 我是通过步骤四直接解决的. 解决方法 1. 检查VcXs ...