Java implementation of the Model Context Protocol (MCP) client for the LangChain4j framework, enabling integration with MCP servers for tools, resources, and prompts
The registry client provides access to the MCP registry, allowing applications to discover and query available MCP servers, their versions, configurations, and metadata.
// 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");// 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;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();
}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
DEPRECATED - Not supported on the official MCP registry. Use getSpecificServerVersion instead.
Parameters: id (String) - Server ID
Returns: McpGetServerResponse
Status: Deprecated
Retrieves detailed information about a specific version of an MCP server.
Parameters:
serverName (String, required) - Server nameversion (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
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
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"
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
Default implementation of the registry client.
class DefaultMcpRegistryClient implements McpRegistryClient {
static 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();
}Sets the registry base URL.
Parameters: baseUrl (String, nullable)
Returns: Builder
Default: https://registry.modelcontextprotocol.io
Use Cases: Custom/private registries, testing
Sets a custom HTTP client.
Parameters: httpClient (HttpClient, nullable)
Returns: Builder
Default: Default LangChain4j HTTP client
Sets static HTTP headers for all requests.
Parameters: headers (Map<String, String>, nullable)
Returns: Builder
Use Cases: Static authentication, client identification
Sets a dynamic headers supplier for each request.
Parameters: headers (Supplier<Map<String, String>>, nullable)
Returns: Builder
Use Cases: Token refresh, request-specific headers
Enables or disables request logging.
Parameters: logRequests (boolean)
Returns: Builder
Default: false
Enables or disables response logging.
Parameters: logResponses (boolean)
Returns: Builder
Default: false
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();
}
}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
Sets the number of items per page.
Parameters: limit (Long, nullable)
Returns: Builder
Default: 30
Valid Range: 1-100 (registry-dependent)
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.
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
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
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)Pagination metadata for server lists.
class McpMetadata {
String getNextCursor();
// Additional pagination fields
}Key Method:
getNextCursor() - Returns cursor for next page (null if last page)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)
}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 nameversion (String, never null) - Server versiondescription (String, nullable) - Server descriptionremotes (List<McpRemote>, may be empty) - Remote configurationspackages (List<McpPackage>, may be empty) - Package informationRemote server configuration.
class McpRemote {
String getName();
McpTransport getTransport();
List<McpEnvironmentVariable> getEnv();
List<McpHeader> getHeaders();
}Use Cases: Extract transport type, environment variables, headers for connection
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.)
Exception thrown by registry client operations.
class McpRegistryClientException extends LangChain4jException {
McpRegistryClientException(String message);
McpRegistryClientException(Throwable cause);
McpRegistryClientException(String message, Throwable cause);
}Common Causes:
// 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);// 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);
}// 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());
}// 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");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());
}// Connect to private/custom registry
McpRegistryClient customRegistry = DefaultMcpRegistryClient.builder()
.baseUrl("https://internal-registry.company.com")
.build();
McpServerList internalServers = customRegistry.listServers(
McpServerListRequest.builder().build()
);// 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();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");
}
}
}// 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
));
}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);
}
}
}limit to balance response size and roundtripsupdatedSince to fetch only changed serversAll methods are thread-safe and can be called concurrently from multiple threads.
Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-mcp@1.11.0