用 OpenCode 自动审计 health-mcp 时,它言之凿凿地列出了 VolumeCache、BuildAndRunConfiguration,可仓库里根本没有。这次改用 TaoToken 作统一 API 通道,注册和创建 Key 在 TaoToken 完成,再把 Codex 的 Base URL 填成 https://taotoken.net/api,按“逐文件核查、不采信幻觉”的纪律把项目重新审计一遍。
health-mcp 不是空仓库。它有容器编排、环境变量、路由、MCP Server 实现、SQLite 数据层,任何模型如果没有按文件读取就直接给结论,都会把“看起来合理”的东西当成“读取到的事实”。OpenCode 那次审计就是教训:端口号从 5012 到 8080 列了一大串,实际上服务只监听 127.0.0.1:7777;Dockerfile 内容也是编的。所以这次让 Codex 干活时,我要求它每一步都先把文件原文读出来再下结论,“是否存在某个组件”一律回到仓库用 grep 验证。模型通道统一之后,长会话里连续读十几个文件也不用担心 Key 额度在多个平台之间来回跳。
1. 审计原则:不采信自动生成的组件名
1.1 OpenCode 那轮审计留下的警示
上一轮用 OpenCode 自动审计 health-mcp,生成的报告里出现了几个明显能“验证失败”的东西。
首先是 VolumeCache 和 Penny FabergXCube-LiteFS 这类组件名。在 Node.js + Hono 的项目里,缓存组件通常挂在服务初始化阶段,但 health-mcp 的实际仓库根本没有这个模块。其次是 BuildAndRunConfiguration,一个听起来很像 IDE 或构建工具会生成的配置文件,仓库里同样没有。然后是端口号:5012、6388、5001、8080,四个端口四个说法,而 docker-compose.yml 里真实暴露的就是 7777。最后是 Dockerfile 内容,它甚至“还原”了一段不存在的构建步骤。
这几个问题说明一件事:模型在长上下文里做全局总结时,会把上下文里“顺理成章”的东西补全成“事实”。7B 模型尤其明显,参数规模限制了它记住精确文件名的能力。逐文件、逐模块核查,是唯一能把这类幻觉挡在结论之外的办法。
1.2 逐文件核查,不是让模型做“项目裁判”
很多审计场景里,开发者习惯把整个目录丢给模型:总结架构、找问题、给结论。结果就是模型读了一堆文件之后,把 A 文件里的变量名安到 B 文件头上,再把 C 文件里根本没有的函数补进架构图。这个教训在项目越大时越明显。health-mcp 不算大,但同样中招。
所以操作上反过来:Codex 只读取指定文件,读完一个文件就贴出这个文件里真实存在的内容,并标注“哪一行是事实、哪一行是推断”。一旦 Codex 在某个环节开始补全,比如擅自提到某个表不存在于 data.db,就立刻让它停住,回到 SQLite 文件本身去验证。把模型当“阅读辅助工具”,而不是“项目裁判”,审计结论才不会变成幻觉的集合。
1.3 五步审计 SOP
审计顺序固定为五步,每步检查对象和目的如下:
| 步骤 | 检查对象 | 目的 |
|---|---|---|
| 第一步 | docker-compose.yml 与 Dockerfile | 确认服务构成、容器化方式、真实端口 |
| 第二步 | .env 及相关环境变量 | 梳理数据库连接、OAuth 凭证、API Key 等配置项 |
| 第三步 | app/routes/ 与路由定义 | 盘点对外 HTTP 接口与 REST 镜像能力 |
| 第四步 | MCP Server 实现 | 理解工具的定义、注册和传输层机制 |
| 第五步 | SQLite 数据层 | 确认存储文件、表结构和凭据分离策略 |
这个 SOP 的顺序有讲究:先看容器怎么跑,再看配置有什么依赖,然后看 HTTP 层暴露了哪些能力,再往下进 MCP 层,最后落到数据存储。每步都在前一步确认的事实基础上继续,不给模型跨章节“脑补”的机会。
2. 给 Codex 接一条可复用通道:TaoToken 的 Key 与 Base URL
2.1 先拿 Key,再定模型 ID
准备材料只有三样:一个 TaoToken 账号、一把 API Key、一个模型 ID。注册和创建 Key 都在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 完成,登录后进控制台,创建一把 Key,复制出来备用。模型 ID 不用背,打开同一页面上的模型广场,以当时列表为准就好,不要凭记忆填带日期的后缀,那种 ID 大多是幻觉源头之一。
这里需要说明一下为什么走统一通道:审计一个项目的 MCP Server,通常要在一个会话里连续读十几个文件,每个文件可能都要追问两三轮。如果 Key 分散在不同平台,来回切换的成本比模型本身的成本高得多。TaoToken 把 Base URL 收敛成一个地址,Key 也只用一把,Codex 的长会话就能一直跑下去。
2.2 修改 ~/.codex/config.toml
Codex CLI 的配置在用户目录下的 ~/.codex/config.toml。把模型供应商指向 TaoToken 的方式如下:
model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY"三个要点:
- model 的值 YOUR_MODEL_ID 是占位符,实际值去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场挑一个支持 Codex 会话的模型。
- base_url 填 https://taotoken.net/api,末尾不要加 /v1。很多配置错误都出在这一步:加上 /v1 之后请求路径整体前移,接口路径对不上,返回 404。
- env_key 告诉 Codex 从哪个环境变量读取密钥。配置完导出一次:
export TAOTOKEN_API_KEY=YOUR_API_KEYYOUR_API_KEY 换成你在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建的那把真实 Key。如果换了终端,记得重新 export,或者写进 shell 的 profile 文件。不同版本的 Codex CLI 对 model_provider 字段名略有差异,保存后可用 codex --version 的对应配置示例核对一遍。
2.3 验证通道:先列文件,不要先下结论
配置保存后,先跑一个最小验证,确认 Codex 能通过 TaoToken 正常调用。在 health-mcp 项目根目录启动 Codex,给它一个足够简单的任务:
列出当前目录下的文件与文件夹,只输出 ls -la 能看到的内容,不要推测任何不存在的文件。正常的话,Codex 会把仓库的顶层结构列出来,不会出现想象中的文件。这一步通过,说明 Base URL、Key、模型 ID 三样都没问题,可以进入逐文件审计了。
3. 五步 SOP 逐文件核查 health-mcp 的实际产出
3.1 第一步:docker-compose.yml 和 Dockerfile 确定服务构成
先让 Codex 读取 docker-compose.yml,把 services、ports、volumes 三段原样贴出来,再对照 Dockerfile 的 FROM 和 COPY 行。health-mcp 的真实构成是一个 Node.js 单体应用,框架是 Hono,一个进程同时承载四类职责:MCP 传输层(/mcp)、REST 镜像接口(/api/)、仪表盘 SPA(/)、OAuth 回调(/auth/)。默认监听地址是 127.0.0.1:7777。
Codex 在这一步常犯的问题,是把“可能的端口”和“实际暴露的端口”混在一起。上一轮审计出现的 5012、6388、5001、8080 就是这样来的。处理办法很直接:问 Codex“docker-compose.yml 里 ports 段逐行列出来”,它列出的每一条都要能在文件里找到对应行。多出来的端口,一律删除,不进入架构文档。
3.2 第二步:.env 和环境变量核对
接着读 .env.example 或者仓库里的环境变量引用。health-mcp 的配置项集中在这一组:
| 环境变量 | 用途 |
|---|---|
| HEALTH_MCP_PORT | HTTP 服务端口,默认 7777 |
| HEALTH_MCP_HOST | 绑定地址,默认 127.0.0.1 |
| HEALTH_MCP_TOKEN | Bearer 认证令牌,未设置时仅限本机访问 |
| HEALTH_MCP_DATA_DIR | 数据目录,默认 ~/.health-mcp |
| HEALTH_MCP_WHOOP_CLIENT_ID / SECRET | Whoop OAuth 凭证 |
| HEALTH_MCP_OURA_CLIENT_ID / SECRET | Oura OAuth 凭证 |
| HEALTH_MCP_USDA_API_KEY | USDA 食品数据 API 密钥 |
| HEALTH_MCP_DASHBOARD | 是否启用仪表盘 |
这一步要特别核对安全机制。health-mcp 在绑定非 loopback 地址时,会强制要求 HEALTH_MCP_TOKEN 是 32 位以上的高熵字符串,否则服务拒绝启动。这个逻辑不是模型推测出来的,是代码里写死的判断。审计时可以让 Codex 去代码里找到校验函数,把判断条件贴出来,再回到表里确认默认值。数据文件和父目录的权限(0600 / 0700)也建议在代码里找到实际的权限设置代码再记入文档。
3.3 第三步:路由盘点
路由层是 health-mcp 对外暴露能力的窗口。让 Codex 从 app/routes/ 目录逐个读取路由文件,整理出完整的 HTTP 路径清单:/mcp 挂载 MCP 传输层,/api/* 提供与 MCP 工具对等的 REST 镜像,/ 是 React 仪表盘入口,/auth/* 处理 OAuth 回调。
REST 镜像接口对应的页面功能包括 Today(当日餐食、营养素目标、饮水、体重)、Log(餐食、饮水、体重、身体测量记录)、Foods / Recipes / Batches(食物图谱)、Goals(宏量营养素与体重目标)、Labs(检验面板与趋势)、Trends(每周汇总)、Wearables(可穿戴设备状态)、Insights(相关性分析)、UISettings(令牌、时区、主题)。这些页面名如果只让模型“总结”,很容易被合并或漏掉;逐文件核对时,每一条都要能在路由文件里找到对应的 handler。
3.4 第四步:MCP Server 实现
MCP 层是 health-mcp 的核心。传输层挂在 POST /mcp,走 Streamable-HTTP 协议。MCP 客户端在请求头里带 Authorization: Bearer <HEALTH_MCP_TOKEN>(如果服务配置了令牌)。初始化采用标准 MCP 握手:initialize 交换协议版本,之后通过 tools/list 发现工具、用 call_tool 调用具体工具。
审计这一层时,让 Codex 把工具定义文件里的工具名列出来,常见工具包括 log_meal(记录餐食)、biomarker_trend(查询生物标志物趋势)、correlate(分析营养摄入与恢复指标的相关性)。这一步不能只看模型输出的“工具名清单”,要让它同时贴出对应函数的入口代码。比如 log_meal 这个工具,实际实现里输入参数是什么、写入了哪张表,都必须从代码里找到依据。
3.5 第五步:数据层 SQLite 确认
数据层相对简单,但容易被模型描述得花哨。health-mcp 的实际存储结构是:数据库文件位于 ~/.health-mcp/data.db;认证令牌单独存放在 ~/.health-mcp/auth.json,与业务数据库分离,避免 OAuth 刷新令牌混进业务表;OAuth 刷新令牌每次使用后会轮转,防止并发刷新导致令牌失效。
让 Codex 读取创建表的初始化代码,把表名和关键字段列出来,和 MCP 工具读取的实体对应上。任何在模型回答里出现、但在建表代码中找不到的表名,直接标注为幻觉,不进审计文档。health-mcp 官方还提到了 Google Health MCP Server 和 Apple Health MCP Server 两个选配组件,但这次审计范围限定在 health-mcp 本体,选配组件不纳入结论,避免把两个不同项目的细节混进来。
4. 审计过程中最容易踩的两个坑
4.1 幻觉残渣:VolumeCache 和 BuildAndRunConfiguration 这类虚构条目
即使限定了逐文件核查,Codex 仍然可能在某个角落里补出一个不存在的组件。遇到这种时刻,正确反应不是去搜索这个组件是干什么的,而是回到仓库验证。用 grep 在项目里搜一下:
grep -r "VolumeCache" . grep -r "BuildAndRunConfiguration" .搜不到,就是幻觉,直接在结论里划掉,不要保留任何“疑似”条目。这个习惯适用于所有 AI 辅助审计:模型给出的每一个专业名词,只要不能对应到具体文件路径或具体代码行,就不算数。审计文档里只记录能指出“在哪个文件、哪一行、长什么样”的内容。
4.2 401 与 404:大多数配置问题都出在这两处
走 TaoToken 通道配置 Codex 时,最常见的两个报错都有明确指向。
401 Unauthorized,基本是 Key 的问题。检查 export 的 TAOTOKEN_API_KEY 是否等于你在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建的那把 Key,以及 config.toml 里 env_key 是否写成了其他名字。还有一点:不要在配置里把 YOUR_API_KEY 原样留着,占位符不会通过认证。
404 Not Found,基本是 Base URL 的问题。Codex 请求路径会被拼在 base_url 后面,如果 base_url 写了 https://taotoken.net/api/v1,请求就成了 .../api/v1/...,自然找不到对应路由。正确的 Base URL 是 https://taotoken.net/api,末尾不加 /v1。检查完这两处,再回控制台看看请求有没有记上账,能更快定位是没发出去还是被拒了。
5. 审计产出与接下来的两个动作
5.1 本次审计的产出
| 产出 | 说明 |
|---|---|
| 经人工核实的架构文档 | 端口 7777、Node.js、Hono、四类职责的单进程模型 |
| 可复用的审计 SOP | 基础设施 → 配置 → API → MCP → 数据层 |
| 规避幻觉的原则 | 逐文件分析、分模块验证、只采信有文件路径的证据 |
这份文档的每一行都能指向具体文件,后面的模块可以放心引用。和 OpenCode 第一轮自动审计相比,最关键的差别是:文档里没有一个“查不到出处”的组件名。
5.2 下一步:n8n 接入前的验证与准备
审计收尾后,按照原计划是进入 n8n 通过 MCP Client 节点接入 health-mcp 的工具。在动手之前,先确认刚才配置的通道在真实请求里是通的。跑通之后,用同一把 Key 在 TaoToken 模型对话 里发一条测试消息,核对模型 ID 和 Base URL 是否匹配;长期用 Codex 跑审计的话,看一眼 Coding Plan 的套餐是否够用;需要补 Key 或轮换 Key 的时候去 控制台 API Keys 创建,Claude Code 那套环境变量格式也可以顺手存一份 接入文档。审计这件事,模型是助手,仓库是法官;把“逐文件核查”写进自己的工作流,比任何模型参数都管用。