pforge_runtime/handler.rs
1use async_trait::async_trait;
2use schemars::JsonSchema;
3use serde::{de::DeserializeOwned, Serialize};
4
5/// Core handler abstraction - zero-cost, compatible with pmcp TypedTool.
6///
7/// The `Handler` trait provides a type-safe interface for implementing MCP tools.
8/// It leverages Rust's type system for compile-time validation and automatic
9/// JSON Schema generation.
10///
11/// # Type Parameters
12///
13/// - `Input`: The input type, must be deserializable and have a JSON schema
14/// - `Output`: The output type, must be serializable and have a JSON schema
15/// - `Error`: Error type that can be converted into `pforge_runtime::Error`
16///
17/// # Examples
18///
19/// ## Basic Handler
20///
21/// ```rust
22/// use pforge_runtime::{Handler, Result};
23/// use serde::{Deserialize, Serialize};
24/// use schemars::JsonSchema;
25///
26/// #[derive(Debug, Deserialize, JsonSchema)]
27/// struct GreetInput {
28/// name: String,
29/// }
30///
31/// #[derive(Debug, Serialize, JsonSchema)]
32/// struct GreetOutput {
33/// message: String,
34/// }
35///
36/// struct GreetHandler;
37///
38/// #[async_trait::async_trait]
39/// impl Handler for GreetHandler {
40/// type Input = GreetInput;
41/// type Output = GreetOutput;
42/// type Error = pforge_runtime::Error;
43///
44/// async fn handle(&self, input: Self::Input) -> Result<Self::Output> {
45/// Ok(GreetOutput {
46/// message: format!("Hello, {}!", input.name),
47/// })
48/// }
49/// }
50/// ```
51///
52/// ## Handler with Validation
53///
54/// ```rust
55/// use pforge_runtime::{Handler, Result, Error};
56/// use serde::{Deserialize, Serialize};
57/// use schemars::JsonSchema;
58///
59/// #[derive(Debug, Deserialize, JsonSchema)]
60/// struct AgeInput {
61/// age: i32,
62/// }
63///
64/// #[derive(Debug, Serialize, JsonSchema)]
65/// struct AgeOutput {
66/// category: String,
67/// }
68///
69/// struct AgeHandler;
70///
71/// #[async_trait::async_trait]
72/// impl Handler for AgeHandler {
73/// type Input = AgeInput;
74/// type Output = AgeOutput;
75/// type Error = Error;
76///
77/// async fn handle(&self, input: Self::Input) -> Result<Self::Output> {
78/// if input.age < 0 {
79/// return Err(Error::Handler("Age cannot be negative".to_string()));
80/// }
81///
82/// let category = match input.age {
83/// 0..=12 => "child",
84/// 13..=19 => "teenager",
85/// 20..=64 => "adult",
86/// _ => "senior",
87/// }.to_string();
88///
89/// Ok(AgeOutput { category })
90/// }
91/// }
92/// ```
93#[async_trait]
94pub trait Handler: Send + Sync + 'static {
95 /// Input type for the handler
96 type Input: JsonSchema + DeserializeOwned + Send;
97
98 /// Output type for the handler
99 type Output: JsonSchema + Serialize + Send;
100
101 /// Error type that can be converted to `pforge_runtime::Error`
102 type Error: Into<crate::Error>;
103
104 /// Execute the handler with type-safe input.
105 ///
106 /// This is the core method where tool logic is implemented.
107 /// Input is automatically deserialized and output is automatically serialized.
108 async fn handle(&self, input: Self::Input) -> Result<Self::Output, Self::Error>;
109
110 /// Generate JSON schema for input (override for custom schemas).
111 ///
112 /// By default, this derives the schema from the `Input` type using schemars.
113 /// Override this method to provide custom validation rules or documentation.
114 fn input_schema() -> schemars::schema::RootSchema {
115 schemars::schema_for!(Self::Input)
116 }
117
118 /// Generate JSON schema for output.
119 ///
120 /// By default, this derives the schema from the `Output` type using schemars.
121 fn output_schema() -> schemars::schema::RootSchema {
122 schemars::schema_for!(Self::Output)
123 }
124}