solidmcp/
handler.rs

1//! MCP Handler Trait and Core Types
2//!
3//! This module defines the core `McpHandler` trait that servers must implement to provide
4//! MCP functionality. It also includes essential types for notifications, context management,
5//! and protocol data structures.
6//!
7//! # Overview
8//!
9//! The `McpHandler` trait is the main integration point for the solidmcp library. You can
10//! either implement this trait directly for full control, or use the higher-level framework
11//! API in the `framework` module for a more ergonomic experience.
12//!
13//! # Example Implementation
14//!
15//! ```rust
16//! use solidmcp::handler::{McpHandler, McpContext, ToolDefinition};
17//! use anyhow::Result;
18//! use async_trait::async_trait;
19//! use serde_json::{json, Value};
20//!
21//! struct MyHandler {
22//!     // Your application state
23//! }
24//!
25//! #[async_trait]
26//! impl McpHandler for MyHandler {
27//!     async fn initialize(&self, params: Value, context: &McpContext) -> Result<Value> {
28//!         Ok(json!({
29//!             "protocolVersion": "2025-06-18",
30//!             "capabilities": {
31//!                 "tools": {}
32//!             },
33//!             "serverInfo": {
34//!                 "name": "my-server",
35//!                 "version": "1.0.0"
36//!             }
37//!         }))
38//!     }
39//!
40//!     async fn list_tools(&self, context: &McpContext) -> Result<Vec<ToolDefinition>> {
41//!         Ok(vec![
42//!             ToolDefinition {
43//!                 name: "hello".to_string(),
44//!                 description: "Say hello".to_string(),
45//!                 input_schema: json!({
46//!                     "type": "object",
47//!                     "properties": {
48//!                         "name": { "type": "string" }
49//!                     },
50//!                     "required": ["name"]
51//!                 }),
52//!             }
53//!         ])
54//!     }
55//!
56//!     async fn call_tool(&self, name: &str, arguments: Value, context: &McpContext) -> Result<Value> {
57//!         match name {
58//!             "hello" => {
59//!                 let name = arguments.get("name")
60//!                     .and_then(|v| v.as_str())
61//!                     .unwrap_or("World");
62//!                 Ok(json!({ "message": format!("Hello, {}!", name) }))
63//!             }
64//!             _ => Err(anyhow::anyhow!("Unknown tool: {}", name))
65//!         }
66//!     }
67//! }
68//! ```
69
70use {
71    anyhow::Result, async_trait::async_trait, schemars::JsonSchema, serde_json::Value,
72    tokio::sync::mpsc,
73};
74
75/// Context provided to MCP handler methods.
76///
77/// This struct contains session-specific information and capabilities that are
78/// available to handler methods. It includes the session ID, notification sender,
79/// protocol version, and client information.
80///
81/// # Fields
82///
83/// - `session_id`: Unique identifier for this client session (used for HTTP session management)
84/// - `notification_sender`: Channel for sending notifications back to the client
85/// - `protocol_version`: The MCP protocol version negotiated during initialization
86/// - `client_info`: Client-provided information from the initialization request
87///
88/// # Example Usage
89///
90/// ```rust
91/// use solidmcp::handler::{McpContext, McpNotification, LogLevel};
92///
93/// async fn my_tool_handler(context: &McpContext) -> Result<Value> {
94///     // Send a notification
95///     if let Some(sender) = &context.notification_sender {
96///         sender.send(McpNotification::LogMessage {
97///             level: LogLevel::Info,
98///             logger: Some("my_tool".to_string()),
99///             message: "Processing started".to_string(),
100///             data: None,
101///         })?;
102///     }
103///     
104///     // Check protocol version for feature compatibility
105///     if context.protocol_version.as_deref() == Some("2025-06-18") {
106///         // Use newer protocol features
107///     }
108///     
109///     Ok(json!({ "status": "completed" }))
110/// }
111/// ```
112#[derive(Clone)]
113pub struct McpContext {
114    /// Session ID for this client connection
115    pub session_id: Option<String>,
116    /// Sender for notifications (if supported)
117    pub notification_sender: Option<mpsc::UnboundedSender<McpNotification>>,
118    /// Protocol version negotiated with client
119    pub protocol_version: Option<String>,
120    /// Client information from initialization
121    pub client_info: Option<Value>,
122}
123
124/// Notification types that can be sent from server to client.
125///
126/// MCP supports various notification types for real-time updates. Notifications
127/// are one-way messages from server to client and don't expect a response.
128///
129/// # Notification Types
130///
131/// - `ToolsListChanged`: Notify when available tools have changed
132/// - `ResourcesListChanged`: Notify when available resources have changed
133/// - `PromptsListChanged`: Notify when available prompts have changed
134/// - `Progress`: Send progress updates for long-running operations
135/// - `LogMessage`: Send log messages with different severity levels
136/// - `Custom`: Send custom notifications with arbitrary method names
137///
138/// # Examples
139///
140/// ```rust
141/// use solidmcp::handler::{McpNotification, LogLevel};
142/// use serde_json::json;
143///
144/// // Send a log message
145/// let notification = McpNotification::LogMessage {
146///     level: LogLevel::Info,
147///     logger: Some("file_processor".to_string()),
148///     message: "Processing complete".to_string(),
149///     data: Some(json!({
150///         "files_processed": 42,
151///         "duration_ms": 1234
152///     })),
153/// };
154///
155/// // Send progress update
156/// let progress = McpNotification::Progress {
157///     progress_token: "task-123".to_string(),
158///     progress: 25.0,
159///     total: Some(100.0),
160/// };
161///
162/// // Notify about resource changes
163/// let resource_update = McpNotification::ResourcesListChanged;
164/// ```
165#[derive(Debug, Clone)]
166pub enum McpNotification {
167    /// Tools have changed
168    ToolsListChanged,
169    /// Resources have changed
170    ResourcesListChanged,
171    /// Prompts have changed
172    PromptsListChanged,
173    /// Progress notification
174    Progress {
175        progress_token: String,
176        progress: f64,
177        total: Option<f64>,
178    },
179    /// Log message
180    LogMessage {
181        level: LogLevel,
182        logger: Option<String>,
183        message: String,
184        data: Option<Value>,
185    },
186    /// Custom notification
187    Custom {
188        method: String,
189        params: Option<Value>,
190    },
191}
192
193/// Log levels for log message notifications.
194///
195/// These levels follow standard logging conventions and help clients
196/// filter and display messages appropriately.
197///
198/// # Log Level Hierarchy (from least to most severe)
199///
200/// - `Debug`: Detailed information for debugging purposes
201/// - `Info`: General informational messages
202/// - `Warning`: Warning messages for potentially problematic situations
203/// - `Error`: Error messages for failures that don't stop execution
204///
205/// # Example
206///
207/// ```rust
208/// use solidmcp::handler::{LogLevel, McpNotification};
209///
210/// fn get_log_level(severity: u8) -> LogLevel {
211///     match severity {
212///         0..=3 => LogLevel::Debug,
213///         4..=6 => LogLevel::Info,
214///         7..=8 => LogLevel::Warning,
215///         _ => LogLevel::Error,
216///     }
217/// }
218/// ```
219#[derive(Debug, Clone, PartialEq)]
220pub enum LogLevel {
221    Debug,
222    Info,
223    Warning,
224    Error,
225}
226
227/// Tool definition for MCP tools/list response.
228///
229/// This struct represents a tool that can be called by MCP clients. It includes
230/// the tool's name, description, and JSON Schema for input validation. This is
231/// a type-erased version that allows storing multiple tools with different input
232/// types in the same collection.
233///
234/// # Fields
235///
236/// - `name`: Unique identifier for the tool
237/// - `description`: Human-readable description of what the tool does
238/// - `input_schema`: JSON Schema defining the expected input parameters
239///
240/// # Example
241///
242/// ```rust
243/// use solidmcp::handler::ToolDefinition;
244/// use serde_json::json;
245///
246/// let tool = ToolDefinition {
247///     name: "calculate".to_string(),
248///     description: "Perform arithmetic operations".to_string(),
249///     input_schema: json!({
250///         "type": "object",
251///         "properties": {
252///             "a": { "type": "number" },
253///             "b": { "type": "number" },
254///             "operation": {
255///                 "type": "string",
256///                 "enum": ["add", "subtract", "multiply", "divide"]
257///             }
258///         },
259///         "required": ["a", "b", "operation"]
260///     }),
261/// };
262/// ```
263#[derive(Debug, Clone)]
264pub struct ToolDefinition {
265    pub name: String,
266    pub description: String,
267    pub input_schema: Value,
268}
269
270impl ToolDefinition {
271    /// Create a new tool definition from a JsonSchema type.
272    ///
273    /// This method automatically generates a JSON Schema from a Rust type that
274    /// implements the `JsonSchema` trait. This is the preferred way to create
275    /// tool definitions when using the framework API.
276    ///
277    /// # Type Parameters
278    ///
279    /// - `T`: Type that implements `schemars::JsonSchema`
280    ///
281    /// # Parameters
282    ///
283    /// - `name`: The unique name for this tool
284    /// - `description`: Human-readable description of the tool's purpose
285    ///
286    /// # Returns
287    ///
288    /// A new `ToolDefinition` with the schema automatically generated from type `T`
289    ///
290    /// # Example
291    ///
292    /// ```rust
293    /// use solidmcp::handler::ToolDefinition;
294    /// use schemars::JsonSchema;
295    /// use serde::Deserialize;
296    ///
297    /// #[derive(JsonSchema, Deserialize)]
298    /// struct SearchInput {
299    ///     query: String,
300    ///     #[serde(default)]
301    ///     limit: u32,
302    /// }
303    ///
304    /// let tool = ToolDefinition::from_schema::<SearchInput>(
305    ///     "search",
306    ///     "Search for items matching a query"
307    /// );
308    /// ```
309    pub fn from_schema<T: JsonSchema>(
310        name: impl Into<String>,
311        description: impl Into<String>,
312    ) -> Self {
313        let schema = schemars::schema_for!(T);
314        let input_schema = serde_json::to_value(schema).unwrap_or_else(|_| {
315            serde_json::json!({
316                "type": "object",
317                "properties": {},
318                "additionalProperties": false
319            })
320        });
321
322        Self {
323            name: name.into(),
324            description: description.into(),
325            input_schema,
326        }
327    }
328
329    /// Convert to the JSON format expected by MCP protocol.
330    ///
331    /// This method serializes the tool definition into the format required
332    /// by the MCP protocol for the tools/list response.
333    ///
334    /// # Returns
335    ///
336    /// A JSON value containing the tool's name, description, and input schema
337    ///
338    /// # Example
339    ///
340    /// ```rust
341    /// let json = tool.to_json();
342    /// assert_eq!(json["name"], "calculate");
343    /// assert_eq!(json["description"], "Perform arithmetic operations");
344    /// assert!(json["input_schema"].is_object());
345    /// ```
346    pub fn to_json(&self) -> Value {
347        serde_json::json!({
348            "name": self.name,
349            "description": self.description,
350            "input_schema": self.input_schema
351        })
352    }
353}
354
355/// Typed tool definition helper that provides compile-time type safety.
356///
357/// This struct provides a type-safe wrapper around tool definitions, ensuring
358/// that the JSON schema is correctly generated from the input type. Use this
359/// when you want to maintain type information before converting to the
360/// type-erased `ToolDefinition`.
361///
362/// # Type Parameters
363///
364/// - `T`: The input type that implements `JsonSchema`
365///
366/// # Example
367///
368/// ```rust
369/// use solidmcp::handler::TypedToolDefinition;
370/// use schemars::JsonSchema;
371/// use serde::Deserialize;
372///
373/// #[derive(JsonSchema, Deserialize)]
374/// struct CalculateInput {
375///     a: f64,
376///     b: f64,
377///     operation: String,
378/// }
379///
380/// let typed_tool = TypedToolDefinition::<CalculateInput>::new(
381///     "calculate",
382///     "Perform arithmetic operations"
383/// );
384///
385/// // Convert to ToolDefinition for storage
386/// let tool = typed_tool.to_tool_definition();
387/// ```
388#[derive(Debug, Clone)]
389pub struct TypedToolDefinition<T: JsonSchema> {
390    pub name: String,
391    pub description: String,
392    pub input_schema: std::marker::PhantomData<T>,
393}
394
395impl<T: JsonSchema> TypedToolDefinition<T> {
396    /// Create a new typed tool definition.
397    ///
398    /// # Parameters
399    ///
400    /// - `name`: The unique name for this tool
401    /// - `description`: Human-readable description of the tool's purpose
402    ///
403    /// # Returns
404    ///
405    /// A new `TypedToolDefinition` maintaining type information for input type `T`
406    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
407        Self {
408            name: name.into(),
409            description: description.into(),
410            input_schema: std::marker::PhantomData,
411        }
412    }
413
414    /// Convert to a type-erased ToolDefinition for collections.
415    ///
416    /// This method creates a `ToolDefinition` with the JSON schema generated
417    /// from the type parameter `T`. Use this when you need to store multiple
418    /// tools with different input types in the same collection.
419    ///
420    /// # Returns
421    ///
422    /// A `ToolDefinition` with the schema generated from type `T`
423    pub fn to_tool_definition(&self) -> ToolDefinition {
424        ToolDefinition::from_schema::<T>(self.name.clone(), self.description.clone())
425    }
426
427    /// Get the JSON schema for this tool's input.
428    ///
429    /// This method generates and returns the JSON Schema for the input type `T`.
430    /// Useful for debugging or when you need direct access to the schema.
431    ///
432    /// # Returns
433    ///
434    /// A JSON value containing the generated schema for type `T`
435    pub fn get_input_schema(&self) -> Value {
436        let schema = schemars::schema_for!(T);
437        serde_json::to_value(schema).unwrap_or_else(|_| {
438            serde_json::json!({
439                "type": "object",
440                "properties": {},
441                "additionalProperties": false
442            })
443        })
444    }
445}
446
447/// Resource information for MCP resources/list response.
448///
449/// This struct represents metadata about a resource that can be accessed
450/// through the MCP protocol. Resources are identified by URIs and can
451/// represent files, database entries, API responses, or any other data.
452///
453/// # Fields
454///
455/// - `uri`: Unique identifier for the resource (e.g., "file:///path/to/file")
456/// - `name`: Human-readable name for the resource
457/// - `description`: Optional description of the resource's contents
458/// - `mime_type`: Optional MIME type hint (e.g., "text/plain", "application/json")
459///
460/// # Example
461///
462/// ```rust
463/// use solidmcp::handler::ResourceInfo;
464///
465/// let resource = ResourceInfo {
466///     uri: "file:///data/users.json".to_string(),
467///     name: "User Database".to_string(),
468///     description: Some("JSON file containing user records".to_string()),
469///     mime_type: Some("application/json".to_string()),
470/// };
471/// ```
472#[derive(Debug, Clone)]
473pub struct ResourceInfo {
474    pub uri: String,
475    pub name: String,
476    pub description: Option<String>,
477    pub mime_type: Option<String>,
478}
479
480/// Resource content for MCP resources/read response.
481///
482/// This struct contains the actual content of a resource when read through
483/// the MCP protocol. It includes the resource URI, optional MIME type,
484/// and the content as a string.
485///
486/// # Fields
487///
488/// - `uri`: The URI of the resource being returned
489/// - `mime_type`: Optional MIME type of the content
490/// - `content`: The actual content of the resource as a string
491///
492/// # Example
493///
494/// ```rust
495/// use solidmcp::handler::ResourceContent;
496///
497/// let content = ResourceContent {
498///     uri: "db://users/123".to_string(),
499///     mime_type: Some("application/json".to_string()),
500///     content: r#"{"id": 123, "name": "Alice", "email": "alice@example.com"}"#.to_string(),
501/// };
502/// ```
503#[derive(Debug, Clone)]
504pub struct ResourceContent {
505    pub uri: String,
506    pub mime_type: Option<String>,
507    pub content: String,
508}
509
510/// Prompt information for MCP prompts/list response.
511///
512/// This struct represents a prompt template that can be used by MCP clients.
513/// Prompts are parameterized templates that generate conversation messages
514/// for AI models based on provided arguments.
515///
516/// # Fields
517///
518/// - `name`: Unique identifier for the prompt
519/// - `description`: Optional human-readable description
520/// - `arguments`: List of arguments that can be passed to the prompt
521///
522/// # Example
523///
524/// ```rust
525/// use solidmcp::handler::{PromptInfo, PromptArgument};
526///
527/// let prompt = PromptInfo {
528///     name: "code_review".to_string(),
529///     description: Some("Generate a code review for the provided code".to_string()),
530///     arguments: vec![
531///         PromptArgument {
532///             name: "code".to_string(),
533///             description: Some("The code to review".to_string()),
534///             required: true,
535///         },
536///         PromptArgument {
537///             name: "focus_areas".to_string(),
538///             description: Some("Specific areas to focus on".to_string()),
539///             required: false,
540///         },
541///     ],
542/// };
543/// ```
544#[derive(Debug, Clone)]
545pub struct PromptInfo {
546    pub name: String,
547    pub description: Option<String>,
548    pub arguments: Vec<PromptArgument>,
549}
550
551/// Prompt argument definition.
552///
553/// Represents a single argument that can be passed to a prompt template.
554/// Arguments can be required or optional and include descriptions to help
555/// users understand their purpose.
556///
557/// # Fields
558///
559/// - `name`: The argument name (used as key when passing values)
560/// - `description`: Optional description of what this argument is for
561/// - `required`: Whether this argument must be provided
562///
563/// # Example
564///
565/// ```rust
566/// use solidmcp::handler::PromptArgument;
567///
568/// let arg = PromptArgument {
569///     name: "language".to_string(),
570///     description: Some("The programming language of the code".to_string()),
571///     required: false,
572/// };
573/// ```
574#[derive(Debug, Clone)]
575pub struct PromptArgument {
576    pub name: String,
577    pub description: Option<String>,
578    pub required: bool,
579}
580
581/// Prompt content for MCP prompts/get response.
582///
583/// Contains the generated messages that make up a prompt. These messages
584/// are typically sent to an AI model as a conversation history.
585///
586/// # Fields
587///
588/// - `messages`: List of messages forming the prompt conversation
589///
590/// # Example
591///
592/// ```rust
593/// use solidmcp::handler::{PromptContent, PromptMessage};
594///
595/// let content = PromptContent {
596///     messages: vec![
597///         PromptMessage {
598///             role: "system".to_string(),
599///             content: "You are a helpful code reviewer.".to_string(),
600///         },
601///         PromptMessage {
602///             role: "user".to_string(),
603///             content: "Please review this Python function for bugs.".to_string(),
604///         },
605///     ],
606/// };
607/// ```
608#[derive(Debug, Clone)]
609pub struct PromptContent {
610    pub messages: Vec<PromptMessage>,
611}
612
613/// Prompt message.
614///
615/// Represents a single message in a prompt conversation. Each message has
616/// a role (typically "system", "user", or "assistant") and content.
617///
618/// # Fields
619///
620/// - `role`: The role of the message sender (e.g., "system", "user", "assistant")
621/// - `content`: The actual message content
622///
623/// # Example
624///
625/// ```rust
626/// use solidmcp::handler::PromptMessage;
627///
628/// let system_message = PromptMessage {
629///     role: "system".to_string(),
630///     content: "You are an expert Rust programmer.".to_string(),
631/// };
632///
633/// let user_message = PromptMessage {
634///     role: "user".to_string(),
635///     content: "How do I implement error handling in Rust?".to_string(),
636/// };
637/// ```
638#[derive(Debug, Clone)]
639pub struct PromptMessage {
640    pub role: String,
641    pub content: String,
642}
643
644/// Core trait that users must implement to provide MCP functionality.
645///
646/// This is the main integration point for the solidmcp library. Implement this
647/// trait to define your server's behavior. All methods have default implementations
648/// except for `list_tools` and `call_tool`, which must be implemented.
649///
650/// # Required Methods
651///
652/// - `list_tools`: Return the list of available tools
653/// - `call_tool`: Execute a tool with given arguments
654///
655/// # Optional Methods
656///
657/// All other methods have default implementations that can be overridden:
658/// - `initialize`: Customize server capabilities and info
659/// - `list_resources`: Provide available resources
660/// - `read_resource`: Implement resource reading
661/// - `list_prompts`: Provide available prompts
662/// - `get_prompt`: Generate prompt content
663/// - `cancel_notification`: Handle cancellation requests
664/// - `handle_initialized`: React to client initialization
665///
666/// # Example Implementation
667///
668/// ```rust
669/// use solidmcp::handler::{McpHandler, McpContext, ToolDefinition};
670/// use anyhow::Result;
671/// use async_trait::async_trait;
672/// use serde_json::{json, Value};
673///
674/// struct MyHandler {
675///     tools: Vec<ToolDefinition>,
676/// }
677///
678/// #[async_trait]
679/// impl McpHandler for MyHandler {
680///     async fn list_tools(&self, _context: &McpContext) -> Result<Vec<ToolDefinition>> {
681///         Ok(self.tools.clone())
682///     }
683///
684///     async fn call_tool(&self, name: &str, arguments: Value, context: &McpContext) -> Result<Value> {
685///         match name {
686///             "echo" => {
687///                 let message = arguments.get("message")
688///                     .and_then(|v| v.as_str())
689///                     .unwrap_or("Hello");
690///                 Ok(json!({ "echoed": message }))
691///             }
692///             _ => Err(anyhow::anyhow!("Unknown tool: {}", name))
693///         }
694///     }
695/// }
696/// ```
697#[async_trait]
698pub trait McpHandler: Send + Sync {
699    /// Initialize the handler with client information.
700    ///
701    /// Called when a client sends an initialize request. This is the first
702    /// method called in the MCP protocol handshake. Override this to customize
703    /// server capabilities and information.
704    ///
705    /// # Parameters
706    ///
707    /// - `params`: The initialization parameters from the client
708    /// - `context`: The MCP context for this session
709    ///
710    /// # Returns
711    ///
712    /// A JSON value containing:
713    /// - `protocolVersion`: The MCP protocol version (e.g., "2025-06-18")
714    /// - `capabilities`: Object describing server capabilities
715    /// - `serverInfo`: Object with server name and version
716    ///
717    /// # Default Implementation
718    ///
719    /// Returns basic server info with no special capabilities
720    async fn initialize(&self, _params: Value, _context: &McpContext) -> Result<Value> {
721        // Default implementation returns basic capabilities
722        Ok(serde_json::json!({
723            "protocolVersion": "2025-06-18",
724            "capabilities": {},
725            "serverInfo": {
726                "name": "solidmcp-server",
727                "version": "0.1.0"
728            }
729        }))
730    }
731
732    /// List available tools.
733    ///
734    /// Called when a client sends a tools/list request. This method must be
735    /// implemented to return all tools that your server provides.
736    ///
737    /// # Parameters
738    ///
739    /// - `context`: The MCP context for this session
740    ///
741    /// # Returns
742    ///
743    /// A vector of `ToolDefinition` structs describing available tools
744    async fn list_tools(&self, context: &McpContext) -> Result<Vec<ToolDefinition>>;
745
746    /// Execute a tool.
747    ///
748    /// Called when a client sends a tools/call request. This method must be
749    /// implemented to handle tool execution with the provided arguments.
750    ///
751    /// # Parameters
752    ///
753    /// - `name`: The name of the tool to execute
754    /// - `arguments`: JSON value containing the tool's input parameters
755    /// - `context`: The MCP context for this session
756    ///
757    /// # Returns
758    ///
759    /// A JSON value containing the tool's output
760    ///
761    /// # Errors
762    ///
763    /// Return an error if:
764    /// - The tool name is not recognized
765    /// - The arguments are invalid
766    /// - The tool execution fails
767    async fn call_tool(&self, name: &str, arguments: Value, context: &McpContext) -> Result<Value>;
768
769    /// List available resources.
770    ///
771    /// Called when a client sends a resources/list request. Override this
772    /// to provide a list of resources that clients can read.
773    ///
774    /// # Parameters
775    ///
776    /// - `context`: The MCP context for this session
777    ///
778    /// # Returns
779    ///
780    /// A vector of `ResourceInfo` structs describing available resources
781    ///
782    /// # Default Implementation
783    ///
784    /// Returns an empty vector (no resources)
785    async fn list_resources(&self, _context: &McpContext) -> Result<Vec<ResourceInfo>> {
786        // Default implementation - no resources
787        Ok(vec![])
788    }
789
790    /// Read a resource.
791    ///
792    /// Called when a client sends a resources/read request. Override this
793    /// to implement resource reading logic.
794    ///
795    /// # Parameters
796    ///
797    /// - `uri`: The URI of the resource to read
798    /// - `context`: The MCP context for this session
799    ///
800    /// # Returns
801    ///
802    /// A `ResourceContent` struct containing the resource data
803    ///
804    /// # Default Implementation
805    ///
806    /// Returns an error indicating the resource was not found
807    async fn read_resource(&self, uri: &str, _context: &McpContext) -> Result<ResourceContent> {
808        Err(anyhow::anyhow!("Resource not found: {}", uri))
809    }
810
811    /// List available prompts.
812    ///
813    /// Called when a client sends a prompts/list request. Override this
814    /// to provide a list of prompt templates that clients can use.
815    ///
816    /// # Parameters
817    ///
818    /// - `context`: The MCP context for this session
819    ///
820    /// # Returns
821    ///
822    /// A vector of `PromptInfo` structs describing available prompts
823    ///
824    /// # Default Implementation
825    ///
826    /// Returns an empty vector (no prompts)
827    async fn list_prompts(&self, _context: &McpContext) -> Result<Vec<PromptInfo>> {
828        // Default implementation - no prompts
829        Ok(vec![])
830    }
831
832    /// Get a prompt.
833    ///
834    /// Called when a client sends a prompts/get request. Override this
835    /// to generate prompt content based on the template and arguments.
836    ///
837    /// # Parameters
838    ///
839    /// - `name`: The name of the prompt template
840    /// - `arguments`: Optional JSON arguments for the prompt
841    /// - `context`: The MCP context for this session
842    ///
843    /// # Returns
844    ///
845    /// A `PromptContent` struct containing the generated messages
846    ///
847    /// # Default Implementation
848    ///
849    /// Returns an error indicating the prompt was not found
850    async fn get_prompt(
851        &self,
852        name: &str,
853        _arguments: Option<Value>,
854        _context: &McpContext,
855    ) -> Result<PromptContent> {
856        Err(anyhow::anyhow!("Prompt not found: {}", name))
857    }
858
859    /// Handle notification cancellation.
860    ///
861    /// Called when a client sends a notifications/cancel request. Override this
862    /// to handle cancellation of long-running operations.
863    ///
864    /// # Parameters
865    ///
866    /// - `params`: Cancellation parameters (typically contains a progress token)
867    /// - `context`: The MCP context for this session
868    ///
869    /// # Returns
870    ///
871    /// An empty JSON object on success
872    ///
873    /// # Default Implementation
874    ///
875    /// Acknowledges the cancellation without taking action
876    async fn cancel_notification(&self, _params: Value, _context: &McpContext) -> Result<Value> {
877        // Default implementation - acknowledge cancellation
878        Ok(serde_json::json!({}))
879    }
880
881    /// Handle initialized notification.
882    ///
883    /// Called when a client sends a notifications/initialized notification,
884    /// indicating that the client has completed its initialization. Override
885    /// this to perform any post-initialization setup.
886    ///
887    /// # Parameters
888    ///
889    /// - `context`: The MCP context for this session
890    ///
891    /// # Returns
892    ///
893    /// Ok(()) on success
894    ///
895    /// # Default Implementation
896    ///
897    /// Does nothing and returns Ok(())
898    async fn handle_initialized(&self, _context: &McpContext) -> Result<()> {
899        // Default implementation - do nothing
900        Ok(())
901    }
902}