Skip to content

Latest commit

 

History

History
375 lines (297 loc) · 9.79 KB

File metadata and controls

375 lines (297 loc) · 9.79 KB

Java MCP Server 开发指南

概述

Model Context Protocol (MCP) 是一个标准化协议,用于连接AI助手和各种工具及数据源。本文档提供了使用Spring AI开发Java MCP Server的完整指南。

核心概念

MCP Server

MCP Server是一个服务端应用,向MCP客户端(如AI助手)提供工具、资源和功能。它通过标准化的协议接口与客户端通信。

Transport Modes

  • 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

工具开发规范

工具注册方式

方式一:使用 @Tool 注解

@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);
    }
}

方式二:使用 ToolCallbackProvider

@Configuration
public class ToolConfiguration {
    
    @Bean
    public ToolCallbackProvider weatherToolProvider(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder()
            .toolObjects(weatherService)
            .build();
    }
}

工具开发最佳实践

1. 参数验证

@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);
}

2. 错误处理

@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());
    }
}

3. 异步处理

@Tool("大数据处理工具")
public Mono<ProcessingResult> processLargeDataset(
    @Parameter(description = "数据集ID") String datasetId) {
    
    return dataProcessingService
        .processAsync(datasetId)
        .timeout(Duration.ofMinutes(5))
        .onErrorReturn(ProcessingResult.failed("处理超时或失败"));
}

开发规则与约定

命名规范

  • 工具名称: 使用动词+名词格式,如 getWeatherForecastexecuteQuery
  • 参数名称: 使用驼峰命名法,如 userIdstartDate
  • 描述文本: 使用中文或英文,保持一致性

安全规范

  1. 输入验证: 所有外部输入必须进行验证
  2. 权限控制: 敏感操作需要权限验证
  3. 数据脱敏: 返回敏感数据时进行脱敏处理
  4. 日志记录: 记录关键操作和异常信息

性能规范

  1. 响应时间: 工具执行时间不应超过30秒
  2. 资源限制: 单次操作内存使用不超过100MB
  3. 并发控制: 使用线程池控制并发访问
  4. 缓存策略: 对频繁访问的数据进行缓存

配置管理

环境配置

@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();
    }
}

部署与运维

Docker部署

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>

故障排查

常见问题

  1. 工具未注册: 检查@Tool注解和Bean配置
  2. 连接失败: 验证传输模式和端口配置
  3. 性能问题: 检查线程池配置和资源使用
  4. 安全问题: 审查输入验证和权限控制

调试技巧

  • 启用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) {
        // 动态工具注册逻辑
    }
}

最佳实践总结

  1. 模块化设计: 将不同功能的工具分组管理
  2. 配置外部化: 使用配置文件管理环境相关参数
  3. 监控完善: 建立完整的监控和告警体系
  4. 文档维护: 保持API文档和用户手册的更新
  5. 版本控制: 采用语义化版本控制策略

通过遵循本指南,您可以开发出高质量、可维护的Java MCP Server应用。