- Published on
Java MCP SDK实战指南: 构建大模型与外部工具的智能连接
- Authors
- Name
- Shoukai Huang

Code in Java(Photo by Iewek Gnos on Unsplash) )
概述
Model Context Protocol (MCP) 作为连接大型语言模型与外部工具的标准协议,正在改变AI应用的开发方式。在当前的AI应用开发中,如何让大型语言模型安全、高效地与外部系统交互是一个关键挑战。Java MCP SDK提供了一套完整的解决方案,使开发者能够快速构建安全、可扩展的AI应用。
本文将详细介绍Java MCP SDK的核心概念、实现原理和实战应用,并通过一个完整的天气服务API示例,帮助开发者快速掌握这一强大工具。
预期目标
在开始介绍 Spring MCP 应用和示例之前,让我们先明确一下本文目标:
- 回顾 Model Context Protocol (MCP) 的核心概念和价值
- 掌握 Spring MCP 的核心功能和实战应用方法
- 讲述如何使用 Spring MCP 创建和注册自定义工具
- 实现一个简单但实用的天气服务 API 示例应用
通过本文的学习,你将能够使用 Spring MCP 构建连接大型语言模型与外部工具的应用,为 AI 赋能更多实用功能。
1. 基础研究
1.1 背景知识
Model Context Protocol (MCP) 是一个专为大型语言模型(LLMs)设计的开放协议,旨在解决 AI 模型与外部工具、数据源交互的挑战。在 AI 应用快速发展的今天,LLMs 如 GPT-4、Claude 等已经展现出强大的语言理解和生成能力,但它们仍然面临一个核心限制:无法直接访问外部世界的数据和工具。
MCP 正是为解决这一问题而生。它提供了一个标准化的接口,使 LLMs 能够:
- 调用外部工具和函数
- 访问实时或特定领域的数据
- 执行复杂的任务流程
在 MCP 出现之前,开发者通常需要使用自定义的、非标准化的方法来实现 AI 与外部系统的交互,这导致了集成复杂、维护困难、兼容性差等问题。MCP 通过定义统一的协议标准,极大地简化了这一过程。
1.2 核心概念
MCP 协议围绕三个核心功能构建:
- 工具(Tools):可被 LLM 调用的函数,通常需要用户批准。例如,发送电子邮件、查询数据库或控制智能家居设备等。
- 资源(Resources):可被客户端读取的类文件数据,如 API 响应、文件内容或数据库查询结果。
- 提示词(Prompts):帮助用户完成特定任务的预设模板,可以指导 LLM 生成更符合特定需求的回答。
Spring MCP 是 Spring AI 项目的一部分,它提供了 MCP 协议的 Spring 集成实现。作为一个长期使用 Spring 的开发者,我发现它的以下特点特别实用:
- 完整的 MCP 规范实现,无需担心协议兼容性问题
- 支持同步和异步 API,适应不同的应用场景
- 多种传输实现:进程间通信(STDIO)、HTTP SSE 流式传输等
- 与 Spring Boot 和 Spring WebFlux/WebMVC 的无缝集成
- 基于注解的工具定义和注册机制,极大简化了开发流程
这些特性使我们能够在几天内就将 AI 能力集成到现有的 Spring 应用中,而不是传统方式需要的数周时间。
2. 理论学习
2.1 Spring MCP 详解
Spring MCP 是 Spring AI 项目的一部分,提供了 MCP 协议与 Spring 生态系统的无缝集成。它基于 MCP Java SDK 的三层架构,并提供了简化的 Spring Boot Starter。
1. 服务器类型
Spring MCP 提供了两种类型的服务器实现:
McpSyncServer
:同步服务器,默认类型,适用于简单的请求-响应模式。通过设置spring.ai.mcp.server.type=SYNC
启用。McpAsyncServer
:异步服务器,优化了非阻塞操作,集成了 Project Reactor 支持。通过设置spring.ai.mcp.server.type=ASYNC
启用。
2. 传输选项
Spring MCP 支持三种传输机制,每种都有专用的 starter:
标准输入/输出 (STDIO)
:使用spring-ai-starter-mcp-server
,适用于进程间通信。Spring MVC (SSE)
:使用spring-ai-starter-mcp-server-webmvc
,基于 Servlet 的 HTTP 流式传输。Spring WebFlux (Reactive SSE)
:使用spring-ai-starter-mcp-server-webflux
,基于响应式 HTTP 流式传输。
3. 工具注解与集成
Spring MCP 提供了简化的工具定义和注册方式:
@Tool
:来自org.springframework.ai.tool.annotation
包的注解,用于标记方法作为 MCP 工具,包含描述和参数信息。ToolCallbacks
:用于将带有 @Tool 注解的类转换为回调函数。McpToolUtils
:工具类,提供toSyncToolSpecifications
和toAsyncToolSpecifications
方法,将回调函数转换为相应的 MCP 工具规范。
4. 功能和能力
Spring MCP 服务器提供了一系列强大的功能,使开发者能够构建完整的 AI 交互系统:
- 工具支持:允许服务器安全地暴露工具供大语言模型调用,并支持工具列表变更通知。
- 资源管理:提供基于 URI 的资源访问机制,包括资源发现、访问和变更通知。
- 提示词管理:实现高级提示词模板和处理机制,使 AI 生成更符合需求的内容。
- 结构化日志:支持详细的日志记录和通知机制,便于调试和监控。
这些功能共同工作,为开发者提供了一个完整的框架,用于构建与大型语言模型交互的应用程序。
5. Spring Boot 无缝集成
Spring MCP 与 Spring Boot 生态系统实现了无缝集成,提供了多种便捷的开发特性:
- 自动配置机制:利用 Spring Boot 的自动配置功能,大大简化了服务器设置和配置过程。
- 灵活的属性配置:通过
spring.ai.mcp.server.*
前缀的属性,支持在应用属性文件中进行细粒度配置。 - 条件化组件注册:使用
@ConditionalOnProperty
注解,根据配置自动启用或禁用不同的传输层和功能组件。 - Spring 生态系统兼容:与 Spring 的其他组件(如 Spring Security、Spring Data 等)完全兼容,实现了综合集成。
这种深度集成使开发者可以快速将 MCP 功能添加到现有的 Spring Boot 应用中,最大限度地减少了开发和配置工作。
2.2 实现原理
Spring MCP 的实现基于 MCP Java SDK 的三层架构,并与 Spring 框架无缝集成。以下是其核心实现原理:
2.2.1 三层架构
MCP Java SDK 遵循三层架构:
- 客户端/服务器层:
McpClient
处理客户端操作,而McpServer
管理服务器端协议操作。两者都使用McpSession
进行通信管理。 - 会话层:通过
DefaultMcpSession
实现管理通信模式和状态。 - 传输层:处理 JSON-RPC 消息的序列化和反序列化,支持多种传输实现。

2.2.2 同步与异步服务器实现
Spring MCP 提供了两种服务器实现:
- 同步服务器 (McpSyncServer):
- 适用于简单的请求-响应模式
- 使用同步工具规范,由
McpToolUtils.toSyncToolSpecifications()
创建 - 适合大多数标准应用场景
- 异步服务器 (McpAsyncServer):
- 优化了非阻塞操作
- 使用异步工具规范,由
McpToolUtils.toAsyncToolSpecifications()
创建 - 集成了 Project Reactor 支持
- 适用于高并发和响应式应用
2.2.3 工具注册与调用流程
在 Spring MCP 中,工具的注册和调用流程如下:
- 使用
@Tool
注解标记服务类中的方法,包含描述和参数信息 - 将带有注解的服务类注册为 Spring Bean
- 通过
ToolCallbacks.from()
将带有注解的服务类转换为回调函数 - 根据服务器类型,使用
McpToolUtils.toSyncToolSpecifications()
或toAsyncToolSpecifications()
将回调函数转换为相应的 MCP 工具规范 - 将工具规范注册到 MCP 服务器中
- 当客户端请求调用工具时,服务器执行相应的回调函数并返回结果
2.2.4 传输实现
Spring MCP 支持多种传输实现,每种都有不同的特点和用例:
STDIO
:适用于进程间通信,如与命令行工具集成WebMVC SSE
:基于 Servlet 的 HTTP 流式传输,适用于传统 Spring MVC 应用WebFlux SSE
:基于响应式编程的 HTTP 流式传输,适用于高并发应用
2.2.5 通信流程
工具调用流程
当客户端请求调用工具时,流程如下:
- 服务器执行工具,并将结果返回给客户端
- 客户端接收结果并处理
资源访问流程
当客户端请求访问资源时,流程如下:
- 客户端发送资源请求,包含资源标识符
- 服务器接收请求并验证
- 服务器在
ResourceRegistry
中查找对应的资源 - 服务器读取资源内容,并将其返回给客户端
- 客户端接收资源内容并处理
2.2.6 错误处理机制
SDK 实现了完善的错误处理机制,包括:
- 请求验证错误
- 工具执行错误
- 资源访问错误
- 网络通信错误
这些错误会被适当地封装和传递,确保客户端能够正确处理异常情况。
3. 实践操作
3.1 环境搭建
要开始使用 Spring MCP首先需要设置项目依赖。以下是使用 Maven 的配置示例:
<!-- 添加核心依赖 -->
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Spring AI MCP -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Model Context Protocol -->
<dependency>
<groupId>io.modelcontextprotocol</groupId>
<artifactId>mcp-spec</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>io.modelcontextprotocol</groupId>
<artifactId>mcp-server</artifactId>
<version>0.7.0</version>
</dependency>
</dependencies>
版本控制
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-bom</artifactId>
<version>>0.8.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
由于目前 Spring AI 和 Spring MCP 版本仅为里程碑版本,还需要加:Spring Milestones
的repository 配置;
3.2 示例项目:天气服务 API
现在,我们将实现一个简单但实用的示例项目:一个允许 LLM 访问天气数据的 MCP 服务。这个服务将提供两个主要功能:
- 获取指定经纬度位置的天气预报
- 获取指定州的天气警报
项目结构
步骤 1:创建主应用类
package com.apframework.sample.mcp.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
}
步骤 2:配置 MCP 服务器
package com.apframework.sample.mcp.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpServerTransportProvider;
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallbacks;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
@Configuration
public class McpServerConfig {
// STDIO transport
@Bean
@ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "stdio")
public StdioServerTransportProvider stdioServerTransportProvider() {
return new StdioServerTransportProvider();
}
// SSE transport
@Bean
@ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse")
public WebFluxSseServerTransportProvider sseServerTransportProvider() {
return new WebFluxSseServerTransportProvider(new ObjectMapper(), "/mcp/message");
}
// Router function for SSE transport used by Spring WebFlux to start an HTTP
// server.
@Bean
@ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse")
public RouterFunction<?> mcpRouterFunction(WebFluxSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
@Bean
public WeatherApiClient weatherApiClient() {
return new WeatherApiClient();
}
@Bean
public McpSyncServer mcpServer(McpServerTransportProvider transportProvider, WeatherApiClient weatherApiClient) {
// Configure server capabilities with resource support
var capabilities = McpSchema.ServerCapabilities.builder()
.tools(true) // Tool support with list changes notifications
.logging() // Logging support
.build();
// Create the server with both tool and resource capabilities
McpSyncServer server = McpServer.sync(transportProvider)
.serverInfo("MCP Demo Weather Server", "1.0.0")
.capabilities(capabilities)
.tools(McpToolUtils.toSyncToolSpecifications(ToolCallbacks.from(weatherApiClient))) // Add @Tools
.build();
return server;
}
}
步骤 3:创建天气 API 客户端
package com.apframework.sample.mcp.server;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
public class WeatherApiClient {
private static final String BASE_URL = "<https://api.weather.gov>";
private final RestClient restClient;
public WeatherApiClient() {
this.restClient = RestClient.builder()
.baseUrl(BASE_URL)
.defaultHeader("Accept", "application/geo+json")
.defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)")
.build();
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Points(@JsonProperty("properties") Props properties) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Props(@JsonProperty("forecast") String forecast) {
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Forecast(@JsonProperty("properties") Props properties) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Props(@JsonProperty("periods") List<Period> periods) {
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Period(@JsonProperty("number") Integer number, @JsonProperty("name") String name,
@JsonProperty("startTime") String startTime, @JsonProperty("endTime") String endTime,
@JsonProperty("isDaytime") Boolean isDayTime, @JsonProperty("temperature") Integer temperature,
@JsonProperty("temperatureUnit") String temperatureUnit,
@JsonProperty("temperatureTrend") String temperatureTrend,
@JsonProperty("probabilityOfPrecipitation") Map probabilityOfPrecipitation,
@JsonProperty("windSpeed") String windSpeed, @JsonProperty("windDirection") String windDirection,
@JsonProperty("icon") String icon, @JsonProperty("shortForecast") String shortForecast,
@JsonProperty("detailedForecast") String detailedForecast) {
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Alert(@JsonProperty("features") List<Feature> features) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Feature(@JsonProperty("properties") Properties properties) {
public String toText() {
return String.format("""
Event: %s
Area: %s
Severity: %s
Description: %s
Instructions: %s
""", properties.event(), properties.areaDesc(), properties.severity(), properties.description(),
properties.instruction());
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Properties(@JsonProperty("event") String event, @JsonProperty("areaDesc") String areaDesc,
@JsonProperty("severity") String severity, @JsonProperty("description") String description,
@JsonProperty("instruction") String instruction) {
}
}
/**
* 获取指定纬度/经度的天气预报
* @param latitude 纬度
* @param longitude 经度
* @return 给定位置的天气预报
* @throws RestClientException 如果请求失败
*/
@Tool(description = "Get weather forecast for a specific latitude/longitude")
public String getWeatherForecastByLocation(double latitude, double longitude) {
var points = restClient.get()
.uri("/points/{latitude},{longitude}", latitude, longitude)
.retrieve()
.body(Points.class);
var forecast = restClient.get().uri(points.properties().forecast()).retrieve().body(Forecast.class);
String forecastText = forecast.properties().periods().stream().map(p -> {
return String.format("""
%s:
Temperature: %s %s
Wind: %s %s
Forecast: %s
""", p.name(), p.temperature(), p.temperatureUnit(), p.windSpeed(), p.windDirection(),
p.detailedForecast());
}).collect(Collectors.joining());
return forecastText;
}
/**
* 获取特定地区的天气警报
* @param state 地区代码。两字母美国州代码(例如 CA, NY)
* @return 人类可读的警报信息
* @throws RestClientException 如果请求失败
*/
@Tool(description = "Get weather alerts for a US state. Input is Two-letter US state code (e.g. CA, NY)")
public String getAlerts(String state) {
Alert alert = restClient.get().uri("/alerts/active/area/{state}", state).retrieve().body(Alert.class);
return alert.features().stream().map(f -> f.toText()).collect(Collectors.joining("\\n"));
}
步骤 4:配置应用属性
# 选择传输模式:SSE 或 STDIO
transport.mode=sse
步骤 5:启动运行
步骤 6:测试与验证
现在我们已经实现了 MCP 文件系统服务,接下来可以测试它的功能。
使用 inspector(Visual testing tool for MCP servers) 工具进行测试
选择 getWeatherForecastByLocation 工具
格式化后,可以看到已经获取到天气数据
4. 扩展探索
在掌握了 Java MCP SDK 的基础用法后,你可以考虑以下扩展方向:
高级功能实现
- 文件内容读取:在一个法律文档分析项目中,我们扩展了 MCP 服务,添加了读取文件内容的功能,使 LLM 能够直接分析合同文本。这将处理效率提高了 3 倍,并减少了人工审核的需求。
- 文件操作:在另一个内容管理系统中,我们实现了创建、修改、删除文件的工具,使 LLM 能够根据用户需求自动生成和组织文档。这里的关键经验是:始终实现严格的权限检查和操作确认机制。
- 权限控制:在处理敏感数据时,我们开发了基于角色的访问控制系统,确保 AI 只能在授权范围内操作。这是企业级应用的必备功能。
与其他系统集成
数据库集成:之前文章分享过,创建连接到数据库的 MCP 工具,使 AI 能够安全地查询客户数据。关键是使用参数化查询防止 SQL 注入,并限制查询范围。
API 集成:在一个市场分析工具中,我们通过 MCP 集成了多个外部 API,使 AI 能够获取实时市场数据。
IoT 设备控制:在智能工厂项目中,通过 MCP 工具连接到生产线设备,使 AI 能够监控和优化生产流程。这里的挑战是确保操作安全性和实时性。
生产环境优化
- 性能优化:针对大规模文件系统或高并发场景,优化服务性能。
- 安全加固:实现更严格的安全措施,防止未授权访问或恶意操作。
- 监控和日志:添加详细的监控和日志记录,便于问题排查和性能分析。
5. 资源获取
要深入学习 Java MCP SDK,以下资源将非常有用:
官方文档与资源
- Java MCP SDK 源码:GitHub 上的官方源码库
- MCP 技术文档:中文技术文档
- Model Context Protocol 官方文档:英文官方文档
社区资源
- GitHub Issues:在 SDK 的 GitHub 仓库中提问和讨论
- Stack Overflow:使用
model-context-protocol
和java
标签搜索相关问题
相关项目
- Spring AI:Spring 的 AI 集成项目,与 MCP SDK 有良好的集成
- Python MCP SDK:Python 版本的 MCP SDK,如果你同时使用 Python 和 Java
总结
本文深入介绍了 Java MCP SDK 的核心概念、实现原理和实际应用。通过构建本地文件系统访问服务的示例,我们展示了如何使用 MCP 协议连接大型语言模型与外部工具和数据源。
MCP 作为一个开放的标准协议,正在改变 AI 应用的开发方式,使 LLM 能够更加灵活地与外部世界交互。Java MCP SDK 则为 Java 开发者提供了便捷的工具,使他们能够轻松地将 MCP 功能集成到现有的 Java 应用中。
随着 AI 技术的不断发展,MCP 协议和相关 SDK 也将持续演进,为开发者提供更多功能和更好的性能。希望本文能够帮助你了解 Java MCP SDK 的基础知识,并在实际项目中应用这一强大的工具。