18
18
import com .fasterxml .jackson .annotation .JsonSubTypes ;
19
19
import com .fasterxml .jackson .annotation .JsonTypeInfo ;
20
20
import com .fasterxml .jackson .annotation .JsonTypeInfo .As ;
21
+ import com .fasterxml .jackson .core .JsonGenerator ;
22
+ import com .fasterxml .jackson .core .JsonParser ;
23
+ import com .fasterxml .jackson .core .JsonToken ;
21
24
import com .fasterxml .jackson .core .type .TypeReference ;
25
+ import com .fasterxml .jackson .databind .DeserializationContext ;
26
+ import com .fasterxml .jackson .databind .JsonDeserializer ;
27
+ import com .fasterxml .jackson .databind .JsonMappingException ;
28
+ import com .fasterxml .jackson .databind .JsonSerializer ;
22
29
import com .fasterxml .jackson .databind .ObjectMapper ;
30
+ import com .fasterxml .jackson .databind .SerializerProvider ;
31
+ import com .fasterxml .jackson .databind .annotation .JsonDeserialize ;
32
+ import com .fasterxml .jackson .databind .annotation .JsonSerialize ;
33
+
23
34
import io .modelcontextprotocol .util .Assert ;
24
35
import org .slf4j .Logger ;
25
36
import org .slf4j .LoggerFactory ;
26
37
38
+ import static java .util .Objects .requireNonNull ;
39
+
27
40
/**
28
41
* Based on the <a href="http://www.jsonrpc.org/specification">JSON-RPC 2.0
29
42
* specification</a> and the <a href=
32
45
*
33
46
* @author Christian Tzolov
34
47
* @author Luca Chang
48
+ * @author Zachary German
35
49
*/
36
50
public final class McpSchema {
37
51
@@ -142,6 +156,103 @@ public static final class ErrorCodes {
142
156
143
157
}
144
158
159
+ /**
160
+ * MCP ID wrapper: MUST be non-null String or Number.
161
+ */
162
+ @ JsonSerialize (using = McpId .Serializer .class )
163
+ @ JsonDeserialize (using = McpId .Deserializer .class )
164
+ public static final class McpId {
165
+
166
+ private final Object value ;
167
+
168
+ public McpId (String value ) {
169
+ this .value = requireNonNull (value , "'id' must not be null" );
170
+ }
171
+
172
+ public McpId (Number value ) {
173
+ this .value = requireNonNull (value , "'id' must not be null" );
174
+ }
175
+
176
+ public static McpId of (Object raw ) {
177
+ if (raw instanceof String s )
178
+ return new McpId (s );
179
+ if (raw instanceof Number n )
180
+ return new McpId (n );
181
+ throw new IllegalArgumentException ("MCP 'id' must be String or Number" );
182
+ }
183
+
184
+ public boolean isString () {
185
+ return value instanceof String ;
186
+ }
187
+
188
+ public boolean isNumber () {
189
+ return value instanceof Number ;
190
+ }
191
+
192
+ public String asString () {
193
+ return (String ) value ;
194
+ }
195
+
196
+ public Number asNumber () {
197
+ return (Number ) value ;
198
+ }
199
+
200
+ public Object raw () {
201
+ return value ;
202
+ }
203
+
204
+ @ Override
205
+ public String toString () {
206
+ return String .valueOf (value );
207
+ }
208
+
209
+ @ Override
210
+ public boolean equals (Object o ) {
211
+ if (this == o )
212
+ return true ;
213
+ if (o == null || getClass () != o .getClass ())
214
+ return false ;
215
+ McpId mcpId = (McpId ) o ;
216
+ return value .equals (mcpId .value );
217
+ }
218
+
219
+ @ Override
220
+ public int hashCode () {
221
+ return value .hashCode ();
222
+ }
223
+
224
+ public static class Deserializer extends JsonDeserializer <McpId > {
225
+
226
+ @ Override
227
+ public McpId deserialize (JsonParser p , DeserializationContext ctxt ) throws IOException {
228
+ JsonToken t = p .getCurrentToken ();
229
+ if (t == JsonToken .VALUE_STRING ) {
230
+ return new McpId (p .getText ());
231
+ }
232
+ else if (t .isNumeric ()) {
233
+ return new McpId (p .getNumberValue ());
234
+ }
235
+ throw JsonMappingException .from (p , "MCP 'id' must be a non-null String or Number" );
236
+ }
237
+
238
+ }
239
+
240
+ public static class Serializer extends JsonSerializer <McpId > {
241
+
242
+ @ Override
243
+ public void serialize (McpId id , JsonGenerator gen , SerializerProvider serializers ) throws IOException {
244
+ if (id .isString ()) {
245
+ gen .writeString (id .asString ());
246
+ }
247
+ else {
248
+ gen .writeNumber (id .asNumber ().toString ());
249
+ }
250
+ }
251
+
252
+ }
253
+
254
+ }
255
+
145
256
public sealed interface Request permits InitializeRequest , CallToolRequest , CreateMessageRequest , ElicitRequest ,
146
257
CompleteRequest , GetPromptRequest , PaginatedRequest , ReadResourceRequest {
147
258
@@ -205,7 +316,7 @@ public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCNotificati
205
316
public record JSONRPCRequest ( // @formatter:off
206
317
@ JsonProperty ("jsonrpc" ) String jsonrpc ,
207
318
@ JsonProperty ("method" ) String method ,
208
- @ JsonProperty ("id" ) Object id ,
319
+ @ JsonProperty ("id" ) McpId id ,
209
320
@ JsonProperty ("params" ) Object params ) implements JSONRPCMessage {
210
321
} // @formatter:on
211
322
@@ -223,7 +334,7 @@ public record JSONRPCNotification( // @formatter:off
223
334
// @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
224
335
public record JSONRPCResponse ( // @formatter:off
225
336
@ JsonProperty ("jsonrpc" ) String jsonrpc ,
226
- @ JsonProperty ("id" ) Object id ,
337
+ @ JsonProperty ("id" ) McpId id ,
227
338
@ JsonProperty ("result" ) Object result ,
228
339
@ JsonProperty ("error" ) JSONRPCError error ) implements JSONRPCMessage {
229
340
0 commit comments