Skip to content

Commit db4a3ed

Browse files
committed
fix: serialize ElicitRequest mode discriminator only once
ElicitRequest declares its subtype discriminator with @JsonTypeInfo(..., property = "mode"), so Jackson already writes the mode property during serialization. ElicitFormRequest and ElicitUrlRequest also annotated their mode() accessor with @JsonProperty("mode"), so mode was written twice, producing a duplicate JSON key. Mark the overridden mode() accessors @JsonIgnore, mirroring the existing fix on Content.type(). Deserialization is unaffected (it uses the discriminator and the @JsonCreator factories). Adds ElicitRequestJsonTests, mirroring ContentJsonTests.
1 parent ff78191 commit db4a3ed

2 files changed

Lines changed: 75 additions & 2 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4805,7 +4805,7 @@ public record ElicitFormRequest( // @formatter:off
48054805
}
48064806

48074807
@Override
4808-
@JsonProperty("mode")
4808+
@JsonIgnore
48094809
public String mode() {
48104810
return MODE;
48114811
}
@@ -4931,7 +4931,7 @@ public record ElicitUrlRequest( // @formatter:off
49314931
}
49324932

49334933
@Override
4934-
@JsonProperty("mode")
4934+
@JsonIgnore
49354935
public String mode() {
49364936
return MODE;
49374937
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2026 - 2026 the original author or authors.
3+
*/
4+
5+
package io.modelcontextprotocol.spec;
6+
7+
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
8+
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
import java.io.IOException;
12+
import java.util.Map;
13+
14+
import io.modelcontextprotocol.json.McpJsonMapper;
15+
import org.junit.jupiter.api.Test;
16+
17+
/**
18+
* Verifies that every {@link McpSchema.ElicitRequest} subtype serializes with exactly one
19+
* {@code mode} property (regression guard for the {@code @JsonIgnore} on the overridden
20+
* {@code mode()} accessors, mirroring {@link ContentJsonTests}).
21+
*/
22+
class ElicitRequestJsonTests {
23+
24+
private final McpJsonMapper mapper = JSON_MAPPER;
25+
26+
@Test
27+
void formRequestHasExactlyOneModeProperty() throws IOException {
28+
McpSchema.ElicitFormRequest request = McpSchema.ElicitFormRequest.builder("msg", Map.of("type", "object"))
29+
.build();
30+
String json = mapper.writeValueAsString(request);
31+
32+
assertExactlyOneModeProperty(json);
33+
assertThatJson(json).node("mode").isEqualTo("form");
34+
}
35+
36+
@Test
37+
void urlRequestHasExactlyOneModeProperty() throws IOException {
38+
McpSchema.ElicitUrlRequest request = McpSchema.ElicitUrlRequest.builder("msg", "https://example.com", "eid")
39+
.build();
40+
String json = mapper.writeValueAsString(request);
41+
42+
assertExactlyOneModeProperty(json);
43+
assertThatJson(json).node("mode").isEqualTo("url");
44+
}
45+
46+
@Test
47+
void formRequestRoundTrip() throws IOException {
48+
McpSchema.ElicitFormRequest original = McpSchema.ElicitFormRequest.builder("msg", Map.of("type", "object"))
49+
.build();
50+
String json = mapper.writeValueAsString(original);
51+
52+
McpSchema.ElicitRequest decoded = mapper.readValue(json, McpSchema.ElicitRequest.class);
53+
assertThat(decoded).isInstanceOf(McpSchema.ElicitFormRequest.class);
54+
assertThat(decoded.mode()).isEqualTo("form");
55+
}
56+
57+
@Test
58+
void urlRequestRoundTrip() throws IOException {
59+
McpSchema.ElicitUrlRequest original = McpSchema.ElicitUrlRequest.builder("msg", "https://example.com", "eid")
60+
.build();
61+
String json = mapper.writeValueAsString(original);
62+
63+
McpSchema.ElicitRequest decoded = mapper.readValue(json, McpSchema.ElicitRequest.class);
64+
assertThat(decoded).isInstanceOf(McpSchema.ElicitUrlRequest.class);
65+
assertThat(decoded.mode()).isEqualTo("url");
66+
}
67+
68+
private static void assertExactlyOneModeProperty(String json) {
69+
long count = java.util.Arrays.stream(json.split("\"mode\"")).count() - 1;
70+
assertThat(count).as("'mode' property must appear exactly once in: %s", json).isEqualTo(1);
71+
}
72+
73+
}

0 commit comments

Comments
 (0)