Java implementation of the Model Context Protocol (MCP) client for the LangChain4j framework, enabling integration with MCP servers for tools, resources, and prompts
The langchain4j-mcp library provides a mechanism to present MCP server resources as executable tools within the LangChain4j framework. This allows chat models to discover and access resources through a standard tool interface.
// 1. Enable resources as tools
McpResourcesAsToolsPresenter presenter =
DefaultMcpResourcesAsToolsPresenter.builder()
.nameOfListResourcesTool("list_resources")
.nameOfGetResourceTool("get_resource")
.build();
// 2. Add to tool provider
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(client1, client2)
.resourcesAsToolsPresenter(presenter)
.build();
// 3. Use with AI assistant
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
// AI can now:
// - Call list_resources to discover resources
// - Call get_resource to read resource contentThe resources-as-tools feature creates two synthetic tools:
list_resources - Lists all available resources from configured MCP serversget_resource - Retrieves content from a specific resourceThis two-phase pattern (discovery → access) enables AI agents to dynamically discover what resources are available and then retrieve them as needed.
Interface for presenting MCP resources as LangChain4j tools.
package dev.langchain4j.mcp.resourcesastools;
public interface McpResourcesAsToolsPresenter {
ToolSpecification createListResourcesSpecification();
ToolExecutor createListResourcesExecutor(List<McpClient> mcpClients);
ToolSpecification createGetResourceSpecification();
ToolExecutor createGetResourceExecutor(List<McpClient> mcpClients);
}Creates a tool specification for the tool that lists available resources.
Returns: ToolSpecification - Tool spec for listing resources (never null)
Thread-safe: Yes
Creates an executor for the tool that lists available resources from multiple MCP clients.
Parameters: mcpClients (List<McpClient>, required) - List of MCP clients to query
Returns: ToolExecutor - Executor that aggregates resources from all clients (never null)
Thread-safe: Yes
Creates a tool specification for the tool that retrieves a specific resource.
Returns: ToolSpecification - Tool spec for getting a resource (never null)
Thread-safe: Yes
Creates an executor for the tool that retrieves a specific resource from an MCP client.
Parameters: mcpClients (List<McpClient>, required) - List of MCP clients that can provide resources
Returns: ToolExecutor - Executor that looks up and retrieves resources (never null)
Thread-safe: Yes
Default implementation that creates two tools with customizable names and descriptions.
package dev.langchain4j.mcp.resourcesastools;
public class DefaultMcpResourcesAsToolsPresenter implements McpResourcesAsToolsPresenter {
static Builder builder();
}public static final String DEFAULT_NAME_OF_GET_RESOURCE_TOOL = "get_resource";
public static final String DEFAULT_DESCRIPTION_OF_GET_RESOURCE_TOOL =
"Retrieves a resource identified by the MCP server name and the resource URI. " +
"The 'list_resources' tool should be called before this one to obtain the list.";
public static final String DEFAULT_DESCRIPTION_OF_MCP_SERVER_PARAMETER_OF_GET_RESOURCE_TOOL =
"The name of the MCP server to get the resource from.";
public static final String DEFAULT_DESCRIPTION_OF_URI_PARAMETER_OF_GET_RESOURCE_TOOL =
"The URI of the resource to get.";
public static final String DEFAULT_NAME_OF_LIST_RESOURCES_TOOL = "list_resources";
public static final String DEFAULT_DESCRIPTION_OF_LIST_RESOURCES_TOOL =
"Lists all available resources.";class Builder {
Builder nameOfGetResourceTool(String name);
Builder descriptionOfGetResourceTool(String description);
Builder descriptionOfMcpServerParameterOfGetResourceTool(String description);
Builder descriptionOfUriParameterOfGetResourceTool(String description);
Builder nameOfListResourcesTool(String name);
Builder descriptionOfListResourcesTool(String description);
DefaultMcpResourcesAsToolsPresenter build();
}Customizes the name of the get resource tool.
Parameters: name (String, nullable) - Custom tool name
Returns: Builder
Default: "get_resource"
Customizes the description of the get resource tool.
Parameters: description (String, nullable) - Custom tool description
Returns: Builder
Default: Standard description
Customizes the description of the MCP server parameter.
Parameters: description (String, nullable) - Custom parameter description
Returns: Builder
Customizes the description of the URI parameter.
Parameters: description (String, nullable) - Custom parameter description
Returns: Builder
Customizes the name of the list resources tool.
Parameters: name (String, nullable) - Custom tool name
Returns: Builder
Default: "list_resources"
Customizes the description of the list resources tool.
Parameters: description (String, nullable) - Custom tool description
Returns: Builder
Default: Standard description
import dev.langchain4j.mcp.resourcesastools.DefaultMcpResourcesAsToolsPresenter;
// Create MCP clients
McpClient aliceClient = DefaultMcpClient.builder()
.transport(aliceTransport)
.key("alice")
.build();
McpClient bobClient = DefaultMcpClient.builder()
.transport(bobTransport)
.key("bob")
.build();
// Create resources-as-tools presenter (using defaults)
DefaultMcpResourcesAsToolsPresenter presenter =
DefaultMcpResourcesAsToolsPresenter.builder().build();
// Create tool provider with resources presented as tools
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(aliceClient, bobClient)
.resourcesAsToolsPresenter(presenter)
.build();
// Use with AI assistant
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
// The tool provider now exposes:
// - All tools from alice and bob servers
// - list_resources synthetic tool
// - get_resource synthetic toolDefaultMcpResourcesAsToolsPresenter presenter =
DefaultMcpResourcesAsToolsPresenter.builder()
.nameOfListResourcesTool("show_available_data")
.descriptionOfListResourcesTool("Shows all available data sources from connected servers")
.nameOfGetResourceTool("fetch_data")
.descriptionOfGetResourceTool("Fetches data from a specific source")
.descriptionOfMcpServerParameterOfGetResourceTool("The server identifier")
.descriptionOfUriParameterOfGetResourceTool("The data source identifier")
.build();
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(client)
.resourcesAsToolsPresenter(presenter)
.build();
// AI can now use:
// - show_available_data (instead of list_resources)
// - fetch_data (instead of get_resource)When the list_resources tool is called, it returns a JSON array of all resources from all configured MCP servers:
[
{
"mcpServer": "alice",
"uri": "file:///info",
"uriTemplate": null,
"name": "basicInfo",
"description": "Basic information about Alice",
"mimeType": "text/plain"
},
{
"mcpServer": "alice",
"uri": null,
"uriTemplate": "file:///data/{id}",
"name": "dataTemplate",
"description": "Access data by ID",
"mimeType": "application/json"
},
{
"mcpServer": "bob",
"uri": "file:///info",
"uriTemplate": null,
"name": "basicInfo",
"description": "Basic information about Bob",
"mimeType": "text/plain"
}
]Response Fields:
mcpServer (string, never null) - The key/name of the MCP client that provides this resourceuri (string, nullable) - The URI of a concrete resource (null for templates)uriTemplate (string, nullable) - The URI template for dynamic resources (null for concrete resources)name (string, never null) - The resource namedescription (string, nullable) - A description of what the resource providesmimeType (string, nullable) - The MIME type of the resource contentAfter discovering resources, use the get_resource tool to retrieve content:
Tool Parameters:
{
"mcpServer": "alice",
"uri": "file:///info"
}Returns: The text content of the resource
Important: Only text resources are supported. Binary resources will throw ToolExecutionException.
interface DataAgent {
String chat(String userMessage);
}
// Build agent with MCP tool provider that includes resources-as-tools
McpToolProvider toolProvider = /* ... configured with resources-as-tools ... */;
DataAgent agent = AiServices.builder(DataAgent.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
// Agent discovers and accesses resources
String response = agent.chat("What resources are available from the Alice server?");
// Agent calls list_resources tool and reports results
String info = agent.chat("Get the basic info from Alice");
// Agent calls get_resource with mcpServer="alice", uri="file:///info"
// Returns the resource contentThe presenter creates two tool executors:
Aggregates resources from all configured MCP clients and returns them as a JSON array.
Behavior:
listResources() and listResourceTemplates()Error Handling: Throws ToolExecutionException on errors querying clients or JSON serialization failures
Retrieves a specific resource by server key and URI.
Behavior:
mcpServer and uri arguments are providedmcpServer keyreadResource(uri) on the clientValidation:
ToolArgumentsException if mcpServer argument is missingToolArgumentsException if uri argument is missingToolArgumentsException if MCP server key is unknownLimitation: Only supports text resources. Binary resources throw ToolExecutionException.
Resources can be static (with a fixed URI) or dynamic (with a URI template):
{
"mcpServer": "alice",
"uri": "file:///config",
"uriTemplate": null,
"name": "config",
"description": "Configuration file",
"mimeType": "application/json"
}To access: get_resource(mcpServer="alice", uri="file:///config")
{
"mcpServer": "alice",
"uri": null,
"uriTemplate": "file:///users/{userId}",
"name": "userProfile",
"description": "User profile by ID",
"mimeType": "application/json"
}To access: get_resource(mcpServer="alice", uri="file:///users/123")
The agent must construct the URI by filling in the template parameters.
Resources-as-tools is integrated into McpToolProvider via the builder:
// With resources as tools
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient1, mcpClient2)
.resourcesAsToolsPresenter(DefaultMcpResourcesAsToolsPresenter.builder().build())
.build();
// The tool provider now exposes:
// 1. All tools from the MCP servers
// 2. list_resources synthetic tool
// 3. get_resource synthetic toolWithout a presenter:
// Only MCP server tools are exposed, resources are NOT available as tools
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient1, mcpClient2)
.build();You can implement McpResourcesAsToolsPresenter to customize how resources are presented:
import dev.langchain4j.mcp.resourcesastools.McpResourcesAsToolsPresenter;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.agent.tool.ToolExecutor;
import dev.langchain4j.mcp.client.McpClient;
import java.util.List;
public class CustomResourcePresenter implements McpResourcesAsToolsPresenter {
@Override
public ToolSpecification createListResourcesSpecification() {
// Custom tool spec with different name/description/parameters
return ToolSpecification.builder()
.name("discover_data_sources")
.description("Discovers all available data sources")
// Custom parameters
.build();
}
@Override
public ToolExecutor createListResourcesExecutor(List<McpClient> mcpClients) {
// Custom executor with filtering or transformation logic
return new CustomListExecutor(mcpClients);
}
@Override
public ToolSpecification createGetResourceSpecification() {
// Custom tool spec
return ToolSpecification.builder()
.name("retrieve_data")
.description("Retrieves data from a source")
// Custom parameters
.build();
}
@Override
public ToolExecutor createGetResourceExecutor(List<McpClient> mcpClients) {
// Custom executor with caching or binary support
return new CustomGetExecutor(mcpClients);
}
}Filter which resources are exposed by wrapping the presenter:
public class FilteredResourcePresenter extends DefaultMcpResourcesAsToolsPresenter {
@Override
public ToolExecutor createListResourcesExecutor(List<McpClient> mcpClients) {
ToolExecutor delegate = super.createListResourcesExecutor(mcpClients);
return (request, context) -> {
ToolExecutionResult result = delegate.executeWithContext(request, context);
// Parse, filter, and return modified JSON
String filteredJson = filterResources(result.resultText());
return ToolExecutionResult.builder()
.resultText(filteredJson)
.build();
};
}
private String filterResources(String json) {
// Filter by MIME type, server, etc.
// Parse JSON, filter, serialize back
return json;
}
}Binary Content Not Supported: The default GetResourceToolExecutor only supports text resources. Binary resources throw ToolExecutionException.
No Streaming: Resources are read completely into memory as strings.
No Caching: Resources are fetched every time get_resource is called.
No Template Validation: When using resource templates, the presenter doesn't validate that the URI matches the template pattern.
To address these limitations, implement a custom McpResourcesAsToolsPresenter.
import dev.langchain4j.mcp.*;
import dev.langchain4j.mcp.client.*;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
import dev.langchain4j.mcp.resourcesastools.DefaultMcpResourcesAsToolsPresenter;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.model.chat.ChatLanguageModel;
public class ResourcesAsToolsExample {
interface Assistant {
String chat(String message);
}
public static void main(String[] args) {
// 1. Create MCP clients for multiple servers
McpClient aliceServer = DefaultMcpClient.builder()
.transport(StdioMcpTransport.builder()
.command(List.of("alice-mcp-server"))
.build())
.key("alice")
.clientName("MyApp")
.build();
McpClient bobServer = DefaultMcpClient.builder()
.transport(StdioMcpTransport.builder()
.command(List.of("bob-mcp-server"))
.build())
.key("bob")
.clientName("MyApp")
.build();
// 2. Configure resources-as-tools presenter
DefaultMcpResourcesAsToolsPresenter presenter =
DefaultMcpResourcesAsToolsPresenter.builder()
.nameOfListResourcesTool("list_resources")
.nameOfGetResourceTool("get_resource")
.build();
// 3. Create tool provider with resources exposed as tools
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(aliceServer, bobServer)
.resourcesAsToolsPresenter(presenter)
.build();
// 4. Create AI agent with the tool provider
ChatLanguageModel model = /* ... configure your model ... */;
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
// 5. Agent can now discover and access resources
String response = assistant.chat(
"What resources are available? Show me Alice's basic info."
);
System.out.println(response);
// The agent will:
// 1. Call list_resources to discover available resources
// 2. See that Alice has a "basicInfo" resource at "file:///info"
// 3. Call get_resource(mcpServer="alice", uri="file:///info")
// 4. Return the content to the user
// 6. Cleanup
aliceServer.close();
bobServer.close();
}
}import dev.langchain4j.agent.tool.ToolExecutionException;
import dev.langchain4j.agent.tool.ToolArgumentsException;
try {
// Agent calls tools
String response = assistant.chat("Get resource X");
} catch (ToolArgumentsException e) {
// Missing or invalid arguments (mcpServer, uri)
System.err.println("Invalid tool arguments: " + e.getMessage());
} catch (ToolExecutionException e) {
// Resource not found, binary resource, or other execution error
System.err.println("Tool execution failed: " + e.getMessage());
}get_resource call fetches from serverInstall with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-mcp@1.11.0