关于 Tailnet MCP bridge
Tailnet MCP bridge 是面向托管 Agent 和沙箱客户端的 stdio MCP 服务,用于访问 Tailscale tailnet 内的 MCP 服务。它内置 Tailscale 节点并将请求转发到 Streamable HTTP 端点,无需将服务暴露到公共互联网。
主要功能
- 01将 stdio 桥接到 Streamable HTTP
- 02以带标签设备加入 tailnet
- 03让 MCP 服务不暴露于公共互联网
- 04在服务重启后恢复会话
- 05仅重试未到达服务的请求
适用场景
- 01让托管 Agent 沙箱连接 tailnet 内的服务
- 02访问通过 tailscale serve 发布的 MCP 服务
- 03在服务重启后维持 MCP 会话
MCP 相关
它作为 stdio MCP 服务运行,并将请求转发到 tailnet 内的 Streamable HTTP MCP 服务。它桥接客户端的 MCP 连接,不提供自己的工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e TS_AUTHKEY ghcr.io/zedong-peng/tailnet-mcp:v0.3.0TS_AUTHKEY密钥Tagged, pre-authorized auth key; only needed the first time本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/zedong-peng/tailnet-mcp:v0.3.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
tailnet-mcp
一个 stdio MCP 服务,会将所有内容转发到 tailnet 内的 Streamable HTTP MCP 服务。
托管 Agent(Grok、云端编码 Agent、沙盒助手)通常通过两种方式接入 MCP 服务。通过 URL:由供应商的服务连接,因此你的服务必须位于公共互联网。通过命令:Agent 在自己的计算机上启动进程,但那台计算机不在你的 tailnet 内,而且你无法在那里运行 tailscaled(没有 root 权限,也没有 TUN)。tailnet-mcp 就是通过命令接入的方式:它内嵌一个 Tailscale 节点(tsnet),以带标签的设备加入你的 tailnet,并在 stdio 与服务之间搭建桥接。你的服务仍在 tailscale serve 后面,不会暴露到互联网。
agent ──stdio──▶ tailnet-mcp (tag:agent) ══tailnet══▶ tailscale serve ──▶ MCP server on 127.0.0.1
安装
从 Releases 下载适用于 linux 或 darwin、amd64 或 arm64 的静态二进制文件,并通过 SHA256SUMS 校验。也可以自行构建:
CGO_ENABLED=0 go install github.com/zedong-peng/tailnet-mcp@latest
或者运行镜像 ghcr.io/zedong-peng/tailnet-mcp,并将卷挂载到 /home/nonroot/.local/state/tailnet-mcp。它也已列入 MCP Registry,名称为 io.github.zedong-peng/tailnet-mcp。
使用
-
仅向 tailnet 发布服务,并使用一个无人能猜到的路径:
tailscale serve --bg --https=8443 --set-path=/$(openssl rand -hex 16) http://127.0.0.1:8080/mcp -
创建一个带有 Agent 标签、一次性且预先授权的 auth key(通过管理控制台或 API),然后从 Agent 的计算机登录一次:
TS_AUTHKEY=tskey-auth-... tailnet-mcp -hostname my-agent -url https://server.tailnet.ts.net:8443/<path> -check-check会建立连接,将服务的工具打印到 stderr,然后退出。节点密钥保存在-state中(默认为~/.local/state/tailnet-mcp/<hostname>);请将其保存在持久存储中。带标签的节点不会过期,因此之后不再需要该密钥。 -
将不带密钥的相同命令注册为 Agent 的 MCP 服务:
tailnet-mcp -hostname my-agent -url https://server.tailnet.ts.net:8443/<path>
每个进程都是一个独立的 tailnet 节点。要接入第二个服务,请使用不同的 -hostname 再运行一个进程。
加强防护
Agent 会读取不可信输入(网页、邮件),因此应假设它可能被诱导执行其工具所允许的任何操作。这里有三层防护,均不依赖 Agent 是否遵守约束:
-
网络。 只授予该标签访问所需端口的权限,不授予其他权限:
"grants": [ {"src": ["tag:my-agent"], "dst": ["server"], "ip": ["tcp:8443"]}, ], "tests": [ {"src": "tag:my-agent", "accept": ["server:8443"], "deny": ["server:22", "server:443"]}, ],允许一切的授权规则(
"src": ["autogroup:member"], "dst": ["*"])不涵盖带标签的设备,因此该标签只能获得这条授权规则授予的权限。 -
路径。
tailscale serve --set-path只会在保密路径上响应;端口上的任何其他路径都会返回 404。tailnet 内的其他设备无法通过猜测端口使用该服务。 -
服务端。 在数据所在的位置强制实施权限,而不是在 Agent 中:只能保存草稿的邮件服务、仅对一个共享集合拥有只读权限的密码库账户。examples/private-tools 会设置这两者。
行为
- 首次请求前,它会持续 ping 服务所在的节点,直到收到响应。重启后,服务可能仍持有指向该节点的过时路径,导致首次拨号超时。
- 只会重试从未到达服务的请求,因此工具调用不会被发送两次。
- 服务重启并丢失会话时(返回 404,或关于会话的 400),桥接程序会重新发送
initialize,然后再次发送请求。客户端不会察觉。 - 请求失败时会返回包含原因的 JSON-RPC 错误,因此客户端不会一直等待不会到来的响应。
- 遇到 EOF 时,它会通过
DELETE结束会话。日志写入 stderr;stdout 只承载 MCP 帧。-v会额外输出 tsnet 自身的日志。
不支持:可选的 GET 流,该流允许服务在响应之外发送消息。服务会在各自的响应中回答每个请求,几乎所有工具服务都是如此。
对比
mcp-remote和其他 stdio 到 HTTP 的桥接工具需要一条通往服务端的网络路径。在 Agent 的沙箱中,这意味着需要一个公开 URL。- 在沙箱中运行
tailscale up需要 root 权限和 TUN 设备。如果有人以用户身份登录,而不是使用带标签的密钥,沙箱就会获得该用户对整个 tailnet 的访问权限。 - Tailscale Aperture 是服务端的网关:它聚合 HTTP MCP 服务,并按用户授予工具权限。它无法让沙箱中的 Agent 接入 tailnet,因此两者可以配合使用。
构建和测试
go test -race ./...
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" .
标记 v* 会发布这四个二进制文件、镜像和注册表条目。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。