跳到主要内容

不用切换到终端:让 Claude 网页通过 MCP 读写 Mac 本地文件

· 阅读需 20 分钟
一介布衣
全栈开发者

我平时会在 Claude 网页里讨论代码、整理文档。但讨论结束后,往往还要把它生成的内容复制到本地文件,再把修改结果贴回聊天。

能不能让 Claude 网页直接读取项目、创建文件,并把修改写回 Mac?

可以。我的做法是:在 Mac 上运行一个受限的文件 MCP 服务,再用 Cloudflare Tunnel 把它接到 Claude 的远程连接器。

这篇文章记录实际搭建过程,也解释其中容易混淆的几个问题:为什么网页能访问本机、是否需要公网 IP、cloudflared 是不是 SSH、TUN 能不能开,以及换 Claude 账号后要做什么。

本文环境为 macOS,使用 Node.js 文件 MCP 服务、Cloudflare Tunnel 和 Claude 网页自定义连接器。界面和功能入口以 2026 年 11 月实际使用情况为参考,Claude 账号是否支持远程自定义连接器及自定义认证头,需要以当前账号界面为准。

local-files-mcp 是本文自建的项目名称,不是一个安装后就具备全部功能的同名公共软件包。文中的 setup、service:install、manager:install 等 npm 命令属于该项目的脚本。本文给出架构、实现要点与部署步骤,不包含完整应用源码;如果使用其他 MCP 文件服务,需要按它的文档替换启动和配置命令。

一、搭完以后可以做什么​

连接成功后,可以直接在 Claude 网页对话中提出这样的要求:

先读取项目里的 docs/intro.md,把使用说明整理得更清晰。修改之前先说明准备改哪些地方,确认后写回原文件。

或者:

在 mcp-demo 目录里创建一份 hello.txt,写入一段测试文字,然后读取回来确认。

我的文件服务提供以下六个工具:

工具用途
list_directory列出目录中的可见文件和子目录
read_file读取文本,同时返回文件版本指纹
search_text在允许的范围内搜索文本
create_directory创建目录
write_file创建或替换文本文件
edit_file替换文件中的一处文本

这套连接器没有提供终端执行、删除文件、Git 提交或部署工具。修改代码后,不会自动运行测试、安装依赖或者发布项目。

二、整体架构:网页的请求怎样到达 Mac​

请求经过四个环节:

Claude 网页发起工具调用
↓
Claude 云端请求远程 MCP 地址
↓ HTTPS
Cloudflare 边缘节点
↓ 已建立的加密隧道
Mac 上的 cloudflared
↓ HTTP,回环地址
127.0.0.1:8787 上的 MCP 服务
↓ 认证、路径和文件版本检查
授权目录中的文件

这里有两个独立程序:

  • cloudflared:Cloudflare 官方的隧道客户端,负责网络转发。
  • 本地 MCP 服务:负责提供工具、检查权限,并真正执行文件读写。

cloudflared 本身不会根据 Claude 的要求修改文件。它把请求送到文件服务,文件服务检查通过后才操作磁盘。

MCP 负责什么​

MCP 可以理解为模型调用外部工具的一套协议。

文件服务通过 MCP 告诉 Claude:有哪些工具、每个工具接受什么参数、调用之后如何返回结果。Claude 根据用户要求选择工具,例如先调用 read_file,再调用 edit_file。

这次使用官方 TypeScript SDK,在 JavaScript 项目中实现 Streamable HTTP 接口,供 Claude 云端访问。仅支持标准输入输出(stdio)的文件 MCP 服务,不能原样填入网页的远程连接器 URL;需要支持 HTTP 的服务或合适的转换层。

Cloudflare Tunnel 负责什么​

家里的 Mac 通常没有可供外网访问的固定公网地址,而且处在路由器之后。

cloudflared 会从 Mac 主动向 Cloudflare 建立连接,并保持在线。Claude 请求公网域名时,Cloudflare 通过已有连接把请求传回 Mac。

它不是让 Mac 反复轮询“有没有新任务”,传来的也不只是通知,而是完整的 HTTP 请求与响应。

隧道通常使用基于 UDP 的 QUIC,也支持基于 TCP 的 HTTP/2。两者都不是 SSH,不过在“通过本地主动建立的通道访问本地服务”这一点上,可以类比 SSH 反向隧道。

因此,这套方案不需要:

  • 给 Mac 配置固定公网 IP。
  • 在路由器上做入站端口映射。
  • 在 Mac 上直接暴露文件服务端口。

但网络必须允许 cloudflared 连接 Cloudflare 的隧道节点。

DevSpace 在这里起了什么作用​

本次最终部署没有使用 DevSpace。

最初我研究过它是否能帮助完成这件事,但最终需要的是“HTTP MCP 文件服务 + 公网隧道”,直接用 Node.js 和 cloudflared 就能完成。

如果指的是 devspace.sh 对应的 DevSpace,它主要面向 Kubernetes 开发环境的部署、同步和调试。本次在 Mac 上直接运行服务,没有引入 Kubernetes,也不依赖 DevSpace。

三、准备条件与目录规划​

准备以下条件:

  1. 一台可以联网的 Mac。
  2. Node.js 22 或以上版本与 npm。
  3. Cloudflare 账号,以及一个可以管理 DNS 的域名。
  4. Claude 网页账号中有可用的远程自定义连接器入口。
  5. 一个支持 HTTP、认证和目录边界检查的文件 MCP 项目。

本次本地服务使用的主要依赖是 @modelcontextprotocol/sdk、Express 和 Zod。

为了区分源码、配置和业务文件,可以按下面的方式放置:

~/code/local-files-mcp/       自建 MCP 项目的源码
~/code/claude/ 固定配置与管理入口
├── .env 含认证密钥,不公开
├── restart.sh 重启文件服务
├── 打开管理页面.command 打开管理页面
├── index.html
├── app.js
└── style.css

~/code/29do/ 允许 Claude 操作的工作目录
~/.cloudflared/ 隧道配置与凭据,不公开

这些目录不是协议要求,只是方便维护的组织方式。源码移动后,后台任务中的程序路径需要同步更新。

本文用 mcp.example.com 表示公网域名。实际操作时,必须在本地服务配置、Cloudflare 配置、DNS 与 Claude 连接器中统一替换成自己的域名,不能直接使用示例地址。

四、先把本地 MCP 文件服务跑通​

1. 明确工具与权限边界​

不要把整个用户主目录直接交给模型。先从单个测试项目开始,确认流程后再调整范围。

本次实现做了以下检查:

  • 工具只接受授权根目录内的相对路径,拒绝 ../ 等越界访问。
  • 拒绝隐藏路径、常见凭据和数据库备份、依赖及构建目录。
  • 拒绝符号链接、硬链接以及 MCP 服务自身目录。
  • 文本限定为 UTF-8,单文件最大 1 MiB。
  • 读写都要求认证;对允许的 Host 和 Origin 做检查。
  • 修改已有文件时校验 SHA-256 版本,避免直接覆盖较新的内容。

这些是本次文件服务的实现规则,并不是所有 MCP 服务默认都有的能力。

目录校验属于应用层限制,不能当作 macOS 系统沙箱。允许范围内的普通源码也可能包含秘密,文件名过滤无法自动识别所有敏感内容。

2. 初始化项目配置​

在已经准备好的配套项目中执行:

cd ~/code/local-files-mcp
npm ci --ignore-scripts
mkdir -p ~/code/29do
npm run setup

本项目的 setup 会创建 ~/code/claude/.env,生成随机认证密钥,并保留已经存在的配置。

配置结构如下,实际密钥不要写成这里的占位文字:

MCP_ROOT="/Users/YOUR_MAC_USER/code/29do"
MCP_PUBLIC_URL=https://mcp.example.com
MCP_PORT=8787
MCP_TOKEN=YOUR_RANDOM_SECRET

其中:

配置项含义
MCP_ROOT文件访问根目录,使用已存在的绝对路径
MCP_PUBLIC_URL公网 HTTPS 来源地址,不带 /mcp
MCP_PORT本地文件服务端口,本例为 8787
MCP_TOKEN足够长的随机认证密钥,本项目至少要求 32 个字符

本次安装脚本默认写入了作者自己的域名,复用源码时要在启动前将 MCP_PUBLIC_URL 改成自己的域名。

保护配置文件:

chmod 700 ~/code/claude
chmod 600 ~/code/claude/.env

不要把 .env、密钥、Cloudflare 凭据提交到 Git,也不要贴进聊天截图或博客代码块。

3. 启动并验证本地服务​

npm test
npm start

另开终端检查:

curl -fsS http://127.0.0.1:8787/health

预期返回:

{"status":"ok"}

再检查未认证的接口:

curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8787/mcp

预期是 401。这是认证保护正常工作的表现。

本地服务只监听 127.0.0.1。暂时不要绑定到 0.0.0.0 来绕过网络问题。

五、配置 Cloudflare 域名与固定隧道​

1. 将域名接入 Cloudflare​

在 Cloudflare 添加自己的域名,按界面要求导入或补齐现有 DNS 记录,再到域名注册商或其代理管理平台更换 nameserver。

这里有一个容易混淆的地方:

  • 修改 A/CNAME 记录,是在当前 DNS 托管平台操作。
  • 更换权威 DNS 服务器(NS),通常要在域名注册管理平台操作。

两者可能不是同一个网站。域名目前在阿里云解析,不代表注册管理入口一定也在阿里云。

对普通完整域名接入方式,按 Cloudflare 分配给当前域名的两条 NS 设置即可。不要复制别人文章里的 NS。更换前核对现有网站、邮件等记录,避免遗漏。

等 Cloudflare 显示域名已激活,并确认边缘 HTTPS 证书可用后,再继续验证公网访问。不同账户方案也可能支持其他接入方式,本文记录的是完整域名接入的过程。

2. 安装 cloudflared​

在已经安装 Homebrew 的 Mac 上执行:

brew install cloudflared
cloudflared --version

cloudflared 是一个命令行客户端,没有必须一直开着的软件窗口。

3. 登录并创建命名隧道​

cloudflared tunnel login

命令会引导浏览器打开 Cloudflare,完成登录并授权目标域名。生成的账号证书保留在本机。

创建隧道:

cloudflared tunnel create 29do-files-mcp

记录命令返回的隧道 UUID 和凭据文件位置。下面示例中的 YOUR_TUNNEL_UUID 必须换成真实 UUID。

4. 编写本地转发配置​

创建文件 ~/.cloudflared/29do-files-mcp.yml:

tunnel: YOUR_TUNNEL_UUID
credentials-file: /Users/YOUR_MAC_USER/.cloudflared/YOUR_TUNNEL_UUID.json

ingress:
- hostname: mcp.example.com
service: http://127.0.0.1:8787
- service: http_status:404

注意 credentials-file 使用实际绝对路径,不要假设 YAML 中的 ~ 一定会被展开。

转发规则的含义是:

  • mcp.example.com 的请求发给本地 8787 端口。
  • 不匹配的域名返回 404。
  • URL 路径会继续传递,所以公网 /mcp 对应本地 /mcp。

校验配置:

cloudflared tunnel --config ~/.cloudflared/29do-files-mcp.yml ingress validate

5. 将域名关联到隧道​

cloudflared tunnel route dns 29do-files-mcp mcp.example.com

这一步通常会在 Cloudflare 创建指向隧道目标的 CNAME。它关联的是隧道身份,不是家里当前的公网 IP,因此网络地址变化时不需要手工更新 A 记录。

如果这个子域名已经有冲突记录,需要先核对用途,再在 DNS 控制台处理,不要直接覆盖其他服务。

6. 启动隧道​

cloudflared tunnel --config ~/.cloudflared/29do-files-mcp.yml run 29do-files-mcp

保持本地 MCP 服务也在运行。看到隧道连接成功注册后,验证公网:

curl -fsS --max-time 20 https://mcp.example.com/health
curl -sS --max-time 20 -o /dev/null -w '%{http_code}\n' https://mcp.example.com/mcp

预期分别是健康 JSON 和 401。

Cloudflare 负责公网 HTTPS 证书,Mac 与 Cloudflare 之间使用加密隧道,本机最后一段 HTTP 只走回环地址。Mac 无需再单独配置公网 HTTPS 证书。

也不要在这个端点前加上需要人工网页登录的 Cloudflare Access 页面,否则 Claude 云端的机器请求可能被登录页面挡住。本例由 MCP 服务本身校验 Bearer 密钥。

六、Clash Verge 开着 TUN,隧道能不能用​

可以,但需要让 cloudflared 的网络路径正确。

我的搭建过程中曾遇到:关闭 TUN 后隧道恢复,开启后连接失败。一开始尝试更换代理节点,没解决根本问题。

最终在当前订阅的扩展规则里,添加了优先执行的规则:

PROCESS-NAME,cloudflared,DIRECT

操作方式是:

  1. 在 Clash Verge 的“订阅”页找到当前正在使用的订阅。
  2. 打开它的“编辑规则”。
  3. 规则类型选择“匹配进程名称(PROCESS-NAME)”。
  4. 内容填写 cloudflared,策略选择 DIRECT。
  5. 添加为前置规则并保存;必要时重新激活订阅。
  6. 确认处于“规则”代理模式。
  7. 在“连接”页筛选 cloudflared,检查实际连接命中 DIRECT。
  8. 重启隧道后,再检查公网健康接口。

最后我实际验证了:TUN 开启、系统代理开启,隧道连接命中 ProcessName(cloudflared) → DIRECT,公网健康检查返回 200。

不过,DIRECT 只是让进程绕过代理,不保证任何网络环境都能直连成功。仍要确认到 Cloudflare 隧道节点的 TCP/UDP 7844 可达,以及 DNS 正常。

这条规则如果保存在某个订阅的扩展规则里,切换其他订阅时需要重新检查。不要只修改软件生成的临时运行配置,否则下次加载可能丢失。

命令行 curl 也不一定继承 macOS 的系统代理。若要通过本机代理检查公网,可以显式使用:

curl --proxy http://127.0.0.1:7897 \
--max-time 20 https://mcp.example.com/health

这里的 7897 是示例本机代理端口,按实际设置修改。这个命令只决定 curl 怎么访问公网,不会把 cloudflared 改成代理连接。

七、在 Claude 网页添加连接器​

进入 Claude 的设置,找到连接器或自定义连接器入口。按钮名称可能随着版本变化。

填写:

项目内容
名称29do 本地文件,可自行命名
Remote MCP server URLhttps://mcp.example.com/mcp
认证本次使用固定请求头方式,不配置 OAuth
请求头名称Authorization
请求头值Bearer 加本地随机密钥

注意 Bearer 和密钥之间有一个空格。

本项目提供了复制脚本:

cd ~/code/local-files-mcp
npm run copy-token

它把完整的请求头值复制到本机剪贴板,不在终端打印。接着直接粘贴到目标账号的连接器认证字段中,不要粘贴进聊天消息。

如果当前 Claude 界面没有固定请求头配置能力,不要删除服务器认证来强行连接。先确认当前账号支持的认证方式;必要时采用符合其要求的 OAuth 实现,这不属于本文这版服务已经实现的功能。

保存并连接后,应该能看到文件工具列表。开始普通对话时,还需要启用这个连接器和需要的工具。Claude 可能对具体写操作再要求批准。

换账号是否需要重新配置​

场景处理方式
退出后重新登录同一个账号通常保留配置
换浏览器登录同一个账号通常保留配置
登录另一个 Claude 账号需要在新账号添加连接器

新账号接入不需要在 Mac 上再部署一套服务,可以使用相同的端点。

但如果共享同一密钥,它们就具有相同的文件访问权限。退出旧账号不会自动撤销凭据。要让旧账号失效,需要轮换服务器密钥,并更新其他仍需使用该服务的账号。当前实现没有按 Claude 账号单独划分授权目录。

八、不要只看“连接成功”,实际做一次文件测试​

先准备一个独立测试目录,不要拿业务配置文件做第一次测试。

可以在 Claude 中逐步提出要求:

使用“29do 本地文件”连接器:

1. 列出根目录,确认当前可访问范围。
2. 创建一个尚不存在的 mcp-demo-test 目录。
3. 在里面新建 hello.txt,写入 MCP_TEST_CREATED。
4. 读取该文件并返回内容。
5. 使用刚读取的文件版本,将内容改为 MCP_TEST_EDITED。
6. 再次读取文件,确认修改结果。

如果测试目录或文件已存在,先停止,不覆盖已有内容。

然后在 Mac 上检查磁盘结果:

cat ~/code/29do/mcp-demo-test/hello.txt

只有看到实际文件和最终内容,才能证明工具完成了写入。网页断流不一定表示工具没有执行,应先检查文件,再决定是否重试,避免重复创建。

我的这次搭建已验证 Claude 网页实际创建本地文件成功;服务端的创建、读取和编辑集成测试通过。网页完整的“编辑后再读取”验收曾受对话断流影响,不能把这一项写成已经全部完成。上面的流程是建议读者执行的完整验收方法。

为什么要带 SHA-256​

假设 Claude 读取文件后,你又用编辑器修改了它。

如果 Claude 无条件覆盖,就可能把你刚写的内容抹掉。为此,read_file 返回文件的 SHA-256,修改时提交 expected_sha256,服务端确认版本没变再写入。

新文件使用 expected_sha256: null,目标已存在就拒绝创建。edit_file 则要求待替换文本恰好出现一次。

这种机制可以减少误覆盖,但不能把应用层读写变成操作系统级的事务,也不能完全消除其他本地进程同时修改路径或文件的竞态。

九、让服务在后台运行,登录后自动启动​

如果一直依赖两个终端窗口运行,关闭终端就可能断开服务。因此我把文件服务、隧道和管理页面都交给 macOS 的 launchd 管理。

本项目的安装命令是:

cd ~/code/local-files-mcp
npm run service:install
npm run tunnel:install-service
npm run manager:install

执行前,先停止之前在终端里前台运行的同一服务,避免争用端口。

三个后台任务的 Label 是本项目自定义名称,不是 Cloudflare 的固定要求:

服务Label
文件 MCPcom.wandoumama.local-files-mcp
隧道com.wandoumama.local-files-tunnel
本机管理页面com.wandoumama.local-files-manager

安装器在 ~/Library/LaunchAgents/ 写入 plist,关键配置包括:

<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>

RunAtLoad 表示任务加载时启动;KeepAlive 表示进程退出后由 launchd 尝试重新拉起。cloudflared 本身也会尝试重连网络。

“开机启动”需要说准确​

这次用的是用户级 LaunchAgent,因此是:

登录 macOS 账号后自动启动,随后在后台运行。

它不是机器刚开机、还停留在登录界面时就已经运行的系统级 LaunchDaemon。

关闭终端、关闭 Claude 或浏览器不会停止已经托管的服务。锁屏通常不影响运行,但电脑休眠、断网或退出 macOS 账号会影响连接。

不要为了方便就给所有进程授予完全磁盘访问权限。是否需要额外的 macOS 文件权限,取决于授权目录和实际报错;先从普通项目目录开始验证。

检查任务是否已加载:

launchctl print "gui/$(id -u)/com.wandoumama.local-files-mcp"
launchctl print "gui/$(id -u)/com.wandoumama.local-files-tunnel"
launchctl print "gui/$(id -u)/com.wandoumama.local-files-manager"

手动重启:

launchctl kickstart -k "gui/$(id -u)/com.wandoumama.local-files-mcp"
launchctl kickstart -k "gui/$(id -u)/com.wandoumama.local-files-tunnel"

启动任务不等于公网已经连接,重启后仍要检查健康接口。

十、用本机管理页面更换授权目录​

我还给这套服务加了一个本机管理页面:

http://127.0.0.1:8788

它和公网 MCP 端点是两个不同的服务:

地址用途
http://127.0.0.1:8788本人管理配置与后台任务
https://mcp.example.com/mcpClaude 调用文件工具

管理页面可以查看服务状态、点击“选择文件夹”打开原生 macOS 目录窗口、修改路径、保存配置并重启文件服务,也能单独重启隧道。

使用顺序是:

  1. 打开管理页面。
  2. 点击“选择文件夹”,或输入已经存在的绝对路径。
  3. 检查路径是否正确。
  4. 点击“保存并重启”。
  5. 等待文件服务恢复,在 Claude 中按新根目录使用相对路径。

选择文件夹只填入路径,保存才真正改变访问范围。

更换根目录不用修改公网域名、隧道或 Claude 的连接器地址。固定的 ~/code/claude/restart.sh 也能用于重启文件服务。

这个页面只监听回环地址,不通过 Cloudflare 发布。接口还会检查 Host、Origin 和自定义请求头,避免其他网站直接诱导浏览器修改本机配置。页面不显示认证密钥。

十一、遇到问题,按链路逐层查​

不要一上来就重装或重建隧道。按下面顺序定位,通常更容易找到原因。

现象优先检查
管理页面打不开管理服务是否启动,8788 端口是否可访问
本地 /health 不通MCP 服务、根目录配置、端口占用与启动日志
本地正常,公网失败隧道连接注册、Clash 路由、DNS 与 Cloudflare 域名状态
公网出现 1033是否没有可用隧道连接;刚重启时可短暂出现,等待一次重连后复查
/mcp 返回 401不带认证时正常;连接器也报错时检查 Bearer 值是否一致
域名访问返回 403Host/Origin 校验、Cloudflare 规则,以及是否有其他访问控制拦截
Claude 看不到文件工具账号配置、端点路径 /mcp、认证方式、对话是否启用连接器
文件拒绝访问当前根目录、相对路径、文件类型及服务端限制
修改提示版本冲突重新读取文件并取得新的 SHA-256,再决定如何合并
网页断流但不确定是否写入先查看本机文件,避免直接重复执行

本项目日志在源码目录下的 .runtime/。文件服务、隧道和管理器各有独立日志。

查看或向别人提供日志时,先检查是否含有密钥、cookie、文件内容或其他私人信息,不要直接上传完整配置目录。

十二、使用这套方案时,我会保留的几条边界​

它让网页调用本地工具变得方便,也把一部分文件操作能力交给了远端模型。我的做法是:

  • 只开放实际需要的目录,从测试项目开始。
  • 保留 Bearer 认证、目录边界和版本检查。
  • 重要修改先让 Claude 说明计划,再批准写入。
  • 用 Git 或其他备份方式保留恢复路径。
  • 不因为一次文件访问报错,就把主目录或敏感配置全部开放。
  • 不在博客截图里展示密钥、Cloudflare 凭据、账号邮箱和真实业务内容。

MCP 负责工具协议,Cloudflare Tunnel 负责把请求送到本机,文件服务负责权限与落盘。把这三层职责分清,之后无论是排查故障,还是换域名、换账号、换工作目录,都会容易很多。

参考资料​