turbomcp_protocol/context/capabilities.rs
1//! Server-to-client communication capabilities for bidirectional MCP communication.
2//!
3//! This module defines the trait that enables servers to make requests to clients,
4//! supporting sampling, elicitation, roots listing, and server-initiated notifications.
5
6use futures::future::BoxFuture;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10use crate::McpError;
11use crate::context::RequestContext;
12use crate::types::{
13 CreateMessageRequest, CreateMessageResult, ElicitRequest, ElicitResult, ListRootsResult,
14 ServerNotification,
15};
16
17/// Trait for server-to-client requests (sampling, elicitation, roots)
18///
19/// This trait provides a type-safe interface for servers to make requests to clients,
20/// enabling bidirectional MCP communication patterns. All methods accept a `RequestContext`
21/// parameter to enable proper context propagation for tracing, attribution, and auditing.
22///
23/// ## Design Rationale
24///
25/// This trait uses typed request/response structures instead of `serde_json::Value` to provide:
26/// - **Type safety**: Compile-time validation of request/response structures
27/// - **Performance**: Zero-cost abstraction with no intermediate serialization
28/// - **Context propagation**: Full support for distributed tracing and user attribution
29/// - **Better errors**: Structured error types instead of generic `Box<dyn Error>`
30///
31/// ## Breaking Change (v2.0.0)
32///
33/// This trait was renamed from `ServerCapabilities` to `ServerToClientRequests` and redesigned
34/// to fix fundamental architecture issues:
35/// - Old: `fn create_message(&self, request: serde_json::Value) -> Result<serde_json::Value, Box<dyn Error>>`
36/// - New: `fn create_message(&self, request: CreateMessageRequest, ctx: RequestContext) -> Result<CreateMessageResult, ServerError>`
37pub trait ServerToClientRequests: Send + Sync + fmt::Debug {
38 /// Send a sampling/createMessage request to the client
39 ///
40 /// This method allows server tools to request LLM sampling from the client.
41 /// The client is responsible for:
42 /// - Selecting an appropriate model based on preferences
43 /// - Making the LLM API call
44 /// - Returning the generated response
45 ///
46 /// # Arguments
47 ///
48 /// * `request` - The sampling request with messages, model preferences, and parameters
49 /// * `ctx` - Request context for tracing, user attribution, and metadata propagation
50 ///
51 /// # Errors
52 ///
53 /// Returns an error if:
54 /// - The client does not support sampling
55 /// - The transport layer fails
56 /// - The client returns an error response
57 /// - The LLM request fails
58 ///
59 /// # Example
60 ///
61 /// ```no_run
62 /// use turbomcp_protocol::context::capabilities::ServerToClientRequests;
63 /// use turbomcp_protocol::RequestContext;
64 /// # use turbomcp_protocol::types::{CreateMessageRequest, SamplingMessage, Role, Content, TextContent};
65 ///
66 /// async fn example(capabilities: &dyn ServerToClientRequests) {
67 /// let request = CreateMessageRequest {
68 /// messages: vec![SamplingMessage {
69 /// role: Role::User,
70 /// content: Content::Text(TextContent {
71 /// text: "What is 2+2?".to_string(),
72 /// annotations: None,
73 /// meta: None,
74 /// }),
75 /// metadata: None,
76 /// }],
77 /// model_preferences: None,
78 /// system_prompt: None,
79 /// include_context: None,
80 /// temperature: None,
81 /// max_tokens: 100,
82 /// stop_sequences: None,
83 /// task: None,
84 /// tools: None,
85 /// tool_choice: None,
86 /// _meta: None,
87 /// };
88 ///
89 /// let ctx = RequestContext::new();
90 /// # #[allow(unused)]
91 /// let result = capabilities.create_message(request, ctx).await;
92 /// }
93 /// ```
94 fn create_message(
95 &self,
96 request: CreateMessageRequest,
97 ctx: RequestContext,
98 ) -> BoxFuture<'_, Result<CreateMessageResult, McpError>>;
99
100 /// Send an elicitation request to the client for user input
101 ///
102 /// This method allows server tools to request structured input from users through
103 /// the client's UI. The client is responsible for presenting the elicitation prompt
104 /// and collecting the user's response according to the requested schema.
105 ///
106 /// # Arguments
107 ///
108 /// * `request` - The elicitation request with prompt and optional schema
109 /// * `ctx` - Request context for tracing, user attribution, and metadata propagation
110 ///
111 /// # Errors
112 ///
113 /// Returns an error if:
114 /// - The client does not support elicitation
115 /// - The transport layer fails
116 /// - The user declines or cancels the request
117 /// - The client returns an error response
118 fn elicit(
119 &self,
120 request: ElicitRequest,
121 ctx: RequestContext,
122 ) -> BoxFuture<'_, Result<ElicitResult, McpError>>;
123
124 /// List client's root capabilities
125 ///
126 /// This method allows servers to discover which directories or files the client
127 /// has granted access to. Roots define the filesystem boundaries for resource access.
128 ///
129 /// # Arguments
130 ///
131 /// * `ctx` - Request context for tracing, user attribution, and metadata propagation
132 ///
133 /// # Errors
134 ///
135 /// Returns an error if:
136 /// - The client does not support roots
137 /// - The transport layer fails
138 /// - The client returns an error response
139 fn list_roots(&self, ctx: RequestContext) -> BoxFuture<'_, Result<ListRootsResult, McpError>>;
140
141 /// Send a notification to the client.
142 ///
143 /// This method allows servers to send notifications to clients for logging,
144 /// progress updates, resource changes, and other events. Unlike requests,
145 /// notifications do not expect a response from the client.
146 ///
147 /// # Arguments
148 ///
149 /// * `notification` - The notification to send (logging, progress, etc.)
150 ///
151 /// # Errors
152 ///
153 /// Returns an error if:
154 /// - The transport layer fails to send the notification
155 /// - The connection is closed
156 ///
157 /// # Example
158 ///
159 /// ```no_run
160 /// use turbomcp_protocol::context::capabilities::ServerToClientRequests;
161 /// use turbomcp_protocol::types::{ServerNotification, LoggingNotification, LogLevel};
162 /// use serde_json::json;
163 ///
164 /// async fn example(capabilities: &dyn ServerToClientRequests) {
165 /// let notification = ServerNotification::Message(LoggingNotification {
166 /// level: LogLevel::Info,
167 /// data: json!("Processing request..."),
168 /// logger: Some("my_tool".to_string()),
169 /// });
170 ///
171 /// let _ = capabilities.send_notification(notification).await;
172 /// }
173 /// ```
174 fn send_notification(
175 &self,
176 notification: ServerNotification,
177 ) -> BoxFuture<'_, Result<(), McpError>>;
178}
179
180/// Communication direction for bidirectional requests
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
182pub enum CommunicationDirection {
183 /// Client to server
184 ClientToServer,
185 /// Server to client
186 ServerToClient,
187}
188
189/// Communication initiator for tracking request origins
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
191pub enum CommunicationInitiator {
192 /// Client initiated the request
193 Client,
194 /// Server initiated the request
195 Server,
196}
197
198/// Types of server-initiated requests
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200pub enum ServerInitiatedType {
201 /// Sampling/message creation request
202 Sampling,
203 /// Elicitation request for user input
204 Elicitation,
205 /// Roots listing request
206 Roots,
207 /// Ping/health check request
208 Ping,
209}
210
211/// Origin of a ping request
212#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
213pub enum PingOrigin {
214 /// Client initiated ping
215 Client,
216 /// Server initiated ping
217 Server,
218}