MCP Transports
The MCP module provides 5 transport implementations plus an OAuth decorator and an auto-detection utility for connecting to MCP servers over stdio, HTTP, SSE, and bidirectional streaming.
McpTransport Interface
Every transport implements this common interface, which defines how to connect, send messages, and receive responses. You generally do not call these methods directly -- the McpClient handles JSON-RPC correlation and dispatching on top of the transport.
public interface McpTransport {
void connect() throws IOException;
void send(String message) throws IOException;
void setOnMessage(Consumer<String> messageConsumer);
void setOnError(Consumer<Throwable> errorConsumer);
void disconnect() throws IOException;
boolean isConnected();
}Messages are JSON-RPC strings. The transport layer handles framing and delivery; the McpClient handles JSON-RPC correlation and method dispatch.
StdioTransport
Communicates with MCP servers via stdin/stdout of a subprocess. The primary transport for local servers launched via npx, uvx, or native executables.
StdioTransport transport = StdioTransport.builder()
.command("npx", "-y", "@modelcontextprotocol/server-filesystem", "/path")
.environment(Map.of("NODE_ENV", "production"))
.workingDirectory("/project")
.maxRetries(5)
.initialRetryDelay(Duration.ofMillis(500))
.maxRetryDelay(Duration.ofSeconds(30))
.healthCheckInterval(Duration.ofSeconds(30))
.processStartTimeout(Duration.ofSeconds(10))
.build();
transport.setOnMessage(msg -> System.out.println("Received: " + msg));
transport.setOnError(err -> System.err.println("Error: " + err));
transport.connect();
// Send a JSON-RPC request
transport.send("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}");Features
The stdio transport includes several reliability features that make it robust for long-running agent sessions.
- Auto-reconnect: On process crash, reconnects with exponential backoff (initial 500ms, max 30s, up to 5 retries)
- Health checks: Periodic pings at configurable intervals (default 30s) detect stale connections
- Process lifecycle: Manages the subprocess from start to shutdown, including graceful stop and forced kill
- Thread-safe: Uses
BufferedReader/BufferedWriterwith synchronized access for concurrent message handling - Crash detection: Monitors stderr for crash indicators and triggers reconnection
HttpTransport
Simple HTTP POST-based transport for stateless MCP servers. Each send() makes an HTTP POST with the JSON-RPC message body and reads the response synchronously.
HttpTransport transport = HttpTransport.builder()
.endpoint("https://api.example.com/mcp")
.headers(Map.of("Authorization", "Bearer " + token))
.timeout(Duration.ofSeconds(30))
.build();Best for simple request-response MCP servers that do not need streaming or server-initiated messages.
SSETransport
Server-Sent Events transport for MCP servers that stream responses. Maintains a long-lived GET connection for receiving server events and sends requests via POST.
SSETransport transport = SSETransport.builder()
.sseEndpoint("https://mcp.example.com/sse")
.postEndpoint("https://mcp.example.com/messages")
.headers(Map.of("Authorization", "Bearer " + token))
.build();The SSE connection receives server-initiated notifications (tool list changes, resource updates) while the POST endpoint handles client requests.
StreamableHttpTransport
Bidirectional streaming transport (MCP protocol 2025-03-26+). Uses HTTP POST with streaming responses, allowing both request-response and server-initiated messages over a single HTTP connection.
StreamableHttpTransport transport = StreamableHttpTransport.builder()
.endpoint("https://mcp.example.com/mcp")
.headers(Map.of("Authorization", "Bearer " + token))
.build();This is the preferred transport for modern MCP servers. It supports session management via Mcp-Session-Id headers and server-initiated notifications within the response stream.
OAuthMcpTransport
A decorator that adds OAuth Bearer token authentication to any McpTransport. Wraps an existing transport and manages token lifecycle (validation, refresh) before each operation.
McpTokenManager tokenManager = new McpTokenManager();
// ... store tokens after OAuth flow ...
OAuthMcpTransport transport = OAuthMcpTransport.builder()
.delegate(new SSETransport.Builder().sseEndpoint("...").build())
.tokenManager(tokenManager)
.serverUrl("https://mcp.example.com")
.tokenEndpoint("https://auth.example.com/token")
.clientId("my-client")
.build();
McpClient client = new McpClient(transport);
client.connect();Token Management (McpTokenManager)
The McpTokenManager handles the lifecycle of OAuth tokens: storing them after the initial flow, checking expiry, and automatically refreshing when needed. This means your application does not have to manually track token lifetimes.
McpTokenManager tokenManager = new McpTokenManager();
// Store tokens (after initial OAuth flow)
tokenManager.storeTokens("https://mcp.example.com",
accessToken, refreshToken, expiresIn);
// Get a valid token (auto-refreshes if expired)
Optional<String> token = tokenManager.getValidToken("https://mcp.example.com");PKCE Flow (PkceFlow)
PKCE (Proof Key for Code Exchange) is a security extension for OAuth that prevents authorization code interception attacks. Use this when your application is a public client (no client secret) and needs to authenticate with an MCP server that requires OAuth.
PkceFlow pkce = new PkceFlow();
// Generate challenge
String codeVerifier = pkce.generateCodeVerifier();
String codeChallenge = pkce.generateCodeChallenge(codeVerifier);
// Use codeChallenge in the authorization request (method: S256)
// Exchange authorization code
TokenResponse tokens = pkce.exchangeCode(
authCode, codeVerifier, tokenEndpoint, clientId, redirectUri);Dynamic Client Registration (McpClientRegistration)
Some MCP servers require your application to register itself as an OAuth client before authenticating. Dynamic client registration automates this process so you do not have to manually create client credentials on the server.
McpClientRegistration registration = new McpClientRegistration();
ClientInfo client = registration.register(
registrationEndpoint,
"TnsAI Agent",
List.of("http://localhost:8080/callback")
);
// client.getClientId(), client.getClientSecret()OAuth Discovery (McpOAuthDiscovery)
OAuth discovery automatically finds the authorization and token endpoints for an MCP server, so you do not have to hardcode them. It probes the server's well-known metadata URL and returns all the endpoints needed for the OAuth flow.
McpOAuthDiscovery discovery = new McpOAuthDiscovery();
OAuthMetadata metadata = discovery.discover("https://mcp.example.com");
// metadata.authorizationEndpoint(), metadata.tokenEndpoint(), etc.TransportDetector
Auto-detects the best transport for a given URL endpoint by probing the server.
McpTransport transport = TransportDetector.detect("https://api.example.com/mcp");
McpClient client = new McpClient(transport);
// With custom headers
McpTransport transport = TransportDetector.detect(
"https://api.example.com/mcp",
Map.of("Authorization", "Bearer " + token));Detection Order
The detector probes the server endpoint in order from newest to oldest protocol, falling back if a probe fails.
- Streamable HTTP: Sends a POST JSON-RPC request. If the server responds with valid JSON-RPC, uses
StreamableHttpTransport. - SSE: Sends a GET with
Accept: text/event-stream. If the server responds with SSE content type, usesSSETransport. - HTTP fallback: Falls back to basic
HttpTransport.
Detection uses a 10-second timeout per probe. For stdio-based servers (local commands), there is no detection -- use StdioTransport directly.
Choosing a Transport
Use this table to pick the right transport for your use case. For local servers (like those launched via npx), use stdio. For remote servers, prefer Streamable HTTP unless the server only supports SSE or plain HTTP.
| Transport | Protocol | Direction | Use Case |
|---|---|---|---|
StdioTransport | stdin/stdout | Bidirectional | Local subprocess servers (npx, uvx) |
HttpTransport | HTTP POST | Request-response | Simple stateless servers |
SSETransport | HTTP GET + POST | Server push + request | Servers needing notifications |
StreamableHttpTransport | HTTP POST (streaming) | Bidirectional | Modern MCP servers (recommended for remote) |
OAuthMcpTransport | Decorator | -- | Any transport needing OAuth authentication |