The MCP Client is a key component in the Model Context Protocol (MCP) architecture, responsible for establishing and managing connections with MCP servers. It implements the client-side of the protocol, handling:
Protocol version negotiation to ensure compatibility with servers
Capability negotiation to determine available features
Message transport and JSON-RPC communication
Tool discovery and execution
Resource access and management
Prompt system interactions
Optional features like roots management and sampling support
The core io.modelcontextprotocol.sdk:mcp module provides STDIO and SSE client transport implementations without requiring external web frameworks.
Spring-specific transport implementations are available as an optional dependency io.modelcontextprotocol.sdk:mcp-spring-webflux for Spring Framework users.
This quickstart demo, based on Spring AI MCP, will show
you how to build an AI client that connects to MCP servers.
The client provides both synchronous and asynchronous APIs for flexibility in different application contexts.
Copy
// Create a sync client with custom configurationMcpSyncClient client = McpClient.sync(transport) .requestTimeout(Duration.ofSeconds(10)) .capabilities(ClientCapabilities.builder() .roots(true) // Enable roots capability .sampling() // Enable sampling capability .build()) .sampling(request -> new CreateMessageResult(response)) .build();// Initialize connectionclient.initialize();// List available toolsListToolsResult tools = client.listTools();// Call a toolCallToolResult result = client.callTool( new CallToolRequest("calculator", Map.of("operation", "add", "a", 2, "b", 3)));// List and read resourcesListResourcesResult resources = client.listResources();ReadResourceResult resource = client.readResource( new ReadResourceRequest("resource://uri"));// List and use promptsListPromptsResult prompts = client.listPrompts();GetPromptResult prompt = client.getPrompt( new GetPromptRequest("greeting", Map.of("name", "Spring")));// Add/remove rootsclient.addRoot(new Root("file:///path", "description"));client.removeRoot("file:///path");// Close clientclient.closeGracefully();
Copy
// Create a sync client with custom configurationMcpSyncClient client = McpClient.sync(transport) .requestTimeout(Duration.ofSeconds(10)) .capabilities(ClientCapabilities.builder() .roots(true) // Enable roots capability .sampling() // Enable sampling capability .build()) .sampling(request -> new CreateMessageResult(response)) .build();// Initialize connectionclient.initialize();// List available toolsListToolsResult tools = client.listTools();// Call a toolCallToolResult result = client.callTool( new CallToolRequest("calculator", Map.of("operation", "add", "a", 2, "b", 3)));// List and read resourcesListResourcesResult resources = client.listResources();ReadResourceResult resource = client.readResource( new ReadResourceRequest("resource://uri"));// List and use promptsListPromptsResult prompts = client.listPrompts();GetPromptResult prompt = client.getPrompt( new GetPromptRequest("greeting", Map.of("name", "Spring")));// Add/remove rootsclient.addRoot(new Root("file:///path", "description"));client.removeRoot("file:///path");// Close clientclient.closeGracefully();
The transport layer handles the communication between MCP clients and servers, providing different implementations for various use cases. The client transport manages message serialization, connection establishment, and protocol-specific communication patterns.
Creates transport for in-process based communication
Copy
ServerParameters params = ServerParameters.builder("npx") .args("-y", "@modelcontextprotocol/server-everything", "dir") .build();McpTransport transport = new StdioClientTransport(params);
Creates transport for in-process based communication
Copy
ServerParameters params = ServerParameters.builder("npx") .args("-y", "@modelcontextprotocol/server-everything", "dir") .build();McpTransport transport = new StdioClientTransport(params);
Creates a framework agnostic (pure Java API) SSE client transport. Included in the core mcp module.
Copy
McpTransport transport = new HttpClientSseClientTransport("http://your-mcp-server");
Creates WebFlux-based SSE client transport. Requires the mcp-webflux-sse-transport dependency.
Copy
WebClient.Builder webClientBuilder = WebClient.builder() .baseUrl("http://your-mcp-server");McpTransport transport = new WebFluxSseClientTransport(webClientBuilder);
The client can be configured with various capabilities:
Copy
var capabilities = ClientCapabilities.builder() .roots(true) // Enable filesystem roots support with list changes notifications .sampling() // Enable LLM sampling support .build();
Roots define the boundaries of where servers can operate within the filesystem:
Copy
// Add a root dynamicallyclient.addRoot(new Root("file:///path", "description"));// Remove a rootclient.removeRoot("file:///path");// Notify server of roots changesclient.rootsListChangedNotification();
The roots capability allows servers to:
Request the list of accessible filesystem roots
Receive notifications when the roots list changes
Understand which directories and files they have access to
The client can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages:
Copy
var mcpClient = McpClient.sync(transport) .loggingConsumer(notification -> { System.out.println("Received log message: " + notification.data()); }) .build();mcpClient.initialize();mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO);// Call the tool that can sends logging notificationsCallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of()));
Clients can control the minimum logging level they receive through the mcpClient.setLoggingLevel(level) request. Messages below the set level will be filtered out.
Supported logging levels (in order of increasing severity): DEBUG (0), INFO (1), NOTICE (2), WARNING (3), ERROR (4), CRITICAL (5), ALERT (6), EMERGENCY (7)
Tools are server-side functions that clients can discover and execute. The MCP client provides methods to list available tools and execute them with specific parameters. Each tool has a unique name and accepts a map of parameters.
Copy
// List available tools and their namesvar tools = client.listTools();tools.forEach(tool -> System.out.println(tool.getName()));// Execute a tool with parametersvar result = client.callTool("calculator", Map.of( "operation", "add", "a", 1, "b", 2));
Copy
// List available tools and their namesvar tools = client.listTools();tools.forEach(tool -> System.out.println(tool.getName()));// Execute a tool with parametersvar result = client.callTool("calculator", Map.of( "operation", "add", "a", 1, "b", 2));
Copy
// List available tools asynchronouslyclient.listTools() .doOnNext(tools -> tools.forEach(tool -> System.out.println(tool.getName()))) .subscribe();// Execute a tool asynchronouslyclient.callTool("calculator", Map.of( "operation", "add", "a", 1, "b", 2 )) .subscribe();
Resources represent server-side data sources that clients can access using URI templates. The MCP client provides methods to discover available resources and retrieve their contents through a standardized interface.
Copy
// List available resources and their namesvar resources = client.listResources();resources.forEach(resource -> System.out.println(resource.getName()));// Retrieve resource content using a URI templatevar content = client.getResource("file", Map.of( "path", "/path/to/file.txt"));
Copy
// List available resources and their namesvar resources = client.listResources();resources.forEach(resource -> System.out.println(resource.getName()));// Retrieve resource content using a URI templatevar content = client.getResource("file", Map.of( "path", "/path/to/file.txt"));
Copy
// List available resources asynchronouslyclient.listResources() .doOnNext(resources -> resources.forEach(resource -> System.out.println(resource.getName()))) .subscribe();// Retrieve resource content asynchronouslyclient.getResource("file", Map.of( "path", "/path/to/file.txt" )) .subscribe();
The prompt system enables interaction with server-side prompt templates. These templates can be discovered and executed with custom parameters, allowing for dynamic text generation based on predefined patterns.
Copy
// List available prompt templatesvar prompts = client.listPrompts();prompts.forEach(prompt -> System.out.println(prompt.getName()));// Execute a prompt template with parametersvar response = client.executePrompt("echo", Map.of( "text", "Hello, World!"));
Copy
// List available prompt templatesvar prompts = client.listPrompts();prompts.forEach(prompt -> System.out.println(prompt.getName()));// Execute a prompt template with parametersvar response = client.executePrompt("echo", Map.of( "text", "Hello, World!"));
Copy
// List available prompt templates asynchronouslyclient.listPrompts() .doOnNext(prompts -> prompts.forEach(prompt -> System.out.println(prompt.getName()))) .subscribe();// Execute a prompt template asynchronouslyclient.executePrompt("echo", Map.of( "text", "Hello, World!" )) .subscribe();
As part of the Completion capabilities, MCP provides a standardized way for servers to offer argument autocompletion suggestions for prompts and resource URIs.
On the client side, the MCP client provides methods to request auto-completions:
Copy
CompleteRequest request = new CompleteRequest( new PromptReference("code_review"), new CompleteRequest.CompleteArgument("language", "py"));CompleteResult result = syncMcpClient.completeCompletion(request);
Copy
CompleteRequest request = new CompleteRequest( new PromptReference("code_review"), new CompleteRequest.CompleteArgument("language", "py"));CompleteResult result = syncMcpClient.completeCompletion(request);
Copy
CompleteRequest request = new CompleteRequest( new PromptReference("code_review"), new CompleteRequest.CompleteArgument("language", "py"));Mono<CompleteResult> result = mcpClient.completeCompletion(request);