第一部分:MCP 深度解析 - AI 的“USB 标准”

1. 什么是 MCP?

MCP (Model Context Protocol) 是由 Anthropic 主导开发的一种开放协议,旨在为大语言模型(LLM)提供一种标准化、安全的方式来访问外部工具、数据和服务

  • 核心比喻:MCP 之于 AI 应用,犹如 USB 之于电脑。它定义了一套通用的“接口”和“协议”,允许模型(如Claude、GPT)即插即用地与各种外部资源(数据库、API、文件系统等)进行交互,而无需为每个工具编写特定的集成代码。
  • 要解决的问题
    1. 集成碎片化:每个AI应用都需要重复编写连接外部工具(如SQL数据库、JIRA、GitHub)的代码,工作量大且难以维护。
    2. 安全性:直接授予AI模型访问生产系统的权限存在巨大风险。
    3. 能力限制:模型的上下文窗口有限,无法直接处理大量外部数据。

2. 为什么 MCP 如此重要?

MCP 是构建下一代 “AI原生” 应用的基础设施,它实现了模型(大脑)工具(手脚) 的彻底解耦。

  • 对模型提供商(Anthropic, OpenAI等):无需不断为模型内置更多功能,只需让模型学会使用MCP协议,即可无限扩展其能力。
  • 对开发者:可以专注于开发好用的工具(MCP Server),而无需关心模型层面的集成。一次开发,所有兼容MCP的模型和应用(如Claude Console、Cline)都能使用。
  • 对企业:可以安全、可控地将内部系统(数据库、CRM、ERP)暴露给AI使用,极大提升自动化能力。

3. MCP 的核心组件与工作原理

MCP 采用客户端-服务器(Client-Server) 架构,通过双向通信(如SSE、WebSocket)进行交互。

Model Context Protocol 交互流程
1. 初始化连接
(SSE/WebSocket)
2. 发送工具列表
(名称, 描述, 参数schema)
3. 用户请求
“分析Q3数据”
4. 决策
“需要调用sql_query_tool”
5. 调用工具
(callTool)
6. 执行 & 返回结果
(SQL查询)
7. 呈现结果给用户
MCP Client
e.g., Claude Code
MCP Server
e.g., SQL Server
LLM Reasoning
End User
  • MCP Client(客户端)

    • 通常是AI应用或模型界面(如Claude Code编辑器、Cursor编辑器、未来可能支持MCP的ChatGPT)。
    • 职责:建立与Server的连接,向模型展示可用的工具列表,并根据模型的决策调用Server上的工具,最后将结果返回给模型/用户。
  • MCP Server(服务器)

    • 工具能力的提供者。一个Server可以提供一种或多种工具(例如,一个SQL Server提供sql_query工具;一个文件系统Server提供read_file, list_files工具)。
    • 职责:启动后向Client注册自己提供的工具列表(包括名称、描述、参数schema),并等待Client的调用请求。执行具体的业务逻辑(如执行SQL查询、读取文件、调用JIRA API)并将结果返回给Client。
  • Session

    • Client和Server之间建立的一个持久连接会话,在整个会话期内保持通信。
  • 工具(Tools)

    • Server提供的具体能力,本质是一个个函数。每个工具都有明确的名称描述参数定义(遵循JSON Schema)。模型的“思考”过程就包括选择正确的工具并提供正确的参数。
  • 资源(Resources)

    • 一种特殊的工具,用于提供静态数据(如一个文件的内容、一个数据库表的结构定义)。Client可以预先加载资源内容到模型的上下文中,帮助模型更好地做出决策。

第二部分:MCP 底层通信协议分析

MCP 协议本质上是一系列定义好的 JSON-RPC 2.0 消息,通过 SSE (Server-Sent Events)WebSocket 传输。

1. 协议流程与关键消息

MCP Client MCP Server LLM User 1. 初始化连接 (SSE) 发送 `initialize` 请求 {协议版本, 能力} 回复 `initialize` 结果 {Server 信息, 能力} 发送 `initialized` 通知 2. 工具发现 发送 `tools/list` 通知 {工具列表} 发送 `resources/list` 通知 {资源列表} opt [可选:资源发现] 3. 用户发起请求 用户输入 + 可用工具列表 决策: 调用工具X with 参数Y 4. 工具调用与执行 发送 `tools/call` 请求 {工具名, 参数} 执行工具逻辑 (e.g., 运行SQL查询) 回复 `tools/call` 结果 {内容, 类型} 5. 呈现结果 将工具执行结果呈现给用户 可以继续调用其他工具 loop [持续交互] 6. 关闭连接 发送 `notify/exit` 通知 MCP Client MCP Server LLM User

2. 传输方式

  • SSE (Server-Sent Events)
    • 特点:基于HTTP,单向(Server->Client)流式通信。MCP巧妙地利用了两条SSE连接实现双向通信(Client->Server通过普通HTTP POST)。
    • 优点:简单,兼容性好,易于调试。
  • WebSocket
    • 特点:真正的全双工通信协议。
    • 优点:延迟更低,通信效率更高,是更现代的选择。

第三部分:Java 项目中的 MCP 应用与实践

Java生态在MCP中主要扮演 MCP Server 提供者的角色,即用Java编写各种工具服务。

方案一:使用 Java 开发 MCP Server(推荐)

这是最核心的应用方式。你可以用Java为你公司的内部系统(数据库、CRM、内部API)构建MCP Server,从而让AI安全地使用这些系统。

1. 技术栈选择

  • 核心库:目前MCP官方库主要针对Node.js/Python。Java开发者需要手动实现MCP协议
  • HTTP Server/SSE:使用 Spring Boot 提供HTTP端点并处理SSE连接。WebClientSseEmitter 是得力工具。
  • JSON处理Jackson 用于序列化/反序列化MCP的JSON-RPC消息。
  • 依赖注入Spring Framework 用于管理工具执行所需的Bean(如DataSource, RestTemplate)。

2. 实现步骤

a. 定义工具接口(以SQL查询为例)

/**
 * MCP Tool 的抽象表示
 */
@Data
public class SQLQueryTool implements McpTool {
    private final String name = "sql_query";
    private final String description = "Execute a SELECT SQL query against the company data warehouse and get the results. Do not perform INSERT/UPDATE/DELETE.";
    // 使用JSON Schema定义参数结构
    private final ObjectNode inputSchema = createInputSchema();

    private ObjectNode createInputSchema() {
        // 使用Jackson构建JSON Schema
        return JsonNodeFactory.instance.objectNode()
                .put("type", "object")
                .set("properties", JsonNodeFactory.instance.objectNode()
                        .set("query", JsonNodeFactory.instance.objectNode()
                                .put("type", "string")
                                .put("description", "The SELECT SQL query to execute")));
    }
}

b. 实现MCP Server的核心控制器

@RestController
@RequestMapping("/mcp")
@Slf4j
public class McpServerController {

    @Autowired
    private SQLService sqlService; // 你的业务服务

    private final Map<String, McpTool> tools = Map.of(
            "sql_query", new SQLQueryTool()
    );

    // 处理SSE连接初始化
    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter handleSseConnection() {
        SseEmitter emitter = new SseEmitter(0L); // 无超时
        // 1. 发送 "initialize" 请求(略)
        // 2. 发送 "tools/list" 通知
        sendToolsList(emitter);
        return emitter;
    }

    private void sendToolsList(SseEmitter emitter) {
        try {
            ObjectNode message = JsonNodeFactory.instance.objectNode();
            message.put("jsonrpc", "2.0");
            message.put("method", "tools/list");
            message.set("params", JsonNodeFactory.instance.objectNode()
                    .set("tools", JsonNodeFactory.instance.arrayNode()
                            .addAll(tools.values().stream()
                                    .map(tool -> JsonNodeFactory.instance.objectNode()
                                            .put("name", tool.getName())
                                            .put("description", tool.getDescription())
                                            .set("inputSchema", tool.getInputSchema()))
                                    .collect(Collectors.toList()))));
            emitter.send(SseEmitter.event().data(message.toString()));
        } catch (IOException e) {
            log.error("Failed to send tools list", e);
        }
    }

    // 处理来自Client的HTTP POST请求(工具调用)
    @PostMapping("/call")
    public ResponseEntity<String> handleToolCall(@RequestBody JsonNode request) {
        String method = request.path("method").asText();
        JsonNode params = request.path("params");

        if ("tools/call".equals(method)) {
            String toolName = params.path("name").asText();
            JsonNode arguments = params.path("arguments");

            if ("sql_query".equals(toolName)) {
                String query = arguments.path("query").asText();
                if (!isSafeQuery(query)) {
                    return ResponseEntity.badRequest().body("{\"error\": \"Only SELECT queries are allowed.\"}");
                }
                try {
                    // 执行安全的查询
                    List<Map<String, Object>> result = sqlService.executeSafeQuery(query);
                    // 构建成功的JSON-RPC响应
                    ObjectNode successResponse = buildSuccessResponse(request.path("id"), result);
                    return ResponseEntity.ok(successResponse.toString());
                } catch (Exception e) {
                    // 构建错误的JSON-RPC响应
                    ObjectNode errorResponse = buildErrorResponse(request.path("id"), -32000, e.getMessage());
                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse.toString());
                }
            }
        }
        // ... 处理其他method
        return ResponseEntity.notFound().build();
    }

    private boolean isSafeQuery(String query) {
        // 实现简单的SQL安全检查:只允许SELECT开头的查询
        String trimmedQuery = query.trim().toLowerCase();
        return trimmedQuery.startsWith("select");
    }
}

c. 实现工具执行服务

@Service
public class SQLService {

    @Autowired
    private JdbcTemplate jdbcTemplate; // Spring JDBC

    public List<Map<String, Object>> executeSafeQuery(String query) {
        // 使用JdbcTemplate执行查询,返回List<Map>格式,便于JSON序列化
        return jdbcTemplate.query(query, new ColumnMapRowMapper());
    }
}

方案二:Java 作为 MCP Client(桥接模式)

在某些场景下,你可能需要用Java编写一个MCP Client,作为你现有Java应用和MCP生态之间的桥接器

应用场景

  • 你有一个传统的Java Web应用,你想为其添加AI辅助功能。
  • 你的Java应用充当Client,连接到一个或多个MCP Server(如Python写的文件操作Server),然后将获取到的上下文信息发送给OpenAI/Anthropic的API。

简化示例

@Component
public class McpClientBridge {

    private final WebClient webClient;

    public McpClientBridge(@Value("${mcp.server.url}") String serverUrl) {
        this.webClient = WebClient.builder().baseUrl(serverUrl).build();
    }

    public Flux<String> streamSqlQueryResult(String userRequest) {
        // 1. 连接到MCP Server,获取工具列表 (模拟LLM的决策过程)
        // 2. 假设我们决定调用 sql_query 工具
        String query = "SELECT * FROM sales WHERE quarter = 'Q3'"; // 应由LLM生成,这里简化

        ObjectNode callRequest = JsonNodeFactory.instance.objectNode()
                .put("jsonrpc", "2.0")
                .put("id", 1)
                .put("method", "tools/call")
                .set("params", JsonNodeFactory.instance.objectNode()
                        .put("name", "sql_query")
                        .set("arguments", JsonNodeFactory.instance.objectNode()
                                .put("query", query)));

        // 3. 调用工具并获取结果
        return webClient.post()
                .uri("/call")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(callRequest.toString())
                .retrieve()
                .bodyToMono(String.class)
                .flatMapMany(response -> {
                    // 4. 解析响应,将结果流式返回给调用者(或发送给LLM API)
                    JsonNode jsonNode = JsonUtils.toJsonNode(response);
                    String result = jsonNode.path("result").path("content").asText();
                    return Flux.just("查询结果:", result);
                });
    }
}

第四部分:生产级考量与最佳实践

  1. 安全性第一

    • 输入验证与净化:对工具参数进行严格校验(如SQL注入检查、路径遍历攻击检查)。
    • 权限控制:MCP Server应实现身份认证和授权,不同用户/模型只能访问被允许的工具和数据。
    • 网络隔离:将MCP Server部署在独立的网络区域,仅开放必要的端口。
  2. 错误处理与鲁棒性

    • 为所有工具调用实现全面的异常捕获和友好的错误信息返回。
    • 使用熔断器模式(Resilience4j)防止MCP Server故障导致主应用雪崩。
    • 为MCP连接设置合理的超时和重试机制。
  3. 性能与可观测性

    • 日志记录:详细记录所有工具调用的请求、响应和耗时,用于审计和调试。
    • 指标监控:使用Micrometer暴露Metrics(QPS、延迟、错误率),并接入Prometheus+Grafana。
    • 资源管理:对于耗时长的工具操作,考虑支持异步调用和状态查询。
  4. 开发体验

    • 为你的Java MCP Server编写详细的文档,说明其提供的工具和用法。
    • 可以考虑提供测试客户端,方便开发者调试工具。

总结

MCP 是连接AI模型与现实世界的桥梁,它通过标准化协议解决了工具集成的核心痛点。对于Java开发者而言,当前最大的价值在于用Java构建强大、安全、可靠的企业级MCP Server,将内部系统的能力安全地暴露给AI。

行动建议

  1. 评估场景:识别你公司内部哪些系统(数据库、知识库、ITSM系统)最需要被AI访问。
  2. 原型开发:使用Spring Boot为一个简单工具(如员工目录查询、订单状态查询)实现MCP Server。
  3. 安全评审:对原型进行严格的安全评审。
  4. 逐步推广:在安全可控的前提下,逐步扩大MCP Server的应用范围。

虽然Java在MCP生态中起步稍晚,但其在企业级应用开发中的统治地位,使其成为构建生产环境MCP Server的绝佳选择。随着MCP协议的日益普及,Java开发者在这一领域将大有可为。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐