Skip to main content
Glama
jcrispiniano

huckleberry-mcp-worker

by jcrispiniano

huckleberry-mcp-worker

Model Context Protocol サーバーで、Huckleberry ベビートラッキングアプリ向けのものです。Cloudflare Worker として動作します。

これは bckenstler/py-huckleberry-mcp の TypeScript 移植版です。オリジナルは Python の stdio サーバーで、google-cloud-firestore を通じて Firestore と通信しますが、これは gRPC を使用するため Workers では実行できません。この移植版は Firebase REST API を fetch で使用して同じバックエンドにアクセスし、MCP を Streamable HTTP で提供します。

実用的な違いは、常にオンラインであることです。クライアントが昼寝を記録するためにラップトップが起動している必要はありません。

ツール

Python サーバーの全 23 ツールに加え、delete_record が実装されています。

エリア

ツール

子供

list_children, get_child_name

睡眠

log_sleep, start_sleep, pause_sleep, resume_sleep, complete_sleep, cancel_sleep, get_sleep_history

授乳

log_breastfeeding, log_bottle_feeding, start_breastfeeding, pause_feeding, resume_feeding, switch_feeding_side, complete_feeding, cancel_feeding, get_feeding_history

おむつ

log_diaper, get_diaper_history

成長

log_growth, get_latest_growth, get_growth_history

記録

delete_record

すべての履歴ツールは各レコードの interval_id を報告します。これは delete_record が受け取るものです。

Related MCP server: remote-mcp-authless

Python サーバーからの修正点

移植中に4つの不具合が見つかり、ここで修正されています。

授乳時間の単位が間違っていました。 バックエンドは leftDuration / rightDuration を秒で保存します(アプリのタイマーが書き込む値です)が、log_breastfeeding は呼び出し元の 分 をそのまま渡していました。5分の授乳を記録すると5秒として記録されていました。以前は呼び出し元が5分を意味するために300を渡す必要がありましたが、ここでは left_duration_minutes: 5 で5分を意味します。

1日の履歴クエリが何も返さないことがありました。 日付範囲の両端が深夜0時に解決されるため、start_date == end_date では空のウィンドウが生成され、サーバーは記録がたくさんある日に対して何も報告しませんでした。範囲は半開区間 [start_of_start_date, start_of_end_date + 1 day) になり、両端を含むようになりました。

睡眠履歴の end_time が常に null でした。 コードはバックエンドが決して書き込まない end フィールドを読み取っていました。現在は start + duration から導出されています。

list_children の birth_date が常に null でした。 バックエンドのフィールドは birthdate ですが、サーバーは birthDate を読み取っていました。

get_feeding_history は各レコードの mode と、それに付随する詳細(哺乳瓶の量と種類、固形食の食品名と反応)も返すようになりました。これらがないと、固形食のレコードは空行になり、長さゼロの授乳セッションと区別がつきません。これは、完全に有効なレコードが欠落していると誤認される原因そのものです。

セットアップ

Node 18 以上と Cloudflare アカウントが必要です。

npm install
npx wrangler login

シークレットを設定します。これらは Cloudflare によって暗号化されて保存され、リポジトリには決して置かれません。

npx wrangler secret put HUCKLEBERRY_EMAIL
npx wrangler secret put HUCKLEBERRY_PASSWORD
npx wrangler secret put MCP_AUTH_TOKEN     # a long random string you generate
npx wrangler secret put HUCKLEBERRY_TIMEZONE   # e.g. America/Sao_Paulo

HUCKLEBERRY_TIMEZONE のデフォルトは America/New_York です。これは "2026-08-17T15:47:00" のようなナイーブな日時をどのように解釈するかを決定するため、正しく設定することが重要です。

デプロイします。

npm run deploy

認証

Worker URL は公開されており、サーバーは子供の健康記録への認証情報を保持しているため、すべてのリクエストはベアラートークンを携帯する必要があります。

Authorization: Bearer <MCP_AUTH_TOKEN>

有効なトークンがないリクエストは、Huckleberry 呼び出しが行われる前に 401 を受け取ります。トークンは openssl rand -base64 32 などで生成してください。

ヘッダーを送信できないクライアント

一部の MCP クライアントは URL のみを受け入れます。たとえば claude.ai のカスタムコネクタは、URL とオプションの OAuth 認証情報を受け取りますが、Authorization のフィールドはありません。そのような場合のために、サーバーはトークンを最後のパスセグメントとしても受け入れます。

POST https://<your-worker>.workers.dev/mcp/<MCP_URL_TOKEN>

MCP_URL_TOKEN は MCP_AUTH_TOKEN とは別のシークレットであり、意図的にそうしています。リクエストパスは、ヘッダーとは異なる方法でアクセスログ、ブラウザ履歴、リファラーに残ります。これらを分離することで、URL を介した漏洩がヘッダーの認証情報を危険にさらすことはなく、それぞれを個別にローテーションできます。MCP_URL_TOKEN が設定されていない場合、ルートは MCP_AUTH_TOKEN にフォールバックします。これは便利ですが、その分離を放棄することになります。

npx wrangler secret put MCP_URL_TOKEN

クライアントがサポートする場合は、ヘッダールートを優先してください。

クライアント設定

Claude Code の場合:

claude mcp add --transport http huckleberry https://<your-worker>.workers.dev/mcp \
  --header "Authorization: Bearer <MCP_AUTH_TOKEN>"

ローカル開発

cp .dev.vars.example .dev.vars   # then fill it in; .dev.vars is gitignored
npm run dev
curl -X POST http://localhost:8787/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

設計ノート

ステートレス。 各リクエストは、Cloudflare の agents SDK の createMcpHandler を介して新しい McpServer を構築します。Durable Objects やセッションストレージは関与しません。すべてのツールは自己完結型の読み取りまたは書き込みだからです。

トークンキャッシュ。 Firebase ID トークンは1時間有効で、モジュールスコープにキャッシュされるため、ウォームなアイソレーションに到達したリクエストは再認証をスキップします。コールドなアイソレーションでは、1回の追加ラウンドトリップが発生します。途中でトークンが拒否された場合は、1回の再認証と再試行がトリガーされます。

数値型。 Firestore は整数と倍精度浮動小数点数を区別し、アプリは一部のフィールドを整数で、一部を倍精度浮動小数点数で書き込みます。倍精度浮動小数点数として保存する必要がある値は dbl() でラップされるため、ここで書き込まれたレコードはアプリで書き込まれたレコードと一致します。

マルチエントリードキュメント。 履歴は2つの形状で存在します。トップレベルの start を持つ通常のドキュメントと、data の下に多数のエントリーを持つバッチドキュメントです。ネストされた開始はサーバー側でフィルタリングできないため、バッチドキュメントは全体を取得し、Worker 内でフィルタリングされます。レコードは is_multi_entry を介して、どの形状から来たかを報告します。

レコードの削除

Python サーバーには削除機能がなく、バックエンドが削除を許可していないというのが通説でした。実際には許可しています。ドキュメントパスへの DELETE は 200 を返します。実際に欠けていたのはレコードの ID であり、履歴ツールはそれを報告していませんでした。

そのため、履歴ツールは interval_id を返すようになり、delete_record はそれが指定するレコードを削除します。バッチエントリー(data の下に複数のレコードが1つのドキュメントにまとめられたもの)は <documentId>#<entryKey> としてアドレス指定され、親のフィールドとして削除されます。

削除はまた、prefs.last* を最新の生存レコードに再ポイントします。アプリはこれらのポインターを直接読み取るため、再ポイントなしで削除すると、存在しないレコードを表示したままになります。

既知の制限事項

  • 削除は永続的です。 元に戻すことはできません。delete_record を呼び出す前に、履歴クエリで確認してください。

  • 固形食は読み取り専用です。 get_feeding_history は固形食エントリーを食品名と反応とともに報告しますが、作成するツールはありません。

  • start_sleep は既に実行中のタイマーを防ぎません。 Python サーバーはその場合に失敗することを文書化していましたが、実際にはチェックしていませんでした。ここではその動作を維持しており、静かに変更していません。

  • メモは睡眠レコードに往復しません。 details フィールドはチェックボックスの固定構造であり、自由テキストではありません。

ライセンス

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    A
    maintenance
    Huckleberry MCP server for Claude, Cursor, and other AI assistants. Query and log baby sleep, feeds, diapers, growth, pumping, and solids.
    29
    21 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for UploadThing that lets AI assistants upload, list, and delete files on UploadThing's CDN via natural language. Runs as a Cloudflare Worker for always-on serverless access.
    16 npm
    MIT