Skip to content

fix: return isError instead of internal error for tool execution failures#258

Merged
sammcj merged 4 commits into
sammcj:mainfrom
blackwell-systems:fix/return-iserror-instead-of-internal-error
Jun 8, 2026
Merged

fix: return isError instead of internal error for tool execution failures#258
sammcj merged 4 commits into
sammcj:mainfrom
blackwell-systems:fix/return-iserror-instead-of-internal-error

Conversation

@blackwell-systems

Copy link
Copy Markdown
Contributor

Summary

  • Tool handler returns (nil, error) when Execute() fails, causing mcp-go to respond with JSON-RPC -32603 internal error
  • MCP clients treat -32603 as a server crash and cannot recover
  • Agents need isError: true with a descriptive message to self-correct

Changed three error paths in main.go to return mcp.NewToolResultError() instead of (nil, fmt.Errorf(...)):

  1. Tool not found in registry
  2. Invalid argument type
  3. Tool execution failure (input validation, missing params, etc.)

This matches the pattern already used in individual tools like get_library_documentation (line 104) which correctly returns mcp.NewToolResultError() for fetch failures.

Reproduction

# Using mcp-assert (https://github.com/blackwell-systems/mcp-assert)
mcp-assert audit --server "mcp-devtools"

# Before fix (4 tools crash):
#   calculator        CRASH   internal error: tool execution failed: missing required parameter...
#   get_tool_help     CRASH   internal error: tool execution failed: tool 'TODO_NAME' not found...
#   internet_search   CRASH   internal error: tool execution failed: 'query' array cannot be empty
#   search_packages   CRASH   internal error: tool execution failed: unsupported ecosystem: TODO

# After fix (all tools healthy):
#   calculator        healthy   valid error handling (isError: true)
#   get_tool_help     healthy   valid error handling (isError: true)
#   internet_search   healthy   valid error handling (isError: true)
#   search_packages   healthy   valid error handling (isError: true)

Change

Three-line change in main.go: replace return nil, fmt.Errorf(...) with return mcp.NewToolResultError(...), nil at all three error return sites in the tool handler.

Found with mcp-assert.

blackwell-systems and others added 2 commits April 26, 2026 07:12
…ures

Tool handler returns (nil, error) when Execute() fails, which causes
mcp-go to respond with JSON-RPC -32603 internal error. MCP clients
treat -32603 as a server crash and cannot recover from it. Agents
need isError: true with a descriptive message to self-correct.

Changed three error paths to return mcp.NewToolResultError() instead:
- tool not found in registry
- invalid argument type
- tool execution failure (input validation, missing params, etc.)

This matches the pattern already used in individual tools like
get_library_documentation (line 104) which correctly returns
mcp.NewToolResultError() for fetch failures.

Affected tools: calculator, get_tool_help, internet_search,
search_packages (all return -32603 on invalid input instead of
isError with helpful messages).

Found with mcp-assert (https://github.com/blackwell-systems/mcp-assert)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the MCP tool handler so tool failures are returned as isError: true tool results (via mcp.NewToolResultError(...)) instead of propagating a Go error that mcp-go converts into JSON-RPC -32603 internal errors, improving client/agent recoverability.

Changes:

  • Return mcp.NewToolResultError(...), nil when a tool is missing from the registry.
  • Return mcp.NewToolResultError(...), nil when tool call arguments are not map[string]any.
  • Return mcp.NewToolResultError(...), nil when Execute() fails.

Comment thread main.go Outdated
sammcj added 2 commits June 8, 2026 22:24
Extract the inline tool-handler closure into newToolHandler so the
error-handling behaviour is unit-testable, and add tests covering
execution failure, unknown tool, and the success path - all asserting a
failure surfaces as an isError result with a nil Go error (not a
JSON-RPC -32603). Include the root package in the make test targets so
these run in CI. Drop a no-op deferred closure left in the handler.
@sammcj sammcj merged commit 6b78919 into sammcj:main Jun 8, 2026
10 checks 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.

3 participants