fix(rmcp): allow both content and structured content (#359)
Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
parent
984cd87ed8
commit
2ee6106fc3
4 changed files with 11 additions and 14 deletions
|
|
@ -1181,8 +1181,6 @@ pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNoti
|
||||||
///
|
///
|
||||||
/// Contains the content returned by the tool execution and an optional
|
/// Contains the content returned by the tool execution and an optional
|
||||||
/// flag indicating whether the operation resulted in an error.
|
/// flag indicating whether the operation resulted in an error.
|
||||||
///
|
|
||||||
/// Note: `content` and `structured_content` are mutually exclusive - exactly one must be provided.
|
|
||||||
#[derive(Debug, Serialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
|
@ -1262,10 +1260,9 @@ impl CallToolResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate that content and structured_content are mutually exclusive
|
/// Validate that content or structured content is provided
|
||||||
pub fn validate(&self) -> Result<(), &'static str> {
|
pub fn validate(&self) -> Result<(), &'static str> {
|
||||||
match (&self.content, &self.structured_content) {
|
match (&self.content, &self.structured_content) {
|
||||||
(Some(_), Some(_)) => Err("content and structured_content are mutually exclusive"),
|
|
||||||
(None, None) => Err("either content or structured_content must be provided"),
|
(None, None) => Err("either content or structured_content must be provided"),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CallToolResult": {
|
"CallToolResult": {
|
||||||
"description": "The result of a tool call operation.\n\nContains the content returned by the tool execution and an optional\nflag indicating whether the operation resulted in an error.\n\nNote: `content` and `structured_content` are mutually exclusive - exactly one must be provided.",
|
"description": "The result of a tool call operation.\n\nContains the content returned by the tool execution and an optional\nflag indicating whether the operation resulted in an error.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"content": {
|
"content": {
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CallToolResult": {
|
"CallToolResult": {
|
||||||
"description": "The result of a tool call operation.\n\nContains the content returned by the tool execution and an optional\nflag indicating whether the operation resulted in an error.\n\nNote: `content` and `structured_content` are mutually exclusive - exactly one must be provided.",
|
"description": "The result of a tool call operation.\n\nContains the content returned by the tool execution and an optional\nflag indicating whether the operation resulted in an error.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"content": {
|
"content": {
|
||||||
|
|
@ -1336,7 +1336,7 @@
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"mime_type": {
|
"mimeType": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
|
|
@ -1360,7 +1360,7 @@
|
||||||
"blob": {
|
"blob": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"mime_type": {
|
"mimeType": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ async fn test_structured_error_in_call_result() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_mutual_exclusivity_validation() {
|
async fn test_mutual_exclusivity_validation() {
|
||||||
// Test that content and structured_content are mutually exclusive
|
// Test that content and structured_content can both be passed separately
|
||||||
let content_result = CallToolResult::success(vec![Content::text("Hello")]);
|
let content_result = CallToolResult::success(vec![Content::text("Hello")]);
|
||||||
let structured_result = CallToolResult::structured(json!({"message": "Hello"}));
|
let structured_result = CallToolResult::structured(json!({"message": "Hello"}));
|
||||||
|
|
||||||
|
|
@ -154,15 +154,15 @@ async fn test_mutual_exclusivity_validation() {
|
||||||
assert!(content_result.validate().is_ok());
|
assert!(content_result.validate().is_ok());
|
||||||
assert!(structured_result.validate().is_ok());
|
assert!(structured_result.validate().is_ok());
|
||||||
|
|
||||||
// Try to create an invalid result with both fields
|
// Try to create a result with both fields
|
||||||
let invalid_json = json!({
|
let json_with_both = json!({
|
||||||
"content": [{"type": "text", "text": "Hello"}],
|
"content": [{"type": "text", "text": "Hello"}],
|
||||||
"structuredContent": {"message": "Hello"}
|
"structuredContent": {"message": "Hello"}
|
||||||
});
|
});
|
||||||
|
|
||||||
// The deserialization itself should fail due to validation
|
// The deserialization itself should not fail
|
||||||
let deserialized: Result<CallToolResult, _> = serde_json::from_value(invalid_json);
|
let deserialized: Result<CallToolResult, _> = serde_json::from_value(json_with_both);
|
||||||
assert!(deserialized.is_err());
|
assert!(deserialized.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue