Java implementation of the Model Context Protocol (MCP) client for the LangChain4j framework, enabling integration with MCP servers for tools, resources, and prompts
The McpToolProvider integrates MCP server tools into LangChain4j's agent framework. It provides dynamic tool discovery, filtering, and execution capabilities for AI assistants.
// Create tool provider
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(client1, client2) // Add clients
.filterToolNames("read_file", "write_file") // Filter by name
.filter((client, tool) -> !tool.name().contains("delete")) // Custom filter
.toolNameMapper((client, tool) -> client.key() + "_" + tool.name()) // Rename tools
.failIfOneServerFails(false) // Resilient mode
.build();
// Use with LangChain4j
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
// Dynamic management
toolProvider.addMcpClient(newClient);
toolProvider.removeMcpClient(oldClient);
toolProvider.addFilter(customFilter);import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.McpToolExecutor;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.McpToolMetadataKeys;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.service.tool.ToolProvider;
import dev.langchain4j.service.tool.ToolProviderRequest;
import dev.langchain4j.service.tool.ToolProviderResult;
import dev.langchain4j.service.tool.ToolExecutor;
import java.util.function.BiPredicate;
import java.util.function.BiFunction;
import java.util.function.Function;The McpToolProvider implements LangChain4j's ToolProvider interface, enabling dynamic tool provisioning from one or more MCP servers.
class McpToolProvider implements ToolProvider {
static Builder builder();
void addMcpClient(McpClient client);
void removeMcpClient(McpClient client);
void addFilter(BiPredicate<McpClient, ToolSpecification> filter);
void setFilter(BiPredicate<McpClient, ToolSpecification> filter);
void setToolNameMapper(BiFunction<McpClient, ToolSpecification, String> toolNameMapper);
void setToolSpecificationMapper(BiFunction<McpClient, ToolSpecification, ToolSpecification> toolSpecificationMapper);
void resetFilters();
ToolProviderResult provideTools(ToolProviderRequest request);
}Dynamically adds an MCP client to the provider, making its tools available.
Parameters: client (McpClient, required) - The MCP client to add
Thread-safe: Yes
When to use: Adding clients at runtime, dynamic service discovery
Removes an MCP client, removing its tools from availability.
Parameters: client (McpClient, required) - The MCP client to remove
Thread-safe: Yes
Adds a tool filter that acts in conjunction (AND) with existing filters.
Parameters: filter (BiPredicate<McpClient, ToolSpecification>, required) - Returns true to include tool
Thread-safe: Yes
Filter Order: Applied before name/specification mapping
Sets a tool filter, replacing all existing filters.
Parameters: filter (BiPredicate<McpClient, ToolSpecification>, required)
Thread-safe: Yes
Sets a custom function to map tool names. Cannot be used with setToolSpecificationMapper.
Parameters: toolNameMapper (BiFunction<McpClient, ToolSpecification, String>, required)
Thread-safe: Yes
Common Use: Prefix tool names with server key to avoid conflicts
Sets a custom function to map entire tool specifications. Cannot be used with setToolNameMapper.
Parameters: toolSpecificationMapper (BiFunction<McpClient, ToolSpecification, ToolSpecification>, required)
Thread-safe: Yes
Use Cases: Modify name, description, and parameters together
Removes all filters, allowing all tools to pass through.
Thread-safe: Yes
Provides tools to LangChain4j's AI services. Called automatically by LangChain4j.
Parameters: request (ToolProviderRequest, required)
Returns: ToolProviderResult - Tools and executors
Thread-safe: Yes
Fluent API for configuring the tool provider.
class Builder {
Builder mcpClients(List<McpClient> mcpClients);
Builder mcpClients(McpClient... mcpClients);
Builder filter(BiPredicate<McpClient, ToolSpecification> mcpToolsFilter);
Builder filterToolNames(String... toolNames);
Builder failIfOneServerFails(boolean failIfOneServerFails);
Builder toolWrapper(Function<ToolExecutor, ToolExecutor> toolWrapper);
Builder resourcesAsToolsPresenter(McpResourcesAsToolsPresenter resourcesAsToolsPresenter);
Builder toolNameMapper(BiFunction<McpClient, ToolSpecification, String> toolNameMapper);
Builder toolSpecificationMapper(BiFunction<McpClient, ToolSpecification, ToolSpecification> toolSpecificationMapper);
McpToolProvider build();
}Sets the initial MCP clients.
Parameters: mcpClients (McpClient..., varargs) - One or more clients
Returns: Builder
Default: Empty (no clients)
Convenience method to filter tools by exact name match. Only tools with specified names will be included.
Parameters: toolNames (String..., varargs) - Tool names to include
Returns: Builder
Example: filterToolNames("read_file", "write_file")
Controls behavior when one MCP server fails to respond.
Parameters: failIfOneServerFails (boolean) - Whether to fail on any server error
Returns: Builder
Default: false (resilient mode - continues with available servers)
When to use true: Strict mode for critical applications
Wraps tool executors for cross-cutting concerns like logging, tracing, rate limiting.
Parameters: toolWrapper (Function<ToolExecutor, ToolExecutor>, nullable) - Wrapper function
Returns: Builder
Use Cases: Logging, tracing, metrics, rate limiting, caching
Enables automatic presentation of MCP resources as synthetic tools (list_resources, get_resource).
Parameters: resourcesAsToolsPresenter (McpResourcesAsToolsPresenter, nullable)
Returns: Builder
Default: null (resources not exposed as tools)
See: Resources as Tools Documentation
Executes MCP tools on behalf of the tool provider.
class McpToolExecutor implements ToolExecutor {
McpToolExecutor(McpClient mcpClient);
McpToolExecutor(McpClient mcpClient, String fixedToolName);
String execute(ToolExecutionRequest executionRequest, Object memoryId);
ToolExecutionResult executeWithContext(ToolExecutionRequest executionRequest, InvocationContext invocationContext);
}Creates an executor for the given client. Tool name taken from execution request.
Parameters: mcpClient (McpClient, required)
Creates an executor with a fixed tool name. Useful when tool names are mapped.
Parameters:
mcpClient (McpClient, required)fixedToolName (String, required) - The actual tool name on the MCP serverConstants for accessing metadata from MCP tool specifications.
class McpToolMetadataKeys {
public static final String TITLE_ANNOTATION = "title-annotation";
public static final String TITLE = "title";
public static final String READ_ONLY_HINT = "readOnlyHint"; // boolean
public static final String DESTRUCTIVE_HINT = "destructiveHint"; // boolean
public static final String IDEMPOTENT_HINT = "idempotentHint"; // boolean
public static final String OPEN_WORLD_HINT = "openWorldHint"; // boolean
}ToolSpecification tool = client.listTools().get(0);
Map<String, Object> metadata = tool.metadata();
Boolean isReadOnly = (Boolean) metadata.get(McpToolMetadataKeys.READ_ONLY_HINT);
Boolean isDestructive = (Boolean) metadata.get(McpToolMetadataKeys.DESTRUCTIVE_HINT);
Boolean isIdempotent = (Boolean) metadata.get(McpToolMetadataKeys.IDEMPOTENT_HINT);
// Filter based on metadata
toolProvider.addFilter((client, toolSpec) -> {
Boolean destructive = (Boolean) toolSpec.metadata()
.get(McpToolMetadataKeys.DESTRUCTIVE_HINT);
return destructive == null || !destructive; // Exclude destructive tools
});// Create MCP client
McpClient mcpClient = DefaultMcpClient.builder()
.transport(transport)
.build();
// Create tool provider
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.build();
// Integrate with LangChain4j
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(chatLanguageModel)
.toolProvider(toolProvider)
.build();
// Use assistant - it can now call MCP tools
String response = assistant.chat("What's the weather in San Francisco?");// Create clients for different servers
McpClient weatherServer = DefaultMcpClient.builder()
.transport(weatherTransport)
.key("weather")
.build();
McpClient filesystemServer = DefaultMcpClient.builder()
.transport(filesystemTransport)
.key("filesystem")
.build();
// Combine with filtering
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(weatherServer, filesystemServer)
// Only allow read operations on filesystem
.filter((client, tool) -> {
if (client.key().equals("filesystem")) {
return tool.name().startsWith("read_") || tool.name().startsWith("list_");
}
return true; // Allow all weather tools
})
.build();// Prefix tool names with server key to avoid conflicts
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(weatherServer, filesystemServer, databaseServer)
.toolNameMapper((client, tool) ->
client.key() + "_" + tool.name())
.build();
// Results in tools like:
// - weather_get_forecast
// - filesystem_read_file
// - database_query// Start with initial clients
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(client1, client2)
.build();
// Add client dynamically
McpClient newClient = DefaultMcpClient.builder()
.transport(newTransport)
.key("new-server")
.build();
toolProvider.addMcpClient(newClient);
// Add filter dynamically
toolProvider.addFilter((client, tool) ->
client.key().equals("trusted-server"));
// Remove client when no longer needed
toolProvider.removeMcpClient(client1);Function<ToolExecutor, ToolExecutor> tracingWrapper = executor -> {
return new ToolExecutor() {
@Override
public String execute(ToolExecutionRequest request, Object memoryId) {
long start = System.currentTimeMillis();
String toolName = request.name();
logger.info("Executing tool: {}", toolName);
try {
String result = executor.execute(request, memoryId);
long duration = System.currentTimeMillis() - start;
logger.info("Tool {} completed in {}ms", toolName, duration);
return result;
} catch (Exception e) {
logger.error("Tool {} failed: {}", toolName, e.getMessage());
throw e;
}
}
@Override
public ToolExecutionResult executeWithContext(
ToolExecutionRequest request,
InvocationContext context) {
return executor.executeWithContext(request, context);
}
};
};
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.toolWrapper(tracingWrapper)
.build();// Only allow read-only, non-destructive tools
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(client)
.filter((mcpClient, toolSpec) -> {
Map<String, Object> metadata = toolSpec.metadata();
// Check if tool is read-only
Boolean readOnly = (Boolean) metadata.get(McpToolMetadataKeys.READ_ONLY_HINT);
if (readOnly != null && readOnly) {
return true;
}
// Check if tool is NOT destructive
Boolean destructive = (Boolean) metadata.get(McpToolMetadataKeys.DESTRUCTIVE_HINT);
return destructive == null || !destructive;
})
.build();import dev.langchain4j.mcp.resourcesastools.DefaultMcpResourcesAsToolsPresenter;
// Enable resources to be presented as tools
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.resourcesAsToolsPresenter(
DefaultMcpResourcesAsToolsPresenter.builder()
.nameOfListResourcesTool("list_available_resources")
.nameOfGetResourceTool("get_resource_content")
.build()
)
.build();
// The assistant can now use:
// - list_available_resources (to discover resources)
// - get_resource_content (to read resource content)
// - Plus all MCP tools from the server// Resilient mode (default) - continues with available servers
McpToolProvider resilient = McpToolProvider.builder()
.mcpClients(server1, server2, server3)
.failIfOneServerFails(false) // Continue even if one fails
.build();
// Strict mode - fails if any server unavailable
McpToolProvider strict = McpToolProvider.builder()
.mcpClients(server1, server2, server3)
.failIfOneServerFails(true) // Fail fast if any server fails
.build();McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.toolSpecificationMapper((client, originalSpec) -> {
// Modify tool specification
return originalSpec.toBuilder()
.name(client.key() + "_" + originalSpec.name())
.description("[" + client.key() + "] " + originalSpec.description())
// Optionally modify parameters
.build();
})
.build();McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(client)
// Filter 1: Exclude delete operations
.filter((client, tool) -> !tool.name().contains("delete"))
// Filter 2: Only from trusted servers
.build();
// Add more filters dynamically
toolProvider.addFilter((client, tool) ->
tool.name().matches("[a-z_]+")) // Only lowercase with underscores
toolProvider.addFilter((client, tool) -> {
// Only allow tools with proper documentation
return tool.description() != null && !tool.description().isEmpty();
});import com.google.common.util.concurrent.RateLimiter;
RateLimiter rateLimiter = RateLimiter.create(10.0); // 10 requests per second
Function<ToolExecutor, ToolExecutor> rateLimitingWrapper = executor -> {
return (request, memoryId) -> {
if (!rateLimiter.tryAcquire(Duration.ofSeconds(5))) {
throw new RuntimeException("Rate limit exceeded");
}
return executor.execute(request, memoryId);
};
};
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.toolWrapper(rateLimitingWrapper)
.build();toolNameMapper to avoid name collisionstoolNameMapper and toolSpecificationMapperInstall with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-mcp