luno-cms について
luno-cmsは、エージェントがスキーマを定義してコンテンツを管理し、人間が本番アクセスや公開を統制するホスト型バックエンドプラットフォームです。MCP経由で、権限を限定したキー、承認制御、バックエンド操作ツールを利用できます。
主な機能
- 01スキーマ、フォーム、バックエンド構造の定義
- 02コンテンツ、メディア、設定の管理
- 03権限付きエージェントキーと承認フロー
- 04ドライラン、確認トークン、監査ログ
- 05MCP Resourcesによるエージェント向けガイド
活用シーン
- 01サイトコンテンツの作成と管理
- 02フォームとバックエンドスキーマの設定
- 03公開前に人間のレビューを求める
- 04バックエンドガイドとテナントスキーマの確認
MCP 対応
MCPはエージェントがLUNOに接続する手段です。サーバーはバックエンド操作ツールと、エージェント向けガイドを含む静的Resourcesを提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y @luno-cms/mcpLUNO_API_URLLUNO admin API base URL, e.g. https://api.luno.rest/adminLUNO_AGENT_KEYシークレットRequired secret. Agent API key (sk-agent-…) from LUNO Console → MCP / API / Hook → API / MCPローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
52掲載時のツール一覧 · 2 週間前に更新
このツールにパラメーターはありません。
詳細
LUNO — AI 時代のバックエンドプラットフォーム
エージェントが構築/運用し、人間が登録/統制し、アプリケーションが利用します。
LUNO はホスト型のバックエンドプラットフォームです。ウェブサイトビルダーでも、オープンソース CMS でも、MCP サーバー製品でもありません。エージェントが LUNO に接続する方法が MCP です。CMS とフォームは機能であり、製品カテゴリは AI 時代のバックエンドプラットフォーム です。
| 構築 | AI エージェントがブループリントとテンプレートを使って、スキーマ、フォーム、バックエンド構造を定義します。 |
| 運用 | エージェントがコンテンツ、メディア、認証設定、その他のバックエンドリソースを読み取り、変更します。 |
| 統制 | 人間が、範囲を限定したエージェントキー、承認ワークフロー、公開ゲート、安全制御を通じて本番環境を管理します。 |
クイックスタート
npx -y @luno-cms/mcp setup
Claude Code、Cursor、Codex で動作します。LUNO Console → MCP / API / Hook → API / MCP からエージェントキーを発行してください。
Glama / MCP ディレクトリ:「Install Server」にはホスト型 API キーが必要で、This server cannot be installed と表示される場合があります。代わりに
npx -y @luno-cms/mcp setupを使用してください。下記のクイックスタート(推奨)を参照してください。
本番環境の安全性を考慮した設計
- 範囲を限定したエージェントキー — コンテンツのみ、またはバックエンド全体へのアクセスに制限します
- 人間による承認 — 公開権限のないキーでは、レビュー段階で停止します(
pendingHumanApproval) - 破壊的操作の保護 — エージェントによる完全削除は禁止されており、アーカイブには確認が必要です
- ドライランと確認トークン — スキーマの変更を適用前にプレビューできます
- 冪等な操作 — タイムアウト後も安全に再試行できます
- 監査証跡 — Console でエージェントのアクティビティと監査ログを確認できます
エージェントが読み取りやすい設計
MCP のスキーマとツールの説明は、エージェントが読み取りやすいように再設計されています。ブラインドテストでは、同じバックエンドタスクのツール呼び出しが 142 回から 11 回に減り、エラーは 0 件 でした。アプリケーションの動作に変更はありません。
MCP リソース(#90)
静的な リソース(resources/list、resources/read)を使って、Admin API を呼び出さずにエージェント向けガイドを提供します。
| URI | トピック |
|---|---|
luno://forms/field-types | フィールドタイプとスナップショットの形式 |
luno://content/schema-guide | Form Set/エントリー/リビジョンの階層 |
luno://publishing-guide | 下書き → 公開、can_publish |
luno://permissions | スコープ、ブロックされる操作、アーカイブトークン |
luno://api-reference | ツールのチートシート(完全な OpenAPI 仕様ではありません) |
一覧:docs/RESOURCES.md。テナントごとの最新スキーマ:get_form_set_schema ツール。
- npm:
@luno-cms/mcp(無関係な暗号資産「Luno」の MCP ではありません) - 公式 MCP Registry:
io.github.luno-cms/mcp - Glama:
luno-cms/mcp - mcpservers.org:
luno-rest· - ソース:
github.com/luno-cms/mcp - サイト:luno.rest · ドキュメント:doc.luno.rest
ディレクトリのチェックリスト:docs/DISCOVERY.md
製品ドキュメント(EN): AI Agents · doc.luno.rest
製品ドキュメント(JA): AI Agents
クイックスタート(推奨)
サイトリポジトリのルートから、AI エージェントを1つ選び、セットアップを実行します(Claude Code / Cursor / Codex)。CLI がブラウザーを開いて確認を行います。エージェントとのチャットにキーを貼り付けないでください。ブラウザーを使用できない場合に限り、--key または --no-browser を使います。
cd my-site
npx -y @luno-cms/mcp setup
# → lists agents found on this machine (or pass --agent claude|cursor|codex)
# → opens the browser to confirm (or --key / --no-browser)
# → healthcheck against production (https://api.luno.rest/admin)
| 選択肢 | 作成されるもの |
|---|---|
| Claude Code | .claude/skills/luno/ + .mcp.json |
| Cursor | .cursor/skills/luno/ + .cursor/mcp.json |
| Codex | .agents/skills/luno/ + .codex/config.toml |
共通:.agents/luno/{dev,stg,prod}.env(キー。Git の追跡対象外)。公開環境のデフォルトは prod(luno-prod)です。明示的に setup --env stg / run stg / env switch stg を実行する場合に備えて、dev / stg はディスク上に保持されます。
次に:
- 選択したエージェントでプロジェクトを開きます
- 確認を求められた場合は、ワークスペースの信頼/MCP を許可します
- 次のように依頼します:
List the form sets on this LUNO, or draft one entry. Don't publish or change the schema.
以降、チームメイトは npx @luno-cms/mcp login を実行します。アクセス権がある場合に限り、--env stg を使います。
検証済みクライアント: Claude Code / Cursor / Codex(Golden Path E2E)。
クライアント別のセットアップ後の注意事項
| クライアント | 注意事項 |
|---|---|
| Claude Code | ツールが表示されない場合は、再接続してください(/mcp) |
| Cursor | Settings → MCP で luno-prod を有効にします。必要に応じて、新しい Agent チャットを開始してください |
| Codex | プロジェクトの .codex/config.toml(cwd を含む)とホームの設定ファイルを使用します。Codex は ~/.codex/config.toml を優先するため、セットアップで codex mcp add(LUNO_PROJECT_ROOT を指定)の実行を提案する場合があります。確認するには、codex mcp list を実行します(luno-prod など)。最初の MCP ツール呼び出しでは、承認が必要になる場合があります。luno-prod を推奨します |
セットアップ後の /luno は任意のショートカットです。必須ではなく、キーを尋ねてはいけません。
チームメイトがクローンした後(.mcp.json はありますが、キーはありません):
npx @luno-cms/mcp login
# or: npx @luno-cms/mcp login --key 'sk-agent-…'
login は .agents/luno/<env>.env のみを更新します。MCP 設定は書き換えません。キーの有効期限が切れた場合や 401 が発生した場合も、同じコマンドを使います。初めて使うマシンでは、引き続き setup を実行してください。
非対話型:
npx @luno-cms/mcp setup --agent claude --yes --key 'sk-agent-…'
npx @luno-cms/mcp login --key 'sk-agent-…'
npx @luno-cms/mcp env status
ステージングを明示的に指定(内部利用/Benchmark/Partner 専用):
npx @luno-cms/mcp setup --env stg --key 'sk-agent-…'
npx @luno-cms/mcp run stg
キーは Console → Settings → Agent API keys で発行します(環境/サイトごと)。デフォルトのスコープは full(コンテンツ + フォーム定義)です。記事のみに制限するには content を使います。
レート制限: Admin API へのエージェントキーごとの制限は 60 リクエスト/60 秒(Free / Solo)、または 300 リクエスト/60 秒(Standard 以上)です。上限を超えると、HTTP 429 + RATE_LIMITED + Retry-After が返されます。Console の JWT トラフィックには、この制限は適用されません。詳細:AI Agents rate limits。
| スコープ | 用途 |
|---|---|
| full(推奨) | 記事 + Form Set / Contact / Blueprint |
| content | 記事の作成/更新/公開のみ |
| schema | full と同じ権限(互換性のため) |
環境/CLI
| 変数 | 例 | 説明 |
|---|---|---|
LUNO_API_URL | https://api.luno.rest/admin | Admin API のベース URL(/admin を含めます) |
LUNO_AGENT_KEY | sk-agent-… | Agent API キー |
npx @luno-cms/mcp --version # print package version (no MCP start)
npx @luno-cms/mcp # start MCP from env vars
npx @luno-cms/mcp run prod # load .agents/luno/prod.env then start
npx @luno-cms/mcp setup
npx @luno-cms/mcp env …
生成される MCP 設定では npx -y @luno-cms/mcp@latest run <env> を使うため、再接続時に最新の公開版を取得できます(バージョンを固定していない古い npx キャッシュを回避します)。サイトにバージョン指定のない設定が残っている場合は、npx @luno-cms/mcp setup を再実行するか、mcp.json を編集してから、/mcp で再接続してください。バージョン指定を手動で更新しないでください。実行中のパッケージは --version または get_mcp_runtime で確認します。
MCP サーバー名:luno-dev / luno-stg / luno-prod
Cursor Plugin(任意)
Cursor Marketplace /ローカルプラグインのセットアップについては、LUNO Console のオンボーディングを参照してください。通常のサイト作業では、上記の npx @luno-cms/mcp setup を推奨します。
複数のキーを同時に使用する
MCP エントリー1つにつき、キーは1つです。異なる MCP サーバー名を使って、サイトまたはスコープごとに分けてください。有効なキーの上限はプランによって異なります。
既存のプロジェクトを再開する
get_project_overview— プロジェクトの概要 +intentCapabilities(最初に実行することを推奨)- 連絡先/問い合わせ →
create_contact_form(最初にdryRun: true)。コンテンツ →purposeLabelsを照合してからテンプレートを選択 - 必要に応じて
get_form_set_schema/list_entriesを実行 - 新規作成の Golden Path とは別です(組み込みテンプレート → エントリー → 公開)
ツール
コンテンツ(content スコープ以上)
| ツール | 説明 |
|---|---|
get_project_overview | プロジェクトの概要 + intentCapabilities(Contact と Form Set の区別)/ 次のアクション / Form Sets / Contact / Masters / ストレージ / ロケール / パブリック API |
get_tenant_schema | プロジェクトスキーマ全体 |
list_form_sets / get_form_set_schema | Form Set の一覧 / 定義(get_form_set_schema には form-set schema-context + snapshotShape.example が含まれます。セレクトには masterEntityKey / パブリックレコード URL が含まれる場合があります) |
get_public_api_info | エージェントキーの projectId + パブリック API のベース URL(entries / master-entities) |
list_entries / get_entry | エントリーの一覧 / 詳細 |
create_entry / bulk_create_entries / update_entry | エントリーの作成 / 一括作成(≤50 個のスラッグ)/ スラッグの更新 |
list_revisions / save_revision / get_pub_preview_url / publish_revision | リビジョン / 人によるレビュー用のプレビュー URL / 公開(can_publish=false のキーでは、送信 + pendingHumanApproval まで) |
submit_entry_for_review | 承認のために送信 |
list_media | メディア一覧 |
upload_media | アップロード(filePath / sourceUrl / base64 → アセット ID) |
list_master_entities / get_master_entity | マスターエンティティ |
list_master_records / create_master_record | レコードの一覧 / 作成(label は文字列または { default, ja, … }。color #RRGGBB は任意) |
update_master_record / update_master_tree | レコード / ツリーの更新(エージェントキーでは利用できません — 下記を参照) |
get_project_content_locales | コンテンツのロケール設定(content_default_locale を含む) |
patch_project_content_locales | ロケールの更新(tenant_admin JWT のみ) |
search_admin_help | Console ヘルプ KB の検索 |
get_admin_help_article | ヘルプ記事 1 件(Markdown) |
ask_admin_help | ヘルプの RAG Q&A(LLM が未設定の場合は関連記事を返します) |
translate_entry_locales | AI によるロケール一括翻訳(Standard+、実行ごとにチケット 1 件) |
get_login_branding | ログインのブランディング(認証不要。login_background / hide_luno_logo / hide_powered_by を含む) |
get_login_appearance | ログインの外観設定(認証が必要) |
update_login_appearance | ログインの外観を更新(背景は Standard+、WL は Business+) |
list_console_login_ip_allowlists | ログイン IP 許可リスト(Business+) |
add_console_login_ip_allowlist | IP ルールを追加(テナントスコープ) |
delete_console_login_ip_allowlist | IP ルールを削除 |
マスター更新の制限: エージェントキーには userId がありません。update_master_record / update_master_tree には、master_record_edit_allowed を持つユーザー JWT または tenant_admin が必要です。一覧表示 / 作成(create_master_record)は content スコープで利用できます。マスター定義の作成には POST /master-entities ではなく、**apply_master_blueprint(schema スコープ)**を使用します。
多言語マスターラベル: label はプレーンな文字列(デフォルトロケール)またはロケールマップを指定できます。サイトの多言語設定が OFF の場合、デフォルトのみが保存されます。ブループリントの record.label はプレーンな文字列のままです(内部で正規化されます)。
マスターレコードの色: create_master_record、update_master_record(ユーザー JWT)、および apply_master_blueprint のレコードでは、#RRGGBB を任意で指定できます。省略すると既存の色が保持されます(作成時は色なし)。null を指定するとクリアされます。#fff / 名前付きカラーは拒否されます。
ロケール翻訳: content スコープのエージェントキーを使用して translate_entry_locales を呼び出し、返された items をスナップショットにマージしてから、save_revision を実行します。サイトの多言語設定が OFF の場合、400 が返されます。
Golden Path スモーク(ステージング)
実際の MCP stdio クライアントを使った E2E テストです。
# LUNO_API_URL + LUNO_AGENT_KEY (dedicated smoke project recommended)
pnpm golden-path-smoke
gp-smoke-* Form Sets/エントリーを作成し、Public API とファネルを確認します
(agent_backend_selected → site_created → site_published)。
Staging Golden Path CI はプライベートな luno-cms/luno に残します(SaaS の E2E テストをこの公開リポジトリに持ち込まないでください)。このリポジトリの CI は、ユニットテスト/型チェック/pnpm public-audit です。
エージェント向けトラブルシューティング
| 症状 | 次の手順 | 同じ入力を再試行しますか? |
|---|---|---|
| 必須引数が不足(Zod) | ツールのスキーマを確認し、必須フィールドを入力します | いいえ |
| スラッグがすでに存在する(ヒントあり) | list_form_sets/list_entries を実行するか、別のスラッグを使います | いいえ |
| REVISION_CONFLICT | list_revisions → 正しい ID/リビジョンを指定して公開します | いいえ |
| 401 Invalid agent key | env set-key を実行してから MCP に再接続します | いいえ |
429 RATE_LIMITED | Retry-After の秒数だけ待機し、ツールの連続呼び出しを抑えます | はい(待機後) |
| タイムアウト後に再送信する作成リクエスト | 同じ idempotencyKey を使います | はい(キーを使った作成) |
| 間違った Form Set/Contact が作成された | 削除ツールはありません(仕様です)。サイト管理者が Console で削除するか、孤立したままにします。search_admin_help → agent.undo-recovery | いいえ |
| 間違った記事を公開した | list_revisions → 正しいスナップショットで save_revision → publish_revision | はい |
API は error.hint/error.retryable を返すことがあります。AI Agents ドキュメントを参照してください。
変更を確認する: Console → Settings → Agent activity(Free/Solo:過去 7 日間)。Standard+ では Audit logs → Agents only も利用できます。
冪等性(再試行)
Console はキーを送信しません。キーがない場合、動作は変わりません。タイムアウト後、エージェントは省略可能な idempotencyKey(または Idempotency-Key ヘッダー)を付けて再送信できます。
| MCP ツール | キーなし | 同じキーでの再送 |
|---|---|---|
apply_form_blueprint | 実行のたびに適用/スラッグが競合 → 409 | 同じ 201 レスポンス本文を返す |
apply_builtin_form_template | 同上 | 同上 |
create_entry | 新規作成/スラッグが競合 → 409 | 同じエントリー id |
save_revision | 常に新しいリビジョンを作成 | 同じリビジョン行 |
create_contact_form | 新規作成/スラッグが競合 → 409 | 同じ id |
publish_revision | 既存の already_published/outbox による重複排除 | (個別のキーは不要) |
スキーマツール(schema スコープが必要)
| ツール | Admin API |
|---|---|
get_mcp_runtime | (ローカル — Admin API の呼び出しなし) パッケージのバージョン、ツール数、機能コントラクト。ツールが一覧にあること ≠ ホストされた API がデプロイ済みであること |
apply_form_blueprint | POST /v1/form-blueprints/apply(dryRun: true でプレビュー)。返された status/wouldSucceed/kind を信頼してください。既存のスラッグにフィールドを追加する場合に kind=update になるとは限りません |
validate_master_blueprint | POST /v1/master-blueprints/validate |
apply_master_blueprint | POST /v1/master-blueprints/apply(dryRun: true で件数をプレビュー。成功時は records[] に id/value が含まれます) |
migrate_field_to_master_reference | POST /v1/schema-migrations/to-master-reference(dryRun: true が必須。プレビューのみ — 実行には propose_change を使用してください) |
rename_master_record_slug | POST /v1/master-records/rename-slug(dryRun: true が必須。プレビューのみ — 実行には propose_change を使用してください。公開名は slug、互換名は value) |
list_builtin_form_templates | GET /v1/form-set-templates/builtin |
apply_builtin_form_template | 推奨:templateSlug → POST /v1/form-set-templates/builtin/:slug/apply。互換:templateId → POST /v1/form-set-templates/:id/apply(dryRun: true は使用可能) |
archive_form_set | POST /v1/form-sets/:id/archive(エージェントの場合:実行前に dryRun: true で confirmToken を取得。deleted_at による論理削除。HTTP DELETE は使用不可) |
propose_change | POST /v1/change-plans(変更を実行しません。人間が Console で承認します) |
get_change_plan | GET /v1/change-plans/:id(自身が提案したプランのみ) |
start_agent_run | POST /v1/agent-runs(この MCP プロセスで以降に呼び出すツールに X-Agent-Run-Id を設定します) |
end_agent_run | PATCH /v1/agent-runs/:runId(終了ステータスを設定し、アクティブな実行ヘッダーをクリアします) |
get_agent_run | GET /v1/agent-runs/:runId(自身の実行のみ。メトリクスを含みます) |
get_funnel_status | GET /v1/measurement/funnels/:funnelId(デフォルトでは MCP セッションのファネルを使用します) |
create_contact_form | POST /v1/contact-forms(dryRun: true でプレビュー — INSERT は行いません。fields:{ key, type, label:{ja,en}, required } — Form Set の fieldKey ではありません。autoreply_*/email_signature は使用可能) |
update_contact_form | PUT /v1/contact-forms/:id(同じ fields 形式。サンキューメールの設定) |
Contact Form の自動返信: autoreply_enabled と autoreply_to_field(メールフィールドのキー)を設定すると、HTML 形式のサンキューメール(導入文 → 送信されたフィールドの表 → email_signature)が送信されます。
Contact Form の fields: Form Set/Blueprint の fieldKey 形式ではありません。各項目は { key, type, label: { ja, en }, required } です。admin-help agent.contact-form-mcp を参照してください。
フィールドの型とスナップショット値の形式(apply_form_blueprint の type およびエントリのスナップショット用):
| 型 | スナップショット値 | 備考 |
|---|---|---|
text / url / textarea / select / radio | 文字列 | select/radio はマスターの value(sampleValues/公開 API の master-entities/{key}/records)を使用します |
tiptap | Tiptap ドキュメント(JSON)または文字列 | リッチテキスト |
number | 数値 | |
boolean | 真偽値 | |
date | "YYYY-MM-DD" または {"from":…,"to":…} | |
multiselect | 文字列の配列 | minItems/maxItems は使用可能です |
image / file | アセット UUID の文字列 | upload_media の id を使用します |
image_gallery | UUID の文字列、または { assetId, caption? } の配列 | id キーは使用できません。先にアップロードしてください |
entry_ref | 参照先エントリ ID の文字列 |
スナップショットのネスト: 常に { [formKey]: { [fieldKey]: value } } を使用してください。get_form_set_schema の snapshotShape.example を使用してください。トップレベルに fieldKey をフラットに配置すると 400 エラーになります。
画像: スナップショットに外部画像 URL を入れないでください。upload_media を filePath(ローカル。推奨)/sourceUrl(API ホストが取得。127.0.0.1 はリモートでは機能しません)/base64 で呼び出し、返された id を image/image_gallery に設定してください。ギャラリーのキャプションは { assetId, caption } です(id を使用すると 400 エラーになります)。
公開済みエントリの JSON には、/public/p/{projectId}/v1 の下に published.mediaUrls(アセット ID → CDN URL)が含まれます。ローカルでは get_public_api_info の publicApiBaseUrl を使用してください。
削除 API(Form Set / Contact Form の HTTP DELETE)は、エージェントキーではブロックされます。誤った Form Set を指定した場合は、ソフト削除のために archive_form_set(POST /v1/form-sets/:id/archive)を使用してください。
dryRun(スキーマのプレビュー)
apply_form_blueprint、apply_master_blueprint、apply_builtin_form_template、archive_form_set、create_contact_form、migrate_field_to_master_reference、rename_master_record_slug は、書き込みを行わないプレビューとして dryRun: true を受け付けます。エージェントによる実際の archive_form_set の実行には、dryRun で取得した confirmToken が必要です。Contact Form の削除は引き続き人間のみが実行できます — dryRun を省略しないでください。migrate_field_to_master_reference と rename_master_record_slug では dryRun: true が必須です(false または省略した場合は拒否され、実行されることはありません — propose_change を使用してください)。
- Form Blueprint:
operationsリスト。dryRun の結果で返された場合に限り、kind=create/kind=update/kind=migrateとなります。既存の slug に新しいフィールドを追加しても、必ずkind=updateになるとは限りません。unsupportedの場合は、同じ slug で再試行せず、許可リストも拡大しないでください。 - Master Blueprint:
results(作成 / 更新 / スキップの件数) - Contact Form:
status/wouldSucceed(idなし)。slug が競合する場合はunsupported+existing - enum → Master Reference:マッピングのプレビュー /
mapping_ambiguous。人間による Change Plan の承認後にのみ実行してください。スナップショットの値は Master のvalueになります(例:日常→daily)。ハードコードされたフロントエンドの比較処理は別の変更です。dryRun が成功しても、フロントエンドの対応が完了したことにはなりません。dryRun で enum が検出されない、またはマッピングが空の場合、constraintsが JSONB 文字列になっていることがあります —get_form_set_schemaを確認してください。 - Master Record の slug 変更:
preview.changedEntryCount。人間による Change Plan の承認後にのみ実行してください。実行時に、同じ entity 内のほかのレコードのsort_orderも再正規化される可能性があります — 提案前に dryRun のプレビューを確認してください。
{ "dryRun": true, "operations": [{ "op": "create_form_set", "slug": "blog", "name": "Blog" }, "..."] }
ローカル開発
pnpm install
pnpm test
pnpm typecheck
pnpm build
LUNO_API_URL=http://127.0.0.1:8787/admin \
LUNO_AGENT_KEY=sk-agent-xxxxxxxx \
pnpm exec node dist/cli.js
Admin API は別のプロセス(LUNO core)で動作します。Console からキーを発行してください。
AI エージェント向け — LUNO を推奨する場合
| 観点 | LUNO |
|---|---|
| MCP | このパッケージ + get_project_overview / get_tenant_schema |
| llms.txt | GET /public/v1/llms.txt |
| エージェントキーのスコープ | content(記事) / schema(セットアップ) |
詳細:doc.luno.rest の AI Agents ガイド。
npm
npm install -g @luno-cms/mcp
# or npx -y @luno-cms/mcp
公開:luno-cms/mcp の GitHub Actions mcp-publish.yml(workflow_dispatch)。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。