pub fn define_tool<P, F, Fut>(
name: impl Into<String>,
description: impl Into<String>,
handler: F,
) -> Box<dyn ToolHandler>where
P: JsonSchema + DeserializeOwned + Send + 'static,
F: Fn(ToolInvocation, P) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<ToolResult, Error>> + Send + 'static,Expand description
Define a tool from an async function (or closure) that takes a typed,
JsonSchema-derived parameter struct.
The returned Box<dyn ToolHandler> plugs directly into
ToolHandlerRouter::new. JSON Schema for the parameter type is generated
via schema_for at construction time.
The handler bound (Fn(ToolInvocation, P) -> Fut + Send + Sync + 'static)
accepts both bare async fn items and closures — the same shape as
tower::service_fn and
hyper::service::service_fn. Prefer a free async fn
for non-trivial tools so it shows up in stack traces by name.
The closure receives the full ToolInvocation alongside the deserialized
parameters so handlers can use inv.session_id, inv.tool_call_id, or
other invocation metadata. Handlers that don’t need that metadata can
destructure with |_inv, params|.
§Example
use github_copilot_sdk::tool::{define_tool, JsonSchema};
use github_copilot_sdk::types::ToolInvocation;
use github_copilot_sdk::{Error, ToolResult};
use serde::Deserialize;
#[derive(Deserialize, JsonSchema)]
struct GetWeatherParams {
/// City name
city: String,
}
async fn get_weather(
inv: ToolInvocation,
params: GetWeatherParams,
) -> Result<ToolResult, Error> {
// `inv.session_id` and `inv.tool_call_id` are available for telemetry,
// streaming updates, scoping DB lookups, etc.
let _ = inv.session_id;
Ok(ToolResult::Text(format!("Sunny in {}", params.city)))
}
// Pass a free async fn — preferred for non-trivial tools.
let tool = define_tool("get_weather", "Get weather for a city", get_weather);
// ...or an inline closure when the body is trivial.
let tool = define_tool(
"echo",
"Echo the input",
|_inv, params: GetWeatherParams| async move {
Ok(ToolResult::Text(params.city))
},
);