* 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
161 lines
4.1 KiB
Rust
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);
|
|
}
|
|
}
|