深入浅出 Model Context Protocol (MCP):从核心概念到多语言服务端实战 2026-10-07 程序之旅,记录 暂无评论 5 次阅读 # 深入浅出 Model Context Protocol (MCP):从核心概念到多语言服务端实战 随着大语言模型(LLM)能力的飞速跃升,让 AI 能够“触及现实”成为了核心诉求。无论是读取本地文件、查询企业数据库,还是调用内部 API,我们需要一种标准化的协议来连接 AI 与外部世界。 **Model Context Protocol (MCP)** 正是为此而生。它就像是 AI 界的“USB-C 接口”,由 Anthropic 提出,旨在标准化大模型与外部工具、资源的交互方式。 本文将系统性地梳理 MCP 的核心概念、架构选择,并通过 TypeScript、Python 以及 Java (SSE) 的实战案例,带你从零开始完成 MCP 服务端的开发与生产级部署。 ## 一、 MCP 的三大核心能力 开发 MCP 服务端,本质上是向大模型暴露以下三种能力: 1. **Tools(工具)**:赋予模型“执行动作”的能力(如:计算器、查询天气、执行 SQL)。模型通过提供参数来调用,服务端返回执行结果。 2. **Resources(资源)**:提供“只读”的数据源(如:本地日志文件、系统状态视图)。模型可以读取这些上下文信息。 3. **Prompts(提示词模板)**:预设的工作流模板,帮助用户快速触发复杂的指令序列。 ## 二、 架构选择:通信传输层决定部署形态 MCP 协议基于 **JSON-RPC 2.0**,但它的底层传输层主要分为两大流派,这决定了你的服务如何运行: - **基于 `stdio`(标准输入输出)**:最主流的本地开发方式。宿主应用(如 Claude Desktop、Cursor)作为父进程直接唤起你的 MCP Server 子进程。双方通过进程的 `stdin` 和 `stdout` 传递消息。极简、安全(无开放端口),但仅限本地单机使用。 - **基于 `SSE` (Server-Sent Events) + `HTTP`**:远程/云端方式。支持分布式部署和跨设备访问。非常适合将企业内部 API 或 SaaS 包装成 MCP 服务,供全公司甚至全网使用。 ## 三、 本地开发实战:TypeScript 与 Python (`stdio`) 对于操作本地文件、快速验证工具,官方的 TypeScript 和 Python SDK 是最佳选择。 ### 1. TypeScript 实现极简服务器 TS 生态与前端结合紧密,借助 `@modelcontextprotocol/sdk` 和 `zod`,我们可以快速构建一个具有严格参数校验的 MCP 服务。 ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; const server = new McpServer({ name: "demo-server", version: "1.0.0" }); // 注册一个加法工具 server.tool( "calculate_sum", "计算两个数字的和", { a: z.number(), b: z.number() }, async ({ a, b }) => ({ content: [{ type: "text", text: `计算结果是: ${a + b}` }] }) ); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Server running on stdio"); // 必须使用 stderr,stdout 被协议占用 } main(); ``` ### 2. Python 实现数据查询工具 Python 在数据科学和 API 集成方面具有得天独厚的优势。 ```python import asyncio from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app = Server("python-weather-server") @app.list_tools() async def list_tools() -> list[Tool]: return [ Tool( name="get_weather", description="获取指定城市天气", inputSchema={ "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict) -> list[TextContent]: if name == "get_weather": city = arguments.get("city") return [TextContent(type="text", text=f"{city} 天气晴朗,25°C")] raise ValueError("Unknown tool") async def main(): async with stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream, app.create_initialization_options()) if __name__ == "__main__": asyncio.run(main()) ``` **集成测试:** 修改 Claude Desktop 的配置文件 `claude_desktop_config.json`,将命令(如 `npx tsx index.ts` 或 `python server.py`)配置进去,重启客户端即可在界面中唤起该工具。 ## 四、 企业级远程实战:Java Spring Boot + SSE 当需要将公司内部的业务系统暴露给模型时,基于 SSE 的 HTTP 传输是首选。MCP 的 SSE 模式需要两个端点模拟全双工通信: 1. **`GET /sse`**:建立长连接,用于服务端向客户端推送响应。 2. **`POST /message`**:客户端通过此接口发送 JSON-RPC 请求。 ### 1. 核心控制器实现 ```java import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.http.MediaType; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @RestController @EnableScheduling // 开启定时保活任务 @CrossOrigin public class McpSseServer { private final Map activeSessions = new ConcurrentHashMap<>(); private final ObjectMapper objectMapper = new ObjectMapper(); // 1. 建立 SSE 连接 @GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter subscribe() { String sessionId = UUID.randomUUID().toString(); SseEmitter emitter = new SseEmitter(0L); // 0L 禁用 Spring 框架超时 activeSessions.put(sessionId, emitter); // 资源清理回调,防止内存泄漏 emitter.onCompletion(() -> activeSessions.remove(sessionId)); emitter.onTimeout(() -> activeSessions.remove(sessionId)); emitter.onError((e) -> activeSessions.remove(sessionId)); try { // MCP 规范:连接建立后必须下发 endpoint 事件,告知 POST 路径 emitter.send(SseEmitter.event().name("endpoint").data("/message?sessionId=" + sessionId)); } catch (IOException e) { emitter.completeWithError(e); } return emitter; } // 2. 接收并处理 JSON-RPC 报文 @PostMapping(path = "/message") public void receiveMessage(@RequestParam String sessionId, @RequestBody JsonNode request) { SseEmitter emitter = activeSessions.get(sessionId); if (emitter == null) return; String method = request.has("method") ? request.get("method").asText() : ""; ObjectNode response = objectMapper.createObjectNode(); response.put("jsonrpc", "2.0"); response.set("id", request.get("id")); // 必须原样回传请求 ID try { if ("tools/list".equals(method)) { // 组装工具列表返回 (代码略) } else if ("tools/call".equals(method)) { // 执行工具逻辑并返回 (代码略) } // 处理完毕后,通过 SSE 推送结果 emitter.send(SseEmitter.event().name("message").data(response)); } catch (Exception e) { e.printStackTrace(); } } // 3. 服务端保活心跳 (防止 Nginx 等网关断开空闲连接) @Scheduled(fixedRate = 15000) public void sendHeartbeat() { activeSessions.forEach((sessionId, emitter) -> { try { emitter.send(SseEmitter.event().name("ping").data("keep-alive")); } catch (IOException e) { activeSessions.remove(sessionId); } }); } } ``` ### 2. 生产环境避坑指南(SSE 稳定性保障) SSE 极其容易因为网络闲置被网关掐断。必须从以下层面进行加固: - **应用层**:如上方代码所示,除了 `SseEmitter(0L)`,还要在 application.yml 中设置 `spring.mvc.async.request-timeout: -1`。 - **心跳机制**:利用 `@Scheduled` 每 15 秒发送 `ping` 事件。 - **网关层 (Nginx)**:Nginx 默认会缓冲响应,导致 SSE 消息滞留或断开,必须针对 `/sse` 路径增加特定配置: ```nginx location /sse { proxy_pass http://your_backend; proxy_buffering off; # 关闭缓冲 proxy_cache off; chunked_transfer_encoding on;# 声明分块传输 proxy_read_timeout 86400s; # 调大网关超时时间 proxy_http_version 1.1; proxy_set_header Connection ""; } ``` ## 五、 协议底层:读懂 JSON-RPC 2.0 报文 无论你使用哪种语言和传输层,MCP 在网络中传递的载体永远是 **JSON-RPC 2.0** 报文。JSON-RPC 规定了所有的报文必须是 JSON 格式,且必须包含 `"jsonrpc": "2.0"`。在 MCP 的通信中,你只需要掌握以下 **4 种核心报文格式**,就能看懂底层抓包,快速定位 Bug。 ### 1. 请求报文 (Request) 客户端主动发给服务端,且**期望得到返回结果**。核心特征是**必带 `id` 字段**。 ```json { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "get_weather", "arguments": { "city": "Beijing" } }, "id": "req-1001" } ``` ### 2. 成功响应报文 (Success Response) 服务端处理成功后返回。核心特征是**包含 `result` 字段,绝对无 `error`,且 `id` 必须与请求一致**。 ```json { "jsonrpc": "2.0", "result": { "content": [ { "type": "text", "text": "Beijing weather is Sunny, 25°C" } ] }, "id": "req-1001" } ``` ### 3. 错误响应报文 (Error Response) 服务端处理失败时返回。核心特征是**包含 `error` 对象,绝对无 `result`,包含 `id`**。官方预留了 `-32700` (解析错误)、`-32601` (找不到方法)、`-32602` (参数无效) 等标准错误码。 ```json { "jsonrpc": "2.0", "error": { "code": -32601, "message": "Method 'tools/call_unknown' not found", "data": "The requested tool does not exist on this server." }, "id": "req-1001" } ``` ### 4. 通知报文 (Notification) 客户端单向发给服务端,**不需要结果**(如初始化完成通知)。核心特征是:**绝对没有 `id` 字段**。服务端收到后**严禁**返回任何 Response。 ```json { "jsonrpc": "2.0", "method": "notifications/initialized" } ``` **在 SSE 架构中的对应关系:** 客户端发来的 POST `request Body` 就是**请求报文**或**通知报文**;而你的 Java 代码处理后,通过 `SseEmitter.send()` 发送出去的 payload 数据,就是**响应报文**或**错误报文**。 ## 结语 开发 MCP 并非高不可攀,其本质就是“一个 JSON-RPC 服务器 + 特定格式的元数据描述”。对于本地提效,Python/TypeScript 配合 `stdio` 足以应对;而面向企业级生产环境,利用 Java/Go 构建基于 SSE 的稳定服务则是未来的方向。掌握 MCP 的开发,不仅是拓展大模型能力的关键,更是下一代 AI 基础设施建设的必备技能。 打赏: 微信, 支付宝 标签: ai, agent, mcp 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。