zhangzs/MCP_Client_Server
If you are the rightful owner of MCP_Client_Server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
This document provides a comprehensive guide to building a Model Context Protocol (MCP) server and client for AI assistants, focusing on file system operations.
MCP实战:从零开始构建 AI 助手的工具系统
在 AI 时代,大语言模型虽然强大,但它们的能力仅限于文本生成和理解。如果想让 AI 助手真正"做事"——比如读写文件、查询数据库、调用 API——就需要为它们提供工具。这就是 MCP(Model Context Protocol) 的用武之地。
本文将通过一个完整的文件系统操作实例,带你从零开始构建 MCP 服务器和客户端,让你的 AI 助手具备真正的"动手能力"。
一、MCP 基本概念
什么是 MCP?
MCP(Model Context Protocol)是由 Anthropic 开发的开放协议,旨在标准化 AI 应用与外部工具、数据源之间的连接方式。简单来说,MCP 就像是 AI 助手的"工具箱接口",让 AI 能够安全、可控地访问和操作外部资源。
MCP 的核心价值
传统的 AI 应用往往需要为每个工具单独编写集成代码,维护成本高、扩展性差。MCP 通过标准化协议解决了这个问题:
- 统一接口:所有工具都遵循相同的调用规范
- 即插即用:开发者可以轻松添加或替换工具
- 安全可控:明确的权限边界和参数验证
- 生态共享:社区可以贡献和复用 MCP 服务器
MCP 的三大核心组件
MCP 协议定义了三种主要的交互方式:
| 组件 | 作用 | 使用场景 |
|---|---|---|
| Tools(工具) | 让 AI 执行具体操作 | 文件读写、API 调用、数据库操作 |
| Resources(资源) | 提供上下文数据 | 文档内容、配置信息、实时数据 |
| Prompts(提示词) | 预设交互模板 | 常用任务模板、工作流引导 |
本文将重点介绍 Tools 和 Resources 的实际应用。
MCP 的工作流程
graph LR
A[用户] -->|发送请求| B[AI 客户端]
B -->|查询可用工具| C[MCP 服务器]
C -->|返回工具列表| B
B -->|发送请求到 LLM| D[大语言模型]
D -->|返回工具调用指令| B
B -->|调用工具| C
C -->|执行并返回结果| B
B -->|将结果发送到 LLM| D
D -->|生成最终回复| B
B -->|展示结果| A
💡 提示:MCP 采用客户端-服务器架构,客户端(AI 应用)通过标准协议与服务器(工具提供方)通信,大语言模型负责决策何时、如何使用这些工具。

说明:展示 MCP Host(AI 应用)通过多个 MCP Client 与不同的 MCP Server(如 Sentry、Filesystem、Database)建立一对一连接的架构
二、搭建 MCP 服务器
接下来,我们将构建一个文件系统操作的 MCP 服务器,让 AI 助手能够读写文件、管理目录。
2.1 环境准备
安装 UV 包管理器
UV 是一个现代化的 Python 包管理工具,速度快、依赖解析准确。
# Windows (PowerShell)
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
创建项目
# 初始化项目
uv init mcp-filesystem
cd mcp-filesystem
# 安装依赖
uv add mcp openai python-dotenv
配置 pyproject.toml
[project]
name = "mcp-client-server"
version = "0.1.0"
description = "MCP 文件系统服务器和客户端示例"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"mcp>=1.17.0", # MCP 核心库
"openai>=2.3.0", # OpenAI SDK(客户端使用)
"python-dotenv>=1.1.1", # 环境变量管理
]
💡 提示:MCP 1.17.0+ 提供了 FastMCP 框架,大大简化了服务器开发。
2.2 服务器代码详解
创建 mcp_server.py 文件,实现一个功能完整的文件系统 MCP 服务器:
from mcp.server.fastmcp import FastMCP
import os
# 创建 MCP 服务器实例,命名为 "filesystem"
mcp = FastMCP("filesystem")
# ==================== 工具定义 ====================
@mcp.tool()
async def read_file(path: str) -> str:
"""
读取文件内容
参数:
path: 文件路径(相对或绝对路径)
返回:
文件的文本内容
"""
with open(path, "r", encoding="utf-8") as file:
return file.read()
@mcp.tool()
async def write_file(path: str, content: str) -> str:
"""
写入文件内容(覆盖模式)
参数:
path: 文件路径
content: 要写入的内容
返回:
操作成功提示
"""
with open(path, "w", encoding="utf-8") as file:
file.write(content)
return "File written successfully"
@mcp.tool()
async def list_files(path: str) -> list[str]:
"""
列出目录中的所有文件和文件夹
参数:
path: 目录路径
返回:
文件和文件夹名称列表
"""
return os.listdir(path)
@mcp.tool()
async def create_file(path: str, content: str) -> str:
"""
创建新文件并写入内容
参数:
path: 新文件路径
content: 初始内容
返回:
操作成功提示
"""
with open(path, "w", encoding="utf-8") as file:
file.write(content)
return "File created successfully"
@mcp.tool()
async def delete_file(path: str) -> str:
"""
删除文件
参数:
path: 要删除的文件路径
返回:
操作成功提示
"""
os.remove(path)
return "File deleted successfully"
@mcp.tool()
async def rename_file(path: str, new_path: str) -> str:
"""
重命名或移动文件
参数:
path: 原文件路径
new_path: 新文件路径
返回:
操作成功提示
"""
os.rename(path, new_path)
return "File renamed successfully"
# ==================== 资源定义 ====================
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""
获取个性化问候语(Resource 示例)
参数:
name: 用户名称
返回:
问候语文本
"""
return f"Hello, {name}!"
# ==================== 启动服务器 ====================
if __name__ == "__main__":
print("MCP server is running...")
mcp.run(transport="stdio") # 使用标准输入输出通信
代码亮点解析
-
FastMCP 框架:
- 使用
@mcp.tool()装饰器即可将普通函数转换为 MCP 工具 - 自动解析函数签名生成工具描述
- 无需手动编写 JSON Schema
- 使用
-
工具设计原则:
- 单一职责:每个工具只做一件事
- 清晰参数:使用类型注解和文档字符串
- 统一返回:返回字符串或简单数据结构
-
Resource 示例:
greeting://{name}展示了参数化资源的用法- Resources 通常用于提供只读的上下文信息
💡 提示:在生产环境中,应添加路径验证、权限检查和错误处理,防止恶意文件访问。
2.3 使用 MCP Inspector 测试
MCP Inspector 是官方提供的调试工具,可以可视化测试服务器功能。
安装 Inspector
npm install -g @modelcontextprotocol/inspector
启动测试
mcp-inspector uv run mcp_server.py
执行后会自动打开浏览器,默认地址为 http://localhost:5173。

说明:显示连接状态、服务器名称(filesystem)、工具数量(6个)
Inspector 界面功能
Inspector 提供了直观的测试界面:
- Tools 标签页:
- 列出所有 6 个工具:
read_file、write_file、list_files、create_file、delete_file、rename_file - 点击工具名称可查看详细描述和参数定义
- 填写参数后点击"Call Tool"执行测试
- 列出所有 6 个工具:

说明:显示 6 个文件操作工具,每个工具的名称和简短描述
-
Resources 标签页:
- 显示
greeting://{name}资源 - 可以输入不同的 name 参数测试动态资源
- 显示
-
测试示例:
测试读取当前目录文件列表:
{
"path": "."
}
测试创建新文件:
{
"path": "test.txt",
"content": "Hello from MCP!"
}
说明:输入参数 {"path": "."},返回当前目录的文件列表
💡 提示:Inspector 会实时显示工具调用结果和错误信息,非常适合开发阶段快速迭代。
三、开发 MCP 客户端
有了 MCP 服务器,下一步是开发客户端,让 AI 助手能够调用这些工具。
3.1 项目配置
环境变量配置
创建 .env 文件,配置大语言模型的连接信息:
ASE_URL=https://api.deepseek.com
MODEL=deepseek-chat
API_KEY="sk-xxxxxxxxxx"
💡 提示:可以使用 OpenAI 官方 API,也可以使用兼容的第三方服务(如 Deepseek、本地部署的模型等)。
3.2 客户端代码架构
创建 llm_client.py,实现一个完整的 MCP 客户端:
import os
import asyncio
from openai import OpenAI
from dotenv import load_dotenv
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import json
# 加载环境变量
load_dotenv()
# 获取配置
base_url = os.getenv("BASE_URL")
model = os.getenv("MODEL")
api_key = os.getenv("API_KEY")
class MCPClient:
"""MCP 客户端:连接 MCP 服务器和大语言模型"""
def __init__(self):
"""初始化客户端"""
self.exit_stack = AsyncExitStack() # 用于管理异步资源
self.openai_api_key = api_key
self.base_url = base_url
self.model = model
# 验证环境变量
if not all([self.openai_api_key, self.base_url, self.model]):
raise ValueError("Missing required environment variables")
# 创建 OpenAI 客户端
self.client = OpenAI(api_key=self.openai_api_key, base_url=self.base_url)
async def __aenter__(self):
"""进入异步上下文管理器"""
return self
async def __aexit__(self, exc_type, exc_value, traceback):
"""退出上下文时清理所有资源"""
# 1. 关闭 OpenAI 客户端
try:
if hasattr(self, 'client') and self.client is not None:
self.client.close()
print("[OK] OpenAI 客户端已关闭")
except Exception as e:
print(f"[WARNING] 关闭 OpenAI 客户端时出错: {e}")
# 2. 清理 MCP 连接
try:
await self.exit_stack.aclose()
print("[OK] MCP 连接已关闭")
except Exception as e:
print(f"[WARNING] 关闭 MCP 连接时出错: {e}")
async def connect_to_server(self, server_url: str):
"""
连接到 MCP 服务器
参数:
server_url: 服务器脚本路径(.py 或 .js 文件)
"""
# 判断服务器类型
is_python = server_url.endswith(".py")
# 转换为绝对路径
if not os.path.isabs(server_url):
server_url = os.path.abspath(server_url)
# 配置启动命令
if is_python:
command = "uv"
args = ["run", server_url]
else:
command = "node"
args = [server_url]
# 创建服务器参数
server_params = StdioServerParameters(
command=command,
args=args,
env=None
)
# 启动服务器并建立通信
stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params)
)
# 创建客户端会话
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(
ClientSession(self.stdio, self.write)
)
# 初始化连接
await self.session.initialize()
async def list_tools(self):
"""列出服务器提供的所有工具"""
response = await self.session.list_tools()
tools = response.tools
print("已连接服务器,支持的工具如下:", [tool.name for tool in tools])
async def process_query(self, query: str) -> str:
"""
处理用户查询:调用 LLM 并执行工具
参数:
query: 用户的自然语言请求
返回:
AI 助手的回复
"""
# 构建对话消息
messages = [
{
"role": "system",
"content": """你是一个智能助手,可以帮助用户操作文件系统。
重要规则:
1. 当用户要求写入特定内容时,必须完全按照用户提供的原文逐字写入
2. 请仔细检查并保留用户输入的原文,确保每个字都准确无误
3. 特别注意中文内容,不要改变、替换或简化任何字词"""
},
{"role": "user", "content": query}
]
try:
# 1. 获取 MCP 服务器的工具列表
response = await self.session.list_tools()
available_tools = [{
'type': 'function',
'function': {
'name': tool.name, # 工具名称
'description': tool.description, # 工具描述
'parameters': tool.inputSchema # 参数定义(JSON Schema)
}
} for tool in response.tools]
# 2. 调用 LLM,传递工具信息
# 使用 run_in_executor 在线程池中运行同步的 OpenAI 调用
response = await asyncio.get_event_loop().run_in_executor(
None,
lambda: self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=available_tools # 告诉 LLM 可用的工具
)
)
content = response.choices[0]
# 3. 检查 LLM 是否要求调用工具
if content.finish_reason == "tool_calls":
# 提取工具调用信息
tool_call = content.message.tool_calls[0]
tool_name = tool_call.function.name
tool_args = json.loads(tool_call.function.arguments)
print(f"调用工具:{tool_name}, 参数:{tool_args}")
# 4. 执行 MCP 工具调用
result = await self.session.call_tool(tool_name, tool_args)
# 5. 将工具调用和结果添加到对话历史
messages.append(content.message.model_dump())
messages.append({
"role": "tool",
"content": result.content[0].text,
"tool_call_id": tool_call.id
})
# 6. 再次请求 LLM,让它基于工具结果生成最终回复
response = await asyncio.get_event_loop().run_in_executor(
None,
lambda: self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=available_tools
)
)
# 7. 返回 AI 的最终回复
return response.choices[0].message.content
except Exception as e:
print(f"Error processing query: {e}")
return f"Error: {e}"
async def chat_loop(self):
"""聊天循环:持续接收用户输入"""
print("\n欢迎使用智能助手,输入 'exit' 退出\n")
while True:
try:
query = input("You: ")
if query.lower() == "exit":
print("再见!")
break
response = await self.process_query(query)
print(f"Assistant: {response}\n")
except Exception as e:
print(f"Error: {e}\n")
async def main():
"""主函数:启动客户端"""
async with MCPClient() as client:
# 连接到 MCP 服务器
await client.connect_to_server("mcp_server.py")
# 列出可用工具
await client.list_tools()
# 进入聊天循环
await client.chat_loop()
if __name__ == "__main__":
asyncio.run(main())
3.3 关键功能实现详解
3.3.1 连接 MCP 服务器
connect_to_server 方法负责启动和连接 MCP 服务器:
# 核心步骤:
# 1. 配置服务器启动命令(uv run for Python, node for JS)
# 2. 创建 StdioServerParameters(标准输入输出通信参数)
# 3. 使用 stdio_client 启动服务器进程
# 4. 创建 ClientSession 建立会话
# 5. 调用 initialize() 完成握手
💡 提示:MCP 使用 stdio(标准输入输出) 作为通信方式,客户端和服务器通过进程间通信交换 JSON-RPC 消息。
3.3.2 工具调用流程
process_query 方法实现了完整的工具调用逻辑:
流程图:
用户输入 → 获取工具列表 → 调用 LLM(附带工具信息)
↓
LLM 决策:需要工具?
↓
是:返回 tool_calls
↓
客户端调用 MCP 工具
↓
将结果添加到对话历史
↓
再次调用 LLM(附带结果)
↓
LLM 生成最终自然语言回复
↓
返回给用户
关键代码片段:
# 检查 LLM 是否要求调用工具
if content.finish_reason == "tool_calls":
# 提取工具名称和参数
tool_call = content.message.tool_calls[0]
tool_name = tool_call.function.name
tool_args = json.loads(tool_call.function.arguments)
# 调用 MCP 工具
result = await self.session.call_tool(tool_name, tool_args)
# 构建工具结果消息
messages.append({
"role": "tool",
"content": result.content[0].text,
"tool_call_id": tool_call.id
})
3.3.3 run_in_executor 的使用
OpenAI SDK 的 chat.completions.create 是同步方法,在异步环境中直接调用会阻塞事件循环。使用 run_in_executor 可以在线程池中运行同步代码:
response = await asyncio.get_event_loop().run_in_executor(
None, # None 表示使用默认的 ThreadPoolExecutor
lambda: self.client.chat.completions.create(...)
)
💡 提示:也可以使用 AsyncOpenAI 客户端,直接支持异步调用,无需 run_in_executor。
3.3.4 资源清理机制
使用 AsyncExitStack 管理多个异步资源的生命周期:
# 进入上下文时资源会被注册
stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params)
)
# 退出上下文时自动清理所有资源
async def __aexit__(self, ...):
await self.exit_stack.aclose() # 按相反顺序关闭所有资源
3.4 运行效果展示
启动客户端:
python llm_client.py
输出示例:
已连接服务器,支持的工具如下: ['read_file', 'write_file', 'list_files', 'create_file', 'delete_file', 'rename_file']
欢迎使用智能助手,输入 'exit' 退出
You: 列出当前目录的文件
调用工具:list_files, 参数:{'path': '.'}
Assistant: 当前目录包含以下文件:
- mcp_server.py
- llm_client.py
- .env
- pyproject.toml
- uv.lock
You: 创建一个名为 hello.txt 的文件,内容是"Hello MCP!"
调用工具:create_file, 参数:{'path': 'hello.txt', 'content': 'Hello MCP!'}
Assistant: 已成功创建文件 hello.txt,内容为"Hello MCP!"
You: 读取 hello.txt 的内容
调用工具:read_file, 参数:{'path': 'hello.txt'}
Assistant: 文件 hello.txt 的内容是:Hello MCP!
说明:显示完整的交互过程,包含 3 个示例对话(列出文件、创建文件、读取文件)
四、总结与扩展
4.1 MCP 的实际应用场景
通过本文的实践,我们看到了 MCP 在文件系统操作中的强大能力。实际上,MCP 的应用远不止于此:
| 应用场景 | MCP 工具示例 | 典型用途 |
|---|---|---|
| 文件系统操作 | read_file, write_file, list_files | 代码生成、文档管理、日志分析 |
| 数据库访问 | query_database, update_record | 数据查询、报表生成、数据分析 |
| API 调用 | http_request, graphql_query | 第三方服务集成、数据获取 |
| 浏览器自动化 | navigate, click, screenshot | Web 测试、数据爬取、UI 交互 |
| 代码分析 | search_code, analyze_ast | 代码审查、重构建议、漏洞检测 |
| 系统操作 | run_command, manage_processes | DevOps 自动化、系统监控 |
4.2 常用的 MCP 服务器推荐
社区已经开发了许多高质量的 MCP 服务器,可以直接使用:
官方服务器
-
@modelcontextprotocol/server-filesystem
- 功能:文件系统完整操作(比本文示例更强大)
- 特点:支持权限控制、路径白名单、文件搜索
-
@modelcontextprotocol/server-postgres
- 功能:PostgreSQL 数据库访问
- 特点:安全的 SQL 查询、参数化查询防注入
-
@modelcontextprotocol/server-playwright
- 功能:浏览器自动化(基于 Playwright)
- 特点:页面导航、元素交互、截图、表单填写
第三方服务器
-
Desktop Commander
- 功能:系统级操作(进程管理、搜索、终端交互)
- 特点:跨平台、强大的文件搜索、REPL 支持
-
mcp-server-git
- 功能:Git 仓库操作
- 特点:提交、分支管理、历史查询
4.3 最佳实践建议
安全性考虑
# 1. 路径验证:防止目录遍历攻击
import os
def is_safe_path(base_dir, path):
"""检查路径是否在允许的目录内"""
abs_path = os.path.abspath(path)
abs_base = os.path.abspath(base_dir)
return abs_path.startswith(abs_base)
# 2. 权限控制:限制可访问的目录
ALLOWED_DIRS = ["/workspace", "/data"]
# 3. 参数验证:使用 Pydantic 模型
from pydantic import BaseModel, Field
class ReadFileArgs(BaseModel):
path: str = Field(..., description="文件路径", pattern=r"^[a-zA-Z0-9_./\-]+$")
错误处理
@mcp.tool()
async def read_file(path: str) -> str:
try:
if not os.path.exists(path):
return f"错误:文件 {path} 不存在"
with open(path, "r", encoding="utf-8") as file:
return file.read()
except PermissionError:
return f"错误:没有权限读取 {path}"
except Exception as e:
return f"读取文件时出错:{str(e)}"
性能优化
# 1. 对于大文件,使用流式读取
@mcp.tool()
async def read_large_file(path: str, max_size: int = 1024 * 1024) -> str:
"""读取文件(最多 1MB)"""
with open(path, "r", encoding="utf-8") as file:
return file.read(max_size)
# 2. 使用异步 I/O(aiofiles)
import aiofiles
@mcp.tool()
async def read_file_async(path: str) -> str:
async with aiofiles.open(path, "r", encoding="utf-8") as file:
return await file.read()
工具设计原则
- 单一职责:每个工具只做一件事,避免"万能工具"
- 清晰命名:使用动词开头,如
read_file而非file - 详细文档:提供清晰的描述和参数说明
- 类型安全:使用 Python 类型注解,自动生成 JSON Schema
- 幂等性:相同输入应产生相同输出(对于查询类工具)
4.4 学习资源
- 官方文档:https://modelcontextprotocol.io
- GitHub 仓库:https://github.com/modelcontextprotocol
- 示例代码:https://github.com/modelcontextprotocol/servers
- Discord 社区:加入 MCP 开发者社区交流
总结
通过本文,我们完成了:
✅ 理解了 MCP 的核心概念和工作原理
✅ 使用 FastMCP 构建了一个功能完整的文件系统服务器
✅ 使用 MCP Inspector 进行了可视化测试
✅ 开发了一个与 OpenAI 集成的智能客户端
✅ 掌握了工具调用的完整流程
MCP 正在快速成长为 AI 应用开发的标准协议。无论你是要构建个人 AI 助手,还是企业级 AI 应用,MCP 都能帮你快速整合各种工具和数据源,让 AI 真正"动起来"。
现在,你可以基于本文的代码,扩展更多工具:数据库操作、API 调用、自动化测试……可能性是无限的!
本文代码仓库:完整代码可在 GitHub 查看(https://github.com/zhangzs/MCP_Client_Server)