
hybridlog
io.github.Imgrund由 GitHub 账号 Imgrund 发布v0.1.13更新于 Oct 24, 2026
Reads your own Garmin mirror: readiness, training load, muscle freshness, and read-only SQL.
概览
一个自托管的 MCP 服务器,把你自己的 Garmin 数据镜像到你可控的数据库中,让助手读取准备度、训练负荷、肌肉恢复状态,并执行只读 SQL 查询。
- 可以做什么
- 一次调用获取健康概览、各身体系统的洞察、肌肉恢复状态与训练负荷指标
- 按动作类别查看力量进展,以及逐圈的比赛分段数据
- 深入查看单次活动,包括各心率区间时长、圈、组和心率曲线
- 描述镜像数据库的表结构,并执行带行数上限的只读 SQL 查询
- 从 Garmin 刷新最近两天数据、记录或删除症状,并把反馈保存为长期准则
- 运行每周回顾提示
- 适用场景
- 适合使用 Garmin 训练、希望助手回答手表和仪表盘无法回答的问题的人,例如本周与上周相比如何、今天是否该休息、某处不适对明天意味着什么。由于没有托管实例,适合愿意自行部署的用户。
- 运行要求
- 需要自托管部署:Docker(或手动安装)配合 PostgreSQL、队列 worker 和调度器,以及已发布的容器镜像。必须提供同一 hybridlog 安装的 APP_KEY 和指向其数据库的 DATABASE_URL。填充镜像需要登录 Garmin 账号(邮箱、密码、MFA 验证码);演示模式无需 Garmin 账号。远程客户端通过 streamable HTTP 并使用仪表盘登录的 OAuth 连接。
安装
在 SourceWeft 中
- 打开 控制台中的 hybridlog,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
hybridlog
[Tests] [License: MIT] [Release] [MCP registry]
Self-hosted MCP server on your own Garmin data. It mirrors everything Garmin answers into a database you control, computes the models the watch does not (training load, muscle freshness, readiness in context), and hands all of it to a language model over stdio or HTTP, so the things Garmin cannot measure can be dictated into the chat instead of typed into a form. A dashboard comes with it: the body map, the training load, and the login the connector authenticates against. There is no hosted instance, and that is the point: it is a health record, and the model reads it through a role that can only read.
Running, lifting or both on the same day: the models do not care which. Sessions that alternate running with station work get a lap-by-lap breakdown of their own, which is the shape of a HYROX race.
There is a demo to
walk through before installing anything: sign in as [email protected]
with the password demo-demo-demo. It runs on generated data and puts
itself back every night.
Every number in these three is generated by fetcher/seed_demo.py.
The interface is English and ships a German translation; it follows the
browser language unless the profile says otherwise.
Read this before you rely on it. The fetcher talks to Garmin Connect's unofficial web API. Garmin can change or close it without notice. This project is not affiliated with or endorsed by Garmin. Nothing it computes is a medical statement: readiness, HRV bands, load ratios and the coach texts are training aids, not diagnoses. It is a personal project published in the hope it is useful, with no promise of support or a stable interface.
What it is
- MCP server: thirteen tools and one prompt over
laravel/mcp, in two transports. Local (stdio) for Claude Code and Claude Desktop, hosted (streamable HTTP with OAuth 2.1, PKCE and dynamic client registration) for claude.ai, ChatGPT, Mistral's Le Chat (renamed Vibe), LM Studio and anything else that speaks it. - Dashboard: Laravel + Alpine + Chart.js. It draws the body map and the training load, and it is also the login the connector authenticates against and the place where you set what the chat may see.
- Fetcher: Python (
fetcher/), pulls daily metrics and activities via python-garminconnect into one athlete's schema of a PostgreSQL database, which both of the above read through a connection that may only read it, and only that athlete's. Each answer is also kept whole beside the columns read out of it, so a field nobody has built a column for is still there to be asked about, and a day Garmin Connect no longer serves is still in the mirror.
What no hosted instance means in practice: the Garmin session sits in a schema of your own database that no reader role can reach, so the SQL a model writes cannot ask for it, and no operator but you holds it. Past Garmin itself, two optional things reach out at all: the weather, which takes a pair of coordinates you fill in yourself, and notifications, where the push service is woken by an empty POST and never carries what it was about.
The tools
Thirteen tools and one prompt. The right-hand column is the switch at
/connect that gates each one; everything is on by default.
Most of them answer a question the app was built around. Two do not:
describe-schema hands the model the mirror's own tables with the
comments that say what each column means, and query-health-data runs
the SELECT it writes from that. It is the difference between a set of
reports and a database somebody can think in, and it is what makes
keeping the raw answers worth the disk: a field nobody has promoted to a
column is still one question away.
Reading is the whole of it, with one documented exception. Symptoms are
the only thing the chat may write, and they go to the app's own schema,
never into the Garmin mirror. Free-form SQL runs through
app/Garmin/ReadOnlyGarminQuery: a single SELECT or WITH, a keyword
blocklist, a read-only transaction, a row cap, on a connection switched
into a role that holds SELECT on one athlete's schema and nothing else.
Quickstart (Docker, demo data, no Garmin account)
That brings up PostgreSQL, the dashboard, a queue worker and a scheduler.
The published image covers amd64 and arm64, so the first start downloads
rather than compiles. After a git pull, plain up -d keeps running the
image it already has: the new code arrives only with --build.
Then fill the mirror with 120 days of plausible data, create the account you log in with, and open the dashboard:
There is no sign-up page, on purpose: a login nobody can register at has no surface to attack. For anybody but yourself, hand over a link instead of a password:
It prints a one-time link, good for seven days (--days), on which they
set their own password. --admin marks the installation owner, who is
the account the local stdio transport acts for. Every account keeps its
own profile, permissions, symptom log, Garmin sign-in, notifications and
mirror, and sees none of anybody else's.
DEMO_MODE=true turns an installation into a shop window instead: one
shared account, everything that would reach out of it closed (the Garmin
sign-in above all), and php artisan demo:reset putting it back nightly.
A shop window is also the one place worth counting visitors in, and
UMAMI_SCRIPT_URL plus UMAMI_WEBSITE_ID render the script tag of an
Umami instance you host yourself: page views without
a cookie and without an identifier that follows anyone off the site.
Both lines are needed, and with either empty, which is the default,
nothing is rendered at all and no page requests a host but this one. It
is not tied to DEMO_MODE, so a private installation stays free of
outside scripts by simply leaving them alone.
Connect an AI
The server reads the same mirror the dashboard draws from, so a chat and the page never disagree. What the page cannot do is answer a question it was not built for: how this week stands against the one before, whether today is a rest day, what a niggle means for tomorrow.
[!IMPORTANT] This hands a language model your health record. It reads through a role that may only read, and only your own schema, but it reads all of it. Which parts is yours to set at
/connect, per switch, and the switches take effect on the next tool call rather than the next reconnect.
One address, the same for every client:
Claude, ChatGPT, Langdock, Le Chat and LM Studio all take it and run OAuth
against the dashboard's own login, so no client ever sees a password and
/connect can cut any of them off again. Claude Code and Claude Desktop
can skip the deployment entirely and talk to the repository over stdio.
docs/connect-ai.md has the steps per client.
Whichever way it connects, it gets the same thirteen tools and the same switches.
Things worth asking, once it is connected:
How did my training week compare with the one before?
Am I ready for a hard session, or do I need a rest day?
Which muscles are fresh, and what should I train today?
Where did my pace go in Saturday's race, and how much of the clock was station work?
My left knee hurt on the box jumps.
Setup with your own Garmin account
Sign in under Garmin in the account menu, or at /connect/garmin:
email, password, and the MFA code if Garmin asks for one. What is stored
is an OAuth token pair, never the password. The sign-in runs on a queue
worker, so one has to be up.
A first sign-in fills the mirror by itself: a ninety-day backfill on the queue, roughly a quarter of an hour, with the page filling in as the history lands. Ninety days is about the minimum for the models to say anything, since the HRV baseline needs three weeks of nights and the load ratios a rolling six weeks. From then on the scheduler fetches three times a day and the Fetch from Garmin button fetches on demand.
Every command works on one athlete (--tenant <user id>, the owner where
left out). Details, backfills and the manual login are in
docs/install.md; how to record so the data is worth
reading is in docs/recording.md.
Documentation
- docs/install.md: installing without Docker, deploying to a platform, backup.
- docs/connect-ai.md: every client's steps, OAuth and discovery, running the MCP server on a public host.
- docs/recording.md: recording rules, weather, notifications.
Architecture notes
- Derived metrics (CTL/ATL/TSB, ACWR fallback, muscle freshness with a
~28 h half-life self-calibrated against 90-day history) are computed on
the fly in
app/Garmin/. - The
garminconnection is separate from Laravel's default one. It points at the same database, but is aimed at one athlete's schema per query:App\Garmin\Mirrorsetssearch_pathtogarmin_t{id}and switches into that tenant's read-only role before the first statement. - The interface language is the one thing the athlete still enters, on
/profile, stored in the database rather than in.env: it belongs to the athlete rather than to the deployment. - Colors follow a validated dataviz palette (light + dark via
prefers-color-scheme), roles defined inresources/css/app.css.
Contributing
Releases are tagged, and CHANGELOG.md says what changed in each one. CONTRIBUTING.md says how to run the suite, what the tests will hold you to, and which changes are deliberately out of scope. Conduct is covered in CODE_OF_CONDUCT.md. Security problems do not belong in an issue: SECURITY.md has the private path.
License
MIT, see LICENSE. The muscle polygons in
resources/data/body-polygons.json come from the
body-highlighter
project family and are MIT-licensed as well.
Two bundled assets carry their own terms. The 3D figure
resources/models/body-zones.glb is an adaptation of BodyParts3D
(© The Database Center for Life Science) and stays under
CC Attribution-Share Alike 2.1 Japan;
resources/models/CREDITS.md carries the
full credit and what the license binds. The Mulish and Oswald fonts in
resources/fonts/ are under the SIL Open Font License 1.1, whose texts
ship next to them.
来源:README.md,提交 958a8ca
工具
0版本历史
1- v0.1.13最新Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通必需25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式必需10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分
