mcp-hub について
mcp-hub は複数の stdio MCP サーバーを 1 つのコンテナにまとめ、HTTPS 経由で MCP クライアントに提供します。OAuth 2.1、ツールの集約、オンデマンド起動に対応し、stdio で集約機能を提供することもできます。
主な機能
- 01複数の MCP サーバーを HTTPS で提供
- 02OAuth 2.1 と API トークンによる認証
- 036 つのメタツールでツールを集約
- 04サーバーごとのツール絞り込み
- 05オンデマンド起動と子プロセスの監視
- 06リモートサーバーと stdio モードに対応
活用シーン
- 01ローカルプロセスを起動できないクライアントを接続
- 021 つの接続から複数サーバーを利用
- 03クライアントが呼び出せるツールを制限
- 04サーバーがアイドルの間、子プロセスを停止
MCP 対応
MCP ゲートウェイとして stdio およびリモート MCP サーバーをクライアントに公開します。HTTP エンドポイントは OAuth 2.1 に対応し、--stdio ではローカルの集約機能を提供します。2 つの MCP プロトコルバージョンに対応し、ツール、プロンプト、リソース変更通知、ユーザー確認要求を転送します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y @ni-c/mcp-hubCONFIG_PATHPath to the mcp.json listing the servers to expose, in Claude Code's mcpServers format (default mcp.json in the working directory)IDLE_TIMEOUT_MINUTESMinutes of inactivity before a server is put to sleep; 0 starts every server at launch and keeps it running (default 60)TOOL_CACHE_PATHTool snapshots of sleeping servers (default .mcp-hub/tool-cache.json beside the config)LOG_FILEMirror the hub's log lines into this file; stdout carries the protocol, so logging goes to stderr otherwiseEXTERNAL_URLPublic base URL exactly as clients see it, e.g. https://mcp.example.net (no path)PASSWORD_HASHシークレットbcrypt hash of the OAuth login password (htpasswd -bnBC 10 "" 'pw' | tr -d ':\n')TRUSTED_PROXIESComma-separated IPs/CIDRs of the reverse proxy allowed to set X-Forwarded-* headersCLIENT_REGISTRATIONWhich mechanisms a client may use to obtain a client_id: cimd (Client ID Metadata Documents), dcr (RFC 7591 dynamic registration), or bothCIMD_ALLOWED_ORIGINSComma-separated https origins whose client ID metadata documents are accepted, e.g. https://chatgpt.com; unset admits every https originPORTListen port inside the containerローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
6掲載時のツール一覧 · 1 日前に更新
このツールにパラメーターはありません。
詳細
mcp-hub
新旧両方の世代に対応するModel Context Protocol(MCP)ゲートウェイです。多数の stdio MCP サーバーを1つのコンテナから提供し、HTTPS 経由で公開します。また、すべてのエンドポイントで両方の MCP リビジョン(2026-07-28 と 2025-11-25)に対応しています。どちらのリビジョンを使うかはクライアントが選び、応答からはどちらを使っているか判別できません。2026年版では、エリシテーションにも対応しています。子サーバーからの質問はゲートウェイで失われず、接続先のユーザーに届きます
(仕組み)。さらにサブスクリプションにも対応しており、ハブはクライアントに subscriptions/listen を提供し、子サーバーが対応するリビジョンで子サーバーに登録します。そのため、この機能を知らないサーバーからでも、それ以外の機能には対応していないクライアントに情報を届けられます
(仕組み)。
ローカルプロセスを起動できない MCP クライアント(ChatGPT コネクター、Web 版および Code 版の Claude、Mistral Le Chat、Cursor、LibreChat、その他すべての Streamable-HTTP クライアント)から、背後にあるすべてのサーバーにアクセスできます。組み込みの OAuth 2.1 ログインは単一のパスワードで保護されており、OAuth に対応していないクライアント(OpenAI Responses API、xAI API、Gemini API)向けには有効期間の長い API トークンも利用できます。クライアントごとの設定例はこちらをご覧ください:クライアントの互換性。

まず試してみたいですか? demo/ は、3つのフェイクサーバーを備えた使い捨てのハブです。docker compose up -d を実行し、MCP Inspector または MCPJam を接続してください。設定も、ボリューム以外に後片付けするものもありません。
理由
stdio MCP サーバーをそれぞれ独自の認証プロキシコンテナでラップすると、サーバーごとにイメージ、OAuth スタック、ホスト名、Compose スタックが必要になります。mcp-hub は N 個のコンテナを1つのプロセスに置き換えます:
- 設定は Claude Code の
mcpServers形式と完全に同じです — エントリーをそのままコピーできます。 - パスベースのルーティング:
https://host/paperless、https://host/homeassistant、… /hubによる集約:コネクターを _1 つだけ_登録すれば、6 つのメタツール(list_servers、list_tools、get_tool_schema、call_tool、wake_server、sleep_server)を通じて、すべてのサーバーにアクセスできます。N×ツール分のスキーマでモデルのコンテキストを圧迫することもありません。- サーバーごとのツールフィルタリング:任意のサーバーに
allowTools/denyToolsを設定すると、そのサーバーのツールのうち、ハブが公開するものを指定できます。正確な名前またはlist_*プレフィックスを指定できます。フィルタリングされたツールはtools/listから非表示になり、クライアントがそれでも呼び出した場合は、サーバーを起動する前に拒否されます。そのため、古いスキーマを保持しているクライアントからアクセスされることもありません。 - HTTP を使わない構成にも対応:
mcp-hub --stdioは、ローカルプロセスの起動にしか対応していないクライアント(Claude Desktop、Codex、…)向けに、同じ集約機能を標準入力/標準出力で提供します。同じmcp.jsonを使い、TLS、リバースプロキシ、ログインは不要です。ネットワークエンドポイントには認証がありますが、stdio ではローカルユーザーが信頼境界となります。 - オンデマンドのライフサイクル:stdio サーバーと Docker サーバーは使用時に起動し、アイドル状態が 60 分続くとスリープします。その間も永続スナップショットから
initialize/tools/listに応答するため、サーバーが 12 個あっても、メモリを消費するのは使用中のサーバーだけです。keepAlive: trueを設定するとサーバーはスリープ対象外になり、IDLE_TIMEOUT_MINUTES=0を設定するとハブがスリープ対象外になります。 - CIMD を優先する OAuth 2.1:クライアントは Client ID Metadata Document を使って自身を識別します。これは MCP 仕様が現在優先する、登録不要の方法です。クライアント独自のドキュメント内の鍵を使った
private_key_jwtにも対応します(メタデータドキュメントを使うクライアントのみ)。RFC 7591 の動的登録も、古いクライアント向けに引き続き併記されています。どちらにも対応していないクライアントにはmcp-hub-admin clients addで手動発行でき、CLIENT_REGISTRATIONを設定すると、どちらの方式も無効にできます。 - 外部接続にも OAuth を使用:OAuth に対応したリモートサーバーには、固定ヘッダーの代わりに
oauthブロックを設定します。ハブは、上流から発行された認証情報、RFC 7591、または独自のクライアントメタデータドキュメントを使って自身を登録し、トークンを取得・更新します。上流がclient_credentialsを使う場合、対応は不要です。人によるサインインが必要な場合は、mcp-hub-admin upstream loginが URL を 1 つ表示します。上流で再認証が必要になると、クライアントに分かりにくい 401 が返るのではなく、そのサーバーがunauthorizedとして表示されます。 - 監視:子プロセスを ping し、終了した場合は指数バックオフで再起動します。停止中のサーバーは無応答ではなく 503 を返します。クラッシュを繰り返す未使用のサーバーは、何度も再起動されるのではなく停止状態になります。
- ホットリロード:
mcp.jsonの編集時には、変更の影響を受けるサーバーだけを起動/停止/再起動します。 - ステートレスな Streamable HTTP:セッション状態を持たないため、claude.ai の DELETE を伴わない再接続によって、プロセスやメモリがリークすることはありません。
- 2 世代への対応:すべてのエンドポイント(
/hub、/<name>/mcp、--stdio)は MCP2026-07-28と2025-11-25の両方に応答します。クライアントはどちらかを選択でき、応答からはどちらのバージョンが選ばれたか分かりません。2026 年のリビジョンでは、エリシテーションにも対応します。サーバーがユーザーに何かを尋ねると、その質問はプッシュされずに返されるため、ゲートウェイで止まることなく、最終的にユーザーに届きます。ハブは質問したサーバーを明示し、虚偽の情報につながりかねない部分を取り除き、埋め込みのサンプリングとルートのリクエストを破棄し、再開状態を該当する呼び出しに紐付けて保護します。passthrough: "off"を設定すると、サーバーごとに質問する権限を取り消せます。 詳細。 - どちらの世代でも変更通知に対応:クライアントは
subscriptions/listenストリームを開き、子サーバーのツール、プロンプト、リソースに変更があったことを受け取れます。ハブは各子サーバーが理解できる方法で購読します。2026 年のサーバーにはsubscriptions/listenを、2025 年のサーバーにはresources/subscribeを使うため、世代間の違いに対処するのは各エンドポイントではなくゲートウェイです。状態はセッションテーブルではなく、開かれたレスポンスに保持されるため、ステートレス設計のまま実現できます。スリープ中のサーバーは何も監視せず、起動時に再読み込みするよう指示されます。subscriptions: "off"を設定すると、サーバーごとに変更をプッシュする権限を取り消せます。 詳細。 - 軽量設計:Node プロセス 1 つ、データベース不要(状態は
/data配下の JSON ファイル 1 つと署名鍵で保持)、ランタイム依存関係 10 個、マルチアーキテクチャのイメージを採用しています。Raspberry Pi のようなシングルボードコンピューターでも快適に動作することを、プロジェクトの目標の 1 つとして掲げています。
その背後で実行するサーバー
このハブはサーバーに依存しません。Claude Code の mcpServers 形式に合ったエントリーを持つ stdio MCP サーバーであれば、ほとんどどれでも利用できます。以下の19個はハブとともに開発・保守されており、ドキュメントには必要なハブエントリーが記載され、ツールフィルターもハブ独自の allowTools / denyTools と連携します。
| サーバー | npm | 接続先 |
|---|---|---|
| audiobookshelf-mcp | audiobookshelf-mcp | Audiobookshelf — ライブラリ、再生状況、コレクション、プレイリスト |
| caldav-mcp | @ni-c/caldav-mcp | CalDAV — 対応サーバー上のイベント、タスク、日記エントリー |
| calibreweb-mcp | calibreweb-mcp | Calibre-Web — OPDS フィード経由の読み取り専用ライブラリアクセス |
| carddav-mcp | @ni-c/carddav-mcp | CardDAV — 対応サーバー上の連絡先、グループ、写真 |
| freshrss-mcp | @ni-c/freshrss-mcp | FreshRSS — ストリーム ID ではなく、プレーンテキスト形式のフィード、カテゴリ、記事 |
| google-search-console-mcp | @ni-c/google-search-console-mcp | Google Search Console — プロパティ、サイトマップ、検索アナリティクス、URL 検査 |
| healthchecks-mcp | healthchecks-mcp | Healthchecks — cron と稼働状況のチェック、および失敗の原因 |
| hetzner-dns-mcp | hetzner-dns-mcp | Hetzner Cloud DNS — ゾーン、レコードセット、BIND のインポート/エクスポート |
| imap-mcp | @ni-c/imap-mcp | IMAP メールボックス — メールの閲覧、検索、整理、下書き作成。送信はできません |
| linkwarden-mcp | linkwarden-mcp | Linkwarden — ブックマーク、コレクション、保存された記事のテキスト |
| mealie-mcp | @ni-c/mealie-mcp | Mealie — レシピ、献立、買い物リスト、料理本 |
| ntfy-mcp | @ni-c/ntfy-mcp | ntfy — 通知の公開と更新、ユーザーとトピックへのアクセス管理 |
| opengist-mcp | opengist-mcp | Opengist — gist、リビジョン、コミット履歴、raw ファイル |
| osm-mcp | osm-mcp | OpenStreetMap — ジオコーディング、ルート検索、等時間圏、POI 検索 |
| rustpad-mcp | rustpad-mcp | Rustpad — 上書きではなく、実際の OT を通じて編集する共同編集パッド |
| smtp-mcp | @ni-c/smtp-mcp | SMTP — メールを送信します。送信先の許可リストと人による確認が必要です |
| wg-easy-mcp | wg-easy-mcp | wg-easy v15+ — WireGuard クライアントのライフサイクル全体 |
| wikijs-mcp | @ni-c/wikijs-mcp | Wiki.js — ページの検索、閲覧、編集に加え、アセット、ユーザー、グループ |
| woodpecker-ci-mcp | @ni-c/woodpecker-ci-mcp | Woodpecker CI — リポジトリ、パイプライン、ログ、シークレット、cron |
| 各サーバーはそれぞれ、stdio 上で単独でも問題なく動作します。ローカルプロセスを起動できないクライアントからアクセスできるようにしたい場合や、19 個のコネクターではなく 1 つのコネクターを登録したい場合は、ハブの背後に配置してください。 |
設定
/config/mcp.json — Claude Code と同一です(${VAR} はコンテナー環境から展開されます。Claude Code は未知のフィールドを無視するため、このファイルは相互に利用できます)。stdio サーバーのバイナリーは、レビュー済みの正確なバージョンをイメージにインストールしてください。実行時に変更される可能性のあるパッケージをダウンロードしないでください。
{
"mcpServers": {
"paperless": {
"command": "paperless-mcp",
"args": [],
"env": { "PAPERLESS_API_TOKEN": "${PAPERLESS_API_TOKEN}" }
},
"homeassistant": {
"type": "http",
"url": "http://homeassistant:8123/api/mcp",
"headers": { "Authorization": "Bearer ${HA_TOKEN}" }
},
"private-thing": { "command": "some-mcp", "args": [], "hub": false },
"paperless-readonly": {
"command": "paperless-mcp",
"allowTools": ["search_*", "get_document"],
"denyTools": ["delete_document"]
},
"untrusted": {
"type": "docker",
"image": "ghcr.io/example/untrusted-mcp@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"network": "none",
"memory": "256m"
}
}
}
stdio サーバー(command/args/env)は、監視対象の子プロセスとして起動されます。リモートサーバー(url と任意の headers を指定した type: "http" または "sse")は、設定されたヘッダーがすべてのリクエストに挿入される MCP クライアントとして接続されます。監視機能(ping、バックオフ付き再接続、ホットリロード)は同様に適用されます。上流サーバーが OAuth に対応している場合は、ヘッダーではなく oauth ブロックを使用します。ハブは(静的に、RFC 7591 経由で、またはクライアントメタデータドキュメント経由で)自身を登録し、トークンを取得して更新します。付与に人の操作が必要な場合は、管理 CLI からブラウザーを起動します。
"hub": false にすると、サーバーは /hub の集約から除外されます。サーバー固有のパスは引き続き利用できます。allowTools/denyTools は、あらゆる種類のサーバーに適用される、より詳細な制御です。フィルターされたツールは tools/list と /hub のどちらにも表示されず、呼び出された場合も、サーバーが起動される前に拒否されます。
予約済みの名前:mcp、hub、authorize、token、register、login、consent、health、livez、revoke、jwks、interaction、session、userinfo、upstream、.well-known。
すべての stdio 子プロセスはハブと同じ Unix ユーザーを共有し、マウントされたファイルを読み取ることができます。完全に信頼できる stdio サーバーのみをインストールしてください。信頼レベルが異なるサーバーは、独自のコンテナーで実行してください。そこへ接続するために HTTP に対応させる必要はありません。
type: "docker"— ハブはコンテナーを作成し、Docker API 経由でコンテナー境界を越えて stdio で通信します。HTTP リスナーもベアラートークンも、イメージ内のブリッジプロセスも不要です。ハブ自体が Docker ソケットを取得することはありません。別のmcp-hub-docker-proxyコンテナーがソケットを保持し、mcp.jsonに記述されたコンテナー操作のみを許可します。特権操作、ホストのマウント、その他のイメージは許可されません。資格情報はプロキシ側に置くことができるため(secretsFrom)、ハブプロセスが資格情報を保持することはありません。資格情報のローテーションも、ファイルを編集するだけです。プロキシがファイルを監視し、新しい値を使ってサンドボックスを再作成します。type: "unix"/"tcp"— コンテナーを起動して、ハブがソケットに接続します。ハブに特権は一切不要で、Unix ソケットを使えばnetwork_mode: noneで実行されているサンドボックスにも接続できます。
どちらも、カスタムトランスポートで再利用するよう仕様が求める、改行区切りの JSON-RPC を使用します。サンドボックス化と SECURITY.md を参照してください。
カスタムイメージを使う場合は、すべてのパッケージを正確なバージョンに固定してください。
FROM ghcr.io/ni-c/mcp-hub:0.10.0 # pin @sha256:<digest> in production
USER root
RUN npm install -g [email protected]
USER node
環境変数
空でない PASSWORD も bcrypt 形式の PASSWORD_HASH も設定されていない場合、HTTP ハブは起動しますが、ログインは無効になります。起動ログにその旨が表示され、サインインページは理由とともに 503 を返し、クライアントを承認できないため、トークンも発行されません。ハッシュが設定されている場合はそちらが優先されます。また、bcrypt 形式でないハッシュが設定されている場合も、PASSWORD にフォールバックせず、同様にログインが無効になります。ローカルの --stdio モードでは、どちらの変数も使用しません。
| 変数 | 必須 | 説明 |
|---|---|---|
EXTERNAL_URL | はい | パブリックなベース URL。例:https://mcp.example.net(パスなし) |
PASSWORD_HASH | いずれか | ログインパスワードの bcrypt ハッシュ(htpasswd -bnBC 10 "" 'pw' | tr -d ':\n') |
PASSWORD | いずれか | PASSWORD_HASH の代わりに使用するプレーンテキスト |
TRUSTED_PROXIES | いいえ | X-Forwarded-* の設定を許可する IP/CIDR のカンマ区切りリスト(下記参照) |
RESOURCE_BOUND_TOKENS | いいえ | /hub または単一の /<name>/mcp にバインドする RFC 8707 トークン。デフォルトは true。0.5 より前のバインドされていないトークンを引き続き使用する場合にのみ false を設定します |
DEFAULT_RESOURCE | いいえ | クライアントが resource パラメーターを送信しない場合にトークンをバインドするサーバー名(または hub)。未設定の場合 → そのようなリクエストは拒否されます |
MCP_BODY_LIMIT | いいえ | 認証済み MCP JSON ボディの上限。デフォルトは 1mb |
MCP_REQUESTS_PER_MINUTE | いいえ | OAuth クライアントごとの上限。デフォルトは 120 |
MCP_MAX_CONCURRENT_REQUESTS | いいえ | OAuth クライアントごとの処理中リクエスト数の上限。デフォルトは 4 |
MCP_MAX_CONCURRENT_STREAMS | いいえ | OAuth クライアントごとのオープン状態の SSE リスニングストリーム数。接続セッションごとに 1 つ、デフォルトは 32 |
HTTP_HEADERS_TIMEOUT_MS | いいえ | Node HTTP ヘッダーのタイムアウト。デフォルトは 10000 |
HTTP_REQUEST_TIMEOUT_MS | いいえ | リクエスト全体のタイムアウト。デフォルトは 310000(ツール呼び出しのタイムアウトをわずかに上回ります) |
PORT | いいえ | リッスンポート(イメージ内のデフォルトは 80、外部では 3000) |
CONFIG_PATH | いいえ | デフォルトは /config/mcp.json |
DATA_PATH | いいえ | デフォルトは /data |
LOG_FILE | いいえ | すべてのログ出力を追加でこのファイルにも書き込みます。例:/data/mcp-hub.log(下記参照) |
CLIENT_REGISTRATION | いいえ | クライアントが client_id に使用できる方式:cimd、dcr、または両方(デフォルト) |
CIMD_ALLOWED_ORIGINS | いいえ | メタデータドキュメントを受け入れる https オリジン(スキームとホストのみ)。未設定の場合 → すべて許可 |
CIMD_ALLOW_PRIVATE_ADDRESSES | いいえ | ローカル開発専用。SSRF ガードを緩和し、起動のたびに警告を表示します |
DCR_MAX_CLIENTS | いいえ | 保存される動的登録の上限。デフォルトは 500 |
DCR_PENDING_TTL_HOURS | いいえ | 一度も承認されていない登録を保持する期間。デフォルトは 24 時間 |
DCR_INACTIVE_DAYS | いいえ | 使用されていない承認済み登録を保持する期間。デフォルトは 90 日 |
IDLE_TIMEOUT_MINUTES | いいえ | オンデマンドサーバーがスリープするまでのアイドル時間(分)。デフォルトは 60。0 にすると無効になります |
TOOL_CACHE_PATH | いいえ | スリープ中のサーバーのスナップショット。デフォルトは <DATA_PATH>/tool-cache.json |
MCP_CALL_TIMEOUT_MS | いいえ | 転送される 1 回のツール呼び出しの期限。デフォルトは 300000 |
MCP_RESET_TIMEOUT_ON_PROGRESS | いいえ | 進行状況通知によって期限を延長します。デフォルトは false |
DOCKER_HOST | Docker サーバー使用時 | ポリシープロキシのソケット。デーモンソケットを直接指定した場合は安全側に倒して接続を拒否します |
| stdio モードで適用される内容を含む完全な表は、 | ||
| 環境リファレンスにあります。 |
/data には、Ed25519 JWT キー、登録済みの OAuth クライアント、承認情報、リフレッシュトークンが保存されます。ボリュームとしてマウントしてください。再作成すると、すべてのコネクターの認証が無効になります。
各アクセストークンは、1 つのリソースに紐付けられます。OAuth クライアントには、エンドポイントの RFC 9728 ドキュメントで公開されているリソースが含まれます。クライアント側での設定は不要です。発行されたトークンはそのリソースでのみ有効です。/paperless/mcp 用のトークンで /hub、/health、または別のサーバーを呼び出すことはできません。短い /<name> ルートは /<name>/mcp に正規化されます。
RESOURCE_BOUND_TOKENS=false を設定すると、この機能が無効になります。これは、リソースを指定せずにトークンが発行され、すべてのパスにアクセスできた 0.4 以前からのデプロイを移行するためのモードです。この設定中は、ハブが警告をログに記録します。設定を削除すると、リソースに紐付いていないトークンが無効になるため、すべてのコネクターで再認証が必要です。
TRUSTED_PROXIES は req.ip の値を決定し、ログインのレート制限でカウントされる IP アドレスに影響します。自分が管理するリバースプロキシのみを指定し、X-Forwarded-For に値を追加するのではなく、必ず既存の値を_上書き_するようにしてください。そうしないと、クライアントが独自のアドレスを指定して切り替え、IP ごとの制限を回避できます。この変数が未設定の場合、すべてのリクエストがプロキシからのものとして扱われ、IP ごとの制限が単一のグローバルカウンターに実質的に置き換わります(ハブは起動時に警告をログに記録します)。いずれの場合も、15 分間に 100 回までというグローバルな上限が適用されます。
実行
オプション A — GHCR からビルド済みイメージを取得(推奨)
main へのプッシュごと、および vX.Y.Z リリースタグごとに、linux/amd64 と linux/arm64 向けに公開されます。バージョンはパッケージページで確認できます。
docker pull ghcr.io/ni-c/mcp-hub:0.10.0
タグは、latest(main の最新)、X.Y.Z と X.Y(リリース)、特定のビルド用の sha-<commit> です。
更新を管理するには、latest ではなくバージョンタグを使用してください。変更されないデプロイにするには、docker image inspect で解決済みのダイジェストを記録し、Compose で ghcr.io/ni-c/mcp-hub:<version>@sha256:<digest> を使用します。
Compose を使う場合は、サンプルをコピーし、ビルドする代わりにイメージを指定します。
services:
mcp-hub:
image: ghcr.io/ni-c/mcp-hub:0.10.0 # replaces `build: .`; pin a digest in production
# ...rest of docker-compose.example.yml unchanged
cp docker-compose.example.yml docker-compose.yml # adjust, swap build → image
mkdir -p config && cp mcp.json.example config/mcp.json # adjust
mkdir -p data && sudo chown -R 1000:1000 data # container runs as uid 1000
docker compose up -d
Compose を使わない場合:
mkdir -p data && sudo chown -R 1000:1000 data # container runs as uid 1000
docker run -d --name mcp-hub \
-p 127.0.0.1:7690:80 \
-e EXTERNAL_URL="https://mcp.example.net" \
-e PASSWORD_HASH="$(htpasswd -bnBC 10 '' 'yourpassword' | tr -d ':\n')" \
-e TRUSTED_PROXIES="192.168.1.0/24" \
-v "$PWD/config:/config:ro" \
-v "$PWD/data:/data" \
ghcr.io/ni-c/mcp-hub:0.10.0
docker compose pull && docker compose up -d で新しいイメージに更新します(または docker pull … を実行してから、コンテナーを再作成します)。
オプション B — ソースからビルド
cp docker-compose.example.yml docker-compose.yml # adjust
mkdir -p config && cp mcp.json.example config/mcp.json # adjust
docker compose up -d --build
オプション C — npm(コンテナーを使わない場合)
CONFIG_PATH=./mcp.json DATA_PATH=./data PASSWORD_HASH='...' \
npx @ni-c/mcp-hub
@ni-c/mcp-hub としてインストールされ(スコープなしの npm 名は無関係なプロジェクトが使用しています)、mcp-hub と mcp-hub-admin のバイナリが提供されます。推奨されるデプロイ方法は引き続きコンテナーです。コンテナーなら、SECURITY.md が前提とする分離、読み取り専用のルートファイルシステム、リソース制限を利用できます。
リバースプロキシの要件:TLS 終端、WebSocket/SSE の許可(プロキシのバッファリングを無効にし、リクエストのタイムアウトを 310 秒より長く設定し、リクエスト本文の上限を MCP_BODY_LIMIT 以下にし、X-Forwarded-Proto/Host を渡してください)。
クライアントを接続するには、ChatGPT(開発者モード)、Claude Web、Mistral Le Chat、Cursor、LibreChat、または OAuth 対応のその他の MCP クライアントで、カスタムコネクターとして https://<host>/hub(または 1 つのサーバーの場合は https://<host>/<name>/mcp)を追加し、パスワードで一度ログインしてください。Claude Code の場合は、claude mcp add -t http name https://<host>/<name>/mcp を実行します。API のみのクライアント(OpenAI Responses API、xAI、Gemini API)では、代わりに管理者が発行したトークンを使用します。詳しくはクライアントの互換性を参照してください。
各クライアントは、接続を要求するサーバーごとに一度だけ承認されます。パスワードを入力すると、ページに表示されたサーバーについて、要求してきたクライアントが承認されます。ログインセッションがまだ有効な間に、これまで見たことのないクライアント、または別のサーバーへの接続を要求するクライアントがあると、コードの代わりに明示的な_承認/拒否_ページが表示されます。承認済みのクライアントは、その後は再接続時に確認を求められません。
クライアントの一覧表示や、クライアントの取り消しができます。CLI は稼働中のハブと /data を共有し、どちらも状態ファイルにアクセスする前に再読み込みするため、稼働中のコンテナーに対して操作できます。取り消しは次のリクエストから有効になります。
docker exec mcp-hub node /app/dist/admin.js clients list
docker exec mcp-hub node /app/dist/admin.js clients revoke CLIENT_ID
docker exec mcp-hub node /app/dist/admin.js clients delete CLIENT_ID
docker exec mcp-hub node /app/dist/admin.js clients prune --dry-run
取り消すと、承認とすべてのリフレッシュトークンが削除され、すでに発行されたアクセストークンも直ちに拒否されます。次回の接続では、明示的な承認が必要です。delete はさらに登録自体を削除し、prune は登録のライフサイクルルールを任意のタイミングで適用します。承認されなかった登録は 1 日後、使用されていない登録は 90 日後に期限切れとなります。また、動的に登録されたクライアントは RFC 7592 を通じて自身の登録を削除することもできます。
OAuth にまったく対応していないクライアント(OpenAI Responses API、xAI API、Gemini の mcp_server ツール、プレーンヘッダー形式のクライアント)には、同じ CLI で有効期間の長い、リソースにひも付いた API トークンを発行できます。
docker exec mcp-hub node /app/dist/admin.js tokens create --resource hub --days 90 --label "openai"
docker exec mcp-hub node /app/dist/admin.js tokens list
docker exec mcp-hub node /app/dist/admin.js tokens revoke TOKEN_ID
トークンは一度だけ表示され、保存されることはありません。tokens revoke を実行すると、取り消しは直ちに有効になります。クライアントごとの設定方法については、クライアントの互換性を参照してください。
エンドポイント
| パス | 認証 | 目的 |
|---|---|---|
/<name>, /<name>/mcp | Bearer | 1 つのサーバーの Streamable HTTP エンドポイント |
/hub | Bearer | 6 つのメタツールを備えた集約エンドポイント |
/livez | なし | 最小限のプロセス稼働確認(200) |
/health | Bearer | サーバーごとの状態(すべて稼働中なら 200/劣化時は 503) |
/authorize, /token, /register, /revoke, /interaction/<uid>/… | — | OAuth 2.1 · CIMD + DCR。ログインページと同意ページは /interaction/ 配下にあります |
/register/<client_id> | 登録トークン | RFC 7592:クライアントは自身の登録情報を読み取り、変更、削除できます |
/upstream/callback | 署名付き state + hub セッション | upstream の upstream login 後のリダイレクト先 |
/.well-known/mcp-hub-client/<id>.json | なし | hub 自身のクライアントメタデータ文書。cimd upstream ごとに 1 つあります |
/.well-known/oauth-authorization-server[/…] | なし | RFC 8414 メタデータ |
/.well-known/oauth-protected-resource[/…] | なし | RFC 9728 メタデータ(パススコープ付き) |
注意事項と制限
- 変更通知(
listChanged、リソースの更新)は、2026-07-28ではsubscriptions/listenを通じて送信されます。この状態はセッションテーブルではなく、 開いているレスポンスです。2025-11-25クライアントにはいずれも提供されません。 このリビジョンには、ステートレスなトランスポートが維持しないチャネルが必要なため、 機能を通知してから破棄するのではなく、機能自体を提供しません。オンデマンドの サーバーは、スリープ中は何も監視しません。次に起動したときにサブスクリプションが 再確立され、クライアントには再読み込みが通知されます。 - エリシテーションは
2026-07-28でエンドツーエンドに送信されます。これは プッシュではなく結果として送られます。サンプリングとログメッセージは転送されません。 - アクセストークンは不透明で、有効期間は 15 分です。クライアントの失効はトークンの 有効期限が切れたときではなく、次のリクエストから有効になります。トークンは保存 レコードへの参照であるため、取り消しはレコードの削除によって行われます。リフレッシュ トークンはローテーションされます。すでにローテーションで無効になったトークンが再利用 された場合は漏えいとして扱われ、アクセストークンを含む付与全体が失効します。
- upstream 認証は hub 自身の OAuth から完全に分離されています。upstream トークンの
有効期限が切れると、そのサーバーだけが
unauthorizedとしてマークされます (そのパスでは503、/healthに表示)。クライアントに upstream の 401 が 表示されることはありません。 - 1 回のログインで複数のコネクターを承認できますが、各トークンが有効なのは、
リクエストされたサーバーまたは
/hubに限られます。MCP 仕様の意図どおり、 登録は引き続きオープンです。クライアントがコードを受け取れるのは確認後に限られ、 確認済みのリダイレクト先でのみ受け取れます。 - ログインの失敗はレート制限され(IP ごとに 15 分間で 10 回)、fail2ban 用に
mcp-hub: authentication failure from <ip>として記録されます。 - 認証ページはフレームへの埋め込みを拒否し、制限の厳しい CSP を適用します。MCP ボディは Bearer の検証後にのみ解析され、サイズ、クライアントごとのリクエストレート、 クライアントごとの同時実行数が制限されます。
fail2ban 用のファイルへのログ記録
LOG_FILE=/data/mcp-hub.log を設定すると、コンソール出力はそのままに、すべてのログ行がそのファイルにも記録されます。各エントリーは ISO-8601 UTC のプレフィックス付きで 1 行に記録されます。そのため、docker logs も引き続き使えます。次のように、jail でファイルを直接読み取ります。
# /etc/fail2ban/filter.d/mcp-hub-auth.conf
[Definition]
failregex = mcp-hub: authentication failure from <HOST>\s*$
mcp-hub: login rate limit exceeded from <HOST>\s*$
mcp-hub: consent with an invalid CSRF token from <HOST>\s*$
ignoreregex =
ミラーされるのはハブ自身の行だけです。stdio の子プロセスは stderr を直接継承するため、その出力はコンテナログに記録され、ファイルは小さいままです。ハブはファイルを開いたままにするため、logrotate(copytruncate)でローテーションしてください。
コンテナ自身のログを読み取らない理由:Docker の json-file のパスにはコンテナ ID が含まれ、コンテナを再作成するたびに変わります。また、journald ドライバーはすべての stderr を優先度 err に割り当てます。MCP サーバーはプロトコル用に stdout を空けておく必要があり、ログを stderr に出力するため、通常の行もすべてシステムエラーとして表示され、ホストの監視を妨げてしまいます。
コンテナベースのリバースプロキシ経由でハブを公開する場合、BAN は DOCKER-USER チェーン(banaction = iptables-allports)に設定します。このトラフィックは DNAT と FORWARD を経由し、INPUT は通過しません。
開発
npm install
npm test # vitest: config, OAuth flow, proxy E2E, hub, hot reload
npm run dev # tsx, needs EXTERNAL_URL/PASSWORD/CONFIG_PATH/DATA_PATH
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。