ModelContextProtocol (MCP) Java SDK v0.8.0 Specification について
このドキュメントは、Java製MCPクライアントとサーバーのAI支援開発向けに、MCP Java SDK 0.8.0を解説します。クライアントとサーバー、トランスポート、ツール、リソース、プロンプト、エラー処理を扱います。
主な機能
- 01クライアントとサーバーの実装
- 02同期・非同期API
- 03Stdioとカスタムトランスポート
- 04ツール、リソース、プロンプト
- 05プロトコル型とエラー処理
活用シーン
- 01Java製MCPクライアントコードの生成
- 02同期または非同期MCPサーバーの構築
- 03トランスポートとリソース処理の設定
MCP 対応
MCPサーバーと通信するクライアント、およびクライアントにツールやリソースを公開するサーバーを実装するJava SDKの仕様です。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
ModelContextProtocol (MCP) Java SDK v0.8.0 仕様
はじめに
Model Context Protocol(MCP)は、AI モデルと外部のツールやリソース間の通信を標準化するプロトコルです。Java SDK は、このプロトコルを堅牢に実装しており、AI モデルにツールやリソースを公開する MCP サーバーと、それらのサーバーと通信する MCP クライアントを Java アプリケーションで作成できます。
このドキュメントは、MCP クライアントおよびサーバーの AI 支援コード生成を目的とした、MCP Java SDK バージョン 0.8.0 の包括的な仕様です。
アーキテクチャ
MCP Java SDK は、関心事を明確に分離したモジュール式アーキテクチャを採用しています。
graph TD
Client[Client] --> Transport[Transport Layer]
Server[Server] --> Transport
Transport --> Protocol[Protocol Layer]
Protocol --> JSON[JSON Schema]
Client --> Resources[Resources]
Client --> Tools[Tools]
Server --> Resources
Server --> Tools
Server --> ErrorHandling[Error Handling]
Client --> ErrorHandling
コアコンポーネント
- クライアント - MCP サーバーと連携して、リソースやツールにアクセスします
- サーバー - MCP クライアントにリソースやツールを公開します
- トランスポート層 - クライアントとサーバー間の通信を処理します
- プロトコル層 - MCP プロトコル仕様を実装します
- リソース - サーバーが公開する静的または動的なデータです
- ツール - サーバーが公開する実行可能な関数です
パッケージ構成
SDK は、次の主要なパッケージで構成されています。
io.modelcontextprotocol.client- クライアントの実装(McpClient)io.modelcontextprotocol.server- サーバーの実装(McpServer、McpSyncServer、McpAsyncServer)io.modelcontextprotocol.client.transport- クライアントのトランスポート実装io.modelcontextprotocol.server.transport- サーバーのトランスポート実装およびプロバイダーio.modelcontextprotocol.spec- コアプロトコル仕様およびスキーマクラスio.modelcontextprotocol.transport- トランスポート層のインターフェースおよび実装io.modelcontextprotocol.types- MCP プロトコルの型定義io.modelcontextprotocol.errors- エラー処理クラスおよびユーティリティ
インストール
Maven 依存関係
すべてのコンポーネントで互換性のあるバージョンを使用するため、MCP BOM(部品表)をプロジェクトに追加します。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-bom</artifactId>
<version>0.8.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
次に、必要な依存関係を追加します。
<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
</dependency>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-test</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
クライアントの実装
ツールを使用する同期クライアント
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import java.util.HashMap;
import java.util.Map;
public class SyncClientStdioToolsExample {
public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");
// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();
// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);
// Create the client using the builder pattern
McpSyncClient client = McpClient.sync(transport)
.clientInfo(clientInfo)
.build();
try {
// Read a resource
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("example://resource");
McpSchema.ReadResourceResult result = client.readResource(request);
// Access the resource contents
if (result.contents() != null && !result.contents().isEmpty()) {
McpSchema.ResourceContents contents = result.contents().get(0);
if (contents instanceof McpSchema.TextResourceContents textContents) {
System.out.println("Resource content: " + textContents.text());
}
}
// Call a tool with a Map of arguments
Map<String, Object> toolArgs = new HashMap<>();
toolArgs.put("param1", "value1");
toolArgs.put("param2", 42);
McpSchema.CallToolRequest toolRequest = new McpSchema.CallToolRequest("example-tool", toolArgs);
McpSchema.CallToolResult toolResponse = client.callTool(toolRequest);
// Access the tool response content
if (toolResponse.content() != null && !toolResponse.content().isEmpty()) {
McpSchema.Content content = toolResponse.content().get(0);
if (content instanceof McpSchema.TextContent textContent) {
System.out.println("Tool response: " + textContent.text());
}
}
} finally {
// Close the client
client.close();
}
}
}
プロンプトを使用する同期クライアント
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SyncClientStdioPromptsExample {
public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");
// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();
// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);
// Create the client using the builder pattern
McpSyncClient client = McpClient.sync(transport)
.clientInfo(clientInfo)
.build();
try {
// List available prompts
ListPromptsResult promptsResult = client.listPrompts();
if (promptsResult.prompts() != null && !promptsResult.prompts().isEmpty()) {
System.out.println("Available prompts:");
for (Prompt prompt : promptsResult.prompts()) {
System.out.println("- " + prompt.name() + ": " + prompt.description());
}
// Get a specific prompt
String promptName = promptsResult.prompts().get(0).name();
// Create arguments for the prompt if needed
Map<String, Object> promptArgs = new HashMap<>();
promptArgs.put("language", "Java");
promptArgs.put("code", "public class Example { public static void main(String[] args) { } }");
GetPromptRequest promptRequest = new GetPromptRequest(promptName, promptArgs);
GetPromptResult promptResult = client.getPrompt(promptRequest);
// Process the prompt result
if (promptResult.messages() != null && !promptResult.messages().isEmpty()) {
System.out.println("Prompt messages:");
for (PromptMessage message : promptResult.messages()) {
System.out.println("Role: " + message.role());
System.out.println("Content: " + message.content());
}
}
} else {
System.out.println("No prompts available from the server.");
}
} finally {
// Close the client
client.close();
}
}
}
ツールを使用する非同期クライアント
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
public class AsyncClientStdioToolsExample {
public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");
// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();
// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);
// Create the client using the builder pattern
McpAsyncClient client = McpClient.async(transport)
.clientInfo(clientInfo)
.build();
try {
// Initialize the client (connects to the server)
client.initialize().block(); // Block until initialization completes
// Read a resource
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("example://resource");
McpSchema.ReadResourceResult result = client.readResource(request).block();
// Access the resource contents
if (result.contents() != null && !result.contents().isEmpty()) {
McpSchema.ResourceContents contents = result.contents().get(0);
if (contents instanceof McpSchema.TextResourceContents textContents) {
System.out.println("Resource content: " + textContents.text());
}
}
// Call a tool with a Map of arguments
Map<String, Object> toolArgs = new HashMap<>();
toolArgs.put("param1", "value1");
toolArgs.put("param2", 42);
McpSchema.CallToolRequest toolRequest = new McpSchema.CallToolRequest("example-tool", toolArgs);
McpSchema.CallToolResult toolResponse = client.callTool(toolRequest).block();
// Access the tool response content
if (toolResponse.content() != null && !toolResponse.content().isEmpty()) {
McpSchema.Content content = toolResponse.content().get(0);
if (content instanceof McpSchema.TextContent textContent) {
System.out.println("Tool response: " + textContent.text());
}
}
} catch (Exception e) {
if (e.getCause() instanceof McpError) {
McpError mcpError = (McpError) e.getCause();
System.err.println("MCP Error: " + mcpError.getMessage());
} else {
System.err.println("Error: " + e.getMessage());
}
} finally {
// Close the client
client.close();
}
}
}
プロンプトを使用する非同期クライアント
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpError;
import java.util.HashMap;
import java.util.Map;
public class AsyncClientStdioPromptsExample {
public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");
// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();
// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);
// Create the client using the builder pattern
McpAsyncClient client = McpClient.async(transport)
.clientInfo(clientInfo)
.build();
try {
// Initialize the client (connects to the server)
client.initialize().block(); // Block until initialization completes
// List available prompts
ListPromptsResult promptsResult = client.listPrompts().block();
if (promptsResult.prompts() != null && !promptsResult.prompts().isEmpty()) {
System.out.println("Available prompts:");
for (Prompt prompt : promptsResult.prompts()) {
System.out.println("- " + prompt.name() + ": " + prompt.description());
}
// Get a specific prompt
String promptName = promptsResult.prompts().get(0).name();
// Create arguments for the prompt if needed
Map<String, Object> promptArgs = new HashMap<>();
promptArgs.put("language", "Java");
promptArgs.put("code", "public class Example { public static void main(String[] args) { } }");
GetPromptRequest promptRequest = new GetPromptRequest(promptName, promptArgs);
GetPromptResult promptResult = client.getPrompt(promptRequest).block();
// Process the prompt result
if (promptResult.messages() != null && !promptResult.messages().isEmpty()) {
System.out.println("Prompt messages:");
for (PromptMessage message : promptResult.messages()) {
System.out.println("Role: " + message.role());
System.out.println("Content: " + message.content());
}
}
} else {
System.out.println("No prompts available from the server.");
}
} catch (Exception e) {
if (e.getCause() instanceof McpError) {
McpError mcpError = (McpError) e.getCause();
System.err.println("MCP Error: " + mcpError.getMessage());
} else {
System.err.println("Error: " + e.getMessage());
}
} finally {
// Close the client
client.close();
}
}
}
サーバーの実装
ツールを使用する同期サーバー
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SyncServerStdioToolsExample {
public static void main(String[] args) throws Exception {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("example-server", "1.0.0");
// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();
// Create server using the builder pattern
McpSyncServer server = McpServer.sync(transportProvider)
.serverInfo(serverInfo)
.tool(
new McpSchema.Tool(
"example-tool",
"An example tool",
createToolSchema()
),
(exchange, toolArgs) -> {
String param1 = (String) toolArgs.get("param1");
Number param2 = (Number) toolArgs.get("param2");
List<McpSchema.Content> content = new ArrayList<>();
content.add(new McpSchema.TextContent(
null,
null,
"Tool executed with param1=" + param1 + ", param2=" + param2
));
return new McpSchema.CallToolResult(content, false);
}
)
.build();
System.err.println("Server started");
}
/**
* Creates the JSON schema for the example tool.
*/
private static McpSchema.JsonSchema createToolSchema() {
// Create input schema for the tool
Map<String, Object> properties = new HashMap<>();
Map<String, Object> param1 = new HashMap<>();
param1.put("type", "string");
param1.put("description", "A string parameter");
Map<String, Object> param2 = new HashMap<>();
param2.put("type", "number");
param2.put("description", "A numeric parameter");
properties.put("param1", param1);
properties.put("param2", param2);
List<String> required = List.of("param1");
return new McpSchema.JsonSchema("object", properties, required, null);
}
}
プロンプトを使用する同期サーバー
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SyncServerStdioPromptsExample {
public static void main(String[] args) throws Exception {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("prompts-example-server", "1.0.0");
// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();
// Create a code analysis prompt
List<PromptArgument> codeAnalysisArgs = new ArrayList<>();
codeAnalysisArgs.add(new PromptArgument(
"language",
"The programming language of the code",
true
));
codeAnalysisArgs.add(new PromptArgument(
"code",
"The code to analyze",
true
));
Prompt codeAnalysisPrompt = new Prompt(
"code-analysis",
"Analyzes code for potential issues and improvements",
codeAnalysisArgs
);
// Create server using the builder pattern
McpSyncServer server = McpServer.sync(transportProvider)
.serverInfo(serverInfo)
.prompt(
codeAnalysisPrompt,
(exchange, request) -> {
// Extract arguments from the request
String language = (String) request.arguments().get("language");
String code = (String) request.arguments().get("code");
// Create prompt messages
List<PromptMessage> messages = new ArrayList<>();
// System message
messages.add(new PromptMessage(
"system",
"You are a code analysis assistant that helps identify issues and suggest improvements."
));
// User message with the code
messages.add(new PromptMessage(
"user",
"Please analyze this " + language + " code:\n\n```" + language + "\n" + code + "\n```"
));
// Assistant message with the analysis
messages.add(new PromptMessage(
"assistant",
"Here's my analysis of your " + language + " code:\n\n" +
"1. The code is very minimal and doesn't do anything yet.\n" +
"2. Consider adding some functionality to the main method.\n" +
"3. Add comments to explain the purpose of the class."
));
// Return the prompt result
return new GetPromptResult(
"Code analysis for " + language,
messages
);
}
)
.build();
System.err.println("Prompts server started");
}
}
ツールを使用する非同期サーバー
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
public class AsyncServerStdioToolsExample {
public static void main(String[] args) {
try {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("example-server", "1.0.0");
// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();
// Create server using the builder pattern
McpAsyncServer server = McpServer.async(transportProvider)
.serverInfo(serverInfo)
.tool(
new McpSchema.Tool(
"example-tool",
"An example tool",
createToolSchema()
),
(exchange, toolArgs) -> {
String param1 = (String) toolArgs.get("param1");
Number param2 = (Number) toolArgs.get("param2");
List<McpSchema.Content> content = new ArrayList<>();
content.add(new McpSchema.TextContent(
null,
null,
"Tool executed with param1=" + param1 + ", param2=" + param2
));
return Mono.just(new McpSchema.CallToolResult(content, false));
}
)
.build();
System.err.println("Server started");
} catch (Exception e) {
System.err.println("Failed to start server: " + e.getMessage());
}
}
/**
* Creates the JSON schema for the example tool.
*/
private static McpSchema.JsonSchema createToolSchema() {
// Create input schema for the tool
Map<String, Object> properties = new HashMap<>();
Map<String, Object> param1 = new HashMap<>();
param1.put("type", "string");
param1.put("description", "A string parameter");
Map<String, Object> param2 = new HashMap<>();
param2.put("type", "number");
param2.put("description", "A numeric parameter");
properties.put("param1", param1);
properties.put("param2", param2);
List<String> required = List.of("param1");
return new McpSchema.JsonSchema("object", properties, required, null);
}
}
リソースを使用する非同期サーバー
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import reactor.core.publisher.Mono;
public class AsyncServerStdioResourcesExample {
// Pattern for matching resource URIs
private static final Pattern RESOURCE_PATTERN = Pattern.compile("data://users/(.+)");
public static void main(String[] args) {
try {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("async-resources-example", "1.0.0");
// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();
// Create server using the builder pattern
McpAsyncServer server = McpServer.async(transportProvider)
.serverInfo(serverInfo)
.resourceTemplate(
new McpSchema.ResourceTemplate(
"data://users/{userId}",
"User Data",
"Data for a specific user",
"application/json",
null
),
(exchange, request) -> {
String uri = request.uri();
// Parse the URI to extract parameters
Matcher matcher = RESOURCE_PATTERN.matcher(uri);
if (matcher.matches()) {
String userId = matcher.group(1);
// Simulate an asynchronous database lookup
return Mono.fromCallable(() -> {
// In a real implementation, this would be a database query
// For this example, we'll just generate some data
String userData = String.format(
"{\"id\":\"%s\",\"name\":\"User %s\",\"email\":\"user%[email protected]\",\"created\":\"2025-03-24\"}",
userId, userId, userId
);
List<McpSchema.ResourceContents> contents = new ArrayList<>();
contents.add(new McpSchema.TextResourceContents(
uri,
"application/json",
userData
));
return new McpSchema.ReadResourceResult(contents);
});
}
return Mono.error(new McpError(
new McpSchema.JSONRPCResponse.JSONRPCError(
McpSchema.ErrorCodes.RESOURCE_NOT_FOUND,
"Resource not found: " + uri,
null
)
));
}
)
.build();
System.err.println("Async resources server started");
} catch (Exception e) {
System.err.println("Failed to start server: " + e.getMessage());
}
}
}
プロンプトを使用する非同期サーバー
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
public class AsyncServerStdioPromptsExample {
public static void main(String[] args) {
try {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("async-prompts-example", "1.0.0");
// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();
// Create a code analysis prompt
List<PromptArgument> codeAnalysisArgs = new ArrayList<>();
codeAnalysisArgs.add(new PromptArgument(
"language",
"The programming language of the code",
true
));
codeAnalysisArgs.add(new PromptArgument(
"code",
"The code to analyze",
true
));
Prompt codeAnalysisPrompt = new Prompt(
"code-analysis",
"Analyzes code for potential issues and improvements",
codeAnalysisArgs
);
// Create server using the builder pattern
McpAsyncServer server = McpServer.async(transportProvider)
.serverInfo(serverInfo)
.prompt(
codeAnalysisPrompt,
(exchange, request) -> {
// Extract arguments from the request
String language = (String) request.arguments().get("language");
String code = (String) request.arguments().get("code");
// Simulate an asynchronous operation (e.g., calling an external API)
return Mono.fromCallable(() -> {
// Create prompt messages
List<PromptMessage> messages = new ArrayList<>();
// System message
messages.add(new PromptMessage(
"system",
"You are a code analysis assistant that helps identify issues and suggest improvements."
));
// User message with the code
messages.add(new PromptMessage(
"user",
"Please analyze this " + language + " code:\n\n```" + language + "\n" + code + "\n```"
));
// Assistant message with the analysis
messages.add(new PromptMessage(
"assistant",
"Here's my analysis of your " + language + " code:\n\n" +
"1. The code is very minimal and doesn't do anything yet.\n" +
"2. Consider adding some functionality to the main method.\n" +
"3. Add comments to explain the purpose of the class."
));
// Return the prompt result
return new GetPromptResult(
"Code analysis for " + language,
messages
);
});
}
)
.build();
System.err.println("Async prompts server started");
} catch (Exception e) {
System.err.println("Failed to start server: " + e.getMessage());
}
}
}
トランスポートの設定
MCP Java SDK は、クライアントとサーバー間の通信に複数のトランスポート方式をサポートしています。
StdioTransport
標準入出力ストリームを介して通信します。サブプロセス間の通信に便利です。
// Server-side
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();
McpSyncServer server = new McpSyncServer(transportProvider, features);
// Client-side
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
ServerParameters serverParams = new ServerParameters.Builder().build();
StdioClientTransport transport = new StdioClientTransport(serverParams);
client.connect(transport);
カスタムトランスポート
適切なトランスポートプロバイダーインターフェースを実装することで、カスタムトランスポートを実装できます。
import io.modelcontextprotocol.transport.Transport;
import io.modelcontextprotocol.transport.ReceiveHandler;
import io.modelcontextprotocol.server.transport.McpServerTransportProvider;
import io.modelcontextprotocol.client.transport.McpClientTransport;
// Server-side custom transport provider
public class CustomServerTransportProvider implements McpServerTransportProvider {
@Override
public Transport createTransport() {
// Implement transport creation logic
return new CustomServerTransport();
}
}
// Client-side custom transport
public class CustomClientTransport implements Transport {
@Override
public void send(String message) {
// Implement sending logic
}
@Override
public void setReceiveHandler(ReceiveHandler handler) {
// Implement receiving logic
}
@Override
public void close() {
// Implement closing logic
}
}
リソースの実装
MCP におけるリソースは、クライアントがアクセスできるデータを表します。静的(事前定義)または動的(要求に応じて生成)にできます。
静的リソース
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import java.util.ArrayList;
import java.util.List;
// Define static resources in server features
features.resourceHandler = (exchange, request) -> {
if (request instanceof McpSchema.ListResourcesRequest) {
List<McpSchema.Resource> resources = new ArrayList<>();
resources.add(new McpSchema.Resource("data://example/static"));
return new McpSchema.ListResourcesResult(resources, null);
} else if (request instanceof McpSchema.ReadResourceRequest) {
McpSchema.ReadResourceRequest readRequest = (McpSchema.ReadResourceRequest) request;
String uri = readRequest.uri();
if ("data://example/static".equals(uri)) {
List<McpSchema.ResourceContents> contents = new ArrayList<>();
contents.add(new McpSchema.TextResourceContents(
uri,
"application/json",
"{\"name\":\"Example\",\"value\":42}"
));
return new McpSchema.ReadResourceResult(contents);
}
throw new McpError(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND, "Resource not found: " + uri);
}
return null;
};
動的リソース(リソーステンプレート)
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
// Define resource templates in server features
features.resourceHandler = (exchange, request) -> {
if (request instanceof McpSchema.ListResourceTemplatesRequest) {
List<McpSchema.ResourceTemplate> templates = new ArrayList<>();
// Define a resource template
templates.add(new McpSchema.ResourceTemplate(
"data://users/{userId}",
"User Data",
"application/json",
"Data for a specific user",
null
));
return new McpSchema.ListResourceTemplatesResult(templates, null);
} else if (request instanceof McpSchema.ReadResourceRequest) {
McpSchema.ReadResourceRequest readRequest = (McpSchema.ReadResourceRequest) request;
String uri = readRequest.uri();
// Parse the URI to extract parameters
Pattern pattern = Pattern.compile("data://users/(.+)");
Matcher matcher = pattern.matcher(uri);
if (matcher.matches()) {
String userId = matcher.group(1);
// Generate dynamic content based on the userId
String userData = String.format("{\"id\":\"%s\",\"name\":\"User %s\",\"email\":\"user%[email protected]\"}",
userId, userId, userId);
List<McpSchema.ResourceContents> contents = new ArrayList<>();
contents.add(new McpSchema.TextResourceContents(
uri,
"application/json",
userData
));
return new McpSchema.ReadResourceResult(contents);
}
throw new McpError(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND, "Resource not found: " + uri);
}
return null;
};
ツールの実装
MCP におけるツールは、クライアントから呼び出せる実行可能な関数を表します。
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.types.CallToolRequestSchema;
import io.modelcontextprotocol.types.ErrorCode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// Define tools in server features
features.toolHandler = (exchange, request) -> {
if (request instanceof McpSchema.ListToolsRequest) {
List<McpSchema.Tool> tools = new ArrayList<>();
// Define a calculator tool
Map<String, Object> properties = new HashMap<>();
Map<String, Object> operation = new HashMap<>();
operation.put("type", "string");
operation.put("description", "Operation to perform (add, subtract, multiply, divide)");
operation.put("enum", List.of("add", "subtract", "multiply", "divide"));
Map<String, Object> a = new HashMap<>();
a.put("type", "number");
a.put("description", "First operand");
Map<String, Object> b = new HashMap<>();
b.put("type", "number");
b.put("description", "Second operand");
properties.put("operation", operation);
properties.put("a", a);
properties.put("b", b);
Map<String, Object> inputSchema = new HashMap<>();
inputSchema.put("type", "object");
inputSchema.put("properties", properties);
inputSchema.put("required", List.of("operation", "a", "b"));
tools.add(new McpSchema.Tool(
"calculator",
"Performs basic arithmetic operations",
new McpSchema.JsonSchema(inputSchema),
null
));
return new McpSchema.ListToolsResult(tools, null);
} else if (request instanceof McpSchema.CallToolRequest) {
McpSchema.CallToolRequest callRequest = (McpSchema.CallToolRequest) request;
String name = callRequest.name();
if ("calculator".equals(name)) {
Map<String, Object> arguments = callRequest.arguments();
String operation = (String) arguments.get("operation");
Number a = (Number) arguments.get("a");
Number b = (Number) arguments.get("b");
double result;
switch (operation) {
case "add":
result = a.doubleValue() + b.doubleValue();
break;
case "subtract":
result = a.doubleValue() - b.doubleValue();
break;
case "multiply":
result = a.doubleValue() * b.doubleValue();
break;
case "divide":
if (b.doubleValue() == 0) {
throw new McpError(ErrorCode.INVALID_PARAMS, "Division by zero");
}
result = a.doubleValue() / b.doubleValue();
break;
default:
throw new McpError(ErrorCode.INVALID_PARAMS, "Unknown operation: " + operation);
}
List<McpSchema.Content> content = new ArrayList<>();
content.add(new McpSchema.TextContent(String.valueOf(result)));
return new McpSchema.CallToolResult(content, false);
}
throw new McpError(ErrorCode.METHOD_NOT_FOUND, "Tool not found: " + name);
}
return null;
};
エラー処理
MCP Java SDK は、McpError クラスを使用する標準化されたエラー処理方法を提供します。
エラーコード
SDK は、McpSchema.ErrorCodes クラスで標準エラーコードを定義しています。
PARSE_ERROR- 無効な JSON を受信しましたINVALID_REQUEST- リクエストが無効ですMETHOD_NOT_FOUND- 要求されたメソッドが存在しませんINVALID_PARAMS- メソッドのパラメーターが無効ですINTERNAL_ERROR- サーバー内部エラーですRESOURCE_NOT_FOUND- 要求されたリソースが存在しません
エラーのスロー
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.types.ErrorCode;
// In a request handler
if (someCondition) {
throw new McpError(ErrorCode.INVALID_PARAMS, "Parameter 'x' must be positive");
}
エラーの処理
// Synchronous error handling
try {
client.callTool(toolRequest);
} catch (McpError e) {
System.err.println("MCP Error: " + e.getMessage());
}
// Asynchronous error handling
client.callTool(toolRequest)
.thenAccept(response -> {
// Handle success
})
.exceptionally(e -> {
if (e.getCause() instanceof McpError) {
McpError mcpError = (McpError) e.getCause();
System.err.println("MCP Error: " + mcpError.getCode() + ": " + mcpError.getMessage());
} else {
System.err.println("Error: " + e.getMessage());
}
return null;
});
サーバー側のエラー処理
// Set a request handler for specific requests
server.setRequestHandler(CallToolRequestSchema, (request) -> {
// Handle the request
return new McpSchema.CallToolResult(content, false);
});
// Set an error handler for specific error types
server.setErrorHandler(error -> {
// Handle the error
System.err.println("Server error: " + error.getMessage());
});
// Set a global error handler in server features
features.errorHandler = error -> {
System.err.println("Server error: " + error.getMessage());
// Log the error, send metrics, etc.
};
ベストプラクティス
全般
- BOMを使用する - すべてのコンポーネントの互換性を確保するため、必ずMCP BOMを使用してください。
- 接続を閉じる - クライアントとサーバーが不要になったら、必ず閉じてください。
- エラーを処理する - すべてのMCP操作に適切なエラー処理を実装してください。
- 入力を検証する - 処理する前に、すべての入力を検証してください。
- 適切なトランスポートを使用する - ユースケースに応じて、適切なトランスポート機構を選択してください。
クライアント側
- クライアントを再利用する - クライアントインスタンスを1つ作成し、複数の操作で再利用してください。
- 切断に対処する - ネットワークトランスポートでは、再接続のロジックを実装してください。
- レスポンスを検証する - サーバーからのすべてのレスポンスを、使用する前に検証してください。
サーバー側
- リソースとツールを文書化する - すべてのリソースとツールについて、明確な説明を記載してください。
- 適切なエラー処理を実装する - 適切なエラーコードとメッセージを返してください。
- リクエストパラメーターを検証する - 処理する前に、すべてのリクエストパラメーターを検証してください。
- リソーステンプレートを使用する - 動的リソースにはリソーステンプレートを使用してください。
参考資料
クラスリファレンス
クライアントクラス
McpClient- MCPクライアントを作成するファクトリーMcpSyncClient- 同期MCPクライアントの実装McpAsyncClient- 非同期MCPクライアントの実装McpSchema.Implementation- 接続初期化用のクライアント情報
サーバークラス
McpServer- MCPサーバーを作成するファクトリーMcpSyncServer- 同期MCPサーバーの実装McpAsyncServer- 非同期MCPサーバーの実装McpSchema.Implementation- 接続初期化用のサーバー情報
トランスポートクラス
StdioClientTransport- 標準入出力を介したクライアントトランスポートStdioServerTransport- 標準入出力を介したサーバートランスポートTransport- すべてのトランスポートの基底インターフェースReceiveHandler- メッセージ受信のハンドラー
コンテンツクラス
McpSchema.ResourceContents- リソースコンテンツのインターフェースMcpSchema.TextResourceContents- テキスト形式のリソースコンテンツMcpSchema.Content- ツールコンテンツのインターフェースMcpSchema.TextContent- テキスト形式のツールコンテンツMcpSchema.CallToolResult- ツール実行からのレスポンス
エラー処理
McpError- MCPプロトコルエラー用のエラークラスMcpSchema.ErrorCodes- エラーコード定数ErrorCode- typesパッケージ内のエラーコード定数
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。