Forge
csharp4405de34
1using System.Text.Json;
2using System.Text.Json.Serialization;
3
4namespace AgentClientProtocol;
5
6[JsonConverter(typeof(JsonRpcMessageJsonConverter))]
7public abstract record JsonRpcMessage
8{
9 [JsonPropertyName("jsonrpc")]
10 public string JsonRpc { get; init; } = "2.0";
11}
12
13public record JsonRpcRequest : JsonRpcMessage
14{
15 [JsonPropertyName("id")]
16 public RequestId Id { get; set; }
17
18 [JsonPropertyName("method")]
19 public required string Method { get; init; }
20
21 [JsonPropertyName("params")]
22 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
23 public JsonElement? Params { get; init; }
24}
25
26public record JsonRpcResponse : JsonRpcMessage
27{
28 [JsonPropertyName("id")]
29 public required RequestId Id { get; init; }
30
31 [JsonPropertyName("result")]
32 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
33 public JsonElement? Result { get; init; }
34
35 [JsonPropertyName("error")]
36 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
37 public JsonRpcError? Error { get; init; }
38}
39
40public record JsonRpcNotification : JsonRpcMessage
41{
42 [JsonPropertyName("method")]
43 public required string Method { get; init; }
44
45 [JsonPropertyName("params")]
46 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
47 public JsonElement? Params { get; init; }
48}
49
50public record JsonRpcError
51{
52 [JsonPropertyName("code")]
53 public required int Code { get; init; }
54
55 [JsonPropertyName("message")]
56 public required string Message { get; init; }
57
58 [JsonPropertyName("data")]
59 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
60 public JsonElement? Data { get; init; }
61}
62
63public sealed class JsonRpcMessageJsonConverter : JsonConverter<JsonRpcMessage>
64{
65 public override JsonRpcMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
66 {
67 if (reader.TokenType != JsonTokenType.StartObject)
68 {
69 throw new JsonException("Expected StartObject token");
70 }
71
72 using var doc = JsonDocument.ParseValue(ref reader);
73 var root = doc.RootElement;
74
75 if (!root.TryGetProperty("jsonrpc", out var versionProperty))
76 {
77 throw new JsonException("Missing jsonrpc version");
78 }
79
80 if (versionProperty.GetString() != "2.0")
81 {
82 throw new JsonException("Invalidg jsonrpc version");
83 }
84
85 var hasId = root.TryGetProperty("id", out _);
86 var hasMethod = root.TryGetProperty("method", out _);
87
88 var rawText = root.GetRawText();
89
90 if (hasId && !hasMethod)
91 {
92 if (root.TryGetProperty("error", out _) || root.TryGetProperty("result", out _))
93 {
94 return JsonSerializer.Deserialize(rawText, options.GetTypeInfo<JsonRpcResponse>());
95 }
96
97 throw new JsonException("Response must have either result or error");
98 }
99
100 if (hasMethod && !hasId)
101 {
102 return JsonSerializer.Deserialize(rawText, options.GetTypeInfo<JsonRpcNotification>());
103 }
104
105 if (hasMethod && hasId)
106 {
107 return JsonSerializer.Deserialize(rawText, options.GetTypeInfo<JsonRpcRequest>());
108 }
109
110 throw new JsonException("Invalid JSON-RPC message format");
111 }
112
113 public override void Write(Utf8JsonWriter writer, JsonRpcMessage value, JsonSerializerOptions options)
114 {
115 switch (value)
116 {
117 case JsonRpcRequest request:
118 JsonSerializer.Serialize(writer, request, options.GetTypeInfo<JsonRpcRequest>());
119 break;
120 case JsonRpcNotification notification:
121 JsonSerializer.Serialize(writer, notification, options.GetTypeInfo<JsonRpcNotification>());
122 break;
123 case JsonRpcResponse response:
124 JsonSerializer.Serialize(writer, response, options.GetTypeInfo<JsonRpcResponse>());
125 break;
126 default:
127 throw new JsonException($"Unknown JSON-RPC message type: {value.GetType()}");
128 }
129 }
130}
View only · write via MCP/CIDE