MCP 是 Anthropic 推出的开源协议,旨在标准化 AI 模型与外部工具、数据源之间的连接方式。它就像 AI 世界的 "USB-C 接口"------一个统一的标准,让任何模型都能无缝接入任何工具。
tip 读完本文你会收获什么
- 彻底理解 MCP 的架构设计思想
- 掌握 Python / TypeScript 双语言 Server 开发
- 学会 Client 与 Server 的完整连接流程
- 了解资源订阅、动态工具、反向采样等高级特性
- 收获可直接落地的生产级代码模板 :::
📖 目录
- [1. 什么是 MCP?](#1. 什么是 MCP? "#1-%E4%BB%80%E4%B9%88%E6%98%AF-mcp")
- [2. 为什么需要 MCP?](#2. 为什么需要 MCP? "#2-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-mcp")
- [3. 核心架构](#3. 核心架构 "#3-%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84")
- [4. 核心概念详解](#4. 核心概念详解 "#4-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E8%AF%A6%E8%A7%A3")
- [5. 传输机制](#5. 传输机制 "#5-%E4%BC%A0%E8%BE%93%E6%9C%BA%E5%88%B6")
- [6. 动手实战:构建 MCP 服务端](#6. 动手实战:构建 MCP 服务端 "#6-%E5%8A%A8%E6%89%8B%E5%AE%9E%E6%88%98%E6%9E%84%E5%BB%BA-mcp-%E6%9C%8D%E5%8A%A1%E7%AB%AF")
- [7. 动手实战:构建 MCP 客户端](#7. 动手实战:构建 MCP 客户端 "#7-%E5%8A%A8%E6%89%8B%E5%AE%9E%E6%88%98%E6%9E%84%E5%BB%BA-mcp-%E5%AE%A2%E6%88%B7%E7%AB%AF")
- [8. 服务端与客户端如何连接](#8. 服务端与客户端如何连接 "#8-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%B8%8E%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%A6%82%E4%BD%95%E8%BF%9E%E6%8E%A5")
- [9. Claude Code 中使用 MCP](#9. Claude Code 中使用 MCP "#9-claude-code-%E4%B8%AD%E4%BD%BF%E7%94%A8-mcp")
- [10. 高级主题](#10. 高级主题 "#10-%E9%AB%98%E7%BA%A7%E4%B8%BB%E9%A2%98")
- [11. 常见问题与调试](#11. 常见问题与调试 "#11-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E8%B0%83%E8%AF%95")
- [12. 最佳实践](#12. 最佳实践 "#12-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5")
1. 什么是 MCP?
MCP(Model Context Protocol,模型上下文协议) 是一个开放的、标准化的通信协议,用于在 AI 大语言模型(LLM)与外部世界之间建立双向连接。
简单来说,MCP 解决了 LLM 的 "信息孤岛" 问题------模型本身只知道训练数据中的内容,无法访问实时数据、文件系统、数据库或 API。MCP 提供了一种标准方式,让模型能够:
- ✅ 调用外部工具(Tool Calling)------ 让 AI 执行操作
- ✅ 读取外部资源(Resource Reading)------ 让 AI 获取实时数据
- ✅ 使用预置提示模板(Prompt Templates)------ 标准化交互流程
- ✅ 沙箱化安全执行------ 内置安全隔离机制
传统方式 vs MCP 方式
scss
┌─────────────────────────────────────────────────────┐
│ 传统方式 │
│ │
│ AI 模型 ──── 定制胶水代码 ──── 工具 A │
│ ──── 定制胶水代码 ──── 工具 B │
│ ──── 定制胶水代码 ──── 工具 C │
│ │
│ 每个工具都需要单独集成,维护成本高 │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ MCP 方式 │
│ │
│ ┌─────────────────┐ │
│ AI 模型 ◄────────│ MCP 客户端 │ │
│ └──────┬──────────┘ │
│ │ MCP 协议 │
│ ┌─────────────┼─────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │MCP 服务端│ │MCP 服务端│ │MCP 服务端│ │
│ │ (文件系统)│ │ (数据库) │ │ (API) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 统一标准,一次集成,处处可用 │
└─────────────────────────────────────────────────────┘
:::tip MCP 的本质 MCP 是一个 标准化的中间层协议。它在 AI 模型和具体工具之间建立了一个通用接口,让模型不需要关心工具的内部实现,也让工具开发者不需要针对每个模型做适配。这就是 "USB-C 接口" 比喻的含义------所有设备用同一个接口通信。 :::
2. 为什么需要 MCP?
2.1 痛点分析
| 痛点 | 传统方案 | MCP 方案 |
|---|---|---|
| 集成成本 | 每个工具都要写定制代码 | 统一协议,即插即用 |
| 安全隔离 | 各方案自行实现 | 协议内置沙箱机制 |
| 跨平台 | 绑定特定平台/框架 | 语言无关,平台无关 |
| 生态碎片 | 工具无法复用 | 社区共享 MCP Server |
| 维护成本 | N×M 的集成矩阵 | 1 个标准协议 |
2.2 两种方式的代码对比
python
# 🔴 传统方式:每个工具都要单独适配
def call_github_api(prompt):
# 100行 GitHub 特定代码
pass
def call_database(prompt):
# 100行数据库特定代码
pass
def call_file_system(prompt):
# 100行文件系统特定代码
pass
# 🟢 MCP 方式:统一接口
# 1. 启动 MCP 服务端(谁都可以写,任何人可以用)
# 2. MCP 客户端自动发现并连接
# 3. AI 模型通过统一接口调用所有工具
:::warning 传统方案的致命缺陷 在没有 MCP 之前,每个 AI 应用如果要接入 10 个工具,就需要写 10 套适配代码。10 个 AI 应用各自接入 10 个工具,就是 100 套适配代码。这是典型的 M×N 集成灾难------而 MCP 用一个协议把这个复杂度降到了 O(N+M)。 :::
3. 核心架构
3.1 架构总览
arduino
┌────────────────────────────────────────────────────────────────┐
│ Host(宿主应用) │
│ │
│ ┌─────────────┐ ┌──────────────────────┐ │
│ │ │ │ │ │
│ │ AI 模型 │◄───►│ MCP Client │ │
│ │ (Claude/ │ │ (MCP 客户端) │ │
│ │ GPT 等) │ │ │ │
│ └─────────────┘ └──────────┬───────────┘ │
│ │ │
└─────────────────────────────────┼────────────────────────────────┘
│
JSON-RPC 2.0 over stdio / SSE / Streamable HTTP
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌────────▼────────┐ ┌──────────▼──────────┐ ┌───────▼───────┐
│ MCP Server A │ │ MCP Server B │ │ MCP Server C │
│ ┌───────────┐ │ │ ┌───────────────┐ │ │ ┌───────────┐ │
│ │ 📦 Tools │ │ │ │ 📦 Tools │ │ │ │ 📦 Tools │ │
│ │ 📁 Resources│ │ │ │ 📁 Resources │ │ │ │📁 Resources│ │
│ │ 💬 Prompts│ │ │ │ 💬 Prompts │ │ │ │💬 Prompts │ │
│ └───────────┘ │ │ └───────────────┘ │ │ └───────────┘ │
│ 文件系统操作 │ │ GitHub API 操作 │ │ 数据库操作 │
└─────────────────┘ └─────────────────────┘ └──────────────┘
3.2 三层角色
| 角色 | 职责 | 实例 |
|---|---|---|
| 🏠 Host(宿主) | 运行 AI 模型、管理 MCP 客户端、协调交互 | Claude Desktop、VS Code、自定义 App |
| 🔌 Client(客户端) | 与 Server 建立连接、协议路由、工具发现 | MCP SDK 中的 Client 类 |
| ⚙️ Server(服务端) | 暴露 Tools / Resources / Prompts,执行实际操作 | 文件系统 Server、GitHub Server、数据库 Server |
4. 核心概念详解
MCP 定义了 三大原语(Primitives),构成了 AI 与外部世界交互的完整体系。理解这三个概念是掌握 MCP 的关键。
4.1 🛠️ Tools(工具)
Tools 是模型可调用的"函数",类比 OpenAI 的 Function Calling。模型通过自然语言理解用户意图,自主决定调用哪个工具。
typescript
// 定义一个 "查询天气" 的工具
{
name: "get_weather",
description: "获取指定城市的实时天气信息",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "城市名称,如 'Beijing', 'Shanghai'"
}
},
required: ["city"]
}
}
Tools 的执行流程:
arduino
用户提问
│
▼
AI 分析意图 ────────────── 直接回答
│ ▲
│ 需要工具 │
▼ │
Client 发送 tool_call ─────────┘
│ AI 解读结果回复用户
▼ ▲
Server 执行工具 ───────────────┘
│ Client 传回结果
▼
返回结构化结果
4.2 📁 Resources(资源)
Resources 是模型可读取的「结构化数据」,以 URI 标识,支持多种 MIME 类型。它就像模型的"文件系统"。
typescript
// Resource 示例:数据库表结构
{
uri: "db://schema/users",
name: "Users Table Schema",
description: "users 表的列定义及其说明",
mimeType: "application/json"
}
// Resource 返回的内容
{
"contents": [
{
"uri": "db://schema/users",
"mimeType": "application/json",
"text": {
"table": "users",
"columns": [
{"name": "id", "type": "INTEGER", "primary": true},
{"name": "email", "type": "VARCHAR(255)", "unique": true},
{"name": "created_at","type": "TIMESTAMP"}
]
}
}
]
}
:::tip Tools vs Resources,如何区分?
- Tools = 执行动作(有副作用),如"创建文件"、"发送邮件"、"更新数据库"
- Resources = 读取数据(无副作用),如"查看文件内容"、"获取表结构"、"读取配置"
简单记忆:Tool 是"动词",Resource 是"名词"。 :::
4.3 💬 Prompts(提示模板)
Prompts 是预定义的提示词模板,包含参数化占位符,帮助用户快速发起标准化的交互。
typescript
// 定义一个 Code Review 提示模板
{
name: "code_review",
description: "对代码片段进行专业的 Code Review",
arguments: [
{
name: "language",
description: "编程语言",
required: true
},
{
name: "code",
description: "待审查的代码",
required: true
}
]
}
:::tip 三大原语的数据流向
- Tools:Host → Client → Server → 执行 → 结果返回 → Host
- Resources:Host → Client → Server → 数据返回 → Host
- Prompts:Host → Client → Server → 模板返回 → Host → 填入 AI 上下文
三者都是 Client 发起、Server 响应,但用途完全不同。 :::
5. 传输机制
MCP 支持多种传输层,适应不同场景:
5.1 传输方式对比
| 传输方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| stdio | 本地进程通信 | 简单、零配置、最安全 | 仅限本地 |
| SSE | 远程 HTTP 通信 | 支持远程、防火墙友好 | 单向推送 |
| Streamable HTTP | 现代远程通信 | 双向流、高效 | 需要 HTTP/2 |
5.2 stdio 传输------最常用的方式
arduino
┌──────────────────┐ ┌──────────────────┐
│ MCP Client │ stdin │ MCP Server │
│ ├─────────►│ │
│ (父进程) │ stdout │ (子进程) │
│ │◄─────────┤ │
└──────────────────┘ └──────────────────┘
通信方式:JSON-RPC 2.0 消息流入/流出
启动方式:Client 作为父进程 spawn Server 进程
:::warning stdio 传输的局限 stdio 模式下,Server 的 stdout 全被 MCP 协议占用。所以 日志必须输出到 stderr,否则会破坏 JSON-RPC 消息格式,导致通信失败。这是新手最常见的坑! :::
5.3 JSON-RPC 2.0 消息格式
MCP 的所有通信都基于 JSON-RPC 2.0,一共四种消息类型:
json
// 📤 请求(Request)------ 有 id,期待响应
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "city": "Beijing" }
}
}
// 📥 成功响应(Response)------ 带 result
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "北京当前天气:晴,温度 28°C,湿度 45%"
}
]
}
}
// 📥 错误响应(Error)------ 带 error
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params",
"data": "city 参数不能为空"
}
}
// 📢 通知(Notification)------ 无 id,无需回复
{
"jsonrpc": "2.0",
"method": "notifications/cancelled",
"params": { "requestId": 1 }
}
6. 动手实战:构建 MCP 服务端
下面分别使用 Python(FastMCP) 和 TypeScript(官方 SDK) 构建一个完整的 MCP Server------包含天气查询、网络搜索、数学计算、文件读取等功能。
6.1 Python 版本 ------ 推荐入门选择
python
# server.py - 天气查询 MCP Server
"""
一个完整的 MCP Server 示例
功能:查询天气 + 文件读取 + 数学计算
"""
from mcp.server.fastmcp import FastMCP
import httpx
import json
from pathlib import Path
# ═══════════════════════════════════
# 1️⃣ 创建 MCP Server 实例
# ═══════════════════════════════════
mcp = FastMCP("我的助手服务")
# ═══════════════════════════════════
# 🛠️ 工具定义
# ═══════════════════════════════════
@mcp.tool()
async def get_weather(city: str) -> str:
"""获取指定城市的实时天气信息。
Args:
city: 城市名称,如 'Beijing', 'Tokyo', 'New York'
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://wttr.in/{city}?format=j1",
timeout=10.0
)
data = response.json()
current = data["current_condition"][0]
return (
f"🌍 {city} 的天气:\n"
f" 🌡️ 温度:{current['temp_C']}°C\n"
f" 💧 湿度:{current['humidity']}%\n"
f" 🌤️ 天气:{current['weatherDesc'][0]['value']}\n"
f" 💨 风速:{current['windspeedKmph']} km/h"
)
@mcp.tool()
async def search_web(query: str, num_results: int = 5) -> str:
"""搜索网络信息。
Args:
query: 搜索关键词
num_results: 返回结果数量,默认5条
"""
return (
f"🔍 关于 '{query}' 的搜索结果(模拟):\n" +
"\n".join(
[f" {i+1}. 结果 {i+1} - {query}"
for i in range(num_results)]
)
)
@mcp.tool()
async def calculate(expression: str) -> str:
"""执行数学计算。
Args:
expression: 数学表达式,如 '2 + 3 * 4', 'sqrt(16)'
"""
import math
try:
# ⚠️ 安全注意:生产环境需更严格的表达式校验
allowed_names = {
k: v for k, v in math.__dict__.items()
if not k.startswith("__")
}
result = eval(
expression,
{"__builtins__": {}},
allowed_names
)
return f"📐 {expression} = {result}"
except Exception as e:
return f"❌ 计算错误: {str(e)}"
# ═══════════════════════════════════
# 📁 资源定义
# ═══════════════════════════════════
@mcp.resource("file://docs/{filename}")
async def get_document(filename: str) -> str:
"""读取文档文件内容。"""
docs_dir = Path("./docs")
file_path = docs_dir / filename
if not file_path.exists():
return f"❌ 文件不存在: {filename}"
if not file_path.is_file():
return f"❌ 不是有效文件: {filename}"
content = file_path.read_text(encoding="utf-8")
return f"📄 {filename}:\n\n{content}"
@mcp.resource("config://settings")
async def get_settings() -> str:
"""获取应用配置信息。"""
return json.dumps({
"version": "1.0.0",
"language": "zh-CN",
"timezone": "Asia/Shanghai",
"features": ["weather", "search", "calculator"]
}, ensure_ascii=False, indent=2)
# ═══════════════════════════════════
# 💬 提示模板定义
# ═══════════════════════════════════
@mcp.prompt()
async def code_review(language: str, code: str) -> str:
"""Code Review 提示模板"""
return f"""请对以下 {language} 代码进行专业的 Code Review:
代码:
{code}
python
请从以下维度评审:
1. 🔒 安全性:是否存在安全漏洞
2. ⚡ 性能:是否有性能瓶颈
3. 📖 可读性:代码是否清晰易懂
4. 🧩 设计:架构是否合理
5. 🧪 测试:是否易于测试
"""
@mcp.prompt()
async def summarize_article(content: str) -> str:
"""文章摘要提示模板"""
return f"""请对以下文章进行总结:
{content}
请提供:
- 📌 一句话总结
- 🔑 3-5 个关键要点
- 💡 个人见解
"""
# ═══════════════════════════════════
# 🚀 启动 Server
# ═══════════════════════════════════
if __name__ == "__main__":
mcp.run(transport="stdio")
:::tip FastMCP vs 原始 SDK FastMCP 是 Python SDK 的上层封装,用装饰器 @mcp.tool() / @mcp.resource() / @mcp.prompt() 即可注册,代码量减少 60%。如果是简单项目,优先使用 FastMCP ;需要更细粒度控制时再用底层 Server 类。 :::
6.2 TypeScript/Node.js 版本
typescript
// server.ts - MCP Server TypeScript 实现
import { McpServer }
from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport }
from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// ═══════════════════════════════════
// 1️⃣ 创建 Server
// ═══════════════════════════════════
const server = new McpServer({
name: "我的助手服务",
version: "1.0.0",
});
// ═══════════════════════════════════
// 🛠️ 工具:获取天气
// ═══════════════════════════════════
server.tool(
"get_weather",
"获取指定城市的实时天气信息",
{
// 使用 Zod 定义参数 Schema
city: z.string().describe("城市名称,如 'Beijing'"),
},
async ({ city }) => {
try {
const response = await fetch(
`https://wttr.in/${encodeURIComponent(city)}?format=j1`
);
const data = await response.json();
const current = data.current_condition[0];
return {
content: [{
type: "text",
text: [
`🌍 ${city} 的天气:`,
` 🌡️ 温度:${current.temp_C}°C`,
` 💧 湿度:${current.humidity}%`,
` 🌤️ 天气:${current.weatherDesc[0].value}`,
` 💨 风速:${current.windspeedKmph} km/h`,
].join("\n"),
}],
};
} catch (error: any) {
return {
content: [{
type: "text",
text: `❌ 获取天气失败:${error.message}`
}],
isError: true,
};
}
}
);
// ═══════════════════════════════════
// 🛠️ 工具:搜索网络
// ═══════════════════════════════════
server.tool(
"search_web",
"搜索网络信息",
{
query: z.string().describe("搜索关键词"),
num_results: z.number()
.min(1).max(10).default(5)
.describe("结果数量"),
},
async ({ query, num_results }) => {
const results = Array.from(
{ length: num_results },
(_, i) => ` ${i + 1}. 关于 "${query}" 的结果 ${i + 1}`
);
return {
content: [{
type: "text",
text: `🔍 关于 '${query}' 的搜索结果:\n${results.join("\n")}`,
}],
};
}
);
// ═══════════════════════════════════
// 📁 资源定义
// ═══════════════════════════════════
server.resource(
"config",
"config://settings",
{
name: "应用配置",
description: "应用的运行时配置"
},
async (uri) => ({
contents: [{
uri: uri.href,
text: JSON.stringify({
version: "1.0.0",
language: "zh-CN",
timezone: "Asia/Shanghai",
features: ["weather", "search", "calculator"],
}, null, 2),
}],
})
);
// ═══════════════════════════════════
// 💬 提示模板
// ═══════════════════════════════════
server.prompt(
"code_review",
"对代码进行 Code Review",
{
language: z.string().describe("编程语言"),
code: z.string().describe("待审查的代码"),
},
({ language, code }) => ({
messages: [{
role: "user",
content: {
type: "text",
text:
`请对以下 ${language} 代码进行专业的 Code Review:\n\n` +
`代码:\n\`\`\`\n${code}\n\`\`\`\n\n` +
`请从安全性、性能、可读性、设计、测试五个维度评审。`,
},
}],
})
);
// ═══════════════════════════════════
// 🚀 启动 Server
// ═══════════════════════════════════
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("🚀 MCP Server 已启动 (stdio 模式)");
}
main().catch(console.error);
7. 动手实战:构建 MCP 客户端
7.1 Python 客户端
python
# client.py - MCP Client 完整实现
"""
MCP 客户端示例
连接上面的天气查询 Server 并与之交互
"""
import asyncio
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# ═══════════════════════════════════
# 1️⃣ 配置 Server 启动参数
# ═══════════════════════════════════
server_params = StdioServerParameters(
command="python",
args=["server.py"],
env=None, # 可注入环境变量
)
# ═══════════════════════════════════
# 2️⃣ 建立连接
# ═══════════════════════════════════
async with AsyncExitStack() as stack:
# 启动 Server 子进程并建立双向通信管道
stdio_transport = await stack.enter_async_context(
stdio_client(server_params)
)
read_stream, write_stream = stdio_transport
# 创建客户端会话
session = await stack.enter_async_context(
ClientSession(read_stream, write_stream)
)
# 握手:交换协议版本和能力信息
await session.initialize()
print("✅ 已连接到 MCP Server")
# ═══════════════════════════════════
# 3️⃣ 发现可用工具
# ═══════════════════════════════════
tools_result = await session.list_tools()
print("\n📋 可用工具:")
for tool in tools_result.tools:
print(f" • {tool.name}: {tool.description}")
# ═══════════════════════════════════
# 4️⃣ 调用工具
# ═══════════════════════════════════
# 4.1 查询天气
print("\n" + "=" * 50)
print("🌤️ 调用 get_weather...")
result = await session.call_tool(
"get_weather",
arguments={"city": "Beijing"}
)
print(result.content[0].text)
# 4.2 搜索网络
print("\n" + "=" * 50)
print("🔍 调用 search_web...")
result = await session.call_tool(
"search_web",
arguments={
"query": "Python MCP tutorial",
"num_results": 3
}
)
print(result.content[0].text)
# 4.3 数学计算
print("\n" + "=" * 50)
print("📐 调用 calculate...")
result = await session.call_tool(
"calculate",
arguments={"expression": "(100 + 200) * 0.15"}
)
print(result.content[0].text)
# ═══════════════════════════════════
# 5️⃣ 读取资源
# ═══════════════════════════════════
print("\n" + "=" * 50)
print("📁 读取资源...")
# 列出所有可用资源
resources_result = await session.list_resources()
print("\n📋 可用资源:")
for resource in resources_result.resources:
print(f" • {resource.uri}: {resource.name}")
# 读取配置资源
config = await session.read_resource("config://settings")
print(f"\n⚙️ 配置内容:\n{config.contents[0].text}")
# ═══════════════════════════════════
# 6️⃣ 获取提示模板
# ═══════════════════════════════════
print("\n" + "=" * 50)
print("💬 获取提示模板...")
prompts_result = await session.list_prompts()
print("\n📋 可用提示模板:")
for prompt in prompts_result.prompts:
print(f" • {prompt.name}: {prompt.description}")
# 使用 Code Review 提示模板
review_prompt = await session.get_prompt(
"code_review",
arguments={
"language": "Python",
"code": "def add(a,b): return a+b"
}
)
print(
f"\n📝 Code Review 提示词:"
f"{review_prompt.messages[0].content.text[:200]}..."
)
# ═══════════════════════════════════
# 🚀 运行
# ═══════════════════════════════════
if __name__ == "__main__":
asyncio.run(main())
7.2 TypeScript 客户端
typescript
// client.ts - MCP Client TypeScript 实现
import { Client }
from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport }
from "@modelcontextprotocol/sdk/client/stdio.js";
async function main() {
// ═══════════════════════════════════
// 1️⃣ 创建传输层(启动 Server 子进程)
// ═══════════════════════════════════
const transport = new StdioClientTransport({
command: "node",
args: ["dist/server.js"], // 编译后的 Server
});
// ═══════════════════════════════════
// 2️⃣ 创建 Client 并连接
// ═══════════════════════════════════
const client = new Client({
name: "我的 MCP 客户端",
version: "1.0.0",
});
await client.connect(transport);
console.log("✅ 已连接到 MCP Server");
// ═══════════════════════════════════
// 3️⃣ 发现工具
// ═══════════════════════════════════
const { tools } = await client.listTools();
console.log("\n📋 可用工具:");
tools.forEach((tool) => {
console.log(` • ${tool.name}: ${tool.description}`);
});
// ═══════════════════════════════════
// 4️⃣ 调用工具
// ═══════════════════════════════════
// 查询天气
console.log("\n" + "=".repeat(50));
console.log("🌤️ 调用 get_weather...");
const weatherResult = await client.callTool({
name: "get_weather",
arguments: { city: "Tokyo" },
});
console.log((weatherResult.content as any)[0].text);
// 计算
console.log("\n" + "=".repeat(50));
console.log("📐 调用 calculate...");
const calcResult = await client.callTool({
name: "calculate",
arguments: { expression: "2 ** 10" },
});
console.log((calcResult.content as any)[0].text);
// ═══════════════════════════════════
// 5️⃣ 读取资源
// ═══════════════════════════════════
const { resources } = await client.listResources();
console.log("\n📋 可用资源:");
resources.forEach((res) => {
console.log(` • ${res.uri}: ${res.name}`);
});
const config = await client.readResource({
uri: "config://settings",
});
console.log(
`\n⚙️ 配置:\n${(config.contents[0] as any).text}`
);
// ═══════════════════════════════════
// 6️⃣ 关闭连接
// ═══════════════════════════════════
await client.close();
console.log("\n👋 连接已关闭");
}
main().catch(console.error);
8. 服务端与客户端如何连接
这一节是整个 MCP 通信的核心------理解连接生命周期,就理解了 MCP 的全貌。
8.1 连接生命周期(五个阶段)
arduino
┌──────────────────────────────────────────────────────────┐
│ MCP 连接生命周期 │
│ │
│ 1. 启动阶段 │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Client spawn Server 子进程 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Server 启动,通过 stdout 发送就绪信号 │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ 2. 握手阶段 ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Client ──── initialize ────────► Server │ │
│ │ Client ◄─── InitializeResult ─── Server │ │
│ │ │ │
│ │ 交换:协议版本 + 能力声明 │ │
│ │ Client:我的协议版本、客户端信息 │ │
│ │ Server:我的能力(tools/resources/prompts) │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ 3. 发现阶段 ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Client ──── tools/list ──────────► Server │ │
│ │ Client ◄─── ListToolsResult ───── Server │ │
│ │ │ │
│ │ Client ──── resources/list ──────► Server │ │
│ │ Client ◄─── ListResourcesResult ── Server │ │
│ │ │ │
│ │ Client ──── prompts/list ────────► Server │ │
│ │ Client ◄─── ListPromptsResult ─── Server │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ 4. 工作阶段 ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 循环: │ │
│ │ AI 决定调用工具 → tools/call → Server 执行 → 返回 │ │
│ │ AI 根据结果继续推理 → 再次调用 → ... │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ 5. 关闭阶段 ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Client 关闭 stdin → kill 子进程 → 连接断开 │ │
│ └───────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
8.2 握手协议------能力协商
握手是 Client 和 Server 互相"认识"的过程,也是能力协商的关键步骤:
json
// === ① 客户端发送 InitializeRequest ===
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {}
},
"clientInfo": {
"name": "MyApp",
"version": "1.0.0"
}
}
}
// === ② 服务端返回 InitializeResult ===
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"prompts": {
"listChanged": true
},
"logging": {}
},
"serverInfo": {
"name": "我的助手服务",
"version": "1.0.0"
}
}
}
// === ③ 客户端确认(发送 initialized 通知)===
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
8.3 完整消息流时序图
arduino
时间线 Client Server
────── ────── ──────
│
│ ① 启动 Server 子进程 ──────────────────────► 启动
│ │
│ ② ◄──────── init (能力声明)───────────────── 就绪
│ ──────── initialized ───────────────────► │
│ │
│ ③ ──────── tools/list ────────────────────► │
│ ◄──────── [get_weather, │
│ search_web, │
│ calculate] ────────────────── │
│ │
│ ④ ──────── tools/call ────────────────────► │
│ {name:"get_weather", │
│ arguments:{city:"Beijing"}} │
│ │ 执行
│ │ API 调用
│ │
│ ◄──────── 天气结果 ───────────────────── │
│ │
│ ⑤ ──────── resources/read ────────────────► │
│ {uri:"config://settings"} │
│ ◄──────── 配置 JSON ──────────────────── │
│ │
│ ⑥ ──────── 关闭 stdin ────────────────────► │
│ ──────── kill 进程 ────────────────────► 退出
▼
:::tip 为什么用子进程而不是 HTTP? stdio 模式中 Client 是父进程、Server 是子进程的设计有深层原因:最小权限原则。Server 的生命周期完全受 Client 控制------Client 启动时创建、关闭时销毁。这种方式天然适合桌面应用和本地开发工具的场景,也避免了端口占用和网络攻击面。 :::
9. Claude Code 中使用 MCP
Claude Code 是目前对 MCP 支持最好的 AI 编程工具,通过简单配置即可接入任意 MCP Server。
9.1 四种 Server 接入方式
json
// 配置文件路径:~/.claude/claude_desktop_config.json
// 或项目级配置:.claude/mcp.json
{
"mcpServers": {
// ─────────────────────────────────
// 方式 1:本地 stdio Server(推荐)
// ─────────────────────────────────
"my-weather": {
"command": "python",
"args": ["/path/to/server.py"],
"env": {
"API_KEY": "your-api-key"
}
},
// ─────────────────────────────────
// 方式 2:远程 HTTP Server (SSE)
// ─────────────────────────────────
"remote-database": {
"url": "https://my-mcp-server.example.com/sse",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
},
// ─────────────────────────────────
// 方式 3:Docker 容器
// ─────────────────────────────────
"docker-tools": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"mcp/server-image:latest"
]
},
// ─────────────────────────────────
// 方式 4:Node.js 包(通过 npx)
// ─────────────────────────────────
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxx"
}
}
}
}
9.2 管理 MCP Server
bash
# 查看已注册的 MCP Server
claude mcp list
# 输出:
# ┌─────────────────┬──────────┬─────────┐
# │ Server │ Status │ Tools │
# ├─────────────────┼──────────┼─────────┤
# │ my-weather │ Connected│ 3 │
# │ remote-database │ Connected│ 5 │
# │ docker-tools │ Stopped │ - │
# │ github │ Connected│ 12 │
# └─────────────────┴──────────┴─────────┘
# 查看某个 Server 的详细信息
claude mcp get my-weather
# 手动控制 Server 启停
claude mcp start my-weather
claude mcp stop my-weather
9.3 社区热门 MCP Server
| Server 名称 | 功能 | 安装命令 |
|---|---|---|
| server-github | GitHub 仓库/PR/Issue 管理 | npx @modelcontextprotocol/server-github |
| server-postgres | PostgreSQL 数据库查询 | npx @modelcontextprotocol/server-postgres |
| server-filesystem | 安全的文件系统操作 | npx @modelcontextprotocol/server-filesystem |
| server-brave-search | Brave 搜索引擎 | npx @modelcontextprotocol/server-brave-search |
| server-puppeteer | 浏览器自动化/截图 | npx @modelcontextprotocol/server-puppeteer |
| server-memory | 持久化记忆存储 | npx @modelcontextprotocol/server-memory |
| server-slack | Slack 消息管理 | npx @modelcontextprotocol/server-slack |
| server-sentry | Sentry 错误监控 | npx @modelcontextprotocol/server-sentry |
:::tip 选择建议
- 个人开发 :从
filesystem+github开始,覆盖日常 80% 场景 - 后端开发 :加
postgres或你使用的数据库对应 Server - 全栈/前端 :加
puppeteer做浏览器交互 - 安全红线 :filesystem Server 务必限制目录范围,比如只允许
/workspace:::
10. 高级主题
:::details 点击展开:资源订阅机制
10.1 资源订阅机制
MCP 支持 "订阅-推送" 模式,Server 可主动向 Client 推送资源变更通知:
python
# Server 端:定义可订阅的资源
@mcp.resource("file://logs/app.log")
async def get_log() -> str:
return Path("./app.log").read_text()
# 当文件变化时,Server 主动通知所有订阅的 Client
# Client 收到通知后决定是否重新读取
# Client 端:订阅资源
await session.subscribe_resource("file://logs/app.log")
# 设置资源变更回调
async def on_resource_updated(uri: str):
"""当订阅的资源更新时触发"""
print(f"📢 资源已更新: {uri}")
content = await session.read_resource(uri)
print(f" 新内容: {content.contents[0].text[:100]}...")
session.on_resource_updated = on_resource_updated
:::
:::details 点击展开:服务端采样(反向调用 AI)
10.2 服务端采样------Server → Client 请求 AI 推理
MCP 支持 反向调用:Server 可以向 Client 请求 AI 推理能力。这意味着你的工具可以利用 AI 来处理中间结果。
python
# Server 端:请求 Client 进行 AI 推理
@mcp.tool()
async def analyze_sentiment(text: str) -> str:
"""分析文本情感(通过反向调用 AI 实现)"""
# 向 Client 请求 AI 推理
response = await mcp.request_sampling(
messages=[{
"role": "user",
"content": {
"type": "text",
"text": (
"分析以下文本的情感,"
"只回答 'positive', 'negative', 或 'neutral':"
f"\n\n{text}"
)
}
}],
max_tokens=10
)
return f"情感分析结果:{response.content.text}"
:::warning 使用限制 并非所有 Client 都支持采样功能。Claude Code 目前不支持服务端采样,自建 Client 需要在 initialize 时声明 sampling 能力。 ::: :::
:::details 点击展开:动态工具注册
10.3 动态工具列表
工具列表可以在运行时动态变化,无需重启 Server:
python
# 动态注册工具
class DynamicTools:
def __init__(self):
self._tools = {}
def register_tool(
self,
name: str,
handler: callable,
schema: dict
):
"""运行时动态添加工具"""
self._tools[name] = {
"handler": handler,
"schema": schema
}
# 通知所有 Client 工具列表已更新
mcp.notify_tool_list_changed()
def unregister_tool(self, name: str):
"""运行时移除工具"""
del self._tools[name]
mcp.notify_tool_list_changed()
dynamic_tools = DynamicTools()
# 根据配置动态加载工具
for tool_config in load_tools_from_config():
dynamic_tools.register_tool(
name=tool_config["name"],
handler=create_handler(tool_config),
schema=tool_config["schema"]
)
:::
:::details 点击展开:多 Server 编排
10.4 多 Server 编排
真实场景中往往需要同时连接多个 MCP Server:
python
# 同时连接多个 MCP Server
async def connect_multiple_servers():
servers = {
"files": StdioServerParameters(
command="npx",
args=[
"-y",
"@modelcontextprotocol/server-filesystem",
"/workspace"
]
),
"database": StdioServerParameters(
command="npx",
args=[
"-y",
"@modelcontextprotocol/server-postgres"
],
env={"DATABASE_URL": "postgresql://..."}
),
"github": StdioServerParameters(
command="npx",
args=[
"-y",
"@modelcontextprotocol/server-github"
],
env={"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxx"}
),
}
sessions = {}
async with AsyncExitStack() as stack:
for name, params in servers.items():
transport = await stack.enter_async_context(
stdio_client(params)
)
read, write = transport
session = await stack.enter_async_context(
ClientSession(read, write)
)
await session.initialize()
sessions[name] = session
print(f"✅ {name} 已连接")
# 汇总所有 Server 的工具
all_tools = {}
for name, session in sessions.items():
result = await session.list_tools()
for tool in result.tools:
all_tools[f"{name}:{tool.name}"] = (
session, tool
)
print(f"\n📋 总计 {len(all_tools)} 个工具可用")
# 使用特定工具
session, _ = all_tools["files:read_file"]
result = await session.call_tool(
"read_file",
{"path": "/workspace/app.py"}
)
:::
11. 常见问题与调试
11.1 开启调试日志
python
# Server 端开启详细日志
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
logging.FileHandler("mcp_server.log"),
logging.StreamHandler() # 注意:输出到 stderr!
]
)
typescript
// Client 端监控所有通信消息
import { createLogger }
from "@modelcontextprotocol/sdk/shared/logging.js";
const logger = createLogger("my-mcp-client");
logger.setLevel("debug");
client.onMessage = (message) => {
logger.debug(
`📩 收到: ${JSON.stringify(message, null, 2)}`
);
};
11.2 常见错误速查表
| 错误症状 | 可能原因 | 解决方案 |
|---|---|---|
Connection refused |
Server 未启动或端口错误 | 检查 Server 进程是否运行 |
Protocol version mismatch |
客户端/服务端协议版本不兼容 | 统一 MCP SDK 版本 |
Tool not found |
工具名称拼写错误或未注册 | 先调用 list_tools() 确认 |
Invalid params |
参数类型/格式不正确 | 对照 inputSchema 仔细检查 |
Timeout |
工具执行超时 | 增加超时时间或优化工具性能 |
Broken pipe |
Server 进程异常崩溃 | 查看 Server 的 stderr 日志 |
stdin/stdout closed |
进程通信管道被意外关闭 | 确保 Server 在工具执行期间不退出 |
11.3 调试技巧
bash
# 1. 直接 stdin 测试 Server
$ python server.py
# 粘贴 JSON-RPC 消息看响应
# 2. 使用官方 Inspector(强烈推荐)
$ npx @modelcontextprotocol/inspector python server.py
# 会打开一个 Web UI,可交互式测试所有工具
# 3. 检查进程
$ ps aux | grep mcp
# 4. 查看 Server 日志(注意是 stderr!)
$ python server.py 2> server.log
$ tail -f server.log
# 5. 测试远程 Server 连通性
$ nc -v my-mcp-server.example.com 3000
:::tip 调试神器:MCP Inspector 官方提供的 @modelcontextprotocol/inspector 是调试 MCP Server 的最佳工具。它会启动一个本地 Web 页面,让你可以:
- 查看 Server 所有 Tools / Resources / Prompts
- 交互式调用工具并查看结果
- 查看完整的 JSON-RPC 消息历史
- 模拟 Client 的各种边界情况 :::
12. 最佳实践
12.1 安全指南
python
# ❌ 危险示例:直接 eval 用户输入
@mcp.tool()
async def dangerous_exec(code: str) -> str:
return str(eval(code)) # 这等于给 AI 一个远程 shell!
# ✅ 安全示例:白名单 + 沙箱
@mcp.tool()
async def safe_calculate(expression: str) -> str:
import re
# 1. 严格白名单校验
if not re.match(r'^[\d\s+\-*/().%\s]+$', expression):
return "❌ 只允许基本数学运算"
# 2. 禁用所有内置函数
safe_dict = {"__builtins__": {}}
# 3. 限制计算资源
try:
result = eval(expression, safe_dict, {})
return f"结果: {result}"
except Exception as e:
return f"❌ 错误: {str(e)}"
:::danger 安全红线
- 永远不要 直接把用户输入传给
eval()、exec()、os.system() - 务必限制 filesystem Server 的根目录范围
- 始终验证 工具参数的类型、长度、范围
- 不要 在工具中硬编码 API Key,用环境变量
- 警惕 工具间循环调用(Tool A → Tool B → Tool A) :::
12.2 错误处理最佳实践
python
# ✅ 分层的错误处理
@mcp.tool()
async def robust_tool(param: str) -> str:
try:
# 1. 参数前置校验
if not param or len(param) > 1000:
return "❌ 参数无效:不能为空且不超过1000字符"
# 2. 执行业务逻辑
result = await do_something(param)
# 3. 返回结构化结果
return f"✅ 操作成功:{result}"
except ConnectionError as e:
return f"❌ 网络错误:{e},请检查网络连接"
except PermissionError as e:
return f"❌ 权限不足:{e}"
except Exception as e:
# 兜底:记录日志但不暴露内部细节
logging.exception(f"工具执行异常: {e}")
return "❌ 未知错误,请联系管理员"
12.3 性能优化
python
# ✅ 使用 TTL 缓存减少重复计算
from functools import lru_cache
import time
@lru_cache(maxsize=100)
def cached_computation(key: str) -> str:
"""缓存计算结果"""
return expensive_operation(key)
@mcp.tool()
async def smart_tool(query: str) -> str:
cache_key = f"tool_result:{hash(query)}"
# 检查缓存是否有效
if cache_key in cache:
cached = cache[cache_key]
if time.time() < cached["expires"]:
return cached["result"]
# 执行并缓存
result = await do_expensive_work(query)
cache[cache_key] = {
"result": result,
"expires": time.time() + 60 # 60秒 TTL
}
return result
12.4 工具设计五原则
| 原则 | 说明 | ✅ 好例子 | ❌ 坏例子 |
|---|---|---|---|
| 单一职责 | 一个工具只做一件事 | read_file(path) |
magic_do_everything() |
| 清晰描述 | description 准确说明功能 | "读取指定路径的文件内容" | "处理文件" |
| 类型安全 | Schema 严格定义 | count: z.number().int() |
count: z.any() |
| 幂等性 | 读操作可安全重复 | get_weather(city) |
toggle_feature_flag() |
| 明确错误 | 错误信息具体可操作 | "文件不存在: /a/b.txt" | "出错了" |
📚 参考资源
- 🔗 MCP 官方文档 ------ 入门必读
- 🔗 MCP 协议规范 ------ 完整规范
- 🔗 MCP GitHub 组织 ------ 所有 SDK 和 Server
- 🔗 Python SDK ------ Python 开发
- 🔗 TypeScript SDK ------ TS/JS 开发
- 🔗 社区 Server 大全 ------ 200+ 现成 Server
- 🔗 Claude Code 官方文档 ------ Claude Code 集成指南
💡 一句话总结:MCP 让 AI 模型从"孤岛"走向"互联",它为 AI Agent 时代提供了标准化的"神经系统"。掌握了 MCP,你就掌握了构建下一代 AI 应用的核心技能。
📝 本文写于 2026年6月 · 欢迎点赞收藏,如有错误或建议,评论区见!