引言:为什么MCP正在改变AI Agent的开发方式
2025年底,Anthropic开源了Model Context Protocol(MCP)规范,到2026年中期,MCP已经成为AI Agent开发生态中最重要的标准化协议之一。它解决了一个核心问题:如何让LLM应用安全、标准化地与外部工具和数据源交互。
在MCP出现之前,每个AI Agent框架都有自己的工具调用约定——OpenAI的function calling、LangChain的Tool抽象、Anthropic的tool use格式互不兼容。开发者想要同时支持多个LLM提供商的工具调用能力,需要写大量的适配代码。MCP的出现就像HTTP协议标准化了Web通信一样,将AI Agent的工具调用抽象为标准化的客户端-服务器架构。

本文将从零开始,带你完整实现一个MCP服务器和客户端,涵盖文件操作、数据库查询、API调用等真实场景,并介绍如何将MCP集成到现有的AI工作流中。
MCP核心架构解析
MCP采用经典的客户端-服务器(Client-Server)架构,但针对AI Agent场景做了大量优化:
| 组件 | 角色 | 典型实现 | ||
|---|---|---|---|---|
| MCP Host | 用户交互的入口,如Claude Desktop、IDE插件 | Claude Desktop、VS Code、Cursor | ||
| MCP Client | 与MCP Server建立一对一连接的客户端 | Python SDK中的
|
||
| MCP Server | 提供标准化工具、资源和提示的轻量服务 | 自定义Python/TypeScript Server | ||
| 传输层 | 通信协议,支持stdio和SSE两种模式 | stdin/stdout管道或HTTP SSE |
最关键的创新是传输层设计:stdio模式让MCP Server可以作为子进程运行,零网络延迟;SSE模式则支持远程服务器。这意味着既可以在本地用Python脚本快速提供服务,也可以在远端部署专用的工具服务器集群。
MCP协议的三大核心能力
MCP定义了三种核心交互原语:
- Tools(工具):LLM可以调用的函数,执行后返回结果给模型。类似function calling但标准化了schema定义。
- Resources(资源):结构化的只读数据,类似文件系统的文件概念,支持URI寻址。
- Prompts(提示模板):预定义的提示模板,用户可以选择性地加载,简化交互流程。
这三种原语组合起来,几乎可以覆盖所有AI Agent与外部世界的交互场景。
环境准备:安装MCP SDK
首先,我们需要安装MCP的Python SDK。MCP官方推荐使用Python 3.10+:
1
2
3
4
5
6
7
8
9 # 创建虚拟环境
python3 -m venv .venv-mcp
source .venv-mcp/bin/activate
# 安装MCP SDK和依赖
pip install mcp httpx aiosqlite pydantic
# 验证安装
python -c "import mcp; print(mcp.__version__)"
如果你使用Node.js生态,也可以安装TypeScript版本:
1 npm install @modelcontextprotocol/sdk
本文以Python为例,但核心概念同样适用于TypeScript实现。
实战1:构建一个文件系统MCP Server
我们先从最实用的场景开始——构建一个文件操作MCP Server,让AI Agent能够安全地读写文件。这是AI编程助手最基础也最强大的能力之一。
定义工具Schema
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 # file_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool
import os
import json
app = Server("file-server")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="read_file",
description="读取指定文件的内容,支持文本文件",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"},
"encoding": {"type": "string", "description": "文件编码,默认utf-8"}
},
"required": ["path"]
}
),
Tool(
name="write_file",
description="写入内容到文件,不存在则创建",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"},
"content": {"type": "string", "description": "文件内容"},
"encoding": {"type": "string", "description": "文件编码,默认utf-8"}
},
"required": ["path", "content"]
}
),
Tool(
name="list_directory",
description="列出目录内容",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "目录路径"}
},
"required": ["path"]
}
)
]
实现工具处理逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 @app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
safe_root = os.path.expanduser("~/mcp_workspace")
os.makedirs(safe_root, exist_ok=True)
if name == "read_file":
# 安全路径检查
abs_path = os.path.abspath(os.path.join(safe_root, arguments["path"]))
if not abs_path.startswith(safe_root):
return [TextContent(type="text", text="错误:路径越界访问")]
encoding = arguments.get("encoding", "utf-8")
with open(abs_path, "r", encoding=encoding) as f:
content = f.read()
return [TextContent(type="text", text=content)]
elif name == "write_file":
abs_path = os.path.abspath(os.path.join(safe_root, arguments["path"]))
if not abs_path.startswith(safe_root):
return [TextContent(type="text", text="错误:路径越界访问")]
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
encoding = arguments.get("encoding", "utf-8")
with open(abs_path, "w", encoding=encoding) as f:
f.write(arguments["content"])
return [TextContent(type="text", text=f"成功写入 {len(arguments['content'])} 字符到 {arguments['path']}")]
elif name == "list_directory":
abs_path = os.path.abspath(os.path.join(safe_root, arguments["path"]))
if not abs_path.startswith(safe_root):
return [TextContent(type="text", text="错误:路径越界访问")]
entries = os.listdir(abs_path)
result = "\n".join(f"{'📁' if os.path.isdir(os.path.join(abs_path, e)) else '📄'} {e}" for e in entries)
return [TextContent(type="text", text=result or "(空目录)")]
raise ValueError(f"未知工具: {name}")
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__":
import asyncio
asyncio.run(main())
这个文件服务器最关键的实践是沙箱路径隔离:通过将操作限制在
1 | ~/mcp_workspace |
目录内,确保AI Agent即使被注入恶意指令,也无法读取系统关键文件。这是MCP Server设计时最重要的安全考量之一。
实战2:数据库查询MCP Server
第二个实战是构建一个数据库查询工具,让AI Agent能够通过自然语言分析数据库。这里我们用SQLite作为演示,实际生产中可以替换为PostgreSQL或MySQL。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 # db_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool
import sqlite3
import json
app = Server("db-server")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_database",
description="执行数据库查询,返回结果JSON",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "SQL查询语句"},
"params": {
"type": "array",
"items": {"type": "string"},
"description": "查询参数"
}
},
"required": ["query"]
}
),
Tool(
name="get_schema",
description="获取数据库中所有表的结构",
inputSchema={
"type": "object",
"properties": {}
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
db_path = "~/mcp_workspace/data.db"
db_path = os.path.expanduser(db_path)
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
try:
if name == "query_database":
query = arguments["query"]
params = arguments.get("params", [])
cursor.execute(query, params)
if query.strip().upper().startswith("SELECT"):
rows = [dict(row) for row in cursor.fetchall()]
result = json.dumps(rows, ensure_ascii=False, default=str)
else:
conn.commit()
result = json.dumps({
"affected_rows": cursor.rowcount,
"last_row_id": cursor.lastrowid
})
return [TextContent(type="text", text=result)]
elif name == "get_schema":
cursor.execute("SELECT name, sql FROM sqlite_master WHERE type='table'")
schema = {}
for row in cursor.fetchall():
schema[row["name"]] = row["sql"]
return [TextContent(type="text", text=json.dumps(schema, ensure_ascii=False))]
finally:
conn.close()
raise ValueError(f"未知工具: {name}")
这个Server的核心价值在于:AI Agent不再需要在提示词中嵌入完整的数据结构,而是通过
1 | get_schema |
工具动态获取表结构,再生成精确的SQL查询。这对于处理大型数据库尤其有效——LLM的上下文窗口再大,也不适合把整个Schema硬编码进提示词里。
实战3:集成外部API的MCP Server
第三个实战是集成第三方API。这里以GitHub API为例,构建一个可以查询仓库信息、Issue、PR的MCP Server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 # github_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool
import httpx
import os
app = Server("github-server")
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="get_repo_info",
description="获取GitHub仓库信息",
inputSchema={
"type": "object",
"properties": {
"owner": {"type": "string"},
"repo": {"type": "string"}
},
"required": ["owner", "repo"]
}
),
Tool(
name="search_code",
description="在GitHub仓库中搜索代码",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string"},
"owner": {"type": "string"},
"repo": {"type": "string"},
"path": {"type": "string"}
},
"required": ["query"]
}
),
Tool(
name="list_issues",
description="列出仓库的Issues",
inputSchema={
"type": "object",
"properties": {
"owner": {"type": "string"},
"repo": {"type": "string"},
"state": {"type": "string", "enum": ["open", "closed", "all"]},
"limit": {"type": "integer"}
},
"required": ["owner", "repo"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
headers = {"Accept": "application/vnd.github.v3+json"}
if GITHUB_TOKEN:
headers["Authorization"] = f"token {GITHUB_TOKEN}"
async with httpx.AsyncClient(headers=headers) as client:
if name == "get_repo_info":
url = f"https://api.github.com/repos/{arguments['owner']}/{arguments['repo']}"
resp = await client.get(url)
data = resp.json()
summary = {
"name": data["full_name"],
"stars": data["stargazers_count"],
"forks": data["forks_count"],
"language": data["language"],
"description": data["description"],
"topics": data.get("topics", []),
"license": data.get("license", {}).get("spdx_id"),
"updated": data["updated_at"]
}
return [TextContent(type="text", text=json.dumps(summary, ensure_ascii=False))]
elif name == "search_code":
q = arguments["query"]
if "owner" in arguments and "repo" in arguments:
q += f" repo:{arguments['owner']}/{arguments['repo']}"
if "path" in arguments:
q += f" path:{arguments['path']}"
resp = await client.get(
"https://api.github.com/search/code",
params={"q": q, "per_page": 5}
)
items = resp.json().get("items", [])
results = [{"file": i["path"], "url": i["html_url"]} for i in items]
return [TextContent(type="text", text=json.dumps(results, ensure_ascii=False))]
raise ValueError(f"未知工具: {name}")
这个Server展示了MCP的一个重要特性:工具的组合性。你可以同时启动文件服务器、数据库服务器和GitHub服务器,在同一个Host中共存。AI Agent会自动判断哪个工具最适合当前任务。
配置与运行MCP Server
MCP Server的配置非常简洁。以Claude Desktop为例,在配置文件中添加:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 # ~/.config/claude/claude_desktop_config.json
{
"mcpServers": {
"file-server": {
"command": "python3",
"args": ["/path/to/file_server.py"],
"env": {}
},
"db-server": {
"command": "python3",
"args": ["/path/to/db_server.py"],
"env": {}
},
"github-server": {
"command": "python3",
"args": ["/path/to/github_server.py"],
"env": {
"GITHUB_TOKEN": "ghp_xxxx"
}
}
}
}
如果你在自定义的AI应用中集成MCP,可以使用MCP Client SDK以编程方式连接:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 from mcp.client.stdio import stdio_client
from mcp import StdioServerParameters
async def use_mcp_tool():
# 启动文件服务器作为子进程
server_params = StdioServerParameters(
command="python3",
args=["file_server.py"]
)
async with stdio_client(server_params) as (read, write):
# 创建会话
async with Client(read, write) as client:
# 获取可用工具列表
tools = await client.list_tools()
print(f"可用工具: {[t.name for t in tools]}")
# 调用工具
result = await client.call_tool(
"write_file",
{"path": "hello.txt", "content": "Hello MCP!"}
)
print(f"结果: {result.content[0].text}")
import asyncio
asyncio.run(use_mcp_tool())
MCP Server的安全最佳实践
MCP在带来强大能力的同时也引入了新的安全风险,以下是必须遵守的安全原则:
1. 最小权限原则
每个MCP Server只应该拥有完成其功能所需的最小权限。文件服务器不应该能访问网络,GitHub服务器不应该能读写本地文件。在stdio模式下,子进程默认只继承父进程的权限,这是天然的安全隔离。
2. 沙箱路径限制
所有文件操作类Server必须做路径验证,防止路径穿越攻击(Path Traversal)。Python中的
1 | os.path.abspath() |
结合
1 | os.path.commonpath() |
是最基本的防护。
3. 速率限制与资源控制
数据库查询工具必须实现超时控制和结果集大小限制,防止AI Agent生成全表扫描这类耗时操作:
1
2
3
4
5
6
7
8 DEFAULT_TIMEOUT = 5 # 查询超时5秒
MAX_ROWS = 1000 # 最多返回1000行
# 在查询工具中添加
cursor.execute(f"SELECT COUNT(*) FROM ({query})", params)
count = cursor.fetchone()[0]
if count > MAX_ROWS:
query = f"SELECT * FROM ({query}) LIMIT {MAX_ROWS}"
4. 环境变量隔离
敏感API密钥通过
1 | env |
字段传入,而不是硬编码在代码中。MCP的配置格式天然支持这一点。
5. 审计日志
生产环境的MCP Server应该记录所有工具调用和结果,便于事后审计和调试:
1
2
3
4
5
6
7
8
9
10
11
12
13 import logging
logging.basicConfig(
filename="mcp_audit.log",
level=logging.INFO,
format="%(asctime)s [%(name)s] %(message)s"
)
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
logging.info(f"工具调用: {name}, 参数: {json.dumps(arguments)[:500]}")
# ... 处理逻辑 ...
logging.info(f"工具返回: {str(result)[:500]}")
return result
MCP生态现状与趋势
截至2026年中期,MCP生态已经非常丰富:
- 官方SDK:Python和TypeScript两个版本都进入稳定期,社区贡献的Rust SDK
1mcp-rs
也日趋成熟。
- Claude Desktop:原生支持MCP,也是最大的MCP客户端。
- VS Code / Cursor:通过MCP插件可以无缝集成自定义工具。
- Hermes Agent:开源AI Agent框架,通过
1hermes mcp add
命令即可添加任意MCP Server。
- LangChain / LangGraph:通过MCP Client Wrapper可以复用MCP生态的工具。
- 社区MCP Server市场:已有超过1000个公开MCP Server,覆盖数据库、云服务、DevOps、设计工具等各种场景。

一个值得关注的趋势是MCP Gateway——在企业内部部署统一的MCP Gateway,对外暴露标准化工具接口,内部对接各种遗留系统和API。这类似于API Gateway在微服务架构中的角色,但针对AI Agent场景做了优化。
总结
MCP正在成为AI Agent开发的事实标准。通过本文的实战,你应该已经掌握了:
- MCP的核心架构和三种交互原语(Tools、Resources、Prompts)
- 如何编写文件操作、数据库查询、外部API集成三类MCP Server
- MCP Server的配置、运行和客户端集成方式
- 生产环境中MCP Server的安全最佳实践
- MCP生态的现状和发展趋势
下一步,你可以尝试将MCP集成到自己的AI工作流中。无论是个人使用的自动化脚本,还是企业级的AI Agent平台,MCP都提供了清晰、标准化的工具集成方案。随着AI Agent越来越普及,掌握MCP将成为每个AI工程师的基本技能。
参考资源:
- MCP官方规范:modelcontextprotocol.io
- Python SDK源码:github.com/modelcontextprotocol/python-sdk
- 社区Server合集:awesome-mcp-servers
- Hermes Agent MCP集成文档:hermes-agent MCP Guide
汤不热吧