Skip to content

Conversation

tzolov
Copy link
Contributor

@tzolov tzolov commented Sep 28, 2025

  • Replace McpError with appropriate standard exceptions (IllegalArgumentException, IllegalStateException) for validation errors
  • Change tool/prompt registration to replace existing items instead of rejecting duplicates with warningsi
    • Change addTool behavior to replace existing tools instead of throwing errors
    • Change addPrompt() to allow replacing existing prompts with warning instead of throwing error
  • Make removal operations idempotent - log warnings instead of throwing errors for non-existent items
    • Change removePrompt() to log warning instead of throwing error for non-existent prompts
    • Change removeTool() to gracefully handle non-existent tools with warnings instead of errors
  • Add listTools() and listPrompts() methods to all server variants (async, sync, stateless)
    • Add listTools() method to all server classes for tool enumeration
    • Add listPrompts() method to all server implementations for retrieving registered prompts
  • Improve error construction using McpError.builder() pattern for protocol-specific errors
  • Update error handling in session classes to properly propagate McpError JSON-RPC errors
    • Use proper error codes (INVALID_PARAMS) for prompt not found scenarios
  • Update tests to reflect new lenient behavior for duplicate registrations and removals

Summary

Improve MCP server resilience and error handling by replacing custom exceptions with standard Java exceptions, making tool/prompt registration more forgiving, adding listing capabilities, and implementing idempotent removal operations.

Motivation and Context

This change addresses several usability and robustness issues in the MCP Java SDK:

  1. Inconsistent error handling: The previous implementation used McpError for validation errors that should use standard Java exceptions
  2. Rigid tool/prompt registration: Attempting to register duplicate tools/prompts would fail, making it difficult to update or reconfigure servers dynamically
  3. Missing introspection capabilities: No way to list currently registered tools and prompts
  4. Non-idempotent operations: Removing non-existent tools/prompts would throw errors instead of being a no-op
  5. Inconsistent error propagation: Protocol-specific errors weren't properly constructed or propagated

These issues made the API less developer-friendly and harder to use in dynamic scenarios where tools and prompts might be reconfigured at runtime.

How Has This Been Tested?

  • Existing unit tests have been updated and pass
  • New test cases added for the lenient behavior (duplicate registration, non-existent removal)
  • Tests verify that listTools() and listPrompts() methods return correct results
  • Error handling tests updated to verify proper exception types are thrown
  • Integration tests confirm backward compatibility is maintained

Breaking Changes

Minimal breaking changes:

  • Exception types changed for validation errors (from McpError to IllegalArgumentException/IllegalStateException)
  • Behavior change: duplicate tool/prompt registration now replaces instead of throwing errors
  • Behavior change: removing non-existent items now logs warnings instead of throwing errors

Most users should not be affected as the core API remains the same. Users who were specifically catching McpError for validation scenarios may need to update their exception handling.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

  • Exception hierarchy: Used standard Java exceptions (IllegalArgumentException, IllegalStateException) for validation errors while preserving McpError for protocol-specific errors using the builder pattern
  • Replacement strategy: When duplicate tools/prompts are registered, the new implementation removes the existing one and adds the new one, logging a warning. This enables dynamic reconfiguration scenarios
  • Idempotent operations: Removal operations now follow the principle of idempotency - calling remove on a non-existent item is safe and logs a warning rather than failing
  • Listing methods: Added listTools() and listPrompts() methods to all server variants (async, sync, stateless) returning Flux/List of registered items for introspection
  • Error propagation: Improved error handling in session classes to properly extract and propagate McpError JSON-RPC errors while falling back to generic internal errors

- Replace McpError with appropriate standard exceptions (IllegalArgumentException, IllegalStateException) for validation errors
- Change tool/prompt registration to replace existing items instead of rejecting duplicates with warningsi
  - Change addTool behavior to replace existing tools instead of throwing errors
  - Change addPrompt() to allow replacing existing prompts with warning instead of throwing error
- Make removal operations idempotent - log warnings instead of throwing errors for non-existent items
  - Change removePrompt() to log warning instead of throwing error for non-existent prompts
  - Change removeTool() to gracefully handle non-existent tools with warnings instead of errors
- Add listTools() and listPrompts() methods to all server variants (async, sync, stateless)
  - Add listTools() method to all server classes for tool enumeration
  - Add listPrompts() method to all server implementations for retrieving registered prompts
- Improve error construction using McpError.builder() pattern for protocol-specific errors
- Update error handling in session classes to properly propagate McpError JSON-RPC errors
  - Use proper error codes (INVALID_PARAMS) for prompt not found scenarios
- Update tests to reflect new lenient behavior for duplicate registrations and removals

This change makes the MCP server APIs more resilient and user-friendly by using appropriate exception types,
supporting item replacement, and providing listing capabilities while maintaining backward compatibility.

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
@tzolov tzolov added this to the 0.14.0 milestone Sep 28, 2025
error.getMessage(), null)))); // TODO: add error message
// through the data field
.onErrorResume(error -> {
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Propagate existing JSONRPCError instead of wrapping it

.map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(), result,
null))
.onErrorResume(e -> {
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (e instanceof McpError mcpError
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Propagate existing JSONRPCError instead of wrapp

public Mono<Void> addTool(McpServerFeatures.AsyncToolSpecification toolSpecification) {
if (toolSpecification == null) {
return Mono.error(new McpError("Tool specification must not be null"));
return Mono.error(new IllegalArgumentException("Tool specification must not be null"));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that those are runtime server errors that are not propagated over the wire to the client.

- Add findRootCause() utility method utility to traverse exception chains
- Update McpServerSession and McpStreamableServerSession to use root cause messages
- Ensures meaningful error messages are returned instead of wrapped exception messages

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
@tzolov tzolov merged commit c1cde20 into modelcontextprotocol:main Sep 30, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant