MCP协议详解:2026年AI工具标准化的新纪元
MCP协议详解:2026年AI工具标准化的新纪元
从一个让人抓狂的下午说起
2025年秋天,老陈正在赶一个AI助手项目的Deadline。
他要把公司的三个工具——数据库查询、内部Wiki搜索、JIRA工单系统——全部接入新的AI助手。用的是OpenAI的Function Calling。好,弄完了,两周时间,测试通过。
然后产品说:"我们要同时支持Claude,客户要求的。"
老陈懵了。
Claude的工具调用格式和OpenAI不一样。OpenAI用的是functions数组,每个function有name、description、parameters(JSON Schema)。Claude用的是tools数组,结构略有差异。同样的工具,要写两套描述代码,还要分别测试。
弄完Claude,产品又来了:"我们要加Gemini的支持。"
老陈打开了辞职信。
这个问题,在AI圈子里有一个专有名词:工具碎片化(Tool Fragmentation)。每个AI平台都有自己的工具调用格式,工具提供方要分别适配,AI应用开发者要分别集成,重复劳动极多。
2024年11月,Anthropic发布了MCP(Model Context Protocol),就是为了解决这个问题。
截止到2026年初,MCP已经成为AI工具标准化的事实标准。OpenAI、Google、Microsoft都在跟进,主流AI IDE(Cursor、Claude Code、Continue)原生支持,Spring AI 1.0也内置了MCP客户端。
今天,让我们彻底搞懂它。
先说结论(TL;DR)
- MCP是Anthropic提出的AI工具通信标准协议,目标是"一次编写,随处使用"
- 架构:MCP Client(AI应用)↔ MCP Server(工具提供方),解耦了AI和工具
- 三类能力:Tools(可调用的函数)、Resources(可读取的数据源)、Prompts(提示模板)
- 传输协议:stdio(本地进程通信)和HTTP SSE(远程网络通信)
- Spring AI 1.0原生支持MCP Client,几行配置即可接入MCP Server
- 2026年生态:数百个开源MCP Server,覆盖数据库、文件系统、浏览器、代码工具等
MCP是什么:Anthropic提出的AI工具通信标准协议
官方定义
MCP is an open protocol that standardizes how applications provide context to LLMs.
翻译过来:MCP是一个开放协议,标准化了"应用如何向LLM提供上下文(工具、数据、提示)"。
一句话理解
如果说USB是硬件设备的标准接口(插上就能用,不管是什么设备)。那MCP就是AI工具的标准接口:不管是数据库、API、文件系统,只要按MCP格式实现一个Server,所有支持MCP的AI应用都能直接使用。
MCP解决的核心问题
MCP架构详解
三层架构
关键角色说明
| 角色 | 说明 | 示例 |
|---|---|---|
| Host | 使用AI能力的应用 | 你的Spring Boot应用、Claude Desktop |
| MCP Client | 内嵌在Host中,管理MCP连接 | Spring AI的McpClient |
| MCP Server | 工具的提供方,暴露Tools/Resources/Prompts | 数据库服务、文件服务 |
| LLM | 决定调用哪个工具 | GPT-4o、Claude 3.5 |
MCP的三类能力
1. Tools(工具)
最常用的能力,LLM可以主动调用执行操作。
// MCP Tool定义示例
{
"name": "query_database",
"description": "执行SQL查询,返回结果集。只支持SELECT语句。",
"inputSchema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "要执行的SQL SELECT语句"
},
"limit": {
"type": "integer",
"description": "最大返回行数,默认100",
"default": 100
}
},
"required": ["sql"]
}
}2. Resources(资源)
LLM可以读取的数据源,类似"文件系统"的概念。
// MCP Resource定义示例
{
"uri": "database://tables/orders",
"name": "订单表数据",
"description": "电商系统的订单表,包含所有历史订单数据",
"mimeType": "application/json"
}Resources vs Tools的核心区别:
- Tools:LLM主动调用,执行操作,有副作用(写操作)
- Resources:LLM读取数据,无副作用,类似GET请求
3. Prompts(提示模板)
可复用的提示词模板,带参数,类似函数。
// MCP Prompt定义示例
{
"name": "code_review",
"description": "代码审查提示模板",
"arguments": [
{
"name": "code",
"description": "要审查的代码",
"required": true
},
{
"name": "language",
"description": "编程语言",
"required": false
}
]
}MCP传输协议:stdio vs HTTP SSE
stdio(标准输入输出)
stdio适合:
- 本地运行的工具(文件系统、本地数据库)
- 安全性要求高(进程隔离)
- 低延迟要求(进程间通信很快)
HTTP SSE(Server-Sent Events)
HTTP SSE适合:
- 远程工具服务(第三方API)
- 多客户端共享的工具服务
- 需要鉴权和流量控制的场景
对比表
| 维度 | stdio | HTTP SSE |
|---|---|---|
| 部署方式 | 本地进程 | 远程HTTP服务 |
| 延迟 | 极低(<1ms) | 较低(网络延迟) |
| 安全性 | 高(进程隔离) | 需要额外鉴权 |
| 多客户端 | 不支持(1:1) | 支持(1:N) |
| 跨网络 | 不支持 | 支持 |
自己动手写一个MCP Server
理解MCP最好的方式是自己动手写一个。我们用Java + Spring AI MCP Server框架实现一个业务数据MCP Server。
MCP Server端依赖
<!-- MCP Server项目的pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies># MCP Server的application.yml
server:
port: 9000
spring:
ai:
mcp:
server:
enabled: true
name: "business-data-mcp-server"
version: "1.0.0"
transport: http-sse
datasource:
url: jdbc:postgresql://localhost:5432/business
username: ${DB_USER}
password: ${DB_PASSWORD}用@Tool注解实现业务工具
// BusinessDataTools.java - MCP Server核心:用@Tool暴露业务能力
package com.laozhang.mcpserver;
import com.fasterxml.jackson.databind.ObjectMapper;
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.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class BusinessDataTools {
private final JdbcTemplate jdbcTemplate;
private final ObjectMapper objectMapper;
@Tool(name = "list_tables",
description = "列出所有可查询的业务数据表及字段说明")
public String listTables() {
return """
可用业务表:
- orders:订单(id, user_id, amount, status, created_at)
- users:用户(id, name, email, level, created_at)
- products:产品(id, name, category, price, stock)
- order_items:订单明细(order_id, product_id, quantity, price)
""";
}
@Tool(name = "query_data",
description = "执行SELECT语句查询业务数据。不支持INSERT/UPDATE/DELETE。")
public String queryData(
@ToolParam(description = "SQL SELECT语句,例如:SELECT * FROM orders LIMIT 10")
String sql) {
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
return "错误:只允许SELECT查询,请检查SQL语句。";
}
try {
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql);
if (results.isEmpty()) return "查询结果为空";
// 最多返回100条
List<Map<String, Object>> limited = results.size() > 100
? results.subList(0, 100) : results;
String suffix = results.size() > 100
? "\n(结果已截断,共" + results.size() + "条,仅展示前100条)" : "";
return objectMapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(limited) + suffix;
} catch (Exception e) {
return "查询失败:" + e.getMessage()
+ "\n提示:用list_tables查看正确的表名和字段名";
}
}
@Tool(name = "get_summary_stats",
description = "获取核心业务指标汇总:订单量、营收、用户数")
public String getSummaryStats(
@ToolParam(description = "统计时间段:today/week/month/all") String period) {
String filter = switch (period.toLowerCase()) {
case "today" -> "WHERE DATE(created_at) = CURRENT_DATE";
case "week" -> "WHERE created_at >= NOW() - INTERVAL '7 days'";
case "month" -> "WHERE created_at >= NOW() - INTERVAL '30 days'";
default -> "";
};
try {
Map<String, Object> orderStats = jdbcTemplate.queryForMap(
"SELECT COUNT(*) as cnt, COALESCE(SUM(amount),0) as revenue " +
"FROM orders " + filter);
Map<String, Object> userStats = jdbcTemplate.queryForMap(
"SELECT COUNT(*) as cnt FROM users " + filter);
return String.format(
"业务摘要(%s):订单 %s 笔,营收 ¥%s,用户 %s 人",
period,
orderStats.get("cnt"),
orderStats.get("revenue"),
userStats.get("cnt")
);
} catch (Exception e) {
return "统计失败:" + e.getMessage();
}
}
}// McpServerConfig.java - 将工具注册到MCP Server
package com.laozhang.mcpserver.config;
import com.laozhang.mcpserver.BusinessDataTools;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class McpServerConfig {
@Bean
public ToolCallbackProvider businessDataToolProvider(BusinessDataTools tools) {
return MethodToolCallbackProvider.builder()
.toolObjects(tools)
.build();
}
}// McpServerApplication.java - 启动类
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
}启动后,任何MCP Client(包括Claude Desktop、Cursor、你的Spring AI应用)都可以通过 http://localhost:9000/mcp 连接并使用这三个工具。一次编写,随处使用——这就是MCP的价值。
深入MCP协议细节
MCP消息格式(JSON-RPC 2.0)
MCP基于JSON-RPC 2.0,所有消息格式如下:
// 请求(Client → Server)
{
"jsonrpc": "2.0",
"id": "req_001",
"method": "tools/call",
"params": {
"name": "query_database",
"arguments": {
"sql": "SELECT * FROM users LIMIT 10"
}
}
}
// 响应(Server → Client)
{
"jsonrpc": "2.0",
"id": "req_001",
"result": {
"content": [
{
"type": "text",
"text": "[{\"id\": 1, \"name\": \"Alice\"}, ...]"
}
],
"isError": false
}
}
// 错误响应
{
"jsonrpc": "2.0",
"id": "req_001",
"error": {
"code": -32603,
"message": "Internal error",
"data": "SQL execution failed: table 'users' not found"
}
}初始化握手
// Client发起初始化
{
"jsonrpc": "2.0",
"id": "init_001",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {"listChanged": true},
"sampling": {}
},
"clientInfo": {
"name": "spring-ai-mcp-client",
"version": "1.0.0"
}
}
}
// Server响应,声明自己的能力
{
"jsonrpc": "2.0",
"id": "init_001",
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {"listChanged": true},
"resources": {"subscribe": true, "listChanged": true},
"prompts": {"listChanged": true}
},
"serverInfo": {
"name": "database-mcp-server",
"version": "1.0.0"
}
}
}Spring AI MCP Client集成(完整代码)
依赖配置
<!-- pom.xml -->
<dependencies>
<!-- Spring AI Core -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring AI MCP Client -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>MCP配置
# application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o
mcp:
client:
enabled: true
# 连接的MCP Server列表
servers:
# 文件系统MCP Server(stdio方式)
filesystem:
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp/workspace"]
transport: stdio
# 数据库MCP Server(stdio方式)
database:
command: java
args: ["-jar", "/opt/mcp/database-server.jar"]
transport: stdio
env:
DB_URL: jdbc:postgresql://localhost:5432/mydb
DB_USER: ${DB_USER}
DB_PASSWORD: ${DB_PASSWORD}
# 远程MCP Server(HTTP SSE方式)
search-service:
url: http://search-mcp-server:8080
transport: http-sse
headers:
Authorization: "Bearer ${MCP_SEARCH_TOKEN}"
logging:
level:
org.springframework.ai.mcp: DEBUGJava代码:注入MCP工具
// McpIntegratedAssistant.java
package com.laozhang.mcp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class McpIntegratedAssistant {
private final ChatClient.Builder chatClientBuilder;
// Spring AI MCP Client会自动注入,包含所有已配置的MCP Server工具
private final SyncMcpToolCallbackProvider mcpToolCallbackProvider;
/**
* 使用MCP工具回答问题
* AI会自动发现并调用合适的MCP工具
*/
public String chat(String userMessage) {
log.info("用户提问: {}", userMessage);
String response = chatClientBuilder.build()
.prompt()
.system("""
你是一个智能助手,可以访问以下工具:
- 文件系统操作(读写文件)
- 数据库查询
- 网络搜索
根据用户的需求,选择合适的工具完成任务。
""")
.user(userMessage)
// 关键:注入所有MCP工具
.tools(mcpToolCallbackProvider.getToolCallbacks())
.call()
.content();
log.info("AI响应: {}", response.substring(0, Math.min(200, response.length())));
return response;
}
}// McpController.java
@RestController
@RequestMapping("/api/mcp")
@RequiredArgsConstructor
public class McpController {
private final McpIntegratedAssistant assistant;
@PostMapping("/chat")
public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
String response = assistant.chat(request.message());
return ResponseEntity.ok(new ChatResponse(response));
}
record ChatRequest(String message) {}
record ChatResponse(String message) {}
}查看已注册的MCP工具
// McpToolInspector.java - 查看所有可用的MCP工具
@Component
@RequiredArgsConstructor
@Slf4j
public class McpToolInspector implements ApplicationRunner {
private final SyncMcpToolCallbackProvider mcpToolCallbackProvider;
@Override
public void run(ApplicationArguments args) {
var tools = mcpToolCallbackProvider.getToolCallbacks();
log.info("=== 已注册的MCP工具 ({} 个) ===", tools.length);
for (var tool : tools) {
log.info(" - {} : {}", tool.getName(), tool.getDescription());
}
}
}MCP与传统Function Calling的完整对比
从开发流程看差异
传统Function Calling开发流程(每次接入新工具要重复这些步骤):
// 传统Function Calling:开发者要在AI应用中定义每个工具的Schema
// 接入OpenAI的写法
OpenAiApi.FunctionTool queryOrderTool = new OpenAiApi.FunctionTool(
OpenAiApi.FunctionTool.Type.FUNCTION,
new OpenAiApi.FunctionTool.Function(
"query_order",
"查询订单信息",
"""
{
"type": "object",
"properties": {
"orderId": {"type": "string", "description": "订单ID"}
},
"required": ["orderId"]
}
"""
)
);
// 接入Claude时要重写一遍(格式有差异)
// 接入Gemini时又要重写一遍...MCP开发流程(工具只需定义一次):
// MCP Server:工具提供方定义一次
@Tool(name = "query_order", description = "查询订单信息")
public String queryOrder(@ToolParam(description = "订单ID") String orderId) {
return orderService.findById(orderId).toString();
}
// 任意MCP Client应用接入(无需重新定义Schema)
chatClient.prompt()
.user("查询订单" + orderId)
.tools(mcpProvider.getToolCallbacks()) // 自动发现所有工具
.call().content();全维度对比表
| 维度 | 传统Function Calling | MCP |
|---|---|---|
| 工具定义位置 | AI应用(调用方) | MCP Server(工具提供方) |
| 跨平台复用 | 每个平台重写 | 一次编写,随处使用 |
| 工具发现 | 手动定义 | 自动发现 |
| 多工具管理 | 代码中维护工具列表 | Server端统一维护 |
| 工具更新 | 需要修改AI应用代码 | 只需更新MCP Server |
| 适用规模 | 工具数量少(<10个) | 工具数量多(无限制) |
| 调试方式 | 直接打日志 | MCP Inspector + 日志 |
| 运维成本 | 低(代码内) | 中(需要运行Server进程) |
| 生态 | 碎片化 | 统一,数百个现成Server |
何时选哪个?
工具少(1-5个)且只用一个AI平台 → 传统Function Calling(简单快捷)
工具多(>5个)或需要跨AI平台 → MCP(一次定义,到处复用)
需要给团队其他人也用你的工具 → MCP(标准接口,他人可直接接入)
工具频繁更新 → MCP(更新Server,客户端无感知)现有的MCP Server生态
官方MCP Server(Anthropic维护)
实用MCP Server配置示例
# 常用MCP Server配置集合(application.yml)
spring:
ai:
mcp:
client:
servers:
# SQLite数据库
sqlite:
command: npx
args: ["-y", "@modelcontextprotocol/server-sqlite", "/data/app.db"]
transport: stdio
# GitHub操作
github:
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
transport: stdio
env:
GITHUB_PERSONAL_ACCESS_TOKEN: ${GITHUB_TOKEN}
# Brave搜索
brave-search:
command: npx
args: ["-y", "@modelcontextprotocol/server-brave-search"]
transport: stdio
env:
BRAVE_API_KEY: ${BRAVE_API_KEY}
# Puppeteer浏览器自动化
puppeteer:
command: npx
args: ["-y", "@modelcontextprotocol/server-puppeteer"]
transport: stdioMCP的安全考量:工具权限控制
// McpSecurityConfig.java - MCP工具权限控制
package com.laozhang.mcp.security;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.stereotype.Component;
import java.util.*;
@Slf4j
@Component
public class McpToolPermissionManager {
// 工具权限矩阵:角色 → 允许的工具集
private static final Map<String, Set<String>> ROLE_PERMISSIONS = Map.of(
"ADMIN", Set.of("*"), // 管理员可以用所有工具
"DEVELOPER", Set.of("filesystem_read", "filesystem_write",
"database_query", "github_*"),
"ANALYST", Set.of("database_query", "database_list_tables",
"brave_search"),
"VIEWER", Set.of("database_query") // 只读权限
);
/**
* 根据用户角色过滤可用工具
*/
public ToolCallback[] filterToolsByRole(ToolCallback[] allTools, String userRole) {
Set<String> allowedPatterns = ROLE_PERMISSIONS.getOrDefault(userRole, Set.of());
if (allowedPatterns.contains("*")) {
return allTools; // 管理员不过滤
}
return Arrays.stream(allTools)
.filter(tool -> isToolAllowed(tool.getName(), allowedPatterns))
.toArray(ToolCallback[]::new);
}
private boolean isToolAllowed(String toolName, Set<String> allowedPatterns) {
for (String pattern : allowedPatterns) {
if (pattern.endsWith("*")) {
// 通配符匹配
String prefix = pattern.substring(0, pattern.length() - 1);
if (toolName.startsWith(prefix)) return true;
} else if (pattern.equals(toolName)) {
return true;
}
}
log.debug("工具 {} 被权限系统拒绝", toolName);
return false;
}
}// 在Assistant中集成权限控制
@Service
@RequiredArgsConstructor
public class SecureMcpAssistant {
private final ChatClient.Builder chatClientBuilder;
private final SyncMcpToolCallbackProvider mcpToolCallbackProvider;
private final McpToolPermissionManager permissionManager;
public String chat(String userId, String userRole, String message) {
// 根据用户角色过滤工具
ToolCallback[] allTools = mcpToolCallbackProvider.getToolCallbacks();
ToolCallback[] allowedTools = permissionManager.filterToolsByRole(allTools, userRole);
log.info("用户 {} (角色:{}) 可使用 {}/{} 个工具",
userId, userRole, allowedTools.length, allTools.length);
return chatClientBuilder.build()
.prompt()
.user(message)
.tools(allowedTools) // 只注入允许的工具
.call()
.content();
}
}工具调用审计日志
// McpAuditLogger.java
@Aspect
@Component
@Slf4j
public class McpAuditLogger {
@Around("execution(* org.springframework.ai.mcp..*ToolCallback.call(..))")
public Object auditToolCall(ProceedingJoinPoint pjp) throws Throwable {
String toolName = extractToolName(pjp);
String input = extractInput(pjp);
long startTime = System.currentTimeMillis();
log.info("[MCP审计] 工具调用开始: tool={}, input={}", toolName, truncate(input, 200));
try {
Object result = pjp.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("[MCP审计] 工具调用成功: tool={}, duration={}ms", toolName, duration);
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
log.error("[MCP审计] 工具调用失败: tool={}, duration={}ms, error={}",
toolName, duration, e.getMessage());
throw e;
}
}
private String truncate(String text, int maxLen) {
if (text == null) return "null";
return text.length() <= maxLen ? text : text.substring(0, maxLen) + "...";
}
private String extractToolName(ProceedingJoinPoint pjp) {
// 从方法签名提取工具名
return pjp.getTarget().getClass().getSimpleName();
}
private String extractInput(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
return args.length > 0 ? String.valueOf(args[0]) : "";
}
}2026年MCP生态现状与趋势
生态现状
截止2026年5月,MCP生态已经相当成熟:
| 类别 | 代表性MCP Server | 状态 |
|---|---|---|
| 数据库 | PostgreSQL, MySQL, SQLite, MongoDB | 稳定 |
| 文件系统 | filesystem(官方), S3, GCS | 稳定 |
| 搜索 | Brave Search, Tavily, Exa | 稳定 |
| 代码工具 | GitHub, GitLab, Jira, Linear | 稳定 |
| 浏览器 | Puppeteer, Playwright | 稳定 |
| 云服务 | AWS工具集, Vercel, Cloudflare | 成长中 |
| AI工具 | Hugging Face, Replicate | 成长中 |
主流AI平台MCP支持
| 平台 | MCP支持状态 |
|---|---|
| Claude Desktop | 原生支持,最完整 |
| Cursor | 原生支持 |
| Claude Code | 原生支持 |
| Continue (VS Code) | 原生支持 |
| Windsurf | 支持 |
| Spring AI 1.0 | 原生支持 Client |
| LangChain4j | 社区实现 |
未来趋势
MCP Server标准库:主流云服务商会提供官方MCP Server(AWS RDS MCP Server、Azure AI MCP Server等)
MCP Registry:类似npm的MCP Server注册中心,一行命令安装工具
MCP安全标准:OAuth2/OIDC标准集成,工具调用的细粒度权限控制
多模态MCP:支持图像、视频、音频工具(不仅限于文本)
生产注意事项
stdio进程管理
// McpProcessManager.java - 管理stdio MCP Server进程
@Component
@Slf4j
public class McpProcessHealthChecker {
/**
* 检查MCP Server进程是否健康
* 如果进程崩溃,Spring AI会自动重启,但可以添加监控
*/
@Scheduled(fixedRate = 60000) // 每分钟检查
public void checkMcpServers() {
// 实际上Spring AI MCP Client会自动管理进程生命周期
// 这里添加自定义健康检查逻辑
log.debug("MCP Server健康检查执行");
}
}超时配置
spring:
ai:
mcp:
client:
request-timeout: 30s # 工具调用超时
initialization-timeout: 10s # 连接初始化超时错误处理
// McpErrorHandler.java
@Component
public class McpErrorHandler {
/**
* MCP工具调用失败的降级策略
*/
public String handleToolFailure(String toolName, Exception e) {
log.error("MCP工具 {} 调用失败: {}", toolName, e.getMessage());
// 根据工具类型给出友好的降级提示
if (toolName.contains("database")) {
return "数据库暂时不可用,请稍后再试或使用其他方式查询";
} else if (toolName.contains("filesystem")) {
return "文件系统访问失败,请检查文件路径是否正确";
} else if (toolName.contains("search")) {
return "搜索服务暂时不可用,建议手动搜索";
}
return "工具调用失败: " + e.getMessage();
}
}常见问题解答
Q1:MCP和Function Calling有什么本质区别?
A:很好的问题。技术层面上,MCP调用最终也是通过Function Calling实现的。本质区别是抽象层次:
- Function Calling:你需要在每个AI应用中手动定义每个工具的Schema
- MCP:工具的Schema定义在MCP Server中,AI应用通过协议自动发现。"工具的定义权"从AI应用转移到了工具提供方。 这就像REST API和GraphQL的区别——GraphQL让客户端自己描述需要什么数据,而MCP让工具服务器自己描述能提供什么能力。
Q2:我的公司内部工具需要暴露在互联网上才能用MCP吗?
A:不需要。stdio模式的MCP Server是本地进程,完全不需要网络。对于内部工具:
- 本地部署:用stdio模式,启动一个Java进程作为MCP Server
- 内网部署:用HTTP SSE模式,部署在内网,只需内网可达
- 安全考量:HTTP SSE模式加上Bearer Token认证,和普通API安全级别一样
Q3:Spring AI的MCP Client支持热重载(运行时增加Server)吗?
A:Spring AI 1.0目前是静态配置,在application.yml中配置的Server在启动时加载。如果需要运行时动态添加MCP Server,需要自行实现McpClient的动态注册逻辑。这个功能在Spring AI的Roadmap中,预计1.1版本会支持。
Q4:MCP Server是否必须用Node.js写?
A:不是。MCP协议是语言无关的,可以用任何语言实现。官方提供了TypeScript和Python的SDK。社区有Java实现(spring-ai-mcp-server)。你也可以不用SDK,直接读写JSON-RPC 2.0格式的消息。
Q5:一个应用同时连接10个MCP Server,性能会有问题吗?
A:主要看工具调用频率。连接10个Server本身开销很小(stdio是进程,HTTP SSE是长连接)。性能瓶颈通常在工具调用本身(数据库查询、网络请求)而不是协议层。建议:只配置实际会用到的MCP Server,避免连接大量空闲Server。
Q6:如何调试MCP工具调用问题?
A:三个工具:
- 开启
logging.level.org.springframework.ai.mcp: DEBUG,会打印所有JSON-RPC消息 - 用MCP Inspector(官方调试工具):
npx @modelcontextprotocol/inspector,可以直接测试MCP Server的工具 - 直接向MCP Server发送测试消息(stdin/stdout或HTTP POST),验证工具是否正常响应
总结
MCP不只是一个协议,它代表了AI工具生态的一次重要整合。
可操作行动清单:
AI工具标准化的时代已经到来,早点上车,早点受益。
