Java使用spring Ai集成的mcp开发自己的mcp
什么是模型上下文协议(MCP)?
MCP(模型上下文协议)是一个开源标准,用于将人工智能应用与外部系统连接起来。通过MCP,像Claude或ChatGPT这样的AI应用可以连接数据源(如本地文件、数据库)、工具(如搜索引擎、计算器)和工作流程(如专门提示)——使它们能够访问关键信息并执行任务。可以把MCP想象成AI应用的USB-C接口。正如USB-C提供了连接电子设备的标准化方式,MCP也提供了将AI应用与外部系统连接的标准化方式。

MCP的文档:What is the Model Context Protocol (MCP)? - Model Context Protocol
开始构建自己的MCP
选择SDK
MCP提供了多种编程语言Q的支持,如Python,Java,Kotin,TypeScrip等。这里以Java SDK为例
SDK主要关注
-
Stdio(标准输入输出)
-
主要是用于本地部署,不需要联网,大模型也在本地直接访问
-
-
WebMVC
-
基于 Spring MVC
-
同步阻塞
-
一个连接占一个线程,主要用于小规模,自用,默认使用Tomcat连接,默认的连接池数量足够使用
-
-
WebFlux
-
异步非阻塞模型(Spring WebFlux,基于 Netty)
-
少量线程处理大量请求
-
主要用于高并发,对外使用
-
其他语言的SDK:SDKs - Model Context Protocol
多种传输方式
MCP(Model Control Protocol)提供的4 种传输方式,本质只有 2 大类:
1. Stdio(标准输入输出)
= 本地进程内通信
= 像命令行一样,程序之间直接传数据
= 不走网络
= 最快、最简单、最安全
2. SSE(Server-Sent Events)
= 网络通信
= 服务器 → 客户端单向实时推消息
= 走 HTTP 长连接
= 分 3 种实现:HTTP、WebMVC、WebFlux
4 种传输方式到底是什么?(超级大白话)
1)Stdio(默认)
-
不联网
-
两个程序在同一台机器上,直接互相读写数据
-
像你在终端输入命令,程序直接输出结果
-
最快、最轻量
-
适合:本地工具、本地插件
2)HTTP SSE(基础版)
-
最原始的 SSE
-
简单 HTTP 长连接
-
不依赖任何框架
-
适合:简单场景、轻量服务
3)WebMVC SSE(传统 Spring)
-
基于 Spring MVC
-
同步阻塞
-
一个连接占一个线程
-
适合:传统企业项目、并发不高的场景
4)WebFlux SSE(响应式 Spring)
-
基于 Spring WebFlux
-
异步非阻塞
-
少量线程可以支持海量连接
-
适合:高并发、实时推送、微服务
最核心区别
| 传输 | 类型 | 线程模型 | 依赖 | 适合场景 |
|---|---|---|---|---|
| Stdio | 本地进程通信 | 无 | 无 | 本地、最快 |
| HTTP SSE | 网络长连接 | 简单 | 无框架 | 简单服务 |
| WebMVC SSE | 网络长连接 | 同步阻塞 | Spring MVC | 传统项目 |
| WebFlux SSE | 网络长连接 | 异步非阻塞 | Spring WebFlux | 高并发 |
其他语言(比如 Python)也是这样区分吗?
✅ 答案:思想完全一样!只是名字不同!
不管是 Java、Python、Go、JS,MCP 传输永远分两大类:
1)本地通信:Stdio(所有语言都支持)
-
Python:
subprocess+ stdin/stdout -
Go:os.Stdin, os.Stdout
-
JS:process.stdin / process.stdout
2)网络通信:SSE(所有语言都能实现)
Python 里对应的是:
-
Flask SSE
-
Django SSE
-
FastAPI SSE(异步,对应 WebFlux)
-
普通 HTTP SSE(对应 WebMVC)
案例
MCP服务器
依赖
基本环境
jdk17
maven3.9.4
springboot3.3.0
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.1.0-M1-PLATFORM-2</version>
</dependency>
注意:如果使用其他的版本例如1.1.0.RC1等,依赖自己会自动扫描注入工具,我们手写的Bean也会注入,会导致有一份注入失败,但是不影响,只是在日志会有告警
org.springaicommunity.mcp...AsyncMcpToolProvider → No tool methods found: [] ← 社区版(空的)
o.springframework.ai.mcp...McpServerAutoConfiguration → Registered tools: 2 ← 官方版(有工具)
application.yml配置文件:
server:
port: 8888
spring:
application:
name: foss-mcp-server
ai:
mcp:
server:
name: foss-mcp-server
version: 1.0.0
type: ASYNC
sse-message-endpoint: /message
sse-endpoint: /sse
logging:
level:
root: INFO
org.springframework.ai.mcp: DEBUG
org.springframework.web.servlet: DEBUG
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: DEBUG
org.springframework.web.servlet.mvc.method.annotation.SseEmitter: TRACE
com.cctv.mcp: DEBUG
config配置文件:作用就是将工具注入
import com.cctv.mcp.service.impl.FossBucketService;
import com.cctv.mcp.service.impl.FossMonitoringStorage;
import com.cctv.mcp.service.impl.FossObjectService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ToolConfig {
@Bean
public ToolCallbackProvider toolCallbackProvider(
FossObjectService fossObjectService) {
System.out.println(">>> [CONFIG] ToolCallbackProvider 正在初始化...");
System.out.println(">>> [CONFIG] FossService 实例:" + fossObjectService);
// 👇 添加这行日志,确认服务启动时拿到了对象
System.out.println(">>> [DEBUG] FossService injected: " + fossObjectService.getClass().getName());
// 👇 手动检查一下类里有没有 @Tool 注解的方法 (可选高级调试)
java.lang.reflect.Method[] methods = fossObjectService.getClass().getDeclaredMethods();
for (java.lang.reflect.Method m : methods) {
if (m.isAnnotationPresent(org.springframework.ai.tool.annotation.Tool.class)) {
System.out.println(">>> [DEBUG] Found @Tool method: " + m.getName());
} else {
// 看看是不是因为导包错导致注解不匹配
for (var ann : m.getAnnotations()) {
if (ann.annotationType().getSimpleName().equals("Tool")) {
System.out.println(">>> [DEBUG] Found a 'Tool' annotation but class mismatch: " + ann.annotationType().getName());
}
}
}
}
return MethodToolCallbackProvider.builder()
.toolObjects(
fossObjectService) // 注入你的工具类
.build();
}
}
具体的工具类:
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
@Component
public class FossObjectService {
@Tool(name = "hello", description = "搜索开源软件信息") // 显式指定 name
public String hello(String name) {
System.out.println(">>> [TOOL EXEC] 收到工具调用!参数 name=" + name);
try {
Thread.sleep(100); // 模拟一点点耗时
} catch (InterruptedException e) {}
String result = "Hello, " + name + "! 调用成功啦!";
System.out.println(">>> [TOOL EXEC] 执行完毕,返回:" + result);
return result;
}
}
方法的返回类型:推荐是string字符串
mcp支持 ① Text(文本) — 最常用 ② Image(图片) — 返回图片数据 ③ Resource(资源) — 返回文件/资源引用
启动类:springboot的启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class McpServerSpringAiApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerSpringAiApplication.class, args);
}
}
到此为止就可以开启服务测试工具了,测试有多种方法:使用postman直接测试接口,使用mcp客户端测试,使用现成的桌面工具如Claude Desktop、Cursor Q等来引入我们的MCP Server。我自己试了下Claude,但是Claude需要国外的手机号注册。
所以本次使用手动编写MCP客户端代码测试
依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
<version>1.1.2</version>
</dependency>
测试类:
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import java.time.Duration;
import java.util.List;
import java.util.Map;
public class McpTestClient {
public static void main(String[] args) throws Exception {
// ✅ 用 builder 创建
HttpClientSseClientTransport transport = HttpClientSseClientTransport
.builder("http://localhost:8888")
.sseEndpoint("/sse")
.build();
// 2. 创建客户端(自动完成 initialize 握手)
var client = McpClient.sync(transport)
.requestTimeout(Duration.ofSeconds(30))
.build();
client.initialize();
System.out.println("=== 连接成功 ===\n");
// 3. 查看所有工具
List<McpSchema.Tool> tools = client.listTools().tools();
System.out.println("=== 可用工具 ===");
for (McpSchema.Tool tool : tools) {
System.out.println("工具名: " + tool.name());
System.out.println("描述: " + tool.description());
System.out.println("参数: " + tool.inputSchema());
System.out.println("---");
}
// 4. 调用工具(不需要大模型,直接传参)
System.out.println("\n=== 调用 hello 工具 ===");
McpSchema.CallToolResult result = client.callTool(
new McpSchema.CallToolRequest("hello",
Map.of("name", "张三"))
);
// 5. 打印返回结果
for (McpSchema.Content content : result.content()) {
System.out.println("返回内容: " + content);
}
// 7. 关闭连接
client.close();
}
}
然后直接启动服务,运行MCP客户端的main方法即可!
更多推荐

所有评论(0)