把本地 stdio MCP 接入 ChatGPT Web:OpenAI Secure MCP Tunnel 实战与排障(以 Google Analytics 为例)

如果你手里有一个只能在本机通过 stdio 启动的 MCP Server,又希望在 ChatGPT Web 里直接调用它,通常会遇到一个核心矛盾:本地客户端(如 Claude Desktop 或本地 Agent)可以直接拉起本地进程,但 ChatGPT Web 运行在云端,不可能跨到你的电脑上执行命令。
OpenAI 官方提供的一条打通路径是使用 Secure MCP Tunnel,它可以在不把本地 MCP Server 暴露到公网的情况下连接 ChatGPT Web:
本地 MCP Server(stdio)
↓
OpenAI Secure MCP Tunnel(tunnel-client)
↓
ChatGPT Web / Developer Mode / 自定义 MCP 应用
本文记录我把 Google 官方的 google-analytics-mcp 运行在 macOS 本机,通过 OpenAI Secure MCP Tunnel 接入 ChatGPT Web,并在对话中直接查询 GA4 数据的完整部署过程。
Google Analytics 只是本次使用的具体案例。更关键的是解决一套通用的工程问题:
- 本地 stdio MCP 如何让云端 ChatGPT Web 发现与调用;
- 为什么不需要开放路由器端口转发或配置公网反向代理;
- MCP Server、Tunnel、ChatGPT 工作区三层如何独立验证;
- Google OAuth、环境变量、本地代理与后台守护进程分别会在哪一层出错;
- 如何把终端一次性运行的命令,做成 Mac 重启也能稳定工作的长期常驻服务。
最终验证结果:ChatGPT Web 成功发现该 MCP,正常读取到有访问权限的 GA4 properties,并顺利执行了 Analytics report;本地 Tunnel 也已经交付 macOS launchd 守护常驻。
核心架构与方案取舍
整套系统的拓扑结构如下:
Google OAuth / ADC
↓
Google Analytics MCP(本机 stdio 进程)
↓
OpenAI Secure MCP Tunnel(监听 127.0.0.1:8766)
↓
ChatGPT Web
↓
Google Analytics Admin API / Data API
本地侧仅由 Tunnel 自身在本地回环地址监听:
127.0.0.1:8766
没有配置任何路由器端口转发,也没有将 MCP Server 直接暴露到公网。
为什么不自己做公网反向代理?
要让云端服务访问本地进程,常规思路包括:
- Cloudflare Tunnel;
- Tailscale;
- frp;
- Nginx + 公网 VPS;
- 自己用代码将 stdio 包装为 HTTP / SSE / Streamable HTTP;
- 接入第三方 MCP Gateway。
本次实践没有继续往这些方向叠加基础设施。因为目标非常明确:让 ChatGPT Web 调用本地 MCP。既然 OpenAI 官方已经提供了专门针对该链路的 Secure MCP Tunnel,由本地主动向云端建立连接,本地只需监听 127.0.0.1:8766,就没有必要为了“先把 MCP 公网化”再额外维护一套公网入口、证书配置、访问控制和反代服务。这并不是说其他方式不能用,而是在本场景下没有必要。
环境与依赖
本次实际使用的软硬件环境配置如下:
| 组件 / 依赖项 | 实际环境说明 / 配置详情 |
|---|---|
| 操作系统与架构 | macOS(Apple Silicon) |
| 包管理与运行环境 | Homebrew,Python / pipx |
| 核心工具链 | Google Cloud CLI,OpenAI tunnel-client |
| 目标 MCP | Google 官方 google-analytics-mcp(仓库:googleanalytics/google-analytics-mcp) |
| ChatGPT 运行环境 | ChatGPT Business 工作区(开启 Developer Mode) |
| 本地网络环境 | 依赖本地代理 http://127.0.0.1:7890 |
| 本地工作目录 | ~/local-mcps/google-analytics-mcp(仅为个人目录习惯,可换成任意目录) |
第一步:脱离 ChatGPT,先验证本地 MCP 与 Google 认证
这是整个排障过程中最重要的原则之一:不要一上来就同时调试 Google OAuth、MCP、Tunnel 和 ChatGPT。否则一旦报错,很难分清到底是哪一层出现了问题。
首先进入项目目录,单独启动官方 MCP:
cd ~/local-mcps/google-analytics-mcp
pipx run analytics-mcp
如果本地这一步都不能正常启动,后面接入 Tunnel 没有任何意义。
1. 启用 Google API
在 Google Cloud Console 中,必须确认已启用以下两个 API:
analyticsadmin.googleapis.com(Google Analytics Admin API)analyticsdata.googleapis.com(Google Analytics Data API)
2. 配置 OAuth 与 ADC 凭据
认证采用 Google Application Default Credentials(ADC),并且遵循最小权限原则,仅授予只读作用域:
https://www.googleapis.com/auth/analytics.readonly
也就是说,该 MCP 仅具备读取 Analytics 数据的权限,完全不具备修改 GA 配置的权限。
凭据文件单独存放在项目的私有目录中,并严格收紧文件权限:
chmod 600 <credential-file>
安全注意:切勿将 Google OAuth Client Secret、ADC 凭据文件、Access Token 提交到 Git 仓库,也不要在排障时直接复制粘贴到 AI 对话窗口或输出到普通日志中。
3. 踩坑排查:Google 提示“此应用已被阻止”
首次进行授权时,浏览器弹出了 Google OAuth 的“此应用已被阻止”提示。
遇到此类错误,很容易让人怀疑是 MCP 有问题、API 没开、scope 写错,或者是 Google 不允许第三方 MCP。但最终确认,该错误与 MCP 本身没有直接关系,属于标准的 Google OAuth 配置层问题。
实际需要排查 Google Cloud 的 OAuth 配置:
- OAuth consent screen(OAuth 同意屏幕)配置是否正确;
- 当前授权账号是否已加入“测试用户”(Test users)列表中;
- OAuth Client 类型是否匹配;
- 浏览器当前实际登录的是否为预期的 Google 账号;
- 申请的 scope 是否合理。
在 Google Cloud 后台修正 OAuth 配置后重新授权即可通过,MCP 本身完全不需要做任何修改。这也明确了后续的排障准则:身份认证失败,直接在认证层解决,不要先去改动 MCP。
4. 本地 MCP 功能验证
认证完成后,脱离 Tunnel 在本地直接验证 Google Analytics MCP 的两项核心能力:
- 列出 Properties:实际读取到了当前账号有权限访问的 10 个 GA4 properties;
- 执行 Report:成功运行了一次
activeUsers指标的 report 查询。
在本地测试时,曾有一次 report 查询返回了 0 行数据。但请注意:对于数据类 MCP,“接口成功返回空结果”与“调用失败”是两种完全不同的状态。 只要 MCP 工具调用成功、Google API 正常返回响应,就说明链路已经成立。返回 0 行只代表当前查询条件(如日期区间、属性、指标)下没有产生数据,应继续检查日期范围、property、dimension 或 metric,而不是怀疑环境并推倒重装 MCP。
本地验证通过后,再开始接 Tunnel。
第二步:使用 OpenAI Secure MCP Tunnel 桥接 stdio 进程
本地 MCP 的通信形态是标准输入输出(stdio),而云端的 ChatGPT Web 无法直接调用本地进程。OpenAI Secure MCP Tunnel 在这里的定位是:由本机主动向云端建立连接,把一个本地 MCP Server 安全地桥接给 ChatGPT。
在本地 Tunnel Profile 中,最终配置指向一个本地启动脚本:
~/local-mcps/google-analytics-mcp/scripts/run-analytics-mcp.sh
该脚本内部执行的依然是官方原生物件:
pipx run analytics-mcp
这里没有 fork Google 官方的 MCP 仓库,也没有修改其内部任何业务逻辑。
启动后,Tunnel 仅在本地回环地址监听:
127.0.0.1:8766
因此整套部署不需要在路由器、防火墙或云服务器上额外开放任何公网入站端口。
第三步:检查 Tunnel 自身健康状态(排障分水岭)
启动 Tunnel 后,不要急着打开 ChatGPT 网页端。先在本地调用三个检查接口,确认 Tunnel 自身是否健康:
curl -fsS http://127.0.0.1:8766/healthz
curl -fsS http://127.0.0.1:8766/readyz
curl -fsS http://127.0.0.1:8766/api/status | jq
最终验证确认的健康指标为:
healthz返回live;readyz返回ready;/api/status接口中显示:- main channel 状态正常;
probe_status=ok;- transport 为
stdio; - MCP command 正确指向自己的启动脚本。
这三个检查接口构成了关键的排障分水岭。如果本地接口都没有返回 ready,千万不要去 ChatGPT 页面里反复删除、重加应用,因为云端重连毫无意义。
此时问题通常还在本机,优先检查以下六点:
- MCP 启动命令执行失败;
pipx不在执行环境的 PATH 中;- OAuth / ADC 凭据不可用或路径错误;
- 依赖的环境变量缺失;
- 本机代理没有成功带入 Tunnel 进程;
- MCP 启动后立即异常退出。
第四步:踩坑排查——终端里能跑,后台进程为什么起不来?
在 macOS 上,这是一类极具迷惑性的典型问题:在 Terminal 手动执行完全正常,但交给后台服务(如 launchd)自动启动就失败。
这是因为:在 Terminal 里手动运行 MCP 或 Tunnel 时,交互式 Shell 环境通常已经加载了完整的 PATH、代理等环境变量;但交给后台守护进程后,执行环境完全不同,并不会默认继承交互式 Shell 的全部变量。
我的本机网络环境访问 Google 必须依赖本地代理:
http://127.0.0.1:7890
当后台进程丢失代理变量或找不到 PATH 时,就会出现“手动正常、自动失败”。
遇到这种情况,优先排查后台服务实际拿到的运行时环境:
PATH;HTTP_PROXY/HTTPS_PROXY;- Google credential 相关环境变量;
- 工作目录(Working Directory);
pipx/ Python 的绝对路径。
实践对策:对于需要长期运行的 MCP,不要假设后台服务会继承交互式 Shell。更稳妥的做法是在 run-analytics-mcp.sh 启动脚本中,显式声明并导出真正需要的运行环境与变量。
第五步:在 ChatGPT Web 中连接与端到端验收
当本地 MCP 与 Tunnel 均完成独立验证后,进入 ChatGPT Web 进行对接:
- 登录 ChatGPT Business 工作区;
- 在工作区设置中开启 Developer Mode;
- 创建对应的 MCP 应用,填入 Tunnel 连接信息。
首次执行 discover(发现工具)时并不是完全顺滑:过程中曾出现过 discover 耗时较慢、控制台打印 server / discover stderr 等现象。但最终 ChatGPT 成功发现了工具列表,因此我并没有去修改 Google 官方 MCP 的代码。
在 ChatGPT Web 对话框中,完成了端到端功能验证:
- 能正常发现并列出 Google Analytics MCP;
- 能成功读取当前账号下的 GA4 properties;
- 能成功执行 Analytics report 查询。
至此整条链路才算真正闭环:
ChatGPT
↓
Secure MCP Tunnel
↓
Mac 本地 MCP
↓
Google Analytics API
真正的工程验收标准:不是“Tunnel 显示在线”就算完成,也不是“本地 MCP 能启动”就算完成,必须以 ChatGPT 在实际对话中成功调用一次目标工具为准。
第六步:配置 launchd 守护进程,实现常驻与容灾
在终端里手动开窗口运行 Tunnel 只适合测试验证。如果想在 ChatGPT Web 中长期稳定使用,本地 Tunnel 必须做到:
- 系统登录后自动在后台启动;
- 进程异常退出后自动恢复;
- Mac 重启后不需要手工再开一个终端窗口。
最终方案是将其交给 macOS 原生的 launchd 守护进程管理。
- 服务标识:
com.m1.google-analytics-mcp-tunnel - plist 配置文件路径:
~/Library/LaunchAgents/com.m1.google-analytics-mcp-tunnel.plist
核心配置项包括:
RunAtLoad:加载时运行;KeepAlive:保持存活;ThrottleInterval:重启节流间隔。
日常管理与排查命令:
# 检查守护服务运行状态
launchctl print gui/$(id -u)/com.m1.google-analytics-mcp-tunnel
# 手动强制重启服务
launchctl kickstart -k gui/$(id -u)/com.m1.google-analytics-mcp-tunnel
容灾恢复实测
这里不是“写了一个 plist,理论上应该能自启”,而是实际测试过它会恢复:主动终止原来的 tunnel-client 进程,launchd 随后拉起了新的 PID,healthz / readyz 恢复正常。
体系化排障顺序(四层检查法)
整条链路包含四层结构:
1. 数据源 / OAuth
↓
2. MCP Server
↓
3. Secure MCP Tunnel
↓
4. ChatGPT Web
一旦调用出现问题,建议固定按照以下自底向上的顺序逐层排障,避免“ChatGPT 没看到工具就全部推倒重装”:
1. 数据源与认证层
Google Analytics 场景重点检查:
- Admin API 与 Data API 是否启用;
- Google OAuth 是否授权成功;
- 本地 ADC 凭据是否有效;
- 申请的 scope 是否正确(只读)。 (若迁移到其他 MCP,则对应检查 API Token、OAuth 刷新机制或数据库连接凭据。)
2. MCP Server 层
脱离 Tunnel,在本地终端单独运行命令,确认:
- Server 进程能够正常启动并驻留;
- tools 列表能够被正常列出;
- 至少有一项真实的工具调用在本地执行成功。
3. Tunnel 层
检查本地三个端点:
healthz是否为 live;readyz是否为 ready;api/status是否正常。 重点判断是 Tunnel 本身网络通信失败,还是它在拉起底层 MCP 进程时失败。
4. ChatGPT Web 层
最后检查云端与界面配置:
- Developer Mode 是否处于开启状态;
- 当前 Business 工作区权限是否匹配;
- MCP 应用是否处于已连接状态;
- 当前对话入口是否能看到该应用;
- discover 工具发现流程是否顺利通过。
几个实际遇到与容易误判的高频问题
1. ChatGPT 找不到工具
不要只盯着 ChatGPT 的前端界面。优先在本地终端检查 Tunnel 的 readyz 和 api/status。如果本机都没有进入 ready 状态,在 ChatGPT 侧如何重试、重加应用都毫无意义。若本地完全正常,再去检查 Developer Mode、工作区权限和应用连接状态。首次 discover 工具耗时可能明显慢于后续常规调用。
2. report 返回 0 行
返回 0 行不等于 MCP 发生故障。对于数据类 MCP,“接口成功返回空结果”与“调用失败”是两种完全不同的状态。此时应先调整日期范围,或更换查询的 property、metric 与 dimension,切勿盲目重新安装 MCP。
3. Mac 重启后失效
优先检查:
launchctl print gui/$(id -u)/com.m1.google-analytics-mcp-tunnel
再看:
curl -fsS http://127.0.0.1:8766/healthz
curl -fsS http://127.0.0.1:8766/readyz
curl -fsS http://127.0.0.1:8766/api/status | jq
不要第一反应就重新生成 Google 凭据。
4. 手动运行正常,launchd 不正常
典型的环境差异问题。检查 PATH、代理、凭据路径和工作目录。
安全边界与成本核算
安全边界控制
- Google Analytics 仅授予只读权限:只使用
analytics.readonly作用域。即使 ChatGPT 端发生非预期调用,也不具备篡改 GA 配置或删除资产的权限; - 不开放公网入站端口:Tunnel 仅在本机回环地址监听(
127.0.0.1:8766),没有在防火墙或路由器上做任何公网映射; - 敏感凭据绝不入库:以下内容均不应写入公开文章、Git 仓库或普通排障日志中:
- Google OAuth Client Secret;
- ADC 凭据 / Refresh Token;
- OpenAI Runtime API Key;
- 本地代理认证信息;
- 真实账号信息与内部资源 ID。 在博客公开发布前,应对本机用户名、完整私有路径、Tunnel ID、GA Property ID 等环境特征进行必要泛化与脱敏。
费用与基础设施开销
- Google Analytics Admin API 与 Data API 均受调用配额限制;
- 本次方案没有额外引入 BigQuery、Cloud Run、公网 VPS 或第三方 MCP SaaS 托管服务;
- 因此本次部署的主要门槛不是额外基础设施成本,而是 ChatGPT 工作区与 Secure MCP Tunnel 的使用权限。
方案向其他 stdio MCP 的迁移复用
如果目标 MCP 本身就是一个可以远程访问的 HTTP / Streamable HTTP MCP,那么未必需要再套本地 Tunnel。
这套方案真正适合复用的标准场景是:
本地命令
↓
stdio MCP Server
↓
需要让 ChatGPT Web 调用
将本方案迁移到其他 MCP 时,Google Analytics 相关部分替换掉即可:
Google OAuth / GA API
换成:
GitHub Token / 数据库凭据 / 本地文件 / 私有 API / 其他 OAuth
而后面的逻辑基本相同:
先验证 MCP
↓
再验证 Tunnel
↓
再接 ChatGPT
↓
最后做守护与安全收尾
这比把所有组件一次性装完再猜哪里出错,稳定得多。
最终检查清单
- 使用官方 Google Analytics MCP,而不是第三方 fork
- Google Analytics Admin API 已启用
- Google Analytics Data API 已启用
- ADC 只授予
analytics.readonly权限 - MCP 在本地脱离 Tunnel 单独调用成功
- 实际读取到 GA4 properties
- 实际执行过 Analytics report 查询
- Tunnel
healthz端点返回 live - Tunnel
readyz端点返回 ready -
api/status中 MCP probe 确认正常 - ChatGPT Web 实际发现并调用 MCP 工具
-
launchd服务自动启动配置完成 -
launchd崩溃重启自愈已实测通过 - 没有开放任何公网入站端口
- 凭据和私有密钥没有进入公开文档或 Git
总结与工程思考
这次部署真正花时间的部分,并不是 Google Analytics 的数据查询本身。
在本地启动一个官方 MCP 很简单;真正容易消耗大量工程时间的是:
- OAuth 流程与最小权限的划分;
- stdio MCP 与云端 ChatGPT 之间的安全桥接;
- Tunnel 与 MCP 的生命周期管理;
- 交互式终端环境与后台守护进程环境的不一致;
- ChatGPT 工作区权限配置与工具发现机制;
- 掌握每一层的独立验证方式,而不是一遇到报错就推倒重来。
因此,如果仅仅把这次经历写成一篇《Google Analytics MCP 安装教程》,反而会遗漏掉其中最有复用价值的技术沉淀。更准确地说,这是一篇把本地 stdio MCP 接入 ChatGPT Web,并将其工程化为长期稳定可用服务的实战记录。Google Analytics 只是跑通整条链路所使用的具体范例。
参考资料
- Google Analytics MCP
- OpenAI Secure MCP Tunnels
- OpenAI tunnel-client