From 672d98f3633db0ce7f6467438f73790e99205d9c Mon Sep 17 00:00:00 2001 From: Sainath Reddy Bobbala Date: Thu, 23 Apr 2026 23:18:21 +0000 Subject: [PATCH] fix: guard against null Prompt.arguments() in completion handlers The 2.0 forward-compat refactor (#972) changed Prompt constructors to stop coercing null arguments to an empty list, but the completion validation code in McpAsyncServer and McpStatelessAsyncServer still called .arguments().stream() without a null guard, causing a NullPointerException when a Prompt with null arguments receives a completion/complete request. Additionally, MIGRATION-2.0.md references Prompt.withDefaults() as the migration path for callers that relied on the old null-to-empty-list coercion, but this factory method was never implemented. This commit: - Adds a null check before .arguments().stream() in both McpAsyncServer and McpStatelessAsyncServer - Adds Prompt.withDefaults(name, description, arguments) factory that coerces null to an empty list, matching the 1.x behaviour --- .../server/McpAsyncServer.java | 5 ++--- .../server/McpStatelessAsyncServer.java | 5 ++--- .../modelcontextprotocol/spec/McpSchema.java | 19 +++++++++++++++++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index e5f57bad8..03134842a 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -999,9 +999,8 @@ private McpRequestHandler completionCompleteRequestHan .message("Prompt not found: " + promptReference.name()) .build()); } - if (!promptSpec.prompt() - .arguments() - .stream() + List promptArgs = promptSpec.prompt().arguments(); + if (promptArgs != null && !promptArgs.stream() .filter(arg -> arg.name().equals(argumentName)) .findFirst() .isPresent()) { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java index 18fc85786..d784e6634 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java @@ -744,9 +744,8 @@ private McpStatelessRequestHandler completionCompleteR .message("Prompt not found: " + promptReference.name()) .build()); } - if (!promptSpec.prompt() - .arguments() - .stream() + List promptArgs = promptSpec.prompt().arguments(); + if (promptArgs != null && !promptArgs.stream() .filter(arg -> arg.name().equals(argumentName)) .findFirst() .isPresent()) { diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index a3ed2dbde..33d916537 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -10,6 +10,9 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -17,11 +20,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; + import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.TypeRef; import io.modelcontextprotocol.util.Assert; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Based on the JSON-RPC 2.0 @@ -1177,6 +1179,19 @@ public Prompt(String name, String description, List arguments) { public Prompt(String name, String title, String description, List arguments) { this(name, title, description, arguments, null); } + + /** + * Creates a Prompt that coerces {@code null} arguments to an empty list, + * preserving the 1.x behaviour. Use this factory when callers expect + * {@code prompt.arguments()} to never return {@code null}. + * @param name the prompt name + * @param description an optional description + * @param arguments the argument list, or {@code null} to default to an empty list + * @return a new Prompt with non-null arguments + */ + public static Prompt withDefaults(String name, String description, List arguments) { + return new Prompt(name, null, description, arguments != null ? arguments : new ArrayList<>(), null); + } } /**