把 GSC 和 GA4 接进 AI:NAS 上跑 MCP 的四个坑
让 AI 直接读 Google Search Console(GSC)和 GA4 的数据,用对话做 SEO 复盘、挖关键词。
这篇给出一份在 NAS 上跑通的 compose:两个只读 MCP 服务对外提供 HTTP,局域网内任意 MCP 客户端填个 URL 就能用。费时间的不是部署,是四个坑。
一、先问清楚值不值得做
有 GSC 有 GA4 的人,问”这些数据怎么接到 AI 里”,通常只会得到一句”有 API,去谷歌云申请”。从申请密钥到真能问出结果,中间是四个坑。
值得做的前提是三条都成立:
- 你手上有一台常开的 NAS 或小主机,能跑 Docker
- 你的站在 GSC 和 GA4 里已经有几周数据(新站数据太少,结论不稳)
- 你能接受只在本机客户端里用
最后一条是这套方案的代价,得先说清楚。
只用内网 IP 接不了网页平台
Claude.ai、ChatGPT 这类网页平台,是从它们自己的服务器发起连接的,访问不到你的内网。
能接的是本机客户端:LobeHub 桌面端、Cherry Studio、Claude Code、Cursor、Gemini CLI。
以后要给网页平台用,再加域名和隧道即可,服务本身不用动。
二、四种方案,为什么选 NAS 上的远程 MCP
| 方案 | 做法 | 优点 | 缺点 | 结论 |
|---|---|---|---|---|
| GSC 自带 Gemini | 在 GSC 里用自然语言查询 | 零配置 | 不能跨 GA4,不能换模型 | 太弱 |
| 托管连接器 | 第三方远程 MCP,OAuth 登录 | 配置最少 | 付费,数据过第三方 | 淘汰 |
| 本地 MCP | 每台电脑用 npx / uvx 跑 | 免费 | 每个客户端配一遍,密钥散落各处 | 可用 |
| NAS 远程 MCP | 在 NAS 上跑成 HTTP 服务 | 免费、配一次处处用、密钥只在 NAS | 部署稍麻烦 | 最终选择 |
最终架构一句话:谷歌服务账号(只读)→ NAS 上两个容器 → 局域网内任意 MCP 客户端。
⚠️ 图里那条虚线是这套方案的重点:密钥只落在 NAS 上,客户端那边只填一个 URL,不碰任何凭据。
用到的开源组件:GSC 是社区的 mcp-server-gsc(谷歌至今没有官方 GSC MCP),GA4 是谷歌官方的 analytics-mcp。
两个都是 stdio 服务,要包装成 HTTP:GSC 用 supergateway(node 镜像),GA4 用 mcp-proxy(python)。
三、先定好这几个值
| 占位符 | 是什么 | 怎么定 |
|---|---|---|
NAS的IP | NAS 在局域网里的地址 | 路由器后台或 ip a |
18001 / 18002 | 宿主机端口,GSC / GA4 各一个 | 没被占用就行 |
GOOGLE_PROJECT_ID | 谷歌云项目 ID | 第一步建项目时记下 |
| 代理地址 | 国内访问 googleapis 必须走代理 | 填你自己的,本文写 192.168.1.10:10809 |
| 服务账号邮箱 | 形如 xxx@项目ID.iam.gserviceaccount.com | 建完服务账号自动生成,不用自己想 |
四、动手:四步接通
第一步:拿服务账号密钥
- 打开 console.cloud.google.com,新建项目,记下项目 ID
- 启用 3 个 API:Google Search Console API、Google Analytics Data API、Google Analytics Admin API
- IAM 和管理 → 服务账号 → 创建服务账号,角色一个都不用选
- 进入服务账号 → 密钥 → 添加密钥 → JSON,下载得到密钥文件
那三个 API 都免费,不用绑卡,也不用点”免费试用”。
⚠️ 密钥 JSON 等于密码,不要发到群里。泄露了就删掉这把重新建一把。
第二步:给服务账号授权
把服务账号邮箱加进两个后台。两边的粒度不一样,这点后面会再咬你一次:
| 路径 | 给什么权限 | 粒度 | |
|---|---|---|---|
| GSC | 设置 → 用户和权限 → 添加用户 | 受限 | 每个网站资源都要单独加一次 |
| GA4 | 管理 → 账号访问权限管理 → 添加用户 | 查看者,取消勾选邮件通知 | 在账号级别加一次,旗下所有媒体资源都能读,以后新建的站也自动覆盖 |
权限不用给大。AI 懂分析,但分析只需要读权限,官方 GA4 MCP 本身就是只读的。GSC 想让 AI 帮你提交站点地图可以给”完整”,但绝不要给”所有者”。
第三步:部署到 NAS
只需要填两处:密钥全文和项目 ID。
# 内网 GSC + GA4 MCP,局域网内任意 MCP 客户端通过 URL 接入# 必改:两处 GSA_KEY_JSON(服务账号 JSON 全文)、GOOGLE_PROJECT_ID# 按需改:宿主机端口 18001 / 18002;代理地址(国内访问 googleapis 必须走代理)# 不用改:其余全部# 密钥通过环境变量传入,启动时写到容器内 /tmp/sa.json,不挂载任何文件# 接入地址:http://NAS的IP:18001/mcp(GSC) http://NAS的IP:18002/mcp(GA4)
services: gsc-mcp: image: supercorp/supergateway:latest container_name: gsc-mcp restart: unless-stopped entrypoint: ["sh", "-c"] command: - | printf '%s' "$$GSA_KEY_JSON" > /tmp/sa.json && exec supergateway --stdio "npx -y mcp-server-gsc" --outputTransport streamableHttp --stateful --port 8000 --streamableHttpPath /mcp ports: - "18001:8000" environment: GOOGLE_APPLICATION_CREDENTIALS: /tmp/sa.json HTTP_PROXY: http://192.168.1.10:10809 HTTPS_PROXY: http://192.168.1.10:10809 NO_PROXY: localhost,127.0.0.1 GSA_KEY_JSON: | # 必改:把服务账号 JSON 全文粘在下面,保持缩进 {"type": "service_account", "project_id": "...", "private_key": "...", "client_email": "..."} healthcheck: test: ["CMD", "node", "-e", "require('net').connect(8000,'127.0.0.1').on('connect',()=>process.exit(0)).on('error',()=>process.exit(1))"] interval: 30s timeout: 5s retries: 3 start_period: 60s
ga4-mcp: image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim container_name: ga4-mcp restart: unless-stopped depends_on: - gsc-mcp entrypoint: ["sh", "-c"] command: - | printf '%s' "$$GSA_KEY_JSON" > /tmp/sa.json && exec uvx --with 'mcp<2' mcp-proxy --host=0.0.0.0 --port=8096 --pass-environment -- uvx analytics-mcp ports: - "18002:8096" environment: GOOGLE_APPLICATION_CREDENTIALS: /tmp/sa.json GOOGLE_PROJECT_ID: # 必改:谷歌云项目 ID HTTP_PROXY: http://192.168.1.10:10809 HTTPS_PROXY: http://192.168.1.10:10809 https_proxy: http://192.168.1.10:10809 NO_PROXY: localhost,127.0.0.1 no_proxy: localhost,127.0.0.1 GSA_KEY_JSON: | # 必改:同上,粘同一份 JSON {"type": "service_account", "project_id": "...", "private_key": "...", "client_email": "..."} volumes: - ./data/uv-cache:/root/.cache/uv healthcheck: test: ["CMD", "python", "-c", "import socket;socket.create_connection(('127.0.0.1',8096),3)"] interval: 30s timeout: 5s retries: 3 start_period: 120s两个健康检查都只探 TCP 端口,不调业务接口——健康检查写错的代价不小,Traefik 会直接跳过 unhealthy 的容器,那次排查记在Traefik 一直 404:问题出在健康检查里。
第四步:在客户端接入
以 LobeHub 桌面端为例:设置 → 连接器 → 添加自定义 MCP → 类型选 Streamable HTTP,每次添加一个:
{ "mcpServers": { "gsc": { "type": "http", "url": "http://NAS的IP:18001/mcp" } } }{ "mcpServers": { "ga4": { "type": "http", "url": "http://NAS的IP:18002/mcp" } } }点”测试连接”,能列出 list_sites、search_analytics、run_report 这些工具就说明通了。
到这里已经能用了——AI 已经可以读你的 GSC 和 GA4 数据。下面四节是那份 compose 为什么长这样。
五、坑一:服务账号密钥创建功能已停用
报错里给出了政策 ID:
服务账号密钥创建功能已停用iam.disableServiceAccountKeyCreation原因:你的谷歌账号挂在一个”组织”下面(控制台顶部显示带大楼图标的组织名)。2024 年之后新建的组织默认禁止创建服务账号密钥。个人 @gmail.com 账号没有组织,不会遇到。
处理(只对这一个项目放开,组织其他地方仍然禁止):
- 顶部切到组织 → IAM → 给自己加 Organization Policy Administrator(组织政策管理员)。这个角色必须加在组织层级,加在项目上无效;组织管理员默认也没有它
- 顶部切回项目 → 组织政策 → 搜
disableServiceAccountKeyCreation→ 管理政策 → 覆盖父级政策 → 添加规则 → 强制执行设为”关闭” → 设置政策 - 新版限制也检查一遍,状态是”已实施”就同样处理:
iam.managed.disableServiceAccountKeyCreation- ⚠️ 生效有延迟。 改完立刻去建密钥还是报同样的错,等 10 到 15 分钟再试
更省事的一条路:换一个个人 Gmail 账号去建项目和服务账号。GSC 和 GA4 只认你添加进去的邮箱,不在乎这个服务账号属于哪个谷歌账号。
六、坑二:FileNotFoundError: ‘uvx’
容器起不来,无限重启,日志里只有一行:
FileNotFoundError: [Errno 2] No such file or directory: 'uvx'原因:一开始用的是 mcp-proxy 的官方镜像,它只装了 mcp-proxy 本身,没有 uvx,所以启动不了 uvx analytics-mcp。
处理:换成 uv 官方的 Python 镜像 ghcr.io/astral-sh/uv:python3.12-bookworm-slim。这个镜像自带 uvx,mcp-proxy 在启动时现装,依然不需要 build。
七、坑三:ImportError: cannot import name ‘request_ctx’
容器能起来一下,然后崩:
ImportError: cannot import name 'request_ctx'原因:mcp 这个 Python 库发布了 2.x 大版本,删掉了 mcp-proxy 还在引用的内部函数。uvx 默认装最新版,于是一启动就崩。
处理:uvx --with 'mcp<2' mcp-proxy,把依赖锁在 1.x。
经验:用 latest 或者不锁版本,就要接受上游随时破坏你的服务。遇到 ImportError 这类报错,先查依赖是不是刚发了大版本。
八、坑四:EISDIR: illegal operation on a directory, read
容器跑起来了,一调用工具就报错:
EISDIR: illegal operation on a directory, read意思是”你让我读文件,这却是个目录”。
原因:原本想用部署面板(Coolify)的”文件内容挂载”生成密钥文件,但这个功能没生效。而 Docker 挂载一个不存在的文件时,会自动在宿主机上建一个同名空目录挂进去,程序读到的就是目录。同一台 NAS 上的其他 Coolify 约定记在在 NAS 上用 Coolify 部署自建服务:十条约定。
处理:彻底不挂载文件。密钥 JSON 放进环境变量,启动命令里先用 printf 写成 /tmp/sa.json,再 exec 启动服务。
两个容易漏的细节:
- compose 里的
$要写成$$,否则会被 compose 当成变量替换掉 mcp<2在 shell 里要加引号,否则<会被当成重定向
经验:bind mount 一个文件之前,这个文件必须先存在于宿主机上。这是 Docker 的经典坑。
九、故障速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 建密钥报政策 ID | 组织默认禁止 | 覆盖父级政策,等 10–15 分钟 |
FileNotFoundError: 'uvx' | 镜像里没有 uvx | 换 uv 官方 Python 镜像 |
ImportError: request_ctx | mcp 升到 2.x | --with 'mcp<2' |
EISDIR | 挂载的文件不存在,变成目录 | 密钥走环境变量 + printf |
客户端 fetch failed | 容器没跑起来 | 先看容器日志,不要截”测试连接失败”那张结果页 |
十、可选:建一个专用 SEO 助手
前置条件是上面两个 MCP 都已接通。建一个助手,把这段放进系统提示词,绑定 gsc 和 ga4 两个技能,之后每次提问只要一句话。
你是我的英文站 SEO 分析师。我做 web 出海,目标是挖掘关键词、看懂并接住用户搜索意图。1. 每次先调用 list_sites 确认站点属性名。GSC 数据有 2-3 天延迟,结束日期取 3 天前。2. 搜索数据用 GSC,落地页行为用 GA4(会话数、互动率、平均互动时长),需要时按页面交叉分析。3. 每个关键词都要归类意图:信息型、导航型、交易型、比较型,并判断当前排名页面是否满足该意图。4. 结论必须落到动作:目标 URL、目标关键词、改什么、预期影响,按"改动小、收益大"排序。5. 用中文回答,关键词和文案用英文原文。先给结论和动作,数据表放后面。常用的六个提问:
- 周复盘:对比最近 7 天和前 7 天,列出涨跌最大的词和页面,给出本周要做的 3 件事
- 差一点上首页的词:平均排名 8 到 20 位、展示量排前 30 的词,逐个说明对应页面缺什么,给出新的 title 和 H2
- 高展示低点击:CTR 低于同排名平均水平的词,每个给出 3 版 title 和 description
- 长尾选题:拉 90 天的查询词,把现有页面没覆盖的按主题聚类,给出新页面标题、slug、内链来源
- 关键词互抢:同一个词被多个页面分流时,判断该合并、重定向还是调整侧重点
- 意图验证:GSC 点击前 20 的页面对照 GA4 互动率,找出”点得进来但留不住”的页面
两个限制要知道:GSC 只能看到你已经有排名的词,真正的新词要配合联网搜索,去 Google 自动补全和 Reddit 找;新站数据少的时候,日期范围拉长到 28 天或 90 天,结论才稳。
十一、附:GA4 里新增或删除一个网站
| 要做什么 | 路径和要点 |
|---|---|
| 新增网站 | 管理 → 创建 → 媒体资源(一个网站一个媒体资源,数据不会混)→ 业务目标勾”了解网站流量”和”查看用户互动度和留存率” → 平台选网站 → 拿到 G- 开头的衡量 ID |
| 装衡量代码 | 把 gtag 代码放进所有页面共用的 <head>。Astro 这类框架里 inline 脚本要加 is:inline |
| 验证 | 无痕窗口访问网站,然后看 GA4 的”报告 → 实时” |
| 删除网站 | 切到该媒体资源 → 媒体资源详细信息 → 移至回收站,35 天内可恢复。删之前先看”数据流”确认它没有还在收数据的站 |
| 和 MCP 的关系 | GA4 账号级授权过一次,新媒体资源自动可读;GSC 不一样,每新增一个网站都要把服务账号邮箱再加一次 |
| 中文站注意 | GA4 的脚本在中国大陆基本加载不出来。读者主要在大陆的话,GA4 会严重少算,需要另配国内能用的统计 |
⚠️ 删之前记得去掉网站上的旧代码,否则数据还在往回收站里的资源发。
十二、五条经验
架构和排错
一、先定架构再动手。 本地 MCP 看起来最简单,但每个客户端要配一遍、密钥散落各处。先想清楚”谁来连、从哪里连”,就能避开返工。
二、报错信息就是答案。 上面四个坑,关键信息全写在报错里:政策 ID、找不到 uvx、ImportError、EISDIR。不懂没关系,把完整日志交给 AI 比自己猜快得多。
依赖、权限、节奏
三、依赖要锁版本,挂载要先有文件。 上游一次大版本升级就能让服务瘫掉;Docker 挂载不存在的文件会变成目录,密钥这类小文件用环境变量传更稳。
四、权限最小化。 AI 分析只需要读权限。只读账号加上内网访问,就算哪个环节泄露,最坏后果也只是数据被别人看到。
五、卡住的时候一步一截图。 谷歌云控制台界面复杂,一次性给十步说明很容易迷路。改成”做一步、看一眼、再下一步”之后,推进反而更快。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


