Transports

This page explains the transport mechanisms available for MCP communication.

Overview

The MCP specification currently defines two standard transports for client-server communication. The stdio transport starts an MCP server as a subprocess and communicates over standard in and out. The HTTP transport connects to a running HTTP server. The HTTP transport is defined in two variants: the "Streamable HTTP" variant (introduced in the protocol version 2025-03-26) replaces the "HTTP/SSE" transport (introduced in protocol version 2024-11-05). The "HTTP/SSE" transport is considered deprecated, but it’s still supported by most clients and servers.

This extension supports the stdio transport and both variants of the HTTP transport. Moreover, it also supports the unofficial websocket transport.

STDIO Transport

If you want to use the stdio transport you’ll need to add the io.quarkiverse.mcp:quarkus-mcp-server-stdio extension to your build file first. For instance, with Maven, add the following dependency to your POM file:

<dependency>
    <groupId>io.quarkiverse.mcp</groupId>
    <artifactId>quarkus-mcp-server-stdio</artifactId>
    <version>2.0.0.Beta2</version>
</dependency>
If you use the stdio transport then your app should not write anything to the standard output. Quarkus console logging is automatically redirected to the standard error. And the standard output stream is set to "null" when the app is started by default.

See Getting Started with STDIO for a complete tutorial.

HTTP Transport

If you want to use the HTTP transport you’ll need to add the io.quarkiverse.mcp:quarkus-mcp-server-http extension to your build file first. For instance, with Maven, add the following dependency to your POM file:

<dependency>
    <groupId>io.quarkiverse.mcp</groupId>
    <artifactId>quarkus-mcp-server-http</artifactId>
    <version>2.0.0.Beta2</version>
</dependency>
The artifactId has changed from quarkus-mcp-server-sse to quarkus-mcp-server-http in version 1.8.0. However, the old artifactId should still work because it’s relocated.

This artifact contains both versions of the HTTP transport. The MCP endpoint (as defined in 2025-03-26) is exposed at {rootPath}, i.e. /mcp by default. The SSE endpoint (as defined in 2024-11-05) is exposed at {rootPath}/sse, i.e. /mcp/sse by default. The {rootPath} is set to mcp by default, but it can be changed with the quarkus.mcp.server.http.root-path configuration property.

The Resumability and Redelivery for the Streamable HTTP is not supported yet.

Stateless Mode

Starting with protocol version 2026-07-28, MCP supports a stateless communication mode. The server automatically detects whether a client is stateless or stateful and handles both paradigms simultaneously on the same endpoint.

Stateless vs. Stateful

In the stateful mode (protocol versions 2025-11-25 and earlier), the client performs an initialize/initialized handshake, the server assigns a session via the Mcp-Session-Id header, and both sides maintain state across requests. In the stateless mode, there is no handshake and no session. Instead, each request is self-contained — the client provides its identity, protocol version, and capabilities via _meta fields and required HTTP headers.

How it works
  • A stateless client sends server/discover (instead of initialize) to learn the server’s capabilities, supported protocol versions, and server info.

  • Every subsequent request includes:

    • MCP-Protocol-Version header with the protocol version

    • Mcp-Method header matching the JSON-RPC method in the body

    • Mcp-Name header for tools/call, prompts/get, and resources/read (the tool name, prompt name, or resource URI)

    • _meta fields: io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientInfo, and optionally io.modelcontextprotocol/clientCapabilities

  • The server creates a transient connection for each request — no session is established and no Mcp-Session-Id header is returned.

  • Stateless connections are transient (McpConnection.isTransient() returns true).

  • The following methods are not available for stateless clients and will be rejected with a METHOD_NOT_FOUND error: ping, logging/setLevel, initialize, notifications/initialized, notifications/roots/list_changed, resources/subscribe, and resources/unsubscribe.

  • GET requests (subsidiary SSE streams) and DELETE requests (session termination) return HTTP 405 Method Not Allowed for stateless clients.

  • Server-initiated requests (sampling, elicitation, roots) are not available for stateless clients. The Multi Round-Trip Requests (MRTR) pattern is used instead — see Multi Round-Trip Request (MRTR).

  • Stateless clients use subscriptions/listen instead of resources/subscribe/resources/unsubscribe to receive notifications. The client specifies a filter for the notification types it wants (e.g. toolsListChanged, resourceSubscriptions), and the server delivers matching notifications on the subscription stream. See Subscription Notifications.

Per-request log level

Since logging/setLevel is not available for stateless clients, the log level can be set per-request using the io.modelcontextprotocol/logLevel _meta field. The value must be a valid log level string (e.g. debug, info, warning, error). The server returns an INVALID_PARAMS error (-32602) if the value is not recognized. The McpConnection.logLevel() method returns this per-request value for transient connections.

See Getting Started with HTTP for a complete tutorial.

Custom Headers from Tool Parameters

MCP servers can designate specific tool parameters to be mirrored into HTTP headers using the x-mcp-header extension property in the tool’s inputSchema. This enables network intermediaries (load balancers, gateways, firewalls) to route and inspect requests based on parameter values without parsing the JSON-RPC body.

When a tool parameter is annotated with @McpParamHeader("Region") (or the programmatic x-mcp-header equivalent), the client includes a Mcp-Param-Region header in the HTTP request whose value matches the argument in the body.

Server validation

The server validates that every Mcp-Param-{Name} header value matches the corresponding value in the request body. A mismatch or missing header results in HTTP 400 Bad Request with a HeaderMismatch JSON-RPC error (code -32020).

The validation follows the MCP specification:

Scenario Client Behavior Server Behavior

Parameter value provided

Client MUST include the header

Server MUST validate header matches body

Parameter value is null

Client MUST omit the header

Server MUST NOT expect the header

Parameter not in arguments

Client MUST omit the header

Server MUST NOT expect the header

Client omits header but value is in body

Non-conforming client

Server MUST reject the request

Value encoding

Non-ASCII or otherwise unsafe header values are Base64-encoded using the sentinel format =?base64?{Base64EncodedValue}?=. The server decodes encoded values before comparing them to the body value.

See Implementing Tools - Custom HTTP Headers for usage with @McpParamHeader and the programmatic API.

WebSocket Transport

If you want to use the WebSocket transport you’ll need to add the io.quarkiverse.mcp:quarkus-mcp-server-websocket extension to your build file first. For instance, with Maven, add the following dependency to your POM file:

<dependency>
    <groupId>io.quarkiverse.mcp</groupId>
    <artifactId>quarkus-mcp-server-websocket</artifactId>
    <version>2.0.0.Beta2</version>
</dependency>

The WebSocket endpoint is exposed at {endpointPath}. The {endpointPath} is set to /mcp/ws by default, but it can be changed with the quarkus.mcp.server.websocket.endpoint-path configuration property.

Using the BOM

You can also use the BOM (Bill Of Materials) to control the versions of all io.quarkiverse.mcp dependencies:

+

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.quarkiverse.mcp</groupId>
            <artifactId>quarkus-mcp-server-bom</artifactId>
            <version>2.0.0.Beta2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>