69
69
*
70
70
* @author Christian Tzolov
71
71
* @author Dariusz Jędrzejczyk
72
+ * @author Jihoon Kim
72
73
* @see McpServer
73
74
* @see McpSchema
74
75
* @see McpClientSession
@@ -269,6 +270,8 @@ private static class AsyncServerImpl extends McpAsyncServer {
269
270
// broadcasting loggingNotification.
270
271
private LoggingLevel minLoggingLevel = LoggingLevel .DEBUG ;
271
272
273
+ private final ConcurrentHashMap <McpSchema .CompleteReference , McpServerFeatures .AsyncCompletionSpecification > completions = new ConcurrentHashMap <>();
274
+
272
275
private List <String > protocolVersions = List .of (McpSchema .LATEST_PROTOCOL_VERSION );
273
276
274
277
AsyncServerImpl (McpServerTransportProvider mcpTransportProvider , ObjectMapper objectMapper ,
@@ -282,6 +285,7 @@ private static class AsyncServerImpl extends McpAsyncServer {
282
285
this .resources .putAll (features .resources ());
283
286
this .resourceTemplates .addAll (features .resourceTemplates ());
284
287
this .prompts .putAll (features .prompts ());
288
+ this .completions .putAll (features .completions ());
285
289
286
290
Map <String , McpServerSession .RequestHandler <?>> requestHandlers = new HashMap <>();
287
291
@@ -314,6 +318,11 @@ private static class AsyncServerImpl extends McpAsyncServer {
314
318
requestHandlers .put (McpSchema .METHOD_LOGGING_SET_LEVEL , setLoggerRequestHandler ());
315
319
}
316
320
321
+ // Add completion API handlers if the completion capability is enabled
322
+ if (this .serverCapabilities .completions () != null ) {
323
+ requestHandlers .put (McpSchema .METHOD_COMPLETION_COMPLETE , completionCompleteRequestHandler ());
324
+ }
325
+
317
326
Map <String , McpServerSession .NotificationHandler > notificationHandlers = new HashMap <>();
318
327
319
328
notificationHandlers .put (McpSchema .METHOD_NOTIFICATION_INITIALIZED , (exchange , params ) -> Mono .empty ());
@@ -706,6 +715,81 @@ private McpServerSession.RequestHandler<Object> setLoggerRequestHandler() {
706
715
};
707
716
}
708
717
718
+ private McpServerSession .RequestHandler <McpSchema .CompleteResult > completionCompleteRequestHandler () {
719
+ return (exchange , params ) -> {
720
+ McpSchema .CompleteRequest request = parseCompletionParams (params );
721
+
722
+ if (request .ref () == null ) {
723
+ return Mono .error (new McpError ("ref must not be null" ));
724
+ }
725
+
726
+ if (request .ref ().type () == null ) {
727
+ return Mono .error (new McpError ("type must not be null" ));
728
+ }
729
+
730
+ String type = request .ref ().type ();
731
+
732
+ // check if the referenced resource exists
733
+ if (type .equals ("ref/prompt" ) && request .ref () instanceof McpSchema .PromptReference promptReference ) {
734
+ McpServerFeatures .AsyncPromptSpecification prompt = this .prompts .get (promptReference .name ());
735
+ if (prompt == null ) {
736
+ return Mono .error (new McpError ("Prompt not found: " + promptReference .name ()));
737
+ }
738
+ }
739
+
740
+ if (type .equals ("ref/resource" )
741
+ && request .ref () instanceof McpSchema .ResourceReference resourceReference ) {
742
+ McpServerFeatures .AsyncResourceSpecification resource = this .resources .get (resourceReference .uri ());
743
+ if (resource == null ) {
744
+ return Mono .error (new McpError ("Resource not found: " + resourceReference .uri ()));
745
+ }
746
+ }
747
+
748
+ McpServerFeatures .AsyncCompletionSpecification specification = this .completions .get (request .ref ());
749
+
750
+ if (specification == null ) {
751
+ return Mono .error (new McpError ("AsyncCompletionSpecification not found: " + request .ref ()));
752
+ }
753
+
754
+ return specification .completionHandler ().apply (exchange , request );
755
+ };
756
+ }
757
+
758
+ /**
759
+ * Parses the raw JSON-RPC request parameters into a
760
+ * {@link McpSchema.CompleteRequest} object.
761
+ * <p>
762
+ * This method manually extracts the `ref` and `argument` fields from the input
763
+ * map, determines the correct reference type (either prompt or resource), and
764
+ * constructs a fully-typed {@code CompleteRequest} instance.
765
+ * @param object the raw request parameters, expected to be a Map containing "ref"
766
+ * and "argument" entries.
767
+ * @return a {@link McpSchema.CompleteRequest} representing the structured
768
+ * completion request.
769
+ * @throws IllegalArgumentException if the "ref" type is not recognized.
770
+ */
771
+ @ SuppressWarnings ("unchecked" )
772
+ private McpSchema .CompleteRequest parseCompletionParams (Object object ) {
773
+ Map <String , Object > params = (Map <String , Object >) object ;
774
+ Map <String , Object > refMap = (Map <String , Object >) params .get ("ref" );
775
+ Map <String , Object > argMap = (Map <String , Object >) params .get ("argument" );
776
+
777
+ String refType = (String ) refMap .get ("type" );
778
+
779
+ McpSchema .CompleteReference ref = switch (refType ) {
780
+ case "ref/prompt" -> new McpSchema .PromptReference (refType , (String ) refMap .get ("name" ));
781
+ case "ref/resource" -> new McpSchema .ResourceReference (refType , (String ) refMap .get ("uri" ));
782
+ default -> throw new IllegalArgumentException ("Invalid ref type: " + refType );
783
+ };
784
+
785
+ String argName = (String ) argMap .get ("name" );
786
+ String argValue = (String ) argMap .get ("value" );
787
+ McpSchema .CompleteRequest .CompleteArgument argument = new McpSchema .CompleteRequest .CompleteArgument (
788
+ argName , argValue );
789
+
790
+ return new McpSchema .CompleteRequest (ref , argument );
791
+ }
792
+
709
793
// ---------------------------------------
710
794
// Sampling
711
795
// ---------------------------------------
0 commit comments