turul_mcp_protocol_2025_06_18/
lib.rs

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