1. filesystem MCP server 端部署:为什么值得折腾
如果你正在用 Claude Desktop、Cline、Cursor 这类支持 MCP 的 AI 工具,大概率遇到过同一个尴尬:AI 能聊天、能写代码,却看不到你本地某个目录里到底有什么文件。想让它读一份需求文档、扫一遍项目结构、批量改几个配置文件,只能手动复制粘贴,效率低还容易漏。
filesystem MCP server 就是解决这个问题的。它本质是一个跑在本地的 Node.js 进程,通过 MCP 协议把指定目录的读写能力暴露给 AI 工具。AI 工具调用它提供的list_directory、read_file、write_file等工具,就能在授权范围内操作文件。适合谁?适合想让 AI 安全读写本地目录的开发者,尤其是需要把项目文档、配置、脚本交给 AI 处理,但又不想把整个磁盘都开放出去的人。
我这次的做法是把 filesystem MCP server 部署成 HTTP 服务,而不是常见的 stdio 模式。原因很直接:stdio 模式只能被同一台机器上的客户端拉起,多台设备、多个工具想共用就得反复配置;HTTP 模式部署一次,局域网内谁都能连,配合 systemd 还能开机自启、崩溃自动重启。代价是要多写一层 HTTP 转发,但换来的是可管理性。
整条链路里还有一个容易被忽略的点:调用凭据管理。AI 工具链里往往不止一个模型服务,Key 散落在各个配置文件里,换一次就得全局搜。我习惯用 TaoToken 统一管理 Key 和 API 通道,后面会给出具体配置片段。下面从零开始,把 Node.js 环境、项目结构、HTTP 转发层、systemd 服务、客户端配置和验证动作全部走一遍。
2. Node.js 环境与项目骨架搭建
2.1 安装 Node.js 18
filesystem MCP server 依赖较新的 Node 运行时,建议 18 及以上。以 Rocky Linux 为例,用 NodeSource 仓库最省事:
curl -fsSL https://rpm.nodesource.com/setup_18.x | bash - dnf install -y nodejs node --version npm --version npx --version三条版本命令都能正常输出就说明装好了。如果仓库方式因为网络原因失败,可以退回到官方二进制包:下载node-v18.18.0-linux-x64.tar.xz,解压到/opt,再把node、npm、npx软链到/usr/local/bin。这一步不复杂,但要注意软链路径别写错,否则后面spawn('node', ...)会找不到可执行文件。
2.2 创建项目目录与依赖
我习惯把 MCP 相关服务统一放在/data/mcp下,目录结构清晰,后面 systemd 的ReadWritePaths也好写:
mkdir -p /data/mcp/mcp-http-filesystem/{logs,data,shared} cd /data/mcp/mcp-http-filesystem npm init -y npm install express npm install @modelcontextprotocol/server-filesystem npm install --save-dev nodemonexpress用来做 HTTP 转发层,@modelcontextprotocol/server-filesystem是官方文件系统 MCP 服务器,nodemon只在开发时热重载用,生产环境可以不装。
接着创建允许 AI 访问的目录和测试文件:
mkdir -p /data/mcp/mcp-http-filesystem/shared/documents mkdir -p /data/mcp/mcp-http-filesystem/shared/projects mkdir -p /data/mcp/mcp-http-filesystem/data echo "Hello from MCP Server" > /data/mcp/mcp-http-filesystem/shared/welcome.txt echo "Test document" > /data/mcp/mcp-http-filesystem/shared/documents/test.txt这里有个安全设计要强调:server-filesystem启动时接收的目录参数就是它的“允许访问白名单”,只有传进去的目录才能被读写。所以千万别图省事传/或/home,按需传shared和data就够了。
2.3 package.json 关键字段
npm init -y生成的package.json需要补一个start脚本,方便 systemd 和手动启动统一入口:
{ "name": "mcp-http-filesystem", "version": "1.0.0", "description": "HTTP wrapper for filesystem MCP server", "main": "http-mcp-server.js", "scripts": { "start": "node http-mcp-server.js", "dev": "nodemon http-mcp-server.js" }, "dependencies": { "@modelcontextprotocol/server-filesystem": "^0.6.2", "express": "^4.19.2" }, "devDependencies": { "nodemon": "^3.1.0" } }版本号以你实际安装的为准,npm install后package-lock.json会锁定精确版本。生产环境建议用npm ci而不是npm install,保证依赖树一致。
3. HTTP 转发层与可复制配置
3.1 为什么要加一层 HTTP
server-filesystem原生是 stdio 模式:客户端通过标准输入输出和它通信。stdio 模式的问题在于,一个进程只能服务一个客户端,而且客户端必须能自己拉起这个进程。我想让局域网里多台机器、多个 AI 工具共用同一个文件系统服务,就必须把它包成 HTTP 服务。
转发层的逻辑不复杂:Express 起一个 HTTP 服务,收到/mcp的 POST 请求后,把 JSON-RPC 请求写进子进程的 stdin,再从子进程的 stdout 读回响应,返回给 HTTP 调用方。中间要处理请求 ID 映射、超时、进程崩溃重启。
3.2 核心转发代码
主文件http-mcp-server.js的关键部分如下。先看配置加载和进程启动:
const express = require('express'); const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const app = express(); const config = { PORT: process.env.PORT || 8008, HOST: process.env.HOST || '0.0.0.0', SHARED_DIR: process.env.SHARED_DIR || '/data/mcp/mcp-http-filesystem/shared', DATA_DIR: process.env.DATA_DIR || '/data/mcp/mcp-http-filesystem/data', MAX_RESTARTS: parseInt(process.env.MAX_RESTARTS) || 5, REQUEST_TIMEOUT: parseInt(process.env.REQUEST_TIMEOUT) || 30000 }; app.use(express.json({ limit: '50mb' }));启动子进程时,把允许目录作为参数传进去:
const mcpServerPath = path.join( __dirname, 'node_modules', '@modelcontextprotocol', 'server-filesystem', 'dist', 'index.js' ); this.mcpProcess = spawn('node', [ mcpServerPath, config.SHARED_DIR, config.DATA_DIR ], { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, NODE_NO_WARNINGS: '1' } });请求转发用 Promise 包一层,配合pendingRequestsMap 做 ID 映射:
async sendRequest(request) { return new Promise((resolve, reject) => { const requestId = this.requestId++; const requestWithId = { ...request, jsonrpc: '2.0', id: requestId }; const timeout = setTimeout(() => { this.pendingRequests.delete(requestId); reject(new Error(`Request ${requestId} timeout`)); }, config.REQUEST_TIMEOUT); this.pendingRequests.set(requestId, { resolve, reject, timeout }); this.mcpProcess.stdin.write(JSON.stringify(requestWithId) + '\n'); }); }HTTP 端点暴露/mcp、/health、/info、/config、/logs五个路由。/mcp接收 JSON-RPC 请求,/health返回进程状态,/info返回版本和端点列表,/config返回脱敏配置,/logs返回最近日志行。
3.3 环境变量与 settings 片段
生产环境用.env文件管理配置,避免硬编码。在项目根目录创建.env:
PORT=8008 HOST=0.0.0.0 NODE_ENV=production LOG_LEVEL=info SHARED_DIR=/data/mcp/mcp-http-filesystem/shared DATA_DIR=/data/mcp/mcp-http-filesystem/data MAX_RESTARTS=5 REQUEST_TIMEOUT=30000如果你的 AI 工具链里还要接模型服务,Key 管理建议统一走 TaoToken。在客户端配置里,模型服务的 Base URL 填https://taotoken.net/api,Key 用 TaoToken 控制台生成的统一 Key。这样换模型、加通道都不用改多个配置文件。控制台地址是https://taotoken.net/console,API Keys 管理在https://taotoken.net/api-keys。
3.4 systemd 服务配置
手动node http-mcp-server.js只能临时跑,重启就没了。用 systemd 托管:
[Unit] Description=HTTP MCP Filesystem Server After=network.target [Service] Type=simple User=youruser Group=youruser WorkingDirectory=/data/mcp/mcp-http-filesystem Environment=NODE_ENV=production Environment=PORT=8008 Environment=HOST=0.0.0.0 ExecStart=/usr/bin/node /data/mcp/mcp-http-filesystem/http-mcp-server.js Restart=always RestartSec=10 StandardOutput=journal StandardError=journal NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/data/mcp/mcp-http-filesystem [Install] WantedBy=multi-user.targetUser和Group换成你自己的运行账号,ReadWritePaths必须包含项目目录,否则ProtectSystem=strict会让进程无法写日志。启用并启动:
sudo systemctl daemon-reload sudo systemctl enable mcp-http.service sudo systemctl start mcp-http.service sudo systemctl status mcp-http.service4. 验证请求与成功结果
4.1 健康检查与工具列表
服务起来后,先打健康检查:
curl http://localhost:8008/health正常返回类似:
{ "status": "ok", "mcpProcess": "running", "mcpInitialized": true, "pendingRequests": 0, "restartCount": 0 }mcpInitialized为true说明子进程完成了 MCP 握手。如果一直是false,多半是子进程启动失败,去看/logs端点或journalctl。
接着列出可用工具:
curl -X POST http://localhost:8008/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }'返回里会包含read_file、write_file、list_directory、list_allowed_directories、search_files等工具定义。
4.2 目录列举与文件读取
先看允许访问的目录:
curl -X POST http://localhost:8008/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "callTool", "params": { "name": "list_allowed_directories", "arguments": {} } }'再列shared目录内容:
curl -X POST http://localhost:8008/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "callTool", "params": { "name": "list_directory", "arguments": { "path": "/data/mcp/mcp-http-filesystem/shared" } } }'应该能看到welcome.txt、documents、projects。最后读一个文件:
curl -X POST http://localhost:8008/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 4, "method": "callTool", "params": { "name": "read_file", "arguments": { "path": "/data/mcp/mcp-http-filesystem/shared/welcome.txt" } } }'返回内容里包含Hello from MCP Server就说明整条链路通了。
4.3 MCP 客户端配置
在 Claude Desktop 或 Cline 的 MCP 配置里,用 HTTP 客户端桥接:
{ "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "mcp-over-http-client@latest", "http://your-server-ip:8008/mcp" ] } } }把your-server-ip换成实际地址。如果客户端支持直接配 HTTP 传输,也可以跳过mcp-over-http-client,直接填 URL。配置里如果还要接模型服务,Base URL 用https://taotoken.net/api,Key 用 TaoToken 统一 Key,Model ID 按你实际用的模型填。这三件套(Base URL + Key + Model ID)在 Cline、Codex 的auth.json、CC Switch 里都是同样的结构,配一次就能复用。
5. 常见报错排查
5.1 401 与鉴权失败
如果客户端调用模型服务时报 401,先确认 Key 是否正确、是否过期。用 TaoToken 统一管理时,检查https://taotoken.net/api-keys里的 Key 状态。注意 Base URL 不要带多余路径,https://taotoken.net/api就是完整前缀,客户端会自动拼/v1/chat/completions之类的端点。
5.2 local proxy failed
这个报错通常出现在客户端配置了本地代理但代理没起来。检查mcp-over-http-client是否安装成功,npx -y mcp-over-http-client@latest能不能手动跑通。如果服务端/health返回mcpProcess: stopped,说明子进程挂了,去看/logs?lines=100或journalctl -u mcp-http.service -n 100。
5.3 reading choices 报错
客户端解析模型返回时出现reading choices相关错误,多半是返回体不是预期的 OpenAI 格式。检查 Base URL 是否指向了正确的 API 端点,Model ID 是否拼写正确。有些客户端对返回结构敏感,用标准 OpenAI 兼容端点最稳。
5.4 OAuth 与 Codex auth.json
Codex 类工具用auth.json管理凭据,结构大致是:
{ "base_url": "https://taotoken.net/api", "api_key": "your-token", "model": "your-model-id" }如果报 OAuth 相关错误,确认没有混用两套鉴权方式。用 API Key 就统一用 API Key,别同时开 OAuth 流程。
5.5 子进程反复重启
/health里restartCount一直涨,说明子进程启动后很快退出。常见原因:server-filesystem路径写错、允许目录不存在、Node 版本太低。手动跑一次node node_modules/@modelcontextprotocol/server-filesystem/dist/index.js /data/mcp/mcp-http-filesystem/shared看报什么错,比看日志快。
6. 把 Key 和通道收拢到一处
filesystem MCP server 解决的是“AI 能碰哪些文件”,但 AI 工具链里还有另一半问题:模型调用凭据散落各处。Claude Code、Cline、Codex、Cursor 各有一套配置,换一次 Key 要改四五个文件,漏一个就报 401。
我的做法是把模型服务的 Base URL 统一指向https://taotoken.net/api,Key 用 TaoToken 控制台生成的统一 Key。这样新增工具时只改一处,旧工具不用动。TaoToken 的接入文档在https://taotoken.net/doc,里面有各客户端的配置示例。如果你主要做长期编码和 Agent 任务,可以看 Coding Plan:https://taotoken.net/coding-plan。想先验证模型通不通,用模型对话页:https://taotoken.net/chat。
回到 filesystem MCP server 本身,部署完之后建议做两件事:一是把shared目录按项目再细分,别让 AI 一次看到所有文件;二是定期看/logs,确认没有异常的文件写入。HTTP 模式的好处是你可以从任意机器curl一下/health,服务活着没活着一目了然。