@mcp-use/tunnel 深度指南:用 WebSocket 中继把本地 MCP 服务器暴露为公共 HTTPS 端点
2026/9/24 17:14:57 网站建设 项目流程
  • 后端
  • MCP 服务
  • MCP Clients
  • AI Agent
  • 人工智能

【免费下载链接】mcp-use

The fullstack MCP framework to develop MCP Apps for ChatGPT / Claude & MCP Servers for AI Agents.

项目地址:https://gitcode.com/gh_mirrors/mc/mcp-use
点击查看免费下载

@mcp-use/tunnel是 mcp-use 全栈 MCP 框架中的独立隧道客户端,它通过托管的 mcp-use WebSocket 中继,把运行在本机的 HTTP、WebSocket 或 MCP 服务器暴露为公网可访问的 HTTPS 地址。本文以 libraries/typescript/packages/tunnel/README.md 为核心,结合 源码 与 测试 深入讲解它的 CLI 用法、参数语义、认证与状态持久化、断线重连机制以及底层转发协议。读完本文,你将掌握用一条npx命令把本地 MCP 服务器临时暴露给 ChatGPT、Claude 等远程客户端进行联调的方法,并理解其内部工作机制。

一、包定位:独立客户端,与 mcp-use CLI 共用同一实现

@mcp-use/tunnel是一个独立的 npm 包(当前仓库内版本为 0.2.1),其作用正如包描述所言:作为 mcp-use 的 WebSocket 隧道客户端,将本地 HTTP、WebSocket 或 MCP 服务器暴露到公网。

npx @mcp-use/tunnel 3000

这条命令会把本机3000端口上运行的服务映射到一个公共 HTTPS 地址,之后远程的 MCP 客户端(如 ChatGPT、Claude)即可通过该公网地址访问你本地的 MCP 端点。

值得强调的是它的设计定位:同样的客户端代码同时驱动着mcp-use dev --tunnelmcp-use start --tunnel两个框架命令。因此安装mcp-use本身并不需要再单独安装本包;而当你只想手动暴露任意一个本地端口时,才直接用npx @mcp-use/tunnel <端口>。这一点在包的 package.json 中也有印证:它的bin字段注册了mcp-tunnel命令,入口指向 dist/bin.js,而 bin.ts 只有寥寥几行——直接调用 cli.ts 中的runTunnelCli,真正的隧道生命周期管理则集中在 src/index.ts 的createTunnelManager中,供独立 CLI 与框架内嵌路径共享。

注意:包要求 Node.js >= 22.22.2(见 package.json 的engines字段),因为实现依赖 Node 22 的原生WebSocketfetch能力,无需安装任何原生二进制或额外的包运行器子进程——这一点在 index.ts 的模块头注释中写得很清楚:“The client connects directly to the relay over WebSocket. No native binary or package-runner subprocess is required.”

二、命令行用法与选项详解

2.1 命令语法

README 给出的完整语法为:

mcp-tunnel <LOCAL_PORT> [--relay RELAY_URL] [--subdomain SUBDOMAIN]

对应源码 cli.ts 中的usage()输出,三个参数的含义如下:

参数说明
<LOCAL_PORT>本机要暴露的 HTTP/WebSocket/MCP 服务器端口,必填。必须是 1~65535 之间的整数,否则抛出Invalid local port错误
--relay RELAY_URL覆盖 WebSocket 中继的 API 地址。需要有效的 HTTP(S) URL,缺少值会报--relay requires a URL
--subdomain SUBDOMAIN请求一个稳定的隧道标识符(子域)。缺少值会报--subdomain requires a value

参数解析逻辑位于 parseArgs:-h/--help打印用法后退出;以-开头的未知选项会抛出Unknown option;重复传入端口或多余的位置参数会报Unexpected argument。这些行为在 bin.test.ts 中都有对应的断言(例如parseArgs(["0"])抛出 “Invalid local port”,parseArgs(["3000", "--unknown", "value"])抛出 “Unknown option”)。

2.2 环境变量:MCP_USE_WS_RELAY

除了命令行--relay之外,还可以通过环境变量指定中继地址:

MCP_USE_WS_RELAY=https://relay.example.com npx @mcp-use/tunnel 3000

在源码中,三者优先级为:显式传入的relayUrl选项 >MCP_USE_WS_RELAY环境变量 > 默认生产中继https://api.tunnel.mcp-use.run(见 index.ts 的tunnelApiBase)。该函数还会校验协议必须是http:https:,否则抛错。这一设计便于在开发/测试环境下指向自建的中继部署,例如 e2e 测试中就通过relayUrl指向本地 relay-fixture.ts 构造的测试中继。

2.3 运行与优雅退出

runTunnelCli(cli.ts)在启动隧道后会打印一行:

Tunnel ready: https://<adjective>-<color>.tunnel.mcp-use.run

然后挂起进程,监听SIGINTSIGTERM。收到任一信号时,它会调用manager.stop()主动释放隧道保留并关闭 WebSocket 连接,实现优雅退出;停止完成后进程才真正结束。这意味着隧道仅在命令运行期间有效,进程退出即失效——如果需要稳定的公共 URL,应当部署服务器而不是依赖本地隧道。

三、Host 头处理:localhost 校验与 x-forwarded-host

README 中特别强调了一个对本地服务器友好的细节:

转发到本地服务器的请求携带Host: localhost(与mcp-use start --tunnel的行为一致),以满足本地主机校验;原始的公共隧道主机名会保留在x-forwarded-host请求头中,供依赖主机名的应用使用。

这一行为由两个层面共同保证:

  1. CLI 默认值runTunnelCli在创建 manager 时固定传入localHostHeader: "localhost"(cli.ts),因此独立 CLI 与框架命令行为完全一致。对应的单元测试也断言了这一点(bin.test.ts)。
  2. 源码兜底逻辑:在 index.ts 的handleRequestStart中,如果localHostHeader有值,则用它覆盖请求头的host;否则回退到转发来的x-forwarded-host。同时sanitizeHeaders会剥离hostcontent-length以及各类 hop-by-hop 头(如connectiontransfer-encodingupgrade等,见 HOP_BY_HOP_HEADERS),由隧道层重新生成可信的头部。

端到端测试 e2e.test.ts 完整验证了这个语义:默认情况下本地服务收到的host等于公共 URL 的主机名、x-forwarded-host也相同;而当使用localHostHeader: "localhost"创建 manager 后,本地服务收到的host变为localhost,但x-forwarded-host依然保留公共主机名。这对那些绑定域名校验的本地框架(例如对 Host 白名单敏感的 MCP 服务器)非常友好——既通过了本地校验,又不会丢失原始公网主机信息。

四、保留、认证与状态持久化

4.1 隧道保留(Reservation)流程

每次启动隧道前,客户端会先向中继申请一个“保留”:

  • 申请POST {relayBase}/api/tunnels/request,请求体为{}(不指定子域)或{"subdomain": "..."}(指定子域),10 秒超时(reserveTunnel);
  • 响应:中继返回tunnel_id(隧道标识,即子域名)、token(认证令牌)、connect_url(WebSocket 连接地址)、public_url(公共访问地址)。任何字段缺失都会被视为无效保留并报错;
  • 连接:客户端随后建立到connect_url的 WebSocket,连接打开后立即发送{"type": "authenticate", "token": ...}控制消息完成认证,中继回发{"type": "ready"}后隧道才算就绪(index.ts)。

也就是说,保留是经过认证的——WebSocket 连接本身不携带凭证,握手后必须显式完成 token 认证,未经认证的连接无法转发流量。

4.2 状态文件 .mcp-use/state/tunnel.json

保留信息会被持久化到.mcp-use/state/tunnel.json(位于当前工作目录下),字段包括:

{ "subdomain": "quiet-amber", "token": "…", "connect_url": "wss://api.tunnel.mcp-use.run/connect/quiet-amber", "public_url": "https://quiet-amber.tunnel.mcp-use.run" }

源码中定义了对应的TunnelStateFile接口(index.ts):subdomain为上次成功分配的隧道标识,token为保留认证值,connect_url用于断线后重新挂接,public_url为稳定的公共地址。持久化时目录会自动递归创建(mkdir+recursive: true),且文件以0o600权限写入(persistState),避免 token 泄露给其他系统用户;写入失败仅打印警告,不影响隧道运行。

这份状态文件的价值在于重启后复用同一标识start()会先读取本地状态,若保存的subdomain与请求一致(或未指定子域),优先尝试用已保存的保留直接挂接;只有挂接失败或保留已失效时才会申请新的隧道(index.ts)。

4.3 优雅关闭时释放保留

stop()会向中继发送DELETE /api/tunnels/{subdomain}(携带Authorization: Bearer <token>,2 秒超时)主动释放保留,然后以关闭码 1000("Client shutdown")关闭 WebSocket(stop)。即使中继不可达导致释放失败,中继侧的超时过期机制也会兜底清理——释放逻辑的注释明确写道 “Expiry provides the final cleanup path when the relay is unreachable”。e2e 测试中的releases the authenticated reservation during graceful shutdown用例正是断言停止后中继的删除计数为 1(e2e.test.ts)。

五、断线自动重连与连接保活

5.1 重连策略

隧道建立后,如果中继连接意外断开,manager 不会直接放弃,而是自动重连:

  • 重挂(reattach):优先复用原保留的connect_url重新建立连接,最多尝试 5 次(REATTACH_ATTEMPTS),退避时间从 1 秒起指数增长、上限 30 秒;
  • 重新保留:重挂失败后,读取本地状态文件释放旧保留,再申请新保留;若保存的子域已不可用,会打印提示并请求一个新标识;
  • 终止条件:当中继以关闭码 1008 关闭,或关闭原因为 “Tunnel expired” / “Tunnel deleted” 时,视为保留已终止,必须申请全新保留;否则一律按可重挂处理(attach)。

整个调度逻辑封装在scheduleRespawn(index.ts)中,并用respawnInFlight防止并发重连;start()期间若已有活跃连接则直接返回现有 URL。单元测试reattaches the same reservation after a deployment disconnect验证了这一点:模拟中继断开后,客户端自动建立了第二条指向同一connect_url的连接,且未发起任何新的保留请求(tunnel.test.ts)。

5.2 Keepalive 保活

为防止空闲连接被中继回收,客户端在收到中继ready消息且携带keepalive: true协商标志时,会以 25 秒为周期发送{"type": "ping"}控制消息,并要求中继回pong;若发出 ping 后未等到 pong(即awaitingPong仍为真),则判定连接失活并以 1011 关闭,触发重连(index.ts)。测试同时覆盖了中继不支持 keepalive 协商时不发送 ping 的降级行为(tunnel.test.ts)。

调试技巧:设置环境变量MCP_USE_TUNNEL_DEBUG=1可开启隧道层的调试日志(前缀[mcp-use]),用于观察重连、保留替换等内部事件(index.ts)。

六、底层转发协议:控制消息与二进制帧

虽然使用方只需一条命令,但理解底层协议有助于排查联调问题。客户端与中继之间的 WebSocket 同时承载两类数据:

1. JSON 控制消息(文本帧):包括authenticate(认证)、ready(就绪)、ping/pong(保活)、request-start(请求开始,含方法、路径、头)、request-end/cancel(请求结束/取消)、response-start(响应开始,含状态码与头)、response-endresponse-error,以及 WebSocket 相关的websocket-open/websocket-ready/websocket-close/websocket-error。单条控制消息上限 64 KiB(MAX_CONTROL_MESSAGE_BYTES)。

2. 二进制帧(请求/响应体与 WebSocket 数据):帧格式为1 字节类型 + 36 字节 requestId + 载荷。类型值在源码中以常量定义:REQUEST_BODY_FRAME=1RESPONSE_BODY_FRAME=2、公共 WebSocket 文本/二进制帧为 3/4、本地 WebSocket 文本/二进制帧为 5/6(index.ts)。requestId必须是 UUID v4 格式(有专门的正则校验),单帧载荷上限 256 KiB(MAX_BODY_FRAME_BYTES),超大响应体会自动分片发送。

其他重要的工程约束包括:

  • 并发本地请求上限 100(MAX_LOCAL_REQUESTS),超过则直接以 1008 关闭连接;
  • 发送缓冲超过 1 MiB(MAX_BUFFERED_SOCKET_BYTES)时应用背压等待,防止内存暴涨(waitForSocketCapacity);
  • 本地 WebSocket 在未 open 期间的入站消息先入队,open 后按序冲刷;缓冲区超限则以 1009 关闭并通知中继;
  • 非法帧、非法 requestId、不支持的请求路径(不以/开头)或未知控制消息类型,都会触发对应的关闭码(1003/1008)——单元测试rejects malformed frames, invalid identifiers, and unsupported messages对此有完整覆盖(tunnel.test.ts)。

值得注意的是,隧道是双向透明转发:不仅支持普通 HTTP 请求,还支持 WebSocket 升级。e2e 测试验证了公共 WebSocket 的文本与二进制消息都能双向回显(e2e.test.ts),同时还验证了 MCP JSON-RPC 请求、流式响应(chunked)以及 8 路并发请求的转发正确性(e2e.test.ts)。

七、在 mcp-use 框架中使用隧道

虽然本包可独立使用,但最常见的场景还是与 mcp-use CLI 集成。官方指南 docs/tunneling/index.mdx 给出了完整流程:

开发阶段:

mcp-use dev --tunnel

测试构建产物:

mcp-use build mcp-use start --port 3000 --tunnel

启动成功后输出会同时给出本地与公共 MCP 端点,例如:

[mcp-use] starting tunnel for port 3000… mcp-use server running at http://localhost:3000/mcp mcp-use public MCP URL: https://proper-black.tunnel.mcp-use.run/mcp

关键点:给客户端(ChatGPT、Claude 等)的 URL 必须以/mcp结尾,只复制隧道源地址是不够的。之后可以用mcp-use clientCLI 快速验证隧道连通性:

npx mcp-use client connect tunnel-test https://proper-black.tunnel.mcp-use.run/mcp npx mcp-use client tunnel-test tools list

connect会完成 MCP 握手并把该隧道保存为tunnel-testtools list则列出你本地服务器暴露的工具。停止联调时在终端按Ctrl+C即可,框架命令会同时停止本地服务器与隧道。另外,由于中继同时转发 HTTP 与 WebSocket 升级,在开发 MCP App 时,通过公共隧道加载的视图依然能保持 Vite HMR 热更新——修改本地视图无需重启隧道即可在远端客户端看到变化。

八、生命周期与配额限制

隧道定位于部署前的本地联调,而非长期公网服务,因此存在明确的时限与配额(见 docs/tunneling/index.mdx 的 “Lifetime and limits” 一节):

  • 隧道仅在命令运行期间有效,进程退出即失效;
  • 框架命令运行期间,掉线的隧道注册会自动重建;
  • 从未连接成功的保留 5 分钟后过期;
  • 已连接的隧道 24 小时后过期;
  • 超过 1 小时无活动的隧道会被清理;
  • 每个源 IP 每小时最多创建 10 条隧道,同时最多保持 5 条活跃隧道。

需要稳定公共 URL 时,应直接部署服务器,而不是依赖本地隧道。

九、小结

@mcp-use/tunnel用一条命令解决了“本地 MCP 服务器如何被远程客户端访问”的问题:通过托管的 WebSocket 中继完成认证保留、状态持久化、断线重连与双向流量转发,且不依赖任何原生二进制。它既是独立的 CLI 工具(npx @mcp-use/tunnel 3000),也是mcp-use dev --tunnel/mcp-use start --tunnel的内置能力——两者共享 createTunnelManager 这套实现。对本地开发而言,它是把 ChatGPT、Claude 等远程 MCP 客户端接入本机服务器的最短路径;需要验证细节时,单元测试 与 端到端测试 都是很好的行为参考。

  • 后端
  • MCP 服务
  • MCP Clients
  • AI Agent
  • 人工智能

【免费下载链接】mcp-use

The fullstack MCP framework to develop MCP Apps for ChatGPT / Claude & MCP Servers for AI Agents.

项目地址:https://gitcode.com/gh_mirrors/mc/mcp-use
点击查看免费下载

相关推荐

上一篇:🔍 QA Team Review Report
下一篇:LlamaIndex RedisChatStore 深度解析:用 Redis 持久化跨进程 Chat Memory(llama-index-storage-chat-store-redis)

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询