Skip to content

feature: API Design Enhancement: Improve type safety for mcpgo.Items() to prevent ToolOption misuse #367

@davidleitw

Description

@davidleitw

Problem Statement

I'm always frustrated when using mcpgo.Items() because it's easy to accidentally pass ToolOption functions instead of schema objects, which causes confusing runtime JSON marshaling errors instead of clear compile-time errors.

While developing our MCP server, we repeatedly made this mistake:

// This looks correct but causes runtime errors
mcpgo.Items(mcpgo.WithString("", mcpgo.Enum("A", "B", "C", "D")))

The error only surfaces during JSON serialization with an unhelpful message:

json: error calling MarshalJSON for type mcp.ToolInputSchema: json: unsupported type: mcp.ToolOption

This confusion stems from the similar syntax between ToolOption functions and the expected schema objects, combined with Items() accepting any type which provides no compile-time protection.

Proposed Solution

I propose adding type-safe helper functions for common array item scenarios while maintaining backward compatibility:

func ItemsString(opts ...PropertyOption) PropertyOption
func ItemsStringWithEnum(values []string) PropertyOption  
func ItemsNumber(opts ...PropertyOption) PropertyOption
func ItemsBoolean(opts ...PropertyOption) PropertyOption

Additionally, enhance the existing Items() function with better runtime validation and error messages that guide developers toward the correct usage.

For improved documentation, clearly distinguish between the three different types of configuration objects:

  • ToolOption functions for top-level tool configuration
  • PropertyOption functions for individual property behavior
  • Schema objects for complex nested structures

MCP Spec Reference

This enhancement relates to the MCP specification's tool definition requirements where tools must provide valid JSON Schema for their input parameters. Better type safety would help ensure compliance with the JSON Schema specification without breaking the MCP protocol.

Example Usage

// Current verbose but correct approach
mcpgo.WithArray("workload_type",
    mcpgo.Description("Filter by workload types"),
    mcpgo.Items(map[string]any{
        "type": "string",
        "enum": []string{"A", "B", "C", "D"},
    }),
)

// Proposed simplified approach
mcpgo.WithArray("workload_type", 
    mcpgo.Description("Filter by workload types"),
    mcpgo.ItemsStringWithEnum([]string{"A", "B", "C", "D"}),
)

// For simple string arrays
mcpgo.WithArray("os_names",
    mcpgo.Description("Operating system names"),
    mcpgo.ItemsString(),
)

Alternatives/Workarounds Considered

Current Workaround: Manually create map[string]any objects for all array items, which works but is verbose and error-prone.

Alternative 1: Use interfaces or generics to make Items() type-safe, but this would break backward compatibility.

Alternative 2: Add runtime type checking to Items() that panics early with helpful error messages, but this doesn't solve the fundamental usability issue.

Alternative 3: Comprehensive documentation improvements alone, but this doesn't address the underlying API design that encourages mistakes.

The proposed helper functions provide the best balance of improved developer experience while maintaining full backward compatibility. Developers can continue using the existing Items() function, but have better alternatives available for common use cases.

Proposed Documentation Enhancement

To further prevent misuse and improve clarity, each proposed helper function (e.g., ItemsString, ItemsStringWithEnum, ItemsNumber, ItemsBoolean) should include detailed GoDoc comments that explicitly show the resulting JSON Schema structure. These comments would help developers understand the expected output and avoid incorrect usage. For example:

// ItemsString configures an array's items to be of type string.
// It accepts optional PropertyOption functions to further customize the schema.
// The resulting JSON Schema will look like:
// {
//   "type": "string"
// }
// Example:
//   mcpgo.WithArray("os_names", mcpgo.ItemsString(mcpgo.Description("OS name")))
func ItemsString(opts ...PropertyOption) PropertyOption

// ItemsStringWithEnum configures an array's items to be of type string with a specified enum.
// The resulting JSON Schema will look like:
// {
//   "type": "string",
//   "enum": ["value1", "value2", ...]
// }
// Example:
//   mcpgo.WithArray("workload_type", mcpgo.ItemsStringWithEnum([]string{"VM", "PC", "PS", "FS"}))
func ItemsStringWithEnum(values []string) PropertyOption

Similarly, the existing Items() function should include a comment clarifying its expected input and output schema structure, such as:

// Items specifies the schema for an array's items, accepting either a schema object (e.g., map[string]any) or PropertyOption functions.
// The resulting JSON Schema depends on the provided input, e.g.:
// {
//   "type": "string",
//   "enum": ["VM", "PC"]
// }
// Example:
//   mcpgo.Items(map[string]any{"type": "string", "enum": []string{"VM", "PC"}})
func Items(item any) PropertyOption

These comments would make it immediately clear what each function produces, reducing the likelihood of passing incorrect types and improving the developer experience by aligning expectations with the MCP's JSON Schema requirements.

I'd be happy to discuss the implementation approach and contribute a pull request if this enhancement aligns with the project's goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: sdkSDK improvements unrelated to MCP specificationtype: enhancementNew feature or enhancement request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions