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

transports.mddocs/

Transport Protocols

MCP clients communicate with servers using transport protocols. The library supports three transport types: stdio (subprocess), WebSocket, and HTTP with streaming.

Quick Reference

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

Transport Selection Guide

FactorStdioWebSocketHTTP
Use whenLocal executableRemote, real-timeRemote, standard protocols
LocationLocal onlyRemote/networkedRemote/networked
CommunicationBidirectionalFull duplexRequest-response
LatencyVery lowLowModerate
AuthenticationVia env varsVia headersVia headers
SSL/TLSN/A✓ (wss://)✓ (https://)
Firewall friendlyN/AModerateHigh
Lifecycle controlFullNoneNone
ReconnectionAutomatic (restart)AutomaticAutomatic

Decision Tree:

  1. Local server? → Use Stdio
  2. Need real-time bidirectional? → Use WebSocket
  3. Standard HTTP infrastructure? → Use HTTP

Imports

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;

McpTransport Interface

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

Stdio transport enables communication with MCP servers running as subprocesses via standard input/output.

class StdioMcpTransport implements McpTransport {
    static Builder builder();
    Process getProcess();
}

Builder

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

Builder Methods

command(List)

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

environment(Map)

Sets environment variables for the subprocess.

Parameters: environment (Map<String, String>, nullable) Returns: Builder Default: System environment Use Cases: API keys, configuration, debug flags

logEvents(boolean)

Enables or disables logging of transport events.

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

executorService(ExecutorService)

Sets a custom executor service for async operations.

Parameters: executorService (ExecutorService, nullable) Returns: Builder Default: Default executor When to use: Custom thread pool management

logger(Logger)

Sets a custom SLF4J logger for the transport.

Parameters: logger (Logger, nullable) Returns: Builder Default: Class logger

getProcess()

Returns the underlying subprocess.

Returns: Process - The subprocess running the MCP server Thread-safe: Yes Use Cases: Process monitoring, killing subprocess

Usage Examples

Basic Stdio Transport

StdioMcpTransport transport = StdioMcpTransport.builder()
    .command(List.of("node", "mcp-server.js"))
    .build();

McpClient client = DefaultMcpClient.builder()
    .transport(transport)
    .build();

With Environment Variables

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

NPX-based Server (No Installation Required)

// Run server directly from npm without installation
StdioMcpTransport transport = StdioMcpTransport.builder()
    .command(List.of("npx", "-y", "@modelcontextprotocol/server-filesystem",
                     "/path/to/allowed/directory"))
    .build();

With Custom Executor

ExecutorService executor = Executors.newFixedThreadPool(4);

StdioMcpTransport transport = StdioMcpTransport.builder()
    .command(List.of("./mcp-server"))
    .executorService(executor)
    .logEvents(true)
    .build();

Stdio Advantages

  • Simple setup: No network configuration needed
  • Subprocess isolation: Server runs in separate process
  • Automatic cleanup: Process killed when client closes
  • Full lifecycle control: Start, stop, restart server
  • No authentication needed: Local-only communication

Stdio Limitations

  • Local only: Cannot connect to remote servers
  • Platform-dependent: Command must be available on system
  • Process management: Must handle subprocess lifecycle

WebSocket Transport

WebSocket transport enables bidirectional, real-time communication with MCP servers over WebSocket protocol.

class WebSocketMcpTransport implements McpTransport {
    static Builder builder();
    void reloadSslContext(SSLContext sslContext);
}

Builder

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

Key Builder Methods

url(String)

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

timeout(Duration)

Sets the connection timeout.

Parameters: timeout (Duration, nullable) Returns: Builder Default: 30 seconds

sslContext(SSLContext)

Sets the SSL context for secure WebSocket connections (wss://).

Parameters: sslContext (SSLContext, nullable) Returns: Builder When required: For wss:// URLs with custom SSL requirements

headersSupplier(Supplier)

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

headersSupplier(McpHeadersSupplier)

Sets an MCP-aware headers supplier that receives call context.

Parameters: headersSupplier (McpHeadersSupplier, nullable) Returns: Builder Use Cases: Context-aware authentication, distributed tracing

reloadSslContext(SSLContext)

Dynamically reloads the SSL context for the connection.

Parameters: sslContext (SSLContext, required) Thread-safe: Yes Use Cases: Certificate rotation, SSL config updates

Usage Examples

Basic WebSocket Transport

WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
    .url("ws://localhost:8080/mcp")
    .build();

Secure WebSocket (wss://)

SSLContext sslContext = SSLContext.getDefault();

WebSocketMcpTransport transport = WebSocketMcpTransport.builder()
    .url("wss://mcp-server.example.com/mcp")
    .sslContext(sslContext)
    .timeout(Duration.ofSeconds(30))
    .build();

With Authentication Headers

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

Context-Aware Headers

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

Dynamic SSL Context Reload

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

WebSocket Advantages

  • Full duplex: Bidirectional communication
  • Low latency: Persistent connection, no handshake overhead
  • Real-time: Immediate server-to-client notifications
  • Event-driven: Good for servers that push updates

WebSocket Limitations

  • Complex setup: Requires WebSocket support on server
  • Proxy issues: Some proxies block WebSocket connections
  • Connection management: Must handle disconnections and reconnections

HTTP Transport

HTTP transport with streaming support for MCP server communication via HTTP and Server-Sent Events (SSE).

StreamableHttpMcpTransport (Recommended)

Modern HTTP transport with full streaming support.

class StreamableHttpMcpTransport implements McpTransport {
    static Builder 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();
}

Key Builder Methods

url(String)

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

customHeaders(Map)

Sets static custom headers for all requests.

Parameters: customHeaders (Map<String, String>, nullable) Returns: Builder Use Cases: Static API keys, client identification

customHeaders(Supplier)

Sets a supplier for dynamic headers.

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

customHeaders(McpHeadersSupplier)

Sets an MCP-aware headers supplier that receives call context.

Parameters: customHeadersSupplier (McpHeadersSupplier, nullable) Returns: Builder Use Cases: Context-aware authentication, distributed tracing

timeout(Duration)

Sets the connection timeout.

Parameters: timeout (Duration, nullable) Returns: Builder Default: 60 seconds

sslContext(SSLContext)

Sets the SSL context for HTTPS connections.

Parameters: sslContext (SSLContext, nullable) Returns: Builder When needed: Custom SSL certificates, corporate proxies

HttpMcpTransport (Deprecated)

Legacy HTTP transport with SSE. Use StreamableHttpMcpTransport instead.

@Deprecated
class HttpMcpTransport implements McpTransport {
    static Builder builder();
}

Usage Examples

Basic HTTP Transport

StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
    .url("http://localhost:8080/mcp")
    .build();

HTTPS with Authentication

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

Dynamic Headers with Token Refresh

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

With Custom SSL Context

// Load custom truststore
KeyStore trustStore = loadTrustStore();
SSLContext sslContext = createSSLContext(trustStore);

StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
    .url("https://mcp-server.example.com/mcp")
    .sslContext(sslContext)
    .build();

With Timeout and Logging

StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
    .url("https://mcp-server.example.com/mcp")
    .timeout(Duration.ofSeconds(60))
    .logRequests(true)
    .logResponses(true)
    .build();

HTTP Advantages

  • Standard protocol: Works with existing HTTP infrastructure
  • Firewall friendly: HTTP/HTTPS widely accepted
  • Easy authentication: Standard HTTP headers
  • Load balancing: Compatible with standard load balancers
  • Caching: Can leverage HTTP caching

HTTP Limitations

  • Higher latency: Request-response overhead
  • No server push: Cannot receive unsolicited server messages
  • Connection overhead: New connection per request (unless keep-alive)

Common Patterns

Pattern 1: Environment-Based Transport Selection

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

Pattern 2: Transport with Retry Logic

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

Pattern 3: Transport Selection Based on Server Capabilities

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

Pattern 4: Migration from Legacy HTTP Transport

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

Performance Considerations

  1. Stdio: Lowest latency, best for local servers
  2. WebSocket: Low latency, persistent connection reduces overhead
  3. HTTP: Higher latency, consider connection pooling
  4. Timeouts: Set appropriate timeouts for expected operation duration
  5. Logging: Disable request/response logging in production for performance

Security Best Practices

Always Use Secure Protocols in Production

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

Always Authenticate Remote Connections

// Good: Authentication required
StreamableHttpMcpTransport transport = StreamableHttpMcpTransport.builder()
    .url("https://mcp-server.example.com/mcp")
    .customHeaders(Map.of(
        "Authorization", "Bearer " + secureToken
    ))
    .build();

Validate SSL Certificates

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

Secure Environment Variables (Stdio)

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

Troubleshooting

Connection Fails

Stdio:

  • Verify command path exists: which node, which python
  • Check file permissions: chmod +x mcp-server
  • Test command manually: node mcp-server.js
  • Check environment variables are accessible

WebSocket:

  • Verify URL accessibility: curl -I <url>
  • Check firewall rules
  • Verify proxy settings (some proxies block WebSocket)
  • Check SSL certificate validity

HTTP:

  • Verify URL accessibility: curl <url>
  • Check authentication headers
  • Verify SSL certificate
  • Check timeout settings

Performance Issues

  1. Stdio: Check subprocess resource usage
  2. WebSocket: Monitor connection stability
  3. HTTP: Consider connection pooling
  4. All: Adjust timeouts for operation duration

Thread Safety

All transport implementations are thread-safe and can be used concurrently from multiple threads.

Related Documentation

  • Client - Using transports with MCP clients
  • Logging and Listeners - Monitoring transport events

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