Skip to main content
Glama
ekelly95

Personal Cronometer MCP

by ekelly95

Personal Cronometer MCP

这是 Cronometer 与 Codex、Claude Code 等 MCP 客户端之间的本地个人桥接。它结合了 Paul Hoskins 的 cronometer-mcp 客户端的实用实时账户覆盖面与更严格的 TypeScript 层,后者会保留缺失的营养数据、如实标记会更改账户的操作,并将凭据排除在 MCP 配置文件之外。

这是一个个人学习项目,由一名 NASM 认证运动营养教练(CSNC)构建,该教练还修读过其专业之外大学水平的营养学课程。这一背景决定了项目的优先级,但本软件不是医疗设备,也不能替代医疗护理。

它通过固定版本的客户端提供完整的实时访问:食物日记读写、覆盖感知的营养素摘要、原始 CSV 导出、食物搜索、宏量营养素目标和计划、断食记录、生物特征数据、日期复制/完成以及重复食物。

重要限制

  • 这是一个非官方的个人工具。Cronometer 不提供也不支持此接口。Cronometer 网站的任何更改都可能在没有警告的情况下使其失效,自动化访问可能会使账户面临风险。在启用之前,请阅读 Cronometer 当前的服务条款

  • 某些 CSV 导出功能可能需要 Cronometer Gold。Cronometer 在账户设置:数据导出中记录了其支持的手动导出流程。

  • 本工具报告已记录的内容以及记录的完整程度。它不是医疗设备,不诊断营养缺乏症,也不提供医疗建议。

  • 保持本地化。该项目刻意不包含 HTTP 服务器、远程部署模式、遥测或任意代码执行工具。

是什么让营养摘要更安全

Cronometer 的每日摘要有一个微妙的陷阱:空的营养素单元格表示“无数据”,而显示为零则表示记录了零。它的 Total 行可能会把缺失的单元格折叠为零。此服务器自行解析日记分组行,计算每种营养素的覆盖率,并且仅在达到所请求的覆盖率阈值时才返回摄入值。在默认阈值 1 下,该营养素对应的每个日记分组单元格都必须包含数据。

因此,营养素结果有两种形态之一:

  • kind: "value" — 包含数值、单位、分组覆盖率、日覆盖率以及 Cronometer Total 比较。

  • kind: "insufficient-data" — 包含覆盖率以及一个明确命名的下界 observedSubtotal,但无法将该小计表示为摄入量。

这一区分从 CSV 解析一直保持到最终的 MCP 输出。

每条日记读取都在此处解析,而不是在上游

固定版本的客户端使用 csv.DictReader 读取 CSV,并返回未类型化字符串的行。作为传输这没问题,但作为日记的模型则毫无用处,因此此服务器不使用它。cronometer_get_food_logcronometer_get_exercisescronometer_get_biometric_logcronometer_get_notes 各自获取原始导出并在此处解析。这带来的好处是:

  • 1.00 container - each 5.3 oz 以数量和单位的形式返回,而不是一个留待以后猜测的字符串。

  • 空白的运动时长被报告为缺失,而不是零分钟。

  • 单位保持与您账户显示的一致。不进行任何转换。

  • 无法读取的行会被丢弃并报告,附带文件和行号,因此短列表永远不会在不知不觉中变短。

  • 从未记录的时间是 null,绝不是午夜。

一个刻意的拒绝行为值得了解。如果 Cronometer 的导出丢失了此服务器需要的一列,解析器将返回零行——这看起来就像你未记录的一天。为了不返回这种结果,调用会失败并指明缺失的列。在这里给出空答案,与把缺失的营养素读作零是同一类错误。

同样的规则现在适用于不走 CSV 的实时读取。如果其中某个响应以连接器无法识别的形式返回空值,结果会带有 unverified: true——意思是“为空,且我无法确认”。真正失败的调用会抛出异常,因此该标志仅保留给一个真正模棱两可的情况:响应提到了它随后找不到的数据。

为了把这个边界弄对,经历了两次尝试。第一次将每个空结果都标记为未验证,理由是缺少元素类型标记可能意味着格式已更改。检查实际响应后结果恰恰相反——空集合没有元素类型,因为它没有元素——因此警告在正确回答时也会触发,而这正是警告变成噪音的方式。现在它仅会在响应包含解析器无法读取的内容时触发。

实时调用也会限速,至少间隔一秒。在你阅读答案时几乎察觉不到,而这正是对话与抓取之间的区别。

设置日记时有一点需要避免:不要将日记分组命名为 Total Cronometer 会把自己每日总计写入与你的分组名相同的列,而导出无法区分两者。具有该名称的分组将被读取为当日总计并从总和中排除,因此其食物会悄悄地从每个摄入数字中消失。任何其他名称都没问题。

营养素单元格的读取也很严格。单元格必须为空或纯非负十进制数;任何其他内容——文本、千位分隔符、负数——都会被记录为缺失,并附注说明哪一列和哪一行,而不是被强制转换为会悄悄改变总和的数字。

Windows 设置

打开 PowerShell 并运行:

Set-Location C:\dev\cronometer
.\scripts\setup-windows.ps1

在 macOS 上,等效命令是 sh scripts/setup-macos.sh,它会按相同的顺序执行相同的操作——请参阅其他平台以了解其中哪些已得到验证,哪些没有。

该设置执行以下操作:

  1. 重新安装锁定的 Node 依赖。

  2. 根据经过哈希检查的五个包锁文件(requests 及其四个传递依赖——协议客户端是内附的,而非安装的)创建或更新私有 Python 3.12 环境。

  3. 构建服务器并运行所有离线测试。

  4. 询问 Cronometer 日记时区。America/New_York 是此计算机的推荐默认值。

  5. 显示不支持接口的警告,并要求输入确切单词 ENABLE

  • cronometer_add_biometric 仅接受体重。请求 60 的心率会创建一条 60 磅的 体重 条目——其他三种指标的编码方式是猜测,而 body_fatweight 的编码逐字节相同,因此它必定会以同样的方式错误归档。一次静默地将数据归档到错误指标的写入操作,会破坏你稍后读取的趋势,且不会留下任何发生过的迹象,因此其他指标被拒绝。请在 Cronometer 应用中记录它们。

  • cronometer_set_day_complete 会失败:Cronometer 已移除 setDayComplete 方法,就像它移除 findFoods 一样。本地没有任何办法解决。

经过真实测试后修复。 cronometer_get_repeated_items 过去会返回颠倒的 food_source_idmeasure_id、始终为空的星期列表,以及值为 0 的日记分组。现在它会按照协议实际写入响应的方式——从后往前——来读取响应,并正确报告星期。

日记分组被报告为 null,因为 Cronometer 不会将其回传:在不同分组中创建的两条规则,除了 id、数量和星期之外,返回的响应完全相同。你在创建规则时选择的分组确实会被应用,只是无法读回。null 表达的就是这个意思;0 看起来会像一个真实的分组。

从未实际执行过。 copy_day(它会复制一整天,且无法干净地撤销,因为服务端 ID 无法从导出中读回)、set_macro_targetsset_macro_schedule_day(Cronometer 会根据你的个人资料计算推荐目标,设置这些会覆盖该计算——不适合作为测试),以及两个禁食工具(没有创建禁食的工具,因此也没有任何东西可以删除或取消)。

写入安全

读取和写入工具被刻意分开。每个会更改账户的工具都被标记为非只读,而每个被标记为破坏性的工具都会拒绝调用,除非存在 confirm: true

不同客户端中的审批机制各不相同,因此下面是你在各处得到的确切行为:

客户端

什么会先要求写入审批

配置方式

Claude Code

14 个会更改账户的工具均带有 anthropic/requiresUserInteraction,因此它在每次调用时都会提示——包括在 acceptEditsautobypassPermissions 之下——并且没有任何允许规则可以跳过它

服务器本身。无需设置。需要 Claude Code 2.1.199 或更高版本

Codex

default_tools_approval_mode = "writes",因此每个未标记为只读的工具都会提示

设置脚本,位于 Codex 的 config.toml

Claude Desktop

Desktop 自己的工具审批提示

Claude Desktop

Claude Code 的情况是最强有力的,因为该要求随工具本身携带,而不是存在于你之后可能更改的配置文件中。其他情况则取决于客户端配置:设置脚本会设置 Codex 的配置,如果无法设置会大声告诉你。较旧的 Claude Code 版本会忽略该标志并回退到其正常的权限处理,其他 MCP 客户端也是如此——未知的 _meta 键是无害的,这就是为什么它会无条件发送。

读取工具刻意不携带此类标志。一个不断打扰你的状态检查只会让你学会不读提示就直接点击通过。

写入操作永远不会自动重试。如果写入超时,服务器会报告其结果未知。在决定是否再次尝试之前,请检查 Cronometer 应用;否则重试可能会重复添加食物、生物特征数据、模板或重复项。

手动注册 MCP

设置通常会自动提供此操作。如果你跳过了它,命令只包含本地启动器路径——绝不包含凭据。

Codex:

codex mcp add cronometer-personal -- pwsh -NoProfile -ExecutionPolicy Bypass -File C:\dev\cronometer\scripts\run-mcp.ps1

然后在此处新增的 [mcp_servers.cronometer-personal] 部分中的 %USERPROFILE%\.codex\config.toml 中添加以下行:

default_tools_approval_mode = "writes"

Claude Code,可供 Windows 用户在每个项目中使用:

claude mcp add --scope user cronometer-personal -- pwsh -NoProfile -ExecutionPolicy Bypass -File C:\dev\cronometer\scripts\run-mcp.ps1

Claude Desktop 没有注册命令。在 %APPDATA%\Claude\claude_desktop_config.jsonmcpServers 对象中添加以下内容,保留已有的所有服务器,然后重启 Desktop:

"cronometer-personal": {
  "command": "C:\\Program Files\\PowerShell\\7\\pwsh.exe",
  "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\dev\\cronometer\\scripts\\run-mcp.ps1"]
}

请使用这台机器上 pwsh.exe 的真实路径——(Get-Command pwsh).Source 会打印它。编辑前请备份该文件:它既保存着 Claude Desktop 自身的偏好设置,也保存着服务器列表,一次错误的编辑就会丢失它们。设置脚本会替你完成所有这些操作,这是更好的途径。

使用 codex mcp get cronometer-personalclaude mcp get cronometer-personal 检查注册。Anthropic 当前的 Claude Code MCP 指南 解释了其配置范围以及适用于 MCP 工具的权限规则。Codex 的 CLI 和 IDE 扩展使用相同的 MCP 配置;请参阅 OpenAI 的 MCP 文档

将其打包为桌面扩展(.mcpb/.dxt)可以省去手动编辑步骤,但扩展包还需要包含凭据提示和 Python 环境。这一点尚未实现;上述启动器加配置的路线才是本仓库支持的方案。

凭据与网络边界

  • scripts/run-mcp.ps1 会解密受 DPAPI 保护的密码,并通过环境变量将其传递给服务器。要清楚这付出了什么代价:启动器会像 MCP 会话一样持续运行,而在其运行期间,明文密码会出现在三个进程的环境中——启动器、Node 和 Python 子进程。它绝不会以明文形式写入磁盘,绝不会出现在命令行中,也绝不会存储在 MCP 配置文件中,但任何以此 Windows 用户身份运行的进程都可能读取到它。这就是不在配置文件中输入密码所换来的权衡。

  • Python 子进程会用经过大小检查的 JSON 替换原先的可执行 pickle 会话,并将其存储在私有应用程序数据目录中。在 Windows 上,该目录由设置脚本应用的 ACL 保护:继承已断开,仅为你自己的账户设置一条访问规则,并且没有 SYSTEM 或管理员组的条目。有两层守护,且它们检查的内容不同。启动器每次启动时都会读取实际的 ACL,如果目录变得可继承或获得了任何其他身份,则拒绝运行。Python 桥接器在没有额外包的情况下无法读取 Windows ACL,因此它只在 CRONOMETER_DATA_DIR 未设置时拒绝运行——这足以阻止会话 cookie 回退到不受保护的主目录默认位置,但并不是对权限本身的检查。通过启动器以外的任何方式启动服务器,你得到的是两者中较弱的那一层。

  • 网络会话只接受确切主机为 cronometer.com 的 HTTPS 请求,包括重定向。代理和证书环境变量不会传递给子进程,因此机器级代理无法介入。

  • 调用是串行化的,日期和标识符会经过两次验证,任何超过 2 MB 的工具结果都会被拒绝,而不是被静默截断——请请求更短的日期范围。

  • 食物名称、备注、网站错误以及所有其他实时文本都会在明确的不可信数据边界内返回,并经过 JSON 编码,使文本无法伪造该边界的结尾。它们绝不能被当作指令对待。

有用的工具

32 个 MCP 工具按概念分组如下:

  • 连接:状态与连接检查。

  • 已下载的导出:列出它们,并对其中一个运行覆盖感知的营养分析。这些操作读取此计算机上的文件夹,绝不接触网络。

  • 日记:食物日志、运动、生物特征历史、备注、覆盖感知的营养摘要、原始 CSV 导出、添加/删除食物、复制一天,以及将一天标记为完成。

  • 食物数据库:搜索和食物详情。

  • 宏量营养素:读取目标/计划,设置每日目标,列出/创建/删除模板,并将模板分配给某个星期几。

  • 禁食:历史记录、统计、删除一次禁食,以及在保持其系列的同时取消进行中的禁食。

  • 生物特征:读取最近的值、添加一个值、删除一个值。

  • 重复食物:列出、添加和删除规则。

刻意没有任意的 GWT 请求工具、浏览器自动化、原始 SQL、shell 执行、自动后台同步或远程 HTTP 传输。

开发验证

所有测试均为离线测试并使用合成数据:

npm run verify      # typecheck, TypeScript, Python, and the setup scripts

这是 450 个 TypeScript 测试、45 个 Python 测试和 24 个设置检查。各个步骤为 npm run typechecknpm testnpm run test:pythonnpm run test:setup;最后一项在缺少 PowerShell 时会大声跳过自身,而不是因为与被检查代码无关的原因而失败。

npm test 会先构建,然后检查传统 MCP 和现代的 2026-07-28 stdio 握手。协议套件会针对一个假的桥接器调用每个工具,验证工具权限标签,检查每个破坏性工具都会拒绝未经确认的调用,确保读取处理器无法触及变更方法,并让恶意多行文本同时穿过成功和错误路径,以证明两者都无法伪造不可信数据边界的结尾。

这些测试所展示的内容有两个诚实的局限。通用输出模式刻意将 data 类型设为 unknown,因为实时响应的形状由 Cronometer 决定——因此“针对输出模式进行验证”只对营养摘要才是真正的检查,因为它是唯一具有完整指定结果的工具。而且每个测试都是离线的:它们证明包装器的行为正常,而不是证明未文档化的接口仍然可用。

唯一可以随意运行的实时检查是连接检查。除非预期的账户更改本身就是测试,否则不要针对真实账户测试写入工具。

关于本仓库不包含的两个文件的说明

部分源代码和安全审计引用了 CLAUDE.mdAGENTS.md。这些是用于构建此项目的 AI 助手的工作文件,它们保留在机器上而不是仓库中——它们是写给助手的,而不是写给读者的,并且它们带有个人上下文,剥离这些上下文后读起来会很差。

没有任何关键内容被隐藏。它们所述的设计规则在执行处可见:src/domain/nutrient.ts 中的缺失与零值之分的类型,src/mcp/registry.ts 中的写入注解,src/mcp/server.ts 中的不可信数据围栏,python/live_bridge.py 中的网络边界。它们背后的推理在 BUILD_PLAN.mdDATA_MODEL.md 和安全审计中,这些都是面向人的。对这两个缺失文件的引用按原样保留,而不是删除,因为尤其是审计是一份带有日期的记录,悄悄改写其引用会让它更不可信,而不是更可信。

其他平台

macOS 已构建:scripts/setup-macos.shscripts/run-mcp.sh 与 Windows 那一对逐步对应,将密码存储在登录钥匙串中而不是 DPAPI 中,并用模式 700 而不是 ACL 来保护数据目录。其他所有内容都是相同的代码。

坦率地说明它的状态,因为这比声明本身更重要:

Windows

macOS

Linux

针对真实账户运行端到端测试

尚未

CI 中的测试套件

仅对 shell 脚本进行 lint 检查

设置脚本和启动器已编写

仅启动器,未测试

macOS 路径中属于普通代码的部分——Claude Desktop 配置写入器、配置验证器、每个解析器——都由每次推送时在 macOS 上的 CI 中运行的测试覆盖。从未在 Mac 上实际运行过的,是那些需要 Mac 才能测试的部分:Keychain 提示、目录模式拒绝,以及一次真实的读写。MACOS.md 明确列出了这四项检查,并指出哪一项最有可能揭示差异。

Linux 也顺带获得了启动器,因为它在数据目录和权限检查上走的是非 Darwin 分支,但没有设置脚本,也没有人实际运行过。

来源与许可证

GWT-RPC 协议实现最初源于 Paul Hoskins 的 MIT 许可的 cronometer-mcp 2.0.3。现在它已被内置并修改python/vendor/cronometer_client.py,而不是从 PyPI 安装,并且按照许可证要求,他的版权声明保留在 THIRD_PARTY_NOTICES.md 中。

这一改动是在 2026 年 8 月出于一个具体原因做出的。上游的最后一次提交是在 2026 年 3 月 8 日。到 8 月时,它有 8 个未关闭的问题和 4 个未合并的拉取请求——其中两个是修复 Cronometer 的一项变更,该变更已经彻底破坏了食物搜索,并随之完全破坏了记录食物的能力。锁定版本的依赖无法打补丁。内置意味着这些修复可以被应用,也意味着下一次故障可以在这里修复,而不只是在他处报告。

所有与原始版本有意的差异都列在内置文件的文件头中,其中两个改编自其他贡献者的公开拉取请求,并在声明中予以致谢。协议逆向工程本身是 Paul Hoskins 的工作,也仍然是这个项目中最困难的部分。

requests 现在是这个项目唯一不拥有的运行时依赖。

这个项目自身的代码采用 MIT 许可证;参见 LICENSE

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • The personal context layer for AI: your profile and files, read by any MCP client over OAuth.

  • Search, document and execute authenticated API calls across 500+ apps via one MCP server

  • Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ekelly95/cronometer-personal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server