rust-sdk/crates/rmcp/tests/test_prompt_handler.rs
Michael Assaf 9349f5cb26
feat: Add prompt support (#351)
* feat: add prompt support with typed argument handling

- Implement #[prompt], #[prompt_router], and #[prompt_handler] macros
- Add automatic JSON schema generation from Rust types for arguments
- Support flexible async handler signatures with automatic adaptation
- Create PromptRouter for efficient prompt dispatch
- Include comprehensive tests and example implementation

This enables MCP servers to provide reusable prompt templates that
LLMs can discover and invoke with strongly-typed parameters, similar
to the existing tool system but optimized for prompt use cases.

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: unify parameter handling between tools and prompts

- Replace Arguments<T> with Parameters<T> for consistent API
- Create shared common module for tool/prompt utilities
- Modernize async handling with futures::future::BoxFuture
- Move cached_schema_for_type to common module for reuse
- Update error types from rmcp::Error to rmcp::ErrorData
- Add comprehensive trait implementations for parameter extraction

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: extract Parameters wrapper to shared module and unify trait usage

- Move Parameters type from common.rs to dedicated wrapper/parameters.rs module
- Unify extractor traits by using FromContextPart for both tools and prompts
- Remove duplicate FromToolCallContextPart and FromPromptContextPart traits
- Add structured output support to Json wrapper with IntoCallToolResult impl
- Improve error messages with more descriptive panic for schema serialization
- Update all imports across codebase to use new module path
- Clean up trailing whitespace and formatting inconsistencies

This consolidates parameter extraction logic and reduces code duplication
between tool and prompt handlers while maintaining backward compatibility.

* chore: remove committed .egg-info directory and update gitignore

* docs: fix documentation formatting
2025-08-21 11:22:19 +08:00

161 lines
4.1 KiB
Rust

//cargo test --test test_prompt_handler --features "client server"
// Tests for verifying that the #[prompt_handler] macro correctly generates
// the ServerHandler trait implementation methods.
#![allow(dead_code)]
use rmcp::{
RoleServer, ServerHandler,
handler::server::router::prompt::PromptRouter,
model::{GetPromptRequestParam, GetPromptResult, ListPromptsResult, PaginatedRequestParam},
prompt_handler,
service::RequestContext,
};
#[derive(Debug, Clone)]
pub struct TestPromptServer {
prompt_router: PromptRouter<Self>,
}
impl Default for TestPromptServer {
fn default() -> Self {
Self::new()
}
}
impl TestPromptServer {
pub fn new() -> Self {
Self {
prompt_router: PromptRouter::new(),
}
}
}
#[prompt_handler]
impl ServerHandler for TestPromptServer {}
#[derive(Debug, Clone)]
pub struct CustomRouterServer {
custom_router: PromptRouter<Self>,
}
impl Default for CustomRouterServer {
fn default() -> Self {
Self::new()
}
}
impl CustomRouterServer {
pub fn new() -> Self {
Self {
custom_router: PromptRouter::new(),
}
}
pub fn get_custom_router(&self) -> &PromptRouter<Self> {
&self.custom_router
}
}
#[prompt_handler(router = self.custom_router)]
impl ServerHandler for CustomRouterServer {}
#[derive(Debug, Clone)]
pub struct GenericPromptServer<T: Send + Sync + 'static> {
prompt_router: PromptRouter<Self>,
_marker: std::marker::PhantomData<T>,
}
impl<T: Send + Sync + 'static> Default for GenericPromptServer<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Send + Sync + 'static> GenericPromptServer<T> {
pub fn new() -> Self {
Self {
prompt_router: PromptRouter::new(),
_marker: std::marker::PhantomData,
}
}
}
#[prompt_handler]
impl<T: Send + Sync + 'static> ServerHandler for GenericPromptServer<T> {}
#[test]
fn test_prompt_handler_basic() {
let server = TestPromptServer::new();
// Test that the server implements ServerHandler
fn assert_server_handler<T: ServerHandler>(_: &T) {}
assert_server_handler(&server);
// Test that the prompt router is accessible
assert_eq!(server.prompt_router.list_all().len(), 0);
}
#[test]
fn test_prompt_handler_custom_router() {
let server = CustomRouterServer::new();
// Test that the server implements ServerHandler
fn assert_server_handler<T: ServerHandler>(_: &T) {}
assert_server_handler(&server);
// Test that the custom router is used
assert_eq!(server.custom_router.list_all().len(), 0);
}
#[test]
fn test_prompt_handler_with_generics() {
let server = GenericPromptServer::<String>::new();
// Test that generic server implements ServerHandler
fn assert_server_handler<T: ServerHandler>(_: &T) {}
assert_server_handler(&server);
// Test with a different generic type
let server2 = GenericPromptServer::<i32>::new();
assert_server_handler(&server2);
}
#[test]
fn test_prompt_handler_trait_implementation() {
// This test verifies that the prompt_handler macro generates proper ServerHandler implementation
// The actual method signatures are tested through the ServerHandler trait bound
fn compile_time_check<T: ServerHandler>() {}
compile_time_check::<TestPromptServer>();
compile_time_check::<CustomRouterServer>();
compile_time_check::<GenericPromptServer<String>>();
}
// Test that the macro works with different server configurations
mod nested {
use super::*;
#[derive(Debug, Clone)]
pub struct NestedServer {
prompt_router: PromptRouter<Self>,
}
impl NestedServer {
pub fn new() -> Self {
Self {
prompt_router: PromptRouter::new(),
}
}
}
#[prompt_handler]
impl ServerHandler for NestedServer {}
#[test]
fn test_nested_prompt_handler() {
let server = NestedServer::new();
// Verify it implements ServerHandler
fn assert_server_handler<T: ServerHandler>(_: &T) {}
assert_server_handler(&server);
}
}