///|
/// Tool execution result following MCP protocol format
pub struct ToolResult {
content : Array[@types.ContentItem]
is_error : Bool
} derive(Eq, Debug)
///|
/// Outcome of a tool execution. Per the 2026-07-28 MRTR pattern, a tool may
/// either complete normally (`Complete`) or request additional client input
/// (`InputRequired`). On retry, the client resubmits with `inputResponses`
/// and the echoed `requestState`; the tool then receives those responses via
/// its arguments and returns `Complete`.
pub(all) enum ToolCallOutcome {
/// Normal completion — the tool finished and produced a result.
Complete(ToolResult)
/// The tool needs client input before it can finish. `input_requests`
/// maps server-assigned keys to elicitation/sampling/roots requests;
/// `state` is the server's continuation data (sealed into `requestState`
/// by the handler before being sent to the client).
InputRequired(
input_requests~ : Map[String, @types.InputRequestEntry],
state~ : Json
)
} derive(Eq, Debug)
///|
/// Create a successful result with text content
pub fn ToolResult::text(text : String) -> ToolResult {
{ content: [@types.ContentItem::Text(text)], is_error: false }
}
///|
/// Create a successful result with multiple content items
pub fn ToolResult::success(content : Array[@types.ContentItem]) -> ToolResult {
{ content, is_error: false }
}
///|
/// Create an error result with error message
pub fn ToolResult::error(message : String) -> ToolResult {
{ content: [@types.ContentItem::Text(message)], is_error: true }
}
///| ToToolResult Trait (Maria-inspired type conversion)
///|
/// Trait for types that can be converted to ToolResult
pub trait ToToolResult {
fn to_tool_result(Self) -> ToolResult
}
///|
/// Built-in conversion from String
pub impl ToToolResult for String with fn to_tool_result(self : String) -> ToolResult {
ToolResult::text(self)
}
///|
/// Built-in conversion from Int
pub impl ToToolResult for Int with fn to_tool_result(self : Int) -> ToolResult {
ToolResult::text(self.to_string())
}
///|
/// Built-in conversion from Bool
pub impl ToToolResult for Bool with fn to_tool_result(self : Bool) -> ToolResult {
ToolResult::text(self.to_string())
}
///|
/// Built-in conversion from Double (number)
pub impl ToToolResult for Double with fn to_tool_result(self : Double) -> ToolResult {
ToolResult::text(self.to_string())
}
///|
/// Built-in conversion from ToolResult (identity)
pub impl ToToolResult for ToolResult with fn to_tool_result(self : ToolResult) -> ToolResult {
self
}
///|
/// Parameter definition for tool schema
pub(all) struct ParamDef {
name : String
description : String
type_ : String // "string", "number", "boolean", "object", "array"
required : Bool
} derive(Eq, Debug)
///|
/// Core Tool trait following MCP protocol
pub(open) trait Tool {
fn name(Self) -> String
fn description(Self) -> String
fn params(Self) -> Array[ParamDef]
/// Execute the tool. Returns `Complete` for a normal result, or
/// `InputRequired` to trigger an MRTR round-trip (the client will be asked
/// for input and the request retried with `inputResponses`).
async fn execute(Self, Json) -> ToolCallOutcome
}
///|
/// Extract string from JSON, returning error ToolResult on failure
pub fn get_string(json : Json, key : String) -> Result[String, ToolResult] {
match json {
Object(map) =>
match map.get(key) {
Some(String(s)) => Ok(s)
Some(_) => Err(ToolResult::error("Field '\{key}' is not a string"))
None => Err(ToolResult::error("Missing required field '\{key}'"))
}
_ => Err(ToolResult::error("Expected JSON object"))
}
}
///|
/// Extract number from JSON, returning error ToolResult on failure
pub fn get_number(json : Json, key : String) -> Result[Double, ToolResult] {
match json {
Object(map) =>
match map.get(key) {
Some(Number(n, ..)) => Ok(n)
Some(_) => Err(ToolResult::error("Field '\{key}' is not a number"))
None => Err(ToolResult::error("Missing required field '\{key}'"))
}
_ => Err(ToolResult::error("Expected JSON object"))
}
}
///|
/// Extract optional string from JSON
pub fn get_optional_string(
json : Json,
key : String,
) -> Result[String?, ToolResult] {
match json {
Object(map) =>
match map.get(key) {
Some(String(s)) => Ok(Some(s))
Some(Null) => Ok(None)
None => Ok(None)
Some(_) => Err(ToolResult::error("Field '\{key}' is not a string"))
}
_ => Err(ToolResult::error("Expected JSON object"))
}
}
///|
/// Extract optional number from JSON
pub fn get_optional_number(
json : Json,
key : String,
) -> Result[Double?, ToolResult] {
match json {
Object(map) =>
match map.get(key) {
Some(Number(n, ..)) => Ok(Some(n))
Some(Null) => Ok(None)
None => Ok(None)
Some(_) => Err(ToolResult::error("Field '\{key}' is not a number"))
}
_ => Err(ToolResult::error("Expected JSON object"))
}
}