feat: include reqwest in transport-streamble-http-client feature (#376)
* fix: add reqwest dependency to transport-streamable-http-client feature - Fix compilation error when using transport-streamable-http-client feature due to missing dependency - Move From<reqwest::Error> implementation to reqwest module * feat(rmcp): enhance transport features by decoupling reqwest - Added reqwest features for reqwest-based implementations. - Updated documentation - Modified error handling in SSE transport to use `String` for content type. - Updated examples to include new features * feat(rmcp): enhance transport features by decoupling reqwest - Added reqwest features for reqwest-based implementations - Updated documentation - Modified error handling in SSE transport to use `String` - Updated examples to include new features
This commit is contained in:
parent
7135f25d3d
commit
7f20a87825
10 changed files with 326 additions and 29 deletions
|
|
@ -103,12 +103,14 @@ server-side-http = [
|
||||||
client-side-sse = ["dep:sse-stream", "dep:http"]
|
client-side-sse = ["dep:sse-stream", "dep:http"]
|
||||||
|
|
||||||
transport-sse-client = ["client-side-sse", "transport-worker"]
|
transport-sse-client = ["client-side-sse", "transport-worker"]
|
||||||
|
transport-sse-client-reqwest = ["transport-sse-client", "reqwest"]
|
||||||
|
|
||||||
transport-worker = ["dep:tokio-stream"]
|
transport-worker = ["dep:tokio-stream"]
|
||||||
|
|
||||||
|
|
||||||
# Streamable HTTP client
|
# Streamable HTTP client
|
||||||
transport-streamable-http-client = ["client-side-sse", "transport-worker"]
|
transport-streamable-http-client = ["client-side-sse", "transport-worker"]
|
||||||
|
transport-streamable-http-client-reqwest = ["transport-streamable-http-client", "reqwest"]
|
||||||
|
|
||||||
|
|
||||||
transport-async-rw = ["tokio/io-util", "tokio-util/codec"]
|
transport-async-rw = ["tokio/io-util", "tokio-util/codec"]
|
||||||
|
|
|
||||||
|
|
@ -199,8 +199,10 @@ RMCP uses feature flags to control which components are included:
|
||||||
- `transport-async-rw`: Async read/write support
|
- `transport-async-rw`: Async read/write support
|
||||||
- `transport-io`: I/O stream support
|
- `transport-io`: I/O stream support
|
||||||
- `transport-child-process`: Child process support
|
- `transport-child-process`: Child process support
|
||||||
- `transport-sse-client` / `transport-sse-server`: SSE support
|
- `transport-sse-client` / `transport-sse-server`: SSE support (client agnostic)
|
||||||
- `transport-streamable-http-client` / `transport-streamable-http-server`: HTTP streaming
|
- `transport-sse-client-reqwest`: a default `reqwest` implementation of the SSE client
|
||||||
|
- `transport-streamable-http-client` / `transport-streamable-http-server`: HTTP streaming (client agnostic, see [`StreamableHttpClientTransport`] for details)
|
||||||
|
- `transport-streamable-http-client-reqwest`: a default `reqwest` implementation of the streamable http client
|
||||||
- `auth`: OAuth2 authentication support
|
- `auth`: OAuth2 authentication support
|
||||||
- `schemars`: JSON Schema generation (for tool definitions)
|
- `schemars`: JSON Schema generation (for tool definitions)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#[cfg(feature = "transport-streamable-http-client")]
|
#[cfg(feature = "transport-streamable-http-client-reqwest")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-client")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-client-reqwest")))]
|
||||||
mod streamable_http_client;
|
mod streamable_http_client;
|
||||||
|
|
||||||
#[cfg(feature = "transport-sse-client")]
|
#[cfg(feature = "transport-sse-client-reqwest")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "transport-sse-client")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "transport-sse-client-reqwest")))]
|
||||||
mod sse_client;
|
mod sse_client;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ use crate::transport::{
|
||||||
sse_client::{SseClient, SseClientConfig, SseTransportError},
|
sse_client::{SseClient, SseClientConfig, SseTransportError},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for SseTransportError<reqwest::Error> {
|
||||||
|
fn from(e: reqwest::Error) -> Self {
|
||||||
|
SseTransportError::Client(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SseClient for reqwest::Client {
|
impl SseClient for reqwest::Client {
|
||||||
type Error = reqwest::Error;
|
type Error = reqwest::Error;
|
||||||
|
|
||||||
|
|
@ -55,7 +61,9 @@ impl SseClient for reqwest::Client {
|
||||||
match response.headers().get(reqwest::header::CONTENT_TYPE) {
|
match response.headers().get(reqwest::header::CONTENT_TYPE) {
|
||||||
Some(ct) => {
|
Some(ct) => {
|
||||||
if !ct.as_bytes().starts_with(EVENT_STREAM_MIME_TYPE.as_bytes()) {
|
if !ct.as_bytes().starts_with(EVENT_STREAM_MIME_TYPE.as_bytes()) {
|
||||||
return Err(SseTransportError::UnexpectedContentType(Some(ct.clone())));
|
return Err(SseTransportError::UnexpectedContentType(Some(
|
||||||
|
String::from_utf8_lossy(ct.as_bytes()).to_string(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -68,6 +76,33 @@ impl SseClient for reqwest::Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SseClientTransport<reqwest::Client> {
|
impl SseClientTransport<reqwest::Client> {
|
||||||
|
/// Creates a new transport using reqwest with the specified SSE endpoint.
|
||||||
|
///
|
||||||
|
/// This is a convenience method that creates a transport using the default
|
||||||
|
/// reqwest client. This method is only available when the
|
||||||
|
/// `transport-sse-client-reqwest` feature is enabled.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `uri` - The SSE endpoint to connect to
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rmcp::transport::SseClientTransport;
|
||||||
|
///
|
||||||
|
/// // Enable the reqwest feature in Cargo.toml:
|
||||||
|
/// // rmcp = { version = "0.5", features = ["transport-sse-client-reqwest"] }
|
||||||
|
///
|
||||||
|
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let transport = SseClientTransport::start("http://localhost:8000/sse").await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Feature requirement
|
||||||
|
///
|
||||||
|
/// This method requires the `transport-sse-client-reqwest` feature.
|
||||||
pub async fn start(
|
pub async fn start(
|
||||||
uri: impl Into<Arc<str>>,
|
uri: impl Into<Arc<str>>,
|
||||||
) -> Result<Self, SseTransportError<reqwest::Error>> {
|
) -> Result<Self, SseTransportError<reqwest::Error>> {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for StreamableHttpError<reqwest::Error> {
|
||||||
|
fn from(e: reqwest::Error) -> Self {
|
||||||
|
StreamableHttpError::Client(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StreamableHttpClient for reqwest::Client {
|
impl StreamableHttpClient for reqwest::Client {
|
||||||
type Error = reqwest::Error;
|
type Error = reqwest::Error;
|
||||||
|
|
||||||
|
|
@ -125,6 +131,30 @@ impl StreamableHttpClient for reqwest::Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamableHttpClientTransport<reqwest::Client> {
|
impl StreamableHttpClientTransport<reqwest::Client> {
|
||||||
|
/// Creates a new transport using reqwest with the specified URI.
|
||||||
|
///
|
||||||
|
/// This is a convenience method that creates a transport using the default
|
||||||
|
/// reqwest client. This method is only available when the
|
||||||
|
/// `transport-streamable-http-client-reqwest` feature is enabled.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `uri` - The server URI to connect to
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use rmcp::transport::StreamableHttpClientTransport;
|
||||||
|
///
|
||||||
|
/// // Enable the reqwest feature in Cargo.toml:
|
||||||
|
/// // rmcp = { version = "0.5", features = ["transport-streamable-http-client-reqwest"] }
|
||||||
|
///
|
||||||
|
/// let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Feature requirement
|
||||||
|
///
|
||||||
|
/// This method requires the `transport-streamable-http-client-reqwest` feature.
|
||||||
pub fn from_uri(uri: impl Into<Arc<str>>) -> Self {
|
pub fn from_uri(uri: impl Into<Arc<str>>) -> Self {
|
||||||
StreamableHttpClientTransport::with_client(
|
StreamableHttpClientTransport::with_client(
|
||||||
reqwest::Client::default(),
|
reqwest::Client::default(),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use std::{pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use futures::{StreamExt, future::BoxFuture};
|
use futures::{StreamExt, future::BoxFuture};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use reqwest::header::HeaderValue;
|
|
||||||
use sse_stream::Error as SseError;
|
use sse_stream::Error as SseError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -28,7 +27,7 @@ pub enum SseTransportError<E: std::error::Error + Send + Sync + 'static> {
|
||||||
#[error("unexpected end of stream")]
|
#[error("unexpected end of stream")]
|
||||||
UnexpectedEndOfStream,
|
UnexpectedEndOfStream,
|
||||||
#[error("Unexpected content type: {0:?}")]
|
#[error("Unexpected content type: {0:?}")]
|
||||||
UnexpectedContentType(Option<HeaderValue>),
|
UnexpectedContentType(Option<String>),
|
||||||
#[cfg(feature = "auth")]
|
#[cfg(feature = "auth")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "auth")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "auth")))]
|
||||||
#[error("Auth error: {0}")]
|
#[error("Auth error: {0}")]
|
||||||
|
|
@ -39,12 +38,6 @@ pub enum SseTransportError<E: std::error::Error + Send + Sync + 'static> {
|
||||||
InvalidUriParts(#[from] http::uri::InvalidUriParts),
|
InvalidUriParts(#[from] http::uri::InvalidUriParts),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<reqwest::Error> for SseTransportError<reqwest::Error> {
|
|
||||||
fn from(e: reqwest::Error) -> Self {
|
|
||||||
SseTransportError::Client(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SseClient: Clone + Send + Sync + 'static {
|
pub trait SseClient: Clone + Send + Sync + 'static {
|
||||||
type Error: std::error::Error + Send + Sync + 'static;
|
type Error: std::error::Error + Send + Sync + 'static;
|
||||||
fn post_message(
|
fn post_message(
|
||||||
|
|
@ -77,6 +70,87 @@ impl<C: SseClient> SseStreamReconnect for SseClientReconnect<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type ServerMessageStream<C> = Pin<Box<SseAutoReconnectStream<SseClientReconnect<C>>>>;
|
type ServerMessageStream<C> = Pin<Box<SseAutoReconnectStream<SseClientReconnect<C>>>>;
|
||||||
|
|
||||||
|
/// A client-agnostic SSE transport for RMCP that supports Server-Sent Events.
|
||||||
|
///
|
||||||
|
/// This transport allows you to choose your preferred HTTP client implementation
|
||||||
|
/// by implementing the [`SseClient`] trait. The transport handles SSE streaming
|
||||||
|
/// and automatic reconnection.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// ## Using reqwest
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rmcp::transport::SseClientTransport;
|
||||||
|
///
|
||||||
|
/// // Enable the reqwest feature in Cargo.toml:
|
||||||
|
/// // rmcp = { version = "0.5", features = ["transport-sse-client-reqwest"] }
|
||||||
|
///
|
||||||
|
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let transport = SseClientTransport::start("http://localhost:8000/sse").await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Using a custom HTTP client
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rmcp::transport::sse_client::{SseClient, SseClientTransport, SseClientConfig};
|
||||||
|
/// use std::sync::Arc;
|
||||||
|
/// use futures::stream::BoxStream;
|
||||||
|
/// use rmcp::model::ClientJsonRpcMessage;
|
||||||
|
/// use sse_stream::{Sse, Error as SseError};
|
||||||
|
/// use http::Uri;
|
||||||
|
///
|
||||||
|
/// #[derive(Clone)]
|
||||||
|
/// struct MyHttpClient;
|
||||||
|
///
|
||||||
|
/// #[derive(Debug, thiserror::Error)]
|
||||||
|
/// struct MyError;
|
||||||
|
///
|
||||||
|
/// impl std::fmt::Display for MyError {
|
||||||
|
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
/// write!(f, "MyError")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl SseClient for MyHttpClient {
|
||||||
|
/// type Error = MyError;
|
||||||
|
///
|
||||||
|
/// async fn post_message(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Uri,
|
||||||
|
/// _message: ClientJsonRpcMessage,
|
||||||
|
/// _auth_token: Option<String>,
|
||||||
|
/// ) -> Result<(), rmcp::transport::sse_client::SseTransportError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn get_stream(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Uri,
|
||||||
|
/// _last_event_id: Option<String>,
|
||||||
|
/// _auth_token: Option<String>,
|
||||||
|
/// ) -> Result<BoxStream<'static, Result<Sse, SseError>>, rmcp::transport::sse_client::SseTransportError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let config = SseClientConfig {
|
||||||
|
/// sse_endpoint: "http://localhost:8000/sse".into(),
|
||||||
|
/// ..Default::default()
|
||||||
|
/// };
|
||||||
|
/// let transport = SseClientTransport::start_with_client(MyHttpClient, config).await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Feature Flags
|
||||||
|
///
|
||||||
|
/// - `transport-sse-client`: Base feature providing the generic transport infrastructure
|
||||||
|
/// - `transport-sse-client-reqwest`: Includes reqwest HTTP client support with convenience methods
|
||||||
pub struct SseClientTransport<C: SseClient> {
|
pub struct SseClientTransport<C: SseClient> {
|
||||||
client: C,
|
client: C,
|
||||||
config: SseClientConfig,
|
config: SseClientConfig,
|
||||||
|
|
|
||||||
|
|
@ -50,17 +50,12 @@ pub enum StreamableHttpError<E: std::error::Error + Send + Sync + 'static> {
|
||||||
Auth(#[from] crate::transport::auth::AuthError),
|
Auth(#[from] crate::transport::auth::AuthError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<reqwest::Error> for StreamableHttpError<reqwest::Error> {
|
|
||||||
fn from(e: reqwest::Error) -> Self {
|
|
||||||
StreamableHttpError::Client(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum StreamableHttpProtocolError {
|
pub enum StreamableHttpProtocolError {
|
||||||
#[error("Missing session id in response")]
|
#[error("Missing session id in response")]
|
||||||
MissingSessionIdInResponse,
|
MissingSessionIdInResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum StreamableHttpPostResponse {
|
pub enum StreamableHttpPostResponse {
|
||||||
Accepted,
|
Accepted,
|
||||||
Json(ServerJsonRpcMessage, Option<String>),
|
Json(ServerJsonRpcMessage, Option<String>),
|
||||||
|
|
@ -490,9 +485,170 @@ impl<C: StreamableHttpClient> Worker for StreamableHttpClientWorker<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A client-agnostic HTTP transport for RMCP that supports streaming responses.
|
||||||
|
///
|
||||||
|
/// This transport allows you to choose your preferred HTTP client implementation
|
||||||
|
/// by implementing the [`StreamableHttpClient`] trait. The transport handles
|
||||||
|
/// session management, SSE streaming, and automatic reconnection.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// ## Using reqwest
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use rmcp::transport::StreamableHttpClientTransport;
|
||||||
|
///
|
||||||
|
/// // Enable the reqwest feature in Cargo.toml:
|
||||||
|
/// // rmcp = { version = "0.5", features = ["transport-streamable-http-client-reqwest"] }
|
||||||
|
///
|
||||||
|
/// let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Using a custom HTTP client
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use rmcp::transport::streamable_http_client::{
|
||||||
|
/// StreamableHttpClient,
|
||||||
|
/// StreamableHttpClientTransport,
|
||||||
|
/// StreamableHttpClientTransportConfig
|
||||||
|
/// };
|
||||||
|
/// use std::sync::Arc;
|
||||||
|
/// use futures::stream::BoxStream;
|
||||||
|
/// use rmcp::model::ClientJsonRpcMessage;
|
||||||
|
/// use sse_stream::{Sse, Error as SseError};
|
||||||
|
///
|
||||||
|
/// #[derive(Clone)]
|
||||||
|
/// struct MyHttpClient;
|
||||||
|
///
|
||||||
|
/// #[derive(Debug, thiserror::Error)]
|
||||||
|
/// struct MyError;
|
||||||
|
///
|
||||||
|
/// impl std::fmt::Display for MyError {
|
||||||
|
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
/// write!(f, "MyError")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl StreamableHttpClient for MyHttpClient {
|
||||||
|
/// type Error = MyError;
|
||||||
|
///
|
||||||
|
/// async fn post_message(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Arc<str>,
|
||||||
|
/// _message: ClientJsonRpcMessage,
|
||||||
|
/// _session_id: Option<Arc<str>>,
|
||||||
|
/// _auth_header: Option<String>,
|
||||||
|
/// ) -> Result<rmcp::transport::streamable_http_client::StreamableHttpPostResponse, rmcp::transport::streamable_http_client::StreamableHttpError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn delete_session(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Arc<str>,
|
||||||
|
/// _session_id: Arc<str>,
|
||||||
|
/// _auth_header: Option<String>,
|
||||||
|
/// ) -> Result<(), rmcp::transport::streamable_http_client::StreamableHttpError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn get_stream(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Arc<str>,
|
||||||
|
/// _session_id: Arc<str>,
|
||||||
|
/// _last_event_id: Option<String>,
|
||||||
|
/// _auth_header: Option<String>,
|
||||||
|
/// ) -> Result<BoxStream<'static, Result<Sse, SseError>>, rmcp::transport::streamable_http_client::StreamableHttpError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let transport = StreamableHttpClientTransport::with_client(
|
||||||
|
/// MyHttpClient,
|
||||||
|
/// StreamableHttpClientTransportConfig::with_uri("http://localhost:8000/mcp")
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Feature Flags
|
||||||
|
///
|
||||||
|
/// - `transport-streamable-http-client`: Base feature providing the generic transport infrastructure
|
||||||
|
/// - `transport-streamable-http-client-reqwest`: Includes reqwest HTTP client support with convenience methods
|
||||||
pub type StreamableHttpClientTransport<C> = WorkerTransport<StreamableHttpClientWorker<C>>;
|
pub type StreamableHttpClientTransport<C> = WorkerTransport<StreamableHttpClientWorker<C>>;
|
||||||
|
|
||||||
impl<C: StreamableHttpClient> StreamableHttpClientTransport<C> {
|
impl<C: StreamableHttpClient> StreamableHttpClientTransport<C> {
|
||||||
|
/// Creates a new transport with a custom HTTP client implementation.
|
||||||
|
///
|
||||||
|
/// This method allows you to use any HTTP client that implements the [`StreamableHttpClient`] trait.
|
||||||
|
/// Use this when you want to use a custom HTTP client or when the reqwest feature is not enabled.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `client` - Your HTTP client implementation
|
||||||
|
/// * `config` - Transport configuration including the server URI
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use rmcp::transport::streamable_http_client::{
|
||||||
|
/// StreamableHttpClient,
|
||||||
|
/// StreamableHttpClientTransport,
|
||||||
|
/// StreamableHttpClientTransportConfig
|
||||||
|
/// };
|
||||||
|
/// use std::sync::Arc;
|
||||||
|
/// use futures::stream::BoxStream;
|
||||||
|
/// use rmcp::model::ClientJsonRpcMessage;
|
||||||
|
/// use sse_stream::{Sse, Error as SseError};
|
||||||
|
///
|
||||||
|
/// // Define your custom client
|
||||||
|
/// #[derive(Clone)]
|
||||||
|
/// struct MyHttpClient;
|
||||||
|
///
|
||||||
|
/// #[derive(Debug, thiserror::Error)]
|
||||||
|
/// struct MyError;
|
||||||
|
///
|
||||||
|
/// impl std::fmt::Display for MyError {
|
||||||
|
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
/// write!(f, "MyError")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl StreamableHttpClient for MyHttpClient {
|
||||||
|
/// type Error = MyError;
|
||||||
|
///
|
||||||
|
/// async fn post_message(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Arc<str>,
|
||||||
|
/// _message: ClientJsonRpcMessage,
|
||||||
|
/// _session_id: Option<Arc<str>>,
|
||||||
|
/// _auth_header: Option<String>,
|
||||||
|
/// ) -> Result<rmcp::transport::streamable_http_client::StreamableHttpPostResponse, rmcp::transport::streamable_http_client::StreamableHttpError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn delete_session(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Arc<str>,
|
||||||
|
/// _session_id: Arc<str>,
|
||||||
|
/// _auth_header: Option<String>,
|
||||||
|
/// ) -> Result<(), rmcp::transport::streamable_http_client::StreamableHttpError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn get_stream(
|
||||||
|
/// &self,
|
||||||
|
/// _uri: Arc<str>,
|
||||||
|
/// _session_id: Arc<str>,
|
||||||
|
/// _last_event_id: Option<String>,
|
||||||
|
/// _auth_header: Option<String>,
|
||||||
|
/// ) -> Result<BoxStream<'static, Result<Sse, SseError>>, rmcp::transport::streamable_http_client::StreamableHttpError<Self::Error>> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let transport = StreamableHttpClientTransport::with_client(
|
||||||
|
/// MyHttpClient,
|
||||||
|
/// StreamableHttpClientTransportConfig::with_uri("http://localhost:8000/mcp")
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
pub fn with_client(client: C, config: StreamableHttpClientTransportConfig) -> Self {
|
pub fn with_client(client: C, config: StreamableHttpClientTransportConfig) -> Self {
|
||||||
let worker = StreamableHttpClientWorker::new(client, config);
|
let worker = StreamableHttpClientWorker::new(client, config);
|
||||||
WorkerTransport::spawn(worker)
|
WorkerTransport::spawn(worker)
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ publish = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rmcp = { workspace = true, features = [
|
rmcp = { workspace = true, features = [
|
||||||
"client",
|
"client",
|
||||||
"transport-sse-client",
|
"transport-sse-client-reqwest",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"transport-streamable-http-client",
|
"transport-streamable-http-client-reqwest",
|
||||||
"transport-child-process",
|
"transport-child-process",
|
||||||
"tower",
|
"tower",
|
||||||
"auth"
|
"auth"
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ rig-core = "0.15.1"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
rmcp = { workspace = true, features = [
|
rmcp = { workspace = true, features = [
|
||||||
"client",
|
"client",
|
||||||
"reqwest",
|
|
||||||
"transport-child-process",
|
"transport-child-process",
|
||||||
"transport-sse-client",
|
"transport-sse-client-reqwest",
|
||||||
"transport-streamable-http-client"
|
"transport-streamable-http-client-reqwest"
|
||||||
] }
|
] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@ toml = "0.9"
|
||||||
rmcp = { workspace = true, features = [
|
rmcp = { workspace = true, features = [
|
||||||
"client",
|
"client",
|
||||||
"transport-child-process",
|
"transport-child-process",
|
||||||
"transport-sse-client",
|
"transport-sse-client-reqwest",
|
||||||
"transport-streamable-http-client",
|
"transport-streamable-http-client-reqwest"
|
||||||
"reqwest"
|
|
||||||
], no-default-features = true }
|
], no-default-features = true }
|
||||||
clap = { version = "4.0", features = ["derive"] }
|
clap = { version = "4.0", features = ["derive"] }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue