Java implementation of the Model Context Protocol (MCP) client for the LangChain4j framework, enabling integration with MCP servers for tools, resources, and prompts
MCP clients communicate with servers using transport protocols. The library supports three transport types: stdio (subprocess), WebSocket, and HTTP with streaming.
// Stdio Transport (local subprocess)
StdioMcpTransport stdioTransport = StdioMcpTransport.builder()
.command(List.of("node", "mcp-server.js"))
.environment(Map.of("API_KEY", "value"))
.build();
// WebSocket Transport (remote, bidirectional)
WebSocketMcpTransport wsTransport = WebSocketMcpTransport.builder()
.url("wss://mcp-server.example.com/mcp")
.headersSupplier(() -> Map.of("Authorization", "Bearer " + token))
.timeout(Duration.ofSeconds(30))
.build();
// HTTP Transport (remote, request-response)
StreamableHttpMcpTransport httpTransport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/mcp")
.customHeaders(Map.of("Authorization", "Bearer " + token))
.timeout(Duration.ofSeconds(60))
.build();| Factor | Stdio | WebSocket | HTTP |
|---|---|---|---|
| Use when | Local executable | Remote, real-time | Remote, standard protocols |
| Location | Local only | Remote/networked | Remote/networked |
| Communication | Bidirectional | Full duplex | Request-response |
| Latency | Very low | Low | Moderate |
| Authentication | Via env vars | Via headers | Via headers |
| SSL/TLS | N/A | ✓ (wss://) | ✓ (https://) |
| Firewall friendly | N/A | Moderate | High |
| Lifecycle control | Full | None | None |
| Reconnection | Automatic (restart) | Automatic | Automatic |
Decision Tree:
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
import dev.langchain4j.mcp.client.transport.websocket.WebSocketMcpTransport;
import dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport;
import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport; // Deprecated
import dev.langchain4j.mcp.client.McpHeadersSupplier;
import javax.net.ssl.SSLContext;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;Base interface for all transport implementations.
interface McpTransport extends AutoCloseable {
void start(McpOperationHandler messageHandler);
CompletableFuture<JsonNode> initialize(McpInitializeRequest request);
CompletableFuture<JsonNode> executeOperationWithResponse(McpClientMessage request);
CompletableFuture<JsonNode> executeOperationWithResponse(McpCallContext context);
void executeOperationWithoutResponse(McpClientMessage request);
void executeOperationWithoutResponse(McpCallContext context);
void checkHealth();
void onFailure(Runnable actionOnFailure);
void close();
}Stdio transport enables communication with MCP servers running as subprocesses via standard input/output.
class StdioMcpTransport implements McpTransport {
static Builder builder();
Process getProcess();
}class Builder {
Builder command(List<String> command); // Required
Builder environment(Map<String, String> environment);
Builder logEvents(boolean logEvents);
Builder executorService(ExecutorService executorService);
Builder logger(Logger logger);
StdioMcpTransport build();
}Sets the command to execute as a subprocess. This is required.
Parameters: command (List<String>, required) - Command and arguments
Returns: Builder
Example: List.of("node", "server.js") or List.of("python", "server.py")
Sets environment variables for the subprocess.
Parameters: environment (Map<String, String>, nullable)
Returns: Builder
Default: System environment
Use Cases: API keys, configuration, debug flags
Enables or disables logging of transport events.
Parameters: logEvents (boolean)
Returns: Builder
Default: false
Sets a custom executor service for async operations.
Parameters: executorService (ExecutorService, nullable)
Returns: Builder
Default: Default executor
When to use: Custom thread pool management
Sets a custom SLF4J logger for the transport.
Parameters: logger (Logger, nullable)
Returns: Builder
Default: Class logger
Returns the underlying subprocess.
Returns: Process - The subprocess running the MCP server
Thread-safe: Yes
Use Cases: Process monitoring, killing subprocess
StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("node", "mcp-server.js"))
.build();
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build();StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("python", "server.py"))
.environment(Map.of(
"API_KEY", System.getenv("API_KEY"),
"DEBUG", "true",
"LOG_LEVEL", "info"
))
.build();// Run server directly from npm without installation
StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("npx", "-y", "@modelcontextprotocol/server-filesystem",
"/path/to/allowed/directory"))
.build();ExecutorService executor = Executors.newFixedThreadPool(4);
StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("./mcp-server"))
.executorService(executor)
.logEvents(true)
.build();WebSocket transport enables bidirectional, real-time communication with MCP servers over WebSocket protocol.
class WebSocketMcpTransport implements McpTransport {
static Builder builder();
void reloadSslContext(SSLContext sslContext);
}class Builder {
Builder url(String url); // Required
Builder logResponses(boolean logResponses);
Builder logRequests(boolean logRequests);
Builder logger(Logger logger);
Builder executor(Executor executor);
Builder timeout(Duration timeout);
Builder sslContext(SSLContext sslContext);
Builder headersSupplier(Supplier<Map<String, String>> headersSupplier);
Builder headersSupplier(McpHeadersSupplier headersSupplier);
WebSocketMcpTransport build();
}Sets the WebSocket server URL. This is required.
Parameters: url (String, required) - WebSocket URL
Returns: Builder
Format: ws://host:port/path or wss://host:port/path
Protocol: Use wss:// for secure connections in production
Sets the connection timeout.
Parameters: timeout (Duration, nullable)
Returns: Builder
Default: 30 seconds
Sets the SSL context for secure WebSocket connections (wss://).
Parameters: sslContext (SSLContext, nullable)
Returns: Builder
When required: For wss:// URLs with custom SSL requirements
Sets a supplier for custom HTTP headers sent during WebSocket handshake.
Parameters: headersSupplier (Supplier<Map<String, String>>, nullable)
Returns: Builder
Use Cases: Authentication, tracking, custom metadata
Sets an MCP-aware headers supplier that receives call context.
Parameters: headersSupplier (McpHeadersSupplier, nullable)
Returns: Builder
Use Cases: Context-aware authentication, distributed tracing
Dynamically reloads the SSL context for the connection.
Parameters: sslContext (SSLContext, required)
Thread-safe: Yes
Use Cases: Certificate rotation, SSL config updates
WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
.url("ws://localhost:8080/mcp")
.build();SSLContext sslContext = SSLContext.getDefault();
WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
.url("wss://mcp-server.example.com/mcp")
.sslContext(sslContext)
.timeout(Duration.ofSeconds(30))
.build();WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
.url("wss://mcp-server.example.com/mcp")
.headersSupplier(() -> {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + getAccessToken());
headers.put("X-Client-ID", "my-app");
return headers;
})
.build();import dev.langchain4j.mcp.client.McpHeadersSupplier;
McpHeadersSupplier headersSupplier = context -> {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + getToken());
// Add context-specific headers
InvocationContext invocationContext = context.invocationContext();
if (invocationContext != null) {
Object sessionId = invocationContext.attributes().get("sessionId");
if (sessionId != null) {
headers.put("X-Session-ID", sessionId.toString());
}
}
return headers;
};
WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
.url("wss://mcp-server.example.com/mcp")
.headersSupplier(headersSupplier)
.build();WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
.url("wss://mcp-server.example.com/mcp")
.sslContext(initialSslContext)
.build();
// Later, reload with new SSL context (e.g., certificate rotation)
SSLContext newSslContext = loadNewSslContext();
transport.reloadSslContext(newSslContext);HTTP transport with streaming support for MCP server communication via HTTP and Server-Sent Events (SSE).
Modern HTTP transport with full streaming support.
class StreamableHttpMcpTransport implements McpTransport {
static Builder builder();
}class Builder {
Builder url(String url); // Required
Builder customHeaders(Map<String, String> customHeaders);
Builder customHeaders(Supplier<Map<String, String>> customHeadersSupplier);
Builder customHeaders(McpHeadersSupplier customHeadersSupplier);
Builder timeout(Duration timeout);
Builder logRequests(boolean logRequests);
Builder logResponses(boolean logResponses);
Builder logger(Logger logger);
Builder executor(Executor executor);
Builder sslContext(SSLContext sslContext);
StreamableHttpMcpTransport build();
}Sets the HTTP server URL. This is required.
Parameters: url (String, required) - HTTP URL
Returns: Builder
Format: http://host:port/path or https://host:port/path
Protocol: Use https:// for production
Sets static custom headers for all requests.
Parameters: customHeaders (Map<String, String>, nullable)
Returns: Builder
Use Cases: Static API keys, client identification
Sets a supplier for dynamic headers.
Parameters: customHeadersSupplier (Supplier<Map<String, String>>, nullable)
Returns: Builder
Use Cases: Token refresh, request-specific headers
Sets an MCP-aware headers supplier that receives call context.
Parameters: customHeadersSupplier (McpHeadersSupplier, nullable)
Returns: Builder
Use Cases: Context-aware authentication, distributed tracing
Sets the connection timeout.
Parameters: timeout (Duration, nullable)
Returns: Builder
Default: 60 seconds
Sets the SSL context for HTTPS connections.
Parameters: sslContext (SSLContext, nullable)
Returns: Builder
When needed: Custom SSL certificates, corporate proxies
Legacy HTTP transport with SSE. Use StreamableHttpMcpTransport instead.
@Deprecated
class HttpMcpTransport implements McpTransport {
static Builder builder();
}StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("http://localhost:8080/mcp")
.build();StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/api/mcp")
.customHeaders(Map.of(
"Authorization", "Bearer your-token-here",
"X-API-Key", "your-api-key"
))
.build();import dev.langchain4j.mcp.client.McpHeadersSupplier;
McpHeadersSupplier headersSupplier = context -> {
Map<String, String> headers = new HashMap<>();
// Refresh token if needed
String token = tokenManager.getValidToken();
headers.put("Authorization", "Bearer " + token);
headers.put("X-Request-ID", UUID.randomUUID().toString());
return headers;
};
StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/mcp")
.customHeaders(headersSupplier)
.build();// Load custom truststore
KeyStore trustStore = loadTrustStore();
SSLContext sslContext = createSSLContext(trustStore);
StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/mcp")
.sslContext(sslContext)
.build();StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/mcp")
.timeout(Duration.ofSeconds(60))
.logRequests(true)
.logResponses(true)
.build();McpTransport transport;
String environment = System.getenv("ENV");
if ("local".equals(environment)) {
// Local development: use stdio
transport = StdioMcpTransport.builder()
.command(List.of("node", "local-server.js"))
.build();
} else if ("staging".equals(environment)) {
// Staging: use WebSocket
transport = WebSocketMcpTransport.builder()
.url("ws://staging.example.com/mcp")
.build();
} else {
// Production: use HTTPS
transport = StreamableHttpMcpTransport.builder()
.url("https://production.example.com/mcp")
.customHeaders(productionHeaders)
.build();
}
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build();AtomicInteger retryCount = new AtomicInteger(0);
StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/mcp")
.build();
transport.onFailure(() -> {
int count = retryCount.incrementAndGet();
if (count < 3) {
logger.info("Retry attempt {}", count);
// Reconnection happens automatically
} else {
logger.error("Failed after 3 retries");
}
});public McpTransport createTransport(String serverUrl, Map<String, Object> capabilities) {
if (capabilities.containsKey("websocket") &&
(boolean) capabilities.get("websocket")) {
return WebSocketMcpTransport.builder()
.url(serverUrl.replace("http", "ws") + "/ws")
.build();
} else {
return StreamableHttpMcpTransport.builder()
.url(serverUrl)
.build();
}
}// OLD (Deprecated)
HttpMcpTransport oldTransport = HttpMcpTransport.builder()
.sseUrl("https://server.com/sse")
.customHeaders(headers)
.build();
// NEW (Recommended)
StreamableHttpMcpTransport newTransport = StreamableHttpMcpTransport.builder()
.url("https://server.com/mcp") // Single URL instead of sseUrl
.customHeaders(headers)
.build();// Good: Secure protocols
WebSocketMcpTransport wsTransport = WebSocketMcpTransport.builder()
.url("wss://server.com/mcp") // wss:// not ws://
.build();
StreamableHttpMcpTransport httpTransport = StreamableHttpMcpTransport.builder()
.url("https://server.com/mcp") // https:// not http://
.build();// Good: Authentication required
StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
.url("https://mcp-server.example.com/mcp")
.customHeaders(Map.of(
"Authorization", "Bearer " + secureToken
))
.build();// Good: Use proper SSL context with certificate validation
SSLContext sslContext = SSLContext.getDefault(); // Validates certificates
WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
.url("wss://mcp-server.example.com/mcp")
.sslContext(sslContext)
.build();// Good: Load sensitive data from secure sources
StdioMcpTransport transport = StdioMcpTransport.builder()
.command(List.of("node", "server.js"))
.environment(Map.of(
"API_KEY", loadFromSecureStore("api_key"),
"DB_PASSWORD", loadFromSecureStore("db_password")
))
.build();Stdio:
which node, which pythonchmod +x mcp-servernode mcp-server.jsWebSocket:
curl -I <url>HTTP:
curl <url>All transport implementations are thread-safe and can be used concurrently from multiple threads.
Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-mcp@1.11.0