Skip to main content

turbomcp_protocol/types/
capabilities.rs

1//! MCP capability negotiation types
2//!
3//! This module contains types for capability discovery and negotiation between
4//! MCP clients and servers. Capabilities define what features each side supports
5//! and are exchanged during the initialization handshake.
6//!
7//! # Capability Types
8//!
9//! - [`ClientCapabilities`] - Client-side capabilities
10//! - [`ServerCapabilities`] - Server-side capabilities
11//! - Feature-specific capability structures for each MCP feature
12
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15
16/// Client capabilities per MCP 2025-11-25 specification
17///
18/// ## Version Support
19/// - MCP 2025-11-25: roots, sampling, elicitation, experimental
20/// - MCP 2025-11-25 draft (SEP-1686): + tasks
21#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22pub struct ClientCapabilities {
23    /// Draft extensions supported by the client
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub extensions: Option<HashMap<String, serde_json::Value>>,
26
27    /// Experimental, non-standard capabilities that the client supports
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub experimental: Option<HashMap<String, serde_json::Value>>,
30
31    /// Present if the client supports listing roots
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub roots: Option<RootsCapabilities>,
34
35    /// Present if the client supports sampling from an LLM
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub sampling: Option<SamplingCapabilities>,
38
39    /// Present if the client supports elicitation from the server
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub elicitation: Option<ElicitationCapabilities>,
42
43    /// Present if the client supports the Tasks API (MCP 2025-11-25 draft, SEP-1686)
44    ///
45    /// When present, indicates the client can act as a receiver for task-augmented requests
46    /// from the server (e.g., sampling/createMessage, elicitation/create).
47    #[cfg(feature = "experimental-tasks")]
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub tasks: Option<ClientTasksCapabilities>,
50}
51
52/// Server capabilities per MCP 2025-11-25 specification
53///
54/// ## Version Support
55/// - MCP 2025-11-25: logging, completions, prompts, resources, tools, experimental
56/// - MCP 2025-11-25 draft (SEP-1686): + tasks
57#[derive(Debug, Clone, Serialize, Deserialize, Default)]
58pub struct ServerCapabilities {
59    /// Draft extensions supported by the server
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub extensions: Option<HashMap<String, serde_json::Value>>,
62
63    /// Experimental, non-standard capabilities that the server supports
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub experimental: Option<HashMap<String, serde_json::Value>>,
66
67    /// Present if the server supports sending log messages to the client
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub logging: Option<LoggingCapabilities>,
70
71    /// Present if the server supports argument autocompletion suggestions
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub completions: Option<CompletionCapabilities>,
74
75    /// Present if the server offers any prompt templates
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub prompts: Option<PromptsCapabilities>,
78
79    /// Present if the server offers any resources to read
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub resources: Option<ResourcesCapabilities>,
82
83    /// Present if the server offers any tools to call
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub tools: Option<ToolsCapabilities>,
86
87    /// Present if the server supports the Tasks API (MCP 2025-11-25 draft, SEP-1686)
88    ///
89    /// When present, indicates the server can act as a receiver for task-augmented requests
90    /// from the client (e.g., tools/call).
91    #[cfg(feature = "experimental-tasks")]
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub tasks: Option<ServerTasksCapabilities>,
94}
95
96/// Sampling capabilities
97#[derive(Debug, Clone, Serialize, Deserialize, Default)]
98pub struct SamplingCapabilities {}
99
100/// Form mode elicitation capabilities
101#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
102pub struct ElicitationFormCapabilities {}
103
104/// URL mode elicitation capabilities
105#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
106pub struct ElicitationUrlCapabilities {}
107
108/// Elicitation capabilities per MCP 2025-11-25 specification
109///
110/// Supports two modes:
111/// - `form`: In-band structured data collection (default if empty object)
112/// - `url`: Out-of-band interactions (OAuth, credentials, payments)
113///
114/// Per spec, an empty capabilities object (`{}`) is equivalent to declaring
115/// support for `form` mode only.
116#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
117pub struct ElicitationCapabilities {
118    /// Form mode elicitation support.
119    /// Per spec, an empty capabilities object defaults to form support.
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub form: Option<ElicitationFormCapabilities>,
122
123    /// URL mode elicitation support (MCP 2025-11-25).
124    /// For sensitive interactions (OAuth, credentials, payments).
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub url: Option<ElicitationUrlCapabilities>,
127
128    /// Whether the client performs JSON schema validation on elicitation responses.
129    /// If true, the client validates user input against the provided schema before sending.
130    /// (TurboMCP extension — not part of the MCP specification.)
131    #[serde(rename = "schemaValidation", skip_serializing_if = "Option::is_none")]
132    pub schema_validation: Option<bool>,
133}
134
135impl ElicitationCapabilities {
136    /// Create with both form and URL mode support (full capabilities)
137    pub fn full() -> Self {
138        Self {
139            form: Some(ElicitationFormCapabilities {}),
140            url: Some(ElicitationUrlCapabilities {}),
141            schema_validation: None,
142        }
143    }
144
145    /// Create with form mode only
146    pub fn form_only() -> Self {
147        Self {
148            form: Some(ElicitationFormCapabilities {}),
149            url: None,
150            schema_validation: None,
151        }
152    }
153
154    /// Whether this client supports form mode elicitation.
155    /// Per spec: empty capabilities object defaults to form support.
156    pub fn supports_form(&self) -> bool {
157        self.form.is_some() || (self.form.is_none() && self.url.is_none())
158    }
159
160    /// Whether this client supports URL mode elicitation.
161    pub fn supports_url(&self) -> bool {
162        self.url.is_some()
163    }
164
165    /// Enable schema validation (TurboMCP extension)
166    pub fn with_schema_validation(mut self) -> Self {
167        self.schema_validation = Some(true);
168        self
169    }
170
171    /// Disable schema validation (TurboMCP extension)
172    pub fn without_schema_validation(mut self) -> Self {
173        self.schema_validation = Some(false);
174        self
175    }
176}
177
178/// Completion capabilities
179#[derive(Debug, Clone, Serialize, Deserialize, Default)]
180pub struct CompletionCapabilities {}
181
182/// Roots capabilities
183#[derive(Debug, Clone, Serialize, Deserialize, Default)]
184pub struct RootsCapabilities {
185    /// Whether list can change
186    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
187    pub list_changed: Option<bool>,
188}
189
190/// Logging capabilities
191#[derive(Debug, Clone, Serialize, Deserialize, Default)]
192pub struct LoggingCapabilities {}
193
194/// Prompts capabilities
195#[derive(Debug, Clone, Serialize, Deserialize, Default)]
196pub struct PromptsCapabilities {
197    /// Whether list can change
198    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
199    pub list_changed: Option<bool>,
200}
201
202/// Resources capabilities
203#[derive(Debug, Clone, Serialize, Deserialize, Default)]
204pub struct ResourcesCapabilities {
205    /// Whether subscribe is supported
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub subscribe: Option<bool>,
208
209    /// Whether list can change
210    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
211    pub list_changed: Option<bool>,
212}
213
214/// Tools capabilities
215#[derive(Debug, Clone, Serialize, Deserialize, Default)]
216pub struct ToolsCapabilities {
217    /// Whether list can change
218    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
219    pub list_changed: Option<bool>,
220}
221
222// ========== Tasks API Capabilities (MCP 2025-11-25 draft, SEP-1686) ==========
223
224/// Server tasks capabilities (MCP 2025-11-25 draft, SEP-1686)
225///
226/// Indicates which task operations and request types the server supports.
227///
228/// ## Example
229///
230/// ```rust,ignore
231/// use turbomcp_protocol::types::{
232///     ServerTasksCapabilities, TasksRequestsCapabilities, TasksToolsCapabilities
233/// };
234///
235/// let tasks_caps = ServerTasksCapabilities {
236///     list: Some(TasksListCapabilities {}),
237///     cancel: Some(TasksCancelCapabilities {}),
238///     requests: Some(TasksRequestsCapabilities {
239///         tools: Some(TasksToolsCapabilities {
240///             call: Some(TasksToolsCallCapabilities {}),
241///         }),
242///         ..Default::default()
243///     }),
244/// };
245/// ```
246#[derive(Debug, Clone, Serialize, Deserialize, Default)]
247#[cfg(feature = "experimental-tasks")]
248pub struct ServerTasksCapabilities {
249    /// Present if the server supports tasks/list
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub list: Option<TasksListCapabilities>,
252
253    /// Present if the server supports tasks/cancel
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub cancel: Option<TasksCancelCapabilities>,
256
257    /// Present if the server supports task-augmented requests
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub requests: Option<ServerTasksRequestsCapabilities>,
260}
261
262/// Client tasks capabilities (MCP 2025-11-25 draft, SEP-1686)
263///
264/// Indicates which task operations and request types the client supports.
265///
266/// ## Example
267///
268/// ```rust,ignore
269/// use turbomcp_protocol::types::{
270///     ClientTasksCapabilities, ClientTasksRequestsCapabilities,
271///     TasksSamplingCapabilities, TasksSamplingCreateMessageCapabilities
272/// };
273///
274/// let tasks_caps = ClientTasksCapabilities {
275///     list: Some(TasksListCapabilities {}),
276///     cancel: Some(TasksCancelCapabilities {}),
277///     requests: Some(ClientTasksRequestsCapabilities {
278///         sampling: Some(TasksSamplingCapabilities {
279///             create_message: Some(TasksSamplingCreateMessageCapabilities {}),
280///         }),
281///         elicitation: Some(TasksElicitationCapabilities {
282///             create: Some(TasksElicitationCreateCapabilities {}),
283///         }),
284///     }),
285/// };
286/// ```
287#[derive(Debug, Clone, Serialize, Deserialize, Default)]
288#[cfg(feature = "experimental-tasks")]
289pub struct ClientTasksCapabilities {
290    /// Present if the client supports tasks/list
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub list: Option<TasksListCapabilities>,
293
294    /// Present if the client supports tasks/cancel
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub cancel: Option<TasksCancelCapabilities>,
297
298    /// Present if the client supports task-augmented requests
299    #[serde(skip_serializing_if = "Option::is_none")]
300    pub requests: Option<ClientTasksRequestsCapabilities>,
301}
302
303/// Task list capability
304#[derive(Debug, Clone, Serialize, Deserialize, Default)]
305#[cfg(feature = "experimental-tasks")]
306pub struct TasksListCapabilities {}
307
308/// Task cancel capability
309#[derive(Debug, Clone, Serialize, Deserialize, Default)]
310#[cfg(feature = "experimental-tasks")]
311pub struct TasksCancelCapabilities {}
312
313/// Server-side task-augmented requests capabilities
314#[derive(Debug, Clone, Serialize, Deserialize, Default)]
315#[cfg(feature = "experimental-tasks")]
316pub struct ServerTasksRequestsCapabilities {
317    /// Present if the server supports task-augmented tools/call
318    #[serde(skip_serializing_if = "Option::is_none")]
319    pub tools: Option<TasksToolsCapabilities>,
320}
321
322/// Client-side task-augmented requests capabilities
323#[derive(Debug, Clone, Serialize, Deserialize, Default)]
324#[cfg(feature = "experimental-tasks")]
325pub struct ClientTasksRequestsCapabilities {
326    /// Present if the client supports task-augmented sampling/createMessage
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub sampling: Option<TasksSamplingCapabilities>,
329
330    /// Present if the client supports task-augmented elicitation/create
331    #[serde(skip_serializing_if = "Option::is_none")]
332    pub elicitation: Option<TasksElicitationCapabilities>,
333}
334
335/// Tools task capabilities
336#[derive(Debug, Clone, Serialize, Deserialize, Default)]
337#[cfg(feature = "experimental-tasks")]
338pub struct TasksToolsCapabilities {
339    /// Present if task-augmented tools/call is supported
340    #[serde(skip_serializing_if = "Option::is_none")]
341    pub call: Option<TasksToolsCallCapabilities>,
342}
343
344/// Tools call task capability
345#[derive(Debug, Clone, Serialize, Deserialize, Default)]
346#[cfg(feature = "experimental-tasks")]
347pub struct TasksToolsCallCapabilities {}
348
349/// Sampling task capabilities
350#[derive(Debug, Clone, Serialize, Deserialize, Default)]
351#[cfg(feature = "experimental-tasks")]
352pub struct TasksSamplingCapabilities {
353    /// Present if task-augmented sampling/createMessage is supported
354    #[serde(rename = "createMessage", skip_serializing_if = "Option::is_none")]
355    pub create_message: Option<TasksSamplingCreateMessageCapabilities>,
356}
357
358/// Sampling create message task capability
359#[derive(Debug, Clone, Serialize, Deserialize, Default)]
360#[cfg(feature = "experimental-tasks")]
361pub struct TasksSamplingCreateMessageCapabilities {}
362
363/// Elicitation task capabilities
364#[derive(Debug, Clone, Serialize, Deserialize, Default)]
365#[cfg(feature = "experimental-tasks")]
366pub struct TasksElicitationCapabilities {
367    /// Present if task-augmented elicitation/create is supported
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub create: Option<TasksElicitationCreateCapabilities>,
370}
371
372/// Elicitation create task capability
373#[derive(Debug, Clone, Serialize, Deserialize, Default)]
374#[cfg(feature = "experimental-tasks")]
375pub struct TasksElicitationCreateCapabilities {}