pub struct TypedTool<T, F>where
T: DeserializeOwned + Send + Sync + 'static,
F: Fn(T, RequestHandlerExtra) -> Pin<Box<dyn Future<Output = Result<Value>> + Send>> + Send + Sync,{ /* private fields */ }Expand description
A typed tool implementation with automatic schema generation and validation.
Implementations§
Source§impl<T, F> TypedTool<T, F>
impl<T, F> TypedTool<T, F>
Sourcepub fn new(name: impl Into<String>, handler: F) -> Selfwhere
T: JsonSchema,
Available on crate feature schema-generation only.
pub fn new(name: impl Into<String>, handler: F) -> Selfwhere
T: JsonSchema,
schema-generation only.Create a new typed tool with automatic schema generation.
Sourcepub fn new_with_schema(
name: impl Into<String>,
schema: Value,
handler: F,
) -> Self
pub fn new_with_schema( name: impl Into<String>, schema: Value, handler: F, ) -> Self
Create a new typed tool with a manually provided schema.
Sourcepub fn with_description(self, description: impl Into<String>) -> Self
pub fn with_description(self, description: impl Into<String>) -> Self
Set the description for this tool.
Sourcepub fn with_annotations(self, annotations: ToolAnnotations) -> Self
pub fn with_annotations(self, annotations: ToolAnnotations) -> Self
Set annotations for this tool.
Annotations provide behavioral hints to AI clients about how this tool should be used (read-only, destructive, idempotent, etc.).
§Example
use pmcp::server::typed_tool::TypedTool;
use pmcp::types::ToolAnnotations;
use serde::Deserialize;
use schemars::JsonSchema;
#[derive(Debug, Deserialize, JsonSchema)]
struct DeleteArgs {
id: String,
}
let tool = TypedTool::new("delete_record", |args: DeleteArgs, _extra| {
Box::pin(async move {
Ok(serde_json::json!({"deleted": true}))
})
})
.with_description("Permanently delete a record")
.with_annotations(
ToolAnnotations::new()
.with_read_only(false)
.with_destructive(true)
.with_idempotent(true)
);Sourcepub fn read_only(self) -> Self
pub fn read_only(self) -> Self
Mark this tool as read-only (convenience method).
Equivalent to .with_annotations(ToolAnnotations::new().with_read_only(true))
Sourcepub fn destructive(self) -> Self
pub fn destructive(self) -> Self
Mark this tool as destructive (convenience method).
Sets readOnlyHint: false and destructiveHint: true.
Sourcepub fn idempotent(self) -> Self
pub fn idempotent(self) -> Self
Mark this tool as idempotent (convenience method).
Equivalent to .with_annotations(ToolAnnotations::new().with_idempotent(true))
Sourcepub fn open_world(self) -> Self
pub fn open_world(self) -> Self
Mark this tool as interacting with external systems (convenience method).
Equivalent to .with_annotations(ToolAnnotations::new().with_open_world(true))
Sourcepub fn with_ui(self, ui_resource_uri: impl Into<String>) -> Self
pub fn with_ui(self, ui_resource_uri: impl Into<String>) -> Self
Associate this tool with a UI resource (MCP Apps Extension).
This sets the nested _meta.ui.resourceUri field and the openai/outputTemplate
alias in the tool’s _meta, allowing both MCP and ChatGPT hosts to display
an interactive UI when this tool is invoked.
§Example
use pmcp::server::typed_tool::TypedTool;
use serde::Deserialize;
use schemars::JsonSchema;
#[derive(Debug, Deserialize, JsonSchema)]
struct AnalyzeArgs {
query: String,
}
let tool = TypedTool::new("analyze_sales", |args: AnalyzeArgs, _extra| {
Box::pin(async move {
Ok(serde_json::json!({"result": "data"}))
})
})
.with_description("Analyze sales data")
.with_ui("ui://charts/sales"); // Associate with UI resourceSourcepub fn with_execution(self, execution: ToolExecution) -> Self
pub fn with_execution(self, execution: ToolExecution) -> Self
Declare execution metadata for this tool (MCP 2025-11-25).
Use this to advertise task support so clients know whether to send
a task field in tools/call requests.
§Example
use pmcp::server::typed_tool::TypedTool;
use pmcp::types::{ToolExecution, TaskSupport};
use serde::Deserialize;
use schemars::JsonSchema;
#[derive(Debug, Deserialize, JsonSchema)]
struct AnalyzeArgs { region: String }
let tool = TypedTool::new("analyze", |args: AnalyzeArgs, _extra| {
Box::pin(async move {
Ok(serde_json::json!({"taskId": "t-1", "status": "working"}))
})
})
.with_description("Long-running analysis")
.with_execution(ToolExecution::new().with_task_support(TaskSupport::Required));Trait Implementations§
Source§impl<T, F> ToolHandler for TypedTool<T, F>
impl<T, F> ToolHandler for TypedTool<T, F>
Source§fn handle<'life0, 'async_trait>(
&'life0 self,
args: Value,
extra: RequestHandlerExtra,
) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn handle<'life0, 'async_trait>(
&'life0 self,
args: Value,
extra: RequestHandlerExtra,
) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn metadata(&self) -> Option<ToolInfo>
fn metadata(&self) -> Option<ToolInfo>
Source§fn handle_output<'life0, 'async_trait>(
&'life0 self,
args: Value,
extra: RequestHandlerExtra,
) -> Pin<Box<dyn Future<Output = Result<ToolOutput>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn handle_output<'life0, 'async_trait>(
&'life0 self,
args: Value,
extra: RequestHandlerExtra,
) -> Pin<Box<dyn Future<Output = Result<ToolOutput>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
ToolOutput for a call. Read more