CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-dev-langchain4j--langchain4j-mcp

Java implementation of the Model Context Protocol (MCP) client for the LangChain4j framework, enabling integration with MCP servers for tools, resources, and prompts

Overview
Eval results
Files

tool-provider.mddocs/

Tool Provider Integration

The McpToolProvider integrates MCP server tools into LangChain4j's agent framework. It provides dynamic tool discovery, filtering, and execution capabilities for AI assistants.

Quick Reference

// 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);

Imports

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;

McpToolProvider

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);
}

Methods

addMcpClient(McpClient)

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

removeMcpClient(McpClient)

Removes an MCP client, removing its tools from availability.

Parameters: client (McpClient, required) - The MCP client to remove Thread-safe: Yes

addFilter(BiPredicate)

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

setFilter(BiPredicate)

Sets a tool filter, replacing all existing filters.

Parameters: filter (BiPredicate<McpClient, ToolSpecification>, required) Thread-safe: Yes

setToolNameMapper(BiFunction)

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

setToolSpecificationMapper(BiFunction)

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

resetFilters()

Removes all filters, allowing all tools to pass through.

Thread-safe: Yes

provideTools(ToolProviderRequest)

Provides tools to LangChain4j's AI services. Called automatically by LangChain4j.

Parameters: request (ToolProviderRequest, required) Returns: ToolProviderResult - Tools and executors Thread-safe: Yes

Builder

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();
}

Key Builder Methods

mcpClients(McpClient...)

Sets the initial MCP clients.

Parameters: mcpClients (McpClient..., varargs) - One or more clients Returns: Builder Default: Empty (no clients)

filterToolNames(String...)

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")

failIfOneServerFails(boolean)

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

toolWrapper(Function)

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

resourcesAsToolsPresenter(McpResourcesAsToolsPresenter)

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

McpToolExecutor

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);
}

Constructors

McpToolExecutor(McpClient)

Creates an executor for the given client. Tool name taken from execution request.

Parameters: mcpClient (McpClient, required)

McpToolExecutor(McpClient, String)

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 server

McpToolMetadataKeys

Constants 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
}

Using Metadata

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
});

Usage Patterns

Pattern 1: Basic Integration

// 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?");

Pattern 2: Multiple Servers with Filtering

// 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();

Pattern 3: Tool Name Prefixing

// 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

Pattern 4: Dynamic Tool Management

// 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);

Pattern 5: Tool Execution Tracing

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();

Pattern 6: Metadata-Based Filtering

// 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();

Pattern 7: Resources as Tools

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

Pattern 8: Resilient vs Strict Mode

// 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();

Advanced Examples

Custom Tool Specification Mapper

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();

Combining Multiple Filters

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();
});

Rate-Limited Tool Wrapper

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();

Performance Considerations

  1. Filter Order: Filters are applied before mapping, so filtering reduces mapping overhead
  2. Client Caching: Tool list caching in MCP clients reduces server roundtrips
  3. Resilient Mode: In resilient mode, failed servers don't block successful ones
  4. Tool Wrappers: Keep wrappers lightweight to avoid overhead on every tool call

Thread Safety

  • All methods are thread-safe
  • Can add/remove clients and filters concurrently
  • Tool provisioning is thread-safe

Common Pitfalls

  1. Conflicting tool names: Use toolNameMapper to avoid name collisions
  2. Using both mappers: Cannot use both toolNameMapper and toolSpecificationMapper
  3. Filter order: Remember filters are ANDed together
  4. Forgotten cleanup: Close MCP clients when removing them from provider

Related Documentation

Install with Tessl CLI

npx tessl i tessl/maven-dev-langchain4j--langchain4j-mcp

docs

client.md

data-models.md

exceptions.md

index.md

logging-listeners.md

registry.md

resources-as-tools.md

tool-provider.md

transports.md

tile.json