Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,11 @@ private JsonRpcResponse handleInitialize(JsonRpcRequest req) {
String pv = null;
if (maybeVersion != null) {
var protocolVersion = ProtocolVersion.version(maybeVersion.asString());
if (!(protocolVersion instanceof ProtocolVersion.UnknownVersion)) {
if (protocolVersion instanceof ProtocolVersion.UnknownVersion) {
// Per the MCP spec, a server that does not support the requested protocol
// version must respond with the latest version it supports.
pv = ProtocolVersion.latestVersion().identifier();
} else {
pv = protocolVersion.identifier();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package software.amazon.smithy.java.mcp.server;

import java.util.Collections;
import java.util.List;
import software.amazon.smithy.utils.SmithyUnstableApi;

@SmithyUnstableApi
Expand Down Expand Up @@ -49,6 +51,20 @@ private UnknownVersion(String identifier) {
}
}

/**
* Holder defers initialization until first use so the version subclasses are fully loaded
* first — a plain static field on this class would read a still-null INSTANCE whenever a
* subclass is the first member of the hierarchy to be initialized.
*/
private static final class SupportedVersions {
private static final List<ProtocolVersion> ALL = List.of(
v2024_11_05.INSTANCE,
v2025_03_26.INSTANCE,
v2025_06_18.INSTANCE,
v2025_11_25.INSTANCE);
private static final ProtocolVersion LATEST = Collections.max(ALL);
}

private final String identifier;

private ProtocolVersion(String identifier) {
Expand All @@ -72,17 +88,26 @@ public final int compareTo(ProtocolVersion o) {
}

public static ProtocolVersion version(String identifier) {
return switch (identifier) {
case null -> v2025_03_26.INSTANCE;
case "2024-11-05" -> v2024_11_05.INSTANCE;
case "2025-03-26" -> v2025_03_26.INSTANCE;
case "2025-06-18" -> v2025_06_18.INSTANCE;
case "2025-11-25" -> v2025_11_25.INSTANCE;
default -> new UnknownVersion(identifier);
};
if (identifier == null) {
return defaultVersion();
}
for (var version : SupportedVersions.ALL) {
if (version.identifier.equals(identifier)) {
return version;
}
}
return new UnknownVersion(identifier);
}

public static ProtocolVersion defaultVersion() {
return v2025_03_26.INSTANCE;
}

/**
* The most recent protocol version this server supports, derived from the supported-version
* registry.
*/
public static ProtocolVersion latestVersion() {
return SupportedVersions.LATEST;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,29 @@ public void initializeWithV2025_11_25ProtocolVersion() {
assertTrue(outputSchema.get("properties").asStringMap().containsKey("outputStr"));
}

@Test
public void initializeWithUnknownProtocolVersionAnswersLatestSupported() {
server = McpServer.builder()
.name("smithy-mcp-server")
.input(input)
.output(output)
.addService("test-mcp",
ProxyService.builder()
.service(ShapeId.from("smithy.test#TestService"))
.proxyEndpoint("http://localhost")
.model(MODEL)
.build())
.build();

server.start();

// Per the MCP spec, a server answers a request for a version it does not support with
// the latest version it supports — not the oldest.
write("initialize", Document.of(Map.of("protocolVersion", Document.of("2026-07-28"))));
var pv = read().getResult().getMember("protocolVersion").asString();
assertEquals("2025-11-25", pv);
}

@Test
public void noOutputSchemaWithUnsupportedProtocolVersion() {
server = McpServer.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ void defaultVersionIs2025_03_26() {
assertEquals("2025-03-26", ProtocolVersion.defaultVersion().identifier());
}

@Test
void latestVersionIs2025_11_25() {
assertInstanceOf(ProtocolVersion.v2025_11_25.class, ProtocolVersion.latestVersion());
assertEquals("2025-11-25", ProtocolVersion.latestVersion().identifier());
}

@Test
void compareToOrdersChronologically() {
assertTrue(ProtocolVersion.v2024_11_05.INSTANCE.compareTo(ProtocolVersion.v2025_03_26.INSTANCE) < 0);
Expand Down
Loading