Skip to content

Commit 2ee6106

Browse files
fix(rmcp): allow both content and structured content (#359)
Co-authored-by: Michael Neale <michael.neale@gmail.com>
1 parent 984cd87 commit 2ee6106

4 files changed

Lines changed: 11 additions & 14 deletions

File tree

crates/rmcp/src/model.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,8 +1181,6 @@ pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNoti
11811181
///
11821182
/// Contains the content returned by the tool execution and an optional
11831183
/// flag indicating whether the operation resulted in an error.
1184-
///
1185-
/// Note: `content` and `structured_content` are mutually exclusive - exactly one must be provided.
11861184
#[derive(Debug, Serialize, Clone, PartialEq)]
11871185
#[serde(rename_all = "camelCase")]
11881186
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@@ -1262,10 +1260,9 @@ impl CallToolResult {
12621260
}
12631261
}
12641262

1265-
/// Validate that content and structured_content are mutually exclusive
1263+
/// Validate that content or structured content is provided
12661264
pub fn validate(&self) -> Result<(), &'static str> {
12671265
match (&self.content, &self.structured_content) {
1268-
(Some(_), Some(_)) => Err("content and structured_content are mutually exclusive"),
12691266
(None, None) => Err("either content or structured_content must be provided"),
12701267
_ => Ok(()),
12711268
}

crates/rmcp/tests/test_message_schema/server_json_rpc_message_schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@
299299
}
300300
},
301301
"CallToolResult": {
302-
"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.",
302+
"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.",
303303
"type": "object",
304304
"properties": {
305305
"content": {

crates/rmcp/tests/test_message_schema/server_json_rpc_message_schema_current.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@
299299
}
300300
},
301301
"CallToolResult": {
302-
"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.",
302+
"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.",
303303
"type": "object",
304304
"properties": {
305305
"content": {
@@ -1336,7 +1336,7 @@
13361336
{
13371337
"type": "object",
13381338
"properties": {
1339-
"mime_type": {
1339+
"mimeType": {
13401340
"type": [
13411341
"string",
13421342
"null"
@@ -1360,7 +1360,7 @@
13601360
"blob": {
13611361
"type": "string"
13621362
},
1363-
"mime_type": {
1363+
"mimeType": {
13641364
"type": [
13651365
"string",
13661366
"null"

crates/rmcp/tests/test_structured_output.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,23 +146,23 @@ async fn test_structured_error_in_call_result() {
146146

147147
#[tokio::test]
148148
async fn test_mutual_exclusivity_validation() {
149-
// Test that content and structured_content are mutually exclusive
149+
// Test that content and structured_content can both be passed separately
150150
let content_result = CallToolResult::success(vec![Content::text("Hello")]);
151151
let structured_result = CallToolResult::structured(json!({"message": "Hello"}));
152152

153153
// Verify the validation
154154
assert!(content_result.validate().is_ok());
155155
assert!(structured_result.validate().is_ok());
156156

157-
// Try to create an invalid result with both fields
158-
let invalid_json = json!({
157+
// Try to create a result with both fields
158+
let json_with_both = json!({
159159
"content": [{"type": "text", "text": "Hello"}],
160160
"structuredContent": {"message": "Hello"}
161161
});
162162

163-
// The deserialization itself should fail due to validation
164-
let deserialized: Result<CallToolResult, _> = serde_json::from_value(invalid_json);
165-
assert!(deserialized.is_err());
163+
// The deserialization itself should not fail
164+
let deserialized: Result<CallToolResult, _> = serde_json::from_value(json_with_both);
165+
assert!(deserialized.is_ok());
166166
}
167167

168168
#[tokio::test]

0 commit comments

Comments
 (0)