クライアントとMCPサーバの統合――検証フローをSprint AIで理解する:Spring AIで始める生成AIプログラミング(10)
Java×Spring AIで始めるAIプログラミングの入門連載。前回までは、Spring AIを使ってMCPサーバを構築し、ファイル検索やリソース提供といった機能を実装する方法を解説してきました。今回は、作成したMCPサーバを「実際に使いこなし、システムとして完成させる」ステップへと進みます。
本連載のサンプルコードをGitHubで公開しています。こちらからダウンロードしてみてください。
目次
はじめに――MCPサーバの検証フロー
前回構築したMCP(Model Context Protocol)サーバを、単なるプログラムではなく「AI(人工知能)という新たなユーザー」への機能提供として捉え直します。本稿では、
- 開発者視点の「単体テスト」
- ユーザー視点の「結合テスト」
という2段階で検証を進め、MCPにおける制御手法を体系的に理解していきます。
MCP Inspectorによる「単体テスト」:プロトコルの信頼性を確保する
まず取り組むべきは、AI(LLM:大規模言語モデル)の曖昧さを排除した状態での機能検証です。この目的のために利用するのが、Anthropicが公式に提供しているデバッグツールであるMCP Inspectorです。
Inspectorを利用する最大の狙いは、MCPサーバの挙動をブラックボックス化せず、プロトコルレベルで直接確認することにあります。クライアントを介さずにサーバへリクエストを送り、レスポンス構造やフィールドの内容を確認することで、実装の誤りや仕様の齟齬(そご)を早期に発見できます。
HTTP/SSE接続でサーバの「生」の動きを見る
MCP Inspectorは、Node.js環境があれば事前インストールなしで即座に起動できます。以下のように、起動中のSpring MCPサーバに接続してみましょう。
$ npx @modelcontextprotocol/inspector Need to install the following packages: @modelcontextprotocol/inspector@0.18.0 Ok to proceed? (y)
初期の起動では、パッケージのインストールが求められるので、そのまま[Enter]キーを押して続けます(図1)。
コマンドを実行するとInspector自身のローカルサーバが起動し、ブラウザで「http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=xxxxx」のようなアドレスが自動的に開きます(注)。このアドレスは、Inspector内部のプロキシ機構を利用するためのもので、MCP_PROXY_AUTH_TOKENには認証用のトークンが付与されています(図2)。
注:MCPサーバを起動した設定によりIPやポートは変わります。筆者は別環境「192.168.100.40」で起動したため、このIPアドレスを利用しています。
画面が表示されたら、前回で作成したMCPサーバの起動URL「http://localhost:8080/mcp」を入力して接続設定を行います。ここで特に注意すべきポイントが、「Transport Type」と「Connection Type」です。
Inspectorの初期状態では、
- Transport Type:STDIO(Standard Input/Output)
- Connection Type:Via Proxy
が既定値として設定されています。
Transport Type」は、MCPにおける通信方式(プロトコル)を表します。今回はHTTPベースで実装したSpring MCPサーバに接続するため、ここを「Streamable HTTP」に変更してください。
「Connection Type」は「Via Proxy」のままとします。これは、現在多くのMCPクライアントがSTDIOモデルを前提として設計されており、HTTPサーバと対話する際にはInspectorが内部プロキシとして振る舞い、通信方式の差異を吸収する必要があるためです。接続に失敗する場合は、まずこの2つの設定が正しいかどうかを確認するとよいでしょう。
設定が完了したら、画面左側の[Connect]ボタンをクリックして接続します。
メソッドを実行してみる
画面上部の[Tools]タブを開くと、自分で作成したMCPサーバで公開しているツール一覧を確認できます。初期状態では一覧が表示されないため、まず[List Tools]ボタンをクリックしてください(図3)。
図3のように作成したツール一覧が表示されたら、各ツールを選択し、そのまま実行できます。図4は、find_file_by_name_pattern_syncを選択、実行した場合です。
引数としてpattern(ファイルパターン)を指定した上で、[Run Tool]ボタンをクリックします。図4のように実際に送受信された通信データが表示されることを確認してみましょう。
このように、MCP Inspectorを利用することで、リクエスト内容、レスポンス構造を直接把握できるので、MCPサーバの機能を単体レベルでテスト/検証する際にも有効です。
Chatboxアプリによる「結合テスト」:AIとの連携を確認する
プロトコルやツール定義といった技術的な正しさを確認できたら、次に検証すべきは「AIがその機能を適切に利用できるかどうか」という点です。そこで、汎用(はんよう)的なMCPクライアントであるChatboxを利用します。
汎用クライアントを用いた検証は、特定のアプリケーションに依存しない「ポータビリティ」を確認することにもつながります。さらに、エンジニア以外のメンバーとも共有しやすくなり、チーム全体でAI活用の成果を享受できるようになります。
Chatboxを利用する利点
必ずしもChatbox固有の機能によるものではありませんが、Chatboxを利用することで、次のような利点があります。
- 複数のLLMに対する挙動や特性(癖)の確認が容易
- エンジニア以外のメンバーとも共有しやすい
OpenAIやGoogle Geminiなど、異なる傾向を持つLLMに対して、自作のMCPサーバがどのように機能するかを横断的に確認できます。特にツールの説明文や引数の扱いは、LLMのモデルによって解釈に差が出ることがあるため、モデルごとの検証は重要です。
また、必要な設定情報を共有するだけで、非開発者であっても作成したMCPツールを利用できる点も大きな利点です。これにより、比較的容易に利用を開始できます。
Chatboxのインストールと設定
まずは公式サイトにアクセスし、自分のOSに対応したインストーラをダウンロードしてインストールしてください(図5)。
インストールが終了すると、Chatboxが自動で起動します。[プロバイダーをセットアップ]ボタンをクリックして、普段利用しているAIの設定を行います。例えば図6は、OpenAIのAPIキーを設定する画面例です。
図6の要領でAPIキーなどの情報を登録し、通常のチャットが利用できる状態にします。APIキーの設定が完了したら、続いてMCPサーバの設定を行います。[MCP]タブから[サーバーを追加]ボタンをクリック、[カスタムサーバーを追加]ボタンで、以下のような設定画面が表示されます。図7の要領で、MCPサーバの設定情報を入力してください。
以上で、ChatboxからMCPサーバを利用するための初期設定は完了です。
Chatboxを通じた利用例
では、実際にChatboxからMCPサーバを利用してみましょう。例えば、
業務で管理しているファイルを探してほしいです。
といった、やや抽象的な指示を入力してみてください。
この場合、状況によっては「業務で管理しているファイルとは何を指しますか?」といった確認の応答が返ってくることがあります。これは、AIがツールの意図や対象を十分に特定できていない状態を示しています。このような結果になった場合、ツール名や引数、説明文において、AIが誤解しにくい用語や表現を用意する必要があることが分かります。
一方、指示の内容が適切に解釈されると、図8のようにMCPサーバと連携した応答が行われます。
このように、さまざまな質問を試しながら、
- どのタイミングでツールが呼び出されるか
- どの引数がどのように補完されるか
- 想定外の解釈が発生しないかどうか
といった点を確認していきましょう。これらを繰り返し検証することで、より適切なAPI設計やツールの説明文を見つけていくことができます。
Spring AIでMCPのAPIを直接コールする
Spring AIでは、MCPサーバと対話するための低レイヤーなクライアントAPIとして、非同期用のMcpClientと、同期用のMcpSyncClientが提供されています。
これまで述べてきた通り、MCPサーバには非同期型と同期型の利用形態があるため、どちらのクライアントを使用するかは、処理の性質や利用シーンに応じて判断してください。
これらのクライアントを使用すると、AIの推論を介さずに、
- MCPサーバからツール一覧を取得する
- 特定のツールを引数付きで明示的に実行する
といった操作を、プログラムから直接実行できます。
ここでは、HTTP/SSEで起動しているMCPサーバに接続し、プログラムから明示的に「ファイル検索ツール」を実行するケースを前提に説明します。
[1]依存関係を追加する
まず、ビルドファイル(build.gradle.kts)にMCPクライアント用の依存関係を追加します(リスト1)。
dependencies {
implementation("org.springframework.ai:spring-ai-starter-mcp-client")
}
[2]McpSyncClientでツール一覧を取得する
次に、MCPサーバとの通信を確立し、ツール一覧を取得するコードを記述します(リスト2)。
// (省略)
@Configuration
public class McpClientConfig {
@ShellMethod(key = "mcp-tools")
public Map<String,String> mcpTools(){
// (1) StreamableHTTPとして接続する
var transport = HttpClientStreamableHttpTransport
.builder(MCP_SERVER_URL).build();
var toolMap = new HashMap<String,String>();
// (2) MCP Clientを同期型として作成
try(McpSyncClient mcpClient = McpClient.sync(transport).build()) {
// (3)初期化
mcpClient.initialize();
// (4)認識しているツール一覧を取得
var tools = mcpClient.listTools().tools();
for (var tool : tools) {
// (5) ツールの名前と説明を取得する
toolMap.put(tool.name(), tool.description());
}
}
return toolMap;
}
}
このコードでは、まずHttpClientStreamableHttpTransportを用いて、Streamable HTTPプロトコルと接続先URLを指定した通信手段を構築しています(1)。これにより、HTTP/SSEで起動しているMCPサーバと通信するための前提条件が整います。
次に、この通信手段をもとに同期型のMCPクライアントであるMcpSyncClientを生成します。同期型クライアントを使用することで、ツール一覧の取得結果をその場で受け取ることができます(2)。なお、クライアントは通信リソースを内部で保持するため、try-with-resources構文を用いてスコープ終了時に確実に解放するようにしています。
クライアント生成後、MCPサーバと実際に対話を開始するためには、initializeメソッドによる初期化処理が必要です(3)。この処理はMCPにおけるハンドシェイクに相当し、これを行わない限り、ツール一覧の取得やツール実行といった操作はできません。
初期化が完了したら、後はlistTools().toolsメソッドを呼び出すことで、サーバが公開しているツール定義を一括で取得できます(4)。ここで得られる情報には、各ツールの識別名や説明文が含まれており、MCPサーバがどのような機能を提供しているかをプログラムから把握できます。
最後に、取得したツール定義からname、descriptionを参照することで、個々のツールの役割や用途を確認できます(5)。この情報は、後続の処理で特定のツールを選択・実行する際の判断材料として利用できます。
以上を理解したら、コマンドを実行してみましょう。以下のようにツール一覧が表示されます。
shell:> mcp-tools
{get_file_content_string_by_file_path=file-content://から始まるファイルパスを元に、ファイルのデータ内容を文字列で返します, .... }
[3]ツールを直接実行する
続いて、先ほどと同様にMcpSyncClientを用いてMCPサーバへ接続し、特定のツールを明示的に実行しています。クライアントの生成とinitializeによる初期化処理までは、ツール一覧を取得する場合と同じ手順です(リスト3)。
@Service
@ShellMethod(key = "mcp-tool-find-files")
public String mapToolFindFiles(String pattern){
// (省略)
try(McpSyncClient mcpClient = McpClient.sync(transport).build()) {
// 初期化までは同じ
mcpClient.initialize();
// (1) ツールを実行するリクエストを作成する
var request = new McpSchema.CallToolRequest(
// (2) 実行したいMCP上のツール名
"find_file_by_name_pattern_sync",
// (3) ツールへの引数を設定する
Map.of("pattern", pattern)
);
// (4) ツールの実行
var response = mcpClient.callTool(request);
// (5) ツールの結果を取得(このメソッドは戻りデータが必ず1つとMCPサーバ側で定義されている)
var content = response.content().get(0);
// (6) ツールの結果
if(content instanceof McpSchema.TextContent textContent){
String jsonText = textContent.text();
// JSONテキストの結果を解析して、自分達が利用しやすいようにする
return jsonText;
}
}
return "Error";
}
ツールを実行するには、まずCallToolRequestを作成します(1)。このリクエストには、MCPサーバ側で定義されている正確なツール名(2)と、そのツールが必要とする引数(3)を指定します。この例であれば、find_file_by_name_pattern_syncというツール名を指定し、検索条件としてファイル名のパターンをMap形式で渡しています。ツール名や引数名がサーバ側の定義と一致していない場合、正しく実行されないので要注意です。
作成したリクエストをcallToolメソッドに渡すことで、クライアントからMCPサーバへツールの実行命令が送信され、処理結果がレスポンスとして返ってきます(4)。MCPの仕様上、ツールの実行結果はリスト形式で返されますが、ここでは戻り値が1つであることを前提としているため、先頭要素を取得しています(5)。
取得した結果はContent型です。ここではTextContentであることを確認した上で、その中身を取り出しています(6)。textメソッドには、MCPサーバ側で生成されたJavaオブジェクトがJSON形式でシリアライズされた文字列として格納されています。そのため、このJSONテキストを解析することで、検索結果を自分たちのアプリケーションに安全に組み込むことができます。
以上を理解したら、コマンドを実行してみましょう。指定したパターンに一致するファイルパス一覧が、次のように取得できることを確認できます。
shell:> mcp-tool-find-files **/*.java ["file-content://src/main/java/jp/enbind/mcp/MainApplication.java",....]
MCPのAPIを直接実行する理由
MCPのAPIは、前々回でも紹介したようにChatClient上からAIに自動的に利用させることもできますし、そもそもChatboxのような汎用ツールを通じて利用する形が、最も一般的に想定されている使い方です。
一方で、MCPのAPIをプログラムから直接実行できるという点は、単なる補助的な機能ではなく、設計上の大きな可能性を持っています。例えば次のような応用が可能になります。
(1)決定論的な処理の統合
AIのハルシネーションに影響されたくない前処理や後処理を、MCPツールとして既存の業務ロジックに組み込み、安定した処理フローを構築できます。
(2)フォールバック処理の実装
AIがツール呼び出しに失敗した場合でも、プログラム側で代替ロジックを実行し、必要なコンテキストを補うといった高度なエラーハンドリングが可能になります。
(3)リソースの事前読み込み
McpResourceを用いて最新の設定情報やマスターデータをあらかじめ取得し、それをシステムプロンプトに埋め込むといった、柔軟な連携も実現できます。
このように設計すれば、AI側には「どのAPIを呼ぶべきか」という判断のみを任せ、その後の処理は明確で堅牢(けんろう)なロジックとしてプログラム側で制御することができます。極論、AIには実体を持たない空のMCPツールだけを提示し、その呼び出し意図をトリガーとして、実際の処理は全てアプリケーション側で直接実行するといった構成も考えられます。
AIに任せることこそがMCPの魅力である一方で、あえてプログラムから直接APIを呼び出せるという点にこそ、システム全体を堅牢に保つための重要なメリットがあるのです。
MCPがJavaエコシステムにもたらす未来
過去2回と今回を通じて、Spring AIを用いたMCPサーバの構築から、検証、そしてアプリケーションへの統合までを一通り見てきました。
これまで「AIとの連携」と言えば、特定のAPIを個別に呼び出すコードを都度書いたり、複雑なプロンプトエンジニアリングによってAIを誘導したりする方法が主流でした。しかし、MCPという共通プロトコルの登場によって、「Javaで書かれた堅牢なロジック」を、そのまま「AIが自律的に利用できる標準的な道具」へと変換できるようになりました。
MCPは、Javaが長年培ってきた設計文化とも非常に相性が良いプロトコルです。疎結合なコンポーネント設計や明確な責務分離といった考え方は、そのままAIが利用可能なツール定義へと落とし込めます。既存の業務ロジックをMCPで包むだけで、それらは即座に「AI時代の資産」として再利用可能になるでしょう。
まとめ
Javaエンジニアにとって、Spring AIとMCPを活用することは、これまで培ってきたスキルセットをそのまま生かしながら、自律的なAIアプリケーションを構築するための強力な武器になります。
次回は本連載の最終回として、これまで触れられなかったトピック、例えば複数のAIベンダーを同時に利用する場合の考え方や、実装時に直面しがちな小さな困り事の解決方法について解説します。
筆者紹介
WINGSプロジェクト
有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティー(代表山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手掛ける。2021年10月時点での登録メンバーは55人で、現在も執筆メンバーを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。
・サーバーサイド技術の学び舎 - WINGS(https://wings.msn.to/)
・RSS(https://wings.msn.to/contents/rss.php)
・X: @WingsPro_info(https://x.com/WingsPro_info)
・Facebook(https://www.facebook.com/WINGSProject)
Copyright © ITmedia, Inc. All Rights Reserved.






