Model Context Protocol (MCP) 是一个标准化协议,用于连接AI助手和各种工具及数据源。本文档提供了使用Spring AI开发Java MCP Server的完整指南。
MCP Server是一个服务端应用,向MCP客户端(如AI助手)提供工具、资源和功能。它通过标准化的协议接口与客户端通信。
- STDIO: 标准输入输出传输,适用于命令行工具
- SSE (Server-Sent Events): HTTP流式传输,支持Web应用
- WebFlux: 响应式编程模型,支持高并发场景
在 pom.xml 中添加必要依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>application.properties:
# MCP Server基础配置
spring.ai.mcp.server.enabled=true
spring.ai.mcp.server.name=my-mcp-server
spring.ai.mcp.server.type=ASYNC
# 传输模式配置
spring.ai.mcp.server.stdio=true
# 服务器能力配置
spring.ai.mcp.server.capabilities.tools=true
spring.ai.mcp.server.capabilities.resources=true
spring.ai.mcp.server.capabilities.prompts=true
spring.ai.mcp.server.capabilities.completions=true
# 日志配置
logging.level.org.springframework.ai.mcp=DEBUG@Component
public class WeatherService {
@Tool("获取指定坐标的天气预报")
public WeatherForecast getWeatherForecast(
@Parameter(description = "纬度") double latitude,
@Parameter(description = "经度") double longitude) {
// 实现天气预报逻辑
return weatherApiClient.getForecast(latitude, longitude);
}
@Tool("获取美国各州的天气警报")
public List<WeatherAlert> getWeatherAlerts(
@Parameter(description = "美国州名") String state) {
// 实现天气警报逻辑
return weatherApiClient.getAlerts(state);
}
}@Configuration
public class ToolConfiguration {
@Bean
public ToolCallbackProvider weatherToolProvider(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}@Tool("数据库查询工具")
public QueryResult executeQuery(@Parameter(description = "SQL查询语句") String sql) {
// 参数验证
if (sql == null || sql.trim().isEmpty()) {
throw new IllegalArgumentException("SQL查询语句不能为空");
}
// SQL注入防护
if (containsSqlInjection(sql)) {
throw new SecurityException("检测到潜在的SQL注入攻击");
}
// 执行查询
return databaseService.executeQuery(sql);
}
private boolean containsSqlInjection(String sql) {
String[] dangerousKeywords = {"DROP", "DELETE", "UPDATE", "INSERT", "ALTER"};
String upperSql = sql.toUpperCase();
return Arrays.stream(dangerousKeywords).anyMatch(upperSql::contains);
}@Tool("文件读取工具")
public FileContent readFile(@Parameter(description = "文件路径") String filePath) {
try {
// 路径验证
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
return FileContent.error("文件不存在: " + filePath);
}
// 权限检查
if (!Files.isReadable(path)) {
return FileContent.error("文件不可读: " + filePath);
}
String content = Files.readString(path);
return FileContent.success(content);
} catch (IOException e) {
return FileContent.error("读取文件失败: " + e.getMessage());
}
}@Tool("大数据处理工具")
public Mono<ProcessingResult> processLargeDataset(
@Parameter(description = "数据集ID") String datasetId) {
return dataProcessingService
.processAsync(datasetId)
.timeout(Duration.ofMinutes(5))
.onErrorReturn(ProcessingResult.failed("处理超时或失败"));
}- 工具名称: 使用动词+名词格式,如
getWeatherForecast、executeQuery - 参数名称: 使用驼峰命名法,如
userId、startDate - 描述文本: 使用中文或英文,保持一致性
- 输入验证: 所有外部输入必须进行验证
- 权限控制: 敏感操作需要权限验证
- 数据脱敏: 返回敏感数据时进行脱敏处理
- 日志记录: 记录关键操作和异常信息
- 响应时间: 工具执行时间不应超过30秒
- 资源限制: 单次操作内存使用不超过100MB
- 并发控制: 使用线程池控制并发访问
- 缓存策略: 对频繁访问的数据进行缓存
@Configuration
@EnableConfigurationProperties(McpServerProperties.class)
public class McpServerConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.ai.mcp.server.type", havingValue = "ASYNC")
public ReactiveToolProcessor asyncToolProcessor() {
return new ReactiveToolProcessor();
}
@Bean
@ConditionalOnProperty(name = "spring.ai.mcp.server.type", havingValue = "SYNC", matchIfMissing = true)
public SyncToolProcessor syncToolProcessor() {
return new SyncToolProcessor();
}
}@ConfigurationProperties(prefix = "app.mcp")
@Data
public class McpServerProperties {
private String serverName = "default-mcp-server";
private int maxConnections = 100;
private Duration requestTimeout = Duration.ofSeconds(30);
private boolean enableMetrics = true;
}@ExtendWith(MockitoExtension.class)
class WeatherServiceTest {
@Mock
private WeatherApiClient weatherApiClient;
@InjectMocks
private WeatherService weatherService;
@Test
void testGetWeatherForecast() {
// Given
double latitude = 39.9042;
double longitude = 116.4074;
WeatherForecast expected = new WeatherForecast("晴天", 25);
when(weatherApiClient.getForecast(latitude, longitude))
.thenReturn(expected);
// When
WeatherForecast result = weatherService.getWeatherForecast(latitude, longitude);
// Then
assertThat(result).isEqualTo(expected);
}
}@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class McpServerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@LocalServerPort
private int port;
@Test
void testToolExecution() {
// 测试工具执行
String url = "http://localhost:" + port + "/mcp/tools/getWeatherForecast";
ToolRequest request = new ToolRequest();
request.setLatitude(39.9042);
request.setLongitude(116.4074);
ResponseEntity<ToolResponse> response = restTemplate.postForEntity(
url, request, ToolResponse.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
}
}FROM openjdk:17-jre-slim
COPY target/mcp-server.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
export:
prometheus:
enabled: true<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.ai.mcp" level="DEBUG"/>
<logger name="com.yourcompany.mcp" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>- 工具未注册: 检查@Tool注解和Bean配置
- 连接失败: 验证传输模式和端口配置
- 性能问题: 检查线程池配置和资源使用
- 安全问题: 审查输入验证和权限控制
- 启用DEBUG日志查看详细信息
- 使用JProfiler或类似工具进行性能分析
- 通过Actuator端点监控应用状态
@Component
public class CustomTransportHandler implements TransportHandler {
@Override
public boolean supports(TransportType type) {
return type == TransportType.CUSTOM;
}
@Override
public void handle(McpRequest request, McpResponse response) {
// 自定义传输逻辑
}
}@Component
public class PluginManager {
@EventListener
public void onToolRegistration(ToolRegistrationEvent event) {
// 动态工具注册逻辑
}
}- 模块化设计: 将不同功能的工具分组管理
- 配置外部化: 使用配置文件管理环境相关参数
- 监控完善: 建立完整的监控和告警体系
- 文档维护: 保持API文档和用户手册的更新
- 版本控制: 采用语义化版本控制策略
通过遵循本指南,您可以开发出高质量、可维护的Java MCP Server应用。