Add pluggable exception handler for MCP tool method invocations#5772
Add pluggable exception handler for MCP tool method invocations#5772eocantu wants to merge 1 commit intospring-projects:mainfrom
Conversation
Signed-off-by: Edgar Cantu <eocantu@users.noreply.github.com>
| Throwable cause = ex.getCause(); | ||
| if (cause instanceof RuntimeException re) { | ||
| throw re; | ||
| } | ||
| if (cause instanceof Error error) { | ||
| throw error; | ||
| } | ||
| throw new RuntimeException(cause); |
There was a problem hiding this comment.
This callMethod always re-wrapped exceptions in a RuntimeException("Error invoking method...). This destroyed the original exception type before it reached the toolCallExceptionClass matching logic, silently breaking the feature for any specific exception subclass.
Additionally, error messages used Java internals (the Java method name), rather than the MCP tool name the client actually knows.
This change here necessitated some updates to tests to match the update to the error string.
| // IllegalArgumentException) and propagates with its original message | ||
| assertThatThrownBy(() -> callback.apply(exchange, request)).isInstanceOf(RuntimeException.class) | ||
| .hasMessageContaining("Error invoking method"); | ||
| .hasMessageContaining("Runtime error: test"); |
There was a problem hiding this comment.
Previously callMethod always re-threw as new RuntimeException("Error invoking method: "). The original exception now propagates unchanged, so the assertion must match its actual message.
| // propagates with the original cause accessible | ||
| assertThatThrownBy(() -> callback.apply(exchange, request)).isInstanceOf(RuntimeException.class) | ||
| .hasMessageContaining("Error invoking method") | ||
| .hasMessageContaining("Business error: test") |
There was a problem hiding this comment.
Same root change: checked exceptions are now re-wrapped as new RuntimeException(cause) (no custom message), so the wrapper's message becomes cause.toString() which includes "Business error: test".
|
Hi @ericbottard ! This error handler would be really helpful to us. Do you think we could get some eyes on this PR? Any thoughts? |
Introduces
McpToolCallExceptionHandler, an interface for customizing how exceptions thrown during MCP tool method invocations are converted intoCallToolResulterror responses. The MCP annotation framework silently swallows all exceptions from tool method calls and returns a generic error result. There's no hook to intercept those exceptions to customize or control the error message format.This feature should help with:
Summary of the changes:
McpToolCallExceptionHandler- New functional interface with adefaultHandler()factory methodAbstractMcpToolProvider.setExceptionHandler()to configure the handler at the provider levelSyncMcpAnnotationProviders/AsyncMcpAnnotationProvidersutility overloads accepting a handler@ConditionalOnMissingBeanMcpToolCallExceptionHandlerbean for Spring Boot override support