gaggiuino-mcp について
gaggiuino-mcpは、GaggiuinoエスプレッソマシンをAIツールに接続し、状態確認、抽出分析、プロファイル管理を行うリモートMCPサーバーです。マシンのデータに、抽出グラフ、調整ガイダンス、ワークフロープロンプト、プロファイル操作を組み合わせています。
主な機能
- 01マシンの状態と設定
- 02抽出の要約と時系列データ
- 03抽出グラフと比較表示
- 04プロファイルの表示と管理
- 05調整ガイダンスとワークフロープロンプト
活用シーン
- 01抽出前のマシン状態確認
- 02調整中の抽出結果の比較
- 03抽出プロファイルの選択や変更
- 04メンテナンス履歴の確認
MCP 対応
リモートMCPサーバーとして、マシン状態、抽出データ、設定、プロファイル、マシンを変更する操作のツールを提供し、ワークフロープロンプトとプロファイルリソースも公開します。プロファイル変更には認証と書き込み権限が必要です。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
docker run -i --rm -e GAGGIUINO_URL -e PORT -e HOST -e MCP_PUBLIC_URL -e MCP_OAUTH_SECRET -e MCP_OAUTH_PASSPHRASE_HASH -e MCP_OAUTH_ISSUER -e MCP_ALLOWED_ORIGINS -e MCP_ALLOWED_HOSTS -e LOG_LEVEL ghcr.io/ljcl/gaggiuino-mcp:6.0.0GAGGIUINO_URLGaggiuino machine URL (default http://gaggiuino.local)PORTServer port (default 8000)HOSTBind address (default 0.0.0.0)MCP_PUBLIC_URLPublic https origin clients reach this server on, with no path (e.g. https://box.tailnet.ts.net). Set with MCP_OAUTH_SECRET to enable OAuth; it is advertised as the OAuth resource and must match the URL entered in the client exactlyMCP_OAUTH_SECRETシークレットSigning key for self-issued OAuth access and refresh tokens, at least 32 characters (openssl rand -hex 32). Keep it stable across restarts so clients stay signed inMCP_OAUTH_PASSPHRASE_HASHシークレットscrypt hash of the passphrase you type on the OAuth consent page, in the form scrypt$N$r$p$salt$hash. Required whenever OAuth is enabled. Generate with `bun run hash-passphrase` — never store the passphrase itselfMCP_OAUTH_ISSUERシークレットExternal OAuth issuer URL to delegate token issuing to (e.g. a Keycloak or Authentik realm). Puts the server in resource-server-only mode; requires MCP_PUBLIC_URL and refuses to start alongside MCP_OAUTH_SECRET or MCP_OAUTH_PASSPHRASE_HASHMCP_ALLOWED_ORIGINSComma-separated browser origins allowed to call /mcp, or * to allow any (unsafe). Requests with no Origin header are unaffectedMCP_ALLOWED_HOSTSComma-separated Host header values to accept on /mcp. Empty disables host validationLOG_LEVELLog verbosity: debug, info, warn, error, or silent (default info). Logs are one JSON object per line on stderrローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ghcr.io/ljcl/gaggiuino-mcp:6.0.0Streamable HTTPツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Gaggiuino MCP サーバー
AI ツールと Gaggiuino エスプレッソマシンを連携するリモート MCP サーバーです。AI アシスタントに、マシンの状態確認、抽出データの分析、ダイヤルインのアドバイスを依頼できます。
機能
MCP ツール
抽出分析
get_status- マシンの現在の状態(温度、圧力、重量、水位)get_latest_shot_id- 最新の抽出の ID と主要な数値をまとめて取得しますlist_recent_shots- 直近の複数の抽出を要約し、セッション中の傾向を確認しますget_shot_data- 指標を含む、構造化された抽出の概要get_shot_raw_data- 時系列データ全体view_shot_graph- インタラクティブな抽出グラフ。MCP Apps に対応するホストではグラフが表示され、その他のホストではテキストの概要のみが表示されます(目標値のオーバーレイと任意の抽出比較を含む、時間経過に伴う圧力、流量、重量)
プロファイルと設定
list_profiles- マシン上のプロファイルを、このサーバーのドキュメントと統合して一覧表示しますget_profile_info- 1 つのプロファイルについて把握している情報をすべて取得しますget_machine_settings- マシンから報告されたボイラー、スチーム、スケールの設定get_maintenance_status- マシンが記録している除石灰とバックフラッシュの履歴、およびそれぞれの実施後の抽出回数get_dial_in_guidance- エスプレッソの抽出を分析するための専門的なアドバイスselect_profile- アクティブなプロファイルを切り替えます(マシンが変更されます。認証済みサーバーが必要です)upload_profile- 新しい抽出プロファイルをマシンに保存します(マシンが変更されます。認証済みサーバーが必要です)。作成のみで、更新は行いません。マシンは毎回新しい ID を割り当てるため、2 回アップロードするとプロファイルが 2 つ作成されますdelete_profile- マシンからプロファイルを削除します(破壊的で元に戻せません。認証済みサーバーが必要です)。確認のため、プロファイルの名前を正確に入力する必要があります。現在選択中のプロファイルは削除できず、ホスト上では「常に許可」を保存している場合でも、必ず承認が求められます
MCP プロンプト - ホストでスラッシュコマンドやメニュー項目として表示されるワークフローテンプレートです。
dial_in_new_bag- まだ抽出したことのないコーヒーで最初の抽出を行うためのプロンプト(豆、および任意で焙煎度、粉量、カップに求める味わい)diagnose_last_shot- 直前の抽出を味の感想と照らし合わせて確認するためのプロンプト(問題点、および任意で変更した内容)choose_profile- コーヒーに合わせて、マシンが実際に保持しているプロファイルを選ぶためのプロンプト(焙煎度、および任意で飲み方やメモ)espresso_shot_analyst- ダイヤルインのアドバイスをシステムプロンプトとして提供します(get_dial_in_guidanceと同じ内容です)
各ワークフロープロンプトでは、呼び出すツールの順序が示されるため、推測ではなく、マシン自身のデータに基づいて分析を始められます。
MCP リソース - プロファイルデータを取得する gaggiuino://profiles と gaggiuino://profiles/{id}
クイックスタート
このサーバーはマルチアーキテクチャイメージ(linux/amd64、linux/arm64)として
ghcr.io/ljcl/gaggiuino-mcp
で公開されているため、クローンやビルドは不要です。また、
MCP Registry
にも io.github.ljcl/gaggiuino-mcp として登録されています。
1. ダウンロードと設定
mkdir gaggiuino-mcp && cd gaggiuino-mcp
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/.env.example
.env を編集し、Gaggiuino マシンのアドレスを設定します。
# Use the IP directly (recommended)
GAGGIUINO_URL=http://192.168.1.100
# Or if mDNS works on your network
GAGGIUINO_URL=http://gaggiuino.local
2. サーバーの起動
docker compose up -d
3. 動作確認
curl http://localhost:8000/health
サーバーは http://<your-docker-host>:8000/mcp で利用できます。
バージョンの選択
compose ファイルは latest を参照しており、常に最新リリースが使用されます。特定のバージョンに固定するには、.env で
GAGGIUINO_MCP_TAG を設定します。
GAGGIUINO_MCP_TAG=5.0 # newest 5.0.x patch
GAGGIUINO_MCP_TAG=5.0.0 # exact release
リリース前の main のビルドは、edge(および main-<sha>)として公開されます。リリースでまだ告知されていない破壊的変更が含まれる可能性があるため、修正の公開前に試す場合にのみ使用してください。
アップグレードするには、次を実行します。
docker compose pull && docker compose up -d
設定
環境変数
| 変数 | デフォルト値 | 説明 |
|---|---|---|
GAGGIUINO_URL | http://gaggiuino.local | Gaggiuino マシンの URL |
PORT | 8000 | MCP サーバーのポート |
HOST | 0.0.0.0 | バインドするホスト |
MCP_PUBLIC_URL | (unset) | クライアントがこのサーバーにアクセスするパブリックな https オリジン。パスは含めません(例:https://box.tailnet.ts.net)。OAuth を有効にするには、MCP_OAUTH_SECRET と合わせて設定します。OAuth の resource として通知されるため、クライアントに入力する URL と完全に一致している必要があります。 |
MCP_OAUTH_SECRET | (unset) | 自己発行 OAuth トークンの署名キー。32 文字以上にしてください(openssl rand -hex 32)。クライアントのログイン状態を維持できるよう、再起動後も同じ値を使用してください。この 2 つのうち一方だけを設定すると、起動時にエラーになります。 |
MCP_OAUTH_PASSPHRASE_HASH | (unset) | クライアント接続時の同意ページで入力するパスフレーズの scrypt ハッシュです。組み込み認可サーバーが有効な場合は必須です。これがないと、同意ページにアクセスした人なら誰でもトークンを取得できてしまうため、サーバーは起動を拒否します。cd apps/server && bun run hash-passphrase で生成してください。パスフレーズ自体は保存しないでください。 |
MCP_OAUTH_ISSUER | (unset) | すでに運用している ID プロバイダー(Authentik、Keycloak、Authelia、Zitadel、Kanidm、tsidp)にトークン発行を委任します。これを設定すると、このサーバーは独自の OAuth エンドポイントを提供せず、代わりに ID プロバイダーの JWKS を使って RS256/ES256 トークンを検証します。MCP_PUBLIC_URL が必要です。また、MCP_OAUTH_SECRET または MCP_OAUTH_PASSPHRASE_HASH と同時に設定すると起動しません。外部 ID プロバイダーを使用するを参照してください。 |
MCP_ALLOWED_ORIGINS | (empty) | /mcp への呼び出しを許可するブラウザーのオリジンをカンマ区切りで指定します。* を指定するとすべて許可されます(安全ではありません)。 |
MCP_ALLOWED_HOSTS | (empty) | 許可する Host ヘッダーの値をカンマ区切りで指定します。空の場合、チェックは無効になります。 |
LOG_LEVEL | info | debug、info、warn、error、または silent。ログは 1 行につき 1 つの JSON オブジェクトとして stderr に出力されます。 |
ヘルスチェックとログ
GET /health は JSON を返します。
{
"status": "ok",
"version": "3.2.0",
"uptimeSec": 3412,
"machine": {
"url": "http://gaggiuino.local",
"state": "unreachable",
"lastCheckedAt": "2026-07-27T21:11:15.274Z",
"lastError": "Unable to connect. Is the computer able to access the url?",
"versions": {
"coreVersion": "a06f97fd",
"frontVersion": "a06f97fd",
"staticVersion": "a06f97fd"
}
}
}
プロセスが稼働していれば、マシンに接続できない場合も含めて常に 200 を返します。エスプレッソマシンは一日の大半が電源オフであり、コンテナーのヘルスチェックはステータスコードを確認するためです。machine.state は ok、unreachable、または unknown で、プローブではなくサーバーがすでに行ったリクエストから確認されます。そのため、/health がマシンに余計な負荷をかけることはありません。machine.versions も同様に、マシンの設定を最初に読み取ったとき(get_machine_settings)に記録されます。/health 自体が取得することはありません。したがって、ここで null の場合は「このサーバーはまだ設定を読み取っていない」という意味であり、「マシンがバージョンを報告していない」という意味ではありません。公開されるのは、ドキュメントに記載されたこれら 3 つのフィールドのみです。/health は認証なしでアクセスできるため、今後のファームウェアが versions に追加する情報は、別途判断されるまでは公開されません。
ログは 1 行につき 1 つの JSON オブジェクトとして出力されるため、必要な情報を取り出せます。
docker compose logs -f | jq -c 'select(.event == "tool.call" and .outcome != "ok")'
エンドポイントを保護する
このサーバーを LAN の外部に公開する前に、OAuth を有効にしてください。 以下のトンネルオプションはいずれも /mcp をパブリックインターネット上に公開します。認証がなければ、URL を知っている人なら誰でも、キッチンにあるマシンに対してすべてのツールを利用できてしまいます。何も設定されていない場合、サーバーは起動時に警告を表示します。
次の 3 つの変数は、すべてまとめて設定する必要があります。
# 1. The public origin clients will reach the server on — no path, https only.
# This is advertised as the OAuth `resource`, so it must match the URL you
# type into Claude exactly.
MCP_PUBLIC_URL=https://your-machine.tail-scale.ts.net
# 2. The key your tokens are signed with. Keep it stable across restarts, or
# every restart signs you out of your phone.
openssl rand -hex 32
# 3. The passphrase you will type on the consent page. Store the hash, never
# the passphrase — this prompts and prints the whole line to paste in.
cd apps/server && bun run hash-passphrase >> ../../.env
その後、コンテナを再作成してください。通常の再起動だけでは不十分です。
docker compose up -d --force-recreate
Compose が追跡するのは env_file のパスの一覧であり、ファイルの内容ではありません。そのため、.env を編集して docker compose up -d(または restart)を実行しても、作成時の環境変数とともに既存のコンテナが再利用されることがあります。新しい変数はプロセスに渡されず、サーバーは設定していない場合とまったく同じように、未認証で起動します。サーバーから見ると何も設定されていないため、エラーも表示されません。実際に渡された内容を確認してください。
docker inspect gaggiuino-mcp --format '{{range .Config.Env}}{{println .}}{{end}}' | grep MCP_
一部の変数だけを設定すると、起動時に不足している変数を示して失敗します。これは意図的な動作です。認証されていないエンドポイントへフォールバックしてしまうと、保護されていると思い込んだままトンネルを公開することになるためです。**原因を調べる際には、次の点も知っておくと役立ちます。**サーバーが起動していて認証されていない場合、3 つの変数はどれも読み込まれていません。設定したはずなのにそうなっている場合、設定が間違っているのではなく、コンテナが古いままです。
共有トークンではなく OAuth を使う理由
このサーバーが受け付ける認証情報は OAuth だけです。**Claude コネクターに提示できる認証情報が OAuth だけだからです。**コネクターはアカウント単位で追加するため、1 つのエントリーで claude.ai、Claude Desktop、iOS のすべてに対応する必要があります。また、個人向けプランの「カスタムコネクターを追加」ダイアログで指定できるのは OAuth Client ID と Secret であり、リクエストヘッダーの項目はありません。ローカルの stdio ブリッジで回避することもできません。iOS では実行できないためです。共有シークレットをヘッダーに設定しても、クライアントから送信できないまま .env に残り、このプロジェクトが対象とする環境で書き込みツールが恒久的に拒否されます。(2.0 より前にインストールし、長期間使っている .env に MCP_AUTH_TOKEN が残っている場合は、その行を削除してください。どこからも読み取られず、/mcp の保護にも使われません。)
接続すると、Claude がエンドポイントを検出し、このサーバーが提供する同意ページに移動します。そこでパスフレーズを入力してください。登録は不要で、保存するクライアントシークレットもありません。
各部分が保護するもの
/health と /.well-known/* のディスカバリードキュメントは、意図的に認証なしでアクセスできるようになっています。コンテナのヘルスチェックは認証情報を提示せず、クライアントが認証するために取得するドキュメント自体に認証を要求することはできないためです。
select_profile、upload_profile、delete_profile は、マシンを変更するツールであり、espresso:write スコープが必要です。このスコープを持たないトークンには 403 が返され、失敗しても何も通知されないのではなく、追加の権限を求めるよう Claude に促します。何も設定されていない場合、これらのツールは実行を拒否し、その旨を伝えます。そのため、LAN 上でサーバーを公開するのは妥当な既定の選択ですが、マシン制御ツールを公開するのは妥当ではありません。
Origin ヘッダーを含むリクエストは、オリジンが MCP_ALLOWED_ORIGINS に記載されていない限り拒否されます。これにより、アクセスした任意のウェブページが、自分のネットワーク上で実行中のサーバーに POST するのを防ぎます。ブラウザーがトークンを代わりに送信するため、ここではトークンでは防げません。Origin のないリクエスト(Claude Desktop、curl、ブラウザー以外のもの)には影響しないため、既定の空リストがほとんどの人にとって適切な設定です。
オリジンを登録すると、/mcp はそのオリジンからの CORS プリフライトに応答し、Access-Control-Allow-Origin(および Access-Control-Expose-Headers: mcp-session-id)を返すようになります。これがないと、ブラウザークライアントはハンドシェイクを読み取れても、続行に必要なセッションを読み取れません。ブラウザーがブロックするオリジンを許可しても、実質的には何も許可していないのと同じです。
scripts/test-auth.sh は、実行中のサーバーに対して上記のすべて、つまりディスカバリーチェーン、401 の形式、ホスト間リダイレクト、オリジン検証をテストします。LAN の外から実行してください。 検出される失敗はすべて、Claude がアクセスする URL の問題です。特に最もよく発生する、MCP_PUBLIC_URL と Claude に入力した URL の不一致は、localhost からは確認できません。
BASE_URL=https://your-machine.tail-scale.ts.net ./scripts/test-auth.sh
カスタマイズ
サーバーには、汎用プロファイルとプロンプトが付属しています。起動時に、次の 2 つのローカル上書きファイルが既定値にマージされます。
-
prompts.local.yaml- 機器に合わせたダイヤルインのガイダンス(グラインダーのモデル、バスケット、その他の機器の詳細など)。 -
profiles.local.yaml- 独自のプロファイル、または既定値の上書き・削除(プロファイル ID にnullを設定すると削除できます)。
サンプルをベースにしてください。
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/apps/server/src/data/prompts.example-local.yaml
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/apps/server/src/data/profiles.example-local.yaml
これらのファイルには個人の機器設定が含まれるため、公開イメージには意図的に組み込まれていません。使用するには、docker-compose.yml の volumes: ブロックのコメントを解除してください。
volumes:
- ./profiles.local.yaml:/app/apps/server/src/data/profiles.local.yaml:ro
- ./prompts.local.yaml:/app/apps/server/src/data/prompts.local.yaml:ro
リポジトリをチェックアウトしている場合は、代わりに各 *.example-local.yaml を *.local.yaml にコピーし、apps/server/src/data/ に配置してください。これらのファイルは gitignore の対象で、自動的に読み込まれます。
上書きファイルがなくても問題ありません。上書きファイルが存在するのに読み込めない場合(たとえば、コンテナのユーザー UID 65534 に読み取り権限がないバインドマウント)、YAML の形式が正しくない場合、または構造が正しくない場合、サーバーは起動時に停止します。ログ行は config.invalid で、ファイル名と問題のあるキーが示されます。サーバーは壊れた上書きファイルを無視して、そのまま起動することはありません。
AI ツールへの接続
多くの AI ツール(Claude Desktop など)は、MCP リクエストをローカルマシンからではなく、独自のサーバー経由でルーティングします。つまり、MCP サーバーにはパブリック HTTPS URL からアクセスできる必要があります。
このセクションのどの方法でも、
/mcpがインターネットに公開されます。まず OAuth を有効にしてください。 詳しくはエンドポイントの保護を参照し、公開しようとしている正確なオリジンにMCP_PUBLIC_URLを設定してください。
どのイングレスを選ぶ場合も、正常に動作するかどうかは次の 3 点で決まります。
- すべてを 1 つのオリジンで提供します。
/mcp、/.well-known/*、/oauth/*のすべてにMCP_PUBLIC_URLからアクセスできる必要があります。これにより、このコンテナーが 1 つで済みます。 - 安定したホスト名を使います。 再起動のたびに変わるクイックトンネルのホスト名では、
公開される
resourceが変わり、コネクターが毎回動作しなくなります。 - ホストをまたぐリダイレクトを避けます。 登録済み URL が別のホストに
301/302/307/308リダイレクトすると、Authorizationヘッダーが途中で削除されます。これは「MCP Inspector や Claude Code では動くのに claude.ai では動かない」場合に よくある原因です。サーバーの前段で apex からwwwへ正規化するのが、よくある発生パターンです。scripts/test-auth.shで確認できます。
Claude は URL ごとにディスカバリードキュメントをグローバルに約 5 分間キャッシュするため、メタデータの変更はすぐには反映されません。また、デプロイに問題があった場合、そのメタデータは修正後も数分間配信されることがあります。
Tailscale Funnel(推奨)
Tailscale Funnel は、安全な HTTPS URL を使ってサーバーをインターネットに公開します。
tailscale funnel --bg 8000
# URL: https://your-machine.tail-scale.ts.net/mcp
# then, in your .env
MCP_PUBLIC_URL=https://your-machine.tail-scale.ts.net
Serve ではなく Funnel を使ってください。 Claude は Anthropic 自身のインフラからコネクターにアクセスします。解決されたアドレスのいずれかがグローバルにルーティング可能でない場合、データを 1 バイトも送信する前にホスト名を拒否します。これには、テイルネット独自の範囲である 100.64.0.0/10 も明示的に含まれます。tailscale serve はまさにこれらのアドレスを公開します。一方、Funnel は Tailscale のリレーを指すパブリックレコードを公開します。また、コネクターは IPv4 のみに対応しているため、AAAA レコードのみを公開するホスト名には到達できません。次の 1 つの確認で両方を検証できます。
dig +short your-machine.tail-scale.ts.net # must return a routable IPv4 address
ここでよく使われがちですが、使うべきではないものが 2 つあります。
- Anthropic の送信範囲(
160.79.104.0/21)をアクセス制御の許可リストに追加しないでください。 Funnel はクライアント IP を転送しません。また、/oauth/authorizeにアクセスするのは Anthropic ではなくユーザー自身のブラウザーです。そのため、IP 許可リストを使うと何も保護できないままログインが妨げられます。 Tailscale-User-Loginヘッダーを信頼しないでください。tailscaledは偽装されたコピーを削除しますが、プロキシするトラフィックに限られます。このプロジェクトはnetwork_mode: hostとHOST=0.0.0.0を使うため、リスナーに直接アクセスでき、ホスト上の誰でもそのヘッダーを設定できます。このヘッダーを信頼すると、書き込み権限に限定された認証バイパスが発生します。
Cloudflare Tunnel
cloudflared を使って、サーバーへの永続的なトンネルを作成します。自分が所有するホスト名の名前付きトンネルを使い、クイックトンネルは使わないでください。trycloudflare.com のホスト名は再起動のたびに変わるため、それに合わせて MCP_PUBLIC_URL も変更する必要があります。
ngrok
ngrok http 8000
同じ注意点があります。無料の ngrok ホスト名は変わります。ドメインを予約するか、接続のたびにコネクターを追加し直してください。
独自のリバースプロキシの背後に配置する場合
3 つのパス(/mcp、/.well-known/*、/oauth/*)すべてを同じアップストリームにプロキシし、MCP_PUBLIC_URL をパブリックオリジンに設定します。プロキシでホストをまたぐ正規化を行わないようにしてください(上記のリダイレクトに関する注意を参照)。また、Authorization ヘッダーを変更せずに転送する必要があります。
外部 ID プロバイダーの使用
すでに Authentik、Authelia、Keycloak、Zitadel、Kanidm、または tsidp
を運用している場合は、MCP_OAUTH_ISSUER にその発行者 URL を設定すると、
このサーバーは認可サーバーとして一切機能しなくなります。
MCP_PUBLIC_URL=https://gaggiuino.tail1234.ts.net
MCP_OAUTH_ISSUER=https://idp.example.com/realms/home
/oauth/authorize、/oauth/token、および
/.well-known/oauth-authorization-server は完全に提供されなくなり、
保護されたリソースのメタデータは、代わりに Claude をお使いの発行者へ誘導します。ここでは一切署名を行わなくなるため、MCP_OAUTH_SECRET と MCP_OAUTH_PASSPHRASE_HASH は不要です。MCP_OAUTH_ISSUER と同時にどちらかを設定すると、無視されるのではなく 起動時に拒否されます。無視してしまうと、実際には表示されない同意ページが表示されると誤解させてしまうためです。
MCP_PUBLIC_URL は引き続き必須です。トークンのオーディエンスは、この値を基に検証されます。
製品を問わず、ID プロバイダーでは次の 4 点を満たす必要があります。
code_challenge_methods_supportedで S256 PKCE を広告していること。- Anthropic の送信元 IP 範囲
160.79.104.0/21からディスカバリーに到達できること。ID プロバイダーの前段に WAF があると、このサーバーに問題なく到達できてもフローが動作しなくなります。 - トークンのオーディエンスが
MCP_PUBLIC_URL+/mcpと一致すること。 Keycloak では、このために Audience-mapper クライアントスコープが必要です。現行の仕様バージョンでは RFC 8707 をサポートしていないため、仕様で最も強く要求されているこの項目は、Keycloak では手動で設定する必要があります。 - リダイレクト URI が
https://claude.ai/api/mcp/auth_callbackであること。 Claude Code を使用する場合は、ポートに依存しないhttp://localhost/callbackとhttp://127.0.0.1/callbackも追加してください。
**動的クライアント登録は必須ではありません。**互換性に関わるため、はっきりお伝えしておきます。Claude では、コネクターのダイアログに事前登録済みのクライアント ID を貼り付けて使用できるため、DCR に対応していない Authelia、Authentik、Zitadel、Kanidm も、クライアントを手動で 1 つ作成すればすべて問題なく使用できます。
トークンには RS256 または ES256 で署名する必要があります。その他のアルゴリズムは検証されません。これは意図的な制限であり、見落としではありません。JWKS から取得した鍵に対して HS256 を許可すると、攻撃者が発行者の 公開 鍵でトークンに署名するという、典型的なアルゴリズム混同攻撃が可能になるためです。
ディスカバリーでは、まず RFC 8414 に従い、次に
/.well-known/openid-configuration にフォールバックします。そのため、OpenID ドキュメントのみを公開する発行者も、変更なしで動作します。scripts/test-auth.sh はすでに、authorization_servers が示す接続先に対してこの一連の手順をテストします。そのため、外部の発行者も組み込みの発行者と同じように診断できます。
tsidp については、推奨というより導入コストの観点から補足が必要です。2 つ目のコンテナー、永続化する /data ボリューム、allow_dcr 用の tailnet ACL 許可、TAILSCALE_USE_WIP_CODE=1 が必要であり、さらに上流プロジェクトは実験的なプレ 1.0 と説明しています。すでに Tailscale を深く活用している場合は選択肢になりますが、そうでなければ組み込みの認可サーバーのほうが構成要素は少なくて済みます。
ローカルネットワークのみ
AI ツールが直接接続する場合(例:ローカル MCP サーバーの設定)は、直接アドレスを使用してください。
http://<docker-host-ip>:8000/mcp
Claude Desktop への追加
- Settings > Integrations > Add More > Add Remote MCP Server の順に移動します。
- URL にパブリック HTTPS エンドポイント(例:
https://your-machine.tail-scale.ts.net/mcp)を設定します。 - 保存して有効にします。
アーキテクチャ
AI Tool (Claude Desktop, etc.)
|
| HTTPS
v
+-----------------------------+
| HTTPS Tunnel |
| (Tailscale / Cloudflare / |
| ngrok / reverse proxy) |
+-----------------------------+
|
| HTTP (localhost:8000)
v
+-----------------------------+
| Gaggiuino MCP Server |
| (Docker container) |
| Bun + Streamable HTTP |
+-----------------------------+
|
| HTTP (local network)
v
+-----------------------------+
| Gaggiuino |
+-----------------------------+
/mcp エンドポイントが提供するのは、公式の v2 MCP SDK を通じた、ステートレスな 2026-07-28 MCP リビジョン のみ です。すべてのリクエストは _meta にプロトコルバージョンとクライアント情報を含み、initialize ハンドシェイクもセッションもありません。また、server/discover は任意の機能プローブです。2025 リビジョン(2025-03-26 から 2025-11-25 まで)を引き続き使用するクライアントには、JSON-RPC エラー -32022 とともに HTTP 400 が返されます。その data.supported には 2026-07-28 が示されます。GET と DELETE には 405 が返されます。
すべてのリクエストは Mcp-Method ヘッダーと Mcp-Name ヘッダーに照らして検証されるため(不一致の場合は HTTP 400、-32020)、リバースプロキシやトンネルでは JSON 本文を解析せずに、ツール名に基づいてログ記録、使用量計測、ブロックを行えます。たとえば、エッジで delete_profile をブロックできます。
開発
git clone https://github.com/ljcl/gaggiuino-mcp.git
cd gaggiuino-mcp
bun install # Install all dependencies
bun run build # Build all packages (Turborepo)
bun run test # Run all tests
bun run lint # Lint all packages
bun run check # lint + test + typecheck + build + knip + boundaries + size
# Server
cd apps/server
bun run dev # Watch mode
bun run test # Server tests only
# Shot graph UI (run from the repo root)
bun run storybook # Storybook on port 6006
# Regenerate JSON schemas (after changing Zod schemas in loader.ts)
cd apps/server
bun run generate-schemas
main ブランチの Storybook は GitHub Pages(ljcl.github.io/gaggiuino-mcp)で公開されています。ローカルで何も実行せずに、ショットグラフと UI コンポーネントを閲覧できる静的ビルドです。
マシンなしで実行する
エスプレッソマシンは一日の大半、電源がオフになっており、マシンのネットワークに接続していない場合もあります。bun run fake-machine を実行すると、記録済みの /api/* ペイロードを配信し、サーバーが通信できるようになります。
bun run fake-machine # port 8080; --port N to change it
# in another shell
GAGGIUINO_URL=http://localhost:8080 bun run dev
これで、動作するサーバーが起動します。get_status、list_profiles、get_profile_info、get_maintenance_status に加えて、get_shot_data と view_shot_graph 用に、実際に取得した約 190 サンプルのショットデータを 2 件利用できます。いずれかのツールがリクエストを送信すると /health は machine.state: "ok" を返し、設定が読み込まれると machine.versions が追加されます。
ペイロードは作り物ではなく、実際に記録したものです。ステータス応答は実機から取得したそのままのデータで、設定とプロファイル定義は同梱されたリファレンスに基づいており、ショットデータも実際に取得したものです。テストでは、実際のクライアントを同じルートテーブル経由で動かすため、クライアントが拒否するペイロードは CI で失敗します。ファームウェアが使用していない通信形式を、偽装マシンが気付かないうちに覚えてしまうことはありません。
このツールでできないこと。 実機での検証に代わるものではなく、構造上、再現できないものもいくつかあります。
- 書き込みは 501 で拒否されます。 状態を保持しないため、
select_profileを成功として応答しても、直後のlist_profilesの結果と矛盾します。 - ファームウェア固有の挙動は再現できません。ショットをフラッシュに書き込む際に ESP32 が返す 503、一度に 1 件ずつのリクエスト処理、WebSocket のみで行うプロファイル更新、まだ記録されていないファームウェアリビジョンの型の不整合などです。
- フィクスチャのショットを自分のものと誤認しないよう、ショット ID は
900000000+の範囲にずらしています。
これは配布されません。実行ファイルはリポジトリのルートにある scripts/ 内にあり、Docker のビルドコンテキストには含まれません。また、その他のテスト用ファイルと同様、ペイロードもイメージから除外されています。
Docker
docker-compose.yml は公開済みのイメージを取得します。チェックアウトからイメージをビルドして実行するには、ビルド用のオーバーライドを重ねて使用します。
docker compose -f docker-compose.yml -f docker-compose.build.yml build
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d
docker compose logs -f
オーバーライドでは、ビルド結果に ghcr.io/ljcl/gaggiuino-mcp:dev というタグを付けるため、ローカルビルドを公開済みリリースと誤認することはありません。
トラブルシューティング
gaggiuino.local に接続できない
mDNS(.local ホスト名)は Docker コンテナ内では機能しない場合があります。GAGGIUINO_URL に IP アドレスを直接指定してください。
AI ツールがサーバーに接続できない
サーバーに HTTPS 経由でパブリックにアクセスできることを確認してください。Claude Desktop などのツールは独自のサーバー経由でリクエストを送信するため、Tailnet のみのアクセス(例:tailscale serve)は機能しません。パブリックトンネル(例:tailscale funnel)が必要です。
サーバーは起動するが Gaggiuino に接続できない
コンテナは network_mode: host を使用して、ホストのネットワークスタックを共有します。Gaggiuino が別のネットワークセグメントにある場合は、Docker のネットワーク設定を調整してください。
ライセンス
MIT © Luke Clark
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。