fix: accept stringified numeric response IDs (#1021)
This commit is contained in:
parent
99ee024499
commit
8803d39034
2 changed files with 214 additions and 9 deletions
|
|
@ -9,11 +9,102 @@ use common::handlers::TestClientHandler;
|
|||
use rmcp::{
|
||||
ServiceExt,
|
||||
model::{
|
||||
ErrorCode, ErrorData, JsonRpcError, JsonRpcVersion2_0, RequestId, ServerJsonRpcMessage,
|
||||
ClientJsonRpcMessage, ErrorCode, ErrorData, InitializeResult, JsonRpcError,
|
||||
JsonRpcVersion2_0, RequestId, ServerCapabilities, ServerJsonRpcMessage, ServerResult,
|
||||
},
|
||||
transport::{IntoTransport, Transport},
|
||||
};
|
||||
|
||||
fn stringify_numeric_id(id: RequestId) -> RequestId {
|
||||
let RequestId::Number(id) = id else {
|
||||
panic!("expected a numeric request ID");
|
||||
};
|
||||
RequestId::String(id.to_string().into())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn client_initialization_accepts_stringified_numeric_response_id() {
|
||||
let (server_transport, client_transport) = tokio::io::duplex(1024);
|
||||
let mut server = IntoTransport::<rmcp::RoleServer, _, _>::into_transport(server_transport);
|
||||
let server_task = tokio::spawn(async move {
|
||||
let ClientJsonRpcMessage::Request(request) =
|
||||
server.receive().await.expect("expected initialize request")
|
||||
else {
|
||||
panic!("expected initialize request");
|
||||
};
|
||||
server
|
||||
.send(ServerJsonRpcMessage::response(
|
||||
ServerResult::InitializeResult(
|
||||
InitializeResult::new(ServerCapabilities::default()),
|
||||
),
|
||||
stringify_numeric_id(request.id),
|
||||
))
|
||||
.await
|
||||
.expect("send initialize response");
|
||||
assert!(matches!(
|
||||
server.receive().await,
|
||||
Some(ClientJsonRpcMessage::Notification(_))
|
||||
));
|
||||
});
|
||||
|
||||
let client = TestClientHandler::new(true, true)
|
||||
.serve(client_transport)
|
||||
.await
|
||||
.expect("client should accept stringified initialize response ID");
|
||||
client.cancel().await.expect("cancel client");
|
||||
server_task.await.expect("server task");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn client_correlates_stringified_numeric_response_id() {
|
||||
let (server_transport, client_transport) = tokio::io::duplex(1024);
|
||||
let mut server = IntoTransport::<rmcp::RoleServer, _, _>::into_transport(server_transport);
|
||||
let server_task = tokio::spawn(async move {
|
||||
let ClientJsonRpcMessage::Request(initialize) =
|
||||
server.receive().await.expect("expected initialize request")
|
||||
else {
|
||||
panic!("expected initialize request");
|
||||
};
|
||||
server
|
||||
.send(ServerJsonRpcMessage::response(
|
||||
ServerResult::InitializeResult(
|
||||
InitializeResult::new(ServerCapabilities::default()),
|
||||
),
|
||||
initialize.id,
|
||||
))
|
||||
.await
|
||||
.expect("send initialize response");
|
||||
assert!(matches!(
|
||||
server.receive().await,
|
||||
Some(ClientJsonRpcMessage::Notification(_))
|
||||
));
|
||||
|
||||
let ClientJsonRpcMessage::Request(request) =
|
||||
server.receive().await.expect("expected tools/list request")
|
||||
else {
|
||||
panic!("expected tools/list request");
|
||||
};
|
||||
server
|
||||
.send(ServerJsonRpcMessage::response(
|
||||
ServerResult::ListToolsResult(Default::default()),
|
||||
stringify_numeric_id(request.id),
|
||||
))
|
||||
.await
|
||||
.expect("send tools/list response");
|
||||
});
|
||||
|
||||
let client = TestClientHandler::new(true, true)
|
||||
.serve(client_transport)
|
||||
.await
|
||||
.expect("initialize client");
|
||||
client
|
||||
.list_tools(None)
|
||||
.await
|
||||
.expect("client should correlate stringified response ID");
|
||||
client.cancel().await.expect("cancel client");
|
||||
server_task.await.expect("server task");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_client_init_handles_jsonrpc_error() {
|
||||
let (server_transport, client_transport) = tokio::io::duplex(1024);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rmcp::{
|
|||
ClientHandler, ClientLifecycleMode, ClientServiceExt, ServerHandler, ServiceExt,
|
||||
model::{
|
||||
ClientJsonRpcMessage, ClientRequest, DiscoverResult, ErrorCode, ErrorData, GetMeta,
|
||||
Implementation, InitializeResult, ProtocolVersion, ServerCapabilities,
|
||||
Implementation, InitializeResult, ProtocolVersion, RequestId, ServerCapabilities,
|
||||
ServerJsonRpcMessage, ServerResult,
|
||||
},
|
||||
service::PeerRequestOptions,
|
||||
|
|
@ -21,6 +21,45 @@ struct StatelessServer;
|
|||
|
||||
impl ServerHandler for StatelessServer {}
|
||||
|
||||
#[tokio::test]
|
||||
async fn discover_startup_accepts_stringified_numeric_response_id() {
|
||||
let (server_transport, client_transport) = tokio::io::duplex(4096);
|
||||
let mut server = IntoTransport::<rmcp::RoleServer, _, _>::into_transport(server_transport);
|
||||
let server_task = tokio::spawn(async move {
|
||||
let ClientJsonRpcMessage::Request(request) =
|
||||
server.receive().await.expect("expected discover request")
|
||||
else {
|
||||
panic!("expected discover request");
|
||||
};
|
||||
let RequestId::Number(response_id) = request.id else {
|
||||
panic!("expected a numeric request ID");
|
||||
};
|
||||
server
|
||||
.send(ServerJsonRpcMessage::response(
|
||||
ServerResult::DiscoverResult(DiscoverResult::new(
|
||||
vec![ProtocolVersion::V_2026_07_28],
|
||||
ServerCapabilities::default(),
|
||||
Implementation::new("discover-server", "1.0.0"),
|
||||
)),
|
||||
RequestId::String(response_id.to_string().into()),
|
||||
))
|
||||
.await
|
||||
.expect("send discover response");
|
||||
});
|
||||
|
||||
let client = DiscoverClient
|
||||
.serve_with_lifecycle(
|
||||
client_transport,
|
||||
ClientLifecycleMode::Discover {
|
||||
preferred_versions: vec![ProtocolVersion::V_2026_07_28],
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("client should accept stringified discover response ID");
|
||||
client.cancel().await.expect("cancel client");
|
||||
server_task.await.expect("server task");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn high_level_server_accepts_discover_startup_without_initialize() {
|
||||
let (server_transport, client_transport) = tokio::io::duplex(4096);
|
||||
|
|
|
|||
Loading…
Reference in a new issue