MCPとは何か、そしてJava開発者が気にすべき理由

Dev.to / 2026/4/23

💬 オピニオンDeveloper Stack & InfrastructureIdeas & Deep AnalysisTools & Practical Usage

要点

  • 記事は、MCP(Model Context Protocol)を、AIエージェントが外部ツールを発見して呼び出すための標準的なHTTPベースの仕組みとして説明し、オーダーメイドのHTTPクライアントのような統合作業を不要にすることを狙っています。
  • MCPは「AIのUSB」のようなもので、エージェントは任意のMCPサーバーに接続して、そのサーバーが提供するツールをサービス固有の“つなぎ込み”コードなしで利用できる、と述べています。
  • Java開発者がマイクロサービス環境で直面する課題(複数のデータベースやサービスにまたがるデータ照会)に対して、MCPがどのように有効かを整理しており、従来はクライアントやDTO、エラーハンドリングが重複して密結合になりがちだと説明しています。
  • JSON-RPCをHTTPで扱い、接続のライフサイクルにSSEを用いる点を通じて、発見(initialize + tools/list)、呼び出し(tools/call)、そしてツール呼び出し間のサーバー側ステートレス性(セッションはSSE接続の期間のみ)を具体的に示しています。
  • 著者が複数のMCPサーバーを本番のマイクロサービス環境で運用していることを挙げ、Java目線かつ実運用志向のコード提示を予告しています。

MCPとは何か、そしてJava開発者が気にすべき理由

あらゆるAIチュートリアルでは、チャットボットの作り方が紹介されています。システムプロンプトを渡して、LLMに接続して、返事をしてもらう。けれど、在庫を確認したり、支払い状況を問い合わせたりといった「実際のこと」をAIにやらせる必要が出た瞬間、独自のHTTPクライアントを書いて、つぎはぎのコードを組み上げることになります。

MCP(Model Context Protocol)がそれを解決します。これは、AIエージェントがHTTP経由で外部ツールを発見して呼び出せるようにする標準プロトコルです。USB for AIだと思ってください。1つ挿せば、どんなデバイスでも使える。どんなエージェントでも、どんなMCPサーバーに接続しても、独自の統合コードなしでそのツールを利用できます。

この連載は、Java開発者の視点からMCPを扱います。理論ではありません。私はマイクロサービス環境で、本番稼働しているMCPサーバーを4つ運用しており、実際のコードをお見せします。

MCPが解決する問題

Kafkaを使って、5つのマイクロサービスをサーガパターンで連携させています。私は、それらすべてにまたがるデータを問い合わせる必要があるAIエージェントを作りました。MongoDBから注文の詳細、PostgreSQLから支払い状況、別のPostgreSQLから在庫レベル、さらに4つ目のデータベースから製品カタログです。

MCPがなければ、選択肢は良くありません。各サービス向けにHTTPクライアントを書けます。クライアントが4つ、DTOのセットが4つ、エラーハンドリング戦略が4つ。サービスが新しい問い合わせを追加するたびに、そのクライアントを更新する必要があります。エージェントと、通信するすべてのサービスとの間に強い結合が生まれます。

あるいは、LangChain4jの@Toolアノテーションを使うこともできます。ですが@Toolは、同じJVM内のメソッドでしか動きません。私のエージェントは別のサービスに存在しています。在庫データは別にあります。

MCPの仕組み

MCPは、接続ライフサイクルにServer-Sent Events(SSE)を用い、HTTP上でJSON-RPCを使います。フローには3つのフェーズがあります。

発見(Discovery)。 クライアントがサーバーのSSEエンドポイントに接続します。initializeリクエストと、tools/listリクエストを送信します。サーバーは、利用可能なツールをすべて返します。ツール名、説明、パラメータのスキーマを含みます。

呼び出し(Invocation)。 エージェントがデータを必要とすると判断したとき、LLMがツール呼び出しを生成します。MCPクライアントは、ツール名と引数を一緒にしてtools/callリクエストを送ります。サーバーがツールを実行し、結果を返します。

ステートレス(Statelessness)。 各ツール呼び出しは独立しています。サーバーは呼び出し間で状態を保持しません。セッションが存在するのは、SSE接続のライフサイクル中だけです。

具体的には、次のように見えます:

Agent → SSE connect to http://localhost:8092/sse → gets sessionId
Agent → POST /mcp/message?sessionId=xxx → {"method": "initialize", ...}
Agent → POST /mcp/message?sessionId=xxx → {"method": "tools/list", ...}
Server responds → ["getStockByProduct", "getLowStockAlert", "checkReservationExists"]
Agent → POST /mcp/message?sessionId=xxx → {"method": "tools/call", "params": {"name": "getStockByProduct", "arguments": {"productCode": "COMIC_BOOKS"}}}
Server responds → {"available": 600}

クライアント側にSDKは不要です。これはJSONボディを伴うHTTPリクエストです。curlでテストすることもできます。

MCPとREST APIの違い

目を細めれば、MCPは追加のステップがあるREST APIのように見えます。どちらもHTTP経由で機能を公開しています。どちらもJSONを使います。では、RESTのエンドポイントを直接呼べばいいのでは?

理由は3つあります。

ツールの発見(Tool discovery)。 REST APIはドキュメントが必要です。誰かがSwaggerの仕様を読み、クライアントを書きます。MCPサーバーは実行時に自分の機能を宣伝します。エージェントが接続すると、利用可能なツール、受け取るパラメータ、そしてそれをいつ使うべきか(descriptionフィールド経由)を即座に理解できます。

LLM統合(LLM integration)。 LangChain4jは、MCPのツールスキーマをLLMが理解できるfunctionDeclarationオブジェクトに変換します。LLMはツールの説明を読み、それを使うタイミングを判断し、正しい引数を生成します。ルーティングのロジックを書く必要はありません。

疎結合(Decoupling)。 REST APIに新しいツールを追加するには、クライアントを更新する必要があります。MCPサーバーに新しいツールを追加する場合、次回の接続時にエージェントがそれを見つけます。エージェント側での変更はゼロです。

詳細:プロトコル

MCPは3つのJSON-RPCメソッドを使います。

initializeは接続を確立し、機能をネゴシエーションします:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
     "protocolVersion": "2024-11-05",
     "clientInfo": { "name": "my-agent",
     "version": "1.0.0" },
     "capabilities": {}  }
}

tools/listは利用可能なすべてのツールを返します:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}

tools/callは特定のツールを呼び出します:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "getStockByProduct",
    "arguments": { "productCode": "COMIC_BOOKS" }
  }
}
}

レスポンスは常に、contentTextContent オブジェクトの配列である CallToolResult として返ってきます。シンプルで予測しやすいです。

Java のエコシステム

Java では、MCP を扱う 2 つのライブラリがあります。

サーバー側: io.modelcontextprotocol.sdk:mcp:1.1.0。これは公式の MCP Java SDK です。transport、tools、server info を使って McpSyncServer を作成します。JSON-RPC プロトコルと SSE のライフサイクルを処理します。

クライアント側: LangChain4j の langchain4j-mcp モジュールです。各サーバーの SSE URL を指す McpClient インスタンスを作成します。次に、それらを McpToolProvider でラップして、エージェントビルダーに渡します。

私の経験では、どちらも安定していて、本番投入に対応しています。サーバー SDK は軽量です(依存関係が 1 つだけ)。クライアント側は LangChain4j が担当するので、すでにエージェントのためにそれを使っているなら、追加でインストールするものはありません。

次に何をするか

次回の記事では、Spring Boot のマイクロサービスを MCP サーバーにする手順を、ステップごとに説明します。payment-service からの実際のコードです。ツールの定義、transport のセットアップ、そして AI エージェントに接続する前に curl でテストするところまで示します。

リポジトリ:github.com/pedrop3/saga-orchestration