@@ -693,13 +693,41 @@ impl Default for ClientInfo {
693693 }
694694}
695695
696+ /// A URL pointing to an icon resource or a base64-encoded data URI.
697+ ///
698+ /// Clients that support rendering icons MUST support at least the following MIME types:
699+ /// - image/png - PNG images (safe, universal compatibility)
700+ /// - image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)
701+ ///
702+ /// Clients that support rendering icons SHOULD also support:
703+ /// - image/svg+xml - SVG images (scalable but requires security precautions)
704+ /// - image/webp - WebP images (modern, efficient format)
696705#[ derive( Debug , Serialize , Deserialize , Clone , PartialEq ) ]
706+ #[ serde( rename_all = "camelCase" ) ]
707+ #[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
708+ pub struct Icon {
709+ /// A standard URI pointing to an icon resource
710+ pub src : String ,
711+ /// Optional override if the server's MIME type is missing or generic
712+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
713+ pub mime_type : Option < String > ,
714+ /// Size specification (e.g., "48x48", "any" for SVG, or "48x48 96x96")
715+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
716+ pub sizes : Option < String > ,
717+ }
718+
719+ #[ derive( Debug , Serialize , Deserialize , Clone , PartialEq ) ]
720+ #[ serde( rename_all = "camelCase" ) ]
697721#[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
698722pub struct Implementation {
699723 pub name : String ,
700724 #[ serde( skip_serializing_if = "Option::is_none" ) ]
701725 pub title : Option < String > ,
702726 pub version : String ,
727+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
728+ pub icons : Option < Vec < Icon > > ,
729+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
730+ pub website_url : Option < String > ,
703731}
704732
705733impl Default for Implementation {
@@ -714,6 +742,8 @@ impl Implementation {
714742 name : env ! ( "CARGO_CRATE_NAME" ) . to_owned ( ) ,
715743 title : None ,
716744 version : env ! ( "CARGO_PKG_VERSION" ) . to_owned ( ) ,
745+ icons : None ,
746+ website_url : None ,
717747 }
718748 }
719749}
@@ -1926,6 +1956,7 @@ mod tests {
19261956 assert_eq ! ( capabilities. tools. unwrap( ) . list_changed, Some ( true ) ) ;
19271957 assert_eq ! ( server_info. name, "ExampleServer" ) ;
19281958 assert_eq ! ( server_info. version, "1.0.0" ) ;
1959+ assert_eq ! ( server_info. icons, None ) ;
19291960 assert_eq ! ( instructions, None ) ;
19301961 }
19311962 other => panic ! ( "Expected InitializeResult, got {other:?}" ) ,
@@ -2021,4 +2052,108 @@ mod tests {
20212052 let v2 = ProtocolVersion :: V_2025_03_26 ;
20222053 assert ! ( v1 < v2) ;
20232054 }
2055+
2056+ #[ test]
2057+ fn test_icon_serialization ( ) {
2058+ let icon = Icon {
2059+ src : "https://example.com/icon.png" . to_string ( ) ,
2060+ mime_type : Some ( "image/png" . to_string ( ) ) ,
2061+ sizes : Some ( "48x48" . to_string ( ) ) ,
2062+ } ;
2063+
2064+ let json = serde_json:: to_value ( & icon) . unwrap ( ) ;
2065+ assert_eq ! ( json[ "src" ] , "https://example.com/icon.png" ) ;
2066+ assert_eq ! ( json[ "mimeType" ] , "image/png" ) ;
2067+ assert_eq ! ( json[ "sizes" ] , "48x48" ) ;
2068+
2069+ // Test deserialization
2070+ let deserialized: Icon = serde_json:: from_value ( json) . unwrap ( ) ;
2071+ assert_eq ! ( deserialized, icon) ;
2072+ }
2073+
2074+ #[ test]
2075+ fn test_icon_minimal ( ) {
2076+ let icon = Icon {
2077+ src : "data:image/svg+xml;base64,PHN2Zy8+" . to_string ( ) ,
2078+ mime_type : None ,
2079+ sizes : None ,
2080+ } ;
2081+
2082+ let json = serde_json:: to_value ( & icon) . unwrap ( ) ;
2083+ assert_eq ! ( json[ "src" ] , "data:image/svg+xml;base64,PHN2Zy8+" ) ;
2084+ assert ! ( json. get( "mimeType" ) . is_none( ) ) ;
2085+ assert ! ( json. get( "sizes" ) . is_none( ) ) ;
2086+ }
2087+
2088+ #[ test]
2089+ fn test_implementation_with_icons ( ) {
2090+ let implementation = Implementation {
2091+ name : "test-server" . to_string ( ) ,
2092+ title : Some ( "Test Server" . to_string ( ) ) ,
2093+ version : "1.0.0" . to_string ( ) ,
2094+ icons : Some ( vec ! [
2095+ Icon {
2096+ src: "https://example.com/icon.png" . to_string( ) ,
2097+ mime_type: Some ( "image/png" . to_string( ) ) ,
2098+ sizes: Some ( "48x48" . to_string( ) ) ,
2099+ } ,
2100+ Icon {
2101+ src: "https://example.com/icon.svg" . to_string( ) ,
2102+ mime_type: Some ( "image/svg+xml" . to_string( ) ) ,
2103+ sizes: Some ( "any" . to_string( ) ) ,
2104+ } ,
2105+ ] ) ,
2106+ website_url : Some ( "https://example.com" . to_string ( ) ) ,
2107+ } ;
2108+
2109+ let json = serde_json:: to_value ( & implementation) . unwrap ( ) ;
2110+ assert_eq ! ( json[ "name" ] , "test-server" ) ;
2111+ assert_eq ! ( json[ "websiteUrl" ] , "https://example.com" ) ;
2112+ assert ! ( json[ "icons" ] . is_array( ) ) ;
2113+ assert_eq ! ( json[ "icons" ] [ 0 ] [ "src" ] , "https://example.com/icon.png" ) ;
2114+ assert_eq ! ( json[ "icons" ] [ 1 ] [ "mimeType" ] , "image/svg+xml" ) ;
2115+ }
2116+
2117+ #[ test]
2118+ fn test_backward_compatibility ( ) {
2119+ // Test that old JSON without icons still deserializes correctly
2120+ let old_json = json ! ( {
2121+ "name" : "legacy-server" ,
2122+ "version" : "0.9.0"
2123+ } ) ;
2124+
2125+ let implementation: Implementation = serde_json:: from_value ( old_json) . unwrap ( ) ;
2126+ assert_eq ! ( implementation. name, "legacy-server" ) ;
2127+ assert_eq ! ( implementation. version, "0.9.0" ) ;
2128+ assert_eq ! ( implementation. icons, None ) ;
2129+ assert_eq ! ( implementation. website_url, None ) ;
2130+ }
2131+
2132+ #[ test]
2133+ fn test_initialize_with_icons ( ) {
2134+ let init_result = InitializeResult {
2135+ protocol_version : ProtocolVersion :: default ( ) ,
2136+ capabilities : ServerCapabilities :: default ( ) ,
2137+ server_info : Implementation {
2138+ name : "icon-server" . to_string ( ) ,
2139+ title : None ,
2140+ version : "2.0.0" . to_string ( ) ,
2141+ icons : Some ( vec ! [ Icon {
2142+ src: "https://example.com/server.png" . to_string( ) ,
2143+ mime_type: Some ( "image/png" . to_string( ) ) ,
2144+ sizes: None ,
2145+ } ] ) ,
2146+ website_url : Some ( "https://docs.example.com" . to_string ( ) ) ,
2147+ } ,
2148+ instructions : None ,
2149+ } ;
2150+
2151+ let json = serde_json:: to_value ( & init_result) . unwrap ( ) ;
2152+ assert ! ( json[ "serverInfo" ] [ "icons" ] . is_array( ) ) ;
2153+ assert_eq ! (
2154+ json[ "serverInfo" ] [ "icons" ] [ 0 ] [ "src" ] ,
2155+ "https://example.com/server.png"
2156+ ) ;
2157+ assert_eq ! ( json[ "serverInfo" ] [ "websiteUrl" ] , "https://docs.example.com" ) ;
2158+ }
20242159}
0 commit comments