fix: disambiguate input-required results (#1103)
This commit is contained in:
parent
830e088d73
commit
684ff5a51e
2 changed files with 65 additions and 4 deletions
|
|
@ -3814,8 +3814,7 @@ pub struct CallToolResult {
|
||||||
// 2. Requires at least one known field to be present, so that `CallToolResult` doesn't
|
// 2. Requires at least one known field to be present, so that `CallToolResult` doesn't
|
||||||
// greedily match arbitrary JSON objects when used inside `#[serde(untagged)]` enums
|
// greedily match arbitrary JSON objects when used inside `#[serde(untagged)]` enums
|
||||||
// (e.g. `ServerResult`), which would shadow `CustomResult`.
|
// (e.g. `ServerResult`), which would shadow `CustomResult`.
|
||||||
// 3. Rejects `resultType: "input_required"` so untagged `ServerResult`
|
// 3. Rejects non-`complete` result types so other `ServerResult` variants can match.
|
||||||
// decoding selects `InputRequiredResult` and preserves input requests and state.
|
|
||||||
impl<'de> Deserialize<'de> for CallToolResult {
|
impl<'de> Deserialize<'de> for CallToolResult {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
|
|
@ -3838,10 +3837,10 @@ impl<'de> Deserialize<'de> for CallToolResult {
|
||||||
if helper
|
if helper
|
||||||
.result_type
|
.result_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(ResultType::is_input_required)
|
.is_some_and(|result_type| !result_type.is_complete())
|
||||||
{
|
{
|
||||||
return Err(serde::de::Error::custom(
|
return Err(serde::de::Error::custom(
|
||||||
"CallToolResult cannot use resultType \"input_required\"",
|
"CallToolResult requires resultType to be \"complete\" when present",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,68 @@ fn legacy_call_tool_result_round_trips_without_result_type() {
|
||||||
assert_eq!(reserialized, legacy);
|
assert_eq!(reserialized, legacy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_tool_result_should_reject_non_complete_result_type() {
|
||||||
|
let input_required = json!({
|
||||||
|
"resultType": "input_required",
|
||||||
|
"_meta": {
|
||||||
|
"example.com/replica": "r1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(serde_json::from_value::<CallToolResult>(input_required).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_result_should_preserve_input_required_result_when_meta_present() {
|
||||||
|
let input_required = json!({
|
||||||
|
"resultType": "input_required",
|
||||||
|
"requestState": "sealed",
|
||||||
|
"_meta": {
|
||||||
|
"example.com/replica": "r1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let result =
|
||||||
|
serde_json::from_value::<ServerResult>(input_required).expect("deserialize ServerResult");
|
||||||
|
|
||||||
|
match result {
|
||||||
|
ServerResult::InputRequiredResult(result) => {
|
||||||
|
assert_eq!(
|
||||||
|
(result.request_state.as_deref(), result.meta.is_some()),
|
||||||
|
(Some("sealed"), true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("expected InputRequiredResult"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_result_should_accept_legacy_call_tool_result_when_only_meta_present() {
|
||||||
|
let legacy = json!({
|
||||||
|
"_meta": {
|
||||||
|
"example.com/replica": "r1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let result =
|
||||||
|
serde_json::from_value::<ServerResult>(legacy).expect("deserialize legacy CallToolResult");
|
||||||
|
|
||||||
|
match result {
|
||||||
|
ServerResult::CallToolResult(result) => {
|
||||||
|
assert_eq!(
|
||||||
|
(
|
||||||
|
result.result_type,
|
||||||
|
result.content.is_empty(),
|
||||||
|
result.meta.is_some()
|
||||||
|
),
|
||||||
|
(None, true, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("expected CallToolResult"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn strip_removes_complete_result_type() {
|
fn strip_removes_complete_result_type() {
|
||||||
let mut result =
|
let mut result =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue