mcpField
M
MCP Proxy

MCP Proxy

ローカル
github.com/punkpeye開発ツール2931 週間前に更新

MCP Proxy について

MCP Proxyは、stdioベースのMCPサーバーをストリーミングHTTPとSSEで公開するTypeScript製プロキシです。HTTPクライアント向けにサーバーを提供・共有する用途を想定し、ストリーミングHTTPエンドポイントでは2025年世代と2026-07-28のプロトコル改訂版に対応します。

主な機能

  1. 01stdioからHTTP・SSEへのプロキシ
  2. 02単一エンドポイントで複数のプロトコル改訂版に対応
  3. 03オプションのAPIキー認証
  4. 04ステートレスHTTPモード
  5. 05再接続後のイベント再生
  6. 06設定可能なCORS

活用シーン

  1. 01stdioサーバーをHTTPクライアントに公開
  2. 02同じURLで新旧プロトコルのクライアントに対応
  3. 03サーバーレス環境でリクエストを個別処理
  4. 04パブリックトンネル経由でローカルサーバーを共有

MCP 対応

stdioでMCPサーバーを起動し、ストリーミングHTTPとSSE経由でMCPクライアントに機能を公開します。HTTPエンドポイントは2025年世代と2026-07-28に対応し、stdioでの提供は2025年世代のクライアントに対応します。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"proxy-punkpeye": {
"command": "npx",
"args": [
"-y",
"mcp-proxy"
]
}
}
}
起動コマンドnpx -y mcp-proxy

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npmmcp-proxy

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

MCP Proxy

stdio トランスポートを使用するMCPサーバー向けの、TypeScript 製ストリーミング可能な HTTP および SSE プロキシです。

メモ

CORS はデフォルトで有効になっており、オプションを設定できます。詳細はCORS の設定をご覧ください。

メモ

逆の用途、つまりインタラクティブな OAuth フローを含め、stdio 専用のクライアントからリモート MCP サーバーにアクセスする方法については、mcp-remoteをご覧ください。

メモ

MCP Proxy は、FastMCP がストリーミング可能な HTTP と SSE を有効にするために使用しています。

プロトコルのリビジョン

MCP Proxy は、同じエンドポイントからプロトコルリビジョン 2026-07-28 と 2025 年代のリビジョン (2024-10-07 から 2025-11-25)を提供します。ストリーミング可能な HTTP エンドポイントへのすべての POST は、 それぞれのリクエスト内容に基づいて分類されます。リビジョン 2026-07-28 ではすべてのリクエストに _meta エンベロープが含まれ、これを含まないリクエストには 2025 年代のセッション機構で応答するため、 旧世代と新世代のクライアントは同じ URL を共有でき、互いの存在を意識する必要はありません。

2025 年代のクライアント2026-07-28 のクライアント
/mcp(ストリーミング可能)はい、セッションありはい、リクエスト単位
/sse(HTTP+SSE)はい該当なし — SSE トランスポートはありません
stdio(startStdioServer)はい未対応 — 以下を参照
変更通知未要求で送信subscriptions/listen ストリーム

進行状況通知とキャンセルは、どちらの世代でもプロキシを介して伝達されます。転送された呼び出しには呼び出し元の 中断シグナルが引き継がれ、中継される進行状況には呼び出し元が送信したトークンが再設定されます。

2025 年代のクライアントのみに対応するには、--no-modern を使用してください。この場合、 2026-07-28 のクライアントには、そのリクエストを認識しない 2025 年代の機構で応答します。 モダン対応はストリーミング可能な HTTP エンドポイントで動作するため、このエンドポイントが無効な場合 (--server sse または streamEndpoint: null)も利用できません。

上流接続、つまり MCP Proxy が起動するサーバーは、独立して自身のリビジョンをネゴシエートするため、 どのような組み合わせでも動作します。デフォルトでは 2025 年の initialize ハンドシェイクを使用します。 まず server/discover でプローブするには --upstreamProtocol auto を渡してください (2026-07-28 のサーバーを前段に置く場合に必要です)。必須にするには --upstreamProtocol modern を渡してください。

# front a 2026-07-28 stdio server, serving both eras downstream
npx mcp-proxy --upstreamProtocol auto -- my-modern-mcp-server
プロキシを通過しないもの

これらは、プロキシがすべての下流接続を単一の上流接続に多重化するという設計判断から生じる制約です。中途半端に解決するのではなく、明示しています。

  • クライアントに入力を求めるもの(エリシテーション、サンプリング、ルート)。2025 年当時のサーバーは、どの呼び出し元のリクエストかを示す情報を含めずに、帯域外でリクエストを送信します。そのため、これを中継するには、どのユーザーの画面にプロンプトを表示するか推測する必要があります。2026-07-28 の上流ではそのような曖昧さはありません。リクエストは、プロキシが転送した呼び出しの input_required 結果として返るため、この方向の課題は相関ではなくスコープの問題です。まだ実装されていません。
  • **2026-07-28 接続の両側におけるログメッセージ。**このリビジョンでは、ログはリクエストごとに送信されます。クライアントはリクエストの _meta でログを要求し、サーバーはそのリクエストのストリームで応答します。プロキシはどちらの処理もできません。下流側にはリクエストごとのログコールバックがなく、通知ハンドラーにもログの帰属先を特定する情報が渡されません。上流側ではログをまったく要求できないため、2026-07-28 サーバーのリクエストごとのロギングは一度も実行されません。2025 年当時のクライアントには、非推奨の非同期送信経路が引き続き使われます。つまり、2025 年当時の上流からのすべてのログと、2026-07-28 上流がリクエストごとのチャネル外で送信するログのみが対象です。準拠したサーバーは、このチャネル外の経路を使用しません。
  • **上流への logging/setLevel。**下流接続ごとに応答し、その接続が要求したレベル未満のメッセージは転送しないよう適用されますが、上流には一切伝達されません。共有される上流のログレベルは 1 つだけなので、転送すると 1 つのクライアントが別のクライアントのログを止められてしまいます。また、このメソッドは 2026-07-28 上流には存在しません。
  • **クライアントの識別情報。**上流からは下流の呼び出し元ではなく、mcp-proxy がクライアントとして認識されます。クライアントにひもづくテレメトリも同様です。
  • stdio 経由の 2026-07-28。startStdioServer が対応するのは 2025 年当時のクライアントのみです。HTTP 側はどちらにも対応します。

メモ

/sse エンドポイントでは、v2 SDK で削除された HTTP+SSE トランスポートの固定版 @modelcontextprotocol/server-legacy を使用しています。このトランスポートは 2026-07-28 をもって非推奨となり、移行期間は 12 か月です。クライアントをストリーミング可能な HTTP エンドポイントに移行してください。

インストール

npm install mcp-proxy

クイックスタート

コマンドライン
# no mcp-proxy options
npx mcp-proxy npx -y @anthropic/mcp-server-filesystem /path

# with mcp-proxy options
npx mcp-proxy --port 8080 --shell -- tsx server.js

2 つ目のコマンドは、stdio 経由で tsx server.js を起動し、ポート 8080 の /mcp(ストリーミング可能な HTTP)と /sse(SSE)で公開します。

メモ

-- 区切り文字について:

  • mcp-proxy にオプションを渡す必要がない場合、-- 区切り文字は省略可能です
  • --port や --shell などのオプションを mcp-proxy に渡す場合は、コマンドと明確に区別するために -- を使用します
  • -- がない場合、最初の位置引数はコマンドとして扱われ、それ以降の引数はすべてそのコマンドに渡されます
  • コマンド自体に mcp-proxy のオプションと競合する可能性のあるフラグがある場合にも、-- 区切り文字が役立ちます

オプション:

  • --server:sse または stream を指定すると、対応するトランスポートのみを有効にします(デフォルト:両方)
  • --endpoint:server に sse または stream が設定されている場合、このオプションでエンドポイントのパスを設定します(デフォルト:/sse または /mcp)
  • --sseEndpoint:SSE エンドポイントのパスを設定します(デフォルト:/sse)。server に sse が設定されている場合、--endpoint を上書きします。
  • --streamEndpoint:ストリーミング可能な HTTP エンドポイントのパスを設定します(デフォルト:/mcp)。server に stream が設定されている場合、--endpoint を上書きします。
  • --stateless:セッションを維持する代わりに、リクエストごとに新しいサーバーインスタンスを作成します。2025-era のレッグに適用されます。2026-07-28 は仕様上、リクエストごとに処理されます。
  • --modern:2025-era のリビジョンに加えて、ストリーミング可能な HTTP エンドポイントでプロトコルリビジョン 2026-07-28 を提供します(デフォルト:true)。2025-era のクライアントのみを提供するには、--no-modern を使用します。プロトコルリビジョンを参照してください。
  • --upstreamProtocol:起動したサーバーとの通信に使用するプロトコルリビジョンです。legacy(デフォルト)、auto、または modern を指定します。プロトコルリビジョンを参照してください。
  • --port:待ち受けるポートを指定します(デフォルト:8080)
  • --connectionTimeout:MCP サーバーへの初期接続のタイムアウトをミリ秒単位で指定します(デフォルト:60000)
  • --requestTimeout:MCP サーバーへのリクエストのタイムアウトをミリ秒単位で指定します(デフォルト:300000)
  • --keepAliveTimeout:ステートフルなストリームセッションの HTTP キープアライブタイムアウトをミリ秒単位で指定します(デフォルト:300000)
  • --sessionIdleTimeout:ストリームが接続されず、リクエストもない状態でステートフルなストリームセッションを維持する時間をミリ秒単位で指定します。経過後、セッションは閉じられます(デフォルト:1800000、30 分)。クライアントが DELETE を送信するまで各セッションを維持するには、0 を設定します。放棄されたセッションを参照してください。
  • --eventStore:再接続後にクライアントが未受信のメッセージを再生できるよう、再開可能性イベントストアを有効にします(デフォルト:true)。--no-eventStore で無効にできます。再開可能性とメモリ使用量を参照してください。
  • --eventStoreMaxEvents:再開可能性イベントストアがセッションごとに保持するイベントの最大バッファー数です。上限を超えると最も古いイベントが破棄されます(デフォルト:1000)。--no-eventStore が設定されている場合は無視されます。
  • --maxBodySize:ストリーミング可能な HTTP エンドポイントが受け付けるリクエスト本文の最大サイズをバイト単位で指定します。これを超えるリクエストには 413 Payload Too Large が返されます(デフォルト:10485760、10 MiB)。制限を無効にするには 0 を設定します。リクエスト本文のサイズを参照してください。
  • --debug:デバッグログを有効にします
  • --shell:ユーザーのシェル経由でサーバーを起動します
  • --apiKey:リクエストの認証に使用する API キーです(X-API-Key ヘッダーを使用)
  • --sslCa:信頼する CA 証明書を上書きするファイル名
  • --sslCert:PEM 形式の証明書チェーンのファイル名
  • --sslKey:PEM 形式の秘密鍵のファイル名
  • --tunnel:パブリックトンネル経由でプロキシを公開します(パブリックトンネルを参照)
  • --tunnelSubdomain:トンネル用の特定のサブドメインをリクエストします(利用可能とは限りません)
  • --corsAddAllowedHeader:Access-Control-Allow-Headers にヘッダー名を追加します(デフォルトは維持されます)。複数追加する場合は繰り返し指定します。--apiKey と併用すると、X-API-Key を含むブラウザーのプリフライトリクエストが成功するため便利です。
Python stdio サーバーのトラブルシューティング

Python stdio サーバーが Expected server to respond to ping でタイムアウトする場合は、Python をバッファリングなしで実行してください。stdout のバッファリングによって JSON-RPC メッセージの送信が遅れ、プロキシがサーバーを応答なしと判断することがあります。

npx mcp-proxy -- python -u -m your_package.mcp_server
# or: PYTHONUNBUFFERED=1 npx mcp-proxy -- python -m your_package.mcp_server

stdio サーバーでは、stdout をプロトコルメッセージ専用にする必要があります。診断情報は stderr に送信し、プロキシ側のログには --debug を使用してください。

パブリックトンネル

ローカルサーバーをパブリックインターネットに公開します。Webhook のテスト、開発サーバーの共有、リモートアクセスに便利です。

npx mcp-proxy --port 8080 --tunnel -- tsx server.js

# request a specific subdomain
npx mcp-proxy --port 8080 --tunnel --tunnelSubdomain myapp -- tsx server.js

トンネルが確立すると、次のようなメッセージが表示されます。

tunnel established at https://abcdefghij.tunnel.gla.ma

メモ

要求したサブドメインが利用できない場合があります。トンネルが確立すると、実際の URL が表示されます。

pipenet を利用し、glama.ai の協賛を受けています。詳しくはお知らせをご覧ください。

ステートレスモード

デフォルトでは、各クライアント接続はセッションが終了するまでサーバーインスタンスを保持します。代わりに --stateless を指定すると、リクエストごとに新しいインスタンスが作成されるため、リクエストが完全に独立します。これは、サーバーレスプラットフォームや負荷分散されたデプロイメント、接続状態を保持したくない環境に適しています。

npx mcp-proxy --port 8080 --stateless -- tsx server.js

# stream-only transport
npx mcp-proxy --port 8080 --stateless --server stream -- tsx server.js

メモ

ステートレスモードが影響するのは、HTTP ストリーマブルトランスポート(/mcp エンドポイント)のみです。SSE トランスポートの動作は変わりません。

API キー認証

任意の機能で、デフォルトでは無効です。有効にすると、クライアントは X-API-Key ヘッダーで有効なキーを送信する必要があります。

npx mcp-proxy --port 8080 --apiKey "your-secret-key" -- tsx server.js

# or via the environment
export MCP_PROXY_API_KEY="your-secret-key"
npx mcp-proxy --port 8080 -- tsx server.js

クライアントはトランスポートヘッダーとしてキーを渡します。

const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:8080/mcp"),
  { headers: { "X-API-Key": "your-secret-key" } },
);

/ping と OPTIONS のプリフライトは認証対象外です。キーはヘッダーで送信されるため、本番環境では HTTPS で配信してください。

CORS の設定
デフォルトの動作

CORS はデフォルトで次の設定で有効になっています。

  • オリジン:*(すべてのオリジンを許可)
  • メソッド:GET, POST, DELETE, OPTIONS
  • ヘッダー:Content-Type, Authorization, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-Id, Mcp-Method, Mcp-Name
  • 認証情報:true
  • 公開ヘッダー:Mcp-Session-Id

許可されたオリジンが * になる場合、Access-Control-Allow-Credentials は省略されます。Fetch Standard では、両方が指定されているとブラウザーがレスポンス全体を拒否するためです。そのため、認証情報を含むブラウザーリクエスト(credentials: "include"、withCredentials)では、ワイルドカードではなく、リストまたは関数による明示的な origin の指定が必要です。

2026-07-28 のストリーマブル HTTP POST では Mcp-Method と Mcp-Name が必要なため、そのリビジョンを使用するブラウザークライアントでは、これらを許可する必要があります。そのリビジョンの Mcp-Param-* ヘッダーはプレフィックスであり、Access-Control-Allow-Headers では表現できません。ツールで x-mcp-header パラメーターを宣言している場合は、--corsAddAllowedHeader または allowedHeaders で該当するヘッダー名を明示的に追加してください。

設定

cors: true(または省略時)はデフォルト設定を使用し、cors: false は無効にします。CorsOptions オブジェクトでは個々のフィールドを上書きできます。

import { startHTTPServer, CorsOptions } from "mcp-proxy";

const cors: CorsOptions = {
  // A list, or a function for dynamic validation
  origin: ["https://app.example.com"],
  methods: ["GET", "POST", "DELETE", "OPTIONS"],

  // "*" allows any header — the usual fix for browser preflight failures.
  // Listing headers instead REPLACES the defaults, so restate the protocol's
  // own: dropping Mcp-Method/Mcp-Name breaks browser clients on 2026-07-28.
  allowedHeaders: "*",

  exposedHeaders: ["Mcp-Session-Id"],
  credentials: true,
  maxAge: 86400,
};

await startHTTPServer({ cors, createServer, port: 3000 });

以前はヘッダーが自動的にワイルドカードになっていた 5.5.6 以前からアップグレードする場合は、allowedHeaders: "*" を設定すると、その動作に戻せます。

CLI:許可するヘッダーの追加

--corsAddAllowedHeader はデフォルトを維持したまま、Access-Control-Allow-Headers のリストにヘッダーを追加します。複数追加する場合は、繰り返し指定してください。よくある用途は、--apiKey でカスタム認証ヘッダーを使用するときに、ブラウザーのプリフライトを通過できるようにすることです。

npx mcp-proxy --port 8080 --apiKey secret \
  --corsAddAllowedHeader X-API-Key \
  -- npx -y @modelcontextprotocol/server-filesystem /srv

オリジンの許可リスト、ワイルドカードヘッダー、または CORS の無効化には、上記のプログラムによる cors オプションを使用してください。

Node.js SDK
proxyServer

サーバーとクライアントの間にプロキシを設定します。

const transport = new StdioClientTransport();
const client = new Client();

await client.connect(transport);

const serverCapabilities = client.getServerCapabilities();

const server = new Server(client.getServerVersion(), {
  capabilities: serverCapabilities,
});

await proxyServer({
  client,
  server,
  serverCapabilities,
});

この例では、サーバーがすべてのリクエストをクライアントに転送し、その逆も行います。ハンドラーは serverCapabilities を基に登録されるため、上流が一度も通知していないメソッドは、確認のために往復することなく、ローカルで「method not found」と応答します。

オプション:

  • client:接続済みの上流クライアント
  • server:接続する下流サーバー
  • serverCapabilities:上流が通知した内容。登録するハンドラーが決まります
  • requestTimeout:転送するすべてのリクエストに適用されるタイムアウト(ミリ秒単位、任意)

メモ

1 回の proxyServer 呼び出しで 1 つの下流接続を処理しますが、上流クライアントは共有されるため、上流からの通知は 1 回受信され、接続中のすべてのクライアントに送られます。転送前に絞り込まれる通知は 2 つあります。notifications/resources/updated はその URI をサブスクライブした接続にのみ届き、notifications/message は logging/setLevel で許可した接続にのみ届きます。レベルを一度も設定していない接続には、すべての通知が届きます。list_changed 通知は絞り込まれません。1 つの上流について通知するものであり、すべての接続がその上流を参照しているためです。1 つのプロキシから複数のテナントにサービスを提供するため、createServer でリクエストごとの ID を導出する場合は、その実装がご自身のモデルに沿っているか確認してください。

startHTTPServer

port でリッスンし、StreamableHTTPServerTransport と SSEServerTransport を介して、接続されたサーバーにメッセージを送信するプロキシを起動します。

import { Server } from "@modelcontextprotocol/server";
import { startHTTPServer } from "mcp-proxy";

const { close, notify } = await startHTTPServer({
  createServer: async () => {
    return new Server();
  },
  port: 8080,
  stateless: false, // Optional: enable stateless mode for streamable HTTP transport
});

close();

オプション:

  • createServer:接続ごとに新しいサーバーインスタンスを作成する関数
  • eventStore:ストリーム可能な HTTP トランスポートの再開サポート用イベントストア(任意)。再開を完全に無効にするには false を指定します。セッションごとに新しい上限付き InMemoryEventStore を使用するには省略します(eventStoreMaxEvents を参照)。独自の EventStore インスタンス(永続化可能/プロセス間共有など)を使用するには、そのインスタンスを指定します。この場合、すべてのセッションで共有されます。再開とメモリ使用量を参照してください。
  • eventStoreMaxEvents:自動作成されるセッションごとの InMemoryEventStore が、最も古いイベントを削除する前に保持するイベント数の上限(デフォルト:1000)。eventStore が明示的に指定されていない場合のみ適用されます。
  • port:リッスンするポート番号
  • host:バインドするホスト(デフォルト:"::")
  • keepAliveTimeout:ステートフルなストリームセッションの HTTP キープアライブタイムアウト(ミリ秒、デフォルト:300000)
  • sessionIdleTimeout:ストリームが接続されておらず、リクエストもない状態でステートフルなストリームセッションを維持する時間(ミリ秒、デフォルト:1800000、30 分)。クライアントが DELETE を送信するまで、すべてのセッションを維持するには 0 を指定します。放棄されたセッションを参照してください。
  • maxBodySize:ストリーム可能な HTTP エンドポイントがバッファリングするリクエスト本文のバイト数の上限。これを超えるリクエストには 413 Payload Too Large が返されます(デフォルト:10485760、10 MiB)。上限を無効にするには false を指定します。リクエスト本文のサイズを参照してください。
  • sseEndpoint:SSE エンドポイントのパス(デフォルト:"/sse"、無効にするには null を設定)
  • streamEndpoint:ストリーム可能な HTTP エンドポイントのパス(デフォルト:"/mcp"、無効にするには null を設定)
  • stateless:HTTP ストリーム可能トランスポートのステートレスモードを有効にします(デフォルト:false)。2025 年世代の経路に適用されます。プロトコルリビジョン 2026-07-28 は、仕様上、リクエストごとに処理されます。
  • modern:streamEndpoint で 2025 年世代のリビジョンとともに、プロトコルリビジョン 2026-07-28 を提供します(デフォルト:true)。プロトコルリビジョンを参照してください。
  • apiKey:リクエスト認証用の API キー(任意)
  • cors:CORS 設定(デフォルト:制限の緩い設定で有効。CORS 設定のセクションを参照)
  • onConnect:サーバーインスタンス作成時のコールバック(任意)。2025 年世代の経路ではセッションごとに 1 回 呼び出されます。2026-07-28 の経路ではリクエストごとに新しいインスタンスが作成されるため、リクエストごとに 1 回 呼び出されます。
  • onClose:サーバーインスタンス破棄時のコールバック(任意)。呼び出し単位は onConnect と同じ世代ごとです。処理は軽量かつ冪等にしてください。拒否されてもログに記録されるだけで、残りの破棄処理は停止しません。
  • onUnhandledRequest:未処理の HTTP リクエストに対するコールバック(任意)

{ close, notify } を返します。notify の用途については、変更通知を参照してください。

変更通知

2025 年頃のクライアントは、同じ接続上で list_changed と resources/updated を非要求で受信し、proxyServer がこれらをクライアントに転送します。

プロトコルリビジョン 2026-07-28 にはそのような接続はありません。これらのイベントは、クライアントが開いた subscriptions/listen ストリーム上でのみ配信され、リクエストごとに作成されるサーバーインスタンスはストリームを所有しません。代わりに、イベントはハンドラーのバスに公開されます。これは startHTTPServer が notify として返すものです。上流からの変更イベントを次のように接続してください。

import { getUpstreamBridge, startHTTPServer } from "mcp-proxy";

const { notify } = await startHTTPServer({ createServer, port: 8080 });

getUpstreamBridge({ client }).subscribe({
  promptsListChanged: () => notify.promptsChanged(),
  resourcesListChanged: () => notify.resourcesChanged(),
  resourceUpdated: ({ uri }) => notify.resourceUpdated(uri),
  toolsListChanged: () => notify.toolsChanged(),
});

modern: false の場合、notify は何もしないため、この接続は無条件に設定しても安全です。mcp-proxy CLI もまさにこの処理を行います。

getUpstreamBridge(client) は、クライアントごとに上流の通知ハンドラーを一度だけ登録し、通知を各所に配信するため、複数の下流接続で 1 つの上流サーバーを共有できます。subscribe(sink) は、addResourceSubscription / removeResourceSubscription / owns / release を備えたリースを返します。リソースのサブスクリプションはリースごとに所有されるため、ある接続がサブスクライブを解除しても、別の接続が引き続き必要としている URI のサブスクリプションは解除されません。また、release() は切断する接続が保持していたものをすべて解放します。

2026-07-28 のクライアントは、resources/subscribe ではなく、subscriptions/listen フィルターの resourceSubscriptions フィールドを使ってリソースの更新を要求します。これらの URI に対処するには onListenSubscriptions を渡してください。CLI が使う実装である acquireListenSubscriptions は、すべてを一括して取得します。そのため、上流がフィルターの一部しか受け入れない場合でも、残りが未解決のまま残ることはありません。

import { acquireListenSubscriptions } from "mcp-proxy";

await startHTTPServer({
  createServer,
  onListenSubscriptions: (uris) => acquireListenSubscriptions({ client, uris }),
  port: 8080,
});
再開可能性とメモリ使用量

ストリーミング可能な HTTP トランスポートは、サーバーからクライアントへのメッセージを保持できるため、Last-Event-ID を指定して再接続したクライアントは、中断したところから再開できます。各セッションは、最大 1000 件のイベントを保持する独自の InMemoryEventStore を持ちます(--eventStoreMaxEvents / eventStoreMaxEvents で設定でき、古いイベントから削除されます)。これにより、長期間存続するセッションのメモリ使用量はプロセスの稼働期間中に増え続けず、上限内に保たれます。

再接続後の再開が不要ですか?--no-eventStore(CLI)または eventStore: false(ライブラリ)を指定すると、記録管理を完全に無効にできます。再生範囲を広げる場合や、プロセス間でストレージを共有する場合(例:Redis)は、独自の EventStore を渡してください。その場合、サイズの制限はご自身の責任で行ってください。

放棄されたセッション

2025 年頃のセッションは、作成元の接続が終了した後も存続します。これによって再接続後の再生が可能になります。セッションは DELETE によって終了させる想定です。しかし実際には、多くのクライアントは DELETE を送信しません。ノートパソコンを閉じたり、プロセスが強制終了されたり、ネットワークが切断されたりして、その後は応答がなくなるだけです。残された各セッションは、サーバーインスタンス、トランスポート、そして配信できなくなった通知をバッファーし続けるイベントストアを保持します。

mcp-proxy は、何も接続されておらず、30 分間何も起きなかったセッションを終了します(--sessionIdleTimeout / sessionIdleTimeout)。ストリームが接続されているセッションは、どれだけ静かでも終了しないため、通知ストリームを開いたままのクライアントには影響しません。カウントダウンは最後のリクエスト時ではなく、ストリームが切断されたときに始まります。セッション終了時には DELETE と同じ処理が実行されるため、これまでどおり onClose が呼び出されます。

その代わり、タイムアウト後は再開できません。後から戻ったクライアントが Last-Event-ID を指定しても、再生ではなく新しいセッションが作成されます。デフォルトのインメモリストアは 1000 件が上限なので、その頃には再生できるイベントはほとんど残っていないでしょう。クライアントが DELETE を送信するまで全セッションを保持するには、タイムアウトを 0 に設定してください。

/sse エンドポイントのセッションは接続が終了すると同時に終了するため、この設定の影響を受けません。また、--stateless モードとプロトコルリビジョン 2026-07-28 も、リクエスト間で何も保持しないため影響を受けません。

リクエストボディのサイズ

ストリーマブル HTTP エンドポイントは解析前に各リクエストボディをバッファーに読み込むため、チャンクをゆっくり送信するクライアントが送信を続ける限り、プロセスのメモリ使用量が増え続ける可能性があります。mcp-proxy はこのバッファーの上限を 10 MiB(--maxBodySize/maxBodySize)に設定します。サイズが上限を超える Content-Length は、ボディを読み取る前に拒否されます。チャンク形式のボディは、上限を超えた時点で読み取りが打ち切られます。どちらの場合も、上限を示す JSON-RPC エラーボディを含む 413 Payload Too Large が返され、その後、接続が閉じられます。

デフォルト値は意図的に大きめに設定されています。base64 画像、ドキュメント、長い貼り付けテキストを含むと、1 回のツール呼び出しのサイズが 1 メガバイトを超えることがよくあるためです。クライアントが正当な理由でこれより大きなデータを送信する場合は値を引き上げるか、0(CLI)/false(ライブラリ)を設定して無効にしてください。無効にするとバッファーのサイズが無制限に戻るため、ボディサイズをすでに制限しているゲートウェイの背後でのみ安全です。この上限は SSE エンドポイントには適用されません。その POST /messages ボディは mcp-proxy ではなく MCP SDK が読み取るためです。

startStdioServer

stdio でリッスンし、接続された sse または streamable サーバーにメッセージを送信するプロキシを起動します。

import { ServerType, startStdioServer } from "mcp-proxy";

await startStdioServer({
  serverType: ServerType.SSE,
  url: "http://127.0.0.1:8080/sse",
});

オプション:

  • serverType:ServerType.SSE または ServerType.HTTPStream
  • url:アップストリーム MCP サーバーの URL
  • transportOptions:クライアントトランスポートに渡すオプション(任意)
  • upstreamProtocol:アップストリームで使用するプロトコルのリビジョン。"legacy"(デフォルト。2025 年の initialize ハンドシェイク)、"auto"(server/discover で確認し、失敗した場合はハンドシェイクにフォールバック)、または "modern"(2026-07-28 を必須とします)。プロトコルのリビジョンを参照してください。この接続で提供される stdio は、いずれにせよ 2025 年当時の仕様です。
  • initStdioServer/initStreamClient:独自の Server/Client インスタンスを使用します(任意)

返されたサーバーを閉じると、startStdioServer が作成したアップストリームクライアントも閉じられます。initStreamClient から提供されたクライアントは、通常のシャットダウン時も呼び出し元が所有します。

tapTransport

トランスポートのイベントを監視し、ログに記録します。

import { tapTransport } from "mcp-proxy";

const transport = tapTransport(new StdioClientTransport(), (event) => {
  console.log(event);
});

開発

MCP Proxy をローカルサーバーと実行する
tsx src/bin/mcp-proxy.ts --debug -- tsx src/fixtures/simple-stdio-server.ts

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。