11using ModelContextProtocol . Server ;
22using System . ComponentModel ;
33using System . Text . Json ;
4+ using System . Text . Json . Nodes ;
45using System . Text . Json . Serialization ;
56
67namespace ModelContextProtocol . Protocol ;
@@ -16,6 +17,8 @@ namespace ModelContextProtocol.Protocol;
1617[ JsonConverter ( typeof ( Converter ) ) ]
1718public abstract class JsonRpcMessage
1819{
20+ private const string JsonRpcPropertyName = "jsonrpc" ;
21+
1922 /// <summary>Prevent external derivations.</summary>
2023 private protected JsonRpcMessage ( )
2124 {
@@ -25,7 +28,7 @@ private protected JsonRpcMessage()
2528 /// Gets the JSON-RPC protocol version used.
2629 /// </summary>
2730 /// <inheritdoc />
28- [ JsonPropertyName ( "jsonrpc" ) ]
31+ [ JsonPropertyName ( JsonRpcPropertyName ) ]
2932 public string JsonRpc { get ; init ; } = "2.0" ;
3033
3134 /// <summary>
@@ -75,6 +78,48 @@ private protected JsonRpcMessage()
7578 [ EditorBrowsable ( EditorBrowsableState . Never ) ]
7679 public sealed class Converter : JsonConverter < JsonRpcMessage >
7780 {
81+ /// <summary>
82+ /// The union to deserialize.
83+ /// </summary>
84+ public struct Union
85+ {
86+ /// <summary>
87+ /// <see cref="JsonRpcMessage.JsonRpc"/>
88+ /// </summary>
89+ [ JsonPropertyName ( JsonRpcPropertyName ) ]
90+ public string JsonRpc { get ; set ; }
91+
92+ /// <summary>
93+ /// <see cref="JsonRpcMessageWithId.Id"/>
94+ /// </summary>
95+ [ JsonPropertyName ( JsonRpcMessageWithId . IdPropertyName ) ]
96+ public RequestId Id { get ; set ; }
97+
98+ /// <summary>
99+ /// <see cref="JsonRpcRequest.Method"/>
100+ /// </summary>
101+ [ JsonPropertyName ( JsonRpcRequest . MethodPropertyName ) ]
102+ public string ? Method { get ; set ; }
103+
104+ /// <summary>
105+ /// <see cref="JsonRpcRequest.Params"/>
106+ /// </summary>
107+ [ JsonPropertyName ( JsonRpcRequest . ParamsPropertyName ) ]
108+ public JsonNode ? Params { get ; set ; }
109+
110+ /// <summary>
111+ /// <see cref="JsonRpcError.Error"/>
112+ /// </summary>
113+ [ JsonPropertyName ( JsonRpcError . ErrorPropertyName ) ]
114+ public JsonRpcErrorDetail ? Error { get ; set ; }
115+
116+ /// <summary>
117+ /// <see cref="JsonRpcResponse.Result"/>
118+ /// </summary>
119+ [ JsonPropertyName ( JsonRpcResponse . ResultPropertyName ) ]
120+ public JsonNode ? Result { get ; set ; }
121+ }
122+
78123 /// <inheritdoc/>
79124 public override JsonRpcMessage ? Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
80125 {
@@ -83,51 +128,63 @@ public sealed class Converter : JsonConverter<JsonRpcMessage>
83128 throw new JsonException ( "Expected StartObject token" ) ;
84129 }
85130
86- using var doc = JsonDocument . ParseValue ( ref reader ) ;
87- var root = doc . RootElement ;
131+ var union = JsonSerializer . Deserialize ( ref reader , options . GetTypeInfo < Union > ( ) ) ;
88132
89133 // All JSON-RPC messages must have a jsonrpc property with value "2.0"
90- if ( ! root . TryGetProperty ( "jsonrpc" , out var versionProperty ) ||
91- versionProperty . GetString ( ) != "2.0" )
134+ if ( union . JsonRpc != "2.0" )
92135 {
93136 throw new JsonException ( "Invalid or missing jsonrpc version" ) ;
94137 }
95138
96- // Determine the message type based on the presence of id, method, and error properties
97- bool hasId = root . TryGetProperty ( "id" , out _ ) ;
98- bool hasMethod = root . TryGetProperty ( "method" , out _ ) ;
99- bool hasError = root . TryGetProperty ( "error" , out _ ) ;
100-
101- var rawText = root . GetRawText ( ) ;
102-
103139 // Messages with an id but no method are responses
104- if ( hasId && ! hasMethod )
140+ if ( union . Id . HasValue && union . Method is null )
105141 {
106142 // Messages with an error property are error responses
107- if ( hasError )
143+ if ( union . Error != null )
108144 {
109- return JsonSerializer . Deserialize ( rawText , options . GetTypeInfo < JsonRpcError > ( ) ) ;
145+ return new JsonRpcError
146+ {
147+ Id = union . Id ,
148+ Error = union . Error ,
149+ JsonRpc = union . JsonRpc ,
150+ } ;
110151 }
111152
112153 // Messages with a result property are success responses
113- if ( root . TryGetProperty ( "result" , out _ ) )
154+ if ( union . Result != null )
114155 {
115- return JsonSerializer . Deserialize ( rawText , options . GetTypeInfo < JsonRpcResponse > ( ) ) ;
156+ return new JsonRpcResponse
157+ {
158+ Id = union . Id ,
159+ Result = union . Result ,
160+ JsonRpc = union . JsonRpc ,
161+ } ;
116162 }
117163
118164 throw new JsonException ( "Response must have either result or error" ) ;
119165 }
120166
121167 // Messages with a method but no id are notifications
122- if ( hasMethod && ! hasId )
168+ if ( union . Method != null && ! union . Id . HasValue )
123169 {
124- return JsonSerializer . Deserialize ( rawText , options . GetTypeInfo < JsonRpcNotification > ( ) ) ;
170+ return new JsonRpcNotification
171+ {
172+ Method = union . Method ,
173+ JsonRpc = union . JsonRpc ,
174+ Params = union . Params ,
175+ } ;
125176 }
126177
127178 // Messages with both method and id are requests
128- if ( hasMethod && hasId )
179+ if ( union . Method != null && union . Id . HasValue )
129180 {
130- return JsonSerializer . Deserialize ( rawText , options . GetTypeInfo < JsonRpcRequest > ( ) ) ;
181+ return new JsonRpcRequest
182+ {
183+ Id = union . Id ,
184+ Method = union . Method ,
185+ JsonRpc = union . JsonRpc ,
186+ Params = union . Params ,
187+ } ;
131188 }
132189
133190 throw new JsonException ( "Invalid JSON-RPC message format" ) ;
0 commit comments