Chapter 10.1: WebSocket Transport

WebSocket provides a persistent, low-latency channel that’s ideal for interactive tools and near–real-time experiences. PMCP supports WebSocket on the client side out of the box and also includes an optional server transport. For most server deployments, Streamable HTTP is recommended; use WebSocket when full‑duplex, long‑lived connections are required or when integrating with existing WS infrastructure.

Capabilities at a Glance

  • Client: WebSocketTransport, WebSocketConfig (feature: websocket)
  • Server (optional): pmcp::server::transport::websocket::{WebSocketServerTransport, WebSocketServerConfig} (feature: websocket)
  • Persistent connection with JSON text frames; ping/pong keepalive

Client: Connect to a WebSocket Server

use pmcp::{Client, ClientCapabilities, WebSocketTransport, WebSocketConfig};
use std::time::Duration;
use url::Url;

#[tokio::main]
async fn main() -> pmcp::Result<()> {
    // Connect to an existing MCP server that speaks WebSocket
    let cfg = WebSocketConfig {
        url: Url::parse("ws://localhost:3000/mcp")?,
        auto_reconnect: true,
        reconnect_delay: Duration::from_secs(1),
        max_reconnect_delay: Duration::from_secs(30),
        max_reconnect_attempts: Some(5),
        ping_interval: Some(Duration::from_secs(30)),
        request_timeout: Duration::from_secs(30),
    };

    let transport = WebSocketTransport::new(cfg);
    transport.connect().await?;

    let mut client = Client::new(transport);
    let _info = client.initialize(ClientCapabilities::minimal()).await?;

    // Use the client normally (list tools, call tools, etc.)
    Ok(())
}

See examples/13_websocket_transport.rs for a complete walkthrough.

Server (Optional): Accept WebSocket Connections

PMCP includes a WebSocket server transport for custom scenarios. It yields a Transport you can pass to server.run(...) after accepting a connection. For most production servers, use Streamable HTTP.

use pmcp::{Server, ServerCapabilities, ToolHandler, RequestHandlerExtra};
use async_trait::async_trait;
use serde_json::{json, Value};
use pmcp::server::transport::websocket::{WebSocketServerTransport, WebSocketServerConfig};

struct Echo;

#[async_trait]
impl ToolHandler for Echo {
    async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> pmcp::Result<Value> {
        Ok(json!({ "echo": args }))
    }
}

#[tokio::main]
async fn main() -> pmcp::Result<()> {
    let server = Server::builder()
        .name("ws-server")
        .version("1.0.0")
        .capabilities(ServerCapabilities::tools_only())
        .tool("echo", Echo)
        .build()?;

    let mut ws = WebSocketServerTransport::new(WebSocketServerConfig::default());
    ws.bind().await?;   // Start listening (default 127.0.0.1:9001)
    ws.accept().await?; // Accept one connection

    // Run server over this transport (handles requests from that connection)
    server.run(ws).await
}

See examples/27_websocket_server_enhanced.rs for a multi‑client demo and additional capabilities.

Feature Flags

[dependencies]
pmcp = { version = "1.7", features = ["websocket"] }

When to Use WebSocket

  • Full-duplex, interactive sessions with low latency
  • Custom desktop/native apps that prefer persistent connections
  • Integration with existing WS gateways or load balancers

Prefer Streamable HTTP for most cloud/server deployments (SSE notifications, session management, and firewall friendliness).