feat: unified error type (#308)
* feat: unified error type * fix: use type id to compare transport type
This commit is contained in:
parent
c74b26b3e3
commit
e615300fea
23 changed files with 286 additions and 161 deletions
|
|
@ -134,7 +134,7 @@ impl ServerHandler for MyToolHandler {
|
|||
&self,
|
||||
request: CallToolRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<CallToolResult, rmcp::Error> {
|
||||
) -> Result<CallToolResult, rmcp::ErrorData> {
|
||||
let tcc = ToolCallContext::new(self, request, context);
|
||||
self.tool_router.call(tcc).await
|
||||
}
|
||||
|
|
@ -143,7 +143,7 @@ impl ServerHandler for MyToolHandler {
|
|||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListToolsResult, rmcp::Error> {
|
||||
) -> Result<ListToolsResult, rmcp::ErrorData> {
|
||||
let items = self.tool_router.list_all();
|
||||
Ok(ListToolsResult::with_all_items(items))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
/// &self,
|
||||
/// request: CallToolRequestParam,
|
||||
/// context: RequestContext<RoleServer>,
|
||||
/// ) -> Result<CallToolResult, rmcp::Error> {
|
||||
/// ) -> Result<CallToolResult, rmcp::ErrorData> {
|
||||
/// let tcc = ToolCallContext::new(self, request, context);
|
||||
/// self.tool_router.call(tcc).await
|
||||
/// }
|
||||
|
|
@ -148,7 +148,7 @@ pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
/// &self,
|
||||
/// _request: Option<PaginatedRequestParam>,
|
||||
/// _context: RequestContext<RoleServer>,
|
||||
/// ) -> Result<ListToolsResult, rmcp::Error> {
|
||||
/// ) -> Result<ListToolsResult, rmcp::ErrorData> {
|
||||
/// let items = self.tool_router.list_all();
|
||||
/// Ok(ListToolsResult::with_all_items(items))
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ pub fn tool_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
|
|||
&self,
|
||||
request: rmcp::model::CallToolRequestParam,
|
||||
context: rmcp::service::RequestContext<rmcp::RoleServer>,
|
||||
) -> Result<rmcp::model::CallToolResult, rmcp::Error> {
|
||||
) -> Result<rmcp::model::CallToolResult, rmcp::ErrorData> {
|
||||
let tcc = rmcp::handler::server::tool::ToolCallContext::new(self, request, context);
|
||||
#router.call(tcc).await
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ pub fn tool_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
|
|||
&self,
|
||||
_request: Option<rmcp::model::PaginatedRequestParam>,
|
||||
_context: rmcp::service::RequestContext<rmcp::RoleServer>,
|
||||
) -> Result<rmcp::model::ListToolsResult, rmcp::Error> {
|
||||
) -> Result<rmcp::model::ListToolsResult, rmcp::ErrorData> {
|
||||
Ok(rmcp::model::ListToolsResult::with_all_items(#router.list_all()))
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ wait for the first release.
|
|||
Creating a server with tools is simple using the `#[tool]` macro:
|
||||
|
||||
```rust, ignore
|
||||
use rmcp::{Error as McpError, ServiceExt, model::*, tool, tool_handler, tool_router, transport::stdio, handler::server::router::tool::ToolRouter};
|
||||
use rmcp::{ErrorData as McpError, ServiceExt, model::*, tool, tool_router, transport::stdio, handler::server::tool::ToolCallContext, handler::server::router::tool::ToolRouter};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use crate::model::ErrorData;
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
|
||||
use crate::ServiceError;
|
||||
pub use crate::model::ErrorData;
|
||||
#[deprecated(
|
||||
note = "Use `rmcp::ErrorData` instead, `rmcp::ErrorData` could become `RmcpError` in the future."
|
||||
)]
|
||||
pub type Error = ErrorData;
|
||||
|
||||
impl Display for ErrorData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {}", self.code.0, self.message)?;
|
||||
|
|
@ -15,3 +17,40 @@ impl Display for ErrorData {
|
|||
}
|
||||
|
||||
impl std::error::Error for ErrorData {}
|
||||
|
||||
/// This is an unified error type for the errors could be returned by the service.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum RmcpError {
|
||||
#[error("Service error: {0}")]
|
||||
Service(#[from] ServiceError),
|
||||
#[cfg(feature = "client")]
|
||||
#[error("Client initialization error: {0}")]
|
||||
ClientInitialize(#[from] crate::service::ClientInitializeError),
|
||||
#[cfg(feature = "server")]
|
||||
#[error("Server initialization error: {0}")]
|
||||
ServerInitialize(#[from] crate::service::ServerInitializeError),
|
||||
#[error("Runtime error: {0}")]
|
||||
Runtime(#[from] tokio::task::JoinError),
|
||||
#[error("Transport creation error: {error}")]
|
||||
// TODO: Maybe we can introduce something like `TryIntoTransport` to auto wrap transport type,
|
||||
// but it could be an breaking change, so we could do it in the future.
|
||||
TransportCreation {
|
||||
into_transport_type_name: Cow<'static, str>,
|
||||
into_transport_type_id: std::any::TypeId,
|
||||
#[source]
|
||||
error: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
// and cancellation shouldn't be an error?
|
||||
}
|
||||
|
||||
impl RmcpError {
|
||||
pub fn transport_creation<T: 'static>(
|
||||
error: impl Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
) -> Self {
|
||||
RmcpError::TransportCreation {
|
||||
into_transport_type_id: std::any::TypeId::of::<T>(),
|
||||
into_transport_type_name: std::any::type_name::<T>().into(),
|
||||
error: error.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
pub mod progress;
|
||||
use crate::{
|
||||
error::Error as McpError,
|
||||
error::ErrorData as McpError,
|
||||
model::*,
|
||||
service::{NotificationContext, RequestContext, RoleClient, Service, ServiceRole},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
error::Error as McpError,
|
||||
error::ErrorData as McpError,
|
||||
model::*,
|
||||
service::{NotificationContext, RequestContext, RoleServer, Service, ServiceRole},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ where
|
|||
&self,
|
||||
notification: <RoleServer as crate::service::ServiceRole>::PeerNot,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> Result<(), crate::Error> {
|
||||
) -> Result<(), crate::ErrorData> {
|
||||
self.service
|
||||
.handle_notification(notification, context)
|
||||
.await
|
||||
|
|
@ -60,7 +60,7 @@ where
|
|||
&self,
|
||||
request: <RoleServer as crate::service::ServiceRole>::PeerReq,
|
||||
context: crate::service::RequestContext<RoleServer>,
|
||||
) -> Result<<RoleServer as crate::service::ServiceRole>::Resp, crate::Error> {
|
||||
) -> Result<<RoleServer as crate::service::ServiceRole>::Resp, crate::ErrorData> {
|
||||
match request {
|
||||
ClientRequest::CallToolRequest(request) => {
|
||||
if self.tool_router.has_route(request.params.name.as_ref())
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl<S: Send + Sync + 'static> ToolRoute<S> {
|
|||
where
|
||||
C: for<'a> Fn(
|
||||
ToolCallContext<'a, S>,
|
||||
) -> BoxFuture<'a, Result<CallToolResult, crate::Error>>
|
||||
) -> BoxFuture<'a, Result<CallToolResult, crate::ErrorData>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
|
|
@ -237,11 +237,11 @@ where
|
|||
pub async fn call(
|
||||
&self,
|
||||
context: ToolCallContext<'_, S>,
|
||||
) -> Result<CallToolResult, crate::Error> {
|
||||
) -> Result<CallToolResult, crate::ErrorData> {
|
||||
let item = self
|
||||
.map
|
||||
.get(context.name())
|
||||
.ok_or_else(|| crate::Error::invalid_params("tool not found", None))?;
|
||||
.ok_or_else(|| crate::ErrorData::invalid_params("tool not found", None))?;
|
||||
(item.call)(context).await
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ pub fn cached_schema_for_type<T: JsonSchema + std::any::Any>() -> Arc<JsonObject
|
|||
}
|
||||
|
||||
/// Deserialize a JSON object into a type
|
||||
pub fn parse_json_object<T: DeserializeOwned>(input: JsonObject) -> Result<T, crate::Error> {
|
||||
pub fn parse_json_object<T: DeserializeOwned>(input: JsonObject) -> Result<T, crate::ErrorData> {
|
||||
serde_json::from_value(serde_json::Value::Object(input)).map_err(|e| {
|
||||
crate::Error::invalid_params(
|
||||
crate::ErrorData::invalid_params(
|
||||
format!("failed to deserialize parameters: {error}", error = e),
|
||||
None,
|
||||
)
|
||||
|
|
@ -93,21 +93,23 @@ impl<'s, S> ToolCallContext<'s, S> {
|
|||
}
|
||||
|
||||
pub trait FromToolCallContextPart<S>: Sized {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error>;
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData>;
|
||||
}
|
||||
|
||||
pub trait IntoCallToolResult {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error>;
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData>;
|
||||
}
|
||||
|
||||
impl<T: IntoContents> IntoCallToolResult for T {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData> {
|
||||
Ok(CallToolResult::success(self.into_contents()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoContents, E: IntoContents> IntoCallToolResult for Result<T, E> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData> {
|
||||
match self {
|
||||
Ok(value) => Ok(CallToolResult::success(value.into_contents())),
|
||||
Err(error) => Ok(CallToolResult::error(error.into_contents())),
|
||||
|
|
@ -115,8 +117,8 @@ impl<T: IntoContents, E: IntoContents> IntoCallToolResult for Result<T, E> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: IntoCallToolResult> IntoCallToolResult for Result<T, crate::Error> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
|
||||
impl<T: IntoCallToolResult> IntoCallToolResult for Result<T, crate::ErrorData> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData> {
|
||||
match self {
|
||||
Ok(value) => value.into_call_tool_result(),
|
||||
Err(error) => Err(error),
|
||||
|
|
@ -134,7 +136,7 @@ pin_project_lite::pin_project! {
|
|||
},
|
||||
Ready {
|
||||
#[pin]
|
||||
result: Ready<Result<CallToolResult, crate::Error>>,
|
||||
result: Ready<Result<CallToolResult, crate::ErrorData>>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,7 +146,7 @@ where
|
|||
F: Future<Output = R>,
|
||||
R: IntoCallToolResult,
|
||||
{
|
||||
type Output = Result<CallToolResult, crate::Error>;
|
||||
type Output = Result<CallToolResult, crate::ErrorData>;
|
||||
|
||||
fn poll(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
|
|
@ -159,8 +161,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoCallToolResult for Result<CallToolResult, crate::Error> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
|
||||
impl IntoCallToolResult for Result<CallToolResult, crate::ErrorData> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -169,10 +171,10 @@ pub trait CallToolHandler<S, A> {
|
|||
fn call(
|
||||
self,
|
||||
context: ToolCallContext<'_, S>,
|
||||
) -> BoxFuture<'_, Result<CallToolResult, crate::Error>>;
|
||||
) -> BoxFuture<'_, Result<CallToolResult, crate::ErrorData>>;
|
||||
}
|
||||
|
||||
pub type DynCallToolHandler<S> = dyn for<'s> Fn(ToolCallContext<'s, S>) -> BoxFuture<'s, Result<CallToolResult, crate::Error>>
|
||||
pub type DynCallToolHandler<S> = dyn for<'s> Fn(ToolCallContext<'s, S>) -> BoxFuture<'s, Result<CallToolResult, crate::ErrorData>>
|
||||
+ Send
|
||||
+ Sync;
|
||||
|
||||
|
|
@ -193,7 +195,9 @@ impl<P: JsonSchema> JsonSchema for Parameters<P> {
|
|||
}
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for CancellationToken {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
Ok(context.request_context.ct.clone())
|
||||
}
|
||||
}
|
||||
|
|
@ -201,7 +205,9 @@ impl<S> FromToolCallContextPart<S> for CancellationToken {
|
|||
pub struct ToolName(pub Cow<'static, str>);
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for ToolName {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
Ok(Self(context.name.clone()))
|
||||
}
|
||||
}
|
||||
|
|
@ -210,11 +216,13 @@ impl<S, P> FromToolCallContextPart<S> for Parameters<P>
|
|||
where
|
||||
P: DeserializeOwned,
|
||||
{
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
let arguments = context.arguments.take().unwrap_or_default();
|
||||
let value: P =
|
||||
serde_json::from_value(serde_json::Value::Object(arguments)).map_err(|e| {
|
||||
crate::Error::invalid_params(
|
||||
crate::ErrorData::invalid_params(
|
||||
format!("failed to deserialize parameters: {error}", error = e),
|
||||
None,
|
||||
)
|
||||
|
|
@ -224,14 +232,18 @@ where
|
|||
}
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for JsonObject {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
let object = context.arguments.take().unwrap_or_default();
|
||||
Ok(object)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for crate::model::Extensions {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
let extensions = context.request_context.extensions.clone();
|
||||
Ok(extensions)
|
||||
}
|
||||
|
|
@ -243,14 +255,16 @@ impl<S, T> FromToolCallContextPart<S> for Extension<T>
|
|||
where
|
||||
T: Send + Sync + 'static + Clone,
|
||||
{
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
let extension = context
|
||||
.request_context
|
||||
.extensions
|
||||
.get::<T>()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
crate::Error::invalid_params(
|
||||
crate::ErrorData::invalid_params(
|
||||
format!("missing extension {}", std::any::type_name::<T>()),
|
||||
None,
|
||||
)
|
||||
|
|
@ -260,14 +274,18 @@ where
|
|||
}
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for crate::Peer<RoleServer> {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
let peer = context.request_context.peer.clone();
|
||||
Ok(peer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for crate::model::Meta {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
let mut meta = crate::model::Meta::default();
|
||||
std::mem::swap(&mut meta, &mut context.request_context.meta);
|
||||
Ok(meta)
|
||||
|
|
@ -276,19 +294,23 @@ impl<S> FromToolCallContextPart<S> for crate::model::Meta {
|
|||
|
||||
pub struct RequestId(pub crate::model::RequestId);
|
||||
impl<S> FromToolCallContextPart<S> for RequestId {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
Ok(RequestId(context.request_context.id.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> FromToolCallContextPart<S> for RequestContext<RoleServer> {
|
||||
fn from_tool_call_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::Error> {
|
||||
fn from_tool_call_context_part(
|
||||
context: &mut ToolCallContext<S>,
|
||||
) -> Result<Self, crate::ErrorData> {
|
||||
Ok(context.request_context.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S> ToolCallContext<'s, S> {
|
||||
pub fn invoke<H, A>(self, h: H) -> BoxFuture<'s, Result<CallToolResult, crate::Error>>
|
||||
pub fn invoke<H, A>(self, h: H) -> BoxFuture<'s, Result<CallToolResult, crate::ErrorData>>
|
||||
where
|
||||
H: CallToolHandler<S, A>,
|
||||
{
|
||||
|
|
@ -331,7 +353,7 @@ macro_rules! impl_for {
|
|||
fn call(
|
||||
self,
|
||||
mut context: ToolCallContext<'_, S>,
|
||||
) -> BoxFuture<'_, Result<CallToolResult, crate::Error>>{
|
||||
) -> BoxFuture<'_, Result<CallToolResult, crate::ErrorData>>{
|
||||
$(
|
||||
let result = $Tn::from_tool_call_context_part(&mut context);
|
||||
let $Tn = match result {
|
||||
|
|
@ -362,7 +384,7 @@ macro_rules! impl_for {
|
|||
fn call(
|
||||
self,
|
||||
mut context: ToolCallContext<S>,
|
||||
) -> BoxFuture<'static, Result<CallToolResult, crate::Error>>{
|
||||
) -> BoxFuture<'static, Result<CallToolResult, crate::ErrorData>>{
|
||||
$(
|
||||
let result = $Tn::from_tool_call_context_part(&mut context);
|
||||
let $Tn = match result {
|
||||
|
|
@ -391,7 +413,7 @@ macro_rules! impl_for {
|
|||
fn call(
|
||||
self,
|
||||
mut context: ToolCallContext<S>,
|
||||
) -> BoxFuture<'static, Result<CallToolResult, crate::Error>> {
|
||||
) -> BoxFuture<'static, Result<CallToolResult, crate::ErrorData>> {
|
||||
$(
|
||||
let result = $Tn::from_tool_call_context_part(&mut context);
|
||||
let $Tn = match result {
|
||||
|
|
@ -416,7 +438,7 @@ macro_rules! impl_for {
|
|||
fn call(
|
||||
self,
|
||||
mut context: ToolCallContext<S>,
|
||||
) -> BoxFuture<'static, Result<CallToolResult, crate::Error>> {
|
||||
) -> BoxFuture<'static, Result<CallToolResult, crate::ErrorData>> {
|
||||
$(
|
||||
let result = $Tn::from_tool_call_context_part(&mut context);
|
||||
let $Tn = match result {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
//!
|
||||
//! ```rust
|
||||
//! use std::sync::Arc;
|
||||
//! use rmcp::{Error as McpError, model::*, tool, tool_router, handler::server::tool::ToolRouter};
|
||||
//! use rmcp::{ErrorData as McpError, model::*, tool, tool_router, handler::server::tool::ToolRouter};
|
||||
//! use tokio::sync::Mutex;
|
||||
//!
|
||||
//! #[derive(Clone)]
|
||||
|
|
@ -90,7 +90,8 @@
|
|||
//! }
|
||||
//! ```
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
#[allow(deprecated)]
|
||||
pub use error::{Error, ErrorData, RmcpError};
|
||||
|
||||
/// Basic data types in MCP specification
|
||||
pub mod model;
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ pub enum RawContent {
|
|||
pub type Content = Annotated<RawContent>;
|
||||
|
||||
impl RawContent {
|
||||
pub fn json<S: Serialize>(json: S) -> Result<Self, crate::Error> {
|
||||
pub fn json<S: Serialize>(json: S) -> Result<Self, crate::ErrorData> {
|
||||
let json = serde_json::to_string(&json).map_err(|e| {
|
||||
crate::Error::internal_error(
|
||||
crate::ErrorData::internal_error(
|
||||
"fail to serialize response to json",
|
||||
Some(json!(
|
||||
{"reason": e.to_string()}
|
||||
|
|
@ -142,7 +142,7 @@ impl Content {
|
|||
RawContent::embedded_text(uri, content).no_annotation()
|
||||
}
|
||||
|
||||
pub fn json<S: Serialize>(json: S) -> Result<Self, crate::Error> {
|
||||
pub fn json<S: Serialize>(json: S) -> Result<Self, crate::ErrorData> {
|
||||
RawContent::json(json).map(|c| c.no_annotation())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use futures::{FutureExt, future::BoxFuture};
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
error::Error as McpError,
|
||||
error::ErrorData as McpError,
|
||||
model::{
|
||||
CancelledNotification, CancelledNotificationParam, Extensions, GetExtensions, GetMeta,
|
||||
JsonRpcBatchRequestItem, JsonRpcBatchResponseItem, JsonRpcError, JsonRpcMessage,
|
||||
JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, Meta, NumberOrString, ProgressToken,
|
||||
RequestId, ServerJsonRpcMessage,
|
||||
},
|
||||
transport::{IntoTransport, Transport},
|
||||
transport::{DynamicTransportError, IntoTransport, Transport},
|
||||
};
|
||||
#[cfg(feature = "client")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
|
||||
|
|
@ -37,7 +37,7 @@ pub enum ServiceError {
|
|||
#[error("Mcp error: {0}")]
|
||||
McpError(McpError),
|
||||
#[error("Transport send error: {0}")]
|
||||
TransportSend(Box<dyn std::error::Error + Send + Sync>),
|
||||
TransportSend(DynamicTransportError),
|
||||
#[error("Transport closed")]
|
||||
TransportClosed,
|
||||
#[error("Unexpected response type")]
|
||||
|
|
@ -48,7 +48,6 @@ pub enum ServiceError {
|
|||
Timeout { timeout: Duration },
|
||||
}
|
||||
|
||||
impl ServiceError {}
|
||||
trait TransferObject:
|
||||
std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static
|
||||
{
|
||||
|
|
@ -79,7 +78,7 @@ pub trait ServiceRole: std::fmt::Debug + Send + Sync + 'static + Copy + Clone {
|
|||
+ TransferObject
|
||||
+ GetMeta
|
||||
+ GetExtensions;
|
||||
type InitializeError<E>;
|
||||
type InitializeError;
|
||||
const IS_CLIENT: bool;
|
||||
type Info: TransferObject;
|
||||
type PeerInfo: TransferObject;
|
||||
|
|
@ -117,10 +116,10 @@ pub trait ServiceExt<R: ServiceRole>: Service<R> + Sized {
|
|||
fn serve<T, E, A>(
|
||||
self,
|
||||
transport: T,
|
||||
) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError<E>>> + Send
|
||||
) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError>> + Send
|
||||
where
|
||||
T: IntoTransport<R, E, A>,
|
||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
Self: Sized,
|
||||
{
|
||||
Self::serve_with_ct(self, transport, Default::default())
|
||||
|
|
@ -129,10 +128,10 @@ pub trait ServiceExt<R: ServiceRole>: Service<R> + Sized {
|
|||
self,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError<E>>> + Send
|
||||
) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError>> + Send
|
||||
where
|
||||
T: IntoTransport<R, E, A>,
|
||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
|
|
@ -537,11 +536,11 @@ where
|
|||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
let (peer, peer_rx) = Peer::new(Arc::new(AtomicU32RequestIdProvider::default()), peer_info);
|
||||
serve_inner(service, transport, peer, peer_rx, ct)
|
||||
serve_inner(service, transport.into_transport(), peer, peer_rx, ct)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn serve_inner<R, S, T, E, A>(
|
||||
fn serve_inner<R, S, T>(
|
||||
service: S,
|
||||
transport: T,
|
||||
peer: Peer<R>,
|
||||
|
|
@ -551,8 +550,7 @@ fn serve_inner<R, S, T, E, A>(
|
|||
where
|
||||
R: ServiceRole,
|
||||
S: Service<R>,
|
||||
T: IntoTransport<R, E, A>,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
T: Transport<R> + 'static,
|
||||
{
|
||||
const SINK_PROXY_BUFFER_SIZE: usize = 64;
|
||||
let (sink_proxy_tx, mut sink_proxy_rx) =
|
||||
|
|
@ -578,25 +576,25 @@ where
|
|||
let handle = tokio::spawn(async move {
|
||||
let mut transport = transport.into_transport();
|
||||
let mut batch_messages = VecDeque::<RxJsonRpcMessage<R>>::new();
|
||||
let mut send_task_set = tokio::task::JoinSet::<SendTaskResult<E>>::new();
|
||||
let mut send_task_set = tokio::task::JoinSet::<SendTaskResult>::new();
|
||||
#[derive(Debug)]
|
||||
enum SendTaskResult<E> {
|
||||
enum SendTaskResult {
|
||||
Request {
|
||||
id: RequestId,
|
||||
result: Result<(), E>,
|
||||
result: Result<(), DynamicTransportError>,
|
||||
},
|
||||
Notification {
|
||||
responder: Responder<Result<(), ServiceError>>,
|
||||
cancellation_param: Option<CancelledNotificationParam>,
|
||||
result: Result<(), E>,
|
||||
result: Result<(), DynamicTransportError>,
|
||||
},
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum Event<R: ServiceRole, E> {
|
||||
enum Event<R: ServiceRole> {
|
||||
ProxyMessage(PeerSinkMessage<R>),
|
||||
PeerMessage(RxJsonRpcMessage<R>),
|
||||
ToSink(TxJsonRpcMessage<R>),
|
||||
SendTaskResult(SendTaskResult<E>),
|
||||
SendTaskResult(SendTaskResult),
|
||||
}
|
||||
|
||||
let quit_reason = loop {
|
||||
|
|
@ -654,7 +652,7 @@ where
|
|||
Event::SendTaskResult(SendTaskResult::Request { id, result }) => {
|
||||
if let Err(e) = result {
|
||||
if let Some(responder) = local_responder_pool.remove(&id) {
|
||||
let _ = responder.send(Err(ServiceError::TransportSend(Box::new(e))));
|
||||
let _ = responder.send(Err(ServiceError::TransportSend(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -664,7 +662,7 @@ where
|
|||
cancellation_param,
|
||||
}) => {
|
||||
let response = if let Err(e) = result {
|
||||
Err(ServiceError::TransportSend(Box::new(e)))
|
||||
Err(ServiceError::TransportSend(e))
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
|
@ -706,8 +704,10 @@ where
|
|||
let send = transport.send(JsonRpcMessage::request(request, id.clone()));
|
||||
{
|
||||
let id = id.clone();
|
||||
send_task_set
|
||||
.spawn(send.map(move |r| SendTaskResult::Request { id, result: r }));
|
||||
send_task_set.spawn(send.map(move |r| SendTaskResult::Request {
|
||||
id,
|
||||
result: r.map_err(DynamicTransportError::new::<T, R>),
|
||||
}));
|
||||
}
|
||||
}
|
||||
Event::ProxyMessage(PeerSinkMessage::Notification {
|
||||
|
|
@ -727,7 +727,7 @@ where
|
|||
send_task_set.spawn(send.map(move |result| SendTaskResult::Notification {
|
||||
responder,
|
||||
cancellation_param,
|
||||
result,
|
||||
result: result.map_err(DynamicTransportError::new::<T, R>),
|
||||
}));
|
||||
}
|
||||
Event::PeerMessage(JsonRpcMessage::Request(JsonRpcRequest {
|
||||
|
|
|
|||
|
|
@ -3,25 +3,29 @@ use std::borrow::Cow;
|
|||
use thiserror::Error;
|
||||
|
||||
use super::*;
|
||||
use crate::model::{
|
||||
CallToolRequest, CallToolRequestParam, CallToolResult, CancelledNotification,
|
||||
CancelledNotificationParam, ClientInfo, ClientJsonRpcMessage, ClientNotification,
|
||||
ClientRequest, ClientResult, CompleteRequest, CompleteRequestParam, CompleteResult,
|
||||
GetPromptRequest, GetPromptRequestParam, GetPromptResult, InitializeRequest,
|
||||
InitializedNotification, JsonRpcResponse, ListPromptsRequest, ListPromptsResult,
|
||||
ListResourceTemplatesRequest, ListResourceTemplatesResult, ListResourcesRequest,
|
||||
ListResourcesResult, ListToolsRequest, ListToolsResult, PaginatedRequestParam,
|
||||
ProgressNotification, ProgressNotificationParam, ReadResourceRequest, ReadResourceRequestParam,
|
||||
ReadResourceResult, RequestId, RootsListChangedNotification, ServerInfo, ServerJsonRpcMessage,
|
||||
ServerNotification, ServerRequest, ServerResult, SetLevelRequest, SetLevelRequestParam,
|
||||
SubscribeRequest, SubscribeRequestParam, UnsubscribeRequest, UnsubscribeRequestParam,
|
||||
use crate::{
|
||||
model::{
|
||||
CallToolRequest, CallToolRequestParam, CallToolResult, CancelledNotification,
|
||||
CancelledNotificationParam, ClientInfo, ClientJsonRpcMessage, ClientNotification,
|
||||
ClientRequest, ClientResult, CompleteRequest, CompleteRequestParam, CompleteResult,
|
||||
GetPromptRequest, GetPromptRequestParam, GetPromptResult, InitializeRequest,
|
||||
InitializedNotification, JsonRpcResponse, ListPromptsRequest, ListPromptsResult,
|
||||
ListResourceTemplatesRequest, ListResourceTemplatesResult, ListResourcesRequest,
|
||||
ListResourcesResult, ListToolsRequest, ListToolsResult, PaginatedRequestParam,
|
||||
ProgressNotification, ProgressNotificationParam, ReadResourceRequest,
|
||||
ReadResourceRequestParam, ReadResourceResult, RequestId, RootsListChangedNotification,
|
||||
ServerInfo, ServerJsonRpcMessage, ServerNotification, ServerRequest, ServerResult,
|
||||
SetLevelRequest, SetLevelRequestParam, SubscribeRequest, SubscribeRequestParam,
|
||||
UnsubscribeRequest, UnsubscribeRequestParam,
|
||||
},
|
||||
transport::DynamicTransportError,
|
||||
};
|
||||
|
||||
/// It represents the error that may occur when serving the client.
|
||||
///
|
||||
/// if you want to handle the error, you can use `serve_client_with_ct` or `serve_client` with `Result<RunningService<RoleClient, S>, ClientError>`
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ClientInitializeError<E> {
|
||||
pub enum ClientInitializeError {
|
||||
#[error("expect initialized response, but received: {0:?}")]
|
||||
ExpectedInitResponse(Option<ServerJsonRpcMessage>),
|
||||
|
||||
|
|
@ -36,7 +40,7 @@ pub enum ClientInitializeError<E> {
|
|||
|
||||
#[error("Send message error {error}, when {context}")]
|
||||
TransportError {
|
||||
error: E,
|
||||
error: DynamicTransportError,
|
||||
context: Cow<'static, str>,
|
||||
},
|
||||
|
||||
|
|
@ -44,11 +48,23 @@ pub enum ClientInitializeError<E> {
|
|||
Cancelled,
|
||||
}
|
||||
|
||||
impl ClientInitializeError {
|
||||
pub fn transport<T: Transport<RoleClient> + 'static>(
|
||||
error: T::Error,
|
||||
context: impl Into<Cow<'static, str>>,
|
||||
) -> Self {
|
||||
Self::TransportError {
|
||||
error: DynamicTransportError::new::<T, _>(error),
|
||||
context: context.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to get the next message from the stream
|
||||
async fn expect_next_message<T, E>(
|
||||
async fn expect_next_message<T>(
|
||||
transport: &mut T,
|
||||
context: &str,
|
||||
) -> Result<ServerJsonRpcMessage, ClientInitializeError<E>>
|
||||
) -> Result<ServerJsonRpcMessage, ClientInitializeError>
|
||||
where
|
||||
T: Transport<RoleClient>,
|
||||
{
|
||||
|
|
@ -59,10 +75,10 @@ where
|
|||
}
|
||||
|
||||
/// Helper function to expect a response from the stream
|
||||
async fn expect_response<T, E>(
|
||||
async fn expect_response<T>(
|
||||
transport: &mut T,
|
||||
context: &str,
|
||||
) -> Result<(ServerResult, RequestId), ClientInitializeError<E>>
|
||||
) -> Result<(ServerResult, RequestId), ClientInitializeError>
|
||||
where
|
||||
T: Transport<RoleClient>,
|
||||
{
|
||||
|
|
@ -86,7 +102,7 @@ impl ServiceRole for RoleClient {
|
|||
type PeerNot = ServerNotification;
|
||||
type Info = ClientInfo;
|
||||
type PeerInfo = ServerInfo;
|
||||
type InitializeError<E> = ClientInitializeError<E>;
|
||||
type InitializeError = ClientInitializeError;
|
||||
const IS_CLIENT: bool = true;
|
||||
}
|
||||
|
||||
|
|
@ -97,10 +113,10 @@ impl<S: Service<RoleClient>> ServiceExt<RoleClient> for S {
|
|||
self,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> impl Future<Output = Result<RunningService<RoleClient, Self>, ClientInitializeError<E>>> + Send
|
||||
) -> impl Future<Output = Result<RunningService<RoleClient, Self>, ClientInitializeError>> + Send
|
||||
where
|
||||
T: IntoTransport<RoleClient, E, A>,
|
||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
Self: Sized,
|
||||
{
|
||||
serve_client_with_ct(self, transport, ct)
|
||||
|
|
@ -110,7 +126,7 @@ impl<S: Service<RoleClient>> ServiceExt<RoleClient> for S {
|
|||
pub async fn serve_client<S, T, E, A>(
|
||||
service: S,
|
||||
transport: T,
|
||||
) -> Result<RunningService<RoleClient, S>, ClientInitializeError<E>>
|
||||
) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
|
||||
where
|
||||
S: Service<RoleClient>,
|
||||
T: IntoTransport<RoleClient, E, A>,
|
||||
|
|
@ -123,29 +139,28 @@ pub async fn serve_client_with_ct<S, T, E, A>(
|
|||
service: S,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> Result<RunningService<RoleClient, S>, ClientInitializeError<E>>
|
||||
) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
|
||||
where
|
||||
S: Service<RoleClient>,
|
||||
T: IntoTransport<RoleClient, E, A>,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
tokio::select! {
|
||||
result = serve_client_with_ct_inner(service, transport, ct.clone()) => { result }
|
||||
result = serve_client_with_ct_inner(service, transport.into_transport(), ct.clone()) => { result }
|
||||
_ = ct.cancelled() => {
|
||||
Err(ClientInitializeError::Cancelled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn serve_client_with_ct_inner<S, T, E, A>(
|
||||
async fn serve_client_with_ct_inner<S, T>(
|
||||
service: S,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> Result<RunningService<RoleClient, S>, ClientInitializeError<E>>
|
||||
) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
|
||||
where
|
||||
S: Service<RoleClient>,
|
||||
T: IntoTransport<RoleClient, E, A>,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
T: Transport<RoleClient> + 'static,
|
||||
{
|
||||
let mut transport = transport.into_transport();
|
||||
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
||||
|
|
@ -164,7 +179,7 @@ where
|
|||
))
|
||||
.await
|
||||
.map_err(|error| ClientInitializeError::TransportError {
|
||||
error,
|
||||
error: DynamicTransportError::new::<T, _>(error),
|
||||
context: "send initialize request".into(),
|
||||
})?;
|
||||
|
||||
|
|
@ -188,13 +203,9 @@ where
|
|||
extensions: Default::default(),
|
||||
}),
|
||||
);
|
||||
transport
|
||||
.send(notification)
|
||||
.await
|
||||
.map_err(|error| ClientInitializeError::TransportError {
|
||||
error,
|
||||
context: "send initialized notification".into(),
|
||||
})?;
|
||||
transport.send(notification).await.map_err(|error| {
|
||||
ClientInitializeError::transport::<T>(error, "send initialized notification")
|
||||
})?;
|
||||
let (peer, peer_rx) = Peer::new(id_provider, Some(initialize_result));
|
||||
Ok(serve_inner(service, transport, peer, peer_rx, ct))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,18 @@ use std::borrow::Cow;
|
|||
use thiserror::Error;
|
||||
|
||||
use super::*;
|
||||
use crate::model::{
|
||||
CancelledNotification, CancelledNotificationParam, ClientInfo, ClientJsonRpcMessage,
|
||||
ClientNotification, ClientRequest, ClientResult, CreateMessageRequest,
|
||||
CreateMessageRequestParam, CreateMessageResult, ErrorData, ListRootsRequest, ListRootsResult,
|
||||
LoggingMessageNotification, LoggingMessageNotificationParam, ProgressNotification,
|
||||
ProgressNotificationParam, PromptListChangedNotification, ProtocolVersion,
|
||||
ResourceListChangedNotification, ResourceUpdatedNotification, ResourceUpdatedNotificationParam,
|
||||
ServerInfo, ServerNotification, ServerRequest, ServerResult, ToolListChangedNotification,
|
||||
use crate::{
|
||||
model::{
|
||||
CancelledNotification, CancelledNotificationParam, ClientInfo, ClientJsonRpcMessage,
|
||||
ClientNotification, ClientRequest, ClientResult, CreateMessageRequest,
|
||||
CreateMessageRequestParam, CreateMessageResult, ErrorData, ListRootsRequest,
|
||||
ListRootsResult, LoggingMessageNotification, LoggingMessageNotificationParam,
|
||||
ProgressNotification, ProgressNotificationParam, PromptListChangedNotification,
|
||||
ProtocolVersion, ResourceListChangedNotification, ResourceUpdatedNotification,
|
||||
ResourceUpdatedNotificationParam, ServerInfo, ServerNotification, ServerRequest,
|
||||
ServerResult, ToolListChangedNotification,
|
||||
},
|
||||
transport::DynamicTransportError,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
|
|
@ -26,7 +30,7 @@ impl ServiceRole for RoleServer {
|
|||
type Info = ServerInfo;
|
||||
type PeerInfo = ClientInfo;
|
||||
|
||||
type InitializeError<E> = ServerInitializeError<E>;
|
||||
type InitializeError = ServerInitializeError;
|
||||
const IS_CLIENT: bool = false;
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +38,7 @@ impl ServiceRole for RoleServer {
|
|||
///
|
||||
/// if you want to handle the error, you can use `serve_server_with_ct` or `serve_server` with `Result<RunningService<RoleServer, S>, ServerError>`
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ServerInitializeError<E> {
|
||||
pub enum ServerInitializeError {
|
||||
#[error("expect initialized request, but received: {0:?}")]
|
||||
ExpectedInitializeRequest(Option<ClientJsonRpcMessage>),
|
||||
|
||||
|
|
@ -55,7 +59,7 @@ pub enum ServerInitializeError<E> {
|
|||
|
||||
#[error("Send message error {error}, when {context}")]
|
||||
TransportError {
|
||||
error: E,
|
||||
error: DynamicTransportError,
|
||||
context: Cow<'static, str>,
|
||||
},
|
||||
|
||||
|
|
@ -63,6 +67,17 @@ pub enum ServerInitializeError<E> {
|
|||
Cancelled,
|
||||
}
|
||||
|
||||
impl ServerInitializeError {
|
||||
pub fn transport<T: Transport<RoleServer> + 'static>(
|
||||
error: T::Error,
|
||||
context: impl Into<Cow<'static, str>>,
|
||||
) -> Self {
|
||||
Self::TransportError {
|
||||
error: DynamicTransportError::new::<T, _>(error),
|
||||
context: context.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type ClientSink = Peer<RoleServer>;
|
||||
|
||||
impl<S: Service<RoleServer>> ServiceExt<RoleServer> for S {
|
||||
|
|
@ -70,7 +85,7 @@ impl<S: Service<RoleServer>> ServiceExt<RoleServer> for S {
|
|||
self,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> impl Future<Output = Result<RunningService<RoleServer, Self>, ServerInitializeError<E>>> + Send
|
||||
) -> impl Future<Output = Result<RunningService<RoleServer, Self>, ServerInitializeError>> + Send
|
||||
where
|
||||
T: IntoTransport<RoleServer, E, A>,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
|
|
@ -83,7 +98,7 @@ impl<S: Service<RoleServer>> ServiceExt<RoleServer> for S {
|
|||
pub async fn serve_server<S, T, E, A>(
|
||||
service: S,
|
||||
transport: T,
|
||||
) -> Result<RunningService<RoleServer, S>, ServerInitializeError<E>>
|
||||
) -> Result<RunningService<RoleServer, S>, ServerInitializeError>
|
||||
where
|
||||
S: Service<RoleServer>,
|
||||
T: IntoTransport<RoleServer, E, A>,
|
||||
|
|
@ -93,10 +108,10 @@ where
|
|||
}
|
||||
|
||||
/// Helper function to get the next message from the stream
|
||||
async fn expect_next_message<T, E>(
|
||||
async fn expect_next_message<T>(
|
||||
transport: &mut T,
|
||||
context: &str,
|
||||
) -> Result<ClientJsonRpcMessage, ServerInitializeError<E>>
|
||||
) -> Result<ClientJsonRpcMessage, ServerInitializeError>
|
||||
where
|
||||
T: Transport<RoleServer>,
|
||||
{
|
||||
|
|
@ -107,10 +122,10 @@ where
|
|||
}
|
||||
|
||||
/// Helper function to expect a request from the stream
|
||||
async fn expect_request<T, E>(
|
||||
async fn expect_request<T>(
|
||||
transport: &mut T,
|
||||
context: &str,
|
||||
) -> Result<(ClientRequest, RequestId), ServerInitializeError<E>>
|
||||
) -> Result<(ClientRequest, RequestId), ServerInitializeError>
|
||||
where
|
||||
T: Transport<RoleServer>,
|
||||
{
|
||||
|
|
@ -123,10 +138,10 @@ where
|
|||
}
|
||||
|
||||
/// Helper function to expect a notification from the stream
|
||||
async fn expect_notification<T, E>(
|
||||
async fn expect_notification<T>(
|
||||
transport: &mut T,
|
||||
context: &str,
|
||||
) -> Result<ClientNotification, ServerInitializeError<E>>
|
||||
) -> Result<ClientNotification, ServerInitializeError>
|
||||
where
|
||||
T: Transport<RoleServer>,
|
||||
{
|
||||
|
|
@ -142,29 +157,28 @@ pub async fn serve_server_with_ct<S, T, E, A>(
|
|||
service: S,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> Result<RunningService<RoleServer, S>, ServerInitializeError<E>>
|
||||
) -> Result<RunningService<RoleServer, S>, ServerInitializeError>
|
||||
where
|
||||
S: Service<RoleServer>,
|
||||
T: IntoTransport<RoleServer, E, A>,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
tokio::select! {
|
||||
result = serve_server_with_ct_inner(service, transport, ct.clone()) => { result }
|
||||
result = serve_server_with_ct_inner(service, transport.into_transport(), ct.clone()) => { result }
|
||||
_ = ct.cancelled() => {
|
||||
Err(ServerInitializeError::Cancelled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn serve_server_with_ct_inner<S, T, E, A>(
|
||||
async fn serve_server_with_ct_inner<S, T>(
|
||||
service: S,
|
||||
transport: T,
|
||||
ct: CancellationToken,
|
||||
) -> Result<RunningService<RoleServer, S>, ServerInitializeError<E>>
|
||||
) -> Result<RunningService<RoleServer, S>, ServerInitializeError>
|
||||
where
|
||||
S: Service<RoleServer>,
|
||||
T: IntoTransport<RoleServer, E, A>,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
T: Transport<RoleServer> + 'static,
|
||||
{
|
||||
let mut transport = transport.into_transport();
|
||||
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
||||
|
|
@ -196,9 +210,8 @@ where
|
|||
transport
|
||||
.send(ServerJsonRpcMessage::error(e.clone(), id))
|
||||
.await
|
||||
.map_err(|error| ServerInitializeError::TransportError {
|
||||
error,
|
||||
context: "sending error response".into(),
|
||||
.map_err(|error| {
|
||||
ServerInitializeError::transport::<T>(error, "sending error response")
|
||||
})?;
|
||||
return Err(ServerInitializeError::InitializeFailed(e));
|
||||
}
|
||||
|
|
@ -219,9 +232,8 @@ where
|
|||
id,
|
||||
))
|
||||
.await
|
||||
.map_err(|error| ServerInitializeError::TransportError {
|
||||
error,
|
||||
context: "sending initialize response".into(),
|
||||
.map_err(|error| {
|
||||
ServerInitializeError::transport::<T>(error, "sending initialize response")
|
||||
})?;
|
||||
|
||||
// Wait for initialize notification
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ impl<S, R: ServiceRole> TowerHandler<S, R> {
|
|||
impl<S, R: ServiceRole> Service<R> for TowerHandler<S, R>
|
||||
where
|
||||
S: TowerService<R::PeerReq, Response = R::Resp> + Sync + Send + Clone + 'static,
|
||||
S::Error: Into<crate::Error>,
|
||||
S::Error: Into<crate::ErrorData>,
|
||||
S::Future: Send,
|
||||
{
|
||||
async fn handle_request(
|
||||
&self,
|
||||
request: R::PeerReq,
|
||||
_context: RequestContext<R>,
|
||||
) -> Result<R::Resp, crate::Error> {
|
||||
) -> Result<R::Resp, crate::ErrorData> {
|
||||
let mut service = self.service.clone();
|
||||
poll_fn(|cx| service.poll_ready(cx))
|
||||
.await
|
||||
|
|
@ -44,7 +44,7 @@ where
|
|||
&self,
|
||||
_notification: R::PeerNot,
|
||||
_context: NotificationContext<R>,
|
||||
) -> impl Future<Output = Result<(), crate::Error>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<(), crate::ErrorData>> + Send + '_ {
|
||||
std::future::ready(Ok(()))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use crate::service::{RxJsonRpcMessage, ServiceRole, TxJsonRpcMessage};
|
||||
|
||||
|
|
@ -141,6 +141,9 @@ where
|
|||
R: ServiceRole,
|
||||
{
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
fn name() -> Cow<'static, str> {
|
||||
std::any::type_name::<Self>().into()
|
||||
}
|
||||
/// Send a message to the transport
|
||||
///
|
||||
/// Notice that the future returned by this function should be `Send` and `'static`.
|
||||
|
|
@ -241,3 +244,36 @@ where
|
|||
std::future::ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Transport [{transport_name}] error: {error}")]
|
||||
pub struct DynamicTransportError {
|
||||
pub transport_name: Cow<'static, str>,
|
||||
pub transport_type_id: std::any::TypeId,
|
||||
#[source]
|
||||
pub error: Box<dyn std::error::Error + Send + Sync>,
|
||||
}
|
||||
|
||||
impl DynamicTransportError {
|
||||
pub fn new<T: Transport<R> + 'static, R: ServiceRole>(e: T::Error) -> Self {
|
||||
Self {
|
||||
transport_name: T::name(),
|
||||
transport_type_id: std::any::TypeId::of::<T>(),
|
||||
error: Box::new(e),
|
||||
}
|
||||
}
|
||||
pub fn downcast<T: Transport<R> + 'static, R: ServiceRole>(self) -> Result<T::Error, Self> {
|
||||
if !self.is::<T, R>() {
|
||||
Err(self)
|
||||
} else {
|
||||
Ok(self
|
||||
.error
|
||||
.downcast::<T::Error>()
|
||||
.map(|e| *e)
|
||||
.expect("type is checked"))
|
||||
}
|
||||
}
|
||||
pub fn is<T: Transport<R> + 'static, R: ServiceRole>(&self) -> bool {
|
||||
self.error.is::<T::Error>() && self.transport_type_id == std::any::TypeId::of::<T>()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
};
|
||||
|
||||
use rmcp::{
|
||||
ClientHandler, Error as McpError, RoleClient, RoleServer, ServerHandler,
|
||||
ClientHandler, ErrorData as McpError, RoleClient, RoleServer, ServerHandler,
|
||||
model::*,
|
||||
service::{NotificationContext, RequestContext},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rmcp::{
|
||||
Error as McpError, handler::server::tool::Parameters, model::*, schemars, tool, tool_router,
|
||||
ErrorData as McpError, handler::server::tool::Parameters, model::*, schemars, tool, tool_router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl ServerHandler for Server {
|
|||
&self,
|
||||
request: rmcp::model::SubscribeRequestParam,
|
||||
context: rmcp::service::RequestContext<rmcp::RoleServer>,
|
||||
) -> Result<(), rmcp::Error> {
|
||||
) -> Result<(), rmcp::ErrorData> {
|
||||
let uri = request.uri;
|
||||
let peer = context.peer;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue