MCP 实战指南:如何在项目中使用 Model Context Protocol 及其通信原理

Model Context Protocol(MCP)正在成为 AI 应用连接外部工具与数据的「事实标准」。本文从通信原理讲起,逐步讲清它在你项目里该怎么用、怎么通信、有哪些坑。

目录

[MCP 是什么,解决什么问题](#MCP 是什么,解决什么问题)

[核心架构:Host / Client / Server 三层](#核心架构:Host / Client / Server 三层)

[三大原语:Tools / Resources / Prompts](#三大原语:Tools / Resources / Prompts)

[通信协议:JSON-RPC 2.0](#通信协议:JSON-RPC 2.0)

[传输方式:stdio 与 Streamable HTTP](#传输方式:stdio 与 Streamable HTTP)

通信全流程:一次完整的生命周期

一个完整的工具调用报文长什么样

[如何在你的项目里使用 MCP](#如何在你的项目里使用 MCP)

注意事项与安全红线

常见误区与最佳实践

总结与扩展阅读


1. MCP 是什么,解决什么问题

在 MCP 出现之前,一个 AI 应用想接入外部能力(数据库、文件系统、GitHub、企业内部 API......),基本是「一应用一适配」:每个数据源都要写一套专门的集成代码。N 个 AI 应用 × M 个数据源,就是 N×M 的适配工作量

MCP 把这个局面改成了 N+M:它定义了一套统一的、开放的标准协议,让 AI 应用通过同一个接口就能连接任意实现了该协议的工具和数据源。

一个广为流传的类比:MCP 之于 AI 应用,就像 USB-C 之于电子设备。 过去每个设备要配一根专用线,现在一个口、一根线通吃。

具体来说,MCP 由 Anthropic 于 2024 年 11 月 开源,定位是「模型上下文协议」------标准化 AI 应用(宿主)与外部工具/数据源(服务端)之间的通信方式。它不绑定任何模型厂商,Claude、GPT、Gemini、各类 IDE 和 Agent 框架都能用。

协议演进(截至 2025-11-25 最新版):

|------------|---------|----------------------------------------------|
| 版本 | 日期 | 关键变化 |
| 2024-11-05 | 2024.11 | 首次发布:stdio + HTTP+SSE,核心原语 |
| 2025-03-26 | 2025.03 | Streamable HTTP 取代旧 SSE 设计,加入会话管理与 OAuth 2.1 |
| 2025-06-18 | 2025.06 | 结构化输出、Elicitation(人机协同)、改进授权 |
| 2025-11-25 | 2025.11 | Tasks(异步任务)、Extensions 框架、企业级认证、图标 |

注意:旧的 HTTP+SSE 双端点传输已在 2025-11-25 版中被 标记为废弃 ,新项目应使用 Streamable HTTP


2. 核心架构:Host / Client / Server 三层

MCP 是一个「参与者模型」,有三类角色:

  • Host(宿主):承载 AI 对话的应用,比如 Claude Desktop、Cursor、你自己写的 Agent 服务。它负责接收用户输入、调用大模型、并把结果呈现给用户。
  • Client(客户端) :协议实现层,运行在 Host 内部。每个 Client 与一个 Server 保持 1:1 连接。它负责把 Host 的意图翻译成标准的 JSON-RPC 消息发给 Server。
  • Server(服务端):暴露具体能力的一方,比如「查天气」「读数据库」「操作 GitHub」。一个 Host 可以同时连接多个 Server。

为什么要有 Client 这一层? 把「宿主」和「协议」解耦。Host 专注产品体验,Client 专注协议细节(连接管理、能力协商、消息路由),这样同一个 Client 实现可以被不同 Host 复用。


3. 三大原语:Tools / Resources / Prompts

MCP 规定 Server 可以向 AI 暴露三类「原语」(primitives),它们回答三个不同的问题:

|-------------------|--------------|----------------|--------------------|
| 原语 | 回答的问题 | 方向 | 例子 |
| Tools(工具) | 我能做什么 | 模型触发 → 执行动作 | 查询数据库、发邮件、创建工单 |
| Resources(资源) | 我有什么数据可读 | 宿主读取 → 获取上下文 | 配置文件、日志、数据库 schema |
| Prompts(提示词) | 有什么模板可复用 | 用户/宿主选择 → 套用模板 | 代码审查模板、SQL 生成模板 |

三者意图不同,这也是 MCP 和传统「函数调用(Function Calling)」最大的区别之一:Tools 由模型决定何时调用,Prompts 由用户决定用哪个,Resources 则是被读取的上下文

除了 Server 侧的原语,MCP 还定义了客户端(Client/Host)侧的能力,让 Server 能「反客为主」:

  • Sampling:Server 反过来请求 Host 帮它做一次 LLM 补全。这样 Server 不用自带模型 SDK,也能「借」宿主的大模型能力。
  • Elicitation:Server 请求用户补充信息或确认操作(人机协同、二次确认)。
  • Logging:Server 向 Client 推送结构化日志,方便调试和监控。

一个常见误区:很多人以为 MCP 只有「工具调用」。其实 Resources(喂上下文)和 Prompts(喂模板)同样重要,很多场景下它们比工具更好用。


4. 通信协议:JSON-RPC 2.0

MCP 的「信的内容」用的是 JSON-RPC 2.0 ,全部消息 UTF-8 编码。它只有四种消息形态:

  1. 请求(Request) :带 id,期待一个响应。
  2. 响应(Response / Result) :带 id,对应某个请求的结果。
  3. 错误(Error) :带 id,对应某个请求的失败。
  4. 通知(Notification)不带 id,单向发送、不期待响应。

// 请求:调用一个工具

{ "jsonrpc": "2.0", "id": 7, "method": "tools/call",

"params": { "name": "search_issues", "arguments": { "query": "auth" } } }

// 响应:工具执行结果

{ "jsonrpc": "2.0", "id": 7,

"result": { "content": { "type": "text", "text": "找到 3 个 issue..." } } }

// 通知:工具列表变了(无 id,无响应)

{ "jsonrpc": "2.0", "method": "notifications/tools/list_changed" }

核心方法一览:

|------|--------------------------------------|--------------|
| 类别 | 方法 | 用途 |
| 生命周期 | initialize | 握手、协商协议版本与能力 |
| | notifications/initialized | 客户端确认就绪 |
| | ping | 心跳/保活 |
| 工具 | tools/list / tools/call | 发现工具 / 执行工具 |
| 资源 | resources/list / resources/read | 列出资源 / 读取资源 |
| 提示词 | prompts/list / prompts/get | 列出模板 / 获取模板 |
| 通知 | notifications/tools/list_changed 等 | 能力动态变化时推送 |

关键点:能力是「运行时发现」的,而不是「编译期写死」的 。Client 通过 tools/list 等接口在连接后才知道 Server 有哪些能力,这是 MCP 能解耦 N×M 问题的根基。


5. 传输方式:stdio 与 Streamable HTTP

MCP 把「信的内容」(JSON-RPC)和「怎么送信」(传输层)分离。规范定义了两种传输:

5.1 stdio(标准输入输出)

Client 把 Server 作为子进程启动 ,通过它的 stdin/stdout 交换 JSON-RPC 消息。

  • 每条消息一行,换行符分隔,消息内部不得包含换行。
  • Server 的日志只能写到 stderrstdout 上只能出现合法的 MCP 消息。
复制代码
# 典型启动方式:Client 拉起 Server 子进程
npx -y @modelcontextprotocol/server-filesystem /tmp/workspace

特点:零网络配置、最简单、生命周期绑定进程。适合本地工具、IDE 集成、单人开发调试。

5.2 Streamable HTTP(可流式 HTTP)

Server 作为独立进程 运行,监听单个 HTTP 端点(如 https://example.com/mcp),可同时服务多个客户端。

  • 客户端 → 服务端 :每次发消息都是一个独立的 HTTP POST 到该端点,Accept 头需同时声明 application/jsontext/event-stream
  • 服务端 → 客户端 :服务端可返回普通 JSON,也可升级为 SSE(Server-Sent Events)流 持续推送消息;客户端也可用 HTTP GET 打开一个 SSE 流来被动接收服务端发起的消息。
  • 会话通过 MCP-Session-Id 头显式管理,SSE 事件 ID 支持断线重连与消息回放。

特点:支持远程、多客户端、横向扩展,能挂到标准 HTTP 基础设施(负载均衡、API 网关、认证中间件)后面。生产环境、云部署首选。

5.3 对比

|------|--------------|---------------------------|
| 维度 | stdio | Streamable HTTP |
| 部署形态 | Client 拉起子进程 | 独立进程,可远程 |
| 网络 | 仅本机 | 可跨网络 |
| 多客户端 | 不支持(1:1 进程) | 支持 |
| 会话管理 | 隐式(随进程生命周期) | 显式(Session-Id 头) |
| 断线重连 | 不适用 | 支持(SSE event ID 回放) |
| 认证 | 无(本机信任) | OAuth 2.1 / API Key / JWT |
| 适用 | 本地工具、开发调试 | 生产、云部署、远程 |

经验法则:开发期用 stdio 起步,生产环境迁移到 Streamable HTTP。


6. 通信全流程:一次完整的生命周期

MCP 的每一次连接都严格遵循 初始化 → 能力协商 → 运行 → 关闭 四个阶段。

逐阶段拆解:

① 初始化(握手) :Client 主动发 initialize,带上自己支持的 protocolVersion(日历版本号,如 2025-06-18)和 capabilities。Server 返回自己的版本与能力。版本若无法达成一致,连接必须断开。

② 能力协商 :双方各自声明支持的特性(tools/resources/prompts/logging/sampling......),并只使用「双方都声明了」的能力。例如 Server 没声明 tools,Client 就不能tools/list

③ 就绪确认 :Client 发一个 notifications/initialized 通知(无 id),表示可以开始正常通信。在此之前,双方只允许发 ping 和日志。

④ 运行 :Client 通过 */list 发现能力、tools/call / resources/read / prompts/get 使用能力;Server 可随时用 notifications/* 推送变化。

⑤ 关闭 :MCP 没有专门的 shutdown 方法,关闭传输连接即结束会话。stdio 下 Client 先关子进程输入流、再发 SIGTERM、必要时 SIGKILL;HTTP 下直接关闭连接。双方都要能优雅处理「连接意外断开」。


7. 一个完整的工具调用报文长什么样

把上面的流程落到最真实的场景:模型决定要「查 GitHub 上 auth 模块的 open issue」,一次完整的 tools/call 长这样:

复制代码
// 1. Client → Server:执行工具
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call",
  "params": {
    "name": "search_issues",
    "arguments": { "query": "auth", "state": "open" }
  } }

// 2. Server → Client:返回结果(结构化 content 数组)
{ "jsonrpc": "2.0", "id": 3,
  "result": {
    "content": [
      { "type": "text",
        "text": "找到 3 个 issue:#12 登录超时、#18 鉴权绕过、#25 token 刷新" }
    ],
    "isError": false
  } }

// 3. 如果工具执行失败(业务错误,不是协议错误)
{ "jsonrpc": "2.0", "id": 3,
  "result": { "content": [ { "type": "text", "text": "查询失败" } ], "isError": true } }

几个值得注意的细节:

  • 工具的输入参数结构由 tools/list 返回的 inputSchema(JSON Schema)声明,模型是读 schema 来决定怎么传参 的------所以 description 写得越清楚,模型用得越准
  • 返回值是 content 数组(可含多个文本/图片/资源块),而不是裸字符串。
  • 业务错误isError: true 表达(工具跑了但失败);协议错误 (请求格式错误、方法不存在)则走 JSON-RPC 的 error 字段,两者要分清。

8. 如何在你的项目里使用 MCP

你的角色决定了用法。分三种情况:

8.1 你是「使用方」:接入现成的 MCP Server

最常见。很多常用能力已有现成 MCP Server,直接配置接入即可,无需自己写代码。

以把「GitHub MCP Server」接进某个 Host 为例,配置形如:

复制代码
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<你的 token>" }
    }
  }
}

不同 Host 的配置入口不同(Claude Desktop 是 claude_desktop_config.json,Cursor 在设置里,WorkBuddy 在连接器管理里),但核心字段几乎一致command + args(stdio 方式)或 url + 认证信息(Streamable HTTP 方式)。

结合你的运维场景:GitHub MCP(管仓库/PR/Issue)、腾讯云轻量服务器 MCP(管实例/防火墙/快照)都能直接接进工作流,让 Agent 直接操作这些资源,而不是人肉去点控制台。

8.2 你是「开发方」:用 SDK 写一个 MCP Server

如果内部有专属工具/数据要暴露给 AI,就用官方或社区 SDK 写一个 Server。下面以 Go 为例(社区流行的 mark3labs/mcp-go,官方亦有 modelcontextprotocol/go-sdk):

复制代码
package main

import (
    "context"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    s := server.NewMCPServer("my-server", "1.0.0")

    // 注册一个 echo 工具
    s.AddTool(
        mcp.NewTool("echo",
            mcp.WithDescription("返回用户输入的内容,用于连通性测试"),
            mcp.WithString("message",
                mcp.Required(),
                mcp.Description("要回显的内容"),
            ),
        ),
        func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
            msg := req.Params.Arguments["message"].(string)
            return mcp.NewToolResultText("echo: " + msg), nil
        },
    )

    // 以 stdio 方式启动(生产可换 Streamable HTTP)
    server.ServeStdio(s)
}

Python 官方 SDK 的写法更简洁(mcp.server.fastmcp 装饰器),适合快速验证;Go 适合写进你现有的服务里。核心思路一致:定义工具 → 注册实现 → 选一个传输方式跑起来。

8.3 你是「集成方」:在自有 Agent 里同时当 Client 和 Server

像需要维护的Go + Gin 的 Agent 服务,典型姿势是:

  • 对外(上游 Agent / IDE)暴露能力 → 用 SDK 起一个 MCP Server
  • 对内(调用下游数据源 / 其他工具)→ 用 SDK 起一个 MCP Client,去连 GitHub MCP、腾讯云 MCP 等。

一个进程可以同时是 Server 又是 Client ,这正是 Agent 作为「中间编排层」的常见形态。选型建议:对外暴露用 Streamable HTTP(可远程、可认证),对内调用本地子进程工具用 stdio


9. 注意事项与安全红线

这是最容易踩坑、也最该认真对待的部分。

9.1 传输层安全(Streamable HTTP 必须做)

官方规范明确要求:

  • 必须校验 Origin ,防止 DNS rebinding 攻击(恶意网页借本地 MCP Server 发起操作)。
  • 本地运行时默认只绑定 127.0.0.1 ,不要轻易绑 0.0.0.0 暴露到公网。
  • 远程 Server 必须做认证(OAuth 2.1 / API Key / JWT)。

9.2 提示词注入(Prompt Injection)

MCP 标准化了通道,但没有标准化「信任」。 Server 返回的内容会被模型直接读取,恶意 Server(或 Server 拉到的外部数据)可能夹带指令诱导模型做出危险操作。务必:

  • 把「工具输出」与「系统指令」在语义上隔离,不要无条件信任工具返回内容当指令执行。
  • 对高危操作(删除、支付、发布)设置人工确认点(可用 Elicitation 实现)。

9.3 权限与治理

  • MCP 不定义 工具级/资源级的细粒度授权,也没有速率限制、审计、成本追踪。这些必须自己建一层(API 网关、中间件、管理平台)。
  • 记住原则:「它说 MCP」只代表它能对话,不代表它有权操作。 该收口的权限要自己收口。

9.4 日志与敏感信息

  • 日志只写 stderr(stdio 下),绝不让日志污染 stdout,否则协议解析会崩。
  • 不打印 Token、密码、完整连接串等敏感信息。

9.5 错误处理与超时

  • 所有请求都要设超时,防止连接挂死、资源耗尽。
  • 区分两类错误:协议错误 (JSON-RPC error)和业务错误isError: true),处理与重试策略不同。
  • 服务端要能优雅处理「连接意外断开」,不要假设正常关闭。

10. 常见误区与最佳实践

误区一:MCP 就是 Function Calling。 不对。MCP 是传输 + 发现 + 生命周期的完整协议,Resources/Prompts/Sampling 等都是 Function Calling 不具备的。它解决的是「连接管理」,而非「让模型会调函数」这件事。

误区二:接一个 MCP Server 就万事大吉。 MCP 不提供能力发现注册中心、不提供编排(多 Agent 协作请用 A2A)、不提供治理。这些是你要在它之上补的。

误区三:工具描述随便写。 description 是模型和你的系统之间的唯一接口。一句模糊的「管理订单」远不如「根据订单号查询订单状态,参数 orderId 必填,返回含状态与时间的 JSON」可靠。

最佳实践小结:

  1. 开发用 stdio,生产用 Streamable HTTP。
  1. 工具 description 写清「输入、输出、副作用、何时用/何时别用」。
  1. 给所有工具调用设超时;监控每次调用的延迟、错误率、token 消耗。
  1. 高危操作加人工确认;对外 Server 强制认证 + Origin 校验。
  1. 为工具定义做版本管理,利用 tools/list_changed 通知客户端变化。
  1. 把 MCP Server 当生产 API 一样做可观测性------它就是你新的「外部依赖」。

11. 总结与扩展阅读

一句话总结 :MCP 是一套「参与者模型(Host/Client/Server)+ 三类意图原语(Tools/Resources/Prompts)+ 基于 JSON-RPC 2.0 的可协商生命周期 + 可插拔传输(stdio / Streamable HTTP)」的开放协议。它把 AI 应用与外部工具的连接从 N×M 降到 N+M,但只标准化了通道,不标准化信任与治理------安全与治理责任在你。

核心要点回顾:

  • 通信内容:JSON-RPC 2.0(请求 / 响应 / 错误 / 通知)。
  • 通信流程:initialize 握手 → 能力协商 → notifications/initialized → 发现(*/list)→ 使用(tools/call 等)→ 关闭传输。
  • 能力是运行时发现的,descriptioninputSchema 是模型正确使用工具的钥匙。
  • 安全红线:校验 Origin、绑 localhost、必须认证、防提示词注入、高危操作人工确认、日志脱敏。

扩展阅读:

相关推荐
AI多Agent协作实战派1 小时前
AI多Agent协作系统实战(四十八):改个名,整个AI团队都不认识人了
后端
wei_shuo1 小时前
KES 故障诊断与应急响应:问题排查、根因分析与应急预案
后端
LinMINGJing0071 小时前
postgre分区方式
后端
何以解忧,唯有..1 小时前
Python 线程编程:从入门到实战
开发语言·python
王中阳Go1 小时前
业务代码凭什么不能直接调 Agent?——我在律所 AI 项目里做的 Harness 运行时治理
人工智能·后端·程序员
FfHUCisI2 小时前
Golang database/sql 标准库基础
数据库·sql·golang
SomeB1oody2 小时前
【RustyML入门】7.2. 深入模型持久化
开发语言·后端·机器学习·rust·教程
知几蜗牛2 小时前
0 后端 · 0 数据库 · 0 备案:用 AI 两天搓出的股票管理系统,开源了
前端·后端·llm