Java×Spring AIで始めるAIプログラミングの入門連載。前回まではRAG(検索拡張生成)を利用したAIシステムの構築について説明してきました。今回からは、LLM内部だけでは対応できない外部機能を呼び出す手法として、Spring AIのTool CallingとMCPについて解説します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
本連載のサンプルコードをGitHubで公開しています。こちらからダウンロードしてみてください。
外部機能連携(Function CallingまたはTool Calling)とは、LLM(大規模言語モデル)の知識ベースや推論能力だけでは対応できない処理を、外部システムと接続することで実装する仕組みです。呼称はベンダーによって異なりますが、本稿ではSpring AIの公式ドキュメントに準じて「Tool Calling」という用語に統一して説明します。
Tool Callingを利用することで、例えばリアルタイム情報の取得(天気、為替など)や外部システムの制御(アラーム設定、通知送信など)が可能になります。Tool Callingは、一見してRAG(Retrieval-Augmented Generation)と似ている点もあります。どちらもLLMの知識を拡張するという意味では「LLM部と連携する」アプローチですが、その性質と動作フェーズが異なります。
RAGとTool Callingの違いは、AIでの質問〜回答のプロセスに目を向けると理解できます(図1)。
RAGは、主に「プロンプト構築」段階で機能します。ユーザーの質問に対して、関連ドキュメントを検索し、それらをプロンプトに統合することで、LLMがより適切な文脈を基に回答を生成できるようにします。一方、Tool Callingは「推論フェーズ(LLMが思考中)」で作用します。LLMは回答を生成する中で必要に応じてツールを呼び出し、その結果を自らの出力に取り入れます。
では早速、サンプルコードを作成しながら、Tool Callingの基本を理解していきます。
[1]今日の日付から天気を取得するメソッドを作成する
Spring AIで、Tool Callingのためのメソッドを定義するのは簡単です。例えば「今日の天気は」という質問は、「今日とは具体的に何日か」と「指定された日の天気は」という2つの質問に分けられると考えられます。そこで、この2つの質問に答えるメソッドをリスト1のように作っていきます。
// 省略
import org.springframework.ai.tool.annotation.Tool;
public class SimpleTools {
// 省略
// (1) @Tool でどのような時に実行されるかを説明
@Tool(description = "現在の日付を取得します。今日や現在時刻などの情報を求められたときに値を提供します")
public String getCurrentDate(){
String date = java.time.LocalDate.now().toString();
return date;
}
// (2) 引数も使ってメソッドを作成する
@Tool(description = "与えられた日付(YYYY-MM-DD形式)から、天気情報を提供します")
public String getWeatherInfo(String date){
// 実際には、ここで天気APIなどを実行して取得する。そのためには位置情報も必要ですが、ここでは省略します
return "晴れ";
}
// 省略
}
(1)が、「今日とは具体的に何日か」という情報を取得するためのメソッドです。メソッドそのものは通常の構文で表せますが、@Toolアノテーションを付与している点がポイントです。@Toolは、AIモデルに対して、このツール(メソッド)の機能を伝えます。
続いて、(2)が「指定された日の天気」を提供するメソッドです。このメソッドは、AIモデルが質問を解析し、必要な引数(日付)を自動で設定して呼び出します。ここでは固定の文字列を返していますが、実際の利用シーンでは、外部APIを実行して動的な情報を取得するような処理が入ることになるでしょう。
[2]作成したメソッドを使えるようにする
続いて、[1]で登録したメソッドが動くように登録していきましょう(リスト2)。
@ShellComponent
public class ToolCommand {
// 省略
public ToolCommand(ApplicationContext context) {
this.context = context;
client = ChatClient.builder(context.getBean(ChatModel.class))
// (1)作成したツールを登録する
.defaultTools(new SimpleTools())
.build();
}
}
ChatClientのインスタンスを作成する際、(1)のdefaultToolsメソッドに、先ほど@Toolアノテーションを付与したメソッドを持つSimpleToolsクラスのインスタンスを登録します。この設定を行うだけで、ChatClientを通じたAPI利用時にツールが自動的に考慮されるようになります。
[3]サンプルを実行する
サンプルコードを実行すると、以下のような結果が得られます。
shell:>tool-query 今日の天気は 今日の天気は晴れです。 shell:>tool-query 明日の天気は 明日の天気は晴れです。 shell:>tool-query 明日は何日でその天気は 明日は2025年11月15日で、天気は晴れです。
ここで興味深いのが、「明日」と入力した場合のLLMの動作です。LLMはまず、質問に答えるためにgetCurrentDateメソッドの実行が必要であると判断し、実行を指示します。
次に、その結果(今日の日付)と、元の質問(プロンプト)に含まれる「明日」という情報から、明日を2025年11月15日(実際には今日の日付によって変わります)と特定します。そして、その日付を引数としてgetWeatherInfoメソッドを実行するわけです。
このような、LLMの自律的な判断と、それに基づいた処理(ツール実行)の連携こそが、Tool Callingのメリットです。
Tool Callingの概念をさらに発展させ、AIと外部システムの連携を標準化/拡張した仕組みとしてMCP(Model Context Protocol)があります(図2)。MCPは、現在のAIシステムにおけるトレンドの一つともいえる比較的新しい概念です。これまでのローカルなツール呼び出し(Tool Calling)から、ネットワーク経由での外部サービスの機能連携を可能にし、より拡張性の高いAIシステム構築を支援します。
このような機能拡張の違いは、技術的な側面だけでなく、市場(マーケット)や利用者から見たニーズにも変化をもたらす可能性があります。例えば、これまで(図3左)は、開発者がAIシステム全体を理解し、その中でツールを呼び出すシステムを構築する必要がありました。ビジネス面では、Tool Callingを実行するシステム提供者がAIのAPI利用料金も支払う必要がありました。
しかし、MCPとしてAI側に外部サービスの機能を提供するだけなら(図3右)、開発者はAIシステムの専門知識がほとんど不要になります。また、外部サービス利用料金はAIを使う利用者が支払うケースが想定されるため、システム提供者側はコストをあまり気にせず、AIと外部システムの統合を実現できるようになります。
MCPサーバにはHTTPプロトコルを使うタイプと、コマンド起動のタイプがあります。ここではTool Callingとの類似性を理解しやすいコマンド起動型(STDIO)のMCPサーバについて説明します。
[1]依存関係を設定する
MCPサーバは従来のプログラムとは別プログラムとなるため、プロジェクトを分けて説明しています。まず、必要な依存関係の設定はリスト3のようになります。
dependencies {
implementation("org.springframework.ai:spring-ai-starter-mcp-server")
// Web提供はしないが、依存解決のために必要
implementation("org.springframework.boot:spring-boot-starter-web")
}
MCPサーバの機能自体はorg.springframework.ai:spring-ai-starter-mcp-serverで提供されますが、spring-boot-starter-webまたはspring-boot-starter-web-fluxがその依存解決のために必要になります。これらの依存関係の違いについては次回で説明します。
[2]アプリの基本情報を定義する
続いて、application.propertiesにリスト4のような設定を追加します。
spring.main.web-application-type=none // (1) Webアプリケーションとして起動しない spring.main.banner-mode=off // (2) ロゴを非表示 spring.ai.mcp.server.stdio=true // (3) STDIO(コマンド型)として機能を提供 spring.ai.mcp.server.name=my-mcp-server // (4) MCPとして好きな名前 spring.ai.mcp.server.version=0.0.1 // (5) バージョン番号
(2)は、起動時のSpring Bootのロゴなどの不要なログ表示を抑制するための設定です。これは、MCP機能がSTDOUT(標準出力)を使うため、機能に関係ないログ出力が混ざらないようにするためです。ただし、完全にログ出力を消してしまうとデバッグ時に分かりにくいため、サンプルコード内のログ出力設定(logback.xmlなど)も参考にしてください。
(4)(5)は、MCPサーバのメタ情報です。この情報(サーバの名前とバージョン)は、MCPを利用するクライアント側に提供される管理情報となります。
[3]機能を実装する
MCPで提供する機能は、Tool Callingで利用したコードと同じように表せます(リスト5)。
import org.springframework.ai.tool.annotation.Tool;
// (省略)
public class SimpleService {
@Tool(description = "現在の日付を取得します。今日や現在時刻などの情報を求められたときに値を提供します")
public String getCurrentDate(){
String date = java.time.LocalDate.now().toString();
return date;
}
@Tool(description = "与えられた日付(YYYY-MM-DD形式)から、天気情報を提供します")
public String getWeatherInfo(String date){
// ここでは、Tool-Callingと答えが必ず異なるように意図的に変えています
return "曇り";
}
}
このことからも、Tool CallingとMCPは非常に似ているということが分かります。
[4]提供する機能を公開する
[3]で作成したSimpleServiceクラスを、MCPサーバ上で動作する機能として提供します(リスト5)。
@Configuration
public class McpConfiguration {
@Bean
// (1) Beanとして公開する
public ToolCallbackProvider simpleTools() {
return MethodToolCallbackProvider.builder()
// (2) [2]で作成したクラスを登録
.toolObjects(new SimpleService())
.build();
}
}
(1)のように、@Beanアノテーションを付与してToolCallbackProvider型で定義することで、このメソッドは自動的にMCPサーバの公開機能として提供されます。公開したい機能を含むクラスは、(2)のようにMethodToolCallbackProviderを利用して、SimpleServiceのインスタンスをtoolObjectsに登録するだけでOKです。
Spring AIで構築したMCPサーバ機能を、これまでのChatClientから利用できるように設定します。MCPクライアント機能を使うことで、MCPサーバが提供する外部機能を、チャットプログラムの中から透過的に呼び出せるようになります。
[1]依存関係を追加する
まず、ビルドファイル(build.gradle.kts)にMCPクライアント用の依存関係を追加します(リスト6)。
dependencies {
implementation("org.springframework.ai:spring-ai-starter-mcp-client")
}
この依存関係を追加することで、MCPサーバと通信するためのクライアント機能が利用可能になります。
[2]MCPサーバを登録する
続いて、MCPクライアント機能を有効にするとともに、MCPサーバ情報を登録するための設定を追加します(リスト7)。
spring.ai.mcp.client.enabled=true // (1) spring.ai.mcp.client.stdio.servers-configuration=classpath:mcp-server.json // (2)
(1)のenabledにtrueを設定することでSpring BootがMCPクライアントとして必要な環境を自動的に設定します。そして、(2)でMCPサーバに関する情報を指定します。実際のMCPサーバの情報は、リスト8の通りです。
{
"mcpServers": {
"filesystem": {
"command": "java",
"args": [
"-jar",
"<path_to_mcp_jar_file>/mcp-0.0.1.jar"
]
}
}
}
この設定は、チャットプログラムの起動時に、以下のコマンドを実行してMCPサーバを起動することを意味します。<path_to_mcp_jar_file>には、MCPサーバプロジェクトでビルドしたJARファイル(mcp-0.0.1.jar)の絶対パスまたは相対パスを指定してください。
java -jar <path_to_mcp_jar_file>/mcp-0.0.1.jar
[3]ChatClientから使えるようにする
Tool Callingと同じようにMCPサーバの機能を利用できるようにするには、ChatClientインスタンスを作成する際に明示的に登録する必要があります(リスト9)。
@ShellComponent
public class McpCommand {
// (省略)
// (1) ToolCallbackProviderは、Spring AI側で自動的に作成される
public McpCommand(ApplicationContext context, ToolCallbackProvider tools) {
this.context = context;
client = ChatClient
.builder(context.getBean(ChatModel.class))
// (2) MCPで使える機能を登録する
.defaultToolCallbacks(tools)
.build();
}
// (省略)
}
(1)のコンストラクタ引数(ToolCallbackProvider)は、MCPクライアントが、設定ファイル(mcp-server.json)に基づき、外部のMCPサーバから取得したツール群を自動的に生成し、SpringのDIコンテナに登録します。自動生成されたToolCallbackProviderを、(2)のようにdefaultToolCallbacksメソッドに登録すれば設定は完了です。ここで作成したChatClientを通じてプロンプトを実行すると、AIモデルが必要だと判断した場合に自動的にMCPサーバの機能が利用されます。
[4]サンプルを実行する
実際にサンプルを実行してみましょう。チャットコマンドなどからChatClientを使ってメッセージを送ると、AIモデルによる処理の途中で、必要に応じて自動的にMCPサーバ上の機能にリクエストが転送されます。そして、その機能から返された最新の情報や処理結果を受け取って回答が生成されます。
shell:>tool-query 明日は何日でその天気は 明日は2025年11月15日で、天気は曇りです
このように、内部の通信プロトコルや実行環境といった細かい部分は異なりますが、Tool CallingでもMCPでも、実際に面倒な連携処理はSpring AIが肩代わりしてくれるため、開発者はほとんど同じ方法で外部機能を利用することができます。
現在はまだ技術的にもハードルが高い部分があるものの、将来的にはAIブラウザ(AIエージェント)の普及に伴い、MCPはITインフラとして標準化される可能性があります。これにより、AI利用コストもネットワーク費用のような企業インフラ費用へと移行し、AIが本格的に企業レベルで普及するための下地が整っていくことになるでしょう。
今回は、Tool CallingからMCPまでの仕組みや流れを把握しました。このため開発環境の構築など細かい部分を省略しましたが、次回は開発環境の構築を含めてMCPサーバをより深く掘り下げていきます。
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.