关于 ModelContextProtocol (MCP) Java SDK v0.8.0 Specification
本文档介绍 MCP Java SDK 0.8.0,供 AI 辅助开发 Java MCP 客户端和服务端使用。内容涵盖客户端与服务端组件、传输方式、工具、资源、提示词及错误处理。
主要功能
- 01客户端与服务端实现
- 02同步与异步 API
- 03Stdio 和自定义传输
- 04工具、资源与提示词
- 05协议类型与错误处理
适用场景
- 01生成 Java MCP 客户端代码
- 02构建同步或异步 MCP 服务端
- 03配置传输方式与资源处理
MCP 相关
该规范介绍用于实现 MCP 客户端及服务端的 Java SDK;客户端可与服务端通信,服务端可向客户端提供工具和资源。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
ModelContextProtocol (MCP) Java SDK v0.8.0 规范
简介
Model Context Protocol(MCP)是一种标准化协议,用于 AI 模型与外部工具或资源之间的通信。Java SDK 提供了该协议的可靠实现,使 Java 应用程序能够创建 MCP 服务,向 AI 模型提供工具和资源;也能创建 MCP 客户端,与这些服务通信。
本文档是 MCP Java SDK 0.8.0 版的完整规范,旨在用于 AI 辅助生成 MCP 客户端和服务端代码。
架构
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 操作实现适当的错误处理。
- 验证输入 - 在处理输入前验证所有输入。
- 使用适当的传输方式 - 根据使用场景选择适当的传输机制。
客户端
- 复用客户端 - 创建单个客户端实例,并将其用于多项操作。
- 处理断连 - 为网络传输实现重连逻辑。
- 验证响应 - 在使用服务端的响应前验证所有响应。
服务端
- 记录资源和工具 - 为所有资源和工具提供清晰的描述。
- 实现适当的错误处理 - 返回适当的错误代码和消息。
- 验证请求参数 - 在处理请求前验证所有请求参数。
- 使用资源模板 - 为动态资源使用资源模板。
参考资料
类参考
客户端类
McpClient- 用于创建 MCP 客户端的工厂McpSyncClient- 同步 MCP 客户端实现McpAsyncClient- 异步 MCP 客户端实现McpSchema.Implementation- 用于连接初始化的客户端信息
服务端类
McpServer- 用于创建 MCP 服务端的工厂McpSyncServer- 同步 MCP 服务端实现McpAsyncServer- 异步 MCP 服务端实现McpSchema.Implementation- 用于连接初始化的服务端信息
传输类
StdioClientTransport- 通过标准 I/O 进行传输的客户端StdioServerTransport- 通过标准 I/O 进行传输的服务端Transport- 所有传输方式的基础接口ReceiveHandler- 用于接收消息的处理器
内容类
McpSchema.ResourceContents- 资源内容接口McpSchema.TextResourceContents- 基于文本的资源内容McpSchema.Content- 工具内容接口McpSchema.TextContent- 基于文本的工具内容McpSchema.CallToolResult- 工具执行的响应
错误处理
McpError- 用于 MCP 协议错误的错误类McpSchema.ErrorCodes- 错误代码常量ErrorCode- types 包中的错误代码常量
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。