Skip to content

Commit 590e68c

Browse files
author
Zachary German
committed
Fixed backward-compatibility issues
1 parent 98f4bff commit 590e68c

File tree

3 files changed

+116
-14
lines changed

3 files changed

+116
-14
lines changed

mcp/src/main/java/io/modelcontextprotocol/server/transport/HttpServletSseServerTransportProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)
215215
PrintWriter writer = response.getWriter();
216216

217217
// Create a new session transport
218-
HttpServletMcpSessionTransport sessionTransport = new HttpServletMcpSessionTransport(sessionId, asyncContext,
219-
writer);
218+
HttpServletMcpSessionTransport sessionTransport = new HttpServletMcpSessionTransport(
219+
McpServerSession.LISTENING_TRANSPORT, asyncContext, writer);
220220

221221
// Create a new session using the session factory
222222
McpServerSession session = sessionFactory.create(sessionTransport);

mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,25 @@
1818
import com.fasterxml.jackson.annotation.JsonSubTypes;
1919
import com.fasterxml.jackson.annotation.JsonTypeInfo;
2020
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;
2124
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;
2229
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+
2334
import io.modelcontextprotocol.util.Assert;
2435
import org.slf4j.Logger;
2536
import org.slf4j.LoggerFactory;
2637

38+
import static java.util.Objects.requireNonNull;
39+
2740
/**
2841
* Based on the <a href="http://www.jsonrpc.org/specification">JSON-RPC 2.0
2942
* specification</a> and the <a href=
@@ -32,6 +45,7 @@
3245
*
3346
* @author Christian Tzolov
3447
* @author Luca Chang
48+
* @author Zachary German
3549
*/
3650
public final class McpSchema {
3751

@@ -142,6 +156,103 @@ public static final class ErrorCodes {
142156

143157
}
144158

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+
145256
public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, ElicitRequest,
146257
CompleteRequest, GetPromptRequest, PaginatedRequest, ReadResourceRequest {
147258

@@ -205,7 +316,7 @@ public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCNotificati
205316
public record JSONRPCRequest( // @formatter:off
206317
@JsonProperty("jsonrpc") String jsonrpc,
207318
@JsonProperty("method") String method,
208-
@JsonProperty("id") Object id,
319+
@JsonProperty("id") McpId id,
209320
@JsonProperty("params") Object params) implements JSONRPCMessage {
210321
} // @formatter:on
211322

@@ -223,7 +334,7 @@ public record JSONRPCNotification( // @formatter:off
223334
// @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
224335
public record JSONRPCResponse( // @formatter:off
225336
@JsonProperty("jsonrpc") String jsonrpc,
226-
@JsonProperty("id") Object id,
337+
@JsonProperty("id") McpId id,
227338
@JsonProperty("result") Object result,
228339
@JsonProperty("error") JSONRPCError error) implements JSONRPCMessage {
229340

mcp/src/main/java/io/modelcontextprotocol/spec/McpServerSession.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,7 @@ else if (message instanceof McpSchema.JSONRPCRequest request) {
282282
transportId = LISTENING_TRANSPORT;
283283
}
284284
else {
285-
if (request.id() instanceof String) {
286-
transportId = (String) request.id();
287-
}
288-
else if (request.id() instanceof Integer) {
289-
transportId = request.id().toString();
290-
}
291-
else {
292-
logger.error("Invalid request ID: {}", String.valueOf(request.id()));
293-
return Mono.error(new RuntimeException("Invalid request ID: " + String.valueOf(request.id())));
294-
}
285+
transportId = request.id().toString();
295286
}
296287
return handleIncomingRequest(request).onErrorResume(error -> {
297288
var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,

0 commit comments

Comments
 (0)