SDK 内容
Java MCP 概览 [Java MCP 客户端] Java MCP 服务端客户端功能
MCP 客户端是模型上下文协议 (MCP) 架构中的关键组件,负责建立和管理与 MCP 服务端的连接。它实现了协议的客户端部分,处理:- 协议版本协商,以确保与服务端的兼容性
- 能力协商,以确定可用功能
- 消息传输和 JSON-RPC 通信
- 工具发现与执行
- 资源访问与管理
- 提示词系统交互
- 可选功能,如根目录管理和采样支持
核心
io.modelcontextprotocol.sdk:mcp 模块提供了 STDIO、Streamable-HTTP 和 SSE 客户端传输实现,无需外部 Web 框架。对于 Spring Framework 用户,Spring 特有的传输实现作为可选依赖项 io.modelcontextprotocol.sdk:mcp-spring-webflux 提供。这个基于 Spring AI MCP 的快速入门演示将向您展示如何构建一个连接到 MCP 服务端的 AI 客户端。
- 同步 API
- 异步 API
复制
// Create a sync client with custom configuration
McpSyncClient client = McpClient.sync(transport)
.requestTimeout(Duration.ofSeconds(10))
.capabilities(ClientCapabilities.builder()
.roots(true) // Enable roots capability
.sampling() // Enable sampling capability
.elicitation() // Enable elicitation capability
.build())
.sampling(request -> CreateMessageResult.builder()...build())
.elicitation(elicitRequest -> ElicitResult.builder()...build())
.toolsChangeConsumer((List<McpSchema.Tool> tools) -> ...)
.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> ...)
.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> ...)
.loggingConsumer((LoggingMessageNotification logging) -> ...)
.progressConsumer((ProgressNotification progress) -> ...)
.build();
// Initialize connection
client.initialize();
// List available tools
ListToolsResult tools = client.listTools();
// Call a tool
CallToolResult result = client.callTool(
new CallToolRequest("calculator",
Map.of("operation", "add", "a", 2, "b", 3))
);
// List and read resources
ListResourcesResult resources = client.listResources();
ReadResourceResult resource = client.readResource(
new ReadResourceRequest("resource://uri")
);
// List and use prompts
ListPromptsResult prompts = client.listPrompts();
GetPromptResult prompt = client.getPrompt(
new GetPromptRequest("greeting", Map.of("name", "Spring"))
);
// Add/remove roots
client.addRoot(new Root("file:///path", "description"));
client.removeRoot("file:///path");
// Close client
client.closeGracefully();
复制
// Create an async client with custom configuration
McpAsyncClient client = McpClient.async(transport)
.requestTimeout(Duration.ofSeconds(10))
.capabilities(ClientCapabilities.builder()
.roots(true) // Enable roots capability
.sampling() // Enable sampling capability
.elicitation() // Enable elicitation capability
.build())
.sampling(request -> Mono.just(new CreateMessageResult(response)))
.elicitation(elicitRequest -> Mono.just(ElicitResult.builder()...build()))
.toolsChangeConsumer(tools -> Mono.fromRunnable(() -> logger.info("Tools updated: {}", tools)))
.resourcesChangeConsumer(resources -> Mono.fromRunnable(() -> logger.info("Resources updated: {}", resources)))
.promptsChangeConsumer(prompts -> Mono.fromRunnable(() -> logger.info("Prompts updated: {}", prompts)))
.loggingConsumer(notification -> Mono.fromRunnable(() -> logger.info("Log: {}", notification.data())))
.progressConsumer(progress -> Mono.fromRunnable(() -> logger.info("Progress update: {}", progress.data())))
.build();
// Initialize connection and use features
client.initialize()
.flatMap(initResult -> client.listTools())
.flatMap(tools -> {
return client.callTool(new CallToolRequest(
"calculator",
Map.of("operation", "add", "a", 2, "b", 3)
));
})
.flatMap(result -> {
return client.listResources()
.flatMap(resources ->
client.readResource(new ReadResourceRequest("resource://uri"))
);
})
.flatMap(resource -> {
return client.listPrompts()
.flatMap(prompts ->
client.getPrompt(new GetPromptRequest(
"greeting",
Map.of("name", "Spring")
))
);
})
.flatMap(prompt -> {
return client.addRoot(new Root("file:///path", "description"))
.then(client.removeRoot("file:///path"));
})
.doFinally(signalType -> {
client.closeGracefully().subscribe();
})
.subscribe();
客户端传输
传输层处理 MCP 客户端和服务端之间的通信,为各种用例提供不同的实现。客户端传输管理消息序列化、连接建立以及特定协议的通信模式。- STDIO
- HttpClient
- WebClient
为基于进程内的通信创建传输
复制
ServerParameters params = ServerParameters.builder("npx")
.args("-y", "@modelcontextprotocol/server-everything", "dir")
.build();
McpTransport transport = new StdioClientTransport(params);
- Streamable-HTTP (HttpClient)
- SSE (HttpClient)
框架无关(仅使用 JDK API)的 Streamable-HTTP 客户端传输
复制
McpTransport transport = HttpClientStreamableHttpTransport
.builder("http://your-mcp-server")
.build();
框架无关(仅使用 JDK API)的 SSE 客户端传输
复制
McpTransport transport = HttpClientSseClientTransport
.builder("http://your-mcp-server")
.build();
HttpRequestBuilder。这在 Streamable HTTP 和 SSE 传输中均可用。使用自定义 HttpRequest.Builder 时,传输发出的每个 HTTP 请求都将使用构建器中的硬编码配置。例如,这对于在标头中提供永不过期的安全令牌非常有用。若要向每个请求添加 X-Custom-Header 标头,请使用:复制
var requestBuilder = HttpRequest
.newBuilder()
.header("X-Custom-Header", "some header value");
HttpClientStreamableHttpTransport
.builder("https://mcp.example.com")
.requestBuilder(requestBuilder)
.build();
McpSyncHttpClientRequestCustomizer 或 McpAsyncHttpClientRequestCustomizer。选择与 MCP 客户端类型(同步或异步)匹配的请求自定义器。请注意,自定义器中可能无法使用线程本地变量 (thread-locals),必须通过 McpTransportContext 访问上下文相关信息(参见 添加上下文信息)。示例实现如下:复制
// Sync
class MyRequestCustomizer implements McpSyncHttpClientRequestCustomizer {
@Override
public void customize(HttpRequest.Builder builder, String method,
URI endpoint, String body, McpTransportContext context) {
// ... custom logic ...
var token = obtainAccessToken(context);
builder.header("Authorization", "Bearer " + token);
}
}
// Async
class MyAsyncRequestCustomizer implements McpAsyncHttpClientRequestCustomizer {
@Override
public Publisher<HttpRequest.Builder> customize(HttpRequest.Builder builder,
String method, URI endpoint, String body, McpTransportContext context) {
// ... custom logic ...
Mono<String> token = obtainAccessToken(context);
return token.map(t ->
builder.copy()
.header("Authorization", "Bearer " + t)
);
}
}
复制
HttpClientStreamableHttpTransport
.builder("https://mcp.example.com")
.httpRequestCustomizer(new MyRequestCustomizer()) // sync
.asyncHttpRequestCustomizer(new MyAsyncRequestCustomizer()) // OR async
.build();
DelegatingMcpSyncHttpClientRequestCustomizer 或 DelegatingMcpAsyncHttpClientRequestCustomizer。基于 WebClient 的客户端传输。需要 mcp-spring-webflux 依赖项。
- Streamable-HTTP (WebClient)
- SSE (WebClient)
复制
WebClient.Builder webClientBuilder = WebClient.builder()
.baseUrl("http://your-mcp-server");
McpTransport transport = WebClientStreamableHttpTransport
.builder(webClientBuilder)
.build();
复制
WebClient.Builder webClientBuilder = WebClient.builder()
.baseUrl("http://your-mcp-server");
McpTransport transport = WebFluxSseClientTransport(webClientBuilder)
.builder(webClientBuilder)
.build();
WebClient.Builder。使用自定义构建器时,传输发送的每个请求都将被自定义。例如,这对于在标头中提供永不过期的安全令牌非常有用。若要向每个请求添加 X-Custom-Header 标头,请使用:复制
var webClientBuilder = WebClient.builder()
.defaultHeader("X-Custom-Header", "some header value");
McpTransport transport = WebClientStreamableHttpTransport
.builder(webClientBuilder)
.build();
ExchangeFilterFunction。在该函数中,可以在运行时计算新请求,并从 Reactor 上下文中获取额外信息。通常将上下文信息存储在 Reactor 上下文中的 McpTransportContext 里(常见于 McpSyncClient),但也可以使用任何上下文键。例如:复制
WebClient.builder()
.filter((request, next) -> {
return Mono.deferContextual(ctx -> {
var transportContext = ctx.get(McpTransportContext.KEY);
var otherInfo = ctx.get("your-context-key");
Mono<String> token = obtainAccessToken(transportContext, otherInfo);
return token.map(t -> {
var newRequest = ClientRequest.from(request)
.header("Authorization", "Bearer " + t)
.build();
// Ensure you call next.exchange to execute the HTTP request
return next.exchange(newRequest);
});
});
});
客户端能力
可以为客户端配置各种能力复制
var capabilities = ClientCapabilities.builder()
.roots(true) // Enable filesystem roots support with list changes notifications
.sampling() // Enable LLM sampling support
.elicitation() // Enable elicitation capability
.build();
Roots (根目录) 支持
Roots (根目录) 定义了服务端在文件系统中可以操作的边界复制
// Add a root dynamically
client.addRoot(new Root("file:///path", "description"));
// Remove a root
client.removeRoot("file:///path");
// Notify server of roots changes
client.rootsListChangedNotification();
- 请求可访问的文件系统根目录列表
- 在根目录列表发生变化时接收通知
- 了解其拥有访问权限的目录和文件
Sampling (采样) 支持
Sampling (采样) 允许服务端通过客户端请求 LLM 交互(“补全”或“生成”)复制
// Configure sampling handler
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
// Sampling implementation that interfaces with LLM
return new CreateMessageResult(response);
};
// Create client with sampling support
var client = McpClient.sync(transport)
.capabilities(ClientCapabilities.builder()
.sampling()
.build())
.sampling(samplingHandler)
.build();
- 服务端无需 API 密钥即可利用 AI 能力
- 客户端保持对模型访问和权限的控制
- 支持基于文本和图像的交互
- 可选地在提示词中包含 MCP 服务端上下文
Elicitation (启发/诱导) 支持
Elicitation (启发) 允许服务端向客户端请求特定信息或澄清复制
// Configure elicitation handler
Function<ElicitRequest, ElicitResult> elicitationHandler = request -> {
// Elicitation implementation that interfaces with LLM
return ElicitResult.builder()...build();
};
// Create client with elicitation support
var client = McpClient.sync(transport)
.capabilities(ClientCapabilities.builder()
.elicitation() // enable elicitation capability
.build())
.elicitation(elicitationHandler) // register elicitation handler
.build();
Logging (日志) 支持
客户端可以注册一个日志消费者以接收来自服务端的日志消息,并设置最小日志级别来过滤消息复制
var mcpClient = McpClient.sync(transport)
.loggingConsumer((LoggingMessageNotification notification) -> {
System.out.println("Received log message: " + notification.data());
})
.build();
mcpClient.initialize();
mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO);
// Call the tool that can send logging notifications
CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of()));
mcpClient.setLoggingLevel(level) 请求控制其接收的最小日志级别。低于设定级别的消息将被过滤掉。支持的日志级别(按严重程度递增排序):DEBUG (0), INFO (1), NOTICE (2), WARNING (3), ERROR (4), CRITICAL (5), ALERT (6), EMERGENCY (7)
Progress (进度) 支持
客户端可以注册一个进度消费者以接收来自服务端的进度更新复制
var mcpClient = McpClient.sync(transport)
.progressConsumer((ProgressNotification progress) -> {
System.out.println("Received progress update: " + progress.data());
})
.build();
mcpClient.initialize();
// Call the tool that can send progress notifications
CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("progress-test", Map.of()));
变更通知
客户端可以注册一个变更消费者,以接收来自服务端关于工具、资源或提示词更新的变更通知复制
var spec = McpClient.sync(transport);
// Adds a consumer to be notified when the available tools change, such as tools
// being added or removed.
spec.toolsChangeConsumer((List<McpSchema.Tool> tools) -> {
// Handle tools change
});
// Adds a consumer to be notified when the available resources change, such as resources
// being added or removed.
spec.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> {
// Handle resources change
});
// Adds a consumer to be notified when the available prompts change, such as prompts
// being added or removed.
spec.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> {
// Handle prompts change
});
使用 MCP 客户端
工具执行
工具是客户端可以发现并执行的服务端函数。MCP 客户端提供了列出可用工具并使用特定参数执行它们的方法。每个工具都有唯一的名称并接受一个参数映射。- 同步 API
- 异步 API
复制
// List available tools and their names
var tools = client.listTools();
tools.forEach(tool -> System.out.println(tool.getName()));
// Execute a tool with parameters
var result = client.callTool("calculator", Map.of(
"operation", "add",
"a", 1,
"b", 2
));
复制
// List available tools asynchronously
client.listTools()
.doOnNext(tools -> tools.forEach(tool ->
System.out.println(tool.getName())))
.subscribe();
// Execute a tool asynchronously
client.callTool("calculator", Map.of(
"operation", "add",
"a", 1,
"b", 2
))
.subscribe();
资源访问
资源代表客户端可以使用 URI 模板访问的服务端数据源。MCP 客户端提供了发现可用资源并通过标准化接口检索其内容的方法。- 同步 API
- 异步 API
复制
// List available resources and their names
var resources = client.listResources();
resources.forEach(resource -> System.out.println(resource.getName()));
// Retrieve resource content using a URI template
var content = client.getResource("file", Map.of(
"path", "/path/to/file.txt"
));
复制
// List available resources asynchronously
client.listResources()
.doOnNext(resources -> resources.forEach(resource ->
System.out.println(resource.getName())))
.subscribe();
// Retrieve resource content asynchronously
client.getResource("file", Map.of(
"path", "/path/to/file.txt"
))
.subscribe();
提示词 (Prompt) 系统
提示词系统允许与服务端提示词模板进行交互。这些模板可以被发现并使用自定义参数执行,从而允许根据预定义模式动态生成文本。- 同步 API
- 异步 API
复制
// List available prompt templates
var prompts = client.listPrompts();
prompts.forEach(prompt -> System.out.println(prompt.getName()));
// Execute a prompt template with parameters
var response = client.executePrompt("echo", Map.of(
"text", "Hello, World!"
));
复制
// List available prompt templates asynchronously
client.listPrompts()
.doOnNext(prompts -> prompts.forEach(prompt ->
System.out.println(prompt.getName())))
.subscribe();
// Execute a prompt template asynchronously
client.executePrompt("echo", Map.of(
"text", "Hello, World!"
))
.subscribe();
使用补全 (Completion)
作为 补全能力 (Completion capabilities) 的一部分,MCP 为服务端提供了一种标准化方式来为提示词和资源 URI 提供参数自动补全建议。 请查看 服务端补全能力 以了解如何在服务端启用和配置补全。 在客户端,MCP 客户端提供了请求自动补全的方法:- 同步 API
- 异步 API
复制
CompleteRequest request = new CompleteRequest(
new PromptReference("code_review"),
new CompleteRequest.CompleteArgument("language", "py"));
CompleteResult result = syncMcpClient.completeCompletion(request);
复制
CompleteRequest request = new CompleteRequest(
new PromptReference("code_review"),
new CompleteRequest.CompleteArgument("language", "py"));
Mono<CompleteResult> result = mcpClient.completeCompletion(request);
添加上下文信息
通过 SSE 或 Streamable HTTP 传输发送的 HTTP 请求可以使用专用 API 进行自定义(参见 客户端传输)。这些自定义器可能需要必须在客户端级别注入的额外上下文特定信息。- 同步 API
- 异步 API
McpSyncClient 用于阻塞环境,可能依赖线程本地变量 (thread-locals) 来共享信息。例如,某些框架将当前服务器请求或安全令牌存储在线程本地变量中。若要使此类信息对底层传输可用,请使用 SyncSpec#transportContextProvider:复制
McpClient.sync(transport)
.transportContextProvider(() -> {
var data = obtainDataFromThreadLocals();
return McpTransportContext.create(
Map.of("some-data", data)
);
})
.build();
McpTransportContext 将在基于 HttpClient 的 McpSyncHttpClientRequestCustomizer 和基于 WebClient 的 ExchangeFilterFunction 中可用。McpAsyncClient 用于响应式环境。信息通过使用 Reactor 上下文的响应式链传递。若要在 Reactor 上下文中插入数据,请在调用客户端操作时使用 contextWrite:复制
var client = McpClient.async(transport)
// ...
.build();
Mono<McpSchema.ListToolsResult> toolList = client
.listTools()
.contextWrite(ctx -> {
var transportContext = McpTransportContext.create(
Map.of("some-key", "some value")
);
return ctx.put(McpTransportContext.KEY, transportContext)
.put("reactor-context-key", "some reactor value");
});
McpTransportContext 将在基于 HttpClient 的 McpAsyncHttpClientRequestCustomizer 中可用。整个 Reactor 上下文也可以通过 Mono.deferContextual 在 McpAsyncHttpClientRequestCustomizer 和 WebClient 的 ExchangeFilterFunction 中使用。复制
WebClient.builder()
.filter((request, next) -> {
return Mono.deferContextual(ctx -> {
var transportContext = ctx.get(McpTransportContext.KEY);
var someReactorValue = ctx.get("reactor-context-key");
// ... use context values ...
});
});