Skip to content

Commit 8b3ca5a

Browse files
committed
Aglign stateless completionCompleteRequestHandler
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
1 parent 837a958 commit 8b3ca5a

File tree

1 file changed

+68
-16
lines changed

1 file changed

+68
-16
lines changed

mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import io.modelcontextprotocol.spec.McpError;
1313
import io.modelcontextprotocol.spec.McpSchema;
1414
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
15+
import io.modelcontextprotocol.spec.McpSchema.CompleteResult.CompleteCompletion;
16+
import io.modelcontextprotocol.spec.McpSchema.ErrorCodes;
1517
import io.modelcontextprotocol.spec.McpSchema.JSONRPCResponse;
1618
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
1719
import io.modelcontextprotocol.spec.McpSchema.ResourceReference;
@@ -667,55 +669,105 @@ private McpStatelessRequestHandler<McpSchema.GetPromptResult> promptsGetRequestH
667669
};
668670
}
669671

672+
private static final Mono<McpSchema.CompleteResult> EMPTY_COMPLETION_RESULT = Mono
673+
.just(new McpSchema.CompleteResult(new CompleteCompletion(List.of(), 0, false)));
674+
670675
private McpStatelessRequestHandler<McpSchema.CompleteResult> completionCompleteRequestHandler() {
671676
return (ctx, params) -> {
672677
McpSchema.CompleteRequest request = parseCompletionParams(params);
673678

674679
if (request.ref() == null) {
675-
return Mono.error(new McpError("ref must not be null"));
680+
return Mono.error(
681+
McpError.builder(ErrorCodes.INVALID_PARAMS).message("Completion ref must not be null").build());
676682
}
677683

678684
if (request.ref().type() == null) {
679-
return Mono.error(new McpError("type must not be null"));
685+
return Mono.error(McpError.builder(ErrorCodes.INVALID_PARAMS)
686+
.message("Completion ref type must not be null")
687+
.build());
680688
}
681689

682690
String type = request.ref().type();
683691

684692
String argumentName = request.argument().name();
685693

686-
// check if the referenced resource exists
694+
// Check if valid a Prompt exists for this completion request
687695
if (type.equals(PromptReference.TYPE)
688696
&& request.ref() instanceof McpSchema.PromptReference promptReference) {
697+
689698
McpStatelessServerFeatures.AsyncPromptSpecification promptSpec = this.prompts
690699
.get(promptReference.name());
691700
if (promptSpec == null) {
692-
return Mono.error(new McpError("Prompt not found: " + promptReference.name()));
701+
return Mono.error(McpError.builder(ErrorCodes.INVALID_PARAMS)
702+
.message("Prompt not found: " + promptReference.name())
703+
.build());
693704
}
694-
if (promptSpec.prompt().arguments().stream().noneMatch(arg -> arg.name().equals(argumentName))) {
705+
if (!promptSpec.prompt()
706+
.arguments()
707+
.stream()
708+
.filter(arg -> arg.name().equals(argumentName))
709+
.findFirst()
710+
.isPresent()) {
711+
712+
logger.warn("Argument not found: {} in prompt: {}", argumentName, promptReference.name());
695713

696-
return Mono.error(new McpError("Argument not found: " + argumentName));
714+
return EMPTY_COMPLETION_RESULT;
697715
}
698716
}
699717

718+
// Check if valid Resource or ResourceTemplate exists for this completion
719+
// request
700720
if (type.equals(ResourceReference.TYPE)
701721
&& request.ref() instanceof McpSchema.ResourceReference resourceReference) {
702-
McpStatelessServerFeatures.AsyncResourceSpecification resourceSpec = resources
703-
.get(resourceReference.uri());
704-
if (resourceSpec == null) {
705-
return Mono.error(RESOURCE_NOT_FOUND.apply(resourceReference.uri()));
706-
}
707-
if (!uriTemplateManagerFactory.create(resourceSpec.resource().uri())
708-
.getVariableNames()
709-
.contains(argumentName)) {
710-
return Mono.error(new McpError("Argument not found: " + argumentName));
722+
723+
var uriTemplateManager = uriTemplateManagerFactory.create(resourceReference.uri());
724+
725+
if (!uriTemplateManager.isUriTemplate(resourceReference.uri())) {
726+
// Attempting to autocomplete a fixed resource URI is not an error in
727+
// the spec (but probably should be).
728+
return EMPTY_COMPLETION_RESULT;
711729
}
712730

731+
McpStatelessServerFeatures.AsyncResourceSpecification resourceSpec = this
732+
.findResourceSpecification(resourceReference.uri())
733+
.orElse(null);
734+
735+
if (resourceSpec != null) {
736+
if (!uriTemplateManagerFactory.create(resourceSpec.resource().uri())
737+
.getVariableNames()
738+
.contains(argumentName)) {
739+
740+
return Mono.error(McpError.builder(ErrorCodes.INVALID_PARAMS)
741+
.message("Argument not found: " + argumentName + " in resource: " + resourceReference.uri())
742+
.build());
743+
}
744+
}
745+
else {
746+
var templateSpec = this.findResourceTemplateSpecification(resourceReference.uri()).orElse(null);
747+
if (templateSpec != null) {
748+
749+
if (!uriTemplateManagerFactory.create(templateSpec.resourceTemplate().uriTemplate())
750+
.getVariableNames()
751+
.contains(argumentName)) {
752+
753+
return Mono.error(McpError.builder(ErrorCodes.INVALID_PARAMS)
754+
.message("Argument not found: " + argumentName + " in resource template: "
755+
+ resourceReference.uri())
756+
.build());
757+
}
758+
}
759+
else {
760+
return Mono.error(RESOURCE_NOT_FOUND.apply(resourceReference.uri()));
761+
}
762+
}
713763
}
714764

715765
McpStatelessServerFeatures.AsyncCompletionSpecification specification = this.completions.get(request.ref());
716766

717767
if (specification == null) {
718-
return Mono.error(new McpError("AsyncCompletionSpecification not found: " + request.ref()));
768+
return Mono.error(McpError.builder(ErrorCodes.INVALID_PARAMS)
769+
.message("AsyncCompletionSpecification not found: " + request.ref())
770+
.build());
719771
}
720772

721773
return specification.completionHandler().apply(ctx, request);

0 commit comments

Comments
 (0)