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