Spring AI MCP 完全开发指南
# Spring AI MCP 完全开发指南
官方标准实现 - 基于Spring AI官方文档 (opens new window)的最佳实践
# 🏗️ 项目架构概览
graph TB
subgraph "MCP生态系统"
Client[MCP客户端<br/>spring-ai-starter-mcp-client]
Server[MCP服务器<br/>spring-ai-starter-mcp-server-webmvc]
Client -->|SSE连接| Server
Client -->|工具调用| Server
Server -->|工具响应| Client
end
subgraph "Spring AI集成"
ChatClient[ChatClient]
ToolCallbacks[ToolCallbacks]
Client --> ToolCallbacks
ToolCallbacks --> ChatClient
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 📦 1. 项目初始化
# 创建MCP服务器项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>mcp-server-demo</artifactId>
<version>1.0.0</version>
<name>MCP Server Demo</name>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.1.0-SNAPSHOT</spring-ai.version>
</properties>
<dependencies>
<!-- MCP服务器 WebMVC Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>${spring-ai.version}</version>
</dependency>
</dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 创建MCP客户端项目
<dependencies>
<!-- Web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MCP客户端 Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<!-- Spring AI Chat (可选 - 用于LLM集成) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>${spring-ai.version}</version>
</dependency>
</dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 🔧 2. MCP服务器开发
# 2.1 基础配置
# application.yml
spring:
ai:
mcp:
server:
name: demo-server
version: 1.0.0
instructions: "这是一个演示服务器,提供计算和文本处理工具"
# SSE端点配置 (自动配置,可自定义)
sse-endpoint: /sse
sse-message-endpoint: /mcp/message
# 能力配置
capabilities:
tool: true
resource: true
prompt: true
completion: true
# 服务器端口
server:
port: 9099
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 2.2 工具开发
# 方式1:使用@Tool注解 (推荐)
@Service
public class CalculatorService {
@Tool(description = "计算两个数字的和")
public double add(
@Parameter(description = "第一个数字") double a,
@Parameter(description = "第二个数字") double b) {
return a + b;
}
@Tool(description = "计算数字的幂运算")
public double power(
@Parameter(description = "底数") double base,
@Parameter(description = "指数") double exponent) {
return Math.pow(base, exponent);
}
}
@Service
public class TextService {
@Tool(description = "将文本转换为大写")
public String uppercase(@Parameter(description = "要转换的文本") String text) {
return text.toUpperCase();
}
@Tool(description = "分析文本,返回字符数、单词数等统计信息")
public String analyzeText(@Parameter(description = "要分析的文本") String text) {
long charCount = text.length();
long wordCount = text.trim().isEmpty() ? 0 : text.trim().split("\\s+").length;
long lineCount = text.split("\n").length;
return String.format("文本分析结果:\n- 字符数: %d\n- 单词数: %d\n- 行数: %d\n- 是否为空: %s\n",
charCount, wordCount, lineCount, text.trim().isEmpty() ? "是" : "否");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 方式2:ToolCallbackProvider注册
@Configuration
public class McpToolsConfiguration {
@Bean
public ToolCallbackProvider calculatorTools(CalculatorService calculatorService) {
return MethodToolCallbackProvider.builder()
.toolObjects(calculatorService)
.build();
}
@Bean
public ToolCallbackProvider textTools(TextService textService) {
return MethodToolCallbackProvider.builder()
.toolObjects(textService)
.build();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.3 启动类
@SpringBootApplication
public class McpServerDemoApplication {
private static final Logger logger = LoggerFactory.getLogger(McpServerDemoApplication.class);
public static void main(String[] args) {
SpringApplication.run(McpServerDemoApplication.class, args);
logger.info("🚀 MCP服务器已启动 - 端口: 9099");
logger.info("📡 SSE端点: http://localhost:9099/sse");
logger.info("💬 消息端点: http://localhost:9099/mcp/message");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 🔌 3. MCP客户端开发
# 3.1 基础配置
# application.yml
spring:
ai:
mcp:
client:
# 基础配置
name: demo-client
type: SYNC
request-timeout: 30s
# 工具集成 (自动启用)
toolcallback:
enabled: true
# SSE连接配置
sse:
connections:
demo-server:
url: http://localhost:9099
sse-endpoint: /sse
# 客户端端口
server:
port: 8080
# OpenAI配置 (可选)
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 3.2 自动Bean注入
@RestController
@RequestMapping("/api/mcp")
public class McpController {
// 自动注入MCP客户端
@Autowired
private List<McpSyncClient> mcpClients;
// 自动注入工具提供者
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
/**
* 获取可用工具列表
*/
@GetMapping("/tools")
public Map<String, Object> getTools() {
ToolCallback[] tools = toolCallbackProvider.getToolCallbacks();
return Map.of(
"toolCount", tools.length,
"tools", Arrays.stream(tools)
.map(tool -> Map.of(
"name", tool.getToolDefinition().name(),
"description", tool.getToolDefinition().description()
))
.toList()
);
}
/**
* 调用MCP工具
*/
@PostMapping("/call/{toolName}")
public Map<String, Object> callTool(
@PathVariable String toolName,
@RequestBody Map<String, Object> arguments) {
try {
ToolCallback[] tools = toolCallbackProvider.getToolCallbacks();
for (ToolCallback tool : tools) {
if (tool.getToolDefinition().name().equals(toolName)) {
String result = tool.call(new ObjectMapper().writeValueAsString(arguments));
return Map.of("success", true, "result", result);
}
}
return Map.of("success", false, "error", "工具不存在: " + toolName);
} catch (Exception e) {
return Map.of("success", false, "error", e.getMessage());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 3.3 与ChatClient集成
@Service
public class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient.Builder chatClientBuilder,
SyncMcpToolCallbackProvider toolCallbackProvider) {
// 自动集成MCP工具到ChatClient
this.chatClient = chatClientBuilder
.defaultToolCallbacks(toolCallbackProvider.getToolCallbacks())
.build();
}
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
@RestController
@RequestMapping("/api/chat")
public class ChatController {
@Autowired
private ChatService chatService;
@PostMapping
public Map<String, String> chat(@RequestBody Map<String, String> request) {
String response = chatService.chat(request.get("message"));
return Map.of("response", response);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 3.4 启动类
@SpringBootApplication
public class McpClientDemoApplication {
public static void main(String[] args) {
SpringApplication.run(McpClientDemoApplication.class, args);
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 🎯 4. 高级功能开发
# 4.1 自定义客户端配置
@Component
public class CustomMcpClientCustomizer implements McpSyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
// 自定义请求超时
spec.requestTimeout(Duration.ofSeconds(30));
// 工具变更通知
spec.toolsChangeConsumer(tools -> {
log.info("🔧 服务器 {} 工具列表已更新,共 {} 个工具",
serverConfigurationName, tools.size());
});
// 资源变更通知
spec.resourcesChangeConsumer(resources -> {
log.info("📁 服务器 {} 资源列表已更新,共 {} 个资源",
serverConfigurationName, resources.size());
});
// 日志处理
spec.loggingConsumer(logNotification -> {
log.info("📝 来自服务器 {} 的日志: [{}] {}",
serverConfigurationName,
logNotification.level(),
logNotification.data());
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 4.2 资源管理
@Configuration
public class McpResourcesConfiguration {
@Bean
public List<McpServerFeatures.SyncResourceSpecification> systemResources() {
var systemInfoResource = new McpSchema.Resource(
"system://info",
"系统信息",
"获取当前系统的基本信息",
"application/json"
);
var resourceSpec = new McpServerFeatures.SyncResourceSpecification(
systemInfoResource,
(exchange, request) -> {
var systemInfo = Map.of(
"timestamp", Instant.now().toString(),
"javaVersion", System.getProperty("java.version"),
"osName", System.getProperty("os.name"),
"availableProcessors", Runtime.getRuntime().availableProcessors(),
"maxMemory", Runtime.getRuntime().maxMemory()
);
String jsonContent = new ObjectMapper().writeValueAsString(systemInfo);
return new McpSchema.ReadResourceResult(List.of(
new McpSchema.TextResourceContents(request.uri(), "application/json", jsonContent)
));
}
);
return List.of(resourceSpec);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 4.3 提示模板管理
@Configuration
public class McpPromptsConfiguration {
@Bean
public List<McpServerFeatures.SyncPromptSpecification> greetingPrompts() {
var prompt = new McpSchema.Prompt(
"greeting",
"友好问候提示模板",
List.of(new McpSchema.PromptArgument("name", "要问候的姓名", true))
);
var promptSpec = new McpServerFeatures.SyncPromptSpecification(
prompt,
(exchange, request) -> {
String name = (String) request.arguments().get("name");
if (name == null) name = "朋友";
var message = new PromptMessage(
Role.USER,
new TextContent("你好 " + name + "!今天我可以为您做些什么?")
);
return new GetPromptResult("个性化问候消息", List.of(message));
}
);
return List.of(promptSpec);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 🚀 5. 部署与运行
# 5.1 本地开发
# 启动服务器
cd mcp-server-demo
mvn spring-boot:run
# 启动客户端 (新终端)
cd mcp-client-demo
mvn spring-boot:run
1
2
3
4
5
6
7
2
3
4
5
6
7
# 5.2 测试验证
# 检查工具列表
curl http://localhost:8080/api/mcp/tools
# 调用计算工具
curl -X POST http://localhost:8080/api/mcp/call/add \
-H "Content-Type: application/json" \
-d '{"a": 15, "b": 25}'
# 调用文本工具
curl -X POST http://localhost:8080/api/mcp/call/uppercase \
-H "Content-Type: application/json" \
-d '{"text": "hello world"}'
# 测试AI聊天 (如果配置了LLM)
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "请计算15加25等于多少"}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 5.3 Docker部署
Dockerfile (服务器):
FROM openjdk:17-jre-slim
COPY target/mcp-server-demo-1.0.0.jar app.jar
EXPOSE 9099
ENTRYPOINT ["java", "-jar", "/app.jar"]
1
2
3
4
2
3
4
docker-compose.yml:
version: '3.8'
services:
mcp-server:
build: ./mcp-server-demo
ports:
- "9099:9099"
environment:
- SPRING_PROFILES_ACTIVE=docker
mcp-client:
build: ./mcp-client-demo
ports:
- "8080:8080"
depends_on:
- mcp-server
environment:
- SPRING_AI_MCP_CLIENT_SSE_CONNECTIONS_DEMO-SERVER_URL=http://mcp-server:9099
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 📊 6. 监控与调试
# 6.1 健康检查
@Component
public class McpHealthIndicator implements HealthIndicator {
@Autowired
private List<McpSyncClient> mcpClients;
@Override
public Health health() {
try {
boolean allHealthy = mcpClients.stream()
.allMatch(client -> {
// 检查客户端连接状态
return true; // 实际实现中检查连接状态
});
return allHealthy ?
Health.up().withDetail("clients", mcpClients.size()).build() :
Health.down().withDetail("issue", "某些MCP客户端连接异常").build();
} catch (Exception e) {
return Health.down().withException(e).build();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6.2 监控端点
# 启用监控端点
management:
endpoints:
web:
exposure:
include: health,info,metrics,mcptools
endpoint:
health:
show-details: always
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 🎯 7. 最佳实践
# 7.1 错误处理
@Tool(description = "安全的除法运算")
public double divide(
@Parameter(description = "被除数") double dividend,
@Parameter(description = "除数") double divisor) {
if (divisor == 0) {
throw new IllegalArgumentException("除数不能为零");
}
return dividend / divisor;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 7.2 参数验证
@Tool(description = "验证邮箱格式")
public String validateEmail(@Parameter(description = "邮箱地址") String email) {
if (email == null || email.trim().isEmpty()) {
return "邮箱地址不能为空";
}
String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
if (email.matches(emailRegex)) {
return "邮箱格式正确: " + email;
} else {
return "邮箱格式错误: " + email;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 7.3 性能优化
// 异步工具执行
@Tool(description = "大文件处理")
public CompletableFuture<String> processLargeFile(
@Parameter(description = "文件路径") String filePath) {
return CompletableFuture.supplyAsync(() -> {
// 耗时处理逻辑
return "文件处理完成: " + filePath;
});
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 🔗 参考资源
上次更新: 2025/06/06, 11:28:26