@@ -322,25 +322,24 @@ private McpNotificationHandler asyncRootsListChangedNotificationHandler(
322
322
*/
323
323
public Mono <Void > addTool (McpServerFeatures .AsyncToolSpecification toolSpecification ) {
324
324
if (toolSpecification == null ) {
325
- return Mono .error (new McpError ("Tool specification must not be null" ));
325
+ return Mono .error (new IllegalArgumentException ("Tool specification must not be null" ));
326
326
}
327
327
if (toolSpecification .tool () == null ) {
328
- return Mono .error (new McpError ("Tool must not be null" ));
328
+ return Mono .error (new IllegalArgumentException ("Tool must not be null" ));
329
329
}
330
330
if (toolSpecification .call () == null && toolSpecification .callHandler () == null ) {
331
- return Mono .error (new McpError ("Tool call handler must not be null" ));
331
+ return Mono .error (new IllegalArgumentException ("Tool call handler must not be null" ));
332
332
}
333
333
if (this .serverCapabilities .tools () == null ) {
334
- return Mono .error (new McpError ("Server must be configured with tool capabilities" ));
334
+ return Mono .error (new IllegalStateException ("Server must be configured with tool capabilities" ));
335
335
}
336
336
337
337
var wrappedToolSpecification = withStructuredOutputHandling (this .jsonSchemaValidator , toolSpecification );
338
338
339
339
return Mono .defer (() -> {
340
- // Check for duplicate tool names
341
- if (this .tools .stream ().anyMatch (th -> th .tool ().name ().equals (wrappedToolSpecification .tool ().name ()))) {
342
- return Mono .error (
343
- new McpError ("Tool with name '" + wrappedToolSpecification .tool ().name () + "' already exists" ));
340
+ // Remove tools with duplicate tool names first
341
+ if (this .tools .removeIf (th -> th .tool ().name ().equals (wrappedToolSpecification .tool ().name ()))) {
342
+ logger .warn ("Replace existing Tool with name '{}'" , wrappedToolSpecification .tool ().name ());
344
343
}
345
344
346
345
this .tools .add (wrappedToolSpecification );
@@ -464,30 +463,40 @@ private static McpServerFeatures.AsyncToolSpecification withStructuredOutputHand
464
463
.build ();
465
464
}
466
465
466
+ /**
467
+ * List all registered tools.
468
+ * @return A Flux stream of all registered tools
469
+ */
470
+ public Flux <Tool > listTools () {
471
+ return Flux .fromIterable (this .tools ).map (McpServerFeatures .AsyncToolSpecification ::tool );
472
+ }
473
+
467
474
/**
468
475
* Remove a tool handler at runtime.
469
476
* @param toolName The name of the tool handler to remove
470
477
* @return Mono that completes when clients have been notified of the change
471
478
*/
472
479
public Mono <Void > removeTool (String toolName ) {
473
480
if (toolName == null ) {
474
- return Mono .error (new McpError ("Tool name must not be null" ));
481
+ return Mono .error (new IllegalArgumentException ("Tool name must not be null" ));
475
482
}
476
483
if (this .serverCapabilities .tools () == null ) {
477
- return Mono .error (new McpError ("Server must be configured with tool capabilities" ));
484
+ return Mono .error (new IllegalStateException ("Server must be configured with tool capabilities" ));
478
485
}
479
486
480
487
return Mono .defer (() -> {
481
- boolean removed = this .tools
482
- .removeIf (toolSpecification -> toolSpecification .tool ().name ().equals (toolName ));
483
- if (removed ) {
488
+ if (this .tools .removeIf (toolSpecification -> toolSpecification .tool ().name ().equals (toolName ))) {
489
+
484
490
logger .debug ("Removed tool handler: {}" , toolName );
485
491
if (this .serverCapabilities .tools ().listChanged ()) {
486
492
return notifyToolsListChanged ();
487
493
}
488
- return Mono .empty ();
489
494
}
490
- return Mono .error (new McpError ("Tool with name '" + toolName + "' not found" ));
495
+ else {
496
+ logger .warn ("Ignore as a Tool with name '{}' not found" , toolName );
497
+ }
498
+
499
+ return Mono .empty ();
491
500
});
492
501
}
493
502
@@ -518,8 +527,10 @@ private McpRequestHandler<CallToolResult> toolsCallRequestHandler() {
518
527
.findAny ();
519
528
520
529
if (toolSpecification .isEmpty ()) {
521
- return Mono .error (new McpError (new JSONRPCResponse .JSONRPCError (McpSchema .ErrorCodes .INVALID_PARAMS ,
522
- "Unknown tool: invalid_tool_name" , "Tool not found: " + callToolRequest .name ())));
530
+ return Mono .error (McpError .builder (McpSchema .ErrorCodes .INVALID_PARAMS )
531
+ .message ("Unknown tool: invalid_tool_name" )
532
+ .data ("Tool not found: " + callToolRequest .name ())
533
+ .build ());
523
534
}
524
535
525
536
return toolSpecification .get ().callHandler ().apply (exchange , callToolRequest );
@@ -747,58 +758,63 @@ private Optional<McpServerFeatures.AsyncResourceTemplateSpecification> findResou
747
758
*/
748
759
public Mono <Void > addPrompt (McpServerFeatures .AsyncPromptSpecification promptSpecification ) {
749
760
if (promptSpecification == null ) {
750
- return Mono .error (new McpError ("Prompt specification must not be null" ));
761
+ return Mono .error (new IllegalArgumentException ("Prompt specification must not be null" ));
751
762
}
752
763
if (this .serverCapabilities .prompts () == null ) {
753
- return Mono .error (new McpError ("Server must be configured with prompt capabilities" ));
764
+ return Mono .error (new IllegalStateException ("Server must be configured with prompt capabilities" ));
754
765
}
755
766
756
767
return Mono .defer (() -> {
757
- McpServerFeatures .AsyncPromptSpecification specification = this .prompts
758
- .putIfAbsent (promptSpecification .prompt ().name (), promptSpecification );
759
- if (specification != null ) {
760
- return Mono .error (
761
- new McpError ("Prompt with name '" + promptSpecification .prompt ().name () + "' already exists" ));
768
+ var previous = this .prompts .put (promptSpecification .prompt ().name (), promptSpecification );
769
+ if (previous != null ) {
770
+ logger .warn ("Replace existing Prompt with name '{}'" , promptSpecification .prompt ().name ());
771
+ }
772
+ else {
773
+ logger .debug ("Added prompt handler: {}" , promptSpecification .prompt ().name ());
762
774
}
763
-
764
- logger .debug ("Added prompt handler: {}" , promptSpecification .prompt ().name ());
765
-
766
- // Servers that declared the listChanged capability SHOULD send a
767
- // notification,
768
- // when the list of available prompts changes
769
775
if (this .serverCapabilities .prompts ().listChanged ()) {
770
- return notifyPromptsListChanged ();
776
+ return this . notifyPromptsListChanged ();
771
777
}
778
+
772
779
return Mono .empty ();
773
780
});
774
781
}
775
782
783
+ /**
784
+ * List all registered prompts.
785
+ * @return A Flux stream of all registered prompts
786
+ */
787
+ public Flux <McpSchema .Prompt > listPrompts () {
788
+ return Flux .fromIterable (this .prompts .values ()).map (McpServerFeatures .AsyncPromptSpecification ::prompt );
789
+ }
790
+
776
791
/**
777
792
* Remove a prompt handler at runtime.
778
793
* @param promptName The name of the prompt handler to remove
779
794
* @return Mono that completes when clients have been notified of the change
780
795
*/
781
796
public Mono <Void > removePrompt (String promptName ) {
782
797
if (promptName == null ) {
783
- return Mono .error (new McpError ("Prompt name must not be null" ));
798
+ return Mono .error (new IllegalArgumentException ("Prompt name must not be null" ));
784
799
}
785
800
if (this .serverCapabilities .prompts () == null ) {
786
- return Mono .error (new McpError ("Server must be configured with prompt capabilities" ));
801
+ return Mono .error (new IllegalStateException ("Server must be configured with prompt capabilities" ));
787
802
}
788
803
789
804
return Mono .defer (() -> {
790
805
McpServerFeatures .AsyncPromptSpecification removed = this .prompts .remove (promptName );
791
806
792
807
if (removed != null ) {
793
808
logger .debug ("Removed prompt handler: {}" , promptName );
794
- // Servers that declared the listChanged capability SHOULD send a
795
- // notification, when the list of available prompts changes
796
809
if (this .serverCapabilities .prompts ().listChanged ()) {
797
810
return this .notifyPromptsListChanged ();
798
811
}
799
812
return Mono .empty ();
800
813
}
801
- return Mono .error (new McpError ("Prompt with name '" + promptName + "' not found" ));
814
+ else {
815
+ logger .warn ("Ignore as a Prompt with name '{}' not found" , promptName );
816
+ }
817
+ return Mono .empty ();
802
818
});
803
819
}
804
820
@@ -834,8 +850,12 @@ private McpRequestHandler<McpSchema.GetPromptResult> promptsGetRequestHandler()
834
850
835
851
// Implement prompt retrieval logic here
836
852
McpServerFeatures .AsyncPromptSpecification specification = this .prompts .get (promptRequest .name ());
853
+
837
854
if (specification == null ) {
838
- return Mono .error (new McpError ("Prompt not found: " + promptRequest .name ()));
855
+ return Mono .error (McpError .builder (ErrorCodes .INVALID_PARAMS )
856
+ .message ("Invalid prompt name" )
857
+ .data ("Prompt not found: " + promptRequest .name ())
858
+ .build ());
839
859
}
840
860
841
861
return Mono .defer (() -> specification .promptHandler ().apply (exchange , promptRequest ));
0 commit comments