はじめに
MCP(Model Context Protocol)サーバーを使い始めると、すぐにある問題にぶつかります。サーバーが増えるほど、クライアント側の設定が雪だるま式に膨らむ ことです。
GitHub 用、Slack 用、社内 DB 用、メモリ用……とサーバーを足していくと、Claude Code・Claude Desktop・Cursor それぞれの設定ファイルに同じサーバー定義を何度も書くことになります。エージェント(M 個)とサーバー(N 個)の組み合わせは M×N に膨れ上がり、認証情報も各所に散らばります。
この記事では、その M×N 問題を M+1+N に簡約する OSS の MCP ゲートウェイ MCPX(Lunar.dev 製・MITライセンス)を、Docker で自己ホストして Claude Code から使うところまでをハンズオンで解説します。
- 対象読者: 複数の MCP サーバーを運用していて、設定の重複やアクセス制御に困っているエンジニア
- ゴール: ローカルに MCPX を立て、複数の上流 MCP サーバーを1つのエンドポイントに集約し、tool 単位のアクセス制御を設定できる状態
- 前提: Docker が動く環境(Docker Desktop でも可)
本記事のコマンド・設定は公式ドキュメントと GitHub リポジトリ(
TheLunarCompany/lunar)の記載に基づきます。MCPX は活発に更新されているため、バージョンやエンドポイントのパスは公式 Docs と Control Plane UI の表示を最終的な正としてください。
TL;DR
- MCPX は 複数の上流 MCP サーバーを集約する MCP サーバー(アグリゲーター)。エージェントは1接続だけ持てばよい。
-
docker run一発で起動。ポートは 9000(本体)/ 5173(管理UI)/ 3000(メトリクス)。 - 設定は2ファイル。
mcp.jsonで上流サーバーを定義し、app.yamlで認証・権限・tool グループを定義する。 -
permissions.base: "block"を起点に、consumer(利用者)ごとに使える tool を絞る ガバナンスが標準装備。 - Claude Code / Claude Desktop / Cursor など MCP 互換クライアントから1つの URL で全 tool にアクセスできる。
なぜゲートウェイなのか — M×N から M+1+N へ
MCP サーバーを直接クライアントに繋ぐ構成では、エージェントの数 M とサーバーの数 N の積だけ接続・設定・認証情報が必要になります。
ゲートウェイを1枚かませると、各エージェントは ゲートウェイへの1接続だけ を持ち、ゲートウェイが上流の N サーバーを束ねます。接続の総数は M + 1 + N(エージェント M 本 + ゲートウェイ + サーバー N 本)に収まり、組み合わせ爆発が消えます。
| 観点 | 直接接続(M×N) | ゲートウェイ経由(M+1+N) |
|---|---|---|
| クライアント設定 | サーバーごとに各クライアントへ記述 | ゲートウェイ1件だけ記述 |
| 認証情報 | 各クライアントに分散 | ゲートウェイに集約・隔離 |
| アクセス制御 | クライアント任せ | ゲートウェイで tool 単位に集中管理 |
| 監査 | 困難 | ゲートウェイで一元的に記録 |
MCPX はこのゲートウェイ層を OSS で提供し、tool 単位のアクセス制御・利用者の識別・監査ログ・認証情報の隔離 を標準で備えます。
Step 1: MCPX を Docker で起動する
まず設定ファイルを置くディレクトリを作ります。MCPX はこのディレクトリに app.yaml と mcp.json を探しに行きます。
mkdir mcpx-config
cd mcpx-config
Docker が動いていることを確認します。
docker version
公式 Quick Start のコマンドで MCPX を起動します。MCPX は上流サーバーを Docker で起動するため --privileged(Docker-in-Docker)が必要です。
docker run --rm --pull always --privileged \
-v ./:/lunar/packages/mcpx-server/config \
-p 9000:9000 -p 5173:5173 -p 3000:3000 \
--name mcpx \
us-central1-docker.pkg.dev/prj-common-442813/mcpx/mcpx:latest
公開されるポートの役割は次のとおりです。
| ポート | 役割 |
|---|---|
| 9000 | 本体(MCP エンドポイント) |
| 5173 | Control Plane UI(ブラウザ管理画面) |
| 3000 | Prometheus メトリクス(任意) |
起動したらブラウザで http://localhost:5173/ を開きます。ここが上流サーバーの追加やクライアント接続スニペットを確認する管理画面です。
--privilegedは強い権限を与えるフラグです。本番では Kubernetes 向けの構成(公式 Docs に専用ガイドあり)や、信頼できるネットワーク内での運用を検討してください。手元の検証では問題ありませんが、無条件に本番へ持ち込まないようにしましょう。
Step 2: 上流 MCP サーバーを mcp.json に定義する
MCPX が束ねる上流サーバーは mcp.json に書きます。起動時にこのファイルを読み込んで、各サーバーを MCPX のプロセス内で立ち上げます。
mcpx-config/mcp.json を作成し、例として memory(永続メモリ)と time(時刻)の2つを登録します。
{
"mcpServers": {
"memory": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
],
"env": {
"MEMORY_FILE_PATH": "/path/to/custom/memory.json"
},
"icon": "📝"
},
"time": {
"command": "uvx",
"args": [
"mcp_server_time",
"--local-timezone=America/New_York"
],
"env": {},
"icon": "⏰"
}
}
}
各サーバーのフィールドは以下のとおりです。
| フィールド | 必須 | 説明 |
|---|---|---|
command |
◯ | サーバーを起動する実行ファイル(npx / uvx など) |
args |
◯ | コマンド引数の配列 |
env |
— | そのサーバープロセスに渡す環境変数 |
icon |
— | 管理UIで識別するための絵文字ラベル |
なお、サーバーの識別名は mcpServers のキー名(上の例なら "memory" / "time")が兼ねます。
ここに GitHub・Slack・Postgres などの公式リファレンスサーバーを追記していけば、それらがすべて MCPX の1エンドポイント配下に集約されます。http://localhost:5173/ の Control Plane UI からも同じ追加操作が行えます。
Step 3: app.yaml で tool 単位のアクセス制御を組む
ゲートウェイの真価は 「誰がどの tool を呼べるか」を中央で制御できる 点にあります。MCPX では app.yaml の permissions と toolGroups でこれを定義します。
mcpx-config/app.yaml の例を見てみましょう。
permissions:
base: "block"
consumers:
developers:
base: "allow"
profiles:
block:
- "admin"
marketing:
profiles:
allow:
- "reads"
toolGroups:
- name: "writes"
services:
slack:
- "post_message"
- "post_reaction"
gmail:
- "send_email"
- "send_attachment"
github: "*"
- name: "reads"
services:
slack:
- "read_messages"
- "read_comments"
gmail:
- "read_email"
- "read_attachment"
- name: "admin"
services:
slack:
- "create_channel"
- "delete_channel"
この設定が表現しているポリシーは次のとおりです。
-
permissions.base: "block"— デフォルトは全拒否。明示的に許可したものだけ通す(ゼロトラスト的な起点)。 -
developers—base: "allow"で広く許可しつつ、profiles.blockでadminグループ(チャンネル作成・削除など破壊的操作)だけを禁止。 -
marketing—profiles.allowでreadsグループのみ許可。読み取り系の tool だけ使える。 -
toolGroups—writes/reads/adminの3グループに、Slack・Gmail・GitHub などサービス横断で tool を束ねる。github: "*"のようにサービス全 tool をまとめて指定もできる。
ポイントは、権限を「サービス × tool」の生の組み合わせで書かず、業務的に意味のあるグループ(読み取り/書き込み/管理)に束ねている ことです。新しいサーバーを足しても、その tool を既存グループに振り分けるだけで、consumer 側のポリシーは変えずに済みます。
カスタム tool を追加したら、それを エージェントがアクセスできる tool グループに登録する のを忘れないようにしましょう。グループに入れない tool は
base: "block"のままブロックされます。
Step 4: Claude Code から MCPX に接続する
最後に、クライアント側を MCPX の 1エンドポイントだけ に向けます。リモート MCP エンドポイントへは mcp-remote を介して接続します。
公式 Quick Start ではホスト版エンドポイントを例示していますが、自己ホストの場合は自分のゲートウェイ URL(ローカルなら本体ポート 9000)に置き換えます。クライアントの MCP 設定に次を追加します。
{
"mcpServers": {
"mcpx": {
"command": "npx",
"args": [
"mcp-remote@0.1.36",
"http://localhost:9000/mcp"
]
}
}
}
設定後はクライアントを再起動します。これで Claude Code からは mcpx という1つのサーバーが見えるだけで、その裏に memory・time・(追加したぶんの)GitHub・Slack……といった上流 tool がすべてぶら下がります。
接続用エンドポイントの正確なパスは MCPX のバージョンによって変わることがあります。
http://localhost:5173/の Control Plane UI に表示される接続スニペットをコピーするのが最も確実です。Claude Desktop・Cursor・VSCode・n8n・CrewAI・LangChain など、他の MCP 互換クライアントでも同じ要領で1接続に集約できます。
ハマりどころと回避策
実際に立てるときに引っかかりやすいポイントを挙げておきます。
| 症状 | 原因 | 回避策 |
|---|---|---|
| 上流サーバーが起動しない |
--privileged を付け忘れ(Docker-in-Docker 不可) |
docker run に --privileged を必ず付ける |
| すべての tool がブロックされる |
permissions.base: "block" のまま許可を書いていない |
consumer に allow か profiles.allow を設定し、tool を tool グループに登録 |
| クライアントから繋がらない | エンドポイントのパス違い・再起動忘れ | Control Plane UI の接続スニペットを使い、設定後にクライアント再起動 |
| 設定ファイルが読まれない |
-v のマウント先がずれている |
mcp.json / app.yaml を config ディレクトリ直下に置きマウントを確認 |
まとめ
MCPX を使うと、散らかった MCP サーバー群を 1つのゲートウェイの裏に隠し、クライアント設定の重複・認証情報の分散・アクセス制御の不在という3つの痛みをまとめて解消できます。
-
mcp.jsonで上流サーバーを集約し、エージェントは1接続だけ持つ(M+1+N) -
app.yamlのbase: "block"+toolGroupsで、業務単位の権限をゲートウェイに集中 - OSS(MIT)なので自己ホストでき、認証情報をクライアント外に隔離できる
MCP サーバーが3つを超えたあたりから、ゲートウェイ導入の費用対効果は一気に上がります。まずは memory + time の2つを束ねるところから手元で試してみてください。
参考リンク
- MCPX(GitHub・TheLunarCompany/lunar): https://github.com/TheLunarCompany/lunar
- MCPX README: https://github.com/TheLunarCompany/lunar/blob/main/mcpx/README.md
- 公式 Docs(Quick Start): https://docs.lunar.dev/mcpx/get_started/
- mcp.json 仕様: https://docs.lunar.dev/mcpx/mcp.json/
- アクセス制御の設計(公式ブログ): https://www.lunar.dev/post/mcp-gateway-access-controls-defining-permissions-for-llm-agents