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
This commit is contained in:
parent
c0452f93a4
commit
9349f5cb26
2 changed files with 3191 additions and 464 deletions
|
|
@ -1,6 +1,12 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
handler::server::tool::IntoCallToolResult,
|
||||
model::{CallToolResult, IntoContents},
|
||||
};
|
||||
|
||||
/// Json wrapper for structured output
|
||||
///
|
||||
|
|
@ -20,3 +26,29 @@ impl<T: JsonSchema> JsonSchema for Json<T> {
|
|||
T::json_schema(generator)
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for Json<T> to create structured content
|
||||
impl<T: Serialize + JsonSchema + 'static> IntoCallToolResult for Json<T> {
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData> {
|
||||
let value = serde_json::to_value(self.0).map_err(|e| {
|
||||
crate::ErrorData::internal_error(
|
||||
format!("Failed to serialize structured content: {}", e),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(CallToolResult::structured(value))
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for Result<Json<T>, E>
|
||||
impl<T: Serialize + JsonSchema + 'static, E: IntoContents> IntoCallToolResult
|
||||
for Result<Json<T>, E>
|
||||
{
|
||||
fn into_call_tool_result(self) -> Result<CallToolResult, crate::ErrorData> {
|
||||
match self {
|
||||
Ok(value) => value.into_call_tool_result(),
|
||||
Err(error) => Ok(CallToolResult::error(error.into_contents())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
55
crates/rmcp/src/handler/server/wrapper/parameters.rs
Normal file
55
crates/rmcp/src/handler/server/wrapper/parameters.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use schemars::JsonSchema;
|
||||
|
||||
/// Parameter extractor for tools and prompts
|
||||
///
|
||||
/// When used in tool and prompt handlers, this wrapper extracts and deserializes
|
||||
/// parameters from the incoming request. The framework will automatically parse
|
||||
/// the JSON arguments from tool calls or prompt arguments and deserialize them
|
||||
/// into the specified type `P`.
|
||||
///
|
||||
/// The `#[serde(transparent)]` attribute ensures that the wrapper doesn't add
|
||||
/// an extra layer in the JSON structure - it directly delegates serialization
|
||||
/// and deserialization to the inner type `P`.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// Use `Parameters<T>` as a parameter in your tool or prompt handler functions:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rmcp::handler::server::wrapper::Parameters;
|
||||
/// # use schemars::JsonSchema;
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// #[derive(Deserialize, JsonSchema)]
|
||||
/// struct CalculationRequest {
|
||||
/// operation: String,
|
||||
/// a: f64,
|
||||
/// b: f64,
|
||||
/// }
|
||||
///
|
||||
/// // In a tool handler
|
||||
/// async fn calculate(params: Parameters<CalculationRequest>) -> Result<String, String> {
|
||||
/// let request = params.0; // Extract the inner value
|
||||
/// match request.operation.as_str() {
|
||||
/// "add" => Ok((request.a + request.b).to_string()),
|
||||
/// _ => Err("Unknown operation".to_string()),
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The framework handles the extraction automatically:
|
||||
/// - For tools: Parses the `arguments` field from tool call requests
|
||||
/// - For prompts: Parses the `arguments` field from prompt requests
|
||||
/// - Returns appropriate error responses if deserialization fails
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Parameters<P>(pub P);
|
||||
|
||||
impl<P: JsonSchema> JsonSchema for Parameters<P> {
|
||||
fn schema_name() -> std::borrow::Cow<'static, str> {
|
||||
P::schema_name()
|
||||
}
|
||||
|
||||
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
|
||||
P::json_schema(generator)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue