Fix: Return CallToolResult with isError for tool errors to conform with MCP spec (#354)
Update server-side tool error handling to return `CallToolResult` with `isError: true` instead of throwing protocol-level exceptions, conforming with MCP specification ## Motivation and Context [The MCP specification states](https://modelcontextprotocol.io/specification/2025-06-18/schema#calltoolresult): > Any errors that originate from the tool SHOULD be reported inside the result object, with isError set to true, not as an MCP protocol-level error response. Otherwise, the LLM would not be able to see that an error occurred and self-correct. This PR updates the kotlin sdk to match this behavior ## How Has This Been Tested? All tests pass successfully with the new behavior ## Breaking Changes None ## Types of changes - [x] 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 - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [x] I have added appropriate error handling - [x] I have added or updated documentation as needed ## Additional context The TypeScript SDK was recently updated [#1044](https://github.com/modelcontextprotocol/typescript-sdk/pull/1044) to conform with the MCP specification regarding tool error handling. That’s why we got exceptions in the integration tests
This commit is contained in:
parent
c5a254b83f
commit
20e4e44509
1 changed files with 95 additions and 77 deletions
|
|
@ -21,6 +21,7 @@ import io.modelcontextprotocol.kotlin.sdk.ReadResourceRequest
|
|||
import io.modelcontextprotocol.kotlin.sdk.ReadResourceResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.Resource
|
||||
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.TextContent
|
||||
import io.modelcontextprotocol.kotlin.sdk.Tool
|
||||
import io.modelcontextprotocol.kotlin.sdk.ToolAnnotations
|
||||
import io.modelcontextprotocol.kotlin.sdk.shared.ProtocolOptions
|
||||
|
|
@ -32,6 +33,7 @@ import kotlinx.collections.immutable.minus
|
|||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toPersistentSet
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
|
|
@ -515,13 +517,30 @@ public open class Server(
|
|||
|
||||
private suspend fun handleCallTool(request: CallToolRequest): CallToolResult {
|
||||
logger.debug { "Handling tool call request for tool: ${request.name}" }
|
||||
|
||||
// Check if tool exists
|
||||
val tool = _tools.value[request.name]
|
||||
?: run {
|
||||
logger.error { "Tool not found: ${request.name}" }
|
||||
throw IllegalArgumentException("Tool not found: ${request.name}")
|
||||
return CallToolResult(
|
||||
content = listOf(TextContent(text = "Tool ${request.name} not found")),
|
||||
isError = true,
|
||||
)
|
||||
}
|
||||
logger.trace { "Executing tool ${request.name} with input: ${request.arguments}" }
|
||||
return tool.handler(request)
|
||||
|
||||
// Execute tool handler and catch any errors
|
||||
return try {
|
||||
logger.trace { "Executing tool ${request.name} with input: ${request.arguments}" }
|
||||
tool.handler(request)
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
logger.error(e) { "Error executing tool ${request.name}" }
|
||||
CallToolResult(
|
||||
content = listOf(TextContent(text = "Error executing tool ${request.name}: ${e.message}")),
|
||||
isError = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleListPrompts(): ListPromptsResult {
|
||||
|
|
|
|||
Loading…
Reference in a new issue