Skip to main content

turul_mcp_protocol_2025_11_25/
lib.rs

1//! # Model Context Protocol (MCP) - 2025-11-25 Specification
2//!
3//! This crate provides a complete implementation of the Model Context Protocol (MCP)
4//! specification version 2025-11-25. It includes all the types, traits, and utilities
5//! needed to build MCP-compliant servers and clients.
6//!
7//! ## Features
8//! - Complete MCP 2025-11-25 specification compliance
9//! - Support for all MCP capabilities (tools, resources, prompts, etc.)
10//! - Built on top of the turul-json-rpc-server foundation
11//! - Support for streamable HTTP and _meta fields
12//! - Progress tokens and cursor support
13//! - Structured user elicitation via JSON Schema (form and URL modes)
14//! - Meta field merging utilities for request/response round-tripping
15//! - Task system (experimental) for long-running operations
16//! - Icons on tools, resources, prompts, and implementations
17//! - Tools in sampling requests
18//!
19//! ## Meta Field Usage
20//!
21//! The protocol supports rich `_meta` fields for pagination, progress tracking,
22//! and custom metadata:
23//!
24//! ```rust
25//! use turul_mcp_protocol_2025_11_25::meta::{Meta, Cursor};
26//! use std::collections::HashMap;
27//! use serde_json::{json, Value};
28//!
29//! // Create meta with pagination
30//! let meta = Meta::with_pagination(
31//!     Some(Cursor::new("next-page")),
32//!     Some(100),
33//!     true
34//! );
35//!
36//! // Merge request extras while preserving structured fields
37//! let mut request_extras = HashMap::new();
38//! request_extras.insert("userContext".to_string(), json!("user_123"));
39//! request_extras.insert("customField".to_string(), json!("custom_value"));
40//!
41//! let response_meta = meta.merge_request_extras(Some(&request_extras));
42//! ```
43
44pub mod completion;
45pub mod content;
46pub mod elicitation;
47pub mod icons;
48pub mod initialize;
49pub mod json_rpc;
50pub mod logging;
51pub mod meta;
52pub mod notifications;
53pub mod param_extraction;
54pub mod ping;
55pub mod prelude;
56pub mod prompts;
57pub mod resources;
58pub mod roots;
59pub mod sampling;
60pub mod schema;
61pub mod tasks;
62pub mod tools;
63pub mod traits;
64pub mod version;
65
66// Re-export key content types for convenience
67pub use content::{
68    BlobResourceContents, ContentBlock, ResourceContents, ResourceReference, TextResourceContents,
69};
70// Re-export key meta types for convenience
71pub use meta::{Annotations, Meta};
72
73#[cfg(test)]
74mod compliance_test;
75
76// Re-export main types
77pub use icons::{Icon, IconTheme};
78pub use initialize::{
79    ClientCapabilities, Implementation, InitializeRequest, InitializeResult, ServerCapabilities,
80    TasksCancelCapabilities, TasksCapabilities, TasksListCapabilities, TasksRequestCapabilities,
81    TasksToolCallCapabilities, TasksToolCapabilities,
82};
83pub use prompts::{
84    GetPromptRequest, GetPromptResult, ListPromptsRequest, ListPromptsResult, Prompt,
85    PromptArgument, PromptMessage,
86};
87pub use resources::{
88    ListResourcesRequest, ListResourcesResult, ReadResourceRequest, ReadResourceResult, Resource,
89    ResourceContent, ResourceSubscription, SubscribeRequest, UnsubscribeRequest,
90};
91pub use tasks::{
92    CancelTaskParams, CancelTaskRequest, CancelTaskResult, CreateTaskResult, GetTaskParams,
93    GetTaskPayloadParams, GetTaskPayloadRequest, GetTaskRequest, GetTaskResult, ListTasksParams,
94    ListTasksRequest, ListTasksResult, Task, TaskMetadata, TaskStatus,
95};
96pub use tools::{
97    CallToolRequest, CallToolResult, ListToolsRequest, ListToolsResult, TaskSupport, Tool,
98    ToolExecution, ToolResult, ToolSchema,
99};
100pub use version::McpVersion;
101// ResourceTemplate functionality is now part of resources module
102// pub use resources::{ResourceTemplate, ListResourceTemplatesRequest, ListResourceTemplatesResult};
103pub use elicitation::{
104    ElicitAction, ElicitCreateParams, ElicitCreateRequest, ElicitResult, ElicitationBuilder,
105    ElicitationSchema, PrimitiveSchemaDefinition, StringFormat,
106};
107pub use json_rpc::{
108    JsonRpcError, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse,
109    RequestParams, ResultWithMeta,
110};
111pub use meta::{
112    Cursor as MetaCursor, PaginatedResponse, ProgressResponse, ProgressToken, WithMeta,
113};
114pub use notifications::{
115    CancelledNotification, ElicitationCompleteNotification, InitializedNotification,
116    LoggingMessageNotification, LoggingMessageNotificationParams, Notification, NotificationParams,
117    ProgressNotification, ProgressNotificationParams, ProgressTokenValue,
118    PromptListChangedNotification, ResourceListChangedNotification, ResourceUpdatedNotification,
119    ResourceUpdatedNotificationParams, RootsListChangedNotification, TaskStatusNotification,
120    ToolListChangedNotification,
121};
122pub use ping::{EmptyParams, EmptyResult, PingRequest};
123pub use schema::JsonSchema;
124pub use traits::{
125    HasData, HasDataParam, HasMeta, HasMetaParam, HasProgressTokenParam, JsonRpcNotificationTrait,
126    JsonRpcRequestTrait, JsonRpcResponseTrait, Params, RpcResult,
127};
128
129// JSON-RPC foundation (legacy - prefer our implementations above)
130pub use turul_mcp_json_rpc_server::{
131    RequestParams as LegacyRequestParams, ResponseResult, types::RequestId,
132};
133
134/// The MCP protocol version implemented by this crate
135pub const MCP_VERSION: &str = "2025-11-25";
136
137/// Common result type for MCP operations
138pub type McpResult<T> = Result<T, McpError>;
139
140/// MCP-specific errors
141#[derive(Debug, thiserror::Error)]
142pub enum McpError {
143    #[error("Protocol version mismatch: expected {expected}, got {actual}")]
144    VersionMismatch { expected: String, actual: String },
145
146    #[error("Invalid capability: {0}")]
147    InvalidCapability(String),
148
149    #[error("Tool not found: {0}")]
150    ToolNotFound(String),
151
152    #[error("Resource not found: {0}")]
153    ResourceNotFound(String),
154
155    #[error("Prompt not found: {0}")]
156    PromptNotFound(String),
157
158    #[error("Invalid request: {message}")]
159    InvalidRequest { message: String },
160
161    #[error("Invalid parameters: {0}")]
162    InvalidParameters(String),
163
164    #[error("Missing required parameter: {0}")]
165    MissingParameter(String),
166
167    #[error("Invalid parameter type for '{param}': expected {expected}, got {actual}")]
168    InvalidParameterType {
169        param: String,
170        expected: String,
171        actual: String,
172    },
173
174    #[error("Parameter '{param}' value {value} is out of range: {constraint}")]
175    ParameterOutOfRange {
176        param: String,
177        value: String,
178        constraint: String,
179    },
180
181    #[error("Tool execution failed: {0}")]
182    ToolExecutionError(String),
183
184    #[error("Resource execution failed: {0}")]
185    ResourceExecutionError(String),
186
187    #[error("Prompt execution failed: {0}")]
188    PromptExecutionError(String),
189
190    #[error("Resource access denied: {0}")]
191    ResourceAccessDenied(String),
192
193    #[error("Configuration error: {0}")]
194    ConfigurationError(String),
195
196    #[error("Session error: {0}")]
197    SessionError(String),
198
199    #[error("Validation error: {0}")]
200    ValidationError(String),
201
202    #[error("IO error: {0}")]
203    IoError(#[from] std::io::Error),
204
205    #[error("Serialization error: {0}")]
206    SerializationError(#[from] serde_json::Error),
207
208    #[error("Transport error: {0}")]
209    TransportError(String),
210
211    #[error("JSON-RPC protocol error: {0}")]
212    JsonRpcProtocolError(String),
213
214    /// A JSON-RPC error with preserved code, message, and optional data.
215    ///
216    /// Used by `tasks/result` to reproduce the original error verbatim, as
217    /// required by the MCP spec: "tasks/result MUST return that same JSON-RPC error."
218    #[error("JSON-RPC error {code}: {message}")]
219    JsonRpcError {
220        code: i64,
221        message: String,
222        data: Option<serde_json::Value>,
223    },
224}
225
226impl From<String> for McpError {
227    fn from(message: String) -> Self {
228        Self::ToolExecutionError(message)
229    }
230}
231
232impl From<&str> for McpError {
233    fn from(message: &str) -> Self {
234        Self::ToolExecutionError(message.to_string())
235    }
236}
237
238impl McpError {
239    /// Create a missing parameter error
240    pub fn missing_param(param: &str) -> Self {
241        Self::MissingParameter(param.to_string())
242    }
243
244    /// Create an invalid parameter type error
245    pub fn invalid_param_type(param: &str, expected: &str, actual: &str) -> Self {
246        Self::InvalidParameterType {
247            param: param.to_string(),
248            expected: expected.to_string(),
249            actual: actual.to_string(),
250        }
251    }
252
253    /// Create a parameter out of range error
254    pub fn param_out_of_range(param: &str, value: &str, constraint: &str) -> Self {
255        Self::ParameterOutOfRange {
256            param: param.to_string(),
257            value: value.to_string(),
258            constraint: constraint.to_string(),
259        }
260    }
261
262    /// Create a tool execution error
263    pub fn tool_execution(message: &str) -> Self {
264        Self::ToolExecutionError(message.to_string())
265    }
266
267    /// Create a resource execution error
268    pub fn resource_execution(message: &str) -> Self {
269        Self::ResourceExecutionError(message.to_string())
270    }
271
272    /// Create a prompt execution error
273    pub fn prompt_execution(message: &str) -> Self {
274        Self::PromptExecutionError(message.to_string())
275    }
276
277    /// Create a validation error
278    pub fn validation(message: &str) -> Self {
279        Self::ValidationError(message.to_string())
280    }
281
282    /// Create a configuration error
283    pub fn configuration(message: &str) -> Self {
284        Self::ConfigurationError(message.to_string())
285    }
286
287    /// Create a transport error
288    pub fn transport(message: &str) -> Self {
289        Self::TransportError(message.to_string())
290    }
291
292    /// Create a JSON-RPC protocol error
293    pub fn json_rpc_protocol(message: &str) -> Self {
294        Self::JsonRpcProtocolError(message.to_string())
295    }
296
297    /// Create a JSON-RPC error with preserved code, message, and optional data.
298    ///
299    /// Used by `tasks/result` to reproduce original errors verbatim.
300    pub fn json_rpc_error(
301        code: i64,
302        message: impl Into<String>,
303        data: Option<serde_json::Value>,
304    ) -> Self {
305        Self::JsonRpcError {
306            code,
307            message: message.into(),
308            data,
309        }
310    }
311
312    /// Convert to a JsonRpcErrorObject for JSON-RPC 2.0 responses
313    pub fn to_error_object(&self) -> turul_mcp_json_rpc_server::error::JsonRpcErrorObject {
314        use turul_mcp_json_rpc_server::error::JsonRpcErrorObject;
315
316        match self {
317            // Request-level errors map to InvalidParams (-32602) with descriptive message
318            McpError::InvalidRequest { message } => JsonRpcErrorObject::invalid_params(message),
319
320            // Parameter-related errors map to InvalidParams (-32602)
321            McpError::InvalidParameters(msg) => JsonRpcErrorObject::invalid_params(msg),
322            McpError::MissingParameter(param) => JsonRpcErrorObject::invalid_params(&format!(
323                "Missing required parameter: {}",
324                param
325            )),
326            McpError::InvalidParameterType {
327                param,
328                expected,
329                actual,
330            } => JsonRpcErrorObject::invalid_params(&format!(
331                "Invalid parameter type for '{}': expected {}, got {}",
332                param, expected, actual
333            )),
334            McpError::ParameterOutOfRange {
335                param,
336                value,
337                constraint,
338            } => JsonRpcErrorObject::invalid_params(&format!(
339                "Parameter '{}' value {} is out of range: {}",
340                param, value, constraint
341            )),
342
343            // Not found errors map to server errors
344            McpError::ToolNotFound(name) => {
345                JsonRpcErrorObject::server_error(-32001, &format!("Tool not found: {}", name), None)
346            }
347            McpError::ResourceNotFound(uri) => JsonRpcErrorObject::server_error(
348                -32002,
349                &format!("Resource not found: {}", uri),
350                None,
351            ),
352            McpError::PromptNotFound(name) => JsonRpcErrorObject::server_error(
353                -32003,
354                &format!("Prompt not found: {}", name),
355                None,
356            ),
357
358            // Access and execution errors
359            McpError::ToolExecutionError(msg) => JsonRpcErrorObject::server_error(
360                -32010,
361                &format!("Tool execution failed: {}", msg),
362                None,
363            ),
364            McpError::ResourceExecutionError(msg) => JsonRpcErrorObject::server_error(
365                -32012,
366                &format!("Resource execution failed: {}", msg),
367                None,
368            ),
369            McpError::PromptExecutionError(msg) => JsonRpcErrorObject::server_error(
370                -32013,
371                &format!("Prompt execution failed: {}", msg),
372                None,
373            ),
374            McpError::ResourceAccessDenied(uri) => JsonRpcErrorObject::server_error(
375                -32011,
376                &format!("Resource access denied: {}", uri),
377                None,
378            ),
379
380            // Validation errors
381            McpError::ValidationError(msg) => JsonRpcErrorObject::server_error(
382                -32020,
383                &format!("Validation error: {}", msg),
384                None,
385            ),
386            McpError::InvalidCapability(cap) => JsonRpcErrorObject::server_error(
387                -32021,
388                &format!("Invalid capability: {}", cap),
389                None,
390            ),
391            McpError::VersionMismatch { expected, actual } => JsonRpcErrorObject::server_error(
392                -32022,
393                &format!(
394                    "Protocol version mismatch: expected {}, got {}",
395                    expected, actual
396                ),
397                None,
398            ),
399
400            // Configuration and session errors
401            McpError::ConfigurationError(msg) => JsonRpcErrorObject::server_error(
402                -32030,
403                &format!("Configuration error: {}", msg),
404                None,
405            ),
406            McpError::SessionError(msg) => {
407                JsonRpcErrorObject::server_error(-32031, &format!("Session error: {}", msg), None)
408            }
409
410            // Transport and protocol layer errors
411            McpError::TransportError(msg) => {
412                JsonRpcErrorObject::server_error(-32040, &format!("Transport error: {}", msg), None)
413            }
414            McpError::JsonRpcProtocolError(msg) => JsonRpcErrorObject::server_error(
415                -32041,
416                &format!("JSON-RPC protocol error: {}", msg),
417                None,
418            ),
419
420            // I/O and serialization errors map to internal errors
421            McpError::IoError(err) => {
422                JsonRpcErrorObject::internal_error(Some(format!("IO error: {}", err)))
423            }
424            McpError::SerializationError(err) => {
425                JsonRpcErrorObject::internal_error(Some(format!("Serialization error: {}", err)))
426            }
427
428            // Pass-through: preserves original code/message/data verbatim
429            McpError::JsonRpcError {
430                code,
431                message,
432                data,
433            } => JsonRpcErrorObject::server_error(*code, message, data.clone()),
434        }
435    }
436
437    /// Create a JSON-RPC error response for this MCP error
438    pub fn to_json_rpc_response(
439        &self,
440        id: Option<turul_mcp_json_rpc_server::RequestId>,
441    ) -> turul_mcp_json_rpc_server::JsonRpcError {
442        turul_mcp_json_rpc_server::JsonRpcError::new(id, self.to_error_object())
443    }
444
445    /// Legacy method for backward compatibility - use to_error_object() instead
446    #[deprecated(note = "Use to_error_object() instead for cleaner architecture")]
447    pub fn to_json_rpc_error(&self) -> turul_mcp_json_rpc_server::error::JsonRpcErrorObject {
448        self.to_error_object()
449    }
450}
451
452// Implement the ToJsonRpcError trait for MCP errors
453impl turul_mcp_json_rpc_server::r#async::ToJsonRpcError for McpError {
454    fn to_error_object(&self) -> turul_mcp_json_rpc_server::error::JsonRpcErrorObject {
455        // Delegate to our existing type-safe implementation
456        McpError::to_error_object(self)
457    }
458}