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

resources-as-tools.mddocs/

Resources as Tools

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.

Quick Reference

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

Overview

The resources-as-tools feature creates two synthetic tools:

  1. list_resources - Lists all available resources from configured MCP servers
  2. get_resource - Retrieves content from a specific resource

This two-phase pattern (discovery → access) enables AI agents to dynamically discover what resources are available and then retrieve them as needed.

Core Interface

McpResourcesAsToolsPresenter

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

Methods

createListResourcesSpecification()

Creates a tool specification for the tool that lists available resources.

Returns: ToolSpecification - Tool spec for listing resources (never null) Thread-safe: Yes

createListResourcesExecutor(List)

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

createGetResourceSpecification()

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

createGetResourceExecutor(List)

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

DefaultMcpResourcesAsToolsPresenter

Default implementation that creates two tools with customizable names and descriptions.

package dev.langchain4j.mcp.resourcesastools;

public class DefaultMcpResourcesAsToolsPresenter implements McpResourcesAsToolsPresenter {
    static Builder builder();
}

Constants

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.";

Builder

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

nameOfGetResourceTool(String)

Customizes the name of the get resource tool.

Parameters: name (String, nullable) - Custom tool name Returns: Builder Default: "get_resource"

descriptionOfGetResourceTool(String)

Customizes the description of the get resource tool.

Parameters: description (String, nullable) - Custom tool description Returns: Builder Default: Standard description

descriptionOfMcpServerParameterOfGetResourceTool(String)

Customizes the description of the MCP server parameter.

Parameters: description (String, nullable) - Custom parameter description Returns: Builder

descriptionOfUriParameterOfGetResourceTool(String)

Customizes the description of the URI parameter.

Parameters: description (String, nullable) - Custom parameter description Returns: Builder

nameOfListResourcesTool(String)

Customizes the name of the list resources tool.

Parameters: name (String, nullable) - Custom tool name Returns: Builder Default: "list_resources"

descriptionOfListResourcesTool(String)

Customizes the description of the list resources tool.

Parameters: description (String, nullable) - Custom tool description Returns: Builder Default: Standard description

Usage Examples

Basic Setup

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 tool

Custom Tool Names and Descriptions

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

Resource Discovery and Access Pattern

Phase 1: List Available Resources

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 resource
  • uri (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 name
  • description (string, nullable) - A description of what the resource provides
  • mimeType (string, nullable) - The MIME type of the resource content

Phase 2: Get Specific Resource

After 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.

Example Agent Interaction

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 content

Tool Executors

The presenter creates two tool executors:

ListResourcesToolExecutor

Aggregates resources from all configured MCP clients and returns them as a JSON array.

Behavior:

  1. Queries each MCP client via listResources() and listResourceTemplates()
  2. Combines results from all clients into a single list
  3. Returns JSON array with resource metadata
  4. Supports invocation context for contextual resource queries

Error Handling: Throws ToolExecutionException on errors querying clients or JSON serialization failures

GetResourceToolExecutor

Retrieves a specific resource by server key and URI.

Behavior:

  1. Validates that mcpServer and uri arguments are provided
  2. Looks up the MCP client by the mcpServer key
  3. Calls readResource(uri) on the client
  4. Extracts text content from the response
  5. Returns content as a string

Validation:

  • Throws ToolArgumentsException if mcpServer argument is missing
  • Throws ToolArgumentsException if uri argument is missing
  • Throws ToolArgumentsException if MCP server key is unknown

Limitation: Only supports text resources. Binary resources throw ToolExecutionException.

Resource Templates

Resources can be static (with a fixed URI) or dynamic (with a URI template):

Static Resource

{
  "mcpServer": "alice",
  "uri": "file:///config",
  "uriTemplate": null,
  "name": "config",
  "description": "Configuration file",
  "mimeType": "application/json"
}

To access: get_resource(mcpServer="alice", uri="file:///config")

Dynamic Resource Template

{
  "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.

Integration with McpToolProvider

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 tool

Without a presenter:

// Only MCP server tools are exposed, resources are NOT available as tools
McpToolProvider toolProvider = McpToolProvider.builder()
    .mcpClients(mcpClient1, mcpClient2)
    .build();

Advanced Usage

Custom Presenter Implementation

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

Filtering Resources

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

Limitations

  1. Binary Content Not Supported: The default GetResourceToolExecutor only supports text resources. Binary resources throw ToolExecutionException.

  2. No Streaming: Resources are read completely into memory as strings.

  3. No Caching: Resources are fetched every time get_resource is called.

  4. 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.

Complete Example

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

Error Handling

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

Performance Considerations

  1. No caching: Each get_resource call fetches from server
  2. No batching: Resources fetched one at a time
  3. Memory: Resources loaded completely into memory
  4. For large/frequent access: Implement custom presenter with caching

Thread Safety

  • All presenter methods are thread-safe
  • Tool executors are thread-safe
  • Can be used concurrently from multiple threads

Related Documentation

  • Tool Provider - Integrating with tool provider
  • Data Models - Resource and content types
  • Client - Resource operations on MCP client

Install with Tessl CLI

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

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