大翔敲着白板:"我们 Python 团队写了一套天气预报 API,Java 这边要用。别给我搞 REST 对接那一套——用 MCP,一行配置搞定。"
MCP 协议深入
定义与作用
MCP(Model Context Protocol)是由 Anthropic 发起、多家 AI 厂商共同支持的开放协议,标准化 AI 应用与外部工具/数据源之间的通信。Spring AI 作为 MCP Client,通过 Stdio 子进程或 HTTP 远程连接消费 MCP Server 暴露的工具。
核心原理
MCP 架构
图释:AI 模型决定调用工具 → Spring AI 通过 MCP Client 发送 JSON-RPC 请求 → MCP Server 执行 Python 代码 → 结果沿原路返回 → AI 基于工具结果生成最终回复。
两种传输方式
| 传输方式 | 通信机制 | 部署模式 | 适用场景 |
|---|---|---|---|
| Stdio | 进程 stdin/stdout | MCP Server 作为子进程 | 本地工具、开发环境 |
| HTTP (SSE) | HTTP + Server-Sent Events | 独立服务 | 远程工具、生产环境 |
完整示例一:Stdio 传输——Python 天气工具
场景说明
大翔要求 Python 团队实现天气预报工具,Java 智能客服通过 MCP Stdio 调用。
Python MCP Server 端
# weather_server.py
from mcp.server import Server, stdio
from mcp.types import Tool, TextContent
import json
server = Server("weather-server")
@server.tool()
async def get_weather(city: str) -> list[TextContent]:
"""获取指定城市的当前天气信息"""
weather_data = {
"北京": "晴,25°C,湿度 40%,风力 2 级",
"上海": "多云,28°C,湿度 65%,风力 3 级",
"广州": "雷阵雨,30°C,湿度 80%,风力 4 级",
}
result = weather_data.get(city, f"暂无 {city} 的天气数据")
return [TextContent(type="text", text=result)]
@server.tool()
async def get_forecast(city: str, days: int = 3) -> list[TextContent]:
"""获取指定城市的未来天气预报"""
forecast = f"{city}未来{days}天:第一天晴,第二天多云,第三天小雨"
return [TextContent(type="text", text=forecast)]
if __name__ == "__main__":
stdio.run(server)
Java MCP Client 配置
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-spring-boot-starter</artifactId>
</dependency>
spring:
ai:
mcp:
client:
type: SYNC
name: weather-mcp-client
version: 1.0.0
request-timeout: 30s
toolchange-notification: true
connections:
weather:
transport: stdio
command: python
args:
- weather_server.py
working-directory: /path/to/python/tools
@Configuration
public class McpConfig {
@Bean
public ChatClient chatClient(
ChatClient.Builder builder,
McpClient mcpClient) {
// 从 MCP Server 发现所有工具并注册
ToolCallback[] mcpTools = mcpClient.listTools().stream()
.map(tool -> FunctionCallback.builder()
.name(tool.name())
.description(tool.description())
.inputType(String.class) // 简单参数可用 String
.responseConverter(r -> r)
.function(ctx -> {
String result = mcpClient.callTool(
new McpToolCallRequest(tool.name(), ctx));
return result;
})
.build())
.toArray(ToolCallback[]::new);
return builder
.defaultTools((Object[]) mcpTools)
.build();
}
}
运行结果
用户:"今天广州天气怎么样?"
日志:
[Spring AI] AI 决定调用工具: getWeather
[Spring AI] MCP tools/call → weather-server (Stdio)
[Python MCP] 查询广州天气...
[Spring AI] 工具返回: "雷阵雨,30°C,湿度 80%,风力 4 级"
AI 回复:"今天广州是雷阵雨天气,气温 30°C,湿度较高达 80%,伴有 4 级风。建议带伞出行!"
完整示例二:HTTP 传输——远程课程查询
场景说明
飞翔科技的课程数据库由独立的微服务管理,通过 MCP HTTP 方式暴露课程查询工具。
Java MCP Server 端(HTTP 传输)
# 课程查询 MCP Server 的 application.yml
spring:
ai:
mcp:
server:
name: course-mcp-server
version: 1.0.0
type: SYNC
protocol: STREAMABLE # HTTP + SSE
port: 8081
// 课程查询工具
@Component
public class CourseTools {
@Tool(description = "查询课程信息,输入课程名称或代码")
public String getCourseInfo(String courseName) {
// 模拟课程数据库查询
return switch (courseName) {
case "CS101" -> """
课程代码: CS101
课程名称: 数据结构与算法
授课教师: 白歌
学分: 4
时间: 周一 8:00-10:00, 周三 10:00-12:00
教室: 飞翔楼 301
""";
case "MATH201" -> """
课程代码: MATH201
课程名称: 线性代数
授课教师: 大翔
学分: 3
时间: 周二 14:00-16:00
教室: 飞翔楼 205
""";
default -> "未找到课程: " + courseName;
};
}
@Tool(description = "查询教师的所有课程安排")
public String getTeacherSchedule(String teacherName) {
if ("白歌".equals(teacherName)) {
return "白歌老师本学期课程:CS101 数据结构与算法、CS305 软件架构设计";
}
return teacherName + "老师本学期暂无课程安排";
}
}
Java MCP Client 端配置
spring:
ai:
mcp:
connections:
course-service:
transport: http
url: http://localhost:8081/mcp
sse-endpoint: http://localhost:8081/mcp/sse
@RestController
public class McpCourseController {
private final ChatClient chatClient;
public McpCourseController(
ChatClient.Builder builder,
@Qualifier("courseMcpTools") ToolCallback[] courseTools) {
this.chatClient = builder
.defaultTools((Object[]) courseTools)
.build();
}
@GetMapping("/mcp/course")
public String askCourse(@RequestParam String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}
运行结果
用户:"白歌老师这学期教什么课?"
AI 调用工具: getTeacherSchedule("白歌")
工具返回: "白歌老师本学期课程:CS101 数据结构与算法、CS305 软件架构设计"
AI 回复:"白歌老师这学期教授两门课程:CS101 数据结构与算法,以及 CS305 软件架构设计。"
MCP vs 直接 @Tool
| 维度 | @Tool(进程内) | MCP |
|---|---|---|
| 实现语言 | 必须 Java | Python / TS / Go / Java 任意语言 |
| 部署方式 | 同一 JVM 进程 | 独立子进程或远程服务 |
| 隔离性 | 共享 JVM 内存 | 进程/网络隔离 |
| 更新方式 | 需重新部署整个应用 | 独立更新 MCP Server |
| 适用场景 | 轻量级函数调用 | 跨语言/跨团队/微服务集成 |
| 调试 | 在同一 IDE 内 | 需分别启动 Client 和 Server |
易错场景与面试考点
易错场景一:Stdio 路径错误导致连接失败
# ❌ 错误:command 路径未找到
connections:
weather:
transport: stdio
command: python # 若环境变量未配置可能失败
args:
- weather_server.py
问题分析
Stdio 依赖系统 PATH 环境变量。Python 不在 PATH 中时需使用绝对路径,或使用 python3。
# ✅ 正确:使用绝对路径
connections:
weather:
transport: stdio
command: /usr/local/bin/python3
args:
- /absolute/path/to/weather_server.py
面试高频题
Q1:MCP 协议的通信流程是怎样的?
①MCP Client 通过
tools/list发现 Server 提供的工具及 JSON Schema;②AI 模型决定调用工具时,MCP Client 通过tools/call发送函数名和参数;③MCP Server 执行工具并返回结果;④Client 将结果封装为 ToolResponseMessage 返回给 AI 模型。全程使用 JSON-RPC 2.0 协议。
Q2:Stdio 和 HTTP 传输如何选择?
Stdio 适合同一台机器上的本地工具,通过子进程隔离,零网络开销;HTTP 适合远程微服务工具,支持分布式部署和水平扩展。生产环境推荐 HTTP,开发环境可用 Stdio。
本章小结
- MCP 是跨语言工具集成的开放标准协议
- Client/Server 架构:Server 暴露工具,Client 消费工具
- Stdio 适合本地子进程,HTTP 适合远程微服务
- Spring AI 通过 McpClient 自动发现并注册 MCP 工具到 ChatClient
- MCP 解决了"跨语言"和"跨进程"两大企业级工具集成难题