feat: provide more context information (#236)
* feat: provide more context information 1. provide context for notification 2. allow extract more info from tool call 3. inject http request part for streamable http server * docs: add document for getting peer from context
This commit is contained in:
parent
9fc6af091d
commit
969fad26cc
11 changed files with 194 additions and 40 deletions
|
|
@ -146,6 +146,39 @@ let service = client.serve(transport).await?;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Access with peer interface when handling message
|
||||||
|
|
||||||
|
You can get the [`Peer`](crate::service::Peer) struct from [`NotificationContext`](crate::service::NotificationContext) and [`RequestContext`](crate::service::RequestContext).
|
||||||
|
|
||||||
|
```rust, ignore
|
||||||
|
# use rmcp::{
|
||||||
|
# ServerHandler,
|
||||||
|
# model::{LoggingLevel, LoggingMessageNotificationParam, ProgressNotificationParam},
|
||||||
|
# service::{NotificationContext, RoleServer},
|
||||||
|
# };
|
||||||
|
# pub struct Handler;
|
||||||
|
|
||||||
|
impl ServerHandler for Handler {
|
||||||
|
async fn on_progress(
|
||||||
|
&self,
|
||||||
|
notification: ProgressNotificationParam,
|
||||||
|
context: NotificationContext<RoleServer>,
|
||||||
|
) {
|
||||||
|
let peer = context.peer;
|
||||||
|
let _ = peer
|
||||||
|
.notify_logging_message(LoggingMessageNotificationParam {
|
||||||
|
level: LoggingLevel::Info,
|
||||||
|
logger: None,
|
||||||
|
data: serde_json::json!({
|
||||||
|
"message": format!("Progress: {}", notification.progress),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Manage Multi Services
|
## Manage Multi Services
|
||||||
|
|
||||||
For many cases you need to manage several service in a collection, you can call `into_dyn` to convert services into the same type.
|
For many cases you need to manage several service in a collection, you can call `into_dyn` to convert services into the same type.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error as McpError,
|
error::Error as McpError,
|
||||||
model::*,
|
model::*,
|
||||||
service::{RequestContext, RoleClient, Service, ServiceRole},
|
service::{NotificationContext, RequestContext, RoleClient, Service, ServiceRole},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<H: ClientHandler> Service<RoleClient> for H {
|
impl<H: ClientHandler> Service<RoleClient> for H {
|
||||||
|
|
@ -26,28 +26,29 @@ impl<H: ClientHandler> Service<RoleClient> for H {
|
||||||
async fn handle_notification(
|
async fn handle_notification(
|
||||||
&self,
|
&self,
|
||||||
notification: <RoleClient as ServiceRole>::PeerNot,
|
notification: <RoleClient as ServiceRole>::PeerNot,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
) -> Result<(), McpError> {
|
) -> Result<(), McpError> {
|
||||||
match notification {
|
match notification {
|
||||||
ServerNotification::CancelledNotification(notification) => {
|
ServerNotification::CancelledNotification(notification) => {
|
||||||
self.on_cancelled(notification.params).await
|
self.on_cancelled(notification.params, context).await
|
||||||
}
|
}
|
||||||
ServerNotification::ProgressNotification(notification) => {
|
ServerNotification::ProgressNotification(notification) => {
|
||||||
self.on_progress(notification.params).await
|
self.on_progress(notification.params, context).await
|
||||||
}
|
}
|
||||||
ServerNotification::LoggingMessageNotification(notification) => {
|
ServerNotification::LoggingMessageNotification(notification) => {
|
||||||
self.on_logging_message(notification.params).await
|
self.on_logging_message(notification.params, context).await
|
||||||
}
|
}
|
||||||
ServerNotification::ResourceUpdatedNotification(notification) => {
|
ServerNotification::ResourceUpdatedNotification(notification) => {
|
||||||
self.on_resource_updated(notification.params).await
|
self.on_resource_updated(notification.params, context).await
|
||||||
}
|
}
|
||||||
ServerNotification::ResourceListChangedNotification(_notification_no_param) => {
|
ServerNotification::ResourceListChangedNotification(_notification_no_param) => {
|
||||||
self.on_resource_list_changed().await
|
self.on_resource_list_changed(context).await
|
||||||
}
|
}
|
||||||
ServerNotification::ToolListChangedNotification(_notification_no_param) => {
|
ServerNotification::ToolListChangedNotification(_notification_no_param) => {
|
||||||
self.on_tool_list_changed().await
|
self.on_tool_list_changed(context).await
|
||||||
}
|
}
|
||||||
ServerNotification::PromptListChangedNotification(_notification_no_param) => {
|
ServerNotification::PromptListChangedNotification(_notification_no_param) => {
|
||||||
self.on_prompt_list_changed().await
|
self.on_prompt_list_changed(context).await
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -87,34 +88,47 @@ pub trait ClientHandler: Sized + Send + Sync + 'static {
|
||||||
fn on_cancelled(
|
fn on_cancelled(
|
||||||
&self,
|
&self,
|
||||||
params: CancelledNotificationParam,
|
params: CancelledNotificationParam,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_progress(
|
fn on_progress(
|
||||||
&self,
|
&self,
|
||||||
params: ProgressNotificationParam,
|
params: ProgressNotificationParam,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_logging_message(
|
fn on_logging_message(
|
||||||
&self,
|
&self,
|
||||||
params: LoggingMessageNotificationParam,
|
params: LoggingMessageNotificationParam,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_resource_updated(
|
fn on_resource_updated(
|
||||||
&self,
|
&self,
|
||||||
params: ResourceUpdatedNotificationParam,
|
params: ResourceUpdatedNotificationParam,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_resource_list_changed(&self) -> impl Future<Output = ()> + Send + '_ {
|
fn on_resource_list_changed(
|
||||||
|
&self,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_tool_list_changed(&self) -> impl Future<Output = ()> + Send + '_ {
|
fn on_tool_list_changed(
|
||||||
|
&self,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_prompt_list_changed(&self) -> impl Future<Output = ()> + Send + '_ {
|
fn on_prompt_list_changed(
|
||||||
|
&self,
|
||||||
|
context: NotificationContext<RoleClient>,
|
||||||
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error as McpError,
|
error::Error as McpError,
|
||||||
model::*,
|
model::*,
|
||||||
service::{RequestContext, RoleServer, Service, ServiceRole},
|
service::{NotificationContext, RequestContext, RoleServer, Service, ServiceRole},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod resource;
|
mod resource;
|
||||||
|
|
@ -71,19 +71,20 @@ impl<H: ServerHandler> Service<RoleServer> for H {
|
||||||
async fn handle_notification(
|
async fn handle_notification(
|
||||||
&self,
|
&self,
|
||||||
notification: <RoleServer as ServiceRole>::PeerNot,
|
notification: <RoleServer as ServiceRole>::PeerNot,
|
||||||
|
context: NotificationContext<RoleServer>,
|
||||||
) -> Result<(), McpError> {
|
) -> Result<(), McpError> {
|
||||||
match notification {
|
match notification {
|
||||||
ClientNotification::CancelledNotification(notification) => {
|
ClientNotification::CancelledNotification(notification) => {
|
||||||
self.on_cancelled(notification.params).await
|
self.on_cancelled(notification.params, context).await
|
||||||
}
|
}
|
||||||
ClientNotification::ProgressNotification(notification) => {
|
ClientNotification::ProgressNotification(notification) => {
|
||||||
self.on_progress(notification.params).await
|
self.on_progress(notification.params, context).await
|
||||||
}
|
}
|
||||||
ClientNotification::InitializedNotification(_notification) => {
|
ClientNotification::InitializedNotification(_notification) => {
|
||||||
self.on_initialized().await
|
self.on_initialized(context).await
|
||||||
}
|
}
|
||||||
ClientNotification::RootsListChangedNotification(_notification) => {
|
ClientNotification::RootsListChangedNotification(_notification) => {
|
||||||
self.on_roots_list_changed().await
|
self.on_roots_list_changed(context).await
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -196,20 +197,28 @@ pub trait ServerHandler: Sized + Send + Sync + 'static {
|
||||||
fn on_cancelled(
|
fn on_cancelled(
|
||||||
&self,
|
&self,
|
||||||
notification: CancelledNotificationParam,
|
notification: CancelledNotificationParam,
|
||||||
|
context: NotificationContext<RoleServer>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_progress(
|
fn on_progress(
|
||||||
&self,
|
&self,
|
||||||
notification: ProgressNotificationParam,
|
notification: ProgressNotificationParam,
|
||||||
|
context: NotificationContext<RoleServer>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_initialized(&self) -> impl Future<Output = ()> + Send + '_ {
|
fn on_initialized(
|
||||||
|
&self,
|
||||||
|
context: NotificationContext<RoleServer>,
|
||||||
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
tracing::info!("client initialized");
|
tracing::info!("client initialized");
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
fn on_roots_list_changed(&self) -> impl Future<Output = ()> + Send + '_ {
|
fn on_roots_list_changed(
|
||||||
|
&self,
|
||||||
|
context: NotificationContext<RoleServer>,
|
||||||
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
std::future::ready(())
|
std::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,42 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, S> FromToolCallContextPart<'a, S> for crate::Peer<RoleServer> {
|
||||||
|
fn from_tool_call_context_part(
|
||||||
|
context: ToolCallContext<'a, S>,
|
||||||
|
) -> Result<(Self, ToolCallContext<'a, S>), crate::Error> {
|
||||||
|
let peer = context.request_context.peer.clone();
|
||||||
|
Ok((peer, context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> FromToolCallContextPart<'a, S> for crate::model::Meta {
|
||||||
|
fn from_tool_call_context_part(
|
||||||
|
mut context: ToolCallContext<'a, S>,
|
||||||
|
) -> Result<(Self, ToolCallContext<'a, S>), crate::Error> {
|
||||||
|
let mut meta = crate::model::Meta::default();
|
||||||
|
std::mem::swap(&mut meta, &mut context.request_context.meta);
|
||||||
|
Ok((meta, context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RequestId(pub crate::model::RequestId);
|
||||||
|
impl<'a, S> FromToolCallContextPart<'a, S> for RequestId {
|
||||||
|
fn from_tool_call_context_part(
|
||||||
|
context: ToolCallContext<'a, S>,
|
||||||
|
) -> Result<(Self, ToolCallContext<'a, S>), crate::Error> {
|
||||||
|
Ok((RequestId(context.request_context.id.clone()), context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> FromToolCallContextPart<'a, S> for RequestContext<RoleServer> {
|
||||||
|
fn from_tool_call_context_part(
|
||||||
|
context: ToolCallContext<'a, S>,
|
||||||
|
) -> Result<(Self, ToolCallContext<'a, S>), crate::Error> {
|
||||||
|
Ok((context.request_context.clone(), context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'s, S> ToolCallContext<'s, S> {
|
impl<'s, S> ToolCallContext<'s, S> {
|
||||||
pub fn invoke<H, A>(self, h: H) -> H::Fut
|
pub fn invoke<H, A>(self, h: H) -> H::Fut
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ pub struct Extensions {
|
||||||
impl Extensions {
|
impl Extensions {
|
||||||
/// Create an empty `Extensions`.
|
/// Create an empty `Extensions`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Extensions {
|
pub const fn new() -> Extensions {
|
||||||
Extensions { map: None }
|
Extensions { map: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,9 @@ pub trait ServiceRole: std::fmt::Debug + Send + Sync + 'static + Copy + Clone {
|
||||||
type PeerResp: TransferObject;
|
type PeerResp: TransferObject;
|
||||||
type PeerNot: TryInto<CancelledNotification, Error = Self::PeerNot>
|
type PeerNot: TryInto<CancelledNotification, Error = Self::PeerNot>
|
||||||
+ From<CancelledNotification>
|
+ From<CancelledNotification>
|
||||||
+ TransferObject;
|
+ TransferObject
|
||||||
|
+ GetMeta
|
||||||
|
+ GetExtensions;
|
||||||
type InitializeError<E>;
|
type InitializeError<E>;
|
||||||
const IS_CLIENT: bool;
|
const IS_CLIENT: bool;
|
||||||
type Info: TransferObject;
|
type Info: TransferObject;
|
||||||
|
|
@ -100,6 +102,7 @@ pub trait Service<R: ServiceRole>: Send + Sync + 'static {
|
||||||
fn handle_notification(
|
fn handle_notification(
|
||||||
&self,
|
&self,
|
||||||
notification: R::PeerNot,
|
notification: R::PeerNot,
|
||||||
|
context: NotificationContext<R>,
|
||||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_;
|
) -> impl Future<Output = Result<(), McpError>> + Send + '_;
|
||||||
fn get_info(&self) -> R::Info;
|
fn get_info(&self) -> R::Info;
|
||||||
}
|
}
|
||||||
|
|
@ -145,8 +148,9 @@ impl<R: ServiceRole> Service<R> for Box<dyn DynService<R>> {
|
||||||
fn handle_notification(
|
fn handle_notification(
|
||||||
&self,
|
&self,
|
||||||
notification: R::PeerNot,
|
notification: R::PeerNot,
|
||||||
|
context: NotificationContext<R>,
|
||||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||||
DynService::handle_notification(self.as_ref(), notification)
|
DynService::handle_notification(self.as_ref(), notification, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_info(&self) -> R::Info {
|
fn get_info(&self) -> R::Info {
|
||||||
|
|
@ -160,7 +164,11 @@ pub trait DynService<R: ServiceRole>: Send + Sync {
|
||||||
request: R::PeerReq,
|
request: R::PeerReq,
|
||||||
context: RequestContext<R>,
|
context: RequestContext<R>,
|
||||||
) -> BoxFuture<Result<R::Resp, McpError>>;
|
) -> BoxFuture<Result<R::Resp, McpError>>;
|
||||||
fn handle_notification(&self, notification: R::PeerNot) -> BoxFuture<Result<(), McpError>>;
|
fn handle_notification(
|
||||||
|
&self,
|
||||||
|
notification: R::PeerNot,
|
||||||
|
context: NotificationContext<R>,
|
||||||
|
) -> BoxFuture<Result<(), McpError>>;
|
||||||
fn get_info(&self) -> R::Info;
|
fn get_info(&self) -> R::Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,8 +180,12 @@ impl<R: ServiceRole, S: Service<R>> DynService<R> for S {
|
||||||
) -> BoxFuture<Result<R::Resp, McpError>> {
|
) -> BoxFuture<Result<R::Resp, McpError>> {
|
||||||
Box::pin(self.handle_request(request, context))
|
Box::pin(self.handle_request(request, context))
|
||||||
}
|
}
|
||||||
fn handle_notification(&self, notification: R::PeerNot) -> BoxFuture<Result<(), McpError>> {
|
fn handle_notification(
|
||||||
Box::pin(self.handle_notification(notification))
|
&self,
|
||||||
|
notification: R::PeerNot,
|
||||||
|
context: NotificationContext<R>,
|
||||||
|
) -> BoxFuture<Result<(), McpError>> {
|
||||||
|
Box::pin(self.handle_notification(notification, context))
|
||||||
}
|
}
|
||||||
fn get_info(&self) -> R::Info {
|
fn get_info(&self) -> R::Info {
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
@ -487,6 +499,15 @@ pub struct RequestContext<R: ServiceRole> {
|
||||||
pub peer: Peer<R>,
|
pub peer: Peer<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request execution context
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NotificationContext<R: ServiceRole> {
|
||||||
|
pub meta: Meta,
|
||||||
|
pub extensions: Extensions,
|
||||||
|
/// An interface to fetch the remote client or server
|
||||||
|
pub peer: Peer<R>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Use this function to skip initialization process
|
/// Use this function to skip initialization process
|
||||||
pub fn serve_directly<R, S, T, E, A>(
|
pub fn serve_directly<R, S, T, E, A>(
|
||||||
service: S,
|
service: S,
|
||||||
|
|
@ -710,7 +731,9 @@ where
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Event::PeerMessage(JsonRpcMessage::Request(JsonRpcRequest {
|
Event::PeerMessage(JsonRpcMessage::Request(JsonRpcRequest {
|
||||||
id, request, ..
|
id,
|
||||||
|
mut request,
|
||||||
|
..
|
||||||
})) => {
|
})) => {
|
||||||
tracing::debug!(%id, ?request, "received request");
|
tracing::debug!(%id, ?request, "received request");
|
||||||
{
|
{
|
||||||
|
|
@ -719,12 +742,17 @@ where
|
||||||
let request_ct = serve_loop_ct.child_token();
|
let request_ct = serve_loop_ct.child_token();
|
||||||
let context_ct = request_ct.child_token();
|
let context_ct = request_ct.child_token();
|
||||||
local_ct_pool.insert(id.clone(), request_ct);
|
local_ct_pool.insert(id.clone(), request_ct);
|
||||||
|
let mut extensions = Extensions::new();
|
||||||
|
let mut meta = Meta::new();
|
||||||
|
// avoid clone
|
||||||
|
std::mem::swap(&mut extensions, request.extensions_mut());
|
||||||
|
std::mem::swap(&mut meta, request.get_meta_mut());
|
||||||
let context = RequestContext {
|
let context = RequestContext {
|
||||||
ct: context_ct,
|
ct: context_ct,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
peer: peer.clone(),
|
peer: peer.clone(),
|
||||||
meta: request.get_meta().clone(),
|
meta,
|
||||||
extensions: request.extensions().clone(),
|
extensions,
|
||||||
};
|
};
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let result = service.handle_request(request, context).await;
|
let result = service.handle_request(request, context).await;
|
||||||
|
|
@ -748,7 +776,7 @@ where
|
||||||
})) => {
|
})) => {
|
||||||
tracing::info!(?notification, "received notification");
|
tracing::info!(?notification, "received notification");
|
||||||
// catch cancelled notification
|
// catch cancelled notification
|
||||||
let notification = match notification.try_into() {
|
let mut notification = match notification.try_into() {
|
||||||
Ok::<CancelledNotification, _>(cancelled) => {
|
Ok::<CancelledNotification, _>(cancelled) => {
|
||||||
if let Some(ct) = local_ct_pool.remove(&cancelled.params.request_id) {
|
if let Some(ct) = local_ct_pool.remove(&cancelled.params.request_id) {
|
||||||
tracing::info!(id = %cancelled.params.request_id, reason = cancelled.params.reason, "cancelled");
|
tracing::info!(id = %cancelled.params.request_id, reason = cancelled.params.reason, "cancelled");
|
||||||
|
|
@ -760,8 +788,18 @@ where
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let service = shared_service.clone();
|
let service = shared_service.clone();
|
||||||
|
let mut extensions = Extensions::new();
|
||||||
|
let mut meta = Meta::new();
|
||||||
|
// avoid clone
|
||||||
|
std::mem::swap(&mut extensions, notification.extensions_mut());
|
||||||
|
std::mem::swap(&mut meta, notification.get_meta_mut());
|
||||||
|
let context = NotificationContext {
|
||||||
|
peer: peer.clone(),
|
||||||
|
meta,
|
||||||
|
extensions,
|
||||||
|
};
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let result = service.handle_notification(notification).await;
|
let result = service.handle_notification(notification, context).await;
|
||||||
if let Err(error) = result {
|
if let Err(error) = result {
|
||||||
tracing::warn!(%error, "Error sending notification");
|
tracing::warn!(%error, "Error sending notification");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,12 @@ where
|
||||||
Some(ClientJsonRpcMessage::notification(notification)),
|
Some(ClientJsonRpcMessage::notification(notification)),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let _ = service.handle_notification(notification).await;
|
let context = NotificationContext {
|
||||||
|
meta: notification.get_meta().clone(),
|
||||||
|
extensions: notification.extensions().clone(),
|
||||||
|
peer: peer.clone(),
|
||||||
|
};
|
||||||
|
let _ = service.handle_notification(notification, context).await;
|
||||||
// Continue processing service
|
// Continue processing service
|
||||||
Ok(serve_inner(service, transport, peer, peer_rx, ct))
|
Ok(serve_inner(service, transport, peer, peer_rx, ct))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::{future::poll_fn, marker::PhantomData};
|
||||||
|
|
||||||
use tower_service::Service as TowerService;
|
use tower_service::Service as TowerService;
|
||||||
|
|
||||||
|
use super::NotificationContext;
|
||||||
use crate::service::{RequestContext, Service, ServiceRole};
|
use crate::service::{RequestContext, Service, ServiceRole};
|
||||||
|
|
||||||
pub struct TowerHandler<S, R: ServiceRole> {
|
pub struct TowerHandler<S, R: ServiceRole> {
|
||||||
|
|
@ -42,6 +43,7 @@ where
|
||||||
fn handle_notification(
|
fn handle_notification(
|
||||||
&self,
|
&self,
|
||||||
_notification: R::PeerNot,
|
_notification: R::PeerNot,
|
||||||
|
_context: NotificationContext<R>,
|
||||||
) -> impl Future<Output = Result<(), crate::Error>> + Send + '_ {
|
) -> impl Future<Output = Result<(), crate::Error>> + Send + '_ {
|
||||||
std::future::ready(Ok(()))
|
std::future::ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use tokio_stream::wrappers::ReceiverStream;
|
||||||
use super::session::SessionManager;
|
use super::session::SessionManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
RoleServer,
|
RoleServer,
|
||||||
model::ClientJsonRpcMessage,
|
model::{ClientJsonRpcMessage, GetExtensions},
|
||||||
serve_server,
|
serve_server,
|
||||||
service::serve_directly,
|
service::serve_directly,
|
||||||
transport::{
|
transport::{
|
||||||
|
|
@ -243,7 +243,7 @@ where
|
||||||
|
|
||||||
// json deserialize request body
|
// json deserialize request body
|
||||||
let (part, body) = request.into_parts();
|
let (part, body) = request.into_parts();
|
||||||
let message = match expect_json(body).await {
|
let mut message = match expect_json(body).await {
|
||||||
Ok(message) => message,
|
Ok(message) => message,
|
||||||
Err(response) => return Ok(response),
|
Err(response) => return Ok(response),
|
||||||
};
|
};
|
||||||
|
|
@ -271,6 +271,20 @@ where
|
||||||
)
|
)
|
||||||
.expect("valid response"));
|
.expect("valid response"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inject request part to extensions
|
||||||
|
match &mut message {
|
||||||
|
ClientJsonRpcMessage::Request(req) => {
|
||||||
|
req.request.extensions_mut().insert(part);
|
||||||
|
}
|
||||||
|
ClientJsonRpcMessage::Notification(not) => {
|
||||||
|
not.notification.extensions_mut().insert(part);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
ClientJsonRpcMessage::Request(_) => {
|
ClientJsonRpcMessage::Request(_) => {
|
||||||
let stream = self
|
let stream = self
|
||||||
|
|
@ -379,11 +393,8 @@ where
|
||||||
self.config.sse_keep_alive,
|
self.config.sse_keep_alive,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
ClientJsonRpcMessage::Notification(notification) => {
|
ClientJsonRpcMessage::Notification(_notification) => {
|
||||||
service
|
// ignore
|
||||||
.handle_notification(notification.notification)
|
|
||||||
.await
|
|
||||||
.map_err(internal_error_response("handle notification"))?;
|
|
||||||
Ok(accepted_response())
|
Ok(accepted_response())
|
||||||
}
|
}
|
||||||
ClientJsonRpcMessage::Response(_json_rpc_response) => Ok(accepted_response()),
|
ClientJsonRpcMessage::Response(_json_rpc_response) => Ok(accepted_response()),
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use rmcp::{
|
use rmcp::{
|
||||||
ClientHandler, Error as McpError, RoleClient, RoleServer, ServerHandler, model::*,
|
ClientHandler, Error as McpError, RoleClient, RoleServer, ServerHandler,
|
||||||
service::RequestContext,
|
model::*,
|
||||||
|
service::{NotificationContext, RequestContext},
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
|
|
@ -83,6 +84,7 @@ impl ClientHandler for TestClientHandler {
|
||||||
fn on_logging_message(
|
fn on_logging_message(
|
||||||
&self,
|
&self,
|
||||||
params: LoggingMessageNotificationParam,
|
params: LoggingMessageNotificationParam,
|
||||||
|
_context: NotificationContext<RoleClient>,
|
||||||
) -> impl Future<Output = ()> + Send + '_ {
|
) -> impl Future<Output = ()> + Send + '_ {
|
||||||
let receive_signal = self.receive_signal.clone();
|
let receive_signal = self.receive_signal.clone();
|
||||||
let received_messages = self.received_messages.clone();
|
let received_messages = self.received_messages.clone();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,11 @@ pub struct Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientHandler for Client {
|
impl ClientHandler for Client {
|
||||||
async fn on_resource_updated(&self, params: rmcp::model::ResourceUpdatedNotificationParam) {
|
async fn on_resource_updated(
|
||||||
|
&self,
|
||||||
|
params: rmcp::model::ResourceUpdatedNotificationParam,
|
||||||
|
_context: rmcp::service::NotificationContext<rmcp::RoleClient>,
|
||||||
|
) {
|
||||||
let uri = params.uri;
|
let uri = params.uri;
|
||||||
tracing::info!("Resource updated: {}", uri);
|
tracing::info!("Resource updated: {}", uri);
|
||||||
self.receive_signal.notify_one();
|
self.receive_signal.notify_one();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue