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