fix!: omit resultType for legacy protocol sessions (#1038)

This commit is contained in:
Dale Seo 2026-07-24 10:22:20 -04:00 • committed by GitHub
commit 6839cfd613
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 438 additions and 109 deletions

View file

@ -199,13 +199,15 @@
]
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"structuredContent": {
"description": "An optional JSON object that represents the structured result of the tool call"
@ -326,13 +328,15 @@
"$ref": "#/definitions/CompletionInfo"
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
}
},
"required": [
@ -1140,13 +1144,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
}
},
"required": [
@ -1783,13 +1789,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -1842,13 +1850,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -1901,13 +1911,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -1959,13 +1971,15 @@
]
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"tools": {
"type": "array",
@ -2581,13 +2595,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -2948,7 +2964,7 @@
}
},
"ResultType": {
"description": "Indicates the type of a result object, allowing the client to\ndetermine how to parse the response.\n\nThe spec defines this as an open string (`\"complete\" | \"input_required\" | string`),\nso unknown values are preserved rather than rejected. Servers implementing this\nprotocol version MUST include `resultType` in every result. For backward\ncompatibility, clients MUST treat an absent field as `\"complete\"`.",
"description": "Indicates the type of a result object, allowing the client to\ndetermine how to parse the response.\n\nThe spec defines this as an open string (`\"complete\" | \"input_required\" | string`),\nso unknown values are preserved rather than rejected. Servers implementing this\nprotocol version MUST include `resultType` in every result. For backward\ncompatibility, clients MUST treat an absent field as `\"complete\"`.\n\nOrdinary results model the field as `Option<ResultType>`: `None` means the\nfield is absent on the wire. Constructors default to `Some(COMPLETE)`, and\nthe server handler strips the `\"complete\"` discriminator before responding\nto peers that negotiated a protocol version older than `2026-07-28`, so\nlegacy sessions keep their historical wire shape (see\n[`ServerResult::strip_result_type_for_legacy_peer`]).",
"type": "string"
},
"Role": {

View file

@ -199,13 +199,15 @@
]
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"structuredContent": {
"description": "An optional JSON object that represents the structured result of the tool call"
@ -326,13 +328,15 @@
"$ref": "#/definitions/CompletionInfo"
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
}
},
"required": [
@ -1140,13 +1144,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
}
},
"required": [
@ -1783,13 +1789,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -1842,13 +1850,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -1901,13 +1911,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -1959,13 +1971,15 @@
]
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"tools": {
"type": "array",
@ -2581,13 +2595,15 @@
}
},
"resultType": {
"description": "Result type discriminator. Absent values deserialize as `\"complete\"`.",
"allOf": [
"description": "Result type discriminator (SEP-2322). Required by the [spec schema]\nfor servers implementing protocol version `2026-07-28`, but optional\nhere because this type also models results from older protocol\nversions, which do not carry the field: `None` means absent on the\nwire, and per the spec \"the client MUST treat the absent field as\n`\"complete\"`\". Constructors default to `Some(ResultType::COMPLETE)`;\nthe server handler clears the field when responding to peers that\nnegotiated an older version.\n\n[spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/5bed7b30527019e34ccb0eb474636651424501f6/schema/draft/schema.ts#L225-L234",
"anyOf": [
{
"$ref": "#/definitions/ResultType"
},
{
"type": "null"
}
],
"default": "complete"
]
},
"ttlMs": {
"description": "Time, in milliseconds, that this result may be treated as fresh (SEP-2549).\nRequired by spec version 2026-07-28, but optional here to maintain compatibility\nwith older spec versions.",
@ -2948,7 +2964,7 @@
}
},
"ResultType": {
"description": "Indicates the type of a result object, allowing the client to\ndetermine how to parse the response.\n\nThe spec defines this as an open string (`\"complete\" | \"input_required\" | string`),\nso unknown values are preserved rather than rejected. Servers implementing this\nprotocol version MUST include `resultType` in every result. For backward\ncompatibility, clients MUST treat an absent field as `\"complete\"`.",
"description": "Indicates the type of a result object, allowing the client to\ndetermine how to parse the response.\n\nThe spec defines this as an open string (`\"complete\" | \"input_required\" | string`),\nso unknown values are preserved rather than rejected. Servers implementing this\nprotocol version MUST include `resultType` in every result. For backward\ncompatibility, clients MUST treat an absent field as `\"complete\"`.\n\nOrdinary results model the field as `Option<ResultType>`: `None` means the\nfield is absent on the wire. Constructors default to `Some(COMPLETE)`, and\nthe server handler strips the `\"complete\"` discriminator before responding\nto peers that negotiated a protocol version older than `2026-07-28`, so\nlegacy sessions keep their historical wire shape (see\n[`ServerResult::strip_result_type_for_legacy_peer`]).",
"type": "string"
},
"Role": {

View file

@ -0,0 +1,82 @@
//! SEP-2322: the `resultType` discriminator follows the negotiated protocol version.
//!
//! Peers negotiating `2026-07-28` or newer receive `resultType: "complete"` on
//! ordinary results; older peers keep the legacy wire shape without the field.
#![cfg(not(feature = "local"))]
#![cfg(feature = "client")]
use rmcp::{
ClientHandler, RoleServer, ServerHandler, ServiceExt,
model::{
CallToolRequestParams, CallToolResponse, CallToolResult, ClientInfo, ContentBlock,
ErrorData, ProtocolVersion, ResultType,
},
service::RequestContext,
};
#[derive(Debug, Clone, Default)]
struct ToolServer;
impl ServerHandler for ToolServer {
async fn call_tool(
&self,
_request: CallToolRequestParams,
_context: RequestContext<RoleServer>,
) -> Result<CallToolResponse, ErrorData> {
Ok(CallToolResult::success(vec![ContentBlock::text("ok")]).into())
}
}
#[derive(Debug, Clone)]
struct VersionedClient {
protocol_version: ProtocolVersion,
}
impl ClientHandler for VersionedClient {
fn get_info(&self) -> ClientInfo {
let mut info = ClientInfo::default();
info.protocol_version = self.protocol_version.clone();
info
}
}
async fn call_tool_result_type(client_version: ProtocolVersion) -> Option<ResultType> {
let (server_transport, client_transport) = tokio::io::duplex(4096);
let server_handle = tokio::spawn(async move {
ToolServer.serve(server_transport).await?.waiting().await?;
anyhow::Ok(())
});
let client = VersionedClient {
protocol_version: client_version,
}
.serve(client_transport)
.await
.expect("client should connect");
let result = client
.call_tool(CallToolRequestParams::new("echo"))
.await
.expect("tool call should succeed");
client.cancel().await.expect("client should cancel");
server_handle.await.expect("server task").expect("server");
result.result_type
}
#[tokio::test]
async fn legacy_version_omits_result_type() {
assert_eq!(
call_tool_result_type(ProtocolVersion::V_2025_11_25).await,
None
);
}
#[tokio::test]
async fn sep_2322_version_gets_complete_result_type() {
assert_eq!(
call_tool_result_type(ProtocolVersion::V_2026_07_28).await,
Some(ResultType::COMPLETE),
);
}

View file

@ -2,10 +2,16 @@
//!
//! These pin the behavior that keeps older/strict peers working:
//! - `EmptyResult` stays a bare `{}` (some peers strict-validate empty results
//! and reject extra keys), and
//! - ordinary results carry `resultType: "complete"`.
//! and reject extra keys),
//! - ordinary results carry `resultType: "complete"` by default, and
//! - [`ServerResult::strip_result_type_for_legacy_peer`] removes the
//! `"complete"` discriminator so legacy sessions keep their historical wire
//! shape (round-tripping a legacy result never adds the field).
use rmcp::model::{CallToolResult, ContentBlock, EmptyResult, ListToolsResult};
use rmcp::model::{
CallToolResult, CompleteResult, ContentBlock, EmptyResult, GetPromptResult, ListToolsResult,
ReadResourceResult, ResultType, ServerResult,
};
use serde_json::json;
#[test]
@ -27,3 +33,94 @@ fn paginated_result_serializes_complete_result_type() {
serde_json::to_value(ListToolsResult::default()).expect("serialize ListToolsResult");
assert_eq!(value["resultType"], "complete");
}
// Guards the convention the server handler relies on: every constructor and
// `Default` impl produces `Some(COMPLETE)`, so 2026-07-28 sessions always
// include the spec-required field unless a handler clears it explicitly.
#[test]
fn constructors_and_defaults_produce_complete_result_type() {
let complete = Some(ResultType::COMPLETE);
assert_eq!(CallToolResult::default().result_type, complete);
assert_eq!(CallToolResult::success(vec![]).result_type, complete);
assert_eq!(CallToolResult::error(vec![]).result_type, complete);
assert_eq!(CallToolResult::structured(json!({})).result_type, complete);
assert_eq!(
CallToolResult::structured_error(json!({})).result_type,
complete
);
assert_eq!(ListToolsResult::default().result_type, complete);
assert_eq!(
ListToolsResult::with_all_items(vec![]).result_type,
complete
);
assert_eq!(ReadResourceResult::new(vec![]).result_type, complete);
assert_eq!(GetPromptResult::default().result_type, complete);
assert_eq!(GetPromptResult::new(vec![]).result_type, complete);
assert_eq!(CompleteResult::default().result_type, complete);
}
#[test]
fn absent_result_type_deserializes_as_none() {
let legacy = json!({
"content": [],
"isError": false,
});
let result: CallToolResult =
serde_json::from_value(legacy).expect("deserialize legacy CallToolResult");
assert_eq!(result.result_type, None);
}
#[test]
fn legacy_call_tool_result_round_trips_without_result_type() {
let legacy = json!({
"content": [],
"isError": false,
});
let result: CallToolResult =
serde_json::from_value(legacy.clone()).expect("deserialize legacy CallToolResult");
let reserialized = serde_json::to_value(result).expect("serialize CallToolResult");
assert_eq!(reserialized, legacy);
}
#[test]
fn strip_removes_complete_result_type() {
let mut result =
ServerResult::CallToolResult(CallToolResult::success(vec![ContentBlock::text("ok")]));
result.strip_result_type_for_legacy_peer();
let value = serde_json::to_value(result).expect("serialize ServerResult");
assert_eq!(
value,
json!({
"content": [{ "type": "text", "text": "ok" }],
"isError": false,
})
);
}
#[test]
fn strip_removes_complete_result_type_from_paginated_result() {
let mut result = ServerResult::ListToolsResult(ListToolsResult::default());
result.strip_result_type_for_legacy_peer();
let value = serde_json::to_value(result).expect("serialize ServerResult");
assert_eq!(value, json!({ "tools": [] }));
}
#[test]
fn strip_preserves_custom_result_type() {
let streaming: ResultType =
serde_json::from_value(json!("streaming")).expect("deserialize ResultType");
let mut tool_result = CallToolResult::success(vec![ContentBlock::text("ok")]);
tool_result.result_type = Some(streaming);
let mut result = ServerResult::CallToolResult(tool_result);
result.strip_result_type_for_legacy_peer();
let value = serde_json::to_value(result).expect("serialize ServerResult");
assert_eq!(value["resultType"], "streaming");
}