-
Notifications
You must be signed in to change notification settings - Fork 705
Description
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.