MCP(Model Context Protocol)实战指南:从协议原理到动手构建 MCP Server 与 Client
如果你在 2025—2026 年做过大模型应用,大概率经历过这样的痛苦:每接一个外部工具(查数据库、读文件、调内部 API),就要手写一套专属的「函数描述 + 调用解析 + 结果回填」胶水代码。换个模型、换个前端,这套逻辑又得重写一遍。
MCP(Model Context Protocol,模型上下文协议) 的出现,就是为了解决这个问题。它由 Anthropic 在 2024 年底提出,并在 2025—2026 年迅速成为「大模型连接外部世界」的事实标准。一句话概括:MCP 是大模型与外部工具、数据源之间的 USB-C 接口——统一的协议,让任何 Server 能被任何支持 MCP 的 Client(Claude Desktop、Cursor、VS Code、各类 Agent 框架)即插即用。
本文从协议原理讲起,带你用官方 SDK 动手构建一个可用的 MCP Server 和 Client,并落到「把企业内部 API 封装成 MCP 服务」的真实场景。
一、为什么需要 MCP:Function Calling 的局限
大模型本身只能「对话」,要产生实际动作必须借助外部能力。早期的做法是 Function Calling(工具调用):
- 开发者把每个函数的名称、参数 schema、描述写死在请求里;
- 模型返回「要调用哪个函数 + 参数」;
- 应用侧解析后执行,再把结果塞回上下文。
问题在于:这套约定是私有且碎片化的。OpenAI、Claude、通义千问各自的工具调用格式不同,参数描述风格不同,鉴权方式不同。每接一个工具、每换一个模型,都要重写适配层。当工具数量从几个涨到几十个、上百个时,维护成本指数级上升。
MCP 把这件事标准化了:
- Server 用统一协议声明自己能提供什么(Tools / Resources / Prompts);
- Client/Host 用统一协议发现、调用这些能力;
- 工具的实现细节(怎么连数据库、怎么调 API)完全封装在 Server 内部,对模型和前端透明。
换模型不用改 Server,加工具不用改前端——这就是 MCP 的核心价值。
二、MCP 架构:Host、Client、Server 三层
一个典型的 MCP 部署包含三个角色:
| 角色 | 是什么 | 例子 |
|---|---|---|
| **Host(宿主)** | 运行 LLM 的应用程序,负责编排 | Claude Desktop、Cursor、VS Code、自建 Agent |
| **Client(客户端)** | Host 内部与某个 Server 一一对应的连接器 | 每个 MCP Server 对应一个 Client 实例 |
| **Server(服务端)** | 提供具体能力(工具、数据、提示模板)的进程或服务 | 文件系统 Server、数据库 Server、天气 API Server |
注意:一个 Host 可以有多个 Client,每个 Client 连接一个 Server,形成「一对多」的能力网络。Server 之间不直接通信,全部经由 Host 里的 LLM 进行「思考与编排」。
传输层(Transport)
MCP 支持多种传输方式,决定 Client 与 Server 怎么通信:
- stdio(标准输入输出):Server 作为本地子进程启动,通过 stdin/stdout 交换 JSON-RPC 消息。最简单,适合本地工具(读文件、跑脚本)。
- Streamable HTTP:2025-03-26 协议版本推荐的新远程传输,替代了旧的 SSE。支持有状态/无状态两种模式,适合把 Server 部署成远程服务。
- SSE(Server-Sent Events):旧版远程传输,已被 Streamable HTTP 取代,新项目不建议使用。
三、三大核心原语:Tools、Resources、Prompts
MCP 把 Server 能提供的东西分成三类「原语」,分工非常清晰:
- Tools(工具):可被模型主动调用的函数,用于「执行动作」。比如「查询订单」「发送邮件」「运行测试」。由模型根据上下文决定是否调用(model-controlled)。
- Resources(资源):可被应用读取的数据源,用于「提供上下文」。比如「某个文件内容」「一条数据库记录」「一份配置」。通常由应用或用户决定何时加载(app-controlled)。
- Prompts(提示模板):预定义的、可复用的交互模板,由用户主动触发(user-controlled)。比如「代码审查模板」「周报生成模板」。
记住一句口诀:Tools 做事,Resources 给料,Prompts 引导。
四、动手:用 Python 构建第一个 MCP Server
官方 Python SDK 名为 mcp。高层封装 FastMCP 让我们可以用装饰器快速声明能力。先安装:
pip install "mcp[cli]"
下面是一个最小可运行的 Server,包含 Tool、Resource、Prompt 三种原语:
from mcp.server.fastmcp import FastMCP
# 创建 Server 实例,名字可自定义
mcp = FastMCP("demo-server")
# 1) Tool:模型可调用的函数,docstring 会被当作工具描述
@mcp.tool()
def add(a: int, b: int) -> int:
"""计算两个整数的和。"""
return a + b
# 2) Resource:可按 URI 读取的数据,支持路径参数
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""根据名字返回一句问候语。"""
return f"你好,{name}!欢迎使用 MCP。"
# 3) Prompt:可复用的提示模板
@mcp.prompt()
def review_code(code: str) -> str:
"""生成一段代码审查提示。"""
return f"请审查下面这段代码的健壮性、性能与安全性:\n\n{code}"
if __name__ == "__main__":
# 默认以 stdio 方式启动;远程部署可用 mcp.run(transport="streamable-http")
mcp.run()
把这段代码保存为 server.py,用 mcp dev server.py 即可启动一个带调试界面的本地 Server;生产部署用 python server.py(stdio)或 mcp run(远程 HTTP)。
关键点:工具的 docstring 和类型注解就是模型的「说明书」。add(a: int, b: int) -> int 会自动生成 JSON Schema 参数描述,模型据此知道该传什么、返回什么,无需你手写 schema。
五、真实案例:把企业内部 API 封装成 MCP Server
假设公司有一个订单查询接口 GET /api/orders?user_id=xxx,我们把它包装成模型可调用的 Tool:
import os
import httpx
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("order-server")
API_BASE = os.getenv("ORDER_API_BASE", "https://internal.example.com")
API_TOKEN = os.getenv("ORDER_API_TOKEN", "")
@mcp.tool()
async def query_orders(user_id: str, status: str = "all", limit: int = 10) -> str:
"""查询指定用户的订单列表。
Args:
user_id: 用户唯一标识
status: 订单状态,可选 paid / shipped / all
limit: 返回条数上限
"""
headers = {"Authorization": f"Bearer {API_TOKEN}"}
params = {"user_id": user_id, "status": status, "limit": limit}
async with httpx.AsyncClient() as client:
resp = await client.get(f"{API_BASE}/api/orders", headers=headers, params=params)
resp.raise_for_status()
data = resp.json()
# 把结构化结果转成模型易读的摘要文本
lines = [f"共 {len(data)} 笔订单:"]
for o in data:
lines.append(f"- 订单 {o['id']} | 状态 {o['status']} | 金额 ¥{o['amount']}")
return "\n".join(lines)
if __name__ == "__main__":
mcp.run(transport="streamable-http")
这样,任何支持 MCP 的 Client 连上这个 Server 后,模型就能用自然语言「帮我查一下 user_123 最近的已付款订单」,而无需前端工程师为每个模型单独写对接代码。
六、客户端:在 Agent 中接入 MCP Server
Host 侧通常已经内置 MCP Client(如 Claude Desktop、Cursor 通过配置文件声明要连哪些 Server)。如果你要自建 Agent,官方 SDK 也提供了 Client 能力。下面是用 Python 以 stdio 方式连上一个本地 Server 并调用工具的骨架:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# 声明要启动的 Server 进程
server_params = StdioServerParameters(
command="python",
args=["server.py"],
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 列出 Server 提供的工具
tools = await session.list_tools()
print([t.name for t in tools.tools])
# 调用工具
result = await session.call_tool("add", {"a": 1, "b": 2})
print(result.content)
if __name__ == "__main__":
asyncio.run(main())
Client 拿到工具列表后,再结合 LLM 的 Function Calling 能力,就能让模型「见到」这些工具并自主决策调用——MCP 负责「统一接口」,LLM 负责「统一决策」,两者配合完成端到端的智能调用。
七、安全与最佳实践
MCP 把外部能力直接交给模型调用,安全是第一优先级:
- 最小权限:Server 只暴露必要的能力,数据库 Server 用只读账号,API Server 走最小 scope 的 token。
- 密钥隔离:像上面的
ORDER_API_TOKEN一律走环境变量,绝不写进代码或提交仓库。 - 人工确认:对「写操作」类工具(发邮件、删数据、下单),Host 应设计审批/确认环节,避免模型误触发。
- 输入校验:Server 侧对参数做严格类型与范围校验,防止越权查询(如 user_id 注入)。
- 沙箱运行:本地 stdio Server 最好在受限环境中启动,限制文件系统与网络访问范围。
八、生态与未来
截至 2026 年,MCP 生态已经相当繁荣:
- 客户端:Claude Desktop、Cursor、VS Code(Copilot + MCP)、Windsurf、各类 Agent 框架(LangChain、LlamaIndex)均已支持。
- 服务端:官方维护 filesystem、fetch、git、postgres、slack、google-drive 等参考 Server;社区涌现大量第三方 Server。
- 注册中心:出现了类似「MCP 市场」的 Server 目录,方便发现与复用。
- 与 Agent 的关系:MCP 解决「模型怎么连工具」,Agent 框架解决「模型怎么思考与规划」,二者互补。可以认为 MCP 是 Agent 的「手脚标准接口」。
协议本身也在演进:从最初的 stdio/SSE,到 2025 年引入 Streamable HTTP、支持远程无状态部署,再到对「可组合 Server」「权限协商」的持续完善。可以预见,MCP 正在成为 AI 应用基础设施里和 REST、gRPC 同等重要的存在。
九、总结
MCP 用一套统一协议,终结了「每个工具、每个模型各自适配」的混乱局面。回顾本文要点:
- 本质:大模型与外部世界的「USB-C」统一接口。
- 架构:Host 编排、Client 连接、Server 提供能力,一对多组网。
- 原语:Tools 做事、Resources 给料、Prompts 引导。
- 上手:
pip install mcp,用@mcp.tool()装饰器几行代码就能发布一个工具。 - 落地:把内部 API 包成 MCP Server,即可被任意支持 MCP 的客户端即插即用地调用。
- 安全:最小权限、密钥隔离、人工确认、输入校验、沙箱运行。
如果你正在构建大模型应用,现在正是把零散的工具调用迁移到 MCP 的最佳时机——一次标准化,长期受益。
本文示例代码基于 `mcp` Python SDK(协议版本 2025-03-26)。实际部署请以官方文档最新版为准。

发表评论 取消回复