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}