fix: Return empty prompt completion result when prompt has no arguments (#934)

Recent changes don't coerce null completion arguments to empty lists so we have
to check for null when handling prompt completions.

Resolves #932

Signed-off-by: Dariusz Jędrzejczyk <2554306+chemicL@users.noreply.github.com>
This commit is contained in:
Dariusz Jędrzejczyk 2026-04-24 14:05:12 +02:00 • committed by GitHub
commit 5895b2edd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 45 additions and 19 deletions

View file

@ -999,12 +999,9 @@ public class McpAsyncServer {
.message("Prompt not found: " + promptReference.name())
.build());
}
if (!promptSpec.prompt()
.arguments()
.stream()
.filter(arg -> arg.name().equals(argumentName))
.findFirst()
.isPresent()) {
List<McpSchema.PromptArgument> arguments = promptSpec.prompt().arguments();
if (arguments == null
|| !arguments.stream().filter(arg -> arg.name().equals(argumentName)).findFirst().isPresent()) {
logger.warn("Argument not found: {} in prompt: {}", argumentName, promptReference.name());

View file

@ -744,12 +744,9 @@ public class McpStatelessAsyncServer {
.message("Prompt not found: " + promptReference.name())
.build());
}
if (!promptSpec.prompt()
.arguments()
.stream()
.filter(arg -> arg.name().equals(argumentName))
.findFirst()
.isPresent()) {
List<McpSchema.PromptArgument> arguments = promptSpec.prompt().arguments();
if (arguments == null
|| !arguments.stream().filter(arg -> arg.name().equals(argumentName)).findFirst().isPresent()) {
logger.warn("Argument not found: {} in prompt: {}", argumentName, promptReference.name());

View file

@ -184,10 +184,10 @@ class HttpServletStatelessIntegrationTests {
true // hasMore
));
AtomicReference<CompleteRequest> samplingRequest = new AtomicReference<>();
AtomicReference<CompleteRequest> completeRequest = new AtomicReference<>();
BiFunction<McpTransportContext, CompleteRequest, CompleteResult> completionHandler = (transportContext,
request) -> {
samplingRequest.set(request);
completeRequest.set(request);
return completionResponse;
};
@ -214,9 +214,9 @@ class HttpServletStatelessIntegrationTests {
assertThat(result).isNotNull();
assertThat(samplingRequest.get().argument().name()).isEqualTo("language");
assertThat(samplingRequest.get().argument().value()).isEqualTo("py");
assertThat(samplingRequest.get().ref().type()).isEqualTo(PromptReference.TYPE);
assertThat(completeRequest.get().argument().name()).isEqualTo("language");
assertThat(completeRequest.get().argument().value()).isEqualTo("py");
assertThat(completeRequest.get().ref().type()).isEqualTo(PromptReference.TYPE);
}
finally {
mcpServer.close();

View file

@ -13,11 +13,9 @@ import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.startup.Tomcat;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
@ -37,6 +35,9 @@ import io.modelcontextprotocol.spec.McpSchema.PromptReference;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpError;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for completion functionality with context support.
*
@ -324,4 +325,35 @@ class McpCompletionTests {
mcpServer.close();
}
@Test
void testPromptWithoutArgumentsCompletionForArgument() {
BiFunction<McpSyncServerExchange, CompleteRequest, CompleteResult> completionHandler = (exchange,
request) -> new CompleteResult(new CompleteResult.CompleteCompletion(List.of("test"), 1, false));
McpSchema.Prompt prompt = new Prompt("test-prompt", "this is a test prompt", null);
var mcpServer = McpServer.sync(mcpServerTransportProvider)
.capabilities(ServerCapabilities.builder().completions().build())
.prompts(new McpServerFeatures.SyncPromptSpecification(prompt,
(mcpSyncServerExchange, getPromptRequest) -> null))
.completions(new McpServerFeatures.SyncCompletionSpecification(
new PromptReference(PromptReference.TYPE, "test-prompt"), completionHandler))
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
.build()) {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
// try completing an argument knowing that the prompt is not parameterized
CompleteRequest request = new CompleteRequest(new PromptReference(PromptReference.TYPE, "test-prompt"),
new CompleteRequest.CompleteArgument("arg", "val"));
CompleteResult completeResult = mcpClient.completeCompletion(request);
assertThat(completeResult.completion().values()).isEmpty();
}
mcpServer.close();
}
}