核心架构
Model Context Protocol (MCP) 构建在一个灵活、可扩展的架构之上,能够实现 LLM 应用程序与集成之间的无缝通信。本文档涵盖了核心架构组件和概念。
概述
MCP 遵循客户端-服务器架构,其中:
- 主机 是发起连接的 LLM 应用程序(如 Claude Desktop 或 IDE)
- 客户端 在主机应用程序内部与服务器保持 1:1 的连接
- 服务器 向客户端提供上下文、工具和提示-
核心组件
协议层
协议层处理消息帧、请求/响应链接以及高级通信模式。
typescript
class Protocol<Request, Notification, Result> {
// 处理传入的请求
setRequestHandler<T>(schema: T, handler: (request: T, extra: RequestHandlerExtra) => Promise<Result>): void
// 处理传入的通知
setNotificationHandler<T>(schema: T, handler: (notification: T) => Promise<void>): void
// 发送请求并等待响应
request<T>(request: Request, schema: T, options?: RequestOptions): Promise<T>
// 发送单向通知
notification(notification: Notification): Promise<void>
}
关键类包括:
Protocol
Client
Server
传输层
传输层处理客户端和服务器之间的实际通信。MCP 支持多种传输机制:
-
Stdio 传输
- 使用标准输入/输出进行通信
- 适用于本地进程
-
HTTP with SSE 传输
- 使用 Server-Sent Events 进行服务器到客户端的消息传递
- 使用 HTTP POST 进行客户端到服务器的消息传递
所有传输都使用 JSON-RPC 2.0 来交换消息。有关 Model Context Protocol 消息格式的详细信息,请参阅 规范。
消息类型
MCP 有以下主要类型的消息:
- 请求 期望从对方获得响应:
typescript
interface Request {
method: string;
params?: { ... };
}
- 结果 是请求的成功响应:
typescript
interface Result {
[key: string]: unknown;
}
- 错误 表示请求失败:
typescript
interface Error {
code: number;
message: string;
data?: unknown;
}
- 通知 是单向消息,不期望响应:
typescript
interface Notification {
method: string;
params?: { ... };
}
连接生命周期
1. 初始化
- 客户端发送带有协议版本和功能的
initialize
请求 - 服务器响应其协议版本和功能
- 客户端发送
initialized
通知作为确认 - 正常消息交换开始
2. 消息交换
初始化后,支持以下模式:
- 请求-响应:客户端或服务器发送请求,对方响应
- 通知:任一方发送单向消息
3. 终止
任一方都可以终止连接:
- 通过
close()
进行干净关闭 - 传输断开
- 错误条件
错误处理
MCP 定义了以下标准错误代码:
typescript
enum ErrorCode {
// 标准 JSON-RPC 错误代码
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603
}
SDK 和应用程序可以在 -32000 以上定义自己的错误代码。
错误通过以下方式传播:
- 对请求的错误响应
- 传输上的错误事件
- 协议级错误处理程序
实现示例
以下是实现 MCP 服务器的基本示例:
typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
resources: {}
}
});
// 处理请求
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "example://resource",
name: "Example Resource"
}
]
};
});
// 连接传输
const transport = new StdioServerTransport();
await server.connect(transport);
最佳实践
传输选择
-
本地通信
- 对本地进程使用 stdio 传输
- 适用于同一台机器的通信
- 简单的进程管理
-
远程通信
- 对需要 HTTP 兼容性的场景使用 SSE
- 考虑包括身份验证和授权的安全影响
消息处理
-
请求处理
- 彻底验证输入
- 使用类型安全的模式
- 优雅地处理错误
- 实现超时
-
进度报告
- 对长时间操作使用进度令牌
- 逐步报告进度
- 在已知时包括总进度
-
错误管理
- 使用适当的错误代码
- 包括有用的错误消息
- 在错误时清理资源
安全考虑
-
传输安全
- 对远程连接使用 TLS
- 验证连接来源
- 在需要时实施身份验证
-
消息验证
- 验证所有传入消息
- 清理输入
- 检查消息大小限制
- 验证 JSON-RPC 格式
-
资源保护
- 实施访问控制
- 验证资源路径
- 监控资源使用情况
- 限制请求速率
-
错误处理
- 不要泄露敏感信息
- 记录与安全相关的错误
- 实施适当的清理
- 处理 DoS 场景
调试和监控
-
日志记录
- 记录协议事件
- 跟踪消息流
- 监控性能
- 记录错误
-
诊断
- 实施健康检查
- 监控连接状态
- 跟踪资源使用情况
- 分析性能
-
测试
- 测试不同的传输
- 验证错误处理
- 检查边缘情况
- 负载测试服务器
资源
资源是Model Context Protocol (MCP)中的基本元素,允许服务器暴露数据和内容,供客户端读取并用作LLM交互的上下文。
注意 :资源设计为应用程序控制,意味着客户端应用程序可以决定如何以及何时使用它们。不同的MCP客户端可能会以不同的方式处理资源。例如:
- Claude Desktop目前要求用户在使用资源之前明确选择资源
- 其他客户端可能会基于启发式自动选择资源
- 某些实现甚至可能允许AI模型本身决定使用哪些资源
服务器作者在实现资源支持时应准备好处理这些交互模式。为了自动向模型暴露数据,服务器作者应使用模型控制 的原语,例如工具。
概述
资源表示MCP服务器希望向客户端提供的任何类型的数据。这可以包括:
- 文件内容
- 数据库记录
- API响应
- 实时系统数据
- 截图和图像
- 日志文件
- 以及其他
每个资源由唯一的URI标识,可以包含文本或二进制数据。
资源URI
资源使用以下格式的URI进行标识:
plaintext
[protocol]://[host]/[path]
例如:
file:///home/user/documents/report.pdf
postgres://database/customers/schema
screen://localhost/display1
协议和路径结构由MCP服务器实现定义。服务器可以定义自己的自定义URI方案。
资源类型
资源可以包含两种类型的内容:
文本资源
文本资源包含UTF-8编码的文本数据。适用于:
- 源代码
- 配置文件
- 日志文件
- JSON/XML数据
- 纯文本
二进制资源
二进制资源包含base64编码的原始二进制数据。适用于:
- 图像
- 音频文件
- 视频文件
- 其他非文本格式
资源发现
客户端可以通过两种主要方法发现可用资源:
直接资源
服务器通过resources/list
端点暴露具体资源列表。每个资源包括:
typescript
{
uri: string; // 资源的唯一标识符
name: string; // 人类可读的名称
description?: string; // 可选描述
mimeType?: string; // 可选的MIME类型
}
资源模板
对于动态资源,服务器可以暴露URI模板,客户端可以使用这些模板构建有效的资源URI:
typescript
{
uriTemplate: string; // 遵循RFC 6570的URI模板
name: string; // 此类资源的人类可读名称
description?: string; // 可选描述
mimeType?: string; // 所有匹配资源的可选MIME类型
}
读取资源
要读取资源,客户端使用资源URI发出resources/read
请求。
服务器响应资源内容列表:
typescript
{
contents: [
{
uri: string; // 资源的URI
mimeType?: string; // 可选的MIME类型
// 以下之一:
text?: string; // 用于文本资源
blob?: string; // 用于二进制资源(base64编码)
}
]
}
提示 :服务器可能会在一次
resources/read
请求中返回多个资源。例如,当读取目录时,可以返回目录中的文件列表。
资源更新
MCP通过两种机制支持资源的实时更新:
列表更改
服务器可以通过notifications/resources/list_changed
通知客户端可用资源列表的变化。
内容更改
客户端可以订阅特定资源的更新:
- 客户端发送带有资源URI的
resources/subscribe
- 当资源发生变化时,服务器发送
notifications/resources/updated
- 客户端可以使用
resources/read
获取最新内容 - 客户端可以使用
resources/unsubscribe
取消订阅
示例实现
以下是一个在MCP服务器中实现资源支持的简单示例:
typescript
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
resources: {}
}
});
// 列出可用资源
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain"
}
]
};
});
// 读取资源内容
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
if (uri === "file:///logs/app.log") {
const logContents = await readLogFile();
return {
contents: [
{
uri,
mimeType: "text/plain",
text: logContents
}
]
};
}
throw new Error("Resource not found");
});
最佳实践
在实现资源支持时:
- 使用清晰、描述性的资源名称和URI
- 包含有用的描述以指导LLM理解
- 在已知时设置适当的MIME类型
- 为动态内容实现资源模板
- 对频繁变化的资源使用订阅
- 使用清晰的错误消息优雅地处理错误
- 考虑对大型资源列表进行分页
- 在适当时缓存资源内容
- 在处理前验证URI
- 记录自定义URI方案
安全考虑
在暴露资源时:
- 验证所有资源URI
- 实施适当的访问控制
- 清理文件路径以防止目录遍历
- 谨慎处理二进制数据
- 考虑对资源读取进行速率限制
- 审计资源访问
- 在传输中加密敏感数据
- 验证MIME类型
- 对长时间运行的读取实施超时
- 适当地处理资源清理
Prompts(提示)
Prompts 使服务器能够定义可重用的提示模板和工作流,客户端可以轻松地向用户和 LLM 公开这些模板。它们提供了一种强大的方式来标准化和共享常见的 LLM 交互。
注意: Prompts 设计为 用户可控,意味着它们从服务器暴露给客户端,用户可以明确选择它们进行使用。
概述
MCP(Model Context Protocol)中的 Prompts 是预定义的模板,可用于:
- 接受动态参数
- 包含资源中的上下文
- 链接多个交互
- 引导特定的工作流
- 作为 UI 元素(如斜杠命令)展示
Prompt 结构
每个 Prompt 由以下字段定义:
typescript
{
name: string; // Prompt 的唯一标识符
description?: string; // 人类可读的描述
arguments?: [ // 可选参数列表
{
name: string; // 参数标识符
description?: string; // 参数描述
required?: boolean; // 该参数是否必填
}
]
}
发现 Prompts
客户端可以通过 prompts/list
端点获取可用的 Prompts:
css
typescript
复制编辑
// 请求
{
method: "prompts/list"
}
// 响应
{
prompts: [
{
name: "analyze-code",
description: "分析代码以寻找潜在的改进点",
arguments: [
{
name: "language",
description: "编程语言",
required: true
}
]
}
]
}
使用 Prompts
客户端可以通过 prompts/get
请求使用 Prompt:
swift
typescript
复制编辑
// 请求
{
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```"
}
}
]
}
动态 Prompts
Prompts 可以是动态的,并包括:
嵌入资源上下文
json
json
复制编辑
{
"name": "analyze-project",
"description": "分析项目日志和代码",
"arguments": [
{
"name": "timeframe",
"description": "要分析日志的时间范围",
"required": true
},
{
"name": "fileUri",
"description": "要审查的代码文件 URI",
"required": true
}
]
}
当处理 prompts/get
请求时:
swift
json
复制编辑
{
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "分析这些系统日志和代码文件中的问题:"
}
},
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "logs://recent?timeframe=1h",
"text": "[2024-03-14 15:32:11] ERROR: Connection timeout in network.py:127\n[2024-03-14 15:32:15] WARN: Retrying connection (attempt 2/3)\n[2024-03-14 15:32:20] ERROR: Max retries exceeded",
"mimeType": "text/plain"
}
}
},
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "file:///path/to/code.py",
"text": "def connect_to_service(timeout=30):\n retries = 3\n for attempt in range(retries):\n try:\n return establish_connection(timeout)\n except TimeoutError:\n if attempt == retries - 1:\n raise\n time.sleep(5)\n\ndef establish_connection(timeout):\n # 连接实现\n pass",
"mimeType": "text/x-python"
}
}
}
]
}
多步骤工作流
vbnet
typescript
复制编辑
const debugWorkflow = {
name: "debug-error",
async getMessages(error: string) {
return [
{
role: "user",
content: {
type: "text",
text: `我遇到了一个错误:${error}`
}
},
{
role: "assistant",
content: {
type: "text",
text: "我会帮助分析这个错误。你尝试过什么方法?"
}
},
{
role: "user",
content: {
type: "text",
text: "我尝试了重启服务,但错误仍然存在。"
}
}
];
}
};
示例实现
以下是一个在 MCP 服务器中实现 Prompts 的完整示例:
javascript
typescript
复制编辑
import { Server } from "@modelcontextprotocol/sdk/server";
import {
ListPromptsRequestSchema,
GetPromptRequestSchema
} from "@modelcontextprotocol/sdk/types";
const PROMPTS = {
"git-commit": {
name: "git-commit",
description: "生成 Git 提交信息",
arguments: [
{
name: "changes",
description: "Git diff 或更改描述",
required: true
}
]
},
"explain-code": {
name: "explain-code",
description: "解释代码的工作原理",
arguments: [
{
name: "code",
description: "要解释的代码",
required: true
},
{
name: "language",
description: "编程语言",
required: false
}
]
}
};
const server = new Server({
name: "example-prompts-server",
version: "1.0.0"
}, {
capabilities: {
prompts: {}
}
});
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: Object.values(PROMPTS)
};
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const prompt = PROMPTS[request.params.name];
if (!prompt) {
throw new Error(`未找到 Prompt:${request.params.name}`);
}
if (request.params.name === "git-commit") {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `为以下更改生成简洁但描述性的提交信息:\n\n${request.params.arguments?.changes}`
}
}
]
};
}
if (request.params.name === "explain-code") {
const language = request.params.arguments?.language || "未知";
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `解释这段 ${language} 代码的工作原理:\n\n${request.params.arguments?.code}`
}
}
]
};
}
throw new Error("未找到 Prompt 实现");
});
最佳实践
- 使用清晰、描述性的 Prompt 名称
- 为 Prompts 和参数提供详细的描述
- 验证所有必填参数
- 友好地处理缺失参数
- 考虑 Prompt 模板的版本管理
- 在合适的场景下缓存动态内容
- 实现错误处理
- 记录参数格式
- 考虑 Prompt 的可组合性
- 使用不同输入测试 Prompts
UI 集成
Prompts 可以在客户端 UI 中以以下方式展示:
- 斜杠命令
- 快捷操作
- 右键菜单项
- 命令面板入口
- 指导性工作流
- 交互式表单
安全考虑
- 验证所有参数
- 清理用户输入
- 考虑速率限制
- 实施访问控制
- 审计 Prompt 使用
- 适当处理敏感数据
- 验证生成内容
- 实施超时机制
- 考虑 Prompt 注入风险
- 记录安全要求
Tools
Tools 是 Model Context Protocol (MCP) 中的一个强大基础元素,它使服务器能够向客户端暴露可执行的功能。通过 Tools,LLMs 可以与外部系统交互、执行计算并在现实世界中采取行动。
注意 : Tools 被设计为模型控制的,这意味着 Tools 从服务器暴露给客户端时,AI 模型能够自动调用它们(需要人工批准)。
概述
MCP 中的 Tools 允许服务器暴露可执行的函数,客户端可以调用这些函数,并由 LLMs 使用来执行操作。Tools 的关键方面包括:
- 发现 : 客户端可以通过
tools/list
端点列出可用的 Tools - 调用 : Tools 通过
tools/call
端点调用,服务器执行请求的操作并返回结果 - 灵活性: Tools 可以从简单的计算到复杂的 API 交互
与 resources 类似,Tools 通过唯一的名称进行标识,并且可以包含描述以指导其使用。然而,与 resources 不同,Tools 表示可以修改状态或与外部系统交互的动态操作。
Tool 定义结构
每个 Tool 的定义结构如下:
typescript
{
name: string; // Tool 的唯一标识符
description?: string; // 人类可读的描述
inputSchema: { // Tool 参数的 JSON Schema
type: "object",
properties: { ... } // Tool 特定的参数
}
}
实现 Tools
以下是在 MCP 服务器中实现基本 Tool 的示例:
typescript
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// 定义可用的 Tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "calculate_sum",
description: "Add two numbers together",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
}
}]
};
});
// 处理 Tool 执行
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "calculate_sum") {
const { a, b } = request.params.arguments;
return {
content: [
{
type: "text",
text: String(a + b)
}
]
};
}
throw new Error("Tool not found");
});
示例 Tool 模式
以下是一些服务器可以提供的 Tool 类型示例:
系统操作
与本地系统交互的 Tools:
typescript
{
name: "execute_command",
description: "Run a shell command",
inputSchema: {
type: "object",
properties: {
command: { type: "string" },
args: { type: "array", items: { type: "string" } }
}
}
}
API 集成
封装外部 API 的 Tools:
typescript
{
name: "github_create_issue",
description: "Create a GitHub issue",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
body: { type: "string" },
labels: { type: "array", items: { type: "string" } }
}
}
}
数据处理
转换或分析数据的 Tools:
typescript
{
name: "analyze_csv",
description: "Analyze a CSV file",
inputSchema: {
type: "object",
properties: {
filepath: { type: "string" },
operations: {
type: "array",
items: {
enum: ["sum", "average", "count"]
}
}
}
}
}
最佳实践
在实现 Tools 时:
- 提供清晰、描述性的名称和描述
- 使用详细的 JSON Schema 定义参数
- 在 Tool 描述中包含示例,以演示模型应如何使用它们
- 实现适当的错误处理和验证
- 对长时间操作使用进度报告
- 保持 Tool 操作专注且原子化
- 记录预期的返回值结构
- 实现适当的超时
- 考虑对资源密集型操作进行速率限制
- 记录 Tool 使用情况以便调试和监控
安全考虑
在暴露 Tools 时:
输入验证
- 根据 Schema 验证所有参数
- 清理文件路径和系统命令
- 验证 URL 和外部标识符
- 检查参数大小和范围
- 防止命令注入
访问控制
- 在需要时实施身份验证
- 使用适当的授权检查
- 审核 Tool 使用情况
- 限制请求速率
- 监控滥用行为
错误处理
- 不要向客户端暴露内部错误
- 记录与安全相关的错误
- 适当处理超时
- 在错误后清理资源
- 验证返回值
Tool 发现和更新
MCP 支持动态 Tool 发现:
- 客户端可以随时列出可用的 Tools
- 服务器可以使用
notifications/tools/list_changed
通知客户端 Tools 的变化 - Tools 可以在运行时添加或删除
- Tool 定义可以更新(尽管应谨慎进行)
错误处理
Tool 错误应在结果对象中报告,而不是作为 MCP 协议级别的错误。这允许 LLM 查看并可能处理错误。当 Tool 遇到错误时:
- 在结果中将
isError
设置为true
- 在
content
数组中包含错误详细信息
以下是正确处理 Tool 错误的示例:
typescript
try {
// Tool 操作
const result = performOperation();
return {
content: [
{
type: "text",
text: `Operation successful: ${result}`
}
]
};
} catch (error) {
return {
isError: true,
content: [
{
type: "text",
text: `Error: ${error.message}`
}
]
};
}
这种方法允许 LLM 看到发生了错误,并可能采取纠正措施或请求人工干预。
测试 Tools
MCP Tools 的全面测试策略应包括:
- 功能测试: 验证 Tools 在有效输入下正确执行,并正确处理无效输入
- 集成测试: 使用真实和模拟的依赖项测试 Tool 与外部系统的交互
- 安全测试: 验证身份验证、授权、输入清理和速率限制
- 性能测试: 检查负载下的行为、超时处理和资源清理
- 错误处理: 确保 Tools 通过 MCP 协议正确报告错误并清理资源
Sampling
Sampling 是 MCP 的一个强大功能,它允许服务器通过客户端请求 LLM 补全,从而实现复杂的代理行为,同时保持安全性和隐私性。
注意:MCP 的这一功能在 Claude Desktop 客户端中尚未支持。
How sampling works
采样流程遵循以下步骤:
- 服务器向客户端发送
sampling/createMessage
请求 - 客户端审查请求并可以修改它
- 客户端从 LLM 中采样
- 客户端审查补全
- 客户端将结果返回给服务器
这种"人在回路"的设计确保用户对 LLM 看到和生成的内容保持控制。
Message format
采样请求使用标准化的消息格式:
typescript
{
messages: [
{
role: "user" | "assistant",
content: {
type: "text" | "image",
// For text:
text?: string,
// For images:
data?: string, // base64 encoded
mimeType?: string
}
}
],
modelPreferences?: {
hints?: [{
name?: string // Suggested model name/family
}],
costPriority?: number, // 0-1, importance of minimizing cost
speedPriority?: number, // 0-1, importance of low latency
intelligencePriority?: number // 0-1, importance of capabilities
},
systemPrompt?: string,
includeContext?: "none" | "thisServer" | "allServers",
temperature?: number,
maxTokens: number,
stopSequences?: string[],
metadata?: Record<string, unknown>
}
Request parameters
Messages
messages
数组包含要发送给 LLM 的对话历史。每条消息包含:
role
: 可以是 "user" 或 "assistant"content
: 消息内容,可以是:- 带有
text
字段的文本内容 - 带有
data
(base64)和mimeType
字段的图像内容
- 带有
Model preferences
modelPreferences
对象允许服务器指定其模型选择偏好:
hints
: 模型名称建议数组,客户端可以使用这些建议来选择适当的模型:name
: 可以匹配完整或部分模型名称的字符串(例如 "claude-3", "sonnet")- 客户端可以将提示映射到不同提供商的等效模型
- 多个提示按优先顺序评估
- 优先级值(0-1 标准化):
costPriority
: 最小化成本的重要性speedPriority
: 低延迟响应的重要性intelligencePriority
: 高级模型能力的重要性
客户端根据这些偏好和可用模型做出最终的模型选择。
System prompt
可选的 systemPrompt
字段允许服务器请求特定的系统提示。客户端可以修改或忽略此提示。
Context inclusion
includeContext
参数指定要包含的 MCP 上下文:
"none"
: 不包含额外的上下文"thisServer"
: 包含来自请求服务器的上下文"allServers"
: 包含来自所有连接的 MCP 服务器的上下文
客户端控制实际包含的上下文。
Sampling parameters
通过以下参数微调 LLM 采样:
temperature
: 控制随机性(0.0 到 1.0)maxTokens
: 生成的最大 token 数stopSequences
: 停止生成的序列数组metadata
: 额外的提供商特定参数
Response format
客户端返回补全结果:
typescript
{
model: string, // Name of the model used
stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string,
role: "user" | "assistant",
content: {
type: "text" | "image",
text?: string,
data?: string,
mimeType?: string
}
}
Example request
以下是向客户端请求采样的示例:
json
{
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "What files are in the current directory?"
}
}
],
"systemPrompt": "You are a helpful file system assistant.",
"includeContext": "thisServer",
"maxTokens": 100
}
}
Best practices
在实现采样时:
- 始终提供清晰、结构良好的提示
- 正确处理文本和图像内容
- 设置合理的 token 限制
- 通过
includeContext
包含相关上下文 - 在使用前验证响应
- 优雅地处理错误
- 考虑对采样请求进行速率限制
- 记录预期的采样行为
- 使用各种模型参数进行测试
- 监控采样成本
Human in the loop controls
采样设计时考虑了人工监督:
For prompts
- 客户端应向用户显示提议的提示
- 用户应能够修改或拒绝提示
- 系统提示可以被过滤或修改
- 上下文包含由客户端控制
For completions
- 客户端应向用户显示补全
- 用户应能够修改或拒绝补全
- 客户端可以过滤或修改补全
- 用户控制使用哪个模型
Security considerations
在实现采样时:
- 验证所有消息内容
- 清理敏感信息
- 实施适当的速率限制
- 监控采样使用情况
- 加密传输中的数据
- 处理用户数据隐私
- 审计采样请求
- 控制成本暴露
- 实施超时
- 优雅地处理模型错误
Common patterns
Agentic workflows
采样支持以下代理模式:
- 读取和分析资源
- 根据上下文做出决策
- 生成结构化数据
- 处理多步骤任务
- 提供交互式帮助
Context management
上下文的最佳实践:
- 请求最小必要的上下文
- 清晰地构建上下文
- 处理上下文大小限制
- 根据需要更新上下文
- 清理过时的上下文
Error handling
稳健的错误处理应:
- 捕获采样失败
- 处理超时错误
- 管理速率限制
- 验证响应
- 提供后备行为
- 适当记录错误
Limitations
请注意以下限制:
- 采样取决于客户端的能力
- 用户控制采样行为
- 上下文大小有限制
- 可能适用速率限制
- 应考虑成本
- 模型可用性不同
- 响应时间不同
- 并非所有内容类型都支持
Roots
Roots 是 MCP 中的一个概念,用于定义服务器可以操作的边界。它们提供了一种方式,让客户端能够告知服务器相关的资源及其位置。
什么是 Roots?
Root 是客户端建议服务器应关注的 URI。当客户端连接到服务器时,它会声明服务器应处理哪些 roots。虽然主要用于文件系统路径,但 roots 可以是任何有效的 URI,包括 HTTP URL。
例如,roots 可以是:
plaintext
file:///home/user/projects/myapp
https://api.example.com/v1
为什么使用 Roots?
Roots 有几个重要的用途:
- 指导:它们告知服务器相关的资源和位置
- 清晰:Roots 明确了哪些资源属于你的工作区
- 组织:多个 roots 允许你同时处理不同的资源
Roots 如何工作?
当客户端支持 roots 时,它会:
- 在连接期间声明
roots
能力 - 向服务器提供建议的 roots 列表
- 在 roots 发生变化时通知服务器(如果支持)
虽然 roots 是信息性的,并不强制执行,但服务器应:
- 尊重提供的 roots
- 使用 root URI 来定位和访问资源
- 优先处理 root 边界内的操作
常见用例
Roots 通常用于定义:
- 项目目录
- 仓库位置
- API 端点
- 配置位置
- 资源边界
最佳实践
在使用 roots 时:
- 仅建议必要的资源
- 使用清晰、描述性的名称来命名 roots
- 监控 root 的可访问性
- 优雅地处理 root 的变化
示例
以下是一个典型的 MCP 客户端如何暴露 roots 的示例:
json
{
"roots": [
{
"uri": "file:///home/user/projects/frontend",
"name": "Frontend Repository"
},
{
"uri": "https://api.example.com/v1",
"name": "API Endpoint"
}
]
}
此配置建议服务器同时关注本地仓库和 API 端点,并在逻辑上保持它们的分离。
Transports
在 Model Context Protocol (MCP) 中,Transports 提供了客户端和服务器之间通信的基础。Transport 负责处理消息发送和接收的底层机制。
Message Format
MCP 使用 JSON-RPC 2.0 作为其传输格式。Transport 层负责将 MCP 协议消息转换为 JSON-RPC 格式进行传输,并将接收到的 JSON-RPC 消息转换回 MCP 协议消息。
有三种类型的 JSON-RPC 消息:
Requests
typescript
{
jsonrpc: "2.0",
id: number | string,
method: string,
params?: object
}
Responses
typescript
{
jsonrpc: "2.0",
id: number | string,
result?: object,
error?: {
code: number,
message: string,
data?: unknown
}
}
Notifications
typescript
{
jsonrpc: "2.0",
method: string,
params?: object
}
Built-in Transport Types
MCP 包含两种标准的 Transport 实现:
Standard Input/Output (stdio)
stdio Transport 通过标准输入和输出流进行通信。这对于本地集成和命令行工具特别有用。
使用 stdio 的场景:
- 构建命令行工具
- 实现本地集成
- 需要简单的进程通信
- 使用 shell 脚本
typescript
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {}
});
const transport = new StdioServerTransport();
await server.connect(transport);
Server-Sent Events (SSE)
SSE Transport 通过 HTTP POST 请求实现客户端到服务器的通信,并通过服务器到客户端的流进行通信。
使用 SSE 的场景:
- 仅需要服务器到客户端的流
- 在受限网络中工作
- 实现简单的更新
typescript
import express from "express";
const app = express();
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {}
});
let transport: SSEServerTransport | null = null;
app.get("/sse", (req, res) => {
transport = new SSEServerTransport("/messages", res);
server.connect(transport);
});
app.post("/messages", (req, res) => {
if (transport) {
transport.handlePostMessage(req, res);
}
});
app.listen(3000);
Custom Transports
MCP 使得为特定需求实现自定义 Transport 变得容易。任何 Transport 实现只需要符合 Transport 接口:
你可以为以下场景实现自定义 Transport:
- 自定义网络协议
- 专用通信通道
- 与现有系统集成
- 性能优化
typescript
interface Transport {
// Start processing messages
start(): Promise<void>;
// Send a JSON-RPC message
send(message: JSONRPCMessage): Promise<void>;
// Close the connection
close(): Promise<void>;
// Callbacks
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
}
Error Handling
Transport 实现应处理各种错误场景:
- 连接错误
- 消息解析错误
- 协议错误
- 网络超时
- 资源清理
示例错误处理:
typescript
class ExampleTransport implements Transport {
async start() {
try {
// Connection logic
} catch (error) {
this.onerror?.(new Error(`Failed to connect: ${error}`));
throw error;
}
}
async send(message: JSONRPCMessage) {
try {
// Sending logic
} catch (error) {
this.onerror?.(new Error(`Failed to send message: ${error}`));
throw error;
}
}
}
Best Practices
在实现或使用 MCP Transport 时:
- 正确处理连接生命周期
- 实现适当的错误处理
- 在连接关闭时清理资源
- 使用适当的超时
- 在发送前验证消息
- 记录 Transport 事件以便调试
- 在适当的情况下实现重连逻辑
- 处理消息队列中的背压
- 监控连接健康状态
- 实施适当的安全措施
Security Considerations
在实现 Transport 时:
Authentication and Authorization
- 实施适当的认证机制
- 验证客户端凭据
- 使用安全的令牌处理
- 实施授权检查
Data Security
- 使用 TLS 进行网络传输
- 加密敏感数据
- 验证消息完整性
- 实施消息大小限制
- 清理输入数据
Network Security
- 实施速率限制
- 使用适当的超时
- 处理拒绝服务场景
- 监控异常模式
- 实施适当的防火墙规则
Debugging Transport
调试 Transport 问题的技巧:
- 启用调试日志
- 监控消息流
- 检查连接状态
- 验证消息格式
- 测试错误场景
- 使用网络分析工具
- 实施健康检查
- 监控资源使用情况
- 测试边缘情况
- 使用适当的错误跟踪