本文假设你已经看过上一篇 Claude Code 工具架构核心解析 ,理解了"权限边界"和"大道至简"的设计哲学。现在我们要把这些最佳实践落地到实际项目中------设计一个高内聚、低耦合的 MCP-Filesystem Server。
前言
基于 Claude Code 的设计理念,本文设计一个 6 个核心工具的 MCP-Filesystem Server,适用于 FastGPT、Dify、Coze 等 AI Agent 平台。
期望实现类似 Google AI Studio、天工(Skywork)、Minimax Agent等工作空间能力。
一、设计目标
基于 Claude Code 的启示,我们的设计目标是:
✅ 高内聚:6 个工具,职责清晰
✅ 权限清晰:每个工具是一个权限边界
✅ 语义明确:工具名称直接反映能力
✅ 安全可控:支持权限过滤和审计
✅ 自动格式识别:支持 txt、md、xlsx、json、csv 等
功能覆盖
- 📁 文件系统:读取、写入、编辑、搜索
- ⚙️ 代码执行:Python 代码执行
- 🌐 前端部署:自动构建并部署到预览服务器
二、工具设计方案
基于 Claude Code 的设计理念,我们设计了 6 个核心工具:
| 工具名称 | 对应 CC 工具 | 核心职责 | 权限级别 | 一句话总结 |
|---|---|---|---|---|
fs_read |
Read | 读取文件 | 只读(✅ 安全) | 只读,零副作用 |
fs_write |
Write | 创建/覆盖文件 | 写入(⚠️ 中等) | 语义明确:我就是要覆盖! |
fs_edit |
Edit | 精确编辑文件 | 修改(⚠️ 中等) | 局部修改,不动其他内容 |
fs_search |
Grep + Glob | 搜索文件/内容 | 只读(✅ 安全) | 合并搜索,减少工具数 |
exec |
- | 执行 Python 代码 | 执行(⚠️ 中等) | 仅支持 Python,安全可控 |
preview_frontend |
- | 前端项目部署 | 部署(⚠️ 中等) | 自动构建并部署到预览服务器 |
优势:
- ✅ 权限边界清晰:每个工具职责单一
- ✅ 工具数量合理:6 个刚好,不多不少
- ✅ AI 容易选择:语义明确,不会选错
- ✅ 易于管理和使用:配置简洁
- ✅ 功能完整:覆盖文件操作、代码执行、前端部署
这就是我们向 Claude Code 学来的高内聚设计。
三、详细设计:6 个工具的完整实现
3.1 工具 1:fs_read(文件读取)
核心原则:只读,零副作用,自动识别格式
工具定义
typescript
{
name: 'fs_read',
description: '读取文件内容,自动识别格式(txt/xlsx/json/csv/pdf/图片等)',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: '文件路径(相对于工作目录)',
required: true
},
format: {
type: 'string',
enum: ['auto', 'text', 'xlsx', 'json', 'csv', 'binary'],
description: '读取格式,默认为 auto(自动识别)',
default: 'auto'
},
sheet: {
type: 'string',
description: 'xlsx 文件的 sheet 名称(可选)'
},
encoding: {
type: 'string',
enum: ['utf-8', 'gbk', 'utf-16'],
description: '文本编码,默认 utf-8',
default: 'utf-8'
}
}
}
}
实现要点
- 路径安全检查(防止路径穿越)
- 根据文件扩展名自动识别格式
- 支持 text、json、csv、xlsx 等格式
返回格式示例
json
// 文本文件
{
"content": "Hello World!",
"format": "text"
}
// JSON 文件
{
"content": { "name": "Alice", "age": 30 },
"format": "json"
}
// XLSX 文件
{
"content": {
"sheets": ["Sheet1", "Sheet2"],
"data": [
["姓名", "年龄", "城市"],
["张三", 30, "北京"],
["李四", 25, "上海"]
],
"currentSheet": "Sheet1"
},
"format": "xlsx"
}
3.2 工具 2:fs_write(文件创建/覆盖)
核心原则:创建新文件或完全覆盖现有文件,语义明确
工具定义
typescript
{
name: 'fs_write',
description: '创建新文件或完全覆盖现有文件(支持 txt/xlsx/json/csv 等格式)',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: '文件路径(相对于工作目录)',
required: true
},
content: {
description: '文件内容(根据格式可以是字符串、对象或数组)',
required: true
},
format: {
type: 'string',
enum: ['auto', 'text', 'xlsx', 'json', 'csv'],
description: '写入格式,默认为 auto(根据扩展名自动识别)',
default: 'auto'
},
encoding: {
type: 'string',
enum: ['utf-8', 'gbk', 'utf-16'],
description: '文本编码,默认 utf-8',
default: 'utf-8'
}
}
}
}
实现要点
- 自动创建目录
- 根据扩展名自动识别格式并写入
- 支持 text、json、csv、xlsx 等格式
3.3 工具 3:fs_edit(精确编辑)
核心原则:精确修改文件的特定部分,避免全文覆盖
支持模式:
replace:字符串替换line_range:按行编辑xlsx_cell:xlsx 单元格编辑
工具定义
typescript
{
name: 'fs_edit',
description: '精确编辑文件的特定部分(支持按行编辑、字符串替换、xlsx 单元格编辑)',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: '文件路径(相对于工作目录)',
required: true
},
editType: {
type: 'string',
enum: ['replace', 'line_range', 'xlsx_cell'],
description: '编辑类型',
required: true
},
// replace 模式参数
oldContent: {
type: 'string',
description: '要替换的内容(editType=replace 时必需)'
},
newContent: {
type: 'string',
description: '新内容(editType=replace 时必需)'
},
replaceAll: {
type: 'boolean',
description: '是否替换所有匹配(默认 false,只替换第一个)',
default: false
},
// line_range 模式参数
startLine: {
type: 'number',
description: '起始行号(从 1 开始,editType=line_range 时必需)'
},
endLine: {
type: 'number',
description: '结束行号(editType=line_range 时必需)'
},
lineContent: {
type: 'string',
description: '新的行内容(editType=line_range 时必需)'
},
// xlsx_cell 模式参数
sheet: {
type: 'string',
description: 'sheet 名称(editType=xlsx_cell 时可选)'
},
cell: {
type: 'string',
description: '单元格地址(如 "A1",editType=xlsx_cell 时必需)'
},
value: {
description: '单元格的新值(editType=xlsx_cell 时必需)'
}
}
}
}
实现示例(Python - 核心部分)
注:以下为 Python 实现示例,MCP Server 可以使用任何语言实现
python
async function fs_edit(params: {
path: string;
editType: 'replace' | 'line_range' | 'xlsx_cell';
// ...其他参数
}): Promise<{ success: boolean; message: string }> {
const { path: filePath, editType } = params;
// 【安全检查】
const safePath = path.resolve(process.env.WORKSPACE_DIR || '.', filePath);
if (!safePath.startsWith(process.env.WORKSPACE_DIR || '.')) {
throw new Error('Access denied: path traversal detected');
}
if (!fs.existsSync(safePath)) {
throw new Error(`File not found: ${filePath}`);
}
switch (editType) {
case 'replace': {
// 【字符串替换模式】学 Claude Code 的 Edit 工具
const { oldContent, newContent, replaceAll = false } = params;
if (!oldContent || newContent === undefined) {
throw new Error('oldContent and newContent are required for replace mode');
}
const content = fs.readFileSync(safePath, 'utf-8');
// 检查 oldContent 是否唯一(防止误操作)
const matches = content.split(oldContent).length - 1;
if (!replaceAll && matches > 1) {
throw new Error(
`Found ${matches} matches for oldContent. Use replaceAll=true or provide more context.`
);
}
const newFileContent = replaceAll
? content.split(oldContent).join(newContent)
: content.replace(oldContent, newContent);
fs.writeFileSync(safePath, newFileContent, 'utf-8');
return { success: true, message: `Replaced ${replaceAll ? 'all' : '1'} occurrence(s)` };
}
case 'line_range': {
// 【按行编辑模式】适合代码修改
const { startLine, endLine, lineContent } = params;
if (!startLine || !endLine || lineContent === undefined) {
throw new Error('startLine, endLine, and lineContent are required for line_range mode');
}
const content = fs.readFileSync(safePath, 'utf-8');
const lines = content.split('\n');
// 检查行号范围
if (startLine < 1 || startLine > lines.length || endLine < 1 || endLine > lines.length) {
throw new Error(`Invalid line range: ${startLine}-${endLine} (file has ${lines.length} lines)`);
}
// 替换行
lines.splice(startLine - 1, endLine - startLine + 1, lineContent);
fs.writeFileSync(safePath, lines.join('\n'), 'utf-8');
return { success: true, message: `Updated lines ${startLine}-${endLine}` };
}
case 'xlsx_cell': {
// 【xlsx 单元格编辑模式】这是我们的特色能力
const { sheet, cell, value } = params;
if (!cell || value === undefined) {
throw new Error('cell and value are required for xlsx_cell mode');
}
const workbook = xlsx.readFile(safePath);
const sheetName = sheet || workbook.SheetNames[0];
if (!workbook.Sheets[sheetName]) {
throw new Error(`Sheet not found: ${sheetName}`);
}
const worksheet = workbook.Sheets[sheetName];
worksheet[cell] = { v: value, t: typeof value === 'number' ? 'n' : 's' };
xlsx.writeFile(workbook, safePath);
return { success: true, message: `Updated cell ${cell} in sheet ${sheetName}` };
}
default:
throw new Error(`Unsupported editType: ${editType}`);
}
}
3.4 工具 4:fs_search(文件/内容搜索)
核心原则:合并文件名搜索和内容搜索,减少工具数量
搜索类型:
filename:使用 glob 模式搜索文件名content:使用正则表达式搜索文件内容
工具定义
typescript
{
name: 'fs_search',
description: '搜索文件名或文件内容(支持 glob 模式和正则表达式)',
inputSchema: {
type: 'object',
properties: {
searchType: {
type: 'string',
enum: ['filename', 'content'],
description: '搜索类型:文件名或内容',
required: true
},
pattern: {
type: 'string',
description: '搜索模式(filename: glob 模式如 "*.js"; content: 正则表达式)',
required: true
},
path: {
type: 'string',
description: '搜索路径(相对于工作目录),默认为整个工作区',
default: '.'
},
caseSensitive: {
type: 'boolean',
description: '是否区分大小写(仅 content 搜索有效)',
default: false
},
maxResults: {
type: 'number',
description: '最大结果数量,默认 100',
default: 100
}
}
}
}
3.5 工具 5:exec(Python 代码执行)
核心原则:仅支持 Python,安全可控
使用方式:
exec(code="print(1 + 1)")- 执行代码字符串exec(file="/greet.py", args=["Alice"])- 执行 Python 文件
工具定义
python
{
name: 'exec',
description: '执行 Python 代码或文件',
inputSchema: {
type: 'object',
properties: {
code: {
type: 'string',
description: '要执行的 Python 代码字符串(与 file 二选一)'
},
file: {
type: 'string',
description: '要执行的 Python 文件路径(相对于工作目录,与 code 二选一)'
},
args: {
type: 'array',
items: { type: 'string' },
description: '传递给 Python 文件的命令行参数(仅 file 模式有效)',
default: []
},
cwd: {
type: 'string',
description: '工作目录(相对于工作区)',
default: '.'
},
timeout: {
type: 'number',
description: '超时时间(毫秒),默认 120000(2分钟)',
default: 120000
},
env: {
type: 'object',
description: '环境变量(可选)'
}
},
required: ['code'] # code 和 file 至少需要一个
}
}
使用示例
python
exec(code="print(1 + 1)")
exec(file="/greet.py", args=["Alice"])
3.6 工具 6:preview_frontend(前端项目部署)
核心原则:自动构建并部署前端项目到预览服务器
使用示例:
preview_frontend("index.html")- 部署简单 HTMLpreview_frontend("dist/index.html")- 部署构建后的项目preview_frontend("index.html", build_command="npm run build", output_dir="dist")- 自动构建并部署
工具定义
python
{
name: 'preview_frontend',
description: '部署前端项目到预览服务器',
inputSchema: {
type: 'object',
properties: {
entry_file: {
type: 'string',
description: '入口 HTML 文件路径(相对于工作目录)',
default: 'index.html'
},
build_command: {
type: 'string',
description: '构建命令(如 "npm run build"),如果项目已构建可省略',
default: None
},
output_dir: {
type: 'string',
description: '构建输出目录(如 "dist"),如果已构建可省略',
default: None
}
},
required: ['entry_file']
}
}
使用示例
python
preview_frontend(entry_file="index.html")
preview_frontend(entry_file="dist/index.html")
preview_frontend(entry_file="index.html", build_command="npm run build", output_dir="dist")
四、MCP Server 完整实现
由于篇幅原因,完整的 MCP Server 代码(包含目录结构、package.json、入口文件)请移步 GitHub 查阅
GitHub 仓库 (待发布):mcp-filesystem
五、与 Dify / FastGPT / Coze 集成简便
直接在可视化界面引入外部sse格式的mcp-sever即可。
六、最佳实践
工具设计原则
- 每个工具是一个权限边界:Read 只读,Write 只写,Edit 只编辑
- 语义清晰:工具名称直接反映能力,AI 容易理解
- 自动格式识别:减少 AI 选择困难
- 安全第一:路径穿越检查、权限验证、超时控制