🔧 MCP 协议深度解析(一):MCP 协议概览与架构设计
📋 目录
- [1. 引言:为什么需要 MCP 协议?](#1. 引言:为什么需要 MCP 协议?)
- [1.1 Function Calling 的局限性](#1.1 Function Calling 的局限性)
- [1.2 Plugin 系统的问题](#1.2 Plugin 系统的问题)
- [1.3 工具生态碎片化的挑战](#1.3 工具生态碎片化的挑战)
- [2. MCP 协议的核心设计理念与目标](#2. MCP 协议的核心设计理念与目标)
- [2.1 标准化工具接口](#2.1 标准化工具接口)
- [2.2 一次编写,处处运行](#2.2 一次编写,处处运行)
- [2.3 安全与隔离](#2.3 安全与隔离)
- [3. MCP 架构全景:Host ↔ Client ↔ Server 三元架构](#3. MCP 架构全景:Host ↔ Client ↔ Server 三元架构)
- [3.1 Host:承载 AI 的应用](#3.1 Host:承载 AI 的应用)
- [3.2 Client:Host 内的 MCP 客户端](#3.2 Client:Host 内的 MCP 客户端)
- [3.3 Server:提供工具能力的服务端](#3.3 Server:提供工具能力的服务端)
- [3.4 三者之间的通信关系](#3.4 三者之间的通信关系)
- [4. MCP 与 Function Calling、Plugin 系统的深度对比](#4. MCP 与 Function Calling、Plugin 系统的深度对比)
- [4.1 技术架构对比表](#4.1 技术架构对比表)
- [4.2 适用场景对比](#4.2 适用场景对比)
- [4.3 优缺点分析](#4.3 优缺点分析)
- [5. MCP 生态现状](#5. MCP 生态现状)
- [5.1 官方 SDK](#5.1 官方 SDK)
- [5.2 支持的客户端](#5.2 支持的客户端)
- [5.3 社区工具生态](#5.3 社区工具生态)
- [6. 快速体验:5 分钟上手第一个 MCP Server](#6. 快速体验:5 分钟上手第一个 MCP Server)
- [6.1 环境准备](#6.1 环境准备)
- [6.2 安装依赖](#6.2 安装依赖)
- [6.3 编写简单的计算器 Server](#6.3 编写简单的计算器 Server)
- [6.4 配置 Claude Desktop](#6.4 配置 Claude Desktop)
- [6.5 测试运行](#6.5 测试运行)
- [7. MCP 的应用场景与最佳实践](#7. MCP 的应用场景与最佳实践)
- [7.1 文件系统访问](#7.1 文件系统访问)
- [7.2 数据库查询](#7.2 数据库查询)
- [7.3 API 调用](#7.3 API 调用)
- [7.4 代码执行](#7.4 代码执行)
- [7.5 最佳实践与反模式](#7.5 最佳实践与反模式)
- [8. 常见问题 FAQ](#8. 常见问题 FAQ)
- [9. 参考文献与延伸阅读](#9. 参考文献与延伸阅读)
1. 引言:为什么需要 MCP 协议?
在 AI 应用蓬勃发展的今天,大型语言模型(LLM)的能力边界正在不断扩展。然而,模型本身的知识是静态的、有限的,要让 AI 真正具备与外部世界交互的能力,就必须引入**工具调用(Tool Calling)**机制。
但传统的工具调用方案存在诸多痛点,MCP(Model Context Protocol,模型上下文协议)正是为了解决这些问题而诞生的。
1.1 Function Calling 的局限性
Function Calling 是 OpenAI 率先提出的一种让 LLM 调用外部函数的标准方式。虽然它极大地扩展了 AI 的能力,但在实际应用中存在明显局限:
┌─────────────────────────────────────────────────────────────┐
│ Function Calling 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ LLM │ ───▶ │ 解析函数 │ ───▶ │ 本地代码 │ │
│ │ 模型 │ │ 调用意图 │ │ 执行函数 │ │
│ └─────────┘ └──────────────┘ └─────────────┘ │
│ │
│ 问题: │
│ ❌ 每个应用需要单独实现工具逻辑 │
│ ❌ 工具定义格式不统一(JSON Schema 各家略有不同) │
│ ❌ 工具与模型紧耦合,难以复用 │
│ ❌ 缺乏标准化的错误处理机制 │
│ │
└─────────────────────────────────────────────────────────────┘
核心痛点:
- 重复造轮子:每个 AI 应用都需要自己实现工具调用逻辑,从参数解析到结果处理
- 格式碎片化:虽然都基于 JSON Schema,但各家实现细节不同,迁移成本高
- 生态封闭:为 ChatGPT 开发的工具无法直接在 Claude 或 Gemini 中使用
- 调试困难:缺乏统一的调试和监控标准
1.2 Plugin 系统的问题
ChatGPT Plugins 是 OpenAI 推出的插件系统,试图解决工具扩展问题,但仍存在根本缺陷:
┌─────────────────────────────────────────────────────────────┐
│ Plugin 系统架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ ChatGPT │ ───▶ │ Plugin │ ───▶ │ 第三方 │ │
│ │ 平台 │ │ Store │ │ 服务 │ │
│ └─────────┘ └──────────────┘ └─────────────┘ │
│ │
│ 问题: │
│ ❌ 平台锁定:Plugin 只能在特定平台使用(ChatGPT) │
│ ❌ 审核门槛:需要平台审核才能上架 │
│ ❌ 技术栈限制:通常需要 HTTP API + OpenAPI 描述 │
│ ❌ 用户体验割裂:Plugin 之间无法协同工作 │
│ │
└─────────────────────────────────────────────────────────────┘
核心痛点:
- 平台锁定:Plugin 与特定平台深度绑定,无法跨平台复用
- 开发门槛:需要遵循平台的特定规范,学习成本高
- 审核依赖:工具上架受平台审核流程制约
- 生态割裂:不同平台的 Plugin 互不兼容
1.3 工具生态碎片化的挑战
当前 AI 工具生态呈现出严重的碎片化状态:
| 平台/框架 | 工具调用方式 | 协议标准 | 生态开放性 |
|---|---|---|---|
| OpenAI | Function Calling | 自有标准 | 封闭 |
| Anthropic | Tool Use | 自有标准 | 封闭 |
| Google Gemini | Function Calling | 自有标准 | 封闭 |
| LangChain | Tools | 框架特定 | 半开放 |
| AutoGPT | Plugins | 自有标准 | 半开放 |
这种碎片化的后果是:
- 开发者负担重:为多个平台重复开发相同功能
- 用户选择受限:工具与平台绑定,无法自由组合
- 创新受阻:小团队难以支持多平台,生态难以繁荣
MCP 协议的诞生正是为了解决这些根本性问题。
2. MCP 协议的核心设计理念与目标
MCP(Model Context Protocol)由 Anthropic 于 2024 年 11 月开源发布,旨在建立一个开放、标准化、可移植的 AI 工具协议。
2.1 标准化工具接口
MCP 定义了一套统一的接口规范,让工具开发者只需实现一次,即可在所有支持 MCP 的客户端中运行。
┌─────────────────────────────────────────────────────────────────────┐
│ MCP 标准化接口设计 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ MCP 协议层 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Tools │ │Resources│ │ Prompts │ │ Roots │ │ │
│ │ │ 工具 │ │ 资源 │ │ 提示词 │ │ 根目录 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┼──────────────────────────────────┐ │
│ │ 传输层 │ │ │
│ │ ┌─────────┐ ┌─────────┐▼┌─────────┐ ┌─────────┐ │ │
│ │ │stdio │ │HTTP/SSE │ │WebSocket│ │ 其他 │ │ │
│ │ │标准输入输出│ │ 服务器 │ │ │ │ │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
四大核心原语:
- Tools(工具):可被 AI 调用的功能,如计算器、文件读取、API 请求
- Resources(资源):可被 AI 读取的数据,如文件内容、数据库记录
- Prompts(提示词):预定义的提示模板,帮助用户快速启动任务
- Roots(根目录):定义 Server 可访问的目录范围,用于安全隔离
2.2 一次编写,处处运行
MCP 的核心价值主张是**"Write Once, Run Anywhere"**。
这意味着:
- 工具开发者:只需关注业务逻辑,无需适配不同平台
- AI 应用开发者:可复用社区丰富的 MCP Server
- 终端用户:自由组合工具,打造个性化 AI 工作流
2.3 安全与隔离
MCP 从设计之初就将安全性作为首要考量:
安全机制:
- 明确授权:用户必须显式授权每个 Server 的权限
- Roots 隔离:通过根目录限制 Server 的文件访问范围
- 进程隔离:每个 Server 运行在独立进程中,崩溃不影响 Host
- 传输安全:支持 stdio、HTTP/SSE 等多种传输方式,可加密通信
- 能力声明:Server 必须声明自己提供的工具和资源,Host 据此控制访问
3. MCP 架构全景:Host ↔ Client ↔ Server 三元架构
MCP 采用经典的三元架构设计,清晰划分了各个组件的职责边界。
3.1 Host:承载 AI 的应用
Host 是最终用户直接交互的 AI 应用程序,负责:
- 提供用户界面(聊天界面、IDE 等)
- 管理 LLM 对话上下文
- 协调多个 MCP Client 的生命周期
- 处理用户授权和安全策略
典型 Host 示例:
| Host 应用 | 类型 | 特点 |
|---|---|---|
| Claude Desktop | 桌面应用 | Anthropic 官方,功能完整 |
| Cursor | IDE | 代码编辑器集成 |
| Zed | 代码编辑器 | 高性能 Rust 实现 |
| Windsurf | IDE | Cascade 工作流 |
| Continue | VS Code 插件 | 开源,可扩展 |
3.2 Client:Host 内的 MCP 客户端
Client 是 Host 内部与 MCP Server 通信的组件:
- 维护与 Server 的连接(stdio、HTTP 等)
- 处理协议消息的序列化和反序列化
- 管理工具调用的请求-响应生命周期
- 向 Host 暴露统一的工具接口
Client 的核心职责:
┌─────────────────────────────────────────────────────────────┐
│ MCP Client 职责 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 连接管理 │
│ ├─ 启动 Server 进程(stdio 模式) │
│ ├─ 建立 HTTP/SSE 连接(远程模式) │
│ └─ 维护连接状态,处理断线重连 │
│ │
│ 2. 协议处理 │
│ ├─ JSON-RPC 消息编码/解码 │
│ ├─ 请求-响应匹配 │
│ └─ 错误处理和超时管理 │
│ │
│ 3. 能力协商 │
│ ├─ 发送 initialize 请求 │
│ ├─ 接收 Server 能力声明 │
│ └─ 缓存工具和资源列表 │
│ │
│ 4. 工具调用 │
│ ├─ 构造 tool call 请求 │
│ ├─ 发送请求并等待响应 │
│ └─ 将结果返回给 Host │
│ │
└─────────────────────────────────────────────────────────────┘
3.3 Server:提供工具能力的服务端
Server 是实现具体工具逻辑的程序,负责:
- 声明自己提供的工具和资源
- 实现工具调用的具体逻辑
- 管理资源的生命周期和更新
- 处理 Prompt 模板
Server 的生命周期:
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐
│ 启动 │───▶│ 初始化 │───▶│ 运行中 │───▶│ 关闭 │───▶│ 结束 │
└─────────┘ └──────────┘ └──────────┘ └──────────┘ └─────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
进程启动 能力协商 处理请求 清理资源
加载配置 注册工具 响应调用 断开连接
声明资源
3.4 三者之间的通信关系
┌─────────────────────────────────────────────────────────────────────────┐
│ MCP 三元架构通信关系 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Host │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ LLM 对话上下文 │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │ 用户输入 │───▶│ 模型推理 │───▶│ 生成回复 │ │ │ │
│ │ │ └─────────┘ └────┬────┘ └─────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ 需要工具调用 │ │ │
│ │ │ ┌─────────────┐ │ │ │
│ │ │ │ MCP Client │ │ │ │
│ │ │ └──────┬──────┘ │ │ │
│ │ └─────────────────────┼───────────────────────────────────┘ │ │
│ └────────────────────────┼───────────────────────────────────────┘ │
│ │ │
│ ▼ MCP 协议 (stdio / HTTP / SSE) │
│ │ │
│ ┌────────────────────────┼───────────────────────────────────────┐ │
│ │ ┌─────┴─────┐ │ │
│ │ │ MCP Server │ │ │
│ │ │ ┌─────┐ │ │ │
│ │ │ │Tools│ │ │ │
│ │ │ └─────┘ │ │ │
│ │ │ ┌─────┐ │ │ │
│ │ │ │Resources│ │ │
│ │ │ └─────┘ │ │ │
│ │ └───────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
通信流程:
-
初始化阶段:
- Host 启动 Server 进程
- Client 发送
initialize请求 - Server 返回能力声明(工具列表、资源列表等)
-
运行阶段:
- Host 将用户输入发送给 LLM
- LLM 判断需要调用工具
- Host 通过 Client 发送
tools/call请求 - Server 执行工具逻辑并返回结果
- Host 将结果注入 LLM 上下文,继续生成回复
-
关闭阶段:
- Host 发送
shutdown请求 - Server 清理资源并退出
- Host 发送
4. MCP 与 Function Calling、Plugin 系统的深度对比
4.1 技术架构对比表
| 特性 | MCP | Function Calling | Plugin 系统 |
|---|---|---|---|
| 协议标准 | 开放标准 | 厂商特定 | 平台特定 |
| 跨平台支持 | ✅ 是 | ❌ 否 | ❌ 否 |
| 传输方式 | stdio / HTTP / SSE | HTTP API | HTTP API |
| 进程模型 | 独立进程 | 内嵌/远程 | 远程服务 |
| 安全隔离 | 进程级 + 权限控制 | 依赖实现 | 依赖平台 |
| 工具发现 | 动态协商 | 静态定义 | 静态配置 |
| 资源访问 | 原生支持 Resources | 需额外实现 | 需额外实现 |
| Prompt 模板 | 原生支持 | 需额外实现 | 需额外实现 |
| 实时更新 | ✅ 支持 | ❌ 不支持 | ⚠️ 部分支持 |
| 离线运行 | ✅ stdio 模式 | ⚠️ 依赖实现 | ❌ 需联网 |
4.2 适用场景对比
┌─────────────────────────────────────────────────────────────────────┐
│ 适用场景决策树 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 是否需要跨平台复用? │
│ ├─ 是 ──▶ 选择 MCP │
│ └─ 否 ──▶ 是否需要深度集成特定平台? │
│ ├─ 是 ──▶ 选择该平台原生方案 │
│ └─ 否 ──▶ 是否需要开放生态? │
│ ├─ 是 ──▶ 选择 MCP │
│ └─ 否 ──▶ 根据团队技术栈选择 │
│ │
└─────────────────────────────────────────────────────────────────────┘
MCP 最佳适用场景:
- 需要为多个 AI 客户端提供工具支持
- 希望工具可被社区复用
- 需要本地文件系统访问
- 重视安全隔离
Function Calling 最佳适用场景:
- 仅需支持单一 LLM 平台
- 已有成熟的内部 API 体系
- 需要最小化架构复杂度
Plugin 系统最佳适用场景:
- 目标用户集中在特定平台
- 需要通过平台审核获得信任背书
- 依赖平台的用户流量分发
4.3 优缺点分析
MCP 的优势:
| 优势 | 说明 |
|---|---|
| 🌐 开放生态 | 不绑定特定厂商,社区驱动发展 |
| 🔧 一次编写 | 工具可跨平台复用,降低维护成本 |
| 🛡️ 安全隔离 | 进程级隔离 + 显式权限控制 |
| 📦 丰富原语 | 原生支持 Tools、Resources、Prompts |
| 🔌 灵活传输 | 支持 stdio、HTTP、SSE 多种方式 |
| 🚀 实时更新 | 支持资源订阅和增量更新 |
MCP 的劣势:
| 劣势 | 说明 |
|---|---|
| 🆕 生态较新 | 相比 Function Calling 成熟度较低 |
| 📚 学习成本 | 需要理解新的协议概念 |
| 🔧 调试复杂 | 多进程架构增加调试难度 |
| 📊 监控困难 | 缺乏统一的监控和日志标准 |
5. MCP 生态现状
5.1 官方 SDK
Anthropic 提供了官方 SDK,支持主流编程语言:
TypeScript SDK:
bash
# 安装
npm install @modelcontextprotocol/sdk
# 快速开始
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({
name: "my-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
resources: {},
},
});
const transport = new StdioServerTransport();
await server.connect(transport);
Python SDK:
bash
# 安装
pip install mcp
# 快速开始
from mcp.server import Server
from mcp.server.stdio import stdio_server
app = Server("my-server")
@app.call_tool()
async def handle_tool(name: str, arguments: dict):
# 处理工具调用
pass
async with stdio_server() as streams:
await app.run(
streams[0],
streams[1],
app.create_initialization_options(),
)
SDK 功能对比:
| 功能 | TypeScript SDK | Python SDK |
|---|---|---|
| Server 开发 | ✅ 完整支持 | ✅ 完整支持 |
| Client 开发 | ✅ 完整支持 | ✅ 完整支持 |
| stdio 传输 | ✅ 支持 | ✅ 支持 |
| HTTP/SSE 传输 | ✅ 支持 | ✅ 支持 |
| 类型安全 | ✅ TypeScript 原生 | ✅ Pydantic |
| 异步支持 | ✅ 原生 async/await | ✅ asyncio |
5.2 支持的客户端
| 客户端 | 类型 | MCP 支持状态 | 特点 |
|---|---|---|---|
| Claude Desktop | 桌面应用 | ✅ 官方支持 | 功能最完整 |
| Cursor | IDE | ✅ 支持 | 代码编辑集成 |
| Zed | 代码编辑器 | ✅ 支持 | 高性能 Rust |
| Windsurf | IDE | ✅ 支持 | Cascade 工作流 |
| Continue | VS Code 插件 | ✅ 支持 | 开源可扩展 |
| Cline | VS Code 插件 | ✅ 支持 | 自主编码代理 |
| OpenClaw | 开发框架 | ✅ 支持 | 多模型支持 |
5.3 社区工具生态
MCP 社区正在快速发展,已涌现大量开源 Server:
官方示例 Server:
| Server | 功能 | 语言 |
|---|---|---|
| filesystem | 文件系统访问 | TypeScript |
| sqlite | SQLite 数据库操作 | TypeScript |
| fetch | HTTP 请求 | TypeScript |
| git | Git 操作 | TypeScript |
| postgres | PostgreSQL 数据库 | TypeScript |
热门社区 Server:
| Server | 功能 | 链接 |
|---|---|---|
| mcp-server-browser | 浏览器自动化 | github.com/... |
| mcp-server-github | GitHub API 操作 | github.com/... |
| mcp-server-slack | Slack 集成 | github.com/... |
| mcp-server-notion | Notion API | github.com/... |
| mcp-server-puppeteer | 网页截图/PDF | github.com/... |
6. 快速体验:5 分钟上手第一个 MCP Server
6.1 环境准备
系统要求:
- Node.js 18+ 或 Python 3.10+
- Claude Desktop(或其他 MCP Host)
检查环境:
bash
# 检查 Node.js 版本
node --version # 需要 v18+
# 或检查 Python 版本
python --version # 需要 3.10+
6.2 安装依赖
TypeScript 版本:
bash
# 创建项目目录
mkdir mcp-calculator
cd mcp-calculator
# 初始化项目
npm init -y
# 安装 MCP SDK
npm install @modelcontextprotocol/sdk
# 安装 TypeScript 依赖
npm install -D typescript @types/node
# 初始化 TypeScript
npx tsc --init
Python 版本:
bash
# 创建项目目录
mkdir mcp-calculator
cd mcp-calculator
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或 venv\Scripts\activate # Windows
# 安装 MCP SDK
pip install mcp
6.3 编写简单的计算器 Server
TypeScript 实现:
typescript
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// 创建 Server 实例
const server = new Server(
{
name: "calculator-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 定义可用工具
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "add",
description: "计算两个数的和",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "第一个数" },
b: { type: "number", description: "第二个数" },
},
required: ["a", "b"],
},
},
{
name: "subtract",
description: "计算两个数的差",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "被减数" },
b: { type: "number", description: "减数" },
},
required: ["a", "b"],
},
},
{
name: "multiply",
description: "计算两个数的积",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "第一个数" },
b: { type: "number", description: "第二个数" },
},
required: ["a", "b"],
},
},
{
name: "divide",
description: "计算两个数的商",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "被除数" },
b: { type: "number", description: "除数" },
},
required: ["a", "b"],
},
},
],
};
});
// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "add": {
const result = (args.a as number) + (args.b as number);
return {
content: [{ type: "text", text: String(result) }],
};
}
case "subtract": {
const result = (args.a as number) - (args.b as number);
return {
content: [{ type: "text", text: String(result) }],
};
}
case "multiply": {
const result = (args.a as number) * (args.b as number);
return {
content: [{ type: "text", text: String(result) }],
};
}
case "divide": {
if (args.b === 0) {
return {
content: [{ type: "text", text: "错误:除数不能为零" }],
isError: true,
};
}
const result = (args.a as number) / (args.b as number);
return {
content: [{ type: "text", text: String(result) }],
};
}
default:
throw new Error(`未知工具: ${name}`);
}
});
// 启动 Server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Calculator MCP Server 已启动");
}
main().catch(console.error);
Python 实现:
python
# calculator_server.py
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool
# 创建 Server 实例
app = Server("calculator-server")
# 定义可用工具
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="add",
description="计算两个数的和",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number", "description": "第一个数"},
"b": {"type": "number", "description": "第二个数"},
},
"required": ["a", "b"],
},
),
Tool(
name="subtract",
description="计算两个数的差",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number", "description": "被减数"},
"b": {"type": "number", "description": "减数"},
},
"required": ["a", "b"],
},
),
Tool(
name="multiply",
description="计算两个数的积",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number", "description": "第一个数"},
"b": {"type": "number", "description": "第二个数"},
},
"required": ["a", "b"],
},
),
Tool(
name="divide",
description="计算两个数的商",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number", "description": "被除数"},
"b": {"type": "number", "description": "除数"},
},
"required": ["a", "b"],
},
),
]
# 处理工具调用
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "add":
result = arguments["a"] + arguments["b"]
return [TextContent(type="text", text=str(result))]
elif name == "subtract":
result = arguments["a"] - arguments["b"]
return [TextContent(type="text", text=str(result))]
elif name == "multiply":
result = arguments["a"] * arguments["b"]
return [TextContent(type="text", text=str(result))]
elif name == "divide":
if arguments["b"] == 0:
return [TextContent(type="text", text="错误:除数不能为零")]
result = arguments["a"] / arguments["b"]
return [TextContent(type="text", text=str(result))]
else:
raise ValueError(f"未知工具: {name}")
# 启动 Server
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options(),
)
if __name__ == "__main__":
asyncio.run(main())
编译 TypeScript:
bash
# 编译
npx tsc
# 编译后的文件在 dist/index.js
6.4 配置 Claude Desktop
编辑 Claude Desktop 的配置文件:
macOS:
bash
# 打开配置文件
open ~/Library/Application\ Support/Claude/claude_desktop_config.json
Windows:
powershell
# 打开配置文件
notepad $env:AppData\Claude\claude_desktop_config.json
添加 Server 配置:
json
{
"mcpServers": {
"calculator": {
"command": "node",
"args": ["/path/to/mcp-calculator/dist/index.js"]
}
}
}
Python 版本配置:
json
{
"mcpServers": {
"calculator": {
"command": "python",
"args": ["/path/to/mcp-calculator/calculator_server.py"]
}
}
}
6.5 测试运行
-
重启 Claude Desktop 以加载新的 MCP Server
-
查看工具列表 :在 Claude Desktop 中,点击输入框的 🔧 图标,应该能看到
add、subtract、multiply、divide四个工具 -
测试对话:
用户:帮我计算 123 + 456
Claude:我来帮你计算 123 + 456。
[Claude 调用 add 工具,参数: {"a": 123, "b": 456}]结果是 579。
用户:计算 (100 - 50) * 3 / 5
Claude:我来分步计算:
- 先计算 100 - 50 = 50
[调用 subtract] - 然后 50 * 3 = 150
[调用 multiply] - 最后 150 / 5 = 30
[调用 divide]
最终结果是 30。
- 先计算 100 - 50 = 50
7. MCP 的应用场景与最佳实践
7.1 文件系统访问
文件系统访问是 MCP 最常见的应用场景之一。
典型用例:
- 读取项目代码文件进行分析
- 批量重命名文件
- 搜索文件内容
- 生成项目文档
代码示例:
typescript
// 文件读取工具示例
{
name: "read_file",
description: "读取指定文件的内容",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "文件路径(相对于工作目录)"
},
encoding: {
type: "string",
enum: ["utf-8", "base64"],
default: "utf-8",
description: "文件编码"
},
},
required: ["path"],
},
}
最佳实践:
- ✅ 使用 Roots 限制可访问的目录范围
- ✅ 对路径进行校验,防止目录遍历攻击
- ✅ 对大文件进行分块读取
- ❌ 不要直接暴露系统敏感目录
- ❌ 不要允许任意路径写入
7.2 数据库查询
MCP Server 可以作为 AI 访问数据库的安全网关。
典型用例:
- 自然语言查询数据库
- 生成数据分析报告
- 数据库结构探索
- 数据迁移和转换
代码示例:
typescript
// SQL 查询工具示例
{
name: "query_database",
description: "执行安全的只读 SQL 查询",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "SQL 查询语句(仅支持 SELECT)"
},
limit: {
type: "number",
default: 100,
description: "返回结果数量限制"
},
},
required: ["query"],
},
}
最佳实践:
- ✅ 使用只读连接,防止误操作
- ✅ 限制返回结果数量,避免内存溢出
- ✅ 对 SQL 进行白名单校验
- ✅ 记录所有查询日志
- ❌ 不要暴露写权限,除非必要
- ❌ 不要返回敏感字段(如密码哈希)
7.3 API 调用
MCP Server 可以封装外部 API,让 AI 安全地访问第三方服务。
典型用例:
- 调用 GitHub API 管理仓库
- 查询天气、新闻等实时信息
- 发送 Slack/邮件通知
- 操作云服务资源
代码示例:
typescript
// HTTP 请求工具示例
{
name: "http_request",
description: "发送 HTTP 请求",
inputSchema: {
type: "object",
properties: {
method: {
type: "string",
enum: ["GET", "POST", "PUT", "DELETE"],
description: "HTTP 方法"
},
url: {
type: "string",
description: "请求 URL"
},
headers: {
type: "object",
description: "请求头"
},
body: {
type: "string",
description: "请求体"
},
},
required: ["method", "url"],
},
}
最佳实践:
- ✅ 使用 API Key 管理器安全存储凭证
- ✅ 设置请求超时和重试机制
- ✅ 限制可访问的域名白名单
- ✅ 对响应进行大小限制
- ❌ 不要在代码中硬编码 API Key
- ❌ 不要允许访问内部网络资源
7.4 代码执行
代码执行是最强大的 MCP 能力,也是风险最高的场景。
典型用例:
- 运行测试脚本
- 执行数据分析代码
- 编译和构建项目
- 自动化运维任务
代码示例:
typescript
// Python 代码执行工具示例
{
name: "execute_python",
description: "在沙箱环境中执行 Python 代码",
inputSchema: {
type: "object",
properties: {
code: {
type: "string",
description: "Python 代码"
},
timeout: {
type: "number",
default: 30,
description: "执行超时时间(秒)"
},
},
required: ["code"],
},
}
最佳实践:
- ✅ 在沙箱环境中执行代码(Docker、VM)
- ✅ 设置严格的资源限制(CPU、内存、时间)
- ✅ 禁用危险操作(网络访问、文件系统写入)
- ✅ 记录所有执行的代码
- ❌ 不要在生产环境直接执行用户输入的代码
- ❌ 不要允许访问系统命令
7.5 最佳实践与反模式
✅ 最佳实践:
| 实践 | 说明 |
|---|---|
| 清晰的工具描述 | 使用详细的描述帮助 LLM 理解工具用途 |
| 合理的参数设计 | 参数要有明确的类型和约束 |
| 错误处理 | 返回友好的错误信息,帮助 LLM 修正 |
| 结果格式化 | 根据内容类型返回结构化数据 |
| 日志记录 | 记录工具调用日志,便于调试和审计 |
| 性能优化 | 对耗时操作使用异步和缓存 |
❌ 反模式:
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 工具过于宽泛 | LLM 难以选择正确的工具 | 拆分为更具体的工具 |
| 参数过于复杂 | 增加 LLM 理解和出错概率 | 简化参数,提供默认值 |
| 返回结果过大 | 超出 LLM 上下文限制 | 分页返回,提供摘要 |
| 缺乏错误处理 | 导致对话中断 | 返回结构化错误信息 |
| 硬编码配置 | 无法适应不同环境 | 使用环境变量或配置文件 |
| 阻塞操作 | 影响用户体验 | 使用异步处理 |
8. 常见问题 FAQ
Q1: MCP 和 Function Calling 有什么区别?
A: MCP 是一个开放协议,定义了 AI 工具的标准接口;Function Calling 是特定 LLM(如 OpenAI)的工具调用机制。MCP Server 可以在任何支持 MCP 的 Host 中运行,而 Function Calling 的工具通常与特定平台绑定。
Q2: 我需要为每个 Host 单独开发 MCP Server 吗?
A: 不需要。这是 MCP 的核心价值------一次编写,处处运行。只要 Host 支持 MCP 协议,你的 Server 就可以在其中运行。
Q3: MCP Server 的安全性如何保障?
A: MCP 提供多层安全机制:
- 进程隔离:每个 Server 运行在独立进程中
- 权限控制:用户需显式授权 Server 的能力
- Roots 限制:限制 Server 的文件访问范围
- 传输安全:支持加密通信
Q4: 如何调试 MCP Server?
A: 推荐以下调试方法:
- 日志输出 :使用
console.error()输出调试信息 - Inspector:使用 MCP Inspector 工具进行交互式调试
- 单元测试:编写测试用例验证工具逻辑
- 日志文件:配置 Host 输出详细的 MCP 通信日志
Q5: MCP 支持哪些编程语言?
A: 官方提供 TypeScript 和 Python SDK。社区还有以下实现:
- Go: mcp-go
- Rust: rust-mcp
- Java: mcp-java
理论上,任何支持 JSON-RPC 和 stdio/HTTP 通信的语言都可以实现 MCP Server。
Q6: 一个 MCP Server 可以包含多少个工具?
A: 没有硬性限制,但建议遵循以下原则:
- 相关工具放在同一个 Server 中
- 单个 Server 工具数量建议控制在 20 个以内
- 过于庞大的 Server 可考虑拆分
Q7: MCP 支持远程 Server 吗?
A: 支持。MCP 支持多种传输方式:
- stdio:本地进程通信(最常用)
- HTTP/SSE:远程服务器通信
- WebSocket:实时双向通信
Q8: 如何处理 MCP Server 的版本更新?
A: 建议:
- 在 Server 名称中包含版本号(如
my-server-v2) - 保持向后兼容的 API 变更
- 在工具描述中注明版本信息
- 使用语义化版本管理
9. 参考文献与延伸阅读
官方资源
-
MCP 官方文档
https://modelcontextprotocol.io/
Anthropic 官方 MCP 文档,包含完整的协议规范和教程
-
MCP 协议规范
https://spec.modelcontextprotocol.io/
详细的协议规范文档,适合深度开发者
-
MCP TypeScript SDK
https://github.com/modelcontextprotocol/typescript-sdk
官方 TypeScript SDK 源码和示例
-
MCP Python SDK
https://github.com/modelcontextprotocol/python-sdk
官方 Python SDK 源码和示例
-
MCP 示例 Servers
https://github.com/modelcontextprotocol/servers
官方维护的示例 Server 集合
社区资源
-
Awesome MCP
https://github.com/appcypher/awesome-mcp
社区整理的 MCP 资源大全
-
MCP 中文文档
https://github.com/.../mcp-docs-zh
社区翻译的中文文档(如有)
相关技术
-
JSON-RPC 2.0 规范
https://www.jsonrpc.org/specification
MCP 底层使用的通信协议
-
JSON Schema
工具参数定义使用的 schema 标准
-
Claude Desktop 文档
Claude Desktop 的使用和配置文档
📝 总结
MCP 协议代表了 AI 工具生态的重要演进方向:从封闭走向开放,从碎片化走向标准化。
通过本文的学习,你应该已经掌握了:
✅ MCP 协议的设计背景和核心价值
✅ MCP 三元架构的工作原理
✅ MCP 与传统工具调用方案的对比
✅ 如何开发自己的 MCP Server
✅ MCP 的最佳实践和常见陷阱
下一步建议:
- 动手实践:按照第 6 节的教程,创建你的第一个 MCP Server
- 深入阅读:研读 MCP 官方协议规范,理解底层细节
- 探索生态:浏览 Awesome MCP,发现社区的优秀 Server
- 贡献社区:将你的 Server 开源,回馈社区
MCP 生态正在快速发展,现在正是加入的最佳时机。期待看到你的 MCP 作品!
💡 系列预告
下一篇我们将深入探讨 MCP 协议底层通信机制,包括:
- JSON-RPC 消息格式详解
- stdio vs HTTP 传输方式对比
- 生命周期管理和错误处理
- 高级特性:资源订阅和增量更新
敬请期待!