Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 94 additions & 8 deletions pmcp-book/src/ch10-01-websocket.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,97 @@
# Ch10 01 Websocket
# Chapter 10.1: WebSocket Transport

This chapter is under development. Check back soon!
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.

## Coming Soon
## 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

```rust
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.

```rust
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

```toml
[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).

This section will cover:
- Core concepts and implementation
- Working examples with explanations
- Best practices and patterns
- Real-world use cases
75 changes: 67 additions & 8 deletions pmcp-book/src/ch10-02-http.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,70 @@
# Ch10 02 Http
# Chapter 10.2: HTTP Transport (Client)

This chapter is under development. Check back soon!
PMCP’s HTTP transport is a client-side implementation that sends JSON-RPC requests over HTTP and can optionally subscribe to Server-Sent Events (SSE) for notifications. For server deployments, see Streamable HTTP in Chapter 10.3.

## Coming Soon
## When to Use HTTP Transport

- Simple request/response interactions against an HTTP MCP endpoint
- Optional SSE notifications via a separate endpoint
- Firewall-friendly and proxy-compatible

If you control the server, prefer Streamable HTTP (single endpoint with JSON and SSE, plus session support).

## Features and Types

- `HttpTransport`, `HttpConfig` (feature: `http`)
- Optional connection pooling and configurable timeouts
- Optional `sse_endpoint` for notifications

## Basic Client Example

```rust
use pmcp::{Client, ClientCapabilities, HttpTransport, HttpConfig};
use url::Url;

#[tokio::main]
async fn main() -> pmcp::Result<()> {
// Create HTTP transport
let transport = HttpTransport::with_url(Url::parse("http://localhost:8080")?)?;

// Optionally connect to SSE notifications if your server exposes one
// transport.connect_sse().await?;

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

// Use the client as usual (list tools, call tools, etc.)
Ok(())
}
```

## Configuration

```rust
use pmcp::HttpConfig;
use url::Url;
use std::time::Duration;

let cfg = HttpConfig {
base_url: Url::parse("https://api.example.com/mcp")?,
sse_endpoint: Some("/events".into()), // or None if not using SSE
timeout: Duration::from_secs(30),
headers: vec![("Authorization".into(), "Bearer <token>".into())],
enable_pooling: true,
max_idle_per_host: 10,
};
```

## Feature Flags

```toml
[dependencies]
pmcp = { version = "1.7", features = ["http"] }
```

## Notes

- This client can target generic HTTP-style MCP servers.
- For PMCP’s recommended server implementation, see Streamable HTTP (Axum-based) in Chapter 10.3.

This section will cover:
- Core concepts and implementation
- Working examples with explanations
- Best practices and patterns
- Real-world use cases
126 changes: 118 additions & 8 deletions pmcp-book/src/ch10-03-streamable-http.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,121 @@
# Ch10 03 Streamable Http
# Chapter 10.3: Streamable HTTP (Server + Client)

This chapter is under development. Check back soon!
Streamable HTTP is PMCP’s preferred transport for servers. It combines JSON requests with Server‑Sent Events (SSE) for notifications, supports both stateless and stateful operation, and uses a single endpoint with content negotiation via the `Accept` header.

## Coming Soon
## Why Streamable HTTP?

- Single endpoint for requests and notifications
- Works well through proxies and enterprise firewalls
- Optional sessions for stateful, multi-request workflows
- Built with Axum, provided by the SDK

## Server (Axum-based)

Types: `pmcp::server::streamable_http_server::{StreamableHttpServer, StreamableHttpServerConfig}` (feature: `streamable-http`).

```rust
use pmcp::{Server, ServerCapabilities, ToolHandler, RequestHandlerExtra};
use pmcp::server::streamable_http_server::{StreamableHttpServer, StreamableHttpServerConfig};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::sync::Mutex;

struct Add;

#[async_trait]
impl ToolHandler for Add {
async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> pmcp::Result<Value> {
let a = args["a"].as_f64().unwrap_or(0.0);
let b = args["b"].as_f64().unwrap_or(0.0);
Ok(json!({"result": a + b}))
}
}

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

let server = Arc::new(Mutex::new(server));
let addr: SocketAddr = ([0,0,0,0], 8080).into();

// Default: stateful with SSE support
let http = StreamableHttpServer::new(addr, server.clone());
let (bound, _handle) = http.start().await?;
println!("Streamable HTTP listening on {}", bound);
Ok(())
}
```

### Stateless vs Stateful

```rust
// Stateless (serverless-friendly): no session tracking
let cfg = StreamableHttpServerConfig {
session_id_generator: None,
enable_json_response: false, // prefer SSE for notifications
event_store: None,
on_session_initialized: None,
on_session_closed: None,
};
let http = StreamableHttpServer::with_config(addr, server, cfg);
```

## Protocol Details

Headers enforced by the server:
- `mcp-protocol-version`: Protocol version (e.g., `2024-11-05`)
- `Accept`: Must include `application/json` or `text/event-stream`
- `mcp-session-id`: Present in stateful mode
- `Last-Event-Id`: For SSE resumption

Accept rules:
- `Accept: application/json` → JSON responses only
- `Accept: text/event-stream` → SSE stream for notifications

## Client (Streamable HTTP)

Types: `pmcp::shared::streamable_http::{StreamableHttpTransport, StreamableHttpTransportConfig}` (feature: `streamable-http`).

```rust
use pmcp::{Client, ClientCapabilities};
use pmcp::shared::streamable_http::{StreamableHttpTransport, StreamableHttpTransportConfig};
use url::Url;

#[tokio::main]
async fn main() -> pmcp::Result<()> {
let cfg = StreamableHttpTransportConfig {
url: Url::parse("http://localhost:8080")?,
extra_headers: vec![],
auth_provider: None,
session_id: None,
enable_json_response: true, // or false to use SSE
on_resumption_token: None,
};

let transport = StreamableHttpTransport::new(cfg);
let mut client = Client::new(transport);
let _info = client.initialize(ClientCapabilities::minimal()).await?;
Ok(())
}
```

## Examples

- `examples/22_streamable_http_server_stateful.rs` – Stateful mode with SSE notifications
- `examples/23_streamable_http_server_stateless.rs` – Stateless/serverless-friendly configuration
- `examples/24_streamable_http_client.rs` – Client connecting to both modes

## Feature Flags

```toml
[dependencies]
pmcp = { version = "1.7", features = ["streamable-http"] }
```

This section will cover:
- Core concepts and implementation
- Working examples with explanations
- Best practices and patterns
- Real-world use cases
Loading