turbomcp_server/
capabilities.rs

1//! Server-to-client request adapter for bidirectional MCP communication.
2//!
3//! This module provides the adapter that bridges the `ServerToClientRequests` trait
4//! (defined in turbomcp-protocol) to the concrete `BidirectionalRouter` implementation,
5//! enabling tools to make server-initiated requests to clients.
6//!
7//! ## Architecture
8//!
9//! ```text
10//! Tool Handler (ctx.create_message)
11//!     ↓
12//! ServerToClientRequests trait (turbomcp-protocol) ← TYPE-SAFE INTERFACE
13//!     ↓
14//! ServerToClientAdapter (this module) ← THE BRIDGE
15//!     ↓
16//! BidirectionalRouter (turbomcp-server)
17//!     ↓
18//! ServerRequestDispatcher (transport abstraction)
19//!     ↓
20//! Transport Layer (stdio, HTTP, WebSocket, etc.)
21//! ```
22//!
23//! ## Design Improvements (v2.0.0)
24//!
25//! This adapter was redesigned to leverage the new type-safe trait:
26//! - **Before**: Double serialization (typed → JSON → typed → JSON)
27//! - **After**: Zero serialization (typed → typed)
28//! - **Before**: Cannot propagate RequestContext
29//! - **After**: Full context propagation for tracing and attribution
30//! - **Before**: Generic `Box<dyn Error>` return type
31//! - **After**: Structured `ServerError` with pattern matching
32//!
33//! ## Bug Fix History
34//!
35//! v1.x: Fixed critical bug where `RequestContext.server_capabilities` was never populated,
36//! causing all sampling/elicitation requests to fail.
37//!
38//! v2.0: Improved trait design to eliminate double serialization and enable context propagation.
39
40use futures::future::BoxFuture;
41use turbomcp_protocol::context::capabilities::ServerToClientRequests;
42use turbomcp_protocol::types::{
43    CreateMessageRequest, CreateMessageResult, ElicitRequest, ElicitResult, ListRootsRequest,
44    ListRootsResult,
45};
46use turbomcp_protocol::{Error as McpError, RequestContext};
47
48use crate::routing::BidirectionalRouter;
49
50/// Adapter that implements the `ServerToClientRequests` trait by delegating to `BidirectionalRouter`.
51///
52/// This adapter bridges the gap between the generic `ServerToClientRequests` trait (defined in
53/// turbomcp-protocol) and the concrete `BidirectionalRouter` implementation (in turbomcp-server).
54///
55/// ## Thread Safety
56///
57/// This adapter is `Send + Sync` because:
58/// - `BidirectionalRouter` implements `Clone` and contains only `Arc<dyn ServerRequestDispatcher>`
59/// - All interior state is immutable after construction
60///
61/// ## Performance
62///
63/// **Zero-overhead abstraction**:
64/// - No intermediate serialization (types flow directly through)
65/// - One `Arc` clone when creating the adapter (shared state)
66/// - Direct delegation to underlying router (no indirection)
67/// - Context propagation with zero allocation
68///
69/// ## Design Improvement (v2.0.0)
70///
71/// Previously named `ServerCapabilitiesAdapter`, this was renamed to `ServerToClientAdapter`
72/// for clarity and redesigned to eliminate double serialization.
73#[derive(Debug, Clone)]
74pub struct ServerToClientAdapter {
75    /// The bidirectional router that handles the actual server-initiated requests
76    bidirectional: BidirectionalRouter,
77}
78
79impl ServerToClientAdapter {
80    /// Create a new adapter wrapping a bidirectional router.
81    ///
82    /// # Example
83    ///
84    /// ```no_run
85    /// use turbomcp_server::capabilities::ServerToClientAdapter;
86    /// use turbomcp_server::routing::BidirectionalRouter;
87    ///
88    /// let router = BidirectionalRouter::new();
89    /// let adapter = ServerToClientAdapter::new(router);
90    /// ```
91    pub fn new(bidirectional: BidirectionalRouter) -> Self {
92        Self { bidirectional }
93    }
94
95    /// Check if bidirectional communication is supported.
96    ///
97    /// Returns `true` if a dispatcher has been configured, `false` otherwise.
98    pub fn supports_bidirectional(&self) -> bool {
99        self.bidirectional.supports_bidirectional()
100    }
101}
102
103impl ServerToClientRequests for ServerToClientAdapter {
104    fn create_message(
105        &self,
106        request: CreateMessageRequest,
107        ctx: RequestContext,
108    ) -> BoxFuture<'_, Result<CreateMessageResult, McpError>> {
109        Box::pin(async move {
110            // Delegate directly to the bidirectional router with full context propagation
111            // No serialization needed - types flow through directly (zero-cost abstraction)
112            // Error conversion preserves protocol errors (e.g., -1 for user rejection)
113            self.bidirectional
114                .send_create_message_to_client(request, ctx)
115                .await
116                .map_err(|e| *Box::<turbomcp_protocol::Error>::from(e))
117        })
118    }
119
120    fn elicit(
121        &self,
122        request: ElicitRequest,
123        ctx: RequestContext,
124    ) -> BoxFuture<'_, Result<ElicitResult, McpError>> {
125        Box::pin(async move {
126            // Delegate directly to the bidirectional router with full context propagation
127            // No serialization needed - types flow through directly (zero-cost abstraction)
128            // Error conversion preserves protocol errors (e.g., -1 for user rejection)
129            self.bidirectional
130                .send_elicitation_to_client(request, ctx)
131                .await
132                .map_err(|e| *Box::<turbomcp_protocol::Error>::from(e))
133        })
134    }
135
136    fn list_roots(&self, ctx: RequestContext) -> BoxFuture<'_, Result<ListRootsResult, McpError>> {
137        Box::pin(async move {
138            // Create the list roots request (only has optional _meta field)
139            let list_roots_request = ListRootsRequest { _meta: None };
140
141            // Delegate directly to the bidirectional router with full context propagation
142            // No serialization needed - types flow through directly (zero-cost abstraction)
143            // Error conversion preserves protocol errors
144            self.bidirectional
145                .send_list_roots_to_client(list_roots_request, ctx)
146                .await
147                .map_err(|e| *Box::<turbomcp_protocol::Error>::from(e))
148        })
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_adapter_creation() {
158        let router = BidirectionalRouter::new();
159        let adapter = ServerToClientAdapter::new(router);
160
161        // Should support bidirectional if dispatcher is configured
162        // (not configured in this test, so should be false)
163        assert!(!adapter.supports_bidirectional());
164    }
165
166    #[test]
167    fn test_adapter_is_send_sync() {
168        fn assert_send_sync<T: Send + Sync>() {}
169        assert_send_sync::<ServerToClientAdapter>();
170    }
171
172    #[test]
173    fn test_adapter_implements_trait() {
174        let router = BidirectionalRouter::new();
175        let adapter = ServerToClientAdapter::new(router);
176
177        // Verify that the adapter implements ServerToClientRequests
178        let _: &dyn ServerToClientRequests = &adapter;
179    }
180
181    #[test]
182    fn test_adapter_clone() {
183        let router = BidirectionalRouter::new();
184        let adapter1 = ServerToClientAdapter::new(router);
185        let adapter2 = adapter1.clone();
186
187        // Both should have the same bidirectional support status
188        assert_eq!(
189            adapter1.supports_bidirectional(),
190            adapter2.supports_bidirectional()
191        );
192    }
193}