0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCPX 実践 — 複数のMCPサーバーを1つのゲートウェイに束ねて自己ホストする

0
Posted at

はじめに

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.yamlmcp.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.yamlpermissionstoolGroups でこれを定義します。

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" — デフォルトは全拒否。明示的に許可したものだけ通す(ゼロトラスト的な起点)。
  • developersbase: "allow" で広く許可しつつ、profiles.blockadmin グループ(チャンネル作成・削除など破壊的操作)だけを禁止。
  • marketingprofiles.allowreads グループのみ許可。読み取り系の tool だけ使える。
  • toolGroupswrites / 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 に allowprofiles.allow を設定し、tool を tool グループに登録
クライアントから繋がらない エンドポイントのパス違い・再起動忘れ Control Plane UI の接続スニペットを使い、設定後にクライアント再起動
設定ファイルが読まれない -v のマウント先がずれている mcp.json / app.yaml を config ディレクトリ直下に置きマウントを確認

まとめ

MCPX を使うと、散らかった MCP サーバー群を 1つのゲートウェイの裏に隠し、クライアント設定の重複・認証情報の分散・アクセス制御の不在という3つの痛みをまとめて解消できます。

  • mcp.json で上流サーバーを集約し、エージェントは1接続だけ持つ(M+1+N)
  • app.yamlbase: "block" + toolGroups で、業務単位の権限をゲートウェイに集中
  • OSS(MIT)なので自己ホストでき、認証情報をクライアント外に隔離できる

MCP サーバーが3つを超えたあたりから、ゲートウェイ導入の費用対効果は一気に上がります。まずは memory + time の2つを束ねるところから手元で試してみてください。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?