Skip to content

Everything server example teaches wrong error pattern: returns (nil, error) for input validation #837

@blackwell-systems

Description

@blackwell-systems

Problem

The everything server example uses return nil, fmt.Errorf(...) for input validation failures:

// examples/everything/main.go:357-359
message, ok := arguments["message"].(string)
if !ok {
    return nil, fmt.Errorf("invalid message argument")
}

In the mcp-go SDK, returning (nil, error) from a tool handler produces -32603 (InternalError) at server.go:1724-1729:

result, err := finalHandler(ctx, request)
if err != nil {
    return nil, &requestError{
        id:   id,
        code: mcp.INTERNAL_ERROR,
        err:  err,
    }
}

The correct pattern for input validation is:

message, ok := arguments["message"].(string)
if !ok {
    return mcp.NewToolResultError("invalid message argument: expected string"), nil
}

This returns isError: true in the tool result, which agents can act on. -32603 is an unrecoverable protocol error that agents cannot distinguish from a server crash.

Impact

Every developer learning from the everything server example copies this pattern. The echo and add tool handlers both use return nil, fmt.Errorf(...) for type assertion failures. This results in:

  • echo with a non-string message: -32603 instead of isError: true
  • add with a non-number argument: -32603 instead of isError: true
  • echo with null/empty/missing arguments: -32603 instead of isError: true

Tested via mcp-assert fuzz against the everything server: sending wrong types, null, and empty args all produce -32603.

Suggestion

Update the example handlers to use mcp.NewToolResultError() for input validation, reserving return nil, error for actual internal errors (e.g., database connection failed, file not found). This would also serve as documentation for the correct pattern.

The WithInputSchemaValidation() option (v0.50.0) catches type mismatches at the SDK level, but the example code should still demonstrate the correct handler-level pattern for cases where schema validation alone isn't sufficient.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions