2025年5月12日
MCP简介
MCP(Model Context Protocol)是一个开放协议,旨在标准化应用程序向大语言模型(LLMs)提供上下文的方式。您可以将 MCP 想象成 AI 应用的 USB-C 接口——正如 USB-C 为设备连接各类外设提供了统一标准,MCP 也为 AI 模型连接不同数据源和工具建立了标准化桥梁。
为什么选择 MCP?
MCP 帮助在 LLMs 之上构建Agent和复杂工作流。由于 LLMs 需要频繁对接数据和工具,MCP 提供:
持续增长的预构建集成库,实现 LLM 即插即用
灵活切换不同 LLM 服务商的能力
核心架构
重点理解MCP的CS架构设计、传输层规范。
模型上下文协议 (MCP) 建立在灵活、可扩展的架构之上,可实现 LLM 应用程序和集成之间的无缝通信。
概述
MCP 采用客户端-服务器架构,支持宿主程序连接多个服务器:
MCP 宿主应用(MCP Hosts):如 Claude Desktop、IDE 或 AI 工具等需要通过 MCP 获取数据的应用程序
MCP 客户端(MCP Clients):运行在宿主应用中,与服务器保持 1:1 连接的协议客户端
MCP 服务器(MCP Servers):通过标准化 Model Context Protocol 暴露特定能力的轻量级程序,向客户端提供上下文、工具和提示词
本地数据源(Local Data Sources):MCP 服务器可安全访问的计算机本地文件、数据库和服务
远程服务(Remote Services):MCP 服务器可通过互联网(如 API)连接的外部系统
flowchart LR subgraph "Your Computer" Host["Host with MCP Client\n(Claude, IDEs, Tools)"] S1["MCP Server A"] S2["MCP Server B"] S3["MCP Server C"] Host <-->|"MCP Protocol"| S1 Host <-->|"MCP Protocol"| S2 Host <-->|"MCP Protocol"| S3 S1 <--> D1[("Local\nData Source A")] S2 <--> D2[("Local\nData Source B")] end subgraph "Internet" S3 <-->|"Web APIs"| D3[("Remote\nService C")] end
flowchart LR subgraph "Host" client1[MCP Client] client2[MCP Client] end subgraph "Server Process" server1[MCP Server] end subgraph "Server Process" server2[MCP Server] end client1 <-->|Transport Layer| server1 client2 <-->|Transport Layer| server2
核心组件
核心组件包括:协议层、传输层、消息类型
协议层
协议层负责消息封装、请求/响应关联及上层的通信模式
// 处理client和server通信的MCP sessioninterface McpSession {// 向模型对应方发送请求,并期望获得类型为 T 的响应。<T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef);// 向模型client或server发送不带参数的通知default Mono<Void> sendNotification(String method) {return sendNotification(method, null);}// 向模型client或server发送带参数的通知Mono<Void> sendNotification(String method, Object params);// 关闭session并异步释放全部关联的资源Mono<Void> closeGracefully();// 关闭session并释放全部关联的资源void close();}
传输层
传输层负责客户端与服务端的实际通信。
MCP 支持多种传输机制:
- 标准输入输出传输(Stdio)
使用标准输入/输出进行通信
适用于本地进程间通信
- HTTP SSE 传输
使用服务器推送事件(SSE)实现服务端到客户端通信
使用 HTTP POST 实现客户端到服务端通信
所有传输均采用JSON-RPC 2.0 协议交换消息。
JSON-RPC:www.jsonrpc.org/
JSON-RPC示例:
服务端请求 --> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}客户端接收 <-- {"jsonrpc": "2.0", "result": 19, "id": 1}服务端请求 --> {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 2}客户端接收 <-- {"jsonrpc": "2.0", "result": 19, "id": 2}
消息类型
MCP 定义以下主要消息类型:
1. **请求(Requests)**需要对方响应
record JSONRPCRequest ( // ... String method, // 方法标识 Object params // 请求参数)
2. **结果(Results)**表示请求成功响应
record JSONRPCResponse ( // ... Object result, // 结果数据 JSONRPCError error)
3. **错误(Errors)**表示请求失败:
record JSONRPCError ( int code, // 错误代码 String message, // 错误描述 Object data)
4. **通知(Notifications)**无需响应的单向消息
record JSONRPCNotification ( // ... String method, Object params)
连接生命周期
一、初始化阶段
客户端发送包含协议版本与能力的initialize
请求
服务端返回协议版本与能力声明
客户端发送initialized
通知确认
开始正常消息交换
sequenceDiagram participant Client participant Server Client->>Server: initialize request Server->>Client: initialize response Client->>Server: initialized notification Note over Client,Server: Connection ready for use
二、消息交换阶段
初始化完成后支持以下模式:
请求-响应:客户端或服务端发送请求,对方响应
通知:任意一方发送单向消息
三、终止阶段
连接可通过以下方式终止:
通过close()
方法优雅关闭
传输层断开连接
错误状态触发断开
错误处理
MCP 定义标准错误代码:
class ErrorCodes { // Standard JSON-RPC error codes int PARSE_ERROR = -32700; int INVALID_REQUEST = -32600; int METHOD_NOT_FOUND = -32601; int INVALID_PARAMS = -32602; int INTERNAL_ERROR = -32603;}
SDK 与应用可自定义高于 -32000 的错误代码。
错误通过以下方式传递:
请求的错误响应
传输层的错误事件
协议层的错误处理
实现示例
基础 MCP 服务端实现示例:
var transport = new StdioServerTransportProvider();// Configure server capabilities with resource supportvar capabilities = McpSchema.ServerCapabilities.builder() .resources(false, true) // No subscribe support, but list changes notifications .tools(true) // Tool support with list changes notifications .prompts(true) // Prompt support with list changes notifications .logging() // Logging support .build();// Create the server with both tool and resource capabilitiesvar server = McpServer.async(transport) .serverInfo("MCP Demo Server", "1.0.0") .capabilities(capabilities) .resources(Collections.emptyList()) .prompts(Collections.emptyList()) .tools(Collections.emptyList()) .build();
最佳实践
- 本地通信
使用 stdio 传输协议
适用于同机进程通信
简化进程管理
- 远程通信
使用 SSE 传输协议
需考虑身份验证与授权等安全因素
基本元素
MCP的基本元素包括:
Resource
Prompt
Tool
Sampling
Root
其中,以Resource、Prompt、Tool为核心元素。
资源(Resources)
资源是模型上下文协议(Model Context Protocol, MCP)中的核心基本元素,它允许服务器暴露数据和内容,供客户端读取并用作LLM交互的上下文。
概述
资源代表MCP服务器希望提供给客户端的任何类型的数据。这可以包括:
文件内容
数据库记录
API响应
实时系统数据
屏幕截图和图像
日志文件
等等
每个资源都由唯一的URI标识,可以包含文本或二进制数据。
资源URI
资源使用以下格式的URI进行标识:
[协议]://[主机]/[路径]
例如:
file:///home/user/documents/report.pdf
postgres://database/customers/schema
screen://localhost/display1
协议和路径结构由MCP服务器实现定义。服务器可以定义自己的自定义URI方案。
资源类型
资源可以包含两种类型的内容:
文本资源
文本资源包含UTF-8编码的文本数据。例如:
源代码
配置文件
日志文件
JSON/XML数据
纯文本
二进制资源
二进制资源包含以base64编码的原始二进制数据。例如:
图像
PDF文件
音频文件
视频文件
其他非文本格式
资源发现
客户端(Client)可以通过两种主要方式发现可用资源
直接资源
服务器通过resources/list
端点暴露具体资源的列表。
record Resource( String uri, // 资源的唯一标识符 String name, // 人类可读的名称 String description, // 可选描述 String mimeType, // 可选MIME类型 Annotations annotations // 可选的注解信息) implements Annotated {}
资源模版
对于动态资源,服务器可以暴露URI模板,客户端可以用它来构建有效的资源URI:
record ResourceTemplate( String uriTemplate, // 遵循RFC6570的URI模板,例如:/user/{userId} String name, String description, String mimeType, Annotations annotations) implements Annotated {}
读取资源
要读取资源,客户端需发送带有资源URI的resources/read
请求。
服务器以资源内容列表响应:
// 返回的资源结构record ReadResourceResult( List<ResourceContents> contents // 资源列表) {}// 文本资源内容record TextResourceContents( String uri, String mimeType, String text // 文本数据) implements ResourceContents {}// 二进制资源内容record BlobResourceContents( String uri, String mimeType, String blob // 二进制数据) implements ResourceContents {}
资源更新通知
MCP通过两种机制支持资源的实时更新:
列表变更
服务器可以通过notifications/resources/list_changed
通知客户端其可用资源列表的变更。
内容变更
客户端可以订阅特定资源的更新:
客户端发送带有资源URI的resources/subscribe
当资源变更时,服务器发送notifications/resources/updated
客户端可以通过resources/read
获取最新内容
客户端可以通过resources/unsubscribe
取消订阅
最佳实践
为动态内容实现资源模板
对频繁变更的资源使用订阅
考虑对大型资源列表进行分页
适当时缓存资源内容
提示词(Prompts)
提示词模板使服务器能够定义可重用的提示模板和工作流程,客户端可以轻松地向用户和LLM呈现这些内容。它们提供了一种强大的方式来标准化和共享常见的LLM交互。
Tips:提示模板设计为用户控制的,这意味着它们从服务器暴露给客户端时,目的是让用户能够明确地选择使用它们。
概述
MCP中的提示模板是预定义的模板,可以:
接受动态参数
包含来自资源的上下文
链接多个交互
指导特定的工作流程
作为UI元素呈现(如斜杠命令)
提示词模版结构
// 提示词模版结构record Prompt( String name, String description, List<PromptArgument> arguments // 可选的参数列表) {}// 提示词模版参数record PromptArgument( String name, String description, Boolean required // 参数是否必填) {}
发现提示词模版
客户端可以通过prompts/list
端点发现可用的提示模板
服务端响应:
record ListPromptsResult( List<Prompt> prompts, // 提示词模版列表 String nextCursor // 分页查询使用的游标) {}
使用提示词模版
要使用提示模板,客户端需发送prompts/get
请求
record GetPromptRequest( String name, Map<String, Object> arguments) implements Request {}
响应的提示词内容有三种类型:
text:普通文本
image:图片
resource:资源(文本或二进制)
// 请求{ method: "prompts/get", params: { name: "analyze-code", arguments: { language: "python" } }}// 响应{ description: "分析Python代码以寻找潜在的改进", messages: [ { role: "user", content: { type: "text", text: "请分析以下Python代码以寻找潜在的改进:\n\n```python\ndef calculate_sum(numbers):\n total = 0\n for num in numbers:\n total = total + num\n return total\n\nresult = calculate_sum([1, 2,3, 4, 5])\nprint(result)\n```" } } ]}
工具(Tools)
工具是MCP中的一种强大的基本元素,它使服务器能够向客户端暴露可执行的功能。通过工具,LLM可以与外部系统交互,执行计算,并在现实世界中采取行动。
工具被设计为由模型控制,这意味着工具从服务器暴露给客户端时,其目的是让AI模型能够自动调用它们。
概述
MCP中的工具允许服务器暴露可执行函数,这些函数可被客户端调用并由LLM使用来执行操作。工具的关键方面包括:
发现:客户端可以通过tools/list
端点列出可用的工具
调用:工具通过tools/call
端点被调用,服务器执行请求的操作并返回结果
灵活性:工具可以从简单的计算到复杂的API交互不等
与资源类似,工具由唯一名称标识,并可以包含描述来指导其使用。然而,与资源不同,工具代表可以修改状态或与外部系统交互的动态操作。
工具定义结构
{ name: string; // 工具的唯一标识符 description?: string; // 人类可读的描述 inputSchema: { // 工具参数的JSON Schema type: "object", properties: { ... } // 工具特定的参数 }, annotations?: { // 关于工具行为的可选提示 title?: string; // 工具的人类可读标题 readOnlyHint?: boolean; // 如果为true,工具不会修改其环境 destructiveHint?: boolean; // 如果为true,工具可能执行破坏性更新 idempotentHint?: boolean; // 如果为true,使用相同参数重复调用不会产生额外效果 openWorldHint?: boolean; // 如果为true,工具与外部实体交互 }}
inputSchema
结构:
record JsonSchema( String type, // 数据类型 Map<String, Object> properties, // key:参数名称, value:参数的描述Schema List<String> required, // 必填的参数名称集合 Boolean additionalProperties, Map<String, Object> defs, Map<String, Object> definitions) {}
工具示例
以下是服务器可以提供的工具类型示例:
系统操作
与本地系统交互的工具:
{ name: "execute_command", description: "运行shell命令", inputSchema: { type: "object", properties: { command: { type: "string" }, args: { type: "array", items: { type: "string" } } } }}
API集成
封装外部API的工具:
{ name: "github_create_issue", description: "创建GitHub issue",inputSchema: { type: "object", properties: { title: { type: "string" }, body: { type: "string" }, labels: { type: "array", items: { type: "string" } } } }}
数据处理
转换或分析数据的工具:
{ name: "analyze_csv", description: "分析CSV文件", inputSchema: { type: "object", properties: { filepath: { type: "string" }, operations: { type: "array", items: { enum: ["sum", "average", "count"] } }} }}
工具发现和更新
MCP支持动态工具发现:
客户端可以随时列出可用工具
服务器可以使用notifications/tools/list_changed
通知客户端工具变更
工具可以在运行时添加或移除
工具定义可以更新(但应谨慎进行)
错误处理
工具错误应在结果对象中报告,而不是作为MCP协议级别的错误。这允许LLM查看并可能处理错误。当工具遇到错误时:
在结果中设置isError
为true
在content
数组中包含错误详情
try { // Tool operation const result = performOperation(); return { content: [ { type: "text", text: `Operation successful: ${result}` } ] };} catch (error) { return { isError: true, content: [ { type: "text", text: `Error: ${error.message}` } ] };}
最佳实践
提供清晰、描述性的名称和描述
为参数使用详细的JSON Schema定义
在工具描述中包含示例,以演示模型应如何使用它们
让工具的功能更聚焦和原子
采样(Sampling)
采样是MCP的一项强大功能,它允许服务器通过客户端请求LLM补全内容,从而实现复杂的智能代理行为,同时保持安全性和隐私性。
采样工作原理
采样流程遵循以下步骤:
服务器向客户端发送sampling/createMessage
请求
客户端审核请求并可以修改它
客户端使用LLM生成补全内容
客户端审核LLM生成的内容
客户端将结果返回给服务器
这种的设计确保用户能够控制LLM看到和生成的内容。
根源(Roots)
Roots(根源)是MCP中定义服务器可操作边界的概念。它提供了一种方式,使得客户端能够告知服务器相关资源及其位置。
Roots是客户端建议服务器应关注的URI。当客户端连接到服务器时,它会声明服务器应该使用哪些根源。虽然主要用于文件系统路径,但根源可以是任何有效的URI,包括HTTP URL。例如:
file:///home/user/projects/myapphttps://api.example.com/v1
示例
{ "roots": [ { "uri": "file:///home/user/projects/frontend", "name": "前端代码库" }, { "uri": "https://api.example.com/v1", "name": "API端点" } ]}
MCP实践
自己编写一个MCP Server
官方提供了多种语言的SDK:
但是不同语言的SDK的质量差距较大,例如java-sdk相比python-sdk就垃圾不少。推荐使用python-sdk。
第一步,安装python项目的环境管理工具uv
curl -LsSf https://astral.sh/uv/install.sh | sh# 或 通过 brew 安装brew install uv
第二步,设置python项目环境
# 为我们的项目创建一个新目录uv init mcp-server-democd mcp-server-demo# 创建虚拟环境并激活它uv venvsource .venv/bin/activate# 安装依赖uv add "mcp[cli]"# 创建我们的MCP服务器文件touch python-mcp-test.py
第三步,编写server代码,代码示例如下
# python-mcp-test.pyfrom mcp.server.fastmcp import FastMCP# 创建一个 Servermcp = FastMCP("python-mcp-test")# 添加一个工具@mcp.tool(description="整数加法计算器")def add(a: int, b: int) -> int: """Add two numbers""" return a + b# 添加一个资源@mcp.resource("files:///documents/QLExpress.md")def get_ql_express_knowledge() -> str: """QL express knowledge""" with open('resources/QLExpress.md', 'r') as f: return f.read()# 添加一个提示词@mcp.prompt(description="查询部门")def query_dept(dept_no: str) -> str: """Query department""" return f"查询编号为{dept_no}的部门信息"if __name__ == "__main__": mcp.run()
第四步,调试server
运行命令,然后在浏览器页面进行调试
uv run mcp dev python-mcp-test.py
以HSF服务作为MCP Server
请参考文章:ata.atatech.org/articles/11…
以Cline作为MCP Host
第一步,安装VS Code,并在VS Code上下载Cline
第二步,开启MCP功能
第三步,连接LLM,以连接DeepSeek为例
第四步,配置MCP Server的连接信息。此处以连接自定义MCP Server为例,也可以使用市场上现成的MCP Server。
示例:
Java MCP Server:
{ "mcpServers": { "spring-ai-mcp-rpc": { "command": "java", "args": [ "-Dspring.ai.mcp.server.stdio=true", "-jar", "// todo jar包文件绝对路径,例如:/Users/yuguo/tools/mvn_repository/org/example/test-demo/0.0.1-SNAPSHOT/test-demo-0.0.1-SNAPSHOT.jar" ], "transportType": "stdio" } }}
Python MCP Server:
{ "mcpServers": { "python-mcp-test": { "command": "/opt/homebrew/bin/uv", "args": [ "--directory", "// todo python文件的文件夹路径,例如:/Users/yuguo/PycharmProjects/mcp-server-demo", "run", "// todo python文件名称,例如:python-mcp-test.py" ], "transportType": "stdio" } }}
HSF SSE:
{ "mcpServers": { "// todo 你的HSF mcp server名称": { "url": "// todo 从HSF控制台复制过来的url" } }}
最终效果:
CLINE自动调用“部门查询”的HSF服务查到了M3775是淘天。