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:
parent
4c8596306d
commit
5895b2edd2
4 changed files with 45 additions and 19 deletions
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue