这是一个面向 ChatGPT 网页版 的 Ubuntu / Docker Compose 部署包。它采用统一身份验证,但不会把多个 MCP 的工具集合并成一个大列表。
flowchart TD
C[ChatGPT 网页版] --> T[Cloudflare Tunnel]
T --> G1[网关:agent-browser :8101]
T --> G2[网关:未来 MCP :8102]
G1 --> A[私有 HTTP 适配器]
A --> S[agent-browser stdio]
S --> B[远程 CDP 浏览器]
G2 --> H[未来原生 HTTP MCP]
每个外部域名对应一个独立 FastMCP 进程、端口、OAuth Resource Indicator 和 ChatGPT 连接。统一的是代码、WorkOS 租户和部署单元,不是工具命名空间。
本项目使用 Docker Hub 上的两个应用镜像:
mahufong/mcp-gatewaymahufong/agent-browser-adapter两个镜像都发布 linux/amd64 与 linux/arm64。发布时必须协调使用同一个标签:
latest 是同一工作流为两个镜像依次更新的移动标签;Docker Hub 无法跨两个镜像仓库原子移动标签。sha-<12-character root commit> 是当前仓库根提交 SHA 的前 12 位,例如 sha-0123456789ab,用于精确标识一次双镜像发布。发布工作流把 SHA 标签当作写一次的不可变发布:两个 SHA 标签都不存在时才构建;两个都已存在时只重新校验并复用;如果只存在其中一个则视为半发布并直接失败,不会覆盖现存 SHA 标签。开发或临时环境可以使用 latest。生产 Compose 或 Kubernetes 应固定同一个 SHA 标签,不能让两个服务使用不同提交,也不能用 latest 作为生产回滚依据。
镜像由 .github/workflows/mcp-gateway.yml 构建和发布。CI 会运行网关/适配器 smoke 检查,并检查每个发布标签是否包含 Linux amd64 和 arm64 manifest。工作流只发布镜像,不执行生产部署;smoke 使用临时测试配置,不需要 WorkOS、Cloudflare Tunnel、CDP 或其他生产 Secret。
截至 2026-09-01,OpenAI 当前帮助页把完整 MCP / 自定义 App 的正式可用范围写为 Business、Enterprise/Edu;但 OpenAI 支持人员也曾说明部分 Plus/Pro 个人账户可在 Developer Mode 创建或安装自定义 MCP。Plus 的实际入口存在账户与灰度差异。
部署前请在 ChatGPT 网页版确认同时能看到:
Settings → Apps → Advanced settings → Developer mode;Settings → Apps → Create(或等价的“创建自定义 App/MCP”入口)。两项都有即可继续。若没有 Create,服务端部署仍然有效,但无法靠修改 FastMCP、WorkOS 或 Cloudflare 绕过 ChatGPT 账户侧限制;需要等待账户开放、改用支持远程 MCP 的其他客户端,或升级到有正式支持的套餐。
参考:OpenAI 当前说明;Plus/Pro Developer Mode 支持回复。
| 项目 | 选择 |
|---|---|
| 公网地址 | https://mcp-agent-browser.mahufong.top/mcp |
| 入口 | Cloudflare Tunnel,不开放主机端口 |
| OAuth | WorkOS AuthKit,由外层网关统一验证 |
| 用户限制 | JWT sub 精确白名单,默认只允许一个 WorkOS 用户 |
| FastMCP | 固定为已验证的 4.0.5;Gateway 直接使用的 mcp==2.2.0、mcp-types==2.2.0、httpx2==2.13.0 同样显式锁定;升级时先跑完整 CI/smoke |
| agent-browser | 发布镜像固定为 0.34.0;升级需修改构建版本并由 CI 重新发布 |
| cloudflared | 固定为 2026.8.2,稳定后再固定镜像 digest |
| stdio 转换 | 与 agent-browser 同一容器内转换为私有 Streamable HTTP |
| CDP URL | 由 .env 的 AGENT_BROWSER_CDP 传给 agent-browser;优先填 http(s) DevTools 端点 |
| 浏览器状态 | 由远程浏览器服务维护;适配器不安装 Chrome、不保存 profile |
| 浏览器并发 | 单并发,避免多个调用同时操作同一浏览器 |
建议 Ubuntu 22.04 或 24.04,并安装 Docker Engine 与较新的 Compose plugin(需要支持 gw_priority,建议 Compose 2.33.1+)。确认:
docker version
docker compose version
将本目录复制到主机,例如 /opt/personal-mcp-gateway,然后:
cd /opt/personal-mcp-gateway
cp .env.example .env
chmod 600 .env
nano .env
填写 WorkOS、Cloudflare 和完整 CDP URL。不要把 .env 提交到 Git。
AGENT_BROWSER_CDP 接受两种形式,优先选择前者:
# 自建浏览器:DevTools HTTP 端点,浏览器重启后依然有效
AGENT_BROWSER_CDP='http://browser-host.example:9222'
# 托管浏览器服务:服务商签发的完整 WebSocket 地址
AGENT_BROWSER_CDP='wss://provider.example/devtools/browser/your-current-id?token=...'
ws://host:9222/devtools/browser/<uuid> 里的 UUID 由 Chrome 在每次启动时重新生成,所以浏览器一重启该地址就失效。http(s) 端点只含主机和端口,agent-browser 会在连接时请求 /json/version 取得当前的 WebSocket 地址,因此浏览器重启后不需要改配置、也不需要重建容器。Kubernetes 的一次性 startupProbe 会执行 /app/warmup.py,经 127.0.0.1:8000/mcp 调用只读的 agent_browser_get_url,从而预热 adapter 的 FastMCP provider、常驻 stdio MCP 链路、agent-browser daemon 和浏览器/CDP。warmup 的本地 HTTP session 会在调用后关闭,但模块级 StdioTransport(keep_alive=True) 会保留其 agent-browser 子进程。之后的周期性 healthcheck 只检查本地监听和 http(s) CDP 端点,不会创建 MCP/SSE 会话。MCP tools/list 协议检查只在 CI smoke 和部署验收时执行,见 adapters/agent-browser/healthcheck.py。
托管服务通常只提供带 token 的 wss 地址,那就照原样填写。完整 CDP URL 往往包含凭据,建议使用单引号,避免其中的 $、# 或 query string 被 Compose 误解析。
修改这个值之后,使用 registry-first 流程重新创建适配器;单纯 docker compose restart 不会重新读取环境变量:
docker compose pull
docker compose up -d --no-build --force-recreate agent-browser-adapter
docker compose up -d --no-build mcp-gateway
工具范围也由 .env 控制:AGENT_BROWSER_TOOLS=core 是较小的默认列表;你可以自行改为 all,或使用逗号分隔的组合,例如 core,network,state,tabs。修改后同样重新创建适配器。
在 WorkOS Dashboard 中完成:
https://xxx.authkit.app 的 AuthKit Domain。https://mcp-agent-browser.mahufong.top/mcp。首个 MCP 可设为默认 Resource。user_... ID,填入 ALLOWED_WORKOS_SUBS。网关按 JWT 的 sub 精确比较,不以邮箱判断。scopes_supported 包含 offline_access,且授权会签发 refresh token;否则 ChatGPT 的登录会在 access token 到期后失效。以后每增加一个域名,都要增加对应的完整 Resource Indicator,例如 https://mcp-notes.mahufong.top/mcp。WorkOS 只能有一个默认 Resource;多个 MCP 时必须确认 ChatGPT 对每个连接正确发送 resource 参数。
创建一个 remotely-managed Tunnel,把 token 写入 .env。在 Tunnel 的 Public Hostnames 中添加:
| Public hostname | Service |
|---|---|
mcp-agent-browser.mahufong.top | http://mcp-gateway:8101 |
这里使用 mcp-agent-browser.mahufong.top,它只比 mahufong.top 多一级,通常可由 Cloudflare Universal SSL 覆盖。agent-browser.mcp.mahufong.top 多两级,默认通配证书通常不覆盖。
不要在该 hostname 前再启用 Cloudflare Access 登录,否则会与 WorkOS OAuth 形成 ChatGPT 无法自动完成的双重认证。应关闭缓存、JS Challenge 和交互式 Bot Challenge;WAF 可保留不需要浏览器挑战的规则。
生产或普通运行主机直接拉取已发布镜像,禁止通过启动命令触发本地构建。生产环境先将两个服务设为同一个固定 SHA 标签:
export MCP_GATEWAY_IMAGE=mahufong/mcp-gateway:sha-0123456789ab
export AGENT_BROWSER_ADAPTER_IMAGE=mahufong/agent-browser-adapter:sha-0123456789ab
不需要固定版本时可省略这两个变量,Compose 默认使用 latest。无论使用哪种标签,都必须先拉取再启动:
docker compose config
docker compose pull
docker compose up -d --no-build
docker compose ps
docker compose logs --tail=100 mcp-gateway agent-browser-adapter cloudflared
pull 必须先执行,up --no-build 用来确保启动失败时不会回退到本地构建。生产环境不要使用 latest 作为回滚依据。
Compose 没有发布任何 ports:。公网流量只能由 cloudflared 经私有 edge 网络进入。
Kubernetes 部署额外提供一个直连内网入口:mcp-gateway-internal NodePort(固定端口 30101)。在节点的 192.168.192.x 地址上访问 http://<节点内网IP>:30101/mcp 时,若配置了 MCP_INTERNAL_CIDRS=192.168.192.0/24,请求不会经过 Cloudflare,也不需要 OAuth。NodePort 必须由节点防火墙限制为该网段,不能发布到公网;公网域名仍始终要求 OAuth。
未带 token 访问 MCP 端点应得到 401,而不是 404 或 502:
curl -i -H 'Accept: application/json, text/event-stream' \
https://mcp-agent-browser.mahufong.top/mcp
OAuth Protected Resource Metadata 应能访问:
curl -fsS \
https://mcp-agent-browser.mahufong.top/.well-known/oauth-protected-resource/mcp
如果 WorkOS 登录成功、但 ChatGPT 看不到工具,先检查:
docker compose logs --tail=200 mcp-gateway agent-browser-adapter
常见原因是 Resource Indicator 与 /mcp 完整 URL 不一致、ALLOWED_WORKOS_SUBS 不是 access token 的 sub、AGENT_BROWSER_CDP 已过期、上游 CDP 握手失败、后端 stdio 启动失败,或 Cloudflare 对流式响应做了挑战/缓存。
Gateway 和 adapter 使用同一个服务端生成的 request_id 输出 payload-free JSON 事件:
mcp_http_request:auth_phase_ms、first_byte_ms、total_ms;mcp_tool_call:queue_ms、upstream_ms(包含通过工具级 subject 检查后的 Gateway→adapter 往返和下游执行;FastMCP 偶发的工具元数据预取只计入 HTTP total_ms);mcp_adapter_request:adapter_total_ms;agent_browser_tool_call:agent_browser_ms(stdio、agent-browser 和 CDP 阶段);agent_browser_queue_timeout:adapter 的单浏览器锁等待超过 MCP_QUEUE_TIMEOUT_SECONDS 时记录 queue_ms,防止 public/internal 两个 Gateway listener 的请求在 adapter 层无限排队。日志不会记录 bearer token、JWT、query、工具参数/结果、响应正文或客户端 IP 明文。origin_connection_id 是进程内加盐后的 origin peer 标识,只能用来判断 cloudflared 到 Gateway 的连接是否复用;Cloudflare 在边缘终止公网 TLS,因此该字段不能证明 ChatGPT 到 Cloudflare 的 TCP/TLS 复用。GET/SSE 长连接应看 first_byte_ms,不要把连接存续时间对应的 total_ms 当作工具执行时间。
在 ChatGPT 的 Developer Mode 中创建一个自定义 MCP 连接:
Agent Browserhttps://mcp-agent-browser.mahufong.top/mcp完成 WorkOS 登录和授权后刷新工具列表。每个后续 MCP 也单独创建连接,因此它们在 ChatGPT 中保持分开。
# 状态与日志
docker compose ps
docker compose logs -f --tail=100
# 修改 config/services.json 后重新创建网关容器
docker compose up -d --no-build --force-recreate mcp-gateway
# 修改 CDP URL 或 AGENT_BROWSER_TOOLS 后拉取并重建适配器容器
docker compose pull
docker compose up -d --no-build --force-recreate agent-browser-adapter
docker compose up -d --no-build mcp-gateway
# 发布了新的 latest 镜像后更新两个服务
docker compose pull
docker compose up -d --no-build --force-recreate mcp-gateway agent-browser-adapter
# 停止网关;远程浏览器状态不在本项目中
docker compose down
Kubernetes 不应使用本目录 Deployment 中的本地占位镜像或 latest。请按照 k8s/README.md 为网关和适配器设置同一个 sha-<12-character root commit> 标签,并通过集群 Secret 注入 WorkOS、CDP 等运行时凭据。CI 不会访问生产集群,也不会需要这些凭据。
adapters/agent-browser 的结构,让转换器与 stdio 进程待在同一个隔离容器,再把私有 HTTP 地址交给网关。0.34.0,避免直接采用刚发布的版本;升级时修改 Dockerfile/工作流中的构建版本并通过 CI 发布新的双镜像标签。.env 中的 AGENT_BROWSER_VERSION 只影响贡献者本地构建。http://host:9222 这类 DevTools 端点,浏览器重启后无需改动;托管服务填其签发的 wss 地址,该地址变更后需要重新创建 agent-browser-adapter 容器。core;你可在 .env 改为 all 或按组组合。full.json,即不额外禁用 eval/upload/download;如以后想收紧,可改用 files.json 或 safe.json。allowed-domains;浏览器实际能访问哪里,由远程浏览器所在网络决定。贡献者可在本目录运行静态检查和基于 uv 隔离环境的单元测试:
./validate.sh
Docker 镜像构建、容器启动和 MCP smoke test 统一由 .github/workflows/mcp-gateway.yml 在 GitHub Actions 中完成;生产主机必须使用上面的 Docker Hub 拉取流程。
Content type
Image
Digest
sha256:895ce6cc6…
Size
168.7 MB
Last updated
24 days ago
docker pull mahufong/agent-browser-adapter