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 服务商的能力
与Function Calling的区别
Function Calling是模型API的能力。模型API内部根据自家模型的微调数据,规定了描述工具的格式、模型API请求工具调用的格式、给模型API工具返回结果的格式。
MCP规范了 工具的发现、工具的描述、工具的调用、工具的返回结构。
sequenceDiagram
participant User
participant Agent
participant ModelAPI
participant Tool
User ->> Agent: 查询今天杭州的天气
Agent ->> Agent: 提示词工程
rect rgba(0, 150, 0, 0.1)
note right of Agent: MCP
Agent ->> Agent: 获取可用工具列表
end
Agent ->> Agent: 把可用工具的描述信息<br>转换为模型API规定的格式
rect rgba(0, 0, 255, 0.1)
note right of Agent: Function Calling
Agent ->> ModelAPI: 工具列表+提示词
ModelAPI ->> ModelAPI: 把工具信息和用户问题<br>转换为文本,调用模型
ModelAPI -->> Agent: 选取工具+参数schema
end
Agent ->> Agent: 把参数schema转换为工具参数
rect rgba(0, 150, 0, 0.1)
note right of Agent: MCP
Agent ->> Tool: 调用工具函数
Tool -->> Agent: 返回结果
end
Agent ->> Agent: 把工具返回结果<br>转换为模型API规定的格式
rect rgba(0, 0, 255, 0.1)
note right of Agent: Function Calling
Agent ->> ModelAPI: 工具返回结果
ModelAPI -->> Agent: 杭州天气是xxxx
end
Agent -->> User: 杭州天气是xxxx
OpenAI对Funciton Calling的描述:platform.openai.com/docs/guides…
OpenAI Funciton Calling的工具定义格式“恰好”和MCP协议中的工具定义格式非常非常相似。
核心架构
重点理解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 session
interface 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 support
var 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 capabilities
var 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/myapp
https://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-demo
cd mcp-server-demo
# 创建虚拟环境并激活它
uv venv
source .venv/bin/activate
# 安装依赖
uv add "mcp[cli]"
# 创建我们的MCP服务器文件
touch python-mcp-test.py
第三步,编写server代码,代码示例如下
# python-mcp-test.py
from mcp.server.fastmcp import FastMCP
# 创建一个 Server
mcp = 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是淘天。