跳到正文
fomoxx.A PERSONAL CORNER OF THE INTERNET

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

AI 工具与 Agent 实践

把本地 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
目标 MCPGoogle 官方 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 的两项核心能力:

  1. 列出 Properties:实际读取到了当前账号有权限访问的 10 个 GA4 properties;
  2. 执行 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 页面里反复删除、重加应用,因为云端重连毫无意义。

此时问题通常还在本机,优先检查以下六点:

  1. MCP 启动命令执行失败;
  2. pipx 不在执行环境的 PATH 中;
  3. OAuth / ADC 凭据不可用或路径错误;
  4. 依赖的环境变量缺失;
  5. 本机代理没有成功带入 Tunnel 进程;
  6. 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 进行对接:

  1. 登录 ChatGPT Business 工作区;
  2. 在工作区设置中开启 Developer Mode
  3. 创建对应的 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 的 readyzapi/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、代理、凭据路径和工作目录。


安全边界与成本核算

安全边界控制

  1. Google Analytics 仅授予只读权限:只使用 analytics.readonly 作用域。即使 ChatGPT 端发生非预期调用,也不具备篡改 GA 配置或删除资产的权限;
  2. 不开放公网入站端口:Tunnel 仅在本机回环地址监听(127.0.0.1:8766),没有在防火墙或路由器上做任何公网映射;
  3. 敏感凭据绝不入库:以下内容均不应写入公开文章、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

常见问题

为什么不能直接在 ChatGPT Web 中调用本地 stdio MCP?

ChatGPT Web 运行在云端,无法直接拉起或执行用户本地电脑上的进程。本地客户端可以直接拉起 stdio 进程,而云端 Web 端必须通过网络反向穿透或主动建立安全通道连接。

为什么使用 OpenAI Secure MCP Tunnel 而不是公网反向代理?

本场景目标非常明确,即在 ChatGPT Web 中调用本地 MCP。OpenAI Secure MCP Tunnel 采用本地主动外联机制,仅在本地监听 127.0.0.1:8766,无需开放路由器端口转发、配置公网证书或维护反代鉴权。

Google 授权提示“此应用已被阻止”该如何解决?

该错误与 MCP 本身无关,属于 Google OAuth 配置问题。应检查 Google Cloud Console 中的 OAuth 同意屏幕、是否将当前账号加入测试用户列表、OAuth Client 类型是否匹配以及申请的 scope 范围是否合理。

为什么 Terminal 手动运行正常,交给 launchd 守护进程却启动失败?

Terminal 交互式 Shell 会自动加载用户配置的环境变量(如完整的 PATH 和 HTTP/HTTPS 代理配置),而 launchd 启动的后台服务拥有最小运行环境,默认不继承交互式 Shell。需在启动脚本中显式声明并导出 PATH、代理变量及凭据路径。

如何判断本地 stdio MCP 与 Tunnel 是否处于健康状态?

在打开 ChatGPT 网页端前,可在本地请求 8766 端口的三个接口进行验证:/healthz 返回 live,/readyz 返回 ready,且 /api/status 显示 main channel 正常、probe_status 为 ok、transport 为 stdio 且命令指向启动脚本。

继续阅读

  1. Codex App 经 CLIProxyAPI 接入中转站报 400(instructions_required)的排障与修复
  2. 企业级 Agent 不一定要多 Agent
  3. AI Agent 协作模式设计