MCP协议深度解析:Model Context Protocol完整实战
MCP协议深度解析:Model Context Protocol完整实战
适读人群:有1-5年Java开发经验,想向AI工程师方向转型的开发者 阅读时长:约20分钟 文章价值:
- 深度理解MCP协议的设计思想和核心机制
- 掌握用Spring AI实现MCP Server和Client的完整方案
- 学会让AI模型安全调用外部工具和数据源
为什么AI应用总是"数据孤岛"
阿强是一个做企业AI助手的创业者,他们的产品是一个帮企业员工提效的AI助手,理想状态是:员工问一个问题,AI能自动查CRM系统、查数据库、查飞书文档,然后给出综合答案。
听起来很美,但实际开发的时候他碰了一墙壁。
每接一个新的数据源,就得写一套新的集成代码:自定义Tool调用、手动序列化参数、处理返回值格式……不同的AI模型(OpenAI/Claude/通义)的Tool接口还不一样,每换一个模型就要改一遍。
他找到我,说:"感觉在给每个AI模型单独写适配器,越来越累。"
这个问题Anthropic也看到了,所以他们在2024年底发布了MCP(Model Context Protocol)——一个统一的AI工具调用协议,让AI模型和外部工具之间有了通用的"插头和插座"。
今天我来深度解析MCP,手把手带你实现一个MCP Server。
MCP是什么
MCP(Model Context Protocol)本质上是一个标准化协议,定义了AI模型和外部工具/数据源之间的通信格式。
你可以把它类比成HTTP协议对Web的意义:
- 没有HTTP之前,每个Web服务都有自己的通信格式
- 有了HTTP之后,任何浏览器都能访问任何网站
- 没有MCP之前,每个AI集成都需要定制适配器
- 有了MCP之后,任何支持MCP的AI模型都能调用任何MCP Server
MCP核心概念
Spring AI MCP Server实战
Spring AI 1.0已经集成了MCP支持,我们来实现一个真实的MCP Server:企业内部数据查询服务。
Maven配置
<dependencies>
<!-- Spring AI MCP Server -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring Boot Web(HTTP SSE Transport) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
</dependencies>application.yml
spring:
ai:
mcp:
server:
name: enterprise-data-server
version: 1.0.0
# HTTP SSE传输模式(推荐生产使用)
transport: sse
sse-endpoint: /mcp/sse
message-endpoint: /mcp/messages
datasource:
url: jdbc:mysql://localhost:3306/enterprise_db
username: ${DB_USER}
password: ${DB_PASSWORD}
server:
port: 8080实现MCP Tools
package com.laozhang.mcp.tools;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* 员工信息查询工具
* 使用@Tool注解标记,Spring AI MCP会自动将其暴露为MCP Tool
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class EmployeeQueryTool {
private final EmployeeRepository employeeRepo;
private final DepartmentRepository deptRepo;
/**
* 查询员工基本信息
*
* MCP会自动生成Tool的schema,包括参数描述和返回值描述
* description对AI理解工具用途非常重要,要写清楚
*/
@Tool(description = "查询员工的基本信息,包括姓名、部门、职位、联系方式。" +
"当需要了解某个员工的信息时使用此工具。")
public EmployeeInfo getEmployeeInfo(
@ToolParam(description = "员工的工号或姓名,支持模糊匹配")
String query
) {
log.info("[MCP Tool调用] getEmployeeInfo, query={}", query);
// 先尝试按工号查,再按姓名模糊查
Employee employee = employeeRepo.findByEmployeeId(query)
.orElseGet(() -> employeeRepo.findFirstByNameContaining(query)
.orElseThrow(() -> new RuntimeException("未找到员工: " + query)));
return new EmployeeInfo(
employee.getEmployeeId(),
employee.getName(),
employee.getDepartment(),
employee.getPosition(),
employee.getEmail(),
employee.getPhone()
);
}
/**
* 查询部门人员列表
*/
@Tool(description = "查询某个部门的所有员工列表,返回员工姓名和职位信息。" +
"当需要了解部门人员构成时使用此工具。")
public List<EmployeeInfo> getDepartmentMembers(
@ToolParam(description = "部门名称,如'技术部'、'产品部'、'销售部'")
String departmentName
) {
log.info("[MCP Tool调用] getDepartmentMembers, dept={}", departmentName);
List<Employee> employees = employeeRepo.findByDepartmentContaining(departmentName);
return employees.stream()
.map(e -> new EmployeeInfo(
e.getEmployeeId(), e.getName(),
e.getDepartment(), e.getPosition(),
e.getEmail(), null // 列表查询不返回电话(隐私保护)
))
.toList();
}
/**
* 查询项目信息
*/
@Tool(description = "查询项目的详细信息,包括负责人、状态、截止日期、参与人员。" +
"当需要了解某个项目进度或人员时使用此工具。")
public ProjectInfo getProjectInfo(
@ToolParam(description = "项目名称或项目编号") String projectQuery
) {
log.info("[MCP Tool调用] getProjectInfo, query={}", projectQuery);
Project project = projectRepo.findByNameOrCode(projectQuery)
.orElseThrow(() -> new RuntimeException("未找到项目: " + projectQuery));
return new ProjectInfo(
project.getCode(),
project.getName(),
project.getOwner(),
project.getStatus(),
project.getDeadline(),
project.getMembers()
);
}
public record EmployeeInfo(
String employeeId,
String name,
String department,
String position,
String email,
String phone
) {}
public record ProjectInfo(
String code,
String name,
String owner,
String status,
String deadline,
List<String> members
) {}
}实现MCP Resources
package com.laozhang.mcp.resources;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.mcp.server.annotation.McpResource;
import org.springframework.stereotype.Service;
/**
* MCP Resources:提供静态/只读数据给AI模型
* Resources不同于Tools,它是数据查看,不是操作执行
*/
@Service
@RequiredArgsConstructor
public class CompanyResourceProvider {
/**
* 提供公司组织架构文档
* URI格式:company://org-chart
*/
@McpResource(
uri = "company://org-chart",
name = "公司组织架构",
description = "公司当前的组织架构,包含所有部门和汇报关系",
mimeType = "text/plain"
)
public String getOrgChart() {
// 实际应从数据库或文件读取
return """
公司组织架构(更新于2026年4月)
CEO:张三
├── 技术部(CTO:李四)
│ ├── 后端研发组(10人)
│ ├── 前端研发组(8人)
│ └── AI研发组(5人)
├── 产品部(CPO:王五)
│ ├── 产品设计组(6人)
│ └── 用户研究组(3人)
└── 运营部(COO:赵六)
├── 市场组(8人)
└── 客服组(12人)
""";
}
/**
* 提供公司政策文档
* URI格式:company://policy/{policyName}
*/
@McpResource(
uri = "company://policy/leave",
name = "请假政策",
description = "公司请假政策,包含各类假期说明和申请流程",
mimeType = "text/plain"
)
public String getLeavePolicy() {
return """
公司请假政策
年假:工作满1年享受5天,满3年享受10天,满5年享受15天
病假:需提供医院证明,连续请假超过3天需副总以上审批
事假:每年最多3天,需提前1天申请,直属领导审批
申请流程:
1. 在HR系统填写请假申请
2. 直属领导审批(3天以内)
3. 部门负责人审批(3天以上)
""";
}
}MCP Server主启动类
package com.laozhang.mcp;
import com.laozhang.mcp.tools.EmployeeQueryTool;
import org.springframework.ai.mcp.server.McpServerFeatures;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
/**
* MCP Server启动类
*
* 启动后,任何支持MCP协议的AI客户端都能连接并使用这些工具
* 支持的客户端:Claude Desktop、Cursor、Spring AI MCP Client等
*/
@SpringBootApplication
public class MCPServerApplication {
public static void main(String[] args) {
SpringApplication.run(MCPServerApplication.class, args);
}
/**
* 注册所有Tool到MCP Server
* Spring AI会自动扫描@Tool注解,这里做显式注册(更清晰)
*/
@Bean
public McpServerFeatures.SyncToolRegistration toolRegistration(
EmployeeQueryTool employeeQueryTool
) {
return McpServerFeatures.SyncToolRegistration.builder()
.tools(employeeQueryTool)
.build();
}
}MCP Client实战:让AI使用这些工具
package com.laozhang.ai.client;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.ai.mcp.client.transport.HttpClientSseClientTransport;
import org.springframework.ai.mcp.spring.McpFunctionCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* MCP Client配置
* 连接MCP Server并将其工具注入到ChatClient
*/
@Configuration
public class MCPClientConfig {
/**
* 创建MCP客户端,连接到我们的企业数据MCP Server
*/
@Bean
public McpSyncClient enterpriseDataClient() {
return McpSyncClient.using(
HttpClientSseClientTransport.builder("http://localhost:8080")
.sseEndpoint("/mcp/sse")
.build()
);
}
/**
* 将MCP Server的所有工具注入到ChatClient
* 这样AI在对话时会自动决定是否调用这些工具
*/
@Bean
public ChatClient aiAssistant(
org.springframework.ai.chat.model.ChatModel chatModel,
McpSyncClient enterpriseDataClient
) {
// 获取MCP Server上的所有可用工具
List<McpFunctionCallback> mcpTools = McpFunctionCallback
.toolCallbacks(enterpriseDataClient);
return ChatClient.builder(chatModel)
.defaultSystem("""
你是一个企业内部智能助手。你有访问公司数据的能力,
可以查询员工信息、项目状态、部门信息等。
当用户询问相关信息时,请主动使用工具查询,给出准确答案。
对于超出权限范围的信息,礼貌拒绝并说明原因。
""")
.defaultFunctions(mcpTools.toArray(new McpFunctionCallback[0]))
.build();
}
}
@RestController
@RequiredArgsConstructor
public class EnterpriseAIController {
private final ChatClient aiAssistant;
/**
* 企业助手对话接口
* AI会自动判断是否需要调用MCP工具
*/
@PostMapping("/assistant/chat")
public String chat(@RequestBody ChatRequest request) {
// 直接对话,ChatClient会在需要时自动调用MCP Server上的工具
return aiAssistant.prompt()
.user(request.message())
.call()
.content();
}
public record ChatRequest(String message) {}
}完整调用流程
MCP vs 直接Function Calling的区别
| 维度 | 直接Function Calling | MCP协议 |
|---|---|---|
| 可复用性 | 每个应用单独定义 | 一套Server,所有AI复用 |
| 跨模型支持 | OpenAI格式/Claude格式不同 | 统一MCP格式 |
| 安全控制 | 在AI层控制 | 在MCP Server层集中控制 |
| 工具发现 | 需要手动配置 | 客户端自动发现 |
| 版本管理 | 随AI应用一起升级 | Server独立升级 |
| 适合场景 | 单应用、少量工具 | 企业级、多应用共享工具 |
生产部署注意事项
安全认证:MCP Server默认没有认证,生产环境必须加。推荐在nginx层做Bearer Token认证,或者使用Spring Security。
权限隔离:不同的AI应用可能需要访问不同的工具。MCP Server可以根据请求来源(客户端ID)返回不同的工具列表。
调用审计:所有MCP工具调用都应该记录日志,包括调用者、调用的工具、参数、结果。既是安全审计,也是排查问题的依据。
工具幂等性:AI可能因为判断错误重复调用同一个工具。写工具时要考虑幂等性,查询类工具天然幂等,操作类工具需要加去重逻辑。
