fix: match shape of the calltoolresult schema (#377)

BREAKING CHANGE: makes the `content` field non-optional
This commit is contained in:
Peter L 2025-08-19 15:58:25 +09:30 • committed by GitHub
commit a47cbaac4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -183,7 +183,7 @@ impl<'de> Deserialize<'de> for ProtocolVersion {
pub enum NumberOrString {
/// A numeric identifier
Number(u32),
/// A string identifier
/// A string identifier
String(Arc<str>),
}
@ -1257,8 +1257,7 @@ pub type CreateElicitationRequest =
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct CallToolResult {
/// The content returned by the tool (text, images, etc.)
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<Vec<Content>>,
pub content: Vec<Content>,
/// An optional JSON object that represents the structured result of the tool call
#[serde(skip_serializing_if = "Option::is_none")]
pub structured_content: Option<Value>,
@ -1271,7 +1270,7 @@ impl CallToolResult {
/// Create a successful tool result with unstructured content
pub fn success(content: Vec<Content>) -> Self {
CallToolResult {
content: Some(content),
content,
structured_content: None,
is_error: Some(false),
}
@ -1279,7 +1278,7 @@ impl CallToolResult {
/// Create an error tool result with unstructured content
pub fn error(content: Vec<Content>) -> Self {
CallToolResult {
content: Some(content),
content,
structured_content: None,
is_error: Some(true),
}
@ -1300,7 +1299,7 @@ impl CallToolResult {
/// ```
pub fn structured(value: Value) -> Self {
CallToolResult {
content: Some(vec![Content::text(value.to_string())]),
content: vec![Content::text(value.to_string())],
structured_content: Some(value),
is_error: Some(false),
}
@ -1325,7 +1324,7 @@ impl CallToolResult {
/// ```
pub fn structured_error(value: Value) -> Self {
CallToolResult {
content: Some(vec![Content::text(value.to_string())]),
content: vec![Content::text(value.to_string())],
structured_content: Some(value),
is_error: Some(true),
}
@ -1341,10 +1340,10 @@ impl CallToolResult {
where
T: DeserializeOwned,
{
let raw_text = match (self.structured_content, &self.content) {
let raw_text = match (self.structured_content, &self.content.first()) {
(Some(value), _) => return serde_json::from_value(value),
(None, Some(contents)) => {
if let Some(text) = contents.first().and_then(|c| c.as_text()) {
if let Some(text) = contents.as_text() {
let text = &text.text;
Some(text)
} else {
@ -1379,13 +1378,13 @@ impl<'de> Deserialize<'de> for CallToolResult {
let helper = CallToolResultHelper::deserialize(deserializer)?;
let result = CallToolResult {
content: helper.content,
content: helper.content.unwrap_or_default(),
structured_content: helper.structured_content,
is_error: helper.is_error,
};
// Validate mutual exclusivity
if result.content.is_none() && result.structured_content.is_none() {
if result.content.is_empty() && result.structured_content.is_none() {
return Err(serde::de::Error::custom(
"CallToolResult must have either content or structured_content",
));