hybridlog について
hybridlogは、Garminデータ向けのセルフホスト型MCPサーバー兼ダッシュボードです。ランニングや筋力トレーニング、両方を行う人を対象に、個人の健康データを複製し、トレーニング負荷、筋肉の回復度、準備度を算出して、LLMに読み取り専用で提供します。
主な機能
- 01Garminデータを同期してミラー化
- 02負荷、準備度、筋肉の回復度を算出
- 03トレーニングとレースの区間分析
- 04健康データのダッシュボード表示
- 05クエリを読み取り専用SELECTに制限
- 06アカウントごとにチャットのデータ閲覧を制御
活用シーン
- 01今週と過去のトレーニング負荷を比較
- 02準備度や回復した筋肉群を確認
- 03レースのランニングとステーション区間を分析
- 04症状を記録し健康データとあわせて相談
MCP 対応
stdioとOAuth 2.1対応のStreamable HTTPでMCP接続を提供し、健康サマリー、トレーニング分析、データクエリのツールと週次レポート用プロンプトを公開します。データ閲覧は権限スイッチで制御され、チャットからの唯一の書き込みである症状記録はアプリ側のデータに保存されます。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://{hybridlog_host}/mcp/garminclaude mcp add --transport http imgrund-hybridlog https://{hybridlog_host}/mcp/garmin{"mcpServers":{"imgrund-hybridlog":{"type":"http","url":"https://{hybridlog_host}/mcp/garmin"}}}Cursor で開く{"servers":{"imgrund-hybridlog":{"type":"http","url":"https://{hybridlog_host}/mcp/garmin"}}}VS Code で開く{"mcpServers":{"imgrund-hybridlog":{"type":"http","url":"https://{hybridlog_host}/mcp/garmin"}}}{"mcpServers":{"imgrund-hybridlog":{"serverUrl":"https://{hybridlog_host}/mcp/garmin"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ghcr.io/imgrund/hybridlog:0.1.13stdioツール
詳細
hybridlog
自分のGarminデータを扱うセルフホスト可能なMCP サーバーです。Garminが返すすべての情報を、管理するデータベースに複製し、ウォッチが算出しないモデル(トレーニング負荷、筋肉の回復度、状況に応じたコンディション)を計算して、そのすべてをstdioまたはHTTP経由でLLMに渡します。そのため、Garminでは測定できない情報をフォームに入力する代わりに、チャットで伝えられます。ダッシュボードも付属しています。身体マップ、トレーニング負荷、そしてコネクターが認証に使うログイン画面を備えています。ホスト型のインスタンスはありません。それがこのプロジェクトの要点です。これは健康記録であり、モデルは読み取り専用のロールを通じてデータを読み取ります。
同じ日にランニング、筋力トレーニング、またはその両方を行っても、モデルはその違いを気にしません。ランニングとステーションワークを交互に行うセッションでは、HYROXレースの形式に沿ったラップごとの詳細が表示されます。
インストールする前に試せるデモがあります。[email protected]でサインインし、パスワードdemo-demo-demoを入力してください。生成データで動作し、毎晩初期状態に戻ります。

|
|
|
これら3つの画像に表示されている数値はすべて、fetcher/seed_demo.pyで生成されています。
インターフェースは英語で、ドイツ語訳も同梱されています。プロフィールで別の言語が指定されていない場合、ブラウザーの言語に従います。
利用する前にお読みください。 データ取得ツールは、Garmin Connectの非公式Web APIを使用します。Garminは予告なくAPIを変更または終了する可能性があります。このプロジェクトはGarminと提携しておらず、Garminから承認も受けていません。算出される情報はいずれも医学的な見解ではありません。コンディション、HRVの範囲、負荷比、コーチのテキストはトレーニング補助であり、診断ではありません。これは、役に立つことを願って公開している個人プロジェクトであり、サポートや安定したインターフェースを保証するものではありません。
概要
- MCP サーバー:
laravel/mcpを使った、13 個のツールと 1 つのプロンプトを、2 種類のトランスポートで提供します。Claude Code と Claude Desktop 向けのローカル(stdio)、および claude.ai、ChatGPT、Mistral の Le Chat(名称変更後の Vibe)、LM Studio、その他このプロトコルに対応するもの向けのホスト型(OAuth 2.1、PKCE、動的クライアント登録に対応したストリーミング可能な HTTP)です。 - ダッシュボード:Laravel + Alpine + Chart.js を使用します。身体マップとトレーニング負荷を表示し、コネクターが認証に使うログイン画面であるとともに、チャットから参照できる情報を設定する場所でもあります。
- フェッチャー:Python(
fetcher/)で実装され、python-garminconnect を介して日々の指標とアクティビティを取得し、PostgreSQL データベース内の 1 人のアスリート用スキーマに保存します。上記の両方は、読み取りのみ、かつそのアスリートのデータのみを参照できる接続を通じて、このスキーマを読み取ります。各回答は、そこから読み取った列と並べて全体が保存されるため、列がまだ作られていないフィールドも質問でき、Garmin Connect で提供されなくなった日もミラーに残ります。
ホスト型のインスタンスがないとは、実際には次のことを意味します。Garmin のセッションは、ご自身のデータベース内にある、いかなる読み取りロールもアクセスできないスキーマに保存されるため、モデルが作成する SQL から参照することはできず、あなた以外の運用担当者がセッションを保持することもありません。Garmin 自体を除き、外部に接続するものは任意の 2 つだけです。天気情報では、ご自身で入力した座標のペアを使用します。通知では、プッシュサービスは空の POST を受け取って起動し、通知の内容は送信されません。
ツール
13 個のツールと 1 つのプロンプトがあります。右端の列は、それぞれを有効にする /connect のスイッチです。すべてデフォルトでオンになっています。
| ツール | 回答する内容 | 必要な権限 |
|---|---|---|
get-health-summary-tool | 1 回の呼び出しで現在の状態を確認します:準備状況、睡眠、負荷、データの鮮度 | 健康データの読み取り |
get-insights-tool | 身体システムごとのアプリ独自の評価と、推奨事項および初期の体調不良のパターン | 健康データの読み取り、身体指標の読み取り |
get-muscle-map-tool | ゾーンごとの回復状況、週ごとのゾーン別運動量、今日鍛える部位 | 健康データの読み取り |
get-training-load-tool | CTL/ATL/TSB、急性負荷と慢性負荷の比率、週ごとの刺激の内訳 | 健康データの読み取り |
get-strength-progress-tool | 運動カテゴリーごとの週単位の進捗:回数、記録がある場合は総重量、最高重量、変化のない項目 | 健康データの読み取り |
get-race-splits-tool | 1 回のセッションをラップごとに確認します:ランニングとステーションワーク、ラップごとのペース、ペースの低下幅 | 健康データの読み取り |
get-activity-detail-tool | 1 回のセッションを詳しく確認します:アスリートのゾーン基準値に対するゾーン滞在時間と割合、ラップ、セット、心拍数の推移、および同種の過去のセッションとの比較 | 健康データの読み取り |
describe-schema-tool | ミラーのテーブルと列を確認し、モデルが独自のクエリを書けるようにします | 健康データの読み取り、身体指標の読み取り |
query-health-data-tool | それ以外のあらゆる情報を、最大 500 行の読み取り専用 SELECT で取得します | 健康データの読み取り、身体指標の読み取り |
refresh-data-tool | Garmin から今日と昨日のデータを同期し(ヘッダーのボタンと同じ処理を 2 日分に限定)、完了まで待機します | データ取得の開始 |
log-symptom-tool | 会話の中で言及された不調を、身体マップ上のマーカーとして記録します | 体調の記録 |
delete-symptom-tool | 不調が治ったら、その記録を削除します | 体調の記録 |
give-feedback-tool | コネクターの恒常的なガイドラインとなる修正を記録します | フィードバックの処理 |
weekly-report(プロンプト) | 日曜の振り返りを進行します。レポートは会話への回答であり、どこにも保存されません | 健康データの読み取り |
ほとんどのツールは、このアプリが対応するよう設計された質問に答えます。例外が 2 つあります。describe-schema は、各列の意味を記したコメントとともにミラー自体のテーブルをモデルに渡し、query-health-data は、モデルがそこから作成した SELECT を実行します。これは、レポートの集まりと、利用者が自ら考えを巡らせられるデータベースの違いです。また、未加工の回答をディスクに保持する価値を生み出すものでもあります。まだ列に昇格していないフィールドについても、あと 1 つ質問すれば確認できます。
読み取り専用が基本であり、例外は明記されています。チャットが書き込めるのは症状だけで、それもアプリ独自のスキーマに記録され、Garmin ミラーには書き込まれません。自由形式の SQL は app/Garmin/ReadOnlyGarminQuery を通じて実行されます。接続は SELECT 権限のみを持ち、それも 1 人のアスリートのスキーマに限られ、ほかの権限は一切持たないロールに切り替えられます。そのうえで、単一の SELECT または WITH、キーワードのブロックリスト、読み取り専用トランザクション、行数制限が適用されます。
クイックスタート(Docker、デモデータ、Garmin アカウント不要)
git clone https://github.com/Imgrund/hybridlog.git
cd hybridlog
cp .env.example .env
docker compose up -d
これにより、PostgreSQL、ダッシュボード、キューワーカー、スケジューラーが起動します。
公開イメージは amd64 と arm64 に対応しているため、初回起動時はコンパイルではなくダウンロードが行われます。git pull の後に通常の up -d を実行しても、既存のイメージが引き続き使われます。新しいコードが反映されるのは --build を指定した場合だけです。
次に、現実的なデータを120日分ミラーに入力し、ログインに使うアカウントを作成して、ダッシュボードを開きます。
docker compose exec app /opt/fetcher/bin/python fetcher/seed_demo.py
docker compose exec app php artisan app:create-user [email protected] --admin
open http://localhost:8080
意図的にサインアップページはありません。誰も登録できないログイン画面には、攻撃対象となるものがないからです。自分以外の人には、パスワードではなくリンクを渡してください。
docker compose exec app php artisan app:invite [email protected] --name="Them"
7日間(--days)有効なワンタイムリンクが出力され、そのリンクから相手が自分のパスワードを設定できます。--admin はインストール所有者を指定します。このアカウントがローカルの stdio トランスポートの動作対象となります。各アカウントは、それぞれ独自のプロフィール、権限、症状ログ、Garmin サインイン、通知、ミラーを保持し、ほかのアカウントの情報は一切見えません。
DEMO_MODE=true にすると、インストールはショーウィンドウとして動作します。アカウントを1つだけ共有し、外部と通信する機能はすべて無効になります(特に Garmin サインイン)。また、php artisan demo:reset によって毎晩リセットされます。
ショーウィンドウは訪問者数を計測する価値がある唯一の場所でもあります。UMAMI_SCRIPT_URL と UMAMI_WEBSITE_ID を設定すると、自分でホストする Umami インスタンスのスクリプトタグが表示されます。Cookie を使わず、サイト外で利用者を追跡する識別子も使わずに、ページビューを計測できます。どちらの設定も必要です。片方でも空(デフォルト)の場合、スクリプトは一切表示されず、このサイト以外のホストにページからリクエストすることもありません。これは DEMO_MODE とは連動していないため、プライベートなインストールでは設定を空のままにしておくだけで、外部スクリプトは読み込まれません。
AI に接続する
サーバーはダッシュボードと同じミラーを読み取るため、チャットとページの内容に食い違いはありません。ただし、ページは想定されていない質問には答えられません。今週の状態を先週と比較した場合、今日は休息日かどうか、ちょっとした痛みが明日にどう影響するか、といった質問です。
重要
これにより、健康記録が言語モデルに渡されます。読み取り専用のロールで、あなた自身のスキーマだけを読み取りますが、記録のすべてを読み取ります。どの情報を共有するかは、
/connectでスイッチごとに設定できます。スイッチの変更は、再接続時ではなく次のツール呼び出しから反映されます。
どのクライアントでも、接続先は同じ1つのアドレスです。
https://<your-domain>/mcp/garmin
Claude、ChatGPT、Langdock、Le Chat、LM Studio はすべてこのアドレスに接続し、ダッシュボード独自のログインに対して OAuth を実行します。そのため、クライアントがパスワードを見ることはなく、/connect からいつでも接続を切断できます。Claude Code と Claude Desktop は、デプロイを省略して stdio 経由でリポジトリと直接通信できます。クライアントごとの手順は docs/connect-ai.md をご覧ください。
どの方法で接続しても、同じ13個のツールと同じスイッチを利用できます。
接続後に尋ねるとよい質問:
トレーニングをした今週は、先週と比べてどうでしたか?
ハードなセッションを行える状態ですか、それとも休息日が必要ですか?
どの筋肉が回復していて、今日は何をトレーニングすべきですか?
土曜日のレースでペースが落ちたのはどこですか?タイムのうち、ステーションでの作業にはどれくらいかかりましたか?
ボックスジャンプで左ひざが痛くなりました。
ご自身のGarminアカウントでセットアップする
アカウントメニューのGarminから、または/connect/garminでサインインします。
メールアドレス、パスワード、Garminから求められた場合はMFAコードを入力します。保存されるのはOAuthトークンのペアで、パスワードは保存されません。サインイン処理はキューワーカーで実行されるため、少なくとも1つ起動している必要があります。
初回のサインイン時には、ミラーが自動的に作成されます。キューで90日分のバックフィルが実行され、約15分かかります。履歴が取り込まれるにつれて、ページに情報が表示されていきます。モデルが何らかの結果を出すには、90日がほぼ最低限の期間です。HRVの基準値には3週間分の夜間データが必要で、負荷比率には直近6週間分のデータが必要だからです。その後はスケジューラーが1日3回データを取得し、Garminから取得ボタンを使えば必要に応じて取得できます。
すべてのコマンドは1人のアスリートに対して実行されます(--tenant <user id>。省略した場合はオーナーが対象です)。詳細、バックフィル、手動ログインについてはdocs/install.mdを、データを有意義なものにする記録方法についてはdocs/recording.mdをご覧ください。
ドキュメント
- docs/install.md:Dockerを使わないインストール、プラットフォームへのデプロイ、バックアップ。
- docs/connect-ai.md:各クライアントの手順、OAuthと検出、公開ホストでのMCPサーバーの実行。
- docs/recording.md:記録のルール、天気、通知。
アーキテクチャに関するメモ
- 派生指標(CTL/ATL/TSB、ACWRのフォールバック、90日分の履歴を基に自己較正する約28時間の半減期を持つ筋肉の回復度)は、
app/Garmin/でその都度計算されます。 garmin接続はLaravelのデフォルト接続とは別です。同じデータベースを指しますが、クエリごとに1人のアスリートのスキーマを対象とします。App\Garmin\Mirrorは最初のステートメントの前にsearch_pathをgarmin_t{id}に設定し、そのテナントの読み取り専用ロールに切り替えます。- アスリートが入力する必要があるのはインターフェース言語のみです。
/profileで設定し、.envではなくデータベースに保存します。これはデプロイ先ではなく、アスリートに属する設定です。 - 配色は検証済みのデータビジュアライゼーション用パレットに従います(
prefers-color-schemeを使ったライトモードとダークモード)。ロールはresources/css/app.cssで定義されています。
コントリビューション
リリースにはタグが付けられ、各リリースの変更点はCHANGELOG.mdに記載されています。CONTRIBUTING.mdには、テストスイートの実行方法、テストで求められる条件、意図的に対象外としている変更について記載されています。行動規範についてはCODE_OF_CONDUCT.mdをご覧ください。セキュリティ上の問題はIssueで報告しないでください。非公開で報告する方法はSECURITY.mdに記載されています。
ライセンス
MITライセンスです。詳細はLICENSEをご覧ください。
resources/data/body-polygons.jsonの筋肉ポリゴンはbody-highlighterプロジェクト群に由来し、こちらもMITライセンスです。
同梱されている2つのアセットには、それぞれ独自のライセンス条件があります。3Dフィギュアのresources/models/body-zones.glbはBodyParts3D(© The Database Center for Life Science)を改変したもので、CC Attribution-Share Alike 2.1 Japanの適用を受けます。resources/models/CREDITS.mdに、完全なクレジット表記とライセンスの適用範囲が記載されています。resources/fonts/のMulishフォントとOswaldフォントにはSIL Open Font License 1.1が適用され、そのライセンス文書も各フォントに同梱されています。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。

