プログラミング

バラバラの体組成・筋トレ・睡眠を自宅サーバーに集約するMCPと自動同期アプリを作りました

体組成計・筋トレアプリ・スマートウォッチに分散していた健康記録を、自宅サーバーの1つのデータベースに集約した話です。スクショをClaudeに読ませて記録するMCPサーバー(gym-mcp)と、Health Connectの連続データを自動送信するAndroidアプリ(からだ同期)の2本立てで、設計判断と実機で見つかったバグまでまとめました。

目次

はじめに

健康関連の記録が、アプリごとにバラバラに散らばっていて、全体を見渡せなくて困ったことはありませんか。

私はまさにこれでした。
記録自体はわりと真面目に取っていたのですが、

  • 体組成(体重・体脂肪・筋肉量など)はタニタの体組成計+Health Planetアプリ
  • 筋トレはGymDayというトレーニング記録アプリ
  • ランニング・心拍・睡眠・歩数はAmazfitのスマートウォッチ+Zeppアプリ

というふうに、3つの世界に分かれていました。
それぞれのアプリの中では完結しているのですが、「トレーニングした日の睡眠は足りていたか」「歩数が多い週は体重がどう動いたか」といった種類をまたいだ振り返りが、まったくできなかったのです。

そこでまず、これらの記録を自宅サーバーの1つのデータベースに集約するために、健康管理のMCPサーバー(gym-mcp)を作りました。
体組成計やアプリの結果画面をスマホで撮って、Claudeに「この結果を記録して」と送るだけで、Claudeが画像から数値を読み取り、サーバーに保存してくれる仕組みです。

ところが、これだけでは片付かない問題が残りました。
心拍や睡眠、歩数といった「連続して計測されるデータ」は、Zeppアプリの中に残ったままだったのです。
これらを毎日スクショして手で記録するのは、さすがに現実的ではありません。

そこで作ったのが、この記事の主役である「からだ同期」というAndroidアプリ(gymlog)です。
スマホの中で勝手に溜まっていく日々の健康データを、自分では何もしなくても自宅サーバーへ自動で吸い上げてくれます。
この記事では、この2つを組み合わせて「バラバラだった健康ログを1か所に集約する仕組み」を、内部の動きまで掘り下げて紹介します。

このシステムで何ができるか

ひとことで言うと、性質の違う健康データを、それぞれに合った方法で、自宅サーバーの1つのデータベースに集めることができます。
やっていることはこうです。

  • 体組成計やランニングの結果画面をスクショで撮ってClaudeに送るだけで記録できる(gym-mcp
  • 記録するときに、総挙上量や推定1RM(最大挙上重量)を自動計算してくれる(gym-mcp
  • たまったデータを、Claudeに分析・相談できる(gym-mcp
  • 歩数・心拍・睡眠などの連続データを、自動でサーバーへ同期する(からだ同期アプリ)
  • 結果として、種類をまたいだ振り返りが1か所でできる

既存の手段との違い

健康データをまとめる手段はいくつかありますが、それぞれに制約があります。

健康データをまとめる既存の手段と、それぞれの制約

このシステムは「自分のサーバーに全部集めて、Claudeを相談相手にしながら、自分のデータとして育てる」ところを狙っています。

解決したい課題

もう少し具体的に、何が不便だったかを書いておきます。

  • 記録が3つの世界に分散している:体組成・筋トレ・連続データが別々のアプリにあり、横断できませんでした。
  • 手入力が続かない:筋トレやランの数値を専用アプリにポチポチ入れるのが面倒で、いつも三日坊主でした。
  • 連続データは取り出しようがない:心拍や睡眠は毎日大量に出ます。これをスクショで記録するのは不可能でした。
  • データが自分の手元に残らない:各アプリのクラウドに預けているだけで、自由に集計・分析できませんでした。

これらを、MCPと自作アプリの2本立てで片付けるのが狙いです。

土台の話:データの性質で「記録の入り口」を分ける

機能の前に、このシステム全体を貫く考え方を説明させてください。
ここが分かると、なぜMCPとアプリの2つを作ったのかが腑に落ちます。

健康データには、性質の違う2種類があります。

  • 単発でリッチなデータ:体組成(筋肉量・内臓脂肪・基礎代謝…)や、ランニングの詳細(心拍ゾーン・ラップ・トレーニング負荷…)。これらは1回の測定に多くの項目が詰まっていて、しかも機器メーカー独自の値が多く、共通の置き場には流れてきません。でも頻度は低いので、スクショ1枚をClaudeに読ませて記録するのが合っています。
  • 連続して溜まるデータ:歩数・心拍・睡眠・SpO2など。1日に大量に出るぶん、スクショで記録するのは苦痛です。でもこれらは、Androidの「Health Connect」という健康データのまとめ役に自動で集まってきます。だから自動で吸い上げるのが合っています。

この「単発リッチはスクショ、連続データは自動」という役割分担が、システム全体の設計思想です。
前者を担うのが gym-mcp、後者を担うのが「からだ同期」アプリです。

そしてもうひとつの土台がHealth Connectです。
これは、いろんなアプリや機器が計測した健康データを、1か所に集めて橋渡しするAndroid標準の仕組みです。
Zeppが計測したデータもここに流れてきます。
Health Connectをハブにしておけば、計測する機器(時計やアプリ)が将来変わっても、最終的にここに集まりさえすれば、こちらの取り込みの作りは変えずに済むのが大きな利点です。

できること(全機能ツアー)

このシステムは2つの部品でできています。
順に見ていきます。

  • gym-mcp:自宅サーバーで動く健康管理のMCPサーバー。スクショ記録・自動計算・分析を担う
  • からだ同期アプリ:Androidアプリ。連続データをHealth Connectから自動でサーバーへ送る

「からだ同期」アプリの画面

「からだ同期」アプリ。画面は1つだけで、権限の取得・接続先設定・最終同期と未送信件数の表示・今すぐ同期ボタン・直近のプレビューだけのシンプルな作りです。

ここから先は、それぞれが内部でどう動いているかを掘り下げます。

詳細①:スクショをClaudeに読ませて記録する(gym-mcp)

gym-mcp で私がいちばん気に入っているのが、記録のとり方です。
手入力ではなく、画像で記録できます。

体組成計に乗ると結果が画面に出ますよね。
その画面をスマホで撮って、Claudeに「この体組成の結果を記録して」と画像つきで送るだけです。
すると、Claudeが画像から数値(体重・体脂肪・筋肉量・内臓脂肪レベルなど)を読み取り、そのままMCPサーバーの「体組成を保存する」機能を呼び出して、サーバーに保存してくれます。
ランニングの結果画面も同じで、スクショを送れば距離やタイム、平均心拍を読み取って記録してくれます。

これは「画像を理解するAI」と「記録を保存するMCP」が組み合わさって初めてできることです。
MCPというのは、Claudeのような生成AIに対して「こういう操作ができますよ」と外部の機能を持たせるための仕組みで、ここでは「トレーニングを保存する」「体組成を取得する」といった機能をClaudeから呼び出せるようにしています。
gym-mcp には、こうした機能(ツール)を14個持たせていて、役割ごとに整理するとこうなります。

gym-mcp が持つ14個のツールを、保存・取得・更新/削除の役割ごとに整理した表

数字を手で打ち込む必要がないので、はかったついでに数秒で記録が終わります。
「面倒だから記録しない」が無くなったのが、自分にとっては大きな変化でした。

詳細②:記録のたびに自動で計算してくれる(gym-mcp)

筋トレを保存するとき、gym-mcp はその場で2つの数字を自動計算します。

  • 総挙上量(トータルボリューム):その日の「重さ×回数」の合計です。同じ種目でも、扱う重さや回数が増えれば数字に表れるので、調子の上下が一目で分かります。ウォームアップのセットは集計から除外しています。
  • 推定1RM(最大挙上重量):1回だけ全力で挙げるとしたら何キロまでいけそうか、の目安です。「Brzycki(ブジツキ)式」という、挙げた重さと回数から推定する計算式を使っています。たとえば「重さ × 36 ÷(37 − 回数)」のような形で、普段のセットの記録から最大値を割り出します。毎回1回の限界に挑まなくても、日々のトレーニングから最大値の伸びを追えるのが便利です。

こうした数字が記録のたびに自動で出ると、自分の成長が目に見えて、トレーニングが続けやすくなります。
記録がたまってきたら、Claudeに「最近1か月のトレーニングをまとめて、気づいたことを教えて」と頼めば、期間のまとめや種目ごとの履歴のツールが呼ばれてデータが取り出され、「ベンチプレスは伸びているが脚の頻度が落ちている」といった、自分では気づきにくい変化を指摘してくれます。
専用アプリのグラフを眺めるだけだった頃より、記録を相談相手として使える納得感があります。

詳細③:なぜ「アプリ」が必要だったのか

ここまでが gym-mcp の話です。
スクショ記録で体組成・筋トレ・ランの単発データは集約できました。
でも、心拍・睡眠・歩数といった連続データは、Zeppの中に残ったままでした。

これらをサーバーに持ってくるには、Health Connectから読み取るしかありません。
そして、Health Connectの読み取りは、Androidのネイティブアプリでしか実現できないのです。
サーバー側のスクリプトやMCPからは触れません。
だから、ここだけは専用のAndroidアプリを作る必要がありました。
それが「からだ同期」アプリです。

このアプリが取り込むのは、Health Connectが確実に流してくれて、かつスクショが苦痛な日次データだけに絞っています。
具体的にはこうです。

からだ同期アプリが取り込む日次データの項目と、Health Connectでの取り方

体重・体脂肪はあえて扱いません。
体組成はタニタの体組成計のほうが項目が圧倒的に細かいので、そちらをスクショ経由で記録する役割分担にしてあります。
Health Connectからは読み取り専用で、こちらから書き込むことはしません。

詳細④:自動同期の仕組み(2段構えと冪等性)

「からだ同期」アプリの中身を掘り下げます。
同期は2段構えになっています。

  • Health Connect → アプリ内のローカルDB:Health Connectから読み取った各指標を、アプリの中のデータベース(日次1行)に取り込みます。指標ごとに別々のレコードから来るので、「その日の行を取ってきて、読めた指標だけを上書きし、欠けている列は潰さない」というマージのしかたにしています。
  • ローカルDB → サーバー:前回から変わった分(差分)だけを、サーバーの受け取り口(/sync という窓口)へ送ります。

ここで効いている設計判断がいくつかあります。

  • 冪等にする(二重送信で重複しない):各行にアプリ側で発行した一意なIDを付け、サーバーはそのIDをキーに上書き保存(upsert)します。だから、同じデータを2回送っても重複行は増えません。通信が不安定な状況で「届いたか分からないからもう一度送る」をしても安全です。
  • 同期ウィンドウは「直近35日」を毎回読み直す:差分の細かい追跡に頼らず、毎回ある程度の期間をまとめて読み直しています。日次の集計はやり直しが必要になることがあるためで、データ量がごく小さいので、この“安全に重ねて読む”やり方のほうが堅牢です。
  • ローカルDBをあえて残す:Health Connectのデータは約30日で古いものから消えていくローリング窓です。アプリ内に累積保持しておくことで、Health Connectから消えた古い値も保ったまま、欠けのない完全な行をサーバーへ送れます。

詳細⑤:「開かなくても溜まる」ための自動化

連続データの取り込みは、自分で意識しないと続きません。
そこで自動化を2段で入れています。

  • アプリ起動時の同期:アプリを開くたびに、まず同期が走ります。
  • 定期同期:Androidのバックグラウンド実行の仕組み(WorkManager)で、12時間ごとにネットワークがあるときに自動同期します。

これにより、アプリをわざわざ開かなくても、Health Connectの約30日のローリング窓が溢れる前にデータを取り込めます。
日々の歩数や睡眠は、文字どおり放っておいても自宅サーバーに溜まっていきます。

同期の処理には自己ガードを入れていて、Health Connectが使えない・権限が無いといった状況では、エラーで落ちずに何もしないで終わります。
だから起動時に無条件で呼んでも安全です。

詳細⑥:実機で見つかった「睡眠の二重計上」バグ

実装で一番ハマったのが睡眠です。
これは実際に自分のスマホで動かして初めて見つかった不具合なので、正直に書いておきます。

最初の作りでは、睡眠の各セッションの長さ(起床−就寝)を、就寝した日に単純に足し合わせていました。
ところが実機で確認すると、ある日の睡眠が異常に長く出ました。
原因を生データで追ったところ、2晩ぶんの睡眠が、同じ暦日に「就寝」していたのです。
たとえば、

前日の深夜 0:28 → 朝 6:34(前日夜から当日朝の睡眠)
当日の夜 23:50 → 翌日 8:04(当日夜から翌日朝の睡眠)

この2つが「同じ日に就寝」と判定され、合算されて14時間超という非現実的な値になっていました。
これを、Health Connectの公式の睡眠集計を1日ごとにスライスして取る方式に変え、日をまたぐ睡眠を日付の境界で按分し、覚醒していた時間を除く形に直しました。
結果、歩数や心拍と同じ集計の経路に統一され、値が正常になりました。

実際に動かさないと出ないタイプのバグで、「動作未確認のまま“たぶん動く”ままにしない」ことの大切さを改めて感じた一件でした。

詳細⑦:プライベートな健康データを守る

健康データは特にセンシティブなので、安全性は外せません。

  • 送信経路はTailscale直:アプリからサーバーへの送信は、インターネットに公開した口ではなく、Tailscale(プライベートな仮想ネットワーク)の中だけを通します。
  • 合言葉(トークン)で認証:受け取り口には、知らない相手が勝手に書き込めないよう、合言葉(Bearerトークン)を必須にしています。
  • Health Connectは読み取り専用:こちらからHealth Connectに書き込むことはありません。必要な6種類の読み取り権限だけを要求します。

健康記録はすべて自宅サーバーのデータベースに保存され、外部のクラウドには出していません。
自分のデータを自分の手元で持つ、という安心感があります。

どう作ったか(技術構成)

中身を自分で作ってみたい人向けに、土台を書いておきます。

gym-mcp(サーバー側)

  • 言語・土台:Python + FastMCP(MCPサーバーを作るためのフレームワーク)
  • 通信:Streamable HTTP
  • 保存先:SQLite(ファイル1つで完結する軽量データベース。書き込みの取りこぼしを防ぐWALモードを有効化)
  • 動かし方:Dockerコンテナ
  • データは用途ごとにテーブルを分けて整理(種目の定義/セッション/各種目の実績/ランニング/体組成)

からだ同期アプリ(Android側)

  • 言語:Kotlin、画面は Jetpack Compose(Material 3)
  • ローカルDB:Room、設定の保存に DataStore
  • Health Connect:公式のHealth Connectクライアント
  • 自動化:WorkManager(定期実行)

どちらも、プログラムの大部分はClaude Codeに書いてもらいました。
非エンジニアでも、作りたいものの全体像と段取りを伝えて、小さく作って動かしながら育てていけば、ここまで形にできます。

導入と設定(概念)

導入の流れだけ、概念として説明します。

  • 自宅サーバーに gym-mcp を立てる(Dockerで起動)
  • claude.aiの外部接続(コネクター)に、このMCPサーバーを登録する
  • Androidに「からだ同期」アプリを入れ、Health Connectの読み取り権限を許可する
  • アプリにサーバーの接続先(URLと合言葉)を設定する
  • あとは放っておけば、連続データは自動で、単発データはスクショで集まっていく

前提として、自宅サーバーが立っていて、そこにTailscaleが入っていることが必要です。
このあたりは別の自宅サーバーの記事で扱っています。

使い方

日々の使い方はとてもシンプルです。

  • 体組成・筋トレ・ランを記録したいとき:結果画面をスクショして、Claudeに「これを記録して」と送る。話しかけて「胸の日、ベンチ60キロ10回3セット」と伝えてもOK。
  • 連続データ(歩数・心拍・睡眠):何もしなくていい。アプリが自動で集める。
  • 振り返りたいとき:Claudeに「先月のトレーニングと睡眠の関係をまとめて」と頼む。

記録したあと、Claudeが「この内容で記録しました」と読み取った数字を返すので、そこで確認して、違っていれば「体脂肪は18.2に直して」と言えば修正できます。
読み取り任せにせず、最後に自分の目で確認するのがコツです。

エコシステム:自宅サーバーの仕組みとの関係

このシステムは、これまで作ってきた自宅サーバーの仕組みの上に乗っています。

  • Tailscaleの記事:アプリからサーバーへ安全にデータを送れるのは、Tailscaleのプライベートネットワークがあるからです。
  • Obsidian MCPの記事gym-mcp は、Obsidian MCPに続いて作った2つ目のMCPサーバーです。「Claudeに外部の機能を持たせる」という同じ作り方の応用編にあたります。
  • スマホからClaude Codeを使う自作アプリの記事:たまった健康データをスマホから分析・相談するときに、その自作Webアプリが活きてきます。

自宅サーバーまわりの記事を読んでくださっている方は、この記事を「健康データを集約する回」として読んでもらえると、つながりが見えると思います。

よくあるトラブルとQ&A

実際に使っていて気づいた点を、Q&Aでまとめます。

Q. スクショから数字を読み間違えることはない?

A. ごくたまにあります。
なので記録のあとにClaudeが読み取った数字を返すようにしてあり、違っていれば修正のツールで直せます。
最後に自分の目で確認するのがコツです。

Q. 睡眠時間が変な値になったりしない?

A. これは作る過程で実際にハマったところです(詳細⑥)。
2晩ぶんの睡眠が同じ日に就寝扱いになって合算される不具合がありましたが、公式の集計を1日ごとにスライスする方式に直して解消しました。

Q. アプリを開かないと同期されない?

A. いいえ。
起動時の同期に加えて、12時間ごとの定期同期が走るので、開かなくてもHealth Connectの窓が溢れる前に取り込めます。

Q. 同じデータが二重に登録されない?

A. 各行に一意なIDを付け、サーバーはそのIDをキーに上書き保存します。
二重に送っても重複しません。

Q. データが外部に漏れることはない?

A. 記録はすべて自宅サーバーに保存し、インターネットには出していません。
アプリからの送信はTailscale経由+合言葉で守り、Health Connectは読み取り専用です。
あくまで自分専用、という前提で運用してください。

まとめ

いかがでしたか。
バラバラに散らばっていた健康データを、性質に合わせた方法で自宅サーバーに集約した話を紹介しました。
要点を振り返ります。

  • 単発でリッチなデータ(体組成・筋トレ・ラン)は、スクショをClaudeに読ませて記録するMCP(gym-mcp)で集約した
  • 連続して溜まるデータ(歩数・心拍・睡眠)は、Health Connectから自動で吸い上げるAndroidアプリで集約した
  • 同期は冪等・35日窓の読み直し・ローカル累積保持で堅牢にし、睡眠の二重計上バグは実機検証で見つけて直した

結果として、「トレーニングした日の睡眠は足りていたか」といった種類をまたいだ振り返りを、Claudeを相談相手にしながら1か所でできるようになりました。

「自分も同じ仕組みを作ってみたい」「コードを見てみたい」という方は、コメントで教えてください。無料でお渡しします。

この記事が誰かの役に立てばうれしいです。