use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
pub use crate::name::ToolName;
use crate::{
ToolError,
execution::ToolResult,
interaction::ToolInteractionCapabilities,
schema::{JsonSchema, ValidationIssue},
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolInfo {
pub name: ToolName,
pub title: Option<String>,
pub description: Option<String>,
pub input_schema: JsonSchema,
pub output_schema: Option<JsonSchema>,
pub annotations: ToolAnnotations,
pub icons: Vec<ToolIcon>,
pub interactions: ToolInteractionCapabilities,
pub metadata: BTreeMap<String, serde_json::Value>,
}
impl ToolInfo {
#[must_use]
pub fn new(name: ToolName, input_schema: JsonSchema) -> Self {
Self {
name,
title: None,
description: None,
input_schema,
output_schema: None,
annotations: ToolAnnotations::default(),
icons: Vec::new(),
interactions: ToolInteractionCapabilities::default(),
metadata: BTreeMap::new(),
}
}
pub(crate) fn validate_output(&self, result: &ToolResult) -> Result<(), ToolError> {
if result.is_error {
return Ok(());
}
let Some(schema) = &self.output_schema else {
return Ok(());
};
let value = result
.structured_content
.as_ref()
.ok_or_else(|| ToolError::InvalidOutput {
message: format!(
"successful output from `{}` omitted structured content",
self.name
),
issues: vec![ValidationIssue {
path: String::new(),
message: "structured content is required by the declared output schema"
.to_owned(),
}],
})?;
let issues = schema.validate(value)?;
if issues.is_empty() {
Ok(())
} else {
Err(ToolError::InvalidOutput {
message: format!(
"structured output from `{}` failed schema validation",
self.name
),
issues,
})
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ToolAnnotations {
pub title: Option<String>,
pub read_only_hint: Option<bool>,
pub destructive_hint: Option<bool>,
pub idempotent_hint: Option<bool>,
pub open_world_hint: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ToolIcon {
pub uri: String,
pub mime_type: Option<String>,
}