Python MCP实战:从零搭建本地文件系统服务器,让Cursor/Claude自由读取本地项目文件(附完整安全代码)
本文仅用于本地开发学习,MCP服务拥有本地文件读写权限,存在敏感文件泄露、路径穿越风险,请严格遵守以下安全规范:
- 仅授权AI访问非隐私、无密钥、无配置文件的项目目录,禁止直接扫描系统根目录、用户桌面、数据库密钥文件夹;
- 代码内置目录白名单校验,生产环境必须开启路径限制,防止
../路径穿越攻击; - stdio模式仅本地客户端使用,streamable-http HTTP模式禁止公网暴露,仅本地localhost访问;
- 本教程技术仅供个人开发调试,请勿用于第三方远程文件读取、批量扫描他人设备。
一、前言
日常使用Cursor、Claude Desktop、VS Code MCP客户端时,AI只能处理对话内粘贴的代码片段,无法直接读取本地项目目录、批量检索源码、统计文件结构、预览日志文件。
核心原因:AI客户端默认无本地文件系统操作权限,而 MCP(Model Context Protocol 模型上下文协议) 是Anthropic推出的标准化AI本地工具通信协议,相当于AI的通用USB接口,一次开发MCP服务,所有支持MCP的AI客户端均可无缝调用本地文件能力。
本文基于Python FastMCP SDK,从零实现文件搜索、目录统计、文件预览三大工具,完整兼容Cursor、Claude Desktop,附带安全权限校验、客户端全平台配置教程。
适用场景
- 本地项目批量检索py/json配置文件
- 代码目录结构统计、文件大小汇总
- 日志、源码快速预览,无需手动打开文件
- AI自动分析本地项目工程结构
二、MCP协议通俗原理
把MCP理解为AI客户端的标准化外设接口:
| 类比事物 | MCP对应概念 |
|---|---|
| 电脑主机 | Claude/Cursor/VS Code AI客户端 |
| U盘/外设 | 自定义Python本地工具服务 |
| USB通信标准 | MCP(JSON-RPC/Streamable HTTP/stdio) |
| 即插即用配置 | 客户端mcpServers配置项 |
传统方案:不同AI工具需要单独开发插件、适配各自私有API;
MCP标准化后:一套MCP服务,全平台AI客户端通用,大幅降低本地工具开发成本。
三、环境部署
1. Python版本要求
Python ≥ 3.10
# 查看版本
python3 --version
2. 安装依赖库
# MCP官方SDK + HTTP服务框架
pip install mcp fastapi uvicorn
依赖说明:
mcp:FastMCP官方Python开发库,快速注册AI可调用工具fastapi+uvicorn:提供Streamable HTTP服务传输通道
3. 项目初始化
# 创建项目文件夹
mkdir -p ~/my-file-mcp && cd ~/my-file-mcp
四、完整MCP服务代码(带安全白名单+详细注释)
新建 file_scanner.py,增加目录白名单防护,规避路径穿越漏洞:
"""
本地文件扫描MCP Server(安全加固版)
功能:文件检索、目录统计、文件内容预览
安全限制:仅允许访问预设白名单目录,防止路径穿越攻击
"""
import os
from pathlib import Path
from mcp.server.fastmcp import FastMCP
# ====================== 安全配置:允许访问的目录白名单(自行修改) ======================
ALLOW_DIRS = [
Path("~/projects").expanduser(),
Path("~/code").expanduser()
]
# 转换为绝对路径,用于校验
ALLOW_ABS = [p.resolve() for p in ALLOW_DIRS]
# 初始化MCP服务
mcp = FastMCP(
"File Scanner",
description="本地文件系统安全扫描工具,仅读取白名单内目录,支持文件检索、目录统计、源码预览",
port=8765
)
def is_path_safe(target_path: str) -> bool:
"""路径安全校验:仅允许访问白名单目录,拦截../路径穿越"""
target = Path(target_path).resolve()
# 判断目标路径是否在任一允许目录下
for safe_dir in ALLOW_ABS:
if str(target).startswith(str(safe_dir)):
return True
return False
@mcp.tool()
def search_files(directory: str, pattern: str = "*.py", max_depth: int = 3) -> str:
"""
递归搜索指定目录下匹配后缀的文件
Args:
directory: 目标检索目录(必须在白名单内)
pattern: 文件匹配规则,如 *.py、*.json
max_depth: 最大递归深度,避免遍历过多文件
Returns:
匹配到的文件路径列表
"""
# 安全校验
if not is_path_safe(directory):
return f"权限拒绝:{directory} 不在允许访问目录列表中,请修改ALLOW_DIRS配置"
base = Path(directory)
if not base.exists():
return f"错误:目录不存在 —— {directory}"
results = []
try:
for depth in range(max_depth + 1):
glob_rule = "**/*" + pattern if depth > 0 else pattern
for file_path in base.glob(glob_rule):
if file_path.is_file() and len(results) < 50:
results.append(str(file_path))
except Exception as e:
return f"文件搜索异常:{str(e)}"
if not results:
return f"在 {directory} 未匹配到 {pattern} 文件(最大深度{max_depth})"
return f"共找到 {len(results)} 个文件:\n" + "\n".join(results)
@mcp.tool()
def dir_stats(path: str) -> str:
"""
统计目录:文件总数、总大小、文件类型分布
Args:
path: 目标统计目录
Returns:
结构化目录统计报告
"""
if not is_path_safe(path):
return f"权限拒绝:{path} 不在允许访问目录列表中"
p = Path(path)
if not p.exists():
return f"错误:路径不存在 —— {path}"
total_files = 0
total_size = 0
ext_count = {}
for item in p.rglob("*"):
if item.is_file():
total_files += 1
size = item.stat().st_size
total_size += size
suffix = item.suffix if item.suffix else "(无后缀)"
ext_count[suffix] = ext_count.get(suffix, 0) + 1
# 按文件数量降序排序
sorted_ext = sorted(ext_count.items(), key=lambda x: -x[1])
lines = [
f"📁 统计目录:{path}",
f"📄 文件总数量:{total_files}",
f"💾 目录总大小:{total_size / 1024:.1f} KB",
"",
"前10类文件分布:"
]
for ext, num in sorted_ext[:10]:
lines.append(f" {ext}:{num} 个")
return "\n".join(lines)
@mcp.tool()
def preview_file(path: str, max_lines: int = 30) -> str:
"""
预览文件前N行内容,避免一次性读取超大文件占满上下文
Args:
path: 文件完整路径
max_lines: 最多预览行数,默认30行
Returns:
文件元信息 + 头部内容预览
"""
if not is_path_safe(path):
return f"权限拒绝:{path} 不在允许访问目录列表中"
p = Path(path)
if not p.exists() or not p.is_file():
return f"错误:文件不存在或非文件 —— {path}"
try:
with open(p, "r", encoding="utf-8", errors="replace") as f:
all_lines = f.readlines()
preview = all_lines[:max_lines]
except Exception as e:
return f"文件读取失败:{str(e)}"
file_size = p.stat().st_size
total_line_num = len(all_lines)
header = (
f"📄 文件路径:{path}\n"
f"文件大小:{file_size/1024:.1f} KB | 总行数:{total_line_num}\n"
f"预览前 {min(max_lines, total_line_num)} 行内容:\n\n"
)
return header + "".join(preview)
if __name__ == "__main__":
# Streamable HTTP 本地服务(仅localhost访问)
mcp.run(transport="streamable-http")
启动服务命令
python file_scanner.py
正常输出示例:
INFO: Uvicorn running on http://0.0.0.0:8765
INFO: Application startup complete.
五、AI客户端接入配置(区分Mac/Windows)
5.1 Claude Desktop 配置
- Mac配置文件路径:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows配置文件路径:
%APPDATA%\Claude\claude_desktop_config.json
配置JSON(stdio传输模式,推荐本地使用):
{
"mcpServers": {
"file-scanner": {
"command": "python",
"args": ["~/my-file-mcp/file_scanner.py"],
"transport": "stdio"
}
}
}
修改完成后重启Claude Desktop,自动加载MCP服务。
5.2 Cursor 编辑器配置
- 打开Cursor → Settings → MCP → Add new MCP Server
- 配置参数:
| 配置项 | 填写内容 |
| Name | file-scanner |
| Command | python |
| Arguments | ~/my-file-mcp/file_scanner.py |
| Transport | stdio |
保存后刷新MCP列表,显示服务在线即连接成功。
六、实操调用示例
客户端连接成功后,直接对话发送指令即可自动调用文件工具:
- 检索项目所有Python文件
调用file-scanner,扫描~/projects目录,找出所有*.py文件,最大深度3
- 统计目录文件结构
统计~/projects目录总文件数量、总大小,展示各类文件占比
- 预览源码文件
读取~/projects/utils.py前20行代码并展示文件信息
七、进阶扩展工具模板
基于@mcp.tool()装饰器可快速扩展更多本地自动化能力,推荐开发:
| 工具函数 | 实现功能 |
|---|---|
| git_log | 读取Git仓库最近提交记录 |
| log_tail | 实时读取日志末尾内容 |
| dir_diff | 对比两个目录文件差异 |
| docker_list | 列出本地运行容器 |
| cron_check | 读取系统定时任务配置 |
新增工具仅需定义函数、增加参数注解,MCP会自动生成工具描述供AI识别。
八、安全加固&生产优化方案
- 严格目录白名单:代码内置
ALLOW_DIRS拦截路径穿越,禁止开放系统根目录; - 限制文件读取行数:
preview_file限制最大预览行数,防止超大文件填满LLM上下文; - HTTP模式禁止公网暴露:streamable-http仅本地localhost访问,不开放0.0.0.0给局域网;
- 增加文件大小校验:可新增代码判断文件体积,拒绝读取超过1MB大文件;
- 日志记录所有文件操作:添加本地日志,审计AI全部文件访问行为。
九、常见踩坑FAQ
Q1:客户端识别不到MCP服务?
A:检查Python路径是否正确、配置文件路径区分Mac/Windows,修改配置后重启客户端;确认mcp库版本≥1.0。
Q2:调用文件工具提示权限拒绝?
A:将目标目录添加到代码顶部ALLOW_DIRS白名单,重启MCP服务生效。
Q3:启动服务报端口占用?
A:修改FastMCP初始化时port=xxxx更换端口,同步修改客户端配置。
Q4:读取中文文件乱码?
A:代码已使用encoding="utf-8", errors="replace",特殊编码文件可自行调整编码参数。
十、全文总结
- MCP协议统一AI本地工具标准,一份服务兼容Cursor、Claude、VS Code多客户端;
- 本文实现安全版文件扫描MCP服务,内置路径白名单规避文件读取安全漏洞;
- 通过
@mcp.tool()装饰器极简开发自定义本地工具,轻松扩展Git、容器、日志自动化能力; - 核心价值:打破AI只能操作对话文本的限制,让AI直接接管本地项目分析、批量文件处理工作。
本教程基于Python MCP SDK v1.x编写,代码可直接本地运行,仅用于个人开发学习。
更多推荐





所有评论(0)