AIの活用では、MCPという言葉が注目を浴びています。連載の最後に、JavaでMCPサーバー/
なお、この記事で取り扱ったコードはこちらからダウンロードできます。
MCPとは
MCP(Model Context Protocol)は、Function Calling(Tool Use)をJSON-RCPに基づいてリモートで呼び出す仕組みです。
Function Callingの場合は機能の実装をチャットプログラムと一緒に行う必要がありました。MCPではチャットプログラムと機能の実装を分離できるため、チャットプログラムにあとから機能を追加できるようになります。つまり、LM StudioやChatGPTのチャットアプリケーションに様々な機能を登録して利用できるわけです。また、さまざまなアプリケーションやサービスがMCPの規格に対応したため、チャットからいろいろな機能やサービスが呼び出せるようになりました。
MCPは、機能を実装するMCPサーバーと、MCPサーバーを呼び出すLLMアプリケーションであるMCPクライアントにわかれます。また、MCPの呼び出しにはサブプロセスとして起動したサーバーに標準入出力でやりとりを行うstdioと、HTTPによるやりとりを行うSSE(Server-Sent Event)があります。
JavaでMCPサーバーを実装する場合、起動時間の問題があるためstdioで呼び出す用途よりもサーバーを起動したままにしてSSEで呼び出すことのほうが多いと考えられます。そこで今回は、Spring Bootを使ったSSE型のMCPサーバーの実装と、LangChain4jを使ったMCPクライアントの実装を行います。
Spring AIによるMCPサーバーの実装
Spring BootでMCPサーバーを実装するには、Spring AIを使います。
プロジェクト作成
まずはSpring Bootのプロジェクトを作成します。
Spring Initializrで、Spring WebとModel Context Protocol Serverを含んだプロジェクトを作成します。Artifactは、ここではdemomcpとしています。

次のようなdependencyが追加されます。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
このようにすると、MCPサーバーがSSE型とstdio型の両方に対応します。
SSE型が不要でstdio型のMCPサーバーを作る場合にはSpring Webは不要です。Spring Webを含めずModel Context Protocol Serverを含めた場合、dependencyとしてspring-ai-starter-mcp-server
が追加されます。
ツールサービスの作成
MCPサーバーの機能実装になるツールサービスを作成します。
package com.example.demomcp;
import java.util.List;
import java.util.Random;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
@Service
public class WeatherService {
private final Random random = new Random();
@Tool(description = "与えられた場所の天気の情報を取得します。")
public String getWeather(String place) {
var wethers = List.of("晴れ","晴れ", "雨", "曇り","曇り", "雪");
return wethers.get(random.nextInt(wethers.size()));
}
@Tool(description = "現在時刻を返します")
public String getTime() {
return LocalDateTime.now().toString();
}
}
このクラスは、基本的に前回のWeatherServiceクラスと同じです。LangChain4jでのFunction Calling(Tool Use)との違いは、@Service
アノテーションがクラスについていることと、@Tool
アノテーションのパッケージがorg.
で、説明にdescription
の指定が必要になっていることです。
ツールの登録
サーバーが公開するツールとして登録します。
package com.example.demomcp;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemomcpApplication {
public static void main(String[] args) {
SpringApplication.run(DemomcpApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}
追加したのは、次のweatherTools
メソッドです。
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
戻り値をToolCallbackProvider
として、ここではMethodToolCallbackProvider
を構築して返します。toolObjects
の登録には、メソッド引数として受け取ったオブジェクトを渡すようにします。引数には、Spring Frameworkが管理するWeatherService
のオブジェクトがインジェクトされて渡されます。
起動
起動は通常のSpring Bootアプリケーションと同様です。mvn
コマンドを使う場合はspring-boot:run
を指定します。
> mvn spring-boot:run
起動ログのうち、McpServerAutoConfiguration
の部分がMCPサーバーに関するログです。

このうち、Registered tools
が2になっていることを確認してください。
LM Studioから使う
それでは、このMCPサーバーをLM Studioから呼び出してみます。この場合、LM StudioがMCPクライアントということになります。
設定
画面右上のフラスコのアイコンをクリックすると、設定が開きます。

「Program」

MCPでは任意のコードが実行できてしまうので、信用できないMCPサーバーを追加するなという警告が表示されますが、

mcp.
{
"mcpServers": {
"spring-ai-weather-sse": {
"url": "http://localhost:8080/sse"
}
}
}
spring-ai-weather-sse
が今回のMCPサーバーの登録名ということになります。他の名前でも構いません。SSE型の場合はurl
を指定します。Spring BootでのMCPサーバーのパスは、sse
になります。
入力したら

上手く登録できると、MCPサーバーとして表示されます。

なんらか問題があればエラーになります。Spring Bootアプリが起動できているか、URLやポートは正しいか確認してください。

使ってみる
それでは使ってみましょう。
入力欄のプラグのアイコンをクリックすると、ツール設定が表示されます。ここで

「東京の天気は?」

すると、MCPでのツール呼び出しを行うかを確認されます。

ここで
「Proceed」

また、いま何月かを聞くと、getTimeの結果から

実際の返答は2025-07-05T03:44:35.
ですが、ここから月だけを答えています。
stdioモードでの登録
Spring AIでSSE型のMCPサーバーを実装する場合、自動的にstdio型にも対応可能になります。
stdioモードで登録する場合は、url
の代わりにcommand
とargs
を指定します。
{
"mcpServers": {
"spring-ai-mcp-weather-stdio": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dlogging.pattern.console=",
"-Dspring.main.bannerMode=off",
"-jar",
"<projectdir>/target/demomcp-0.0.1-SNAPSHOT.jar"
]
}
}
}
<projectdir>
にはプロジェクトフォルダを指定します。ここで、Windowsの場合はフォルダの区切りが\\
になることに注意してください。例えば"C:\\demomcp\\target\\demomcp-0.
のようになります。
また、3つの環境変数を指定していますが、これはapplication.
に設定しても構いません。ただ、SSEとの共存を考えると環境変数での指定のほうがいいでしょう。
今回詳しく説明はしませんが、Claude DesktopではSSE型に対応しておらずstdio型で登録する必要があります。登録は、claude_
を編集して上記のように設定してください。
設定がうまくいくと、次のようにプロンプト入力のツール設定にMCPサーバーが表示されます。

次のように、ツールの呼び出しができます。

Claude DesktopでSSE型のMCPサーバーに接続したい場合には、mcp-remoteというnpmパッケージを使えばいいようです。
LangChain4jでのMCPクライアントからの接続
MCPサーバーが実装できてLM Studioからの接続もできました。そこで、LangChain4jを使ったMCPクライアントから接続を試してみましょう。
詳しくは次のドキュメントを参照してください。
Mavenプロジェクトのdependenciesには次のようにlangchain4j-mcp
を加えてください。執筆時点ではbeta8でした。
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-mcp</artifactId>
<version>1.2.0-beta8</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-http-client-jdk</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
第2回時点でのLangChain4jのバージョンは1.
MCPサーバーを呼び出すコードは次のようになります。
import dev.langchain4j.http.client.jdk.JdkHttpClient;
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.tool.ToolProvider;
import java.net.http.HttpClient;
public class McpClinet {
interface Assistant {
String chat(String userMessage);
}
public static void main(String[] args) throws Exception {
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:8080/sse")
.build();
McpClient client = new DefaultMcpClient.Builder()
.transport(transport)
.build();
ToolProvider provider = McpToolProvider.builder()
.mcpClients(client)
.build();
String modelName =
"qwen/qwen3-1.7b";
ChatModel model = OpenAiChatModel.builder()
.baseUrl("http://localhost:1234/v1")
.modelName(modelName)
.httpClientBuilder(JdkHttpClient.builder().httpClientBuilder(
HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)))
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.toolProvider(provider)
.build();
String res = assistant.chat("""
/no_think
東京の天気は?いま何時?""");
System.out.println(res);
client.close();
}
}
次のように、getWeather
とgetTime
が呼び出されて返答が作られています。
<think> </think> 東京の天気は「雨」です。現在の時間は2025年7月8日 14時40分頃です。
それではコードを見てみます。基本的にはAiServiceを使うコードになりますが、toolProvider
を指定するところが前回のサンプルと違うところです。
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.toolProvider(provider)
.build();
ToolProvider
を得るには、まずMCPに接続するトランスポートをMcpTransport
として指定します。SSE型のMCPサーバーに接続する場合にはHttpMcpTransport
を使ってsseUrl
を指定します。
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:8080/sse")
.build();
stdio型のMCPサーバーに接続する場合にはStdioMcpTransport
を使って、command
にコマンドを指定します。
McpTransport transport = new StdioMcpTransport.Builder()
.command(List.of(
"java",
"-Dspring.ai.mcp.server.stdio=true",
"-Dlogging.pattern.console=",
"-Dspring.main.bannerMode=off",
"-jar",
"<projectdir>/target/demomcp-0.0.1-SNAPSHOT.jar"
))
.build();
あとはtransport
として先ほどのMcpTransport
を指定したMcpClient
を作成し、そのMcpClient
を指定したToolProvider
を作成してAiServices
のビルダーに渡します。
McpClient client = new DefaultMcpClient.Builder()
.transport(transport)
.build();
ToolProvider provider = McpToolProvider.builder()
.mcpClients(client)
.build();
このコードからも、MCPがFunctionCallingのリモート版ということがわかります。McpClient
はAutoClosable
になっているので、try-with-resource構文を使うほうがいいのですが、今までのサンプルとの対比のためにclose
を明示的に呼び出す形にしています。
今回は、次のように天気と時刻を同時に尋ねているため、getWeather
とgetTime
が両方呼び出されて
String res = assistant.chat("""
/no_think
東京の天気は?いま何時?""");
終わりに
今回はMCPを使ってLLMから呼び出される機能の実装、LLMからMCPを使った機能を呼び出す処理の実装を行いました。これで、LLMを使うJavaプログラムは一通り実装できるのではないかと思います。
ただ、実用的なAIアプリケーションを開発しようとすると、AIエージェントということになっていきます。その際にはLangGraph4jのようなライブラリも有用になるでしょう。
また、AIエージェントを作成するとなると、ライブラリの使い方だけではなく、AIエージェントをどう作るかということも大事になります。
この連載をきっかけにLLMを使ったJavaアプリケーションの開発に興味を持ってもらえたら幸いです。