Skip to main content

HttpClientTransport

Struct HttpClientTransport 

Source
pub struct HttpClientTransport { /* private fields */ }
Expand description

Client transport for MCP servers over Streamable HTTP.

Connects to a remote MCP server using the Streamable HTTP transport protocol. Manages session lifecycle (mcp-session-id), opens an SSE stream for server-initiated messages, and sends client requests via HTTP POST.

§How it works

The transport bridges HTTP’s request/response model with the ClientTransport trait’s send()/recv() message-passing model:

  • send() POSTs JSON-RPC messages to the server and queues the response body into an internal channel for recv() to return.
  • recv() reads from that channel, which also receives SSE events from a background task.

After the initialize handshake establishes a session, an SSE stream is automatically opened to receive server notifications and server-initiated requests.

§Example

use tower_mcp::client::{McpClient, HttpClientTransport};

let transport = HttpClientTransport::new("http://localhost:3000");
let client = McpClient::connect(transport).await?;

let info = client.initialize("my-client", "1.0.0").await?;
let tools = client.list_tools().await?;
client.shutdown().await?;

Implementations§

Source§

impl HttpClientTransport

Source

pub fn new(url: impl Into<String>) -> Self

Create a new HTTP client transport targeting the given URL.

Uses default configuration. The URL should be the MCP server’s Streamable HTTP endpoint (e.g., http://localhost:3000).

§Example
use tower_mcp::client::HttpClientTransport;

let transport = HttpClientTransport::new("http://localhost:3000");
Source

pub fn with_config(url: impl Into<String>, config: HttpClientConfig) -> Self

Create with custom configuration.

§Example
use tower_mcp::client::{HttpClientTransport, HttpClientConfig};
use std::time::Duration;

let config = HttpClientConfig {
    request_timeout: Duration::from_secs(60),
    sse_reconnect: false,
    ..Default::default()
};
let transport = HttpClientTransport::with_config("http://localhost:3000", config);
Source

pub fn with_client(url: impl Into<String>, client: Client) -> Self

Create with an existing reqwest::Client.

Use this when you need custom TLS configuration, proxy settings, or connection pooling.

§Example
use tower_mcp::client::HttpClientTransport;

let client = reqwest::Client::builder()
    .danger_accept_invalid_certs(true) // for development
    .build()
    .unwrap();
let transport = HttpClientTransport::with_client("https://mcp.example.com", client);
Source

pub fn bearer_token(self, token: impl Into<String>) -> Self

Set a Bearer token for Authorization: Bearer <token> authentication.

The token is included on every HTTP request (POST and SSE GET).

§Example
use tower_mcp::client::HttpClientTransport;

let transport = HttpClientTransport::new("http://localhost:3000")
    .bearer_token("sk-my-secret-token");
Source

pub fn api_key(self, key: impl Into<String>) -> Self

Set an API key for authentication.

Sends as Authorization: Bearer <key>. Use api_key_header for a custom header name.

§Example
use tower_mcp::client::HttpClientTransport;

let transport = HttpClientTransport::new("http://localhost:3000")
    .api_key("sk-my-api-key");
Source

pub fn api_key_header( self, name: impl Into<String>, key: impl Into<String>, ) -> Self

Set an API key using a custom header name.

Sends the key as the raw header value (no Bearer prefix).

§Example
use tower_mcp::client::HttpClientTransport;

let transport = HttpClientTransport::new("http://localhost:3000")
    .api_key_header("X-API-Key", "sk-my-api-key");
Source

pub fn basic_auth( self, username: impl AsRef<str>, password: impl AsRef<str>, ) -> Self

Set Basic authentication credentials.

Encodes username:password as Base64 and sends as Authorization: Basic <encoded>.

§Example
use tower_mcp::client::HttpClientTransport;

let transport = HttpClientTransport::new("http://localhost:3000")
    .basic_auth("admin", "secret");
Source

pub fn header(self, name: impl Into<String>, value: impl Into<String>) -> Self

Add a custom header to every request.

Can be called multiple times to add multiple headers.

§Example
use tower_mcp::client::HttpClientTransport;

let transport = HttpClientTransport::new("http://localhost:3000")
    .header("X-Custom-Header", "my-value")
    .header("X-Request-Source", "my-app");
Source

pub fn disable_session_recovery(self) -> Self

Disable automatic session recovery.

By default, if the server returns a session expired error (HTTP 404 with session ID or JSON-RPC -32005), the client will automatically re-initialize and retry the failed operation. Call this to disable that behavior and surface the error to the caller instead.

Source

pub fn with_token_provider(self, provider: impl TokenProvider) -> Self

Set a dynamic token provider for authentication.

The provider’s TokenProvider::get_token() is called before each HTTP request, and the returned token is sent as Authorization: Bearer <token>. This overrides any static Authorization header set via bearer_token() or basic_auth().

Use OAuthClientCredentials for OAuth 2.0 Client Credentials grants, or implement TokenProvider for custom token acquisition logic.

§Example
use tower_mcp::client::{HttpClientTransport, OAuthClientCredentials};

let provider = OAuthClientCredentials::builder()
    .client_id("my-client")
    .client_secret("my-secret")
    .token_endpoint("https://auth.example.com/token")
    .resource("http://localhost:3000")
    .build()?;

let transport = HttpClientTransport::new("http://localhost:3000")
    .with_token_provider(provider);
Source

pub fn with_scope_aware_token_provider<P>( self, provider: P, config: OAuthScopeEscalationConfig, ) -> Self

Set a token provider with bounded runtime scope escalation.

When an MCP operation receives an HTTP 403 Bearer challenge with error="insufficient_scope", the transport unions the challenged scopes with the scopes already tracked by config, invokes OAuthScopeEscalationHandler::reauthorize, asks the provider for a fresh token, and retries the same operation. Reauthorization is serialized across concurrent requests, and each operation is bounded by OAuthScopeEscalationConfig::maximum_attempts.

The provider and handler are the same value so the handler can update the token returned by TokenProvider::get_token.

Trait Implementations§

Source§

impl ClientTransport for HttpClientTransport

Source§

fn send<'life0, 'life1, 'async_trait>( &'life0 mut self, message: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Send a raw JSON message to the server. Read more
Source§

fn recv<'life0, 'async_trait>( &'life0 mut self, ) -> Pin<Box<dyn Future<Output = Result<Option<String>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Receive the next raw JSON message from the server. Read more
Source§

fn is_connected(&self) -> bool

Check if the transport is still connected.
Source§

fn close<'life0, 'async_trait>( &'life0 mut self, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Close the transport gracefully. Read more
Source§

fn reset_session<'life0, 'async_trait>( &'life0 mut self, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Reset the transport’s session state for re-initialization. Read more
Source§

fn supports_session_recovery(&self) -> bool

Whether this transport supports automatic session recovery. Read more
Source§

fn cancel_request<'life0, 'life1, 'async_trait>( &'life0 mut self, request_id: &'life1 RequestId, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Cancel one in-flight request. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more