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}