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

registry.mddocs/

MCP Registry Client

The registry client provides access to the MCP registry, allowing applications to discover and query available MCP servers, their versions, configurations, and metadata.

Quick Reference

// Create registry client
McpRegistryClient registryClient = DefaultMcpRegistryClient.builder()
    .baseUrl("https://registry.modelcontextprotocol.io") // Default
    .build();

// Health check
McpRegistryHealth health = registryClient.healthCheck();
McpRegistryPong pong = registryClient.ping();

// List servers
McpServerListRequest request = McpServerListRequest.builder()
    .search("weather")
    .version("latest")
    .limit(30L)
    .build();
McpServerList servers = registryClient.listServers(request);

// Get specific server version
McpGetServerResponse server =
    registryClient.getSpecificServerVersion("weather-server", "latest");

// Get all versions
McpServerList allVersions =
    registryClient.getAllVersionsOfServer("weather-server");

Imports

// Core Registry Client
import dev.langchain4j.mcp.registryclient.McpRegistryClient;
import dev.langchain4j.mcp.registryclient.DefaultMcpRegistryClient;
import dev.langchain4j.mcp.registryclient.McpRegistryClientException;

// Registry Models
import dev.langchain4j.mcp.registryclient.model.McpServerList;
import dev.langchain4j.mcp.registryclient.model.McpServerListRequest;
import dev.langchain4j.mcp.registryclient.model.McpGetServerResponse;
import dev.langchain4j.mcp.registryclient.model.McpServer;
import dev.langchain4j.mcp.registryclient.model.McpRegistryHealth;
import dev.langchain4j.mcp.registryclient.model.McpRegistryPong;

// Supporting Models
import dev.langchain4j.mcp.registryclient.model.*;

// HTTP Client
import dev.langchain4j.http.client.HttpClient;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.function.Supplier;

McpRegistryClient Interface

Interface for communicating with an MCP registry to discover and query servers.

interface McpRegistryClient {
    McpServerList listServers(McpServerListRequest request);
    @Deprecated
    McpGetServerResponse getServerDetails(String id);
    McpGetServerResponse getSpecificServerVersion(String serverName, String version);
    McpServerList getAllVersionsOfServer(String serverName);
    McpRegistryHealth healthCheck();
    McpRegistryPong ping();
}

Methods

listServers(McpServerListRequest)

Retrieves a list of MCP servers from the registry with optional filtering and pagination.

Parameters: request (McpServerListRequest, required) - Request with filters and pagination Returns: McpServerList - List of servers with metadata (never null) Throws: McpRegistryClientException - If the request fails Thread-safe: Yes

Pagination: Results are paginated, use metadata.getNextCursor() for next page

getServerDetails(String)

DEPRECATED - Not supported on the official MCP registry. Use getSpecificServerVersion instead.

Parameters: id (String) - Server ID Returns: McpGetServerResponse Status: Deprecated

getSpecificServerVersion(String, String)

Retrieves detailed information about a specific version of an MCP server.

Parameters:

  • serverName (String, required) - Server name
  • version (String, required) - Version string or "latest"

Returns: McpGetServerResponse - Server version details (never null) Throws: McpRegistryClientException - If the request fails or server not found Thread-safe: Yes

Special Version: Use "latest" to get the most recent version

getAllVersionsOfServer(String)

Retrieves all available versions for a specific MCP server.

Parameters: serverName (String, required) - Server name Returns: McpServerList - List of all server versions (never null) Throws: McpRegistryClientException - If the request fails Thread-safe: Yes

healthCheck()

Performs a health check against the MCP registry.

Returns: McpRegistryHealth - Health check result with status field Throws: McpRegistryClientException - If the health check fails Thread-safe: Yes

Healthy Response: status field will be "ok"

ping()

Sends a ping message to the MCP registry.

Returns: McpRegistryPong - Ping response with pong field Throws: McpRegistryClientException - If the ping fails Thread-safe: Yes

Success Response: pong field will be true

DefaultMcpRegistryClient

Default implementation of the registry client.

class DefaultMcpRegistryClient implements McpRegistryClient {
    static Builder builder();
}

Builder

Fluent API for configuring the registry client.

class Builder {
    Builder baseUrl(String baseUrl);
    Builder httpClient(HttpClient httpClient);
    Builder headers(Map<String, String> headers);
    Builder headersSupplier(Supplier<Map<String, String>> headers);
    Builder logRequests(boolean logRequests);
    Builder logResponses(boolean logResponses);
    DefaultMcpRegistryClient build();
}

baseUrl(String)

Sets the registry base URL.

Parameters: baseUrl (String, nullable) Returns: Builder Default: https://registry.modelcontextprotocol.io Use Cases: Custom/private registries, testing

httpClient(HttpClient)

Sets a custom HTTP client.

Parameters: httpClient (HttpClient, nullable) Returns: Builder Default: Default LangChain4j HTTP client

headers(Map)

Sets static HTTP headers for all requests.

Parameters: headers (Map<String, String>, nullable) Returns: Builder Use Cases: Static authentication, client identification

headersSupplier(Supplier)

Sets a dynamic headers supplier for each request.

Parameters: headers (Supplier<Map<String, String>>, nullable) Returns: Builder Use Cases: Token refresh, request-specific headers

logRequests(boolean)

Enables or disables request logging.

Parameters: logRequests (boolean) Returns: Builder Default: false

logResponses(boolean)

Enables or disables response logging.

Parameters: logResponses (boolean) Returns: Builder Default: false

Registry Models

McpServerListRequest

Request for listing servers with filtering and pagination.

class McpServerListRequest {
    static Builder builder();

    class Builder {
        Builder cursor(String cursor);
        Builder limit(Long limit);
        Builder search(String search);
        Builder updatedSince(LocalDateTime updatedSince);
        Builder version(String version);
        McpServerListRequest build();
    }
}

Builder Methods

cursor(String)

Sets the pagination cursor for fetching the next page.

Parameters: cursor (String, nullable) - Pagination cursor from previous response Returns: Builder Source: McpMetadata.getNextCursor() from previous response

limit(Long)

Sets the number of items per page.

Parameters: limit (Long, nullable) Returns: Builder Default: 30 Valid Range: 1-100 (registry-dependent)

search(String)

Filters servers by name using substring matching (case-insensitive).

Parameters: search (String, nullable) - Search query Returns: Builder Example: "weather" matches "Weather Server", "weather-api", etc.

updatedSince(LocalDateTime)

Only returns servers updated since the specified date and time.

Parameters: updatedSince (LocalDateTime, nullable) - Cutoff date/time (must be in UTC) Returns: Builder Use Cases: Incremental updates, change tracking

version(String)

Filters by version.

Parameters: version (String, nullable) - Version filter Returns: Builder Special Values: Use "latest" for latest versions Example: "1.2.3" for specific version

McpServerList

List of MCP servers with pagination metadata.

class McpServerList {
    List<McpGetServerResponse> getServers();
    McpMetadata getMetadata();
}

Methods:

  • getServers() - Returns list of servers (never null, may be empty)
  • getMetadata() - Returns pagination metadata (never null)

McpMetadata

Pagination metadata for server lists.

class McpMetadata {
    String getNextCursor();
    // Additional pagination fields
}

Key Method:

  • getNextCursor() - Returns cursor for next page (null if last page)

McpGetServerResponse

Response containing detailed server information.

class McpGetServerResponse {
    // Wraps McpServer data
    String getName();
    String getDescription();
    String getVersion();
    McpRepository getRepository();
    List<McpRemote> getRemotes();
    List<McpPackage> getPackages();
    McpMeta getMeta();
    // ... (delegates to McpServer)
}

McpServer

Detailed information about an MCP server.

class McpServer {
    String getName();
    String getDescription();
    String getSchema();
    @Deprecated String getStatus();
    McpRepository getRepository();
    String getVersion();
    String getWebsiteUrl();
    List<McpRemote> getRemotes();
    McpMeta getMeta();
    List<McpPackage> getPackages();
}

Important Fields:

  • name (String, never null) - Server name
  • version (String, never null) - Server version
  • description (String, nullable) - Server description
  • remotes (List<McpRemote>, may be empty) - Remote configurations
  • packages (List<McpPackage>, may be empty) - Package information

McpRemote

Remote server configuration.

class McpRemote {
    String getName();
    McpTransport getTransport();
    List<McpEnvironmentVariable> getEnv();
    List<McpHeader> getHeaders();
}

Use Cases: Extract transport type, environment variables, headers for connection

McpPackage

Package/runtime information.

class McpPackage {
    String getName();
    String getManager();
    String getVersion();
    String getRuntimeHint();
    McpTransport getTransport();
    List<McpRuntimeArgument> getRuntimeArguments();
    List<McpPackageArgument> getPackageArguments();
    List<McpEnvironmentVariable> getEnvironmentVariables();
}

Use Cases: Determine how to run the server (npm, python, etc.)

McpRegistryClientException

Exception thrown by registry client operations.

class McpRegistryClientException extends LangChain4jException {
    McpRegistryClientException(String message);
    McpRegistryClientException(Throwable cause);
    McpRegistryClientException(String message, Throwable cause);
}

Common Causes:

  • HTTP request failures (network, timeout)
  • JSON parsing errors
  • Server not found (404)
  • Authentication failures (401, 403)
  • Registry server errors (500)

Usage Examples

Basic Registry Client

// Use official MCP registry
McpRegistryClient registryClient = DefaultMcpRegistryClient.builder()
    .build();

// Health check
McpRegistryHealth health = registryClient.healthCheck();
System.out.println("Registry health: " + health);

// Ping
McpRegistryPong pong = registryClient.ping();
System.out.println("Ping successful: " + pong);

Listing Servers

// List all servers (first page)
McpServerListRequest request = McpServerListRequest.builder()
    .limit(30L)
    .build();

McpServerList serverList = registryClient.listServers(request);

for (McpGetServerResponse server : serverList.getServers()) {
    System.out.println("Server: " + server.getName());
    System.out.println("Description: " + server.getDescription());
    System.out.println("Version: " + server.getVersion());
}

// Pagination
McpMetadata metadata = serverList.getMetadata();
if (metadata.getNextCursor() != null) {
    McpServerListRequest nextPage = McpServerListRequest.builder()
        .cursor(metadata.getNextCursor())
        .build();
    McpServerList nextPageResults = registryClient.listServers(nextPage);
}

Searching for Servers

// Search by name
McpServerListRequest searchRequest = McpServerListRequest.builder()
    .search("weather")
    .version("latest")
    .build();

McpServerList searchResults = registryClient.listServers(searchRequest);

for (McpGetServerResponse server : searchResults.getServers()) {
    System.out.println("Found: " + server.getName());
}

Getting Server Details

// Get latest version
McpGetServerResponse latestWeather =
    registryClient.getSpecificServerVersion("weather-server", "latest");

System.out.println("Server: " + latestWeather.getName());
System.out.println("Version: " + latestWeather.getVersion());
System.out.println("Description: " + latestWeather.getDescription());

// Get specific version
McpGetServerResponse specificVersion =
    registryClient.getSpecificServerVersion("weather-server", "1.2.3");

Extracting Server Configuration

McpGetServerResponse server =
    registryClient.getSpecificServerVersion("example-server", "latest");

// Repository info
McpRepository repo = server.getRepository();
if (repo != null) {
    System.out.println("Repository: " + repo.getUrl());
    System.out.println("Type: " + repo.getType());
}

// Remote configurations
List<McpRemote> remotes = server.getRemotes();
for (McpRemote remote : remotes) {
    System.out.println("Remote: " + remote.getName());

    // Transport configuration
    McpTransport transport = remote.getTransport();
    System.out.println("Transport type: " + transport.getType());

    // Environment variables
    if (remote.getEnv() != null) {
        for (McpEnvironmentVariable envVar : remote.getEnv()) {
            System.out.println("Env: " + envVar.getName() + "=" + envVar.getValue());
        }
    }

    // Headers (for HTTP transport)
    if (remote.getHeaders() != null) {
        for (McpHeader header : remote.getHeaders()) {
            System.out.println("Header: " + header.getName() + "=" + header.getValue());
        }
    }
}

// Packages
List<McpPackage> packages = server.getPackages();
for (McpPackage pkg : packages) {
    System.out.println("Package: " + pkg.getName());
    System.out.println("Manager: " + pkg.getManager());
}

Custom Registry URL

// Connect to private/custom registry
McpRegistryClient customRegistry = DefaultMcpRegistryClient.builder()
    .baseUrl("https://internal-registry.company.com")
    .build();

McpServerList internalServers = customRegistry.listServers(
    McpServerListRequest.builder().build()
);

Authenticated Registry Access

// Static authentication
McpRegistryClient authenticatedClient = DefaultMcpRegistryClient.builder()
    .baseUrl("https://private-registry.example.com")
    .headers(Map.of(
        "Authorization", "Bearer " + apiToken,
        "X-API-Key", apiKey
    ))
    .build();

// Dynamic authentication (token refresh)
McpRegistryClient dynamicAuthClient = DefaultMcpRegistryClient.builder()
    .headersSupplier(() -> Map.of(
        "Authorization", "Bearer " + getCurrentToken()
    ))
    .build();

Error Handling

try {
    McpGetServerResponse server =
        registryClient.getSpecificServerVersion("unknown-server", "latest");

} catch (McpRegistryClientException e) {
    System.err.println("Registry error: " + e.getMessage());

    Throwable cause = e.getCause();
    if (cause != null) {
        System.err.println("Caused by: " + cause.getMessage());

        if (cause instanceof java.net.SocketTimeoutException) {
            System.err.println("Request timed out");
        } else if (cause instanceof java.net.ConnectException) {
            System.err.println("Cannot connect to registry");
        }
    }
}

Server Discovery and Connection

// 1. Discover server in registry
McpServerListRequest searchRequest = McpServerListRequest.builder()
    .search("weather")
    .version("latest")
    .build();

McpServerList results = registryClient.listServers(searchRequest);

if (!results.getServers().isEmpty()) {
    McpGetServerResponse weatherServer = results.getServers().get(0);

    // 2. Extract configuration
    if (weatherServer.getRemotes() != null &&
        !weatherServer.getRemotes().isEmpty()) {

        McpRemote remote = weatherServer.getRemotes().get(0);

        // 3. Create transport based on configuration
        if ("stdio".equals(remote.getTransport().getType())) {
            List<String> command = extractCommand(remote);

            StdioMcpTransport transport = StdioMcpTransport.builder()
                .command(command)
                .environment(extractEnvironment(remote))
                .build();

            // 4. Create MCP client
            McpClient client = DefaultMcpClient.builder()
                .transport(transport)
                .build();

            // 5. Use the client
            List<ToolSpecification> tools = client.listTools();
            System.out.println("Available tools: " + tools.size());
        }
    }
}

private List<String> extractCommand(McpRemote remote) {
    // Implementation depends on remote structure
    return List.of("node", "server.js");
}

private Map<String, String> extractEnvironment(McpRemote remote) {
    if (remote.getEnv() == null) return Map.of();

    return remote.getEnv().stream()
        .collect(Collectors.toMap(
            McpEnvironmentVariable::getName,
            McpEnvironmentVariable::getValue
        ));
}

Caching Registry Results

class CachedRegistryClient {
    private final McpRegistryClient delegate;
    private final Map<String, CachedResult<McpGetServerResponse>> cache =
        new ConcurrentHashMap<>();
    private final Duration cacheDuration = Duration.ofHours(1);

    CachedRegistryClient(McpRegistryClient delegate) {
        this.delegate = delegate;
    }

    public McpGetServerResponse getSpecificServerVersion(
            String serverName,
            String version) {

        String cacheKey = serverName + ":" + version;
        CachedResult<McpGetServerResponse> cached = cache.get(cacheKey);

        if (cached != null && cached.isValid()) {
            return cached.value;
        }

        McpGetServerResponse result =
            delegate.getSpecificServerVersion(serverName, version);
        cache.put(cacheKey, new CachedResult<>(result, cacheDuration));
        return result;
    }

    private static class CachedResult<T> {
        final T value;
        final Instant expiresAt;

        CachedResult(T value, Duration ttl) {
            this.value = value;
            this.expiresAt = Instant.now().plus(ttl);
        }

        boolean isValid() {
            return Instant.now().isBefore(expiresAt);
        }
    }
}

Performance Considerations

  1. Cache results: Registry data changes infrequently, cache server details
  2. Pagination: Use appropriate limit to balance response size and roundtrips
  3. Incremental updates: Use updatedSince to fetch only changed servers
  4. Connection pooling: Reuse HTTP client with connection pooling

Thread Safety

All methods are thread-safe and can be called concurrently from multiple threads.

Related Documentation

  • Client - Creating MCP clients from registry data
  • Transports - Transport configuration from registry

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