@@ -4,8 +4,11 @@ mod common;
44
55use common:: handlers:: TestServer ;
66use rmcp:: {
7- ServiceExt ,
8- model:: { ClientJsonRpcMessage , ServerJsonRpcMessage , ServerResult } ,
7+ ServerHandler , ServiceExt ,
8+ model:: {
9+ ClientJsonRpcMessage , ProtocolVersion , ServerCapabilities , ServerInfo ,
10+ ServerJsonRpcMessage , ServerResult ,
11+ } ,
912 transport:: { IntoTransport , Transport } ,
1013} ;
1114
@@ -220,6 +223,82 @@ async fn server_init_buffers_request_before_initialized() {
220223 result. unwrap ( ) . cancel ( ) . await . unwrap ( ) ;
221224}
222225
226+ fn init_request_with_version ( v : & str ) -> ClientJsonRpcMessage {
227+ msg ( & format ! (
228+ r#"{{
229+ "jsonrpc": "2.0",
230+ "id": 1,
231+ "method": "initialize",
232+ "params": {{
233+ "protocolVersion": "{v}",
234+ "capabilities": {{}},
235+ "clientInfo": {{ "name": "test-client", "version": "0.0.1" }}
236+ }}
237+ }}"#
238+ ) )
239+ }
240+
241+ async fn negotiate_version < H > ( handler : H , client_version : & str ) -> ProtocolVersion
242+ where
243+ H : ServerHandler + ' static ,
244+ {
245+ let ( server_transport, client_transport) = tokio:: io:: duplex ( 4096 ) ;
246+ let _server = tokio:: spawn ( async move { handler. serve ( server_transport) . await } ) ;
247+ let mut client = IntoTransport :: < rmcp:: RoleClient , _ , _ > :: into_transport ( client_transport) ;
248+
249+ client
250+ . send ( init_request_with_version ( client_version) )
251+ . await
252+ . unwrap ( ) ;
253+ let response = client. receive ( ) . await . unwrap ( ) ;
254+ let ServerJsonRpcMessage :: Response ( r) = response else {
255+ panic ! ( "expected initialize response, got {response:?}" ) ;
256+ } ;
257+ let ServerResult :: InitializeResult ( init) = r. result else {
258+ panic ! ( "expected InitializeResult" ) ;
259+ } ;
260+ init. protocol_version
261+ }
262+
263+ #[ tokio:: test]
264+ async fn server_echoes_client_protocol_version_when_known_old ( ) {
265+ let negotiated = negotiate_version ( TestServer :: new ( ) , "2024-11-05" ) . await ;
266+ assert_eq ! ( negotiated, ProtocolVersion :: V_2024_11_05 ) ;
267+ }
268+
269+ #[ tokio:: test]
270+ async fn server_echoes_client_protocol_version_when_latest ( ) {
271+ let negotiated = negotiate_version ( TestServer :: new ( ) , "2025-11-25" ) . await ;
272+ assert_eq ! ( negotiated, ProtocolVersion :: LATEST ) ;
273+ }
274+
275+ #[ tokio:: test]
276+ async fn server_falls_back_when_client_protocol_version_unknown ( ) {
277+ let negotiated = negotiate_version ( TestServer :: new ( ) , "2099-99-99" ) . await ;
278+ assert_eq ! ( negotiated, ProtocolVersion :: LATEST ) ;
279+ }
280+
281+ struct PinnedServer ;
282+
283+ impl ServerHandler for PinnedServer {
284+ fn get_info ( & self ) -> ServerInfo {
285+ ServerInfo :: new ( ServerCapabilities :: builder ( ) . build ( ) )
286+ . with_protocol_version ( ProtocolVersion :: V_2025_06_18 )
287+ }
288+ }
289+
290+ #[ tokio:: test]
291+ async fn server_pinned_version_does_not_override_known_client_request ( ) {
292+ let negotiated = negotiate_version ( PinnedServer , "2025-11-25" ) . await ;
293+ assert_eq ! ( negotiated, ProtocolVersion :: LATEST ) ;
294+ }
295+
296+ #[ tokio:: test]
297+ async fn server_pinned_version_used_as_fallback_for_unknown_client_request ( ) {
298+ let negotiated = negotiate_version ( PinnedServer , "2099-99-99" ) . await ;
299+ assert_eq ! ( negotiated, ProtocolVersion :: V_2025_06_18 ) ;
300+ }
301+
223302// Server buffers multiple requests before initialized and processes them in order.
224303#[ tokio:: test]
225304async fn server_init_buffers_multiple_requests_before_initialized ( ) {
0 commit comments