@@ -322,25 +322,24 @@ private McpNotificationHandler asyncRootsListChangedNotificationHandler(
322322 */
323323 public Mono <Void > addTool (McpServerFeatures .AsyncToolSpecification toolSpecification ) {
324324 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" ));
326326 }
327327 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" ));
329329 }
330330 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" ));
332332 }
333333 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" ));
335335 }
336336
337337 var wrappedToolSpecification = withStructuredOutputHandling (this .jsonSchemaValidator , toolSpecification );
338338
339339 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 ());
344343 }
345344
346345 this .tools .add (wrappedToolSpecification );
@@ -464,30 +463,40 @@ private static McpServerFeatures.AsyncToolSpecification withStructuredOutputHand
464463 .build ();
465464 }
466465
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+
467474 /**
468475 * Remove a tool handler at runtime.
469476 * @param toolName The name of the tool handler to remove
470477 * @return Mono that completes when clients have been notified of the change
471478 */
472479 public Mono <Void > removeTool (String toolName ) {
473480 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" ));
475482 }
476483 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" ));
478485 }
479486
480487 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+
484490 logger .debug ("Removed tool handler: {}" , toolName );
485491 if (this .serverCapabilities .tools ().listChanged ()) {
486492 return notifyToolsListChanged ();
487493 }
488- return Mono .empty ();
489494 }
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 ();
491500 });
492501 }
493502
@@ -518,8 +527,10 @@ private McpRequestHandler<CallToolResult> toolsCallRequestHandler() {
518527 .findAny ();
519528
520529 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 ());
523534 }
524535
525536 return toolSpecification .get ().callHandler ().apply (exchange , callToolRequest );
@@ -747,58 +758,63 @@ private Optional<McpServerFeatures.AsyncResourceTemplateSpecification> findResou
747758 */
748759 public Mono <Void > addPrompt (McpServerFeatures .AsyncPromptSpecification promptSpecification ) {
749760 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" ));
751762 }
752763 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" ));
754765 }
755766
756767 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 ());
762774 }
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
769775 if (this .serverCapabilities .prompts ().listChanged ()) {
770- return notifyPromptsListChanged ();
776+ return this . notifyPromptsListChanged ();
771777 }
778+
772779 return Mono .empty ();
773780 });
774781 }
775782
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+
776791 /**
777792 * Remove a prompt handler at runtime.
778793 * @param promptName The name of the prompt handler to remove
779794 * @return Mono that completes when clients have been notified of the change
780795 */
781796 public Mono <Void > removePrompt (String promptName ) {
782797 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" ));
784799 }
785800 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" ));
787802 }
788803
789804 return Mono .defer (() -> {
790805 McpServerFeatures .AsyncPromptSpecification removed = this .prompts .remove (promptName );
791806
792807 if (removed != null ) {
793808 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
796809 if (this .serverCapabilities .prompts ().listChanged ()) {
797810 return this .notifyPromptsListChanged ();
798811 }
799812 return Mono .empty ();
800813 }
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 ();
802818 });
803819 }
804820
@@ -834,8 +850,12 @@ private McpRequestHandler<McpSchema.GetPromptResult> promptsGetRequestHandler()
834850
835851 // Implement prompt retrieval logic here
836852 McpServerFeatures .AsyncPromptSpecification specification = this .prompts .get (promptRequest .name ());
853+
837854 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 ());
839859 }
840860
841861 return Mono .defer (() -> specification .promptHandler ().apply (exchange , promptRequest ));
0 commit comments