Skip to main content
Glama

2つのMCPサーバー、1つのプロダクト、2つのプロトコル改訂

同じショッピングカートを、旧ステートフルMCP仕様(2025-11-25)と新ステートレス仕様(2026-07-28)の2回実装しました。並べて実行して、どちらかが落ちるのを見てください。

目的は動くコードではありません——仕様がなぜ変わったのかを理解することです。ここにあるすべての実験は、失敗がはっきり見え、その理由がワイヤーレベルで確認できるように設計されています。

MCPとは何か?(3文で)

MCP——Model Context Protocol——は、AIアプリケーションが他人の書いたツールを呼び出すための標準的な方法です。Language Server Protocolが各エディタが独自にTypeScriptサポートを書くのを置き換えたのと同じように、Nアプリケーション×M統合をN+Mに置き換えます。具体的には、合意されたメソッド名を持つJSON-RPC 2.0メッセージで、stdioまたはHTTPで送信されます。

もっと長い説明が必要なら: docs/01 — MCPが存在する理由

Related MCP server: Online Boutique AI Assistant MCP Server

このリポジトリが示すもの

5つのツール——catalog_listcart_createcart_add_itemcart_viewcart_checkout——は、両サーバーで同一の名前を持ち、MCPを知らない共有cart-coreパッケージに同一のビジネスロジックを持ちます。2つのサーバーの唯一の違いはプロトコル層であり、それがまさに研究対象です。

観察できる4つのこと:

  1. server-oldは、単純なラウンドロビンロードバランサーの背後でハンドシェイクを完了できません。server-newはロードバランサーの存在に気づきもしません。

  2. 会話の途中でserver-oldを再起動すると、カートは永久に消え、クライアントが送信できるリカバリ用のリクエストはありません。

  3. 「この合計で確認しますか?」という質問は、server-oldでは人間が考える時間の間ずっとソケットを開いたままにします(測定値1522ms)。server-newは2つの独立したリクエスト、4ms + 15msで完了し、開始したマシンとは別のマシンで終了できます。

  4. 安定したリスト順序とキャッシュヒント——そして、欠落した.sort()が年間約**$4,200**に相当することを示す計算。

サーバーは意図的にプロトコルコードを共有するようにリファクタリングされていません。両者の間には意図的に重複があり、それぞれを最初から最後まで読んで差分を取れるようになっています。

ワイヤーを見逃さないでください

両サーバーはすべてのリクエストをHTTPレベルで出力します:メソッド、パス、すべてのMCPヘッダー、JSON-RPCメソッドとパラメータ、どのインスタンスが処理したか、そしてresultTypeを含むレスポンス。SDKの抽象化の背後に隠されているものはありません。実験中に1つだけ読むなら、色付きのログ行を読んでください。

前提条件

  • Node.js 20以降(25.5で開発)。node -vで確認。

  • ANSIカラーを表示するターミナル——ログはそれに大きく依存しています。

  • ポート3000–300230113012が空いていること。

  • データベース不要、Docker不要、クラウドアカウント不要。共有状態はJSONファイルです。

このリポジトリにはPythonは一切ありません。

インストール

git clone <this repo>
cd mcp-server
npm install
npm run typecheck    # should print nothing and exit 0

npm installは、MCP SDKの2世代を同時に含むnpmワークスペースをセットアップします。パッケージ名が異なるため、エイリアスなどのトリックなしで共存できます:

パッケージ

バージョン

使用箇所

@modelcontextprotocol/sdk

1.30.0

server-old、旧クライアント

@modelcontextprotocol/{core,server,client,node}

2.0.0

server-new、新クライアント

4つの実験(順番に)

それぞれ1コマンドです。それぞれが独自のサーバーを起動・停止します——2つ目のターミナルは不要です。実行にリンク先の解説を読んでください。それぞれが、今見たこととその理由を説明します。

順序

コマンド

学べること

1

npm run exp:01

ロードバランサー背後にある2つのインスタンス — 旧サーバーは挨拶すら完了できません。新サーバーは動じません。ここから始めてください。

2

npm run exp:02

会話の途中での再起動 — カートが実際にどこにあったのか、そして「Redisを追加するだけ」が半分しか機能しない理由。

3

npm run exp:03

チェックアウト前の確認 — 1522msの保持ソケット vs 2つの4msリクエスト、そして旧方式がサーバーレスで決して実行できない理由。

4

npm run exp:04

キャッシュヒントと安定した順序付け — ストップウォッチではなくカウンターでキャッシュヒットを証明し、.sort()の金銭的根拠。

次に、4つを結びつけるアーキテクチャドキュメントを読んでください:

  • 01 — MCPが存在する理由 — N×M問題、そしてMCPが何であり何でないか。MCPに不慣れなら最初に読んでください。

  • 02 — 旧アーキテクチャ — ハンドシェイク、セッションID、そしてすべての運用上の問題点をその原因に遡って説明。

  • 03 — 新アーキテクチャ — ハンドル、MRTR、キャッシュヒント、そして失うもの。

  • 04 — 並べて比較 — リリースのすべての変更、このリポジトリでの場所、そしてこのリポジトリがカバーしていないことの正直なリスト。

手動で操作する

少なくとも一度はやる価値があります。ペースを自分で決められ、各ログ行が表示されるたびに読めるからです。

# Old server (port 3001)
npm run old:server
npm run client -- --target old --scenario basic
npm run client -- --target old --scenario checkout
npm run client -- --target old --scenario checkout --decline

# New server (port 3002)
npm run new:server
npm run client -- --target new --scenario basic
npm run client -- --target new --scenario checkout
npm run client -- --target new --scenario discover    # server/discover — new spec only

2つのインスタンスとロードバランサーを手動で:

PORT=3002 INSTANCE_ID=A npm run new:server
PORT=3012 INSTANCE_ID=B npm run new:server
PORT=3000 TARGETS=http://localhost:3002,http://localhost:3012 npm run lb
npm run client -- --target new --url http://localhost:3000/mcp --scenario basic

両方のサーバーターミナルを確認してください:同じカートIDがそれぞれが処理するリクエストに現れ、どちらも気にしません。

curlで試す

新しい仕様のリクエストがどれほど自己記述的かを感じる最も直接的な方法です。npm run new:serverを起動してから:

# A complete, valid request — note how much has to be in it
curl -s -X POST http://localhost:3002/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H 'mcp-protocol-version: 2026-07-28' \
  -H 'mcp-method: tools/call' \
  -H 'mcp-name: catalog_list' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
        "name":"catalog_list","arguments":{},
        "_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
                 "io.modelcontextprotocol/clientInfo":{"name":"curl","version":"1"},
                 "io.modelcontextprotocol/clientCapabilities":{}}}}'

次に、1つずつ壊してエラーがどう変わるかを見てください:

変更

期待される結果

-H 'mcp-method: tools/list'(ボディはtools/callのまま)

-32020 HeaderMismatch

-H 'mcp-name: cart_view'

-32020、不一致を指名

mcp-methodヘッダーを削除

-32020"the required Mcp-Method header is absent"

_metaブロックを削除

-32602、欠落しているエンベロープキーを列挙

mcp-protocol-version: 2099-01-01をヘッダーと_metaの両方に

-32022 Unsupported protocol version

curl http://localhost:3002/mcp(GET)

405 — GETエンドポイントは廃止されました

旧サーバーに対して同じことを行うと、最初にinitializeするように指示されます。

リポジトリ構成

packages/
  cart-core/     the actual product. zero MCP knowledge. shared by both servers.
  server-old/    MCP 2025-11-25. sessions, handshake, held-open streams.
  server-new/    MCP 2026-07-28. stateless, handles, MRTR, cache hints.
  client-demo/   both clients — one per SDK generation.
  round-robin/   ~50-line load balancer. no stickiness, on purpose.
experiments/     four runnable scripts + a write-up each.
docs/            the four architecture notes.
.cart-store/     server-new's shared state. a JSON file. delete it freely.

コードの読み順:cart-core/src/cart.ts(プロダクトが何をするか)→ server-old/src/index.tsserver-new/src/index.ts。両サーバーのプロトコルレベルのコードは行ごとにコメントされています。配管部分はコメントされていません。

npm run cleanはビルド出力と.cart-storeを削除します。


用語集

全体で使用される用語を、問題になる順に説明します。

ロードバランサー — サーバーの同一コピー数台の前に置かれ、受信リクエストをそれらに分散するボックス。デフォルトのポリシーはラウンドロビン:各リクエストをリストの次のコピーに送信します。どのコピーも任意のリクエストに応答できることを前提としており、それがまさに旧MCP仕様が破った前提です。ここではpackages/round-robin、約50行です。

セッション — 複数のリクエストにまたがるクライアントのサーバー側メモリ。2025-11-25では、サーバーはハンドシェイク中にMcp-Session-Idを発行し、クライアントはすべてのリクエストでそれをエコーし、サーバーはそれをインメモリマップのキーとして使用しました。セッションIDは1つのプロセスのヒープへのポインタであり、そこからすべての問題が発生します。

ステートレス — サーバーはリクエスト間に何も保持しません。すべてのリクエストは、サービスを提供するために必要なすべてを運びます。これが意味しないことに注意してください:カートは依然として存在し、依然として保存されています。消えたのは、特定のプロセス内に暗黙的に、接続をキーとして保持されていた状態です。共有データベース内のアプリケーション状態は、ステートレスプロトコルと完全に互換性があります。

スティッキーセッション(セッションアフィニティ) — 1つのクライアントからのすべてのリクエストが同じサーバーコピーに戻るようにロードバランサーを設定すること。通常はCookieまたはヘッダーのハッシュによって行われます。ステートフルプロトコルに対する標準的な回避策です。機能しますが、均等な負荷分散、苦痛のないデプロイ、有用なオートスケーリング、アプリケーションプロトコルを理解する必要のないロードバランサーを犠牲にします。コストのリストはdocs/02にあります。

エリシテーション — サーバーが操作の途中でエンドユーザーに質問すること(「合計は$180.36ですが、確認しますか?」)。旧仕様では、サーバーは保持されたストリーム上でクライアントに独自のリクエストを送信し、人間がそれについて考えている間、ツールハンドラー内でブロックしました。その単一の機能には、ライブプロセス、開いたソケット、同じボックスへのルーティングの保証が必要でした。

MRTR(Multi Round-Trip Requests) — 2026-07-28がエリシテーションを行う方法です。サーバーはresultType: "input_required"inputRequests内の質問、そして不透明な署名付きrequestStateを含む通常の200を返します。そのリクエストは終了です——何も保持されません。クライアントは回答を集め、inputResponsesと同じrequestStateを運ぶ新しいリクエスト(新しいJSON-RPC ID)を送信します。進行中の状態はプロセス内に置かれる代わりにクライアントを通って移動したため、ラウンド2は完全に異なるマシンで処理できます。

ハンドル — サーバーが発行する識別子で、通常のツール出力として返され、通常の引数として渡されます。cart_createcartIdを返し、cart_add_itemはそれを受け取ります。これが2026-07-28がセッション状態を置き換える方法であり、旧設計との違いは誰がキーを保持するかです:トランスポートが目に見えない形で保持するのか、それともクライアントがモデルが読み取って渡すことのできる値として保持するのか。注意点:ハンドルだけではベアラートークンです——認証されたユーザーにスコープする必要があり、docs/04で正直に説明されています。

プロンプトキャッシング — LLMプロバイダーはプロンプトのプレフィックスをキャッシュします:同じ冒頭バイトを再度送信すると、プロバイダーはそれらのトークンを再処理する代わりに計算済みの状態を再利用し、入力価格のおよそ10分の1になります。これを脆弱にする2つの特性:一致は正確なバイトで行われ、先頭からの位置に依存します。したがって、ツールリストやカタログがプレフィックスにあり、2つのエントリが入れ替わると、入れ替わった後のすべてのトークンで割引を失います。そのため2026-07-28はサーバーが決定的な順序でリストを返すべき(SHOULD)と述べており、listProducts()が名前や価格ではなく一意のidでソートする理由でもあります——一意のキーは、ソート実装が異なる方法で解決する同点のない全順序を与えます。価格付きの具体例:実験04


3つだけ覚えるなら

  1. 「ステートレス」は「状態がない」という意味ではなく、「プロセスに固定された状態がない」という意味です。 カートは依然として存在します。どのインスタンスからも到達できる場所に移動しただけです。

  2. スティッキーセッションは実際のコストを伴う実際の修正でした、そしてそのコストの1つは、インフラストラクチャにアプリケーションプロトコルを解析させることでした。

  3. サーバーレスを解放したのは、ステートレス性ではなくMRTRでした。 ステートレス性はMCPをロードバランサーの背後に置きました。エリシテーションは、人間がダイアログを読んでいる間、プロセスが生き続けることを依然として必要としました——そしてそれはまさにサーバーレスが取り除いたものです。

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ritik913553/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server