跳到主要内容
本篇模型上下文协议 (MCP) 概述讨论了其范围核心概念,并提供了一个展示每个核心概念的示例 由于 MCP SDK 抽象了许多底层细节,大多数开发者可能会发现数据层协议部分最为实用。它讨论了 MCP 服务端如何为 AI 应用提供上下文。 有关特定的实现细节,请参阅针对您所用特定语言 SDK 的文档。

范围

模型上下文协议包括以下项目
  • MCP 规范:MCP 的技术规范,概述了客户端和服务端的实现要求。
  • MCP SDK:实现 MCP 协议的不同编程语言 SDK。
  • MCP 开发工具:用于开发 MCP 服务端和客户端的工具,包括 MCP Inspector
  • MCP 参考服务端实现:MCP 服务端的参考实现示例。
MCP 仅专注于上下文交换的协议——它并不规定 AI 应用如何使用 LLM 或如何管理提供的上下文。

MCP 概念

参与者

MCP 采用客户端-服务端架构,其中 MCP 宿主(Host)—— 如 Claude CodeClaude Desktop 等 AI 应用 —— 会建立与一个或多个 MCP 服务端的连接。MCP 宿主通过为每个 MCP 服务端创建一个 MCP 客户端来实现这一点。每个 MCP 客户端与其对应的 MCP 服务端保持专用连接。 使用 STDIO 传输的本地 MCP 服务端通常服务于单个 MCP 客户端,而使用可流式 HTTP 传输的远程 MCP 服务端通常会服务于多个 MCP 客户端。 MCP 架构中的关键参与者包括:
  • MCP 宿主 (Host):协调和管理一个或多个 MCP 客户端的 AI 应用
  • MCP 客户端 (Client):维持与 MCP 服务端连接并获取上下文供 MCP 宿主使用的组件
  • MCP 服务端 (Server):为 MCP 客户端提供上下文的程序
例如:Visual Studio Code 充当 MCP 宿主。当 Visual Studio Code 建立与 MCP 服务端(如 Sentry MCP 服务端)的连接时,Visual Studio Code 运行时会实例化一个 MCP 客户端对象来维持该连接。当 Visual Studio Code 随后连接到另一个 MCP 服务端(如本地文件系统服务端)时,它会实例化另一个 MCP 客户端对象来维持此连接。 请注意,MCP 服务端是指提供上下文数据的程序,无论其运行在何处。MCP 服务端可以在本地或远程执行。例如,当 Claude Desktop 启动文件系统服务端时,该服务端由于使用 STDIO 传输而在同一台机器上本地运行,这通常被称为“本地”MCP 服务端。官方的 Sentry MCP 服务端运行在 Sentry 平台上,并使用可流式 HTTP 传输,这通常被称为“远程”MCP 服务端。

分层

MCP 由两层组成
  • 数据层:定义基于 JSON-RPC 的客户端-服务端通信协议,包括生命周期管理以及工具、资源、提示词和通知等核心原语。
  • 传输层:定义支持客户端和服务端之间数据交换的通信机制和通道,包括特定传输方式的连接建立、消息分帧和授权。
从概念上讲,数据层是内层,而传输层是外层。

数据层

数据层实现了一个基于 JSON-RPC 2.0 的交互协议,定义了消息结构和语义。该层包括
  • 生命周期管理:处理客户端和服务端之间的连接初始化、能力协商和连接终止
  • 服务端特性:使服务端能够提供核心功能,包括用于 AI 操作的“工具”、用于上下文数据的“资源”以及用于交互模板的“提示词”
  • 客户端特性:使服务端能够请求客户端从宿主 LLM 进行采样、引导用户输入以及向客户端记录日志消息
  • 实用特性:支持额外功能,如用于实时更新的“通知”和用于长时间运行操作的“进度跟踪”

传输层

传输层管理客户端和服务端之间的通信通道和认证。它负责处理 MCP 参与者之间的连接建立、消息分帧和安全通信。 MCP 支持两种传输机制:
  • Stdio 传输:使用标准输入/输出流进行同一机器上本地进程间的直接通信,提供无网络开销的最佳性能。
  • 可流式 HTTP 传输:使用 HTTP POST 发送客户端到服务端的请求,并结合可选的服务器发送事件 (SSE) 实现流式功能。此传输方式支持远程服务器通信,并支持标准 HTTP 认证方法,包括 Bearer 令牌、API 密钥和自定义标头。MCP 建议使用 OAuth 来获取身份验证令牌。
传输层将通信细节从协议层中抽象出来,使得相同的 JSON-RPC 2.0 消息格式可以在所有传输机制中使用。

数据层协议

MCP 的核心部分是定义 MCP 客户端和 MCP 服务端之间的架构和语义。开发者可能会发现数据层——尤其是这组原语——是 MCP 中最有趣的部分。这部分定义了开发者从 MCP 服务端向 MCP 客户端共享上下文的方式。 MCP 使用 JSON-RPC 2.0 作为其底层 RPC 协议。客户端和服务端相互发送请求并做出相应响应。在不需要响应时可以使用通知。

生命周期管理

MCP 是一个需要生命周期管理。生命周期管理的目的是协商客户端和服务端都支持的。详细信息可以在规范中找到,示例展示了初始化序列。

原语

MCP 原语是 MCP 中最重要的概念。它们定义了客户端和服务端可以相互提供的内容。这些原语指定了可以与 AI 应用共享的上下文信息类型以及可以执行的操作范围。 MCP 定义了 服务端 可以暴露的三种核心原语:
  • 工具 (Tools):AI 应用可以调用以执行操作的可执行函数(例如:文件操作、API 调用、数据库查询)
  • 资源 (Resources):为 AI 应用提供上下文信息的数据源(例如:文件内容、数据库记录、API 响应)
  • 提示词 (Prompts):帮助构建与语言模型交互的可重用模板(例如:系统提示词、少样本示例)
每种原语类型都有关联的发现 (*/list)、获取 (*/get) 以及在某些情况下的执行 (tools/call) 方法。MCP 客户端将使用 */list 方法来发现可用的原语。例如,客户端可以先列出所有可用工具 (tools/list) 然后执行它们。这种设计允许列表是动态的。 举一个具体的例子,考虑一个提供数据库上下文的 MCP 服务端。它可以暴露用于查询数据库的“工具”,一个包含数据库模式的“资源”,以及一个包含与工具交互的少样本示例的“提示词”。 有关服务端原语的更多详情,请参阅服务端概念 MCP 还定义了 客户端 可以暴露的原语。这些原语允许 MCP 服务端作者构建更丰富的交互。
  • 采样 (Sampling):允许服务端请求客户端 AI 应用进行语言模型补全。当服务端作者想要访问语言模型,但希望保持模型独立且不愿在 MCP 服务端中包含语言模型 SDK 时,这非常有用。他们可以使用 sampling/complete 方法请求客户端 AI 应用进行语言模型补全。
  • 引导 (Elicitation):允许服务端向用户请求额外信息。当服务端作者想要从用户那里获取更多信息或请求对某项操作的确认时,这非常有用。他们可以使用 elicitation/request 方法向用户请求额外信息。
  • 日志记录 (Logging):使服务端能够向客户端发送日志消息,用于调试和监控目的。
有关客户端原语的更多详情,请参阅客户端概念 除了服务端和客户端原语外,该协议还提供了一些横向实用原语,用于增强请求的执行:
  • 任务 (Tasks,实验性):持久执行封装器,支持对 MCP 请求(如昂贵的计算、工作流自动化、批处理、多步操作)进行延迟结果获取和状态跟踪

通知

该协议支持实时通知,以实现服务端和客户端之间的动态更新。例如,当服务端的可用工具发生变化时——比如新功能上线或现有工具被修改——服务端可以发送工具更新通知,以告知已连接的客户端这些变化。通知以 JSON-RPC 2.0 通知消息的形式发送(不期望响应),并使 MCP 服务端能够向已连接的客户端提供实时更新。

示例

数据层

本节提供了 MCP 客户端-服务端交互的逐步演示,重点关注数据层协议。我们将使用 JSON-RPC 2.0 消息演示生命周期序列、工具操作和通知。
1

初始化(生命周期管理)

MCP 从通过能力协商握手进行的生命周期管理开始。如生命周期管理部分所述,客户端发送 initialize 请求以建立连接并协商支持的特性。
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "elicitation": {}
    },
    "clientInfo": {
      "name": "example-client",
      "version": "1.0.0"
    }
  }
}

理解初始化交互

初始化过程是 MCP 生命周期管理的关键部分,具有几个重要目的
  1. 协议版本协商protocolVersion 字段(例如 “2025-06-18”)确保客户端和服务端使用的是兼容的协议版本。这可以防止不同版本尝试交互时可能发生的通信错误。如果没有协商出共同兼容的版本,则应终止连接。
  2. 能力发现capabilities 对象允许各方声明它们支持哪些特性,包括它们可以处理哪些原语(工具、资源、提示词)以及是否支持通知等特性。这通过避免不支持的操作来实现高效通信。
  3. 身份交换clientInfoserverInfo 对象提供标识和版本信息,用于调试和兼容性目的。
在此示例中,能力协商展示了 MCP 原语是如何声明的:客户端能力
  • "elicitation": {} - 客户端声明它可以处理用户交互请求(可以接收 elicitation/create 方法调用)
服务端能力:
  • "tools": {"listChanged": true} - 服务端支持工具原语,并且可以在工具列表更改时发送 tools/list_changed 通知
  • "resources": {} - 服务端还支持资源原语(可以处理 resources/listresources/read 方法)
初始化成功后,客户端发送通知表示已就绪
通知 (Notification)
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

在 AI 应用中如何运作

在初始化期间,AI 应用的 MCP 客户端管理器会建立与已配置服务端的连接,并存储它们的能力以备后用。应用使用此信息来确定哪些服务端可以提供特定类型的功能(工具、资源、提示词)以及它们是否支持实时更新。
AI 应用初始化的伪代码
# Pseudo Code
async with stdio_client(server_config) as (read, write):
    async with ClientSession(read, write) as session:
        init_response = await session.initialize()
        if init_response.capabilities.tools:
            app.register_mcp_server(session, supports_tools=True)
        app.set_server_ready(session)
2

工具发现(原语)

既然连接已建立,客户端就可以通过发送 tools/list 请求来发现可用的工具。此请求是 MCP 工具发现机制的基础——它允许客户端在尝试使用工具之前了解服务端上有哪些工具可用。
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list"
}

理解工具发现请求

tools/list 请求很简单,不包含任何参数。

理解工具发现响应

响应包含一个 tools 数组,提供有关每个可用工具的详尽元数据。这种基于数组的结构允许服务端同时暴露多个工具,同时保持不同功能之间的清晰界限。响应中的每个工具对象包含几个关键字段:
  • name:服务端命名空间内工具的唯一标识符。这是工具执行的主键,应遵循清晰的命名模式(例如:calculator_arithmetic 而不仅仅是 calculate
  • title:工具的人类可读显示名称,客户端可以向用户显示
  • description:关于工具用途及何时使用的详细说明
  • inputSchema:一个 JSON Schema,定义了预期的输入参数,支持类型验证并提供关于必填和可选参数的清晰文档

在 AI 应用中如何运作

AI 应用从所有连接的 MCP 服务端获取可用工具,并将它们合并到一个语言模型可以访问的统一工具注册表中。这使得 LLM 能够理解它可以执行哪些操作,并在对话期间自动生成适当的工具调用。
AI 应用工具发现的伪代码
# Pseudo-code using MCP Python SDK patterns
available_tools = []
for session in app.mcp_server_sessions():
    tools_response = await session.list_tools()
    available_tools.extend(tools_response.tools)
conversation.register_available_tools(available_tools)
3

工具执行(原语)

客户端现在可以使用 tools/call 方法执行工具。这演示了 MCP 原语在实践中是如何使用的:在发现可用工具后,客户端可以使用适当的参数调用它们。

理解工具执行请求

tools/call 请求遵循结构化格式,确保客户端和服务端之间的类型安全和清晰通信。请注意,我们使用的是发现响应中的完整工具名称 (weather_current),而不是简化的名称
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "weather_current",
    "arguments": {
      "location": "San Francisco",
      "units": "imperial"
    }
  }
}

工具执行的关键要素

请求结构包含几个重要组件
  1. name:必须与发现响应中的工具名称 (weather_current) 完全匹配。这确保了服务端可以正确识别要执行哪个工具。
  2. arguments:包含由工具的 inputSchema 定义的输入参数。在本例中
    • location: “San Francisco”(必填参数)
    • units: “imperial”(可选参数,如果不指定则默认为 “metric”)
  3. JSON-RPC 结构:使用标准 JSON-RPC 2.0 格式,带有用于请求-响应关联的唯一 id

理解工具执行响应

响应展示了 MCP 灵活的内容系统
  1. content 数组:工具响应返回内容对象的数组,支持丰富的多格式响应(文本、图像、资源等)
  2. 内容类型:每个内容对象都有一个 type 字段。在本例中,"type": "text" 表示纯文本内容,但 MCP 支持针对不同用例的各种内容类型。
  3. 结构化输出:响应提供了可操作的信息,AI 应用可以将其用作语言模型交互的上下文。
这种执行模式允许 AI 应用动态调用服务端功能,并接收可以集成到与语言模型对话中的结构化响应。

在 AI 应用中如何运作

当语言模型在对话期间决定使用工具时,AI 应用会拦截该工具调用,将其路由到适当的 MCP 服务端执行,并将结果作为对话流的一部分返回给 LLM。这使得 LLM 能够访问实时数据并在外部世界执行操作。
# Pseudo-code for AI application tool execution
async def handle_tool_call(conversation, tool_name, arguments):
    session = app.find_mcp_session_for_tool(tool_name)
    result = await session.call_tool(tool_name, arguments)
    conversation.add_tool_result(result.content)
4

实时更新(通知)

MCP 支持实时通知,使服务端能够告知客户端更改而无需明确请求。这演示了通知系统,这是一个保持 MCP 连接同步和响应灵敏的关键特性。

理解工具列表变更通知

当服务端的可用工具发生变化时——例如新功能上线、现有工具被修改或工具暂时不可用——服务端可以主动通知已连接的客户端
请求
{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed"
}

MCP 通知的主要特性

  1. 无需响应:请注意通知中没有 id 字段。这遵循了 JSON-RPC 2.0 通知语义,即不期望也不发送响应。
  2. 基于能力:此通知仅由在初始化期间在其工具能力中声明了 "listChanged": true 的服务端发送(如步骤 1 所示)。
  3. 事件驱动:服务端根据内部状态更改决定何时发送通知,使 MCP 连接具有动态性和响应性。

客户端对通知的响应

收到此通知后,客户端通常会通过请求更新后的工具列表来做出反应。这创建了一个刷新循环,使客户端对可用工具的理解保持最新
请求
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/list"
}

为什么通知很重要

此通知系统至关重要,原因如下
  1. 动态环境:工具可能会根据服务端状态、外部依赖或用户权限而出现或消失
  2. 效率:客户端不需要轮询更改;当更新发生时它们会被通知
  3. 一致性:确保客户端始终拥有关于可用服务端能力的准确信息
  4. 实时协作:支持能够适应不断变化的上下文的响应式 AI 应用
这种通知模式不仅限于工具,还扩展到了其他 MCP 原语,实现了客户端和服务端之间全面的实时同步。

在 AI 应用中如何运作

当 AI 应用收到有关工具更改的通知时,它会立即刷新其工具注册表并更新 LLM 的可用能力。这确保了正在进行的对话始终可以访问最新的工具集,并且 LLM 可以动态适应新功能。
# Pseudo-code for AI application notification handling
async def handle_tools_changed_notification(session):
    tools_response = await session.list_tools()
    app.update_available_tools(session, tools_response.tools)
    if app.conversation.is_active():
        app.conversation.notify_llm_of_new_capabilities()