Java implementation of the Model Context Protocol (MCP) client for the LangChain4j framework, enabling integration with MCP servers for tools, resources, and prompts
The MCP client is the core interface for communicating with MCP servers. It provides methods for listing and executing tools, accessing resources, managing prompts, and monitoring server health.
// Create client
McpClient client = DefaultMcpClient.builder()
.transport(transport) // Required
.key("my-server") // Optional: unique identifier
.toolExecutionTimeout(Duration.ofSeconds(60))
.build();
// List and execute tools
List<ToolSpecification> tools = client.listTools();
ToolExecutionResult result = client.executeTool(request);
// Access resources
List<McpResource> resources = client.listResources();
McpReadResourceResult content = client.readResource("file:///path");
// Work with prompts
List<McpPrompt> prompts = client.listPrompts();
McpGetPromptResult prompt = client.getPrompt("name", args);
// Health check
client.checkHealth();
// Cleanup
client.close();import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.invocation.InvocationContext;
import dev.langchain4j.agent.tool.ToolExecutionRequest;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.service.tool.ToolExecutionResult;
import java.time.Duration;
import java.util.List;
import java.util.Map;The McpClient interface defines the contract for MCP server communication.
interface McpClient extends AutoCloseable {
// Identity
String key();
// Tools
List<ToolSpecification> listTools();
List<ToolSpecification> listTools(InvocationContext invocationContext);
ToolExecutionResult executeTool(ToolExecutionRequest executionRequest);
ToolExecutionResult executeTool(ToolExecutionRequest executionRequest, InvocationContext invocationContext);
// Resources
List<McpResource> listResources();
List<McpResource> listResources(InvocationContext invocationContext);
List<McpResourceTemplate> listResourceTemplates();
List<McpResourceTemplate> listResourceTemplates(InvocationContext invocationContext);
McpReadResourceResult readResource(String uri);
McpReadResourceResult readResource(String uri, InvocationContext invocationContext);
// Prompts
List<McpPrompt> listPrompts();
List<McpPrompt> listPrompts(InvocationContext invocationContext);
McpGetPromptResult getPrompt(String name, Map<String, Object> arguments);
// Management
void checkHealth();
void setRoots(List<McpRoot> roots);
void close();
}Returns the unique identifier for this client.
Returns: String - Client's unique key
Nullability: Never null, defaults to generated value if not set
Thread-safe: Yes
Usage:
String clientKey = client.key();
System.out.println("Client: " + clientKey);Retrieves the list of tools available on the MCP server. Results are cached by default.
Returns: List<ToolSpecification> - List of available tools (never null, may be empty)
Throws: McpException - If the server returns an error
Throws: IllegalResponseException - If the response structure is invalid
Caching: Enabled by default, use evictToolListCache() to clear
Thread-safe: Yes
When to use:
evictToolListCache() to refresh tool listExample:
List<ToolSpecification> tools = client.listTools();
for (ToolSpecification tool : tools) {
System.out.println("Tool: " + tool.name());
System.out.println("Description: " + tool.description());
}Retrieves tools with invocation context for tracing and monitoring.
Parameters:
invocationContext (InvocationContext, nullable) - Context for the invocationReturns: List<ToolSpecification> - List of available tools
Throws: Same as listTools()
Thread-safe: Yes
When to use:
Executes a tool on the MCP server. Supports text-based results and JSON structured content.
Parameters:
executionRequest (ToolExecutionRequest, required) - Request containing tool name and arguments as JSON stringReturns: ToolExecutionResult - Result of tool execution (never null)
Throws: McpException - If the tool execution fails or times out
Throws: IllegalArgumentException - If request is null or tool name is missing
Timeout: Configured via toolExecutionTimeout (default 60 seconds)
Thread-safe: Yes
Important Notes:
Example:
ToolExecutionRequest request = ToolExecutionRequest.builder()
.name("get_weather")
.arguments("{\"location\": \"San Francisco\"}")
.build();
try {
ToolExecutionResult result = client.executeTool(request);
System.out.println("Result: " + result.text());
} catch (McpException e) {
System.err.println("Tool failed: " + e.errorMessage());
}Executes a tool with invocation context for tracing.
Parameters:
executionRequest (ToolExecutionRequest, required)invocationContext (InvocationContext, nullable) - Context for tracingReturns: ToolExecutionResult
Throws: Same as executeTool(ToolExecutionRequest)
Thread-safe: Yes
Retrieves the list of static resources available on the MCP server. Does not include resource templates.
Returns: List<McpResource> - List of static resources (never null, may be empty)
Throws: McpException - If the server returns an error
Throws: IllegalResponseException - If response is missing required fields
Timeout: Configured via resourcesTimeout (default 60 seconds)
Thread-safe: Yes
Example:
List<McpResource> resources = client.listResources();
for (McpResource resource : resources) {
System.out.println("Resource: " + resource.name());
System.out.println("URI: " + resource.uri());
System.out.println("MIME Type: " + resource.mimeType());
}Retrieves resources with invocation context.
Parameters:
invocationContext (InvocationContext, nullable)Returns: List<McpResource>
Throws: Same as listResources()
Retrieves the list of resource templates (dynamic resources with URI patterns).
Returns: List<McpResourceTemplate> - List of templates (never null, may be empty)
Throws: McpException - If the server returns an error
Throws: IllegalResponseException - If response structure is invalid
Thread-safe: Yes
URI Template Patterns:
file:///{userId}/profile.jsonfile:///12345/profile.jsonExample:
List<McpResourceTemplate> templates = client.listResourceTemplates();
for (McpResourceTemplate template : templates) {
System.out.println("Template: " + template.name());
System.out.println("URI Pattern: " + template.uriTemplate());
}
// Read using template (substitute placeholders)
McpReadResourceResult result = client.readResource("file:///12345/profile.json");Retrieves resource templates with invocation context.
Parameters:
invocationContext (InvocationContext, nullable)Returns: List<McpResourceTemplate>
Retrieves the contents of a resource by its URI. Works for both static resources and dynamic resources (templates with placeholders substituted).
Parameters:
uri (String, required) - The URI of the resource (must not be null or empty)Returns: McpReadResourceResult - Resource contents (never null, but may contain empty content list)
Throws: McpException - If the resource cannot be read or does not exist
Throws: IllegalArgumentException - If URI is null or empty
Timeout: Configured via resourcesTimeout (default 60 seconds)
Thread-safe: Yes
Content Types:
McpTextResourceContents with text() methodMcpBlobResourceContents with base64-encoded blob() methodExample:
McpReadResourceResult result = client.readResource("file:///path/to/file.txt");
for (McpResourceContents contents : result.contents()) {
if (contents instanceof McpTextResourceContents text) {
System.out.println("Text: " + text.text());
} else if (contents instanceof McpBlobResourceContents blob) {
System.out.println("Binary data (base64): " + blob.blob());
}
}Retrieves resource contents with invocation context.
Parameters:
uri (String, required)invocationContext (InvocationContext, nullable)Returns: McpReadResourceResult
Throws: Same as readResource(String)
Retrieves the list of prompt templates available on the MCP server.
Returns: List<McpPrompt> - List of prompts (never null, may be empty)
Throws: McpException - If the server returns an error
Throws: IllegalResponseException - If response structure is invalid
Timeout: Configured via promptsTimeout (default 60 seconds)
Thread-safe: Yes
Example:
List<McpPrompt> prompts = client.listPrompts();
for (McpPrompt prompt : prompts) {
System.out.println("Prompt: " + prompt.name());
System.out.println("Description: " + prompt.description());
if (prompt.arguments() != null) {
for (McpPromptArgument arg : prompt.arguments()) {
String req = arg.required() ? "required" : "optional";
System.out.println(" - " + arg.name() + " (" + req + ")");
}
}
}Retrieves prompts with invocation context.
Parameters:
invocationContext (InvocationContext, nullable)Returns: List<McpPrompt>
Renders a prompt template with the specified arguments.
Parameters:
name (String, required) - The name of the prompt (must not be null)arguments (Map<String, Object>, nullable) - Arguments for rendering (null treated as empty map)Returns: McpGetPromptResult - Rendered prompt messages (never null)
Throws: McpException - If prompt not found, required arguments missing, or rendering fails
Throws: IllegalArgumentException - If name is null
Timeout: Configured via promptsTimeout (default 60 seconds)
Thread-safe: Yes
Argument Requirements:
Example:
Map<String, Object> args = Map.of(
"topic", "artificial intelligence",
"length", "short",
"style", "formal"
);
try {
McpGetPromptResult promptResult = client.getPrompt("essay_prompt", args);
System.out.println("Description: " + promptResult.description());
for (McpPromptMessage message : promptResult.messages()) {
System.out.println("Role: " + message.role());
if (message.content() instanceof McpTextContent text) {
System.out.println("Content: " + text.text());
}
}
} catch (McpException e) {
System.err.println("Prompt error: " + e.errorMessage());
}Performs a health check by sending a ping request to the MCP server.
Returns: Nothing (void) - Returns normally if healthy
Throws: Exception - If the server is not responding or unhealthy
Timeout: Configured via pingTimeout (default 10 seconds)
Thread-safe: Yes
When to use:
Example:
try {
client.checkHealth();
System.out.println("Server is healthy");
} catch (Exception e) {
System.err.println("Server health check failed: " + e.getMessage());
// Consider reconnection or failover
}Sets the roots (directories/locations) made available to the server. After setting, automatically sends notifications/roots/list_changed to the server.
Parameters:
roots (List<McpRoot>, required) - List of roots (must not be null, but can be empty)Returns: Nothing (void)
Throws: McpException - If notification fails
Side Effects: Sends notification to server
Thread-safe: Yes
When to use:
Example:
List<McpRoot> roots = List.of(
new McpRoot("workspace", "file:///home/user/workspace"),
new McpRoot("documents", "file:///home/user/documents")
);
client.setRoots(roots);Closes the client connection and releases resources. After closing, the client cannot be reused.
Returns: Nothing (void) Throws: Exception - If error occurs during closing Idempotent: Safe to call multiple times Thread-safe: Yes
Important: Always close clients to prevent resource leaks.
Example:
// Explicit close
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build();
try {
// Use client
client.listTools();
} finally {
client.close();
}
// Preferred: try-with-resources
try (McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build()) {
client.listTools();
} // Automatically closedThe default implementation of the McpClient interface.
class DefaultMcpClient implements McpClient {
static Builder builder();
void evictToolListCache();
}Manually evicts the cached tool list, forcing the next listTools() call to fetch fresh data from the server.
Returns: Nothing (void) Thread-safe: Yes
When to use:
Example:
// Cache is populated
List<ToolSpecification> tools1 = client.listTools(); // Fetches from server
// Subsequent call uses cache
List<ToolSpecification> tools2 = client.listTools(); // Returns cached
// Evict cache
client.evictToolListCache();
// Next call fetches fresh
List<ToolSpecification> tools3 = client.listTools(); // Fetches from serverThe DefaultMcpClient.Builder provides a fluent API for configuring and creating MCP clients.
class Builder {
Builder transport(McpTransport transport);
Builder key(String key);
Builder clientName(String clientName);
Builder clientVersion(String clientVersion);
Builder protocolVersion(String protocolVersion);
Builder initializationTimeout(Duration initializationTimeout);
Builder toolExecutionTimeout(Duration toolExecutionTimeout);
Builder resourcesTimeout(Duration resourcesTimeout);
Builder promptsTimeout(Duration promptsTimeout);
Builder toolExecutionTimeoutErrorMessage(String message);
Builder logHandler(McpLogMessageHandler logHandler);
Builder pingTimeout(Duration pingTimeout);
Builder reconnectInterval(Duration reconnectInterval);
Builder autoHealthCheck(boolean autoHealthCheck);
Builder autoHealthCheckInterval(Duration interval);
Builder roots(List<McpRoot> roots);
Builder cacheToolList(boolean cacheToolList);
Builder listener(McpClientListener listener);
DefaultMcpClient build();
}Sets the transport implementation for server communication. This is required.
Parameters:
transport (McpTransport, required) - The transport implementationReturns: Builder Default: None (must be set)
Transport Options:
StdioMcpTransport - For local subprocess serversWebSocketMcpTransport - For remote WebSocket serversStreamableHttpMcpTransport - For remote HTTP serversExample:
StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("node", "server.js"))
.build();
McpClient client = DefaultMcpClient.builder()
.transport(transport) // Required
.build();Sets the unique identifier for this client. Used when managing multiple MCP clients.
Parameters:
key (String, nullable) - Unique client identifierReturns: Builder Default: Auto-generated UUID if not set Uniqueness: Should be unique across all clients in your application
When to use:
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.key("weather-server")
.build();Sets the client name sent during MCP handshake for server identification.
Parameters:
clientName (String, nullable) - Client nameReturns: Builder Default: "LangChain4j MCP Client"
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.clientName("MyApplication")
.build();Sets the client version sent during MCP handshake.
Parameters:
clientVersion (String, nullable) - Client versionReturns: Builder Default: Library version
Sets the MCP protocol version to use.
Parameters:
protocolVersion (String, nullable) - Protocol version stringReturns: Builder Default: "2025-11-25" Compatibility: Must match or be compatible with server version
When to change:
Sets the timeout for MCP connection initialization.
Parameters:
initializationTimeout (Duration, nullable) - Timeout durationReturns: Builder Default: 30 seconds Valid Range: 1 second to 10 minutes recommended
When to adjust:
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.initializationTimeout(Duration.ofSeconds(60))
.build();Sets the timeout for tool execution requests.
Parameters:
toolExecutionTimeout (Duration, nullable) - Timeout durationReturns: Builder Default: 60 seconds Valid Range: 1 second to several minutes depending on tool
When to adjust:
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.toolExecutionTimeout(Duration.ofMinutes(5)) // Long-running tools
.build();Sets the timeout for resource operations (list, read).
Parameters:
resourcesTimeout (Duration, nullable) - Timeout durationReturns: Builder Default: 60 seconds
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.resourcesTimeout(Duration.ofSeconds(30))
.build();Sets the timeout for prompt operations (list, get).
Parameters:
promptsTimeout (Duration, nullable) - Timeout durationReturns: Builder Default: 60 seconds
Sets a custom error message returned when tool execution times out.
Parameters:
message (String, nullable) - Custom timeout error messageReturns: Builder Default: Standard timeout message
When to use:
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.toolExecutionTimeoutErrorMessage(
"Tool execution timed out. Try again or check server status.")
.build();Sets the handler for log messages received from the MCP server.
Parameters:
logHandler (McpLogMessageHandler, nullable) - Log message handlerReturns: Builder Default: No handler (logs ignored)
Handler Options:
DefaultMcpLogMessageHandler - Forwards to SLF4JExample:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.logHandler(new DefaultMcpLogMessageHandler())
.build();Sets the timeout for ping requests used in health checks.
Parameters:
pingTimeout (Duration, nullable) - Ping timeoutReturns: Builder Default: 10 seconds
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.pingTimeout(Duration.ofSeconds(5))
.build();Sets the interval for reconnection attempts after connection failure.
Parameters:
reconnectInterval (Duration, nullable) - Reconnect intervalReturns: Builder Default: 5 seconds
Enables or disables automatic periodic health checks.
Parameters:
autoHealthCheck (boolean) - Enable auto health checksReturns: Builder Default: true (enabled)
When to disable:
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.autoHealthCheck(true)
.autoHealthCheckInterval(Duration.ofSeconds(30))
.build();Sets the interval between automatic health checks.
Parameters:
interval (Duration, nullable) - Health check intervalReturns: Builder Default: 30 seconds
Sets the initial list of roots to make available to the server.
Parameters:
roots (List<McpRoot>, nullable) - Initial rootsReturns: Builder Default: Empty list
Example:
List<McpRoot> roots = List.of(
new McpRoot("workspace", "file:///home/user/workspace"),
new McpRoot("documents", "file:///home/user/documents")
);
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.roots(roots)
.build();Enables or disables caching of the tool list.
Parameters:
cacheToolList (boolean) - Enable cachingReturns: Builder Default: true (enabled)
When to disable:
Example:
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.cacheToolList(false) // Always fetch fresh
.build();Sets the event listener for client lifecycle events.
Parameters:
listener (McpClientListener, nullable) - Event listenerReturns: Builder Default: No listener
Use Cases:
Example:
McpClientListener listener = new McpClientListener() {
@Override
public void beforeExecuteTool(McpCallContext context) {
System.out.println("Executing tool...");
}
@Override
public void afterExecuteTool(McpCallContext context,
ToolExecutionResult result,
Map<String, Object> rawResult) {
System.out.println("Tool executed successfully");
}
@Override
public void onExecuteToolError(McpCallContext context, Throwable error) {
System.err.println("Tool failed: " + error.getMessage());
}
};
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.listener(listener)
.build();Builds and returns the configured DefaultMcpClient instance.
Returns: DefaultMcpClient Throws: IllegalArgumentException - If transport is not set
Example:
try {
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build();
} catch (IllegalArgumentException e) {
System.err.println("Invalid configuration: " + e.getMessage());
}import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
import java.util.List;
StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("node", "mcp-server.js"))
.build();
try (McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build()) {
List<ToolSpecification> tools = client.listTools();
System.out.println("Available tools: " + tools.size());
}import java.time.Duration;
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.key("production-server")
.clientName("MyApplication")
.clientVersion("1.0.0")
// Timeouts
.initializationTimeout(Duration.ofSeconds(60))
.toolExecutionTimeout(Duration.ofMinutes(5))
.resourcesTimeout(Duration.ofSeconds(30))
.promptsTimeout(Duration.ofSeconds(30))
.pingTimeout(Duration.ofSeconds(10))
// Health checks
.autoHealthCheck(true)
.autoHealthCheckInterval(Duration.ofSeconds(30))
// Caching
.cacheToolList(true)
// Monitoring
.listener(new PerformanceMonitoringListener())
.logHandler(new DefaultMcpLogMessageHandler())
// Initial roots
.roots(List.of(
new McpRoot("workspace", "file:///app/workspace"),
new McpRoot("data", "file:///app/data")
))
.build();try (McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build()) {
// Execute tool with error handling
ToolExecutionRequest request = ToolExecutionRequest.builder()
.name("process_data")
.arguments("{\"file\": \"data.csv\"}")
.build();
ToolExecutionResult result = client.executeTool(request);
System.out.println("Success: " + result.text());
} catch (McpException e) {
// MCP protocol error
System.err.println("MCP Error " + e.errorCode() + ": " + e.errorMessage());
if (e.errorCode() == -32601) {
System.err.println("Tool not found");
} else if (e.errorCode() == -32602) {
System.err.println("Invalid parameters");
}
} catch (IllegalResponseException e) {
// Malformed response
System.err.println("Invalid response: " + e.getMessage());
} catch (Exception e) {
// Network, timeout, or other errors
System.err.println("Client error: " + e.getMessage());
}McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build();
// Set initial roots
client.setRoots(List.of(
new McpRoot("workspace", "file:///home/user/workspace")
));
// Later, add more roots
List<McpRoot> updatedRoots = List.of(
new McpRoot("workspace", "file:///home/user/workspace"),
new McpRoot("projects", "file:///home/user/projects"),
new McpRoot("documents", "file:///home/user/documents")
);
client.setRoots(updatedRoots);// Client with caching enabled (default)
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.cacheToolList(true)
.build();
// First call fetches from server
List<ToolSpecification> tools1 = client.listTools();
System.out.println("Fetched from server: " + tools1.size());
// Second call returns cached result
List<ToolSpecification> tools2 = client.listTools();
System.out.println("From cache: " + tools2.size());
// Server capabilities changed, refresh cache
client.evictToolListCache();
// Next call fetches fresh data
List<ToolSpecification> tools3 = client.listTools();
System.out.println("Fresh from server: " + tools3.size());Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-mcp@1.11.0