实践

用 Tailscale Funnel 暴露 DevSpace 给 ChatGPT

让 DevSpace 和 Tailscale Funnel 运行在同一个本地环境,用公网 HTTPS MCP 地址把本机项目接入 ChatGPT。

作者:黄撑 更新于 2026-07-09

这篇教程解决一个很具体的问题:DevSpace 已经能在自己的电脑或服务器上运行,怎么给它一个 ChatGPT 能访问的公网 HTTPS MCP 地址。

这里不再把 WSL 当成前提。你可以使用 Linux、WSL、Windows、macOS,甚至一台远程 Linux 开发机。真正重要的只有一个原则:

Tailscale Funnel 必须能够通过本机回环地址访问 DevSpace。最简单的做法,就是让 Tailscale 和 DevSpace 运行在同一个系统环境里。

最终架构与验收顺序

这套方案的核心链路始终一样:

ChatGPT -> Tailscale Funnel -> 127.0.0.1:7676 -> DevSpace

不同系统的区别,只在于 DevSpace 怎么启动、Tailscale 怎么安装、服务怎么常驻。Funnel、公网域名、OAuth 验收和 ChatGPT 连接方式不变。

VALIDATION PATH

先本地,再公网,最后做真实调用

哪一层没有达到预期,就停在这一层排查。不要在本地还没通时先查 ChatGPT。

https://...ts.net/mcp
01 本机服务 DevSpace 127.0.0.1:7676/mcp 401

端点存在,只是当前请求还没有授权。

通过后
02 OAuth 发现 Metadata /.well-known/... 200 × 2

受保护资源和授权服务器信息都能访问。

通过后
03 公网隧道 Tailscale Funnel funnel status Funnel on

公网域名已经代理到本地的 7676。

通过后
04 最终验收 ChatGPT 读取测试文件 真实调用

能读取 allowedRoots 中的文件,整条链路才算完成。

排查规则

看到 `401` 不代表失败;这里真正的失败,是某一层没有得到它应该出现的信号。

401 MCP 端点可达 200 × 2 OAuth 发现完整 Funnel on 公网代理已开 真实调用 权限和项目目录闭环

普通 Tailscale 的 100.x 地址只给 Tailnet 内的设备访问。ChatGPT 不在你的 Tailnet 里,所以不能填 http://100.x.x.x:7676/mcp,必须使用 Funnel 提供的公网 HTTPS 地址。

先决定 DevSpace 和 Tailscale 放在哪里

不要先复制命令。先确认两者是不是处于同一个本地环境。

推荐

Linux / 远程开发机

DevSpace 和 Tailscale 都直接运行在同一台 Linux 机器上。

  • Funnel 直接代理 127.0.0.1:7676
  • 需要常驻时可使用 systemd
推荐

WSL

DevSpace 和 Tailscale 都装进同一个 WSL 发行版。

  • 避免 Windows 到 WSL 的跨系统转发
  • Funnel 和 DevSpace 共用同一个回环地址
可用

Windows

DevSpace 和 Tailscale 都直接运行在 Windows 主机。

  • 先确认 tailscale CLI 可用
  • 不要再把服务转发进另一个虚拟环境
可用

macOS

DevSpace 和支持 Funnel CLI 的 Tailscale 客户端运行在同一台 Mac。

  • 先确认当前客户端变体支持 Funnel
  • 端口共享和文件共享的限制并不相同

开始前准备

先确认下面几件事:

  • DevSpace 已经安装,并且至少能在当前环境里手动启动一次。
  • 当前环境能运行 tailscale CLI。
  • 已有 Tailscale 账号,并有权限为这台设备启用 Funnel。
  • 已经确定 DevSpace 只允许访问哪些项目目录。

开始前替换这些占位符

全文使用下面的占位符,复制命令时先替换成自己的真实值。

占位符替换内容
devspace-device这台 Tailscale 设备的 hostname
your-tailnet.ts.net你的 Tailscale Funnel 域名后缀
https://devspace-device.your-tailnet.ts.netFunnel 提供的公网 Origin
/absolute/path/to/project允许 DevSpace 访问的项目绝对路径
YOUR_DEVSPACE_START_COMMAND这台机器真正能启动 DevSpace 的命令

第一步:先确定 DevSpace 的真实启动方式

这一步不要先配置开机启动。先手动启动一次 DevSpace,确认这台机器真正使用的命令。

Linux / WSL / macOS

先检查:

command -v node
command -v devspace
devspace --help

Windows PowerShell

可以检查:

Get-Command node
Get-Command devspace
devspace --help

情况 A:devspace CLI 可以直接运行

如果当前版本支持下面的方式启动:

devspace serve

就把这个真实可用的命令记录下来。后续需要常驻时,再让系统启动机制执行它。

情况 B:找不到全局 devspace,但 DevSpace 已经能运行

这不一定是安装失败。Node 可能来自 NVM、Volta、asdf、某个工具内置环境或其他受管目录,DevSpace 也可能通过 Node 脚本启动。

这种情况不要强行改全局 PATH。直接记录已经验证可用的启动命令,例如:

/absolute/path/to/node /absolute/path/to/start-script.mjs

核心原则只有一个:后续自动启动时,使用的必须是已经手动验证成功的真实命令,不要假设每台机器都有全局 nodedevspace

第二步:安装并登录 Tailscale

不同系统的安装方式不同,这里不把某一个系统写成唯一标准。你只需要在 运行 DevSpace 的同一个环境 里完成安装和登录。

Linux / WSL 可以直接使用官方安装脚本:

curl -fsSL https://tailscale.com/install.sh | sh

如果当前环境使用 systemd,再启动服务:

sudo systemctl enable --now tailscaled

没有 systemd 的环境不要硬套这条命令,按当前系统实际支持的方式启动 Tailscale。判断是否成功的标准不是用了哪种服务管理器,而是 tailscale status 能正常返回设备状态。

Windows 和 macOS 使用对应客户端安装后,确认终端里能够执行:

tailscale status

登录,并给设备设置一个容易识别的 hostname:

tailscale up --hostname=devspace-device

如果当前平台需要管理员权限,就按平台要求执行;已经登录的设备不一定需要重新 up,重点是最后能看到设备在线。

确认:

tailscale status

这一步看到 100.x.x.x 很正常,但不要把它填进 ChatGPT。100.x 是 Tailnet 内网地址,不是公网 MCP 地址。

接着确认这台设备的完整 DNS 名称,例如:

devspace-device.your-tailnet.ts.net

从下一步开始,用这个真实域名替换全文占位符。到这里仍然没有打开 Funnel,只是先拿到 DevSpace 需要的公网 Origin。

第三步:配置 DevSpace

打开 DevSpace 配置文件。

Linux、WSL 和 macOS 通常是:

~/.devspace/config.json

Windows 则使用当前用户目录下对应的 .devspace/config.json。不要照抄别人机器的绝对路径。

核心配置类似这样:

{
  "host": "127.0.0.1",
  "port": 7676,
  "allowedRoots": [
    "/absolute/path/to/project"
  ],
  "publicBaseUrl": "https://devspace-device.your-tailnet.ts.net",
  "allowedHosts": [
    "localhost",
    "127.0.0.1",
    "::1",
    "devspace-device.your-tailnet.ts.net"
  ]
}

publicBaseUrl 只写公网 Origin,不带 /mcp。ChatGPT 里填写连接地址时,才加 /mcp

allowedRoots 必须使用这台机器上 DevSpace 真正能识别的绝对路径。Linux、WSL、macOS 和 Windows 的路径格式不同,不要把示例路径原样复制到所有系统。

第四步:启动 DevSpace,并完成本地 OAuth 验收

先用第一步已经验证过的命令启动 DevSpace。

YOUR_DEVSPACE_START_COMMAND

这时先不要打开 Funnel。先确认本地端点和 OAuth discovery 都是完整的。

先看 /mcp 是否可达

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

预期:

401

这个 401 是好结果:说明 /mcp 存在,只是当前请求还没有授权。

Windows PowerShell 也可以用自己习惯的 HTTP 请求工具验证,判断标准仍然是相同的状态码。

再看 OAuth metadata 是否完整

curl -sS -o /dev/null -w '%{http_code}\n' \
  http://127.0.0.1:7676/.well-known/oauth-protected-resource/mcp

curl -sS -o /dev/null -w '%{http_code}\n' \
  http://127.0.0.1:7676/.well-known/oauth-authorization-server/mcp

理想结果是:

200
200

只有这三个本地信号全部成立,才继续打开公网:

/mcp -> 401
protected resource metadata -> 200
authorization server metadata -> 200

如果 authorization server metadata 不是 200

不要先去排查 Funnel,也不要直接在 ChatGPT 里反复重连。先处理 DevSpace 本地 OAuth discovery。

优先顺序:

  1. 先确认当前 DevSpace 版本是否已经原生提供这个路由。
  2. 如果当前安装方式仍然缺少该路由,再加一个很薄的本地启动包装。

这个包装不负责公网代理,只是在 DevSpace app 开始监听前补上。下面只展示兼容路由的核心逻辑,不是完整启动脚本:

const publicBaseUrl = config.publicBaseUrl.replace(/\/+$/, '');

app.get('/.well-known/oauth-authorization-server/mcp', (_req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.json({
    issuer: `${publicBaseUrl}/`,
    authorization_endpoint: `${publicBaseUrl}/authorize`,
    response_types_supported: ['code'],
    code_challenge_methods_supported: ['S256'],
    token_endpoint: `${publicBaseUrl}/token`,
    grant_types_supported: ['authorization_code', 'refresh_token'],
    scopes_supported: config.oauth.scopes,
    revocation_endpoint: `${publicBaseUrl}/revoke`,
    registration_endpoint: `${publicBaseUrl}/register`,
  });
});

这段路由必须注册在 app.listen(...) 之前。至于 configapp 怎么导入,要按你的 DevSpace 安装方式处理,不要照抄别人的 Node 目录或包路径。

如果改成包装脚本启动,记得同步更新第一步记录的真实启动命令,然后重新验收本地三个信号。

可选:让 DevSpace 长期常驻

常驻不是 Funnel 的前提。 第一次配置时先手动跑通整条链路,确认没有问题后,再根据系统选择常驻方式。

系统常见方式
Linux / WSLsystemd
macOSlaunchd 或登录项
Windows任务计划程序或系统服务

Linux / WSL 的 systemd 示例:

[Unit]
Description=DevSpace MCP server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=YOUR_USER
WorkingDirectory=/home/YOUR_USER
Environment=DEVSPACE_TOOL_MODE=codex
Environment=DEVSPACE_TRUST_PROXY=1
ExecStart=YOUR_DEVSPACE_START_COMMAND
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target

这里的 UserWorkingDirectoryExecStart 都只是占位符,必须替换成当前机器的真实值。其他平台同样遵守一个原则:自动启动机制只负责执行已经验证成功的 DevSpace 启动命令。

第五步:开启 Funnel 指向 DevSpace

直接把 Funnel 指向 DevSpace 的本地端口:

tailscale funnel --bg --yes 7676

查看状态:

tailscale funnel status

目标结构类似:

https://devspace-device.your-tailnet.ts.net
|-- / proxy http://127.0.0.1:7676

如果命令提示需要在 Tailscale 管理后台启用 Funnel,就按提示完成授权,再回到当前环境重新执行命令。

第六步:验证公网和 OAuth metadata

公网 /mcp 应该返回 401

curl -sS -o /dev/null -w '%{http_code}\n' \
  https://devspace-device.your-tailnet.ts.net/mcp

预期:

401

再验证 OAuth protected resource:

curl -sS -o /dev/null -w '%{http_code}\n' \
  https://devspace-device.your-tailnet.ts.net/.well-known/oauth-protected-resource/mcp

预期:

200

再验证 authorization server metadata:

curl -sS -o /dev/null -w '%{http_code}\n' \
  https://devspace-device.your-tailnet.ts.net/.well-known/oauth-authorization-server/mcp

预期:

200

这一步只是把第四步的三个本地信号搬到公网再验收一次。如果本地已经是 401 / 200 / 200,公网却不是,就只查 Funnel、域名和 Host 校验,不要回头改 DevSpace 业务逻辑。

刚启用 Funnel 时,公网 DNS 可能需要一点时间生效。一次解析失败不代表 Tailscale 安装坏了,先看 tailscale funnel status 是否正确,再继续判断。

第七步:在 ChatGPT 中连接

在 ChatGPT 的自定义 App / MCP 配置中填写:

https://devspace-device.your-tailnet.ts.net/mcp

认证使用 DevSpace 提供的 OAuth 流程。

不要填写:

http://100.x.x.x:7676/mcp
http://127.0.0.1:7676/mcp
https://devspace-device.your-tailnet.ts.net

最后做一次真实调用验收:在 allowedRoots 里创建一个测试文件,让 ChatGPT 读取它。能读到文件,才说明网络、OAuth、DevSpace 权限和项目根目录全部闭环。

常见问题

内网地址

把 100.x IP 填给 ChatGPT

100.x 是 Tailnet 内地址,ChatGPT 默认访问不到。

  • 错误:http://100.x.x.x:7676/mcp
  • 正确:https://devspace-device.your-tailnet.ts.net/mcp
配置项

publicBaseUrl 多写 /mcp

DevSpace 的 publicBaseUrl 只写 Origin,MCP 路径由客户端请求。

  • 错误:https://domain/mcp
  • 正确:https://domain
公网入口

Funnel 没真正打开

本地一切正常,但公网地址不可用时,先看 Funnel 状态和权限。

  • tailscale funnel status 必须显示 Funnel 已开启
  • 首次使用时按提示启用 Funnel 权限
Host 校验

allowedHosts 没加 Funnel 域名

Funnel 已经可达,但 DevSpace 仍可能拒绝外部 Host。

  • 加入 devspace-device.your-tailnet.ts.net
  • 修改后重启 DevSpace
ChatGPT

OAuth metadata 缺路由

公网 /mcp 返回 401,不代表 ChatGPT 的 OAuth discovery 一定完整。

  • 先在本地检查两个 metadata 端点
  • 缺路由时回到第四步处理兼容层
回环地址

Tailscale 和 DevSpace 不在同一环境

宿主机、WSL、虚拟机和容器里的 127.0.0.1 不一定指向同一个地方。

  • 优先把两者放到同一个运行环境
  • WSL 场景尤其不要默认依赖 Windows 到 WSL 转发

最终检查清单

这套方案真正要记住的只有一句:DevSpace 负责 MCP,Tailscale Funnel 负责公网 HTTPS;两者尽量放在同一个运行环境里。 Linux、WSL、Windows 和 macOS 的启动方式可以不同,但验收路径始终是同一条。