turbomcp_client/client/operations/
prompts.rs

1//! Prompt operations for MCP client
2//!
3//! This module provides prompt-related functionality including listing prompts,
4//! retrieving prompt templates, and supporting parameter substitution.
5
6use std::sync::atomic::Ordering;
7
8use turbomcp_protocol::types::{
9    GetPromptRequest, GetPromptResult, ListPromptsResult, Prompt, PromptInput,
10};
11use turbomcp_protocol::{Error, Result};
12
13impl<T: turbomcp_transport::Transport + 'static> super::super::core::Client<T> {
14    /// List available prompt templates from the server
15    ///
16    /// Retrieves the complete list of prompt templates that the server provides,
17    /// including all metadata: title, description, and argument schemas. This is
18    /// the MCP-compliant implementation that provides everything needed for UI generation
19    /// and dynamic form creation.
20    ///
21    /// # Returns
22    ///
23    /// Returns a vector of `Prompt` objects containing:
24    /// - `name`: Programmatic identifier
25    /// - `title`: Human-readable display name (optional)
26    /// - `description`: Description of what the prompt does (optional)
27    /// - `arguments`: Array of argument schemas with validation info (optional)
28    ///
29    /// # Errors
30    ///
31    /// Returns an error if:
32    /// - The client is not initialized
33    /// - The server doesn't support prompts
34    /// - The request fails
35    ///
36    /// # Examples
37    ///
38    /// ```rust,no_run
39    /// # use turbomcp_client::Client;
40    /// # use turbomcp_transport::stdio::StdioTransport;
41    /// # async fn example() -> turbomcp_protocol::Result<()> {
42    /// let mut client = Client::new(StdioTransport::new());
43    /// client.initialize().await?;
44    ///
45    /// let prompts = client.list_prompts().await?;
46    /// for prompt in prompts {
47    ///     println!("Prompt: {} ({})", prompt.name, prompt.title.unwrap_or("No title".to_string()));
48    ///     if let Some(args) = prompt.arguments {
49    ///         println!("  Arguments: {:?}", args);
50    ///         for arg in args {
51    ///             let required = arg.required.unwrap_or(false);
52    ///             println!("    - {}: {} (required: {})", arg.name,
53    ///                     arg.description.unwrap_or("No description".to_string()), required);
54    ///         }
55    ///     }
56    /// }
57    /// # Ok(())
58    /// # }
59    /// ```
60    pub async fn list_prompts(&self) -> Result<Vec<Prompt>> {
61        if !self.inner.initialized.load(Ordering::Relaxed) {
62            return Err(Error::bad_request("Client not initialized"));
63        }
64
65        // Execute with plugin middleware - return full Prompt objects per MCP spec
66        let response: ListPromptsResult = self.execute_with_plugins("prompts/list", None).await?;
67        Ok(response.prompts)
68    }
69
70    /// Get a specific prompt template with argument support
71    ///
72    /// Retrieves a specific prompt template from the server with support for
73    /// parameter substitution. When arguments are provided, the server will
74    /// substitute them into the prompt template using {parameter} syntax.
75    ///
76    /// This is the MCP-compliant implementation that supports the full protocol specification.
77    ///
78    /// # Arguments
79    ///
80    /// * `name` - The name of the prompt to retrieve
81    /// * `arguments` - Optional parameters for template substitution
82    ///
83    /// # Returns
84    ///
85    /// Returns `GetPromptResult` containing the prompt template with parameters substituted.
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if:
90    /// - The client is not initialized
91    /// - The prompt name is empty
92    /// - The prompt doesn't exist
93    /// - Required arguments are missing
94    /// - Argument types don't match schema
95    /// - The request fails
96    ///
97    /// # Examples
98    ///
99    /// ```rust,no_run
100    /// # use turbomcp_client::Client;
101    /// # use turbomcp_transport::stdio::StdioTransport;
102    /// # use turbomcp_protocol::types::PromptInput;
103    /// # use std::collections::HashMap;
104    /// # async fn example() -> turbomcp_protocol::Result<()> {
105    /// let mut client = Client::new(StdioTransport::new());
106    /// client.initialize().await?;
107    ///
108    /// // Get prompt without arguments (template form)
109    /// let template = client.get_prompt("greeting", None).await?;
110    /// println!("Template has {} messages", template.messages.len());
111    ///
112    /// // Get prompt with arguments (substituted form)
113    /// let mut args = HashMap::new();
114    /// args.insert("name".to_string(), serde_json::Value::String("Alice".to_string()));
115    /// args.insert("greeting".to_string(), serde_json::Value::String("Hello".to_string()));
116    ///
117    /// let result = client.get_prompt("greeting", Some(args)).await?;
118    /// println!("Generated prompt with {} messages", result.messages.len());
119    /// # Ok(())
120    /// # }
121    /// ```
122    pub async fn get_prompt(
123        &self,
124        name: &str,
125        arguments: Option<PromptInput>,
126    ) -> Result<GetPromptResult> {
127        if !self.inner.initialized.load(Ordering::Relaxed) {
128            return Err(Error::bad_request("Client not initialized"));
129        }
130
131        if name.is_empty() {
132            return Err(Error::bad_request("Prompt name cannot be empty"));
133        }
134
135        // Send prompts/get request with full argument support
136        let request = GetPromptRequest {
137            name: name.to_string(),
138            arguments, // Support for parameter substitution
139            _meta: None,
140        };
141
142        self.execute_with_plugins(
143            "prompts/get",
144            Some(serde_json::to_value(request).map_err(|e| {
145                Error::protocol(format!("Failed to serialize prompt request: {}", e))
146            })?),
147        )
148        .await
149    }
150}