-
Notifications
You must be signed in to change notification settings - Fork 190
Support for #[prompt]-like macro similar to #[tool] #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Good idea! This would be on the todo list after the latest revision works! |
@4t145 |
@blue-orange-yellow We don't have one yet. But in the old rmcp repository, we do have a todo list for every release version. |
I would love to help tackle this issue. Let's discuss how it looks like. My naive idea is to make it the same as tool one. We'll have these macros #[prompt(prompt_box)]
#[prompt(name="example_prompt", description="Example Prompt")]
#[prompt(param)] The usage will looks like this Sample Usage
pub struct Counter {
counter: Arc<Mutex<i32>>
}
#[tool(tool_box)]
#[prompt(prompt_box)]
impl Counter {
pub fn new() -> Self {
Self {
counter: Arc::new(Mutex::new(0)),
}
}
#[tool(description = "Increment the counter by 1")]
async fn increment(&self) -> Result<CallToolResult, McpError> {
let mut counter = self.counter.lock().await;
*counter += 1;
Ok(CallToolResult::success(vec![Content::text(
counter.to_string(),
)]))
}
#[prompt(name="example_prompt", description="Example description")]
fn example_prompt(
&self,
#[prompt(param)]
#[schemars(description = "Param description")]
message: String,
) -> Result<GetPromptResult, McpError> {
let prompt = format!("Example message: '{message}'");
Ok(GetPromptResult {
description: None,
messages: vec![PromptMessage {
role: PromptMessageRole::User,
content: PromptMessageContent::text(prompt),
}],
})
}
} Given that code, it will yield expanded rust code as follows: Sample Expanded Code
pub struct Counter {
counter: Arc<Mutex<i32>>
}
impl Counter {
pub fn new() -> Self {
Self {
counter: Arc::new(Mutex::new(0)),
}
}
fn example_prompt_attr() -> rmcp::model::Prompt {
rmcp::model::Prompt::new(
name: "example_prompt".into(),
description: "Example prompt".into(),
)
}
fn example_prompt_get_prompt(
context: rmcp::handler::server::prompt::GetPromptContext
) -> Result<GetPromptResult, McpError> {
use rmcp::handler::server::prompt::*;
let (__rmcp_prompt_receiver, context) = <&Self>::from_prompt_get_prompt_context_part(
context,
)?;
Self::example_prompt(__rmcp_prompt_receiver).await.into_get_prompt_prompt_result()
}
fn example_prompt(&self, message: String) -> Result<GetPromptResult, McpError> {
Ok(GetPromptResult {
description: None,
messages: vec![PromptMessage {
role: PromptMessageRole::User,
content: PromptMessageContent::text(prompt),
}],
})
}
fn prompt_box() -> &'static ::rmcp::handler::server::prompt::PromptBox<Counter> {
use ::rmcp::handler::server::prompt::{PromptBox, PromptBoxItem};
static PROMPT_BOX: std::sync::OnceLock<PromptBox<Counter>> = std::sync::OnceLock::new();
PROMPT_BOX
.get_or_init(|| {
let mut prompt_box = PromptBox::new();
prompt_box
.add(
PromptBoxItem::new(
Counter::example_prompt_attr(),
|context| Box::pin(Counter::example_prompt_get_prompt(context)),
),
);
prompt_box
})
}
}
impl ServerHandler for Counter {
async fn list_prompts(
&self,
_request: PaginatedRequestParam,
_: RequestContext<RoleServer>,
) -> Result<ListPromptsResult, McpError> {
Ok(::rmcp::model::ListPromptsResult {
next_cursor: None,
prompt: Self::prompt_box().list(),
})
}
async fn get_prompt(
&self,
get_prompt_request_param: ::rmcp::model::GetPromptRequestParam,
context: ::rmcp::service::RequestContext<::rmcp::service::RoleServer>,
) -> Result<GetPromptResult, McpError> {
let context = ::rmcp::handler::server::tool::GetPromptContext::new(
self,
get_prompt_request_param,
context,
);
Self::prompt_box().get_prompt(context).await
}
} What do you think @4t145 @blue-orange-yellow ? We can iterate from it or if there is better approach, would love to hear as well. |
@jefrydco By the way, before using this SDK, I was using the following crate: It had a similar approach to what you suggested, and I found it quite easy to use. |
@jefrydco @blue-orange-yellow And here is a possible composable handler solution, you can review it and give me your suggestion. |
@4t145 Personally, I find this interface to be user-friendly and highly general-purpose. |
@jefrydco I think your outlined approach looks great - follows the Could we follow a similar approach and just have a single top level #[tool(tool_box)]
#[prompt(prompt_box)] |
No description provided.
The text was updated successfully, but these errors were encountered: