Merge pull request #333 from koic/tool_validation_error
Return tool argument validation failures as tool execution errors
This commit is contained in:
commit
38d952adce
2 changed files with 88 additions and 27 deletions
|
|
@ -533,7 +533,7 @@ module MCP
|
||||||
add_instrumentation_data(error: :missing_required_arguments)
|
add_instrumentation_data(error: :missing_required_arguments)
|
||||||
|
|
||||||
missing = tool.input_schema.missing_required_arguments(arguments).join(", ")
|
missing = tool.input_schema.missing_required_arguments(arguments).join(", ")
|
||||||
raise RequestHandlerError.new("Missing required arguments: #{missing}", request, error_type: :invalid_params)
|
return error_tool_response("Missing required arguments: #{missing}")
|
||||||
end
|
end
|
||||||
|
|
||||||
if configuration.validate_tool_call_arguments && tool.input_schema
|
if configuration.validate_tool_call_arguments && tool.input_schema
|
||||||
|
|
@ -542,7 +542,7 @@ module MCP
|
||||||
rescue Tool::InputSchema::ValidationError => e
|
rescue Tool::InputSchema::ValidationError => e
|
||||||
add_instrumentation_data(error: :invalid_schema)
|
add_instrumentation_data(error: :invalid_schema)
|
||||||
|
|
||||||
raise RequestHandlerError.new(e.message, request, error_type: :invalid_params)
|
return error_tool_response(e.message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ module MCP
|
||||||
assert_instrumentation_data({ method: "tools/call", tool_name: tool_name, tool_arguments: tool_args })
|
assert_instrumentation_data({ method: "tools/call", tool_name: tool_name, tool_arguments: tool_args })
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#handle tools/call returns protocol error in JSON-RPC format if required tool arguments are missing" do
|
test "#handle tools/call returns tool execution error if required tool arguments are missing" do
|
||||||
tool_with_required_argument = Tool.define(
|
tool_with_required_argument = Tool.define(
|
||||||
name: "test_tool",
|
name: "test_tool",
|
||||||
title: "Test tool",
|
title: "Test tool",
|
||||||
|
|
@ -336,10 +336,10 @@ module MCP
|
||||||
|
|
||||||
response = server.handle(request)
|
response = server.handle(request)
|
||||||
|
|
||||||
assert_nil response[:result]
|
assert_nil response[:error]
|
||||||
assert_equal(-32602, response[:error][:code])
|
assert(response[:result][:isError])
|
||||||
assert_equal "Invalid params", response[:error][:message]
|
assert_equal "text", response[:result][:content][0][:type]
|
||||||
assert_includes response[:error][:data], "Missing required arguments: message"
|
assert_includes response[:result][:content][0][:text], "Missing required arguments: message"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#handle_json tools/call executes tool and returns result" do
|
test "#handle_json tools/call executes tool and returns result" do
|
||||||
|
|
@ -1562,11 +1562,10 @@ module MCP
|
||||||
refute response[:result].key?(:instructions)
|
refute response[:result].key?(:instructions)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tools/call returns protocol error in JSON-RPC format for missing arguments" do
|
test "tools/call returns tool execution error for missing arguments" do
|
||||||
server = Server.new(
|
configuration = Configuration.new(validate_tool_call_arguments: true)
|
||||||
tools: [TestTool],
|
configuration.instrumentation_callback = instrumentation_helper.callback
|
||||||
configuration: Configuration.new(validate_tool_call_arguments: true),
|
server = Server.new(tools: [TestTool], configuration: configuration)
|
||||||
)
|
|
||||||
|
|
||||||
response = server.handle(
|
response = server.handle(
|
||||||
{
|
{
|
||||||
|
|
@ -1581,17 +1580,22 @@ module MCP
|
||||||
|
|
||||||
assert_equal "2.0", response[:jsonrpc]
|
assert_equal "2.0", response[:jsonrpc]
|
||||||
assert_equal 1, response[:id]
|
assert_equal 1, response[:id]
|
||||||
assert_nil response[:result]
|
assert_nil response[:error]
|
||||||
assert_equal(-32602, response[:error][:code])
|
assert(response[:result][:isError])
|
||||||
assert_equal "Invalid params", response[:error][:message]
|
assert_equal "text", response[:result][:content][0][:type]
|
||||||
assert_includes response[:error][:data], "Missing required arguments"
|
assert_includes response[:result][:content][0][:text], "Missing required arguments"
|
||||||
|
assert_instrumentation_data({
|
||||||
|
method: "tools/call",
|
||||||
|
tool_name: "test_tool",
|
||||||
|
tool_arguments: {},
|
||||||
|
error: :missing_required_arguments,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tools/call returns protocol error in JSON-RPC format for invalid arguments when validate_tool_call_arguments is true" do
|
test "tools/call returns tool execution error for invalid arguments when validate_tool_call_arguments is true" do
|
||||||
server = Server.new(
|
configuration = Configuration.new(validate_tool_call_arguments: true)
|
||||||
tools: [TestTool],
|
configuration.instrumentation_callback = instrumentation_helper.callback
|
||||||
configuration: Configuration.new(validate_tool_call_arguments: true),
|
server = Server.new(tools: [TestTool], configuration: configuration)
|
||||||
)
|
|
||||||
|
|
||||||
response = server.handle(
|
response = server.handle(
|
||||||
{
|
{
|
||||||
|
|
@ -1607,10 +1611,44 @@ module MCP
|
||||||
|
|
||||||
assert_equal "2.0", response[:jsonrpc]
|
assert_equal "2.0", response[:jsonrpc]
|
||||||
assert_equal 1, response[:id]
|
assert_equal 1, response[:id]
|
||||||
assert_nil response[:result]
|
assert_nil response[:error]
|
||||||
assert_equal(-32602, response[:error][:code])
|
assert(response[:result][:isError])
|
||||||
assert_equal "Invalid params", response[:error][:message]
|
assert_equal "text", response[:result][:content][0][:type]
|
||||||
assert_includes response[:error][:data], "Invalid arguments"
|
assert_includes response[:result][:content][0][:text], "Invalid arguments"
|
||||||
|
assert_instrumentation_data({
|
||||||
|
method: "tools/call",
|
||||||
|
tool_name: "test_tool",
|
||||||
|
tool_arguments: { message: 123 },
|
||||||
|
error: :invalid_schema,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "tools/call returns tool execution error for nested schema validation failure" do
|
||||||
|
server = Server.new(
|
||||||
|
tools: [ComplexTypesTool],
|
||||||
|
configuration: Configuration.new(validate_tool_call_arguments: true),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = server.handle(
|
||||||
|
{
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: 1,
|
||||||
|
method: "tools/call",
|
||||||
|
params: {
|
||||||
|
name: "complex_types_tool",
|
||||||
|
arguments: {
|
||||||
|
numbers: [1, 2, 3],
|
||||||
|
strings: ["a", "b", "c"],
|
||||||
|
objects: [{ name: 123 }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_nil response[:error]
|
||||||
|
assert(response[:result][:isError])
|
||||||
|
assert_equal "text", response[:result][:content][0][:type]
|
||||||
|
assert_includes response[:result][:content][0][:text], "Invalid arguments"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tools/call skips argument validation when validate_tool_call_arguments is false" do
|
test "tools/call skips argument validation when validate_tool_call_arguments is false" do
|
||||||
|
|
@ -1695,7 +1733,7 @@ module MCP
|
||||||
assert_equal "OK", response[:result][:content][0][:content]
|
assert_equal "OK", response[:result][:content][0][:content]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tools/call returns protocol error in JSON-RPC format when additionalProperties set to false" do
|
test "tools/call returns tool execution error when additionalProperties set to false" do
|
||||||
server = Server.new(
|
server = Server.new(
|
||||||
tools: [TestToolWithAdditionalPropertiesSetToFalse],
|
tools: [TestToolWithAdditionalPropertiesSetToFalse],
|
||||||
configuration: Configuration.new(validate_tool_call_arguments: true),
|
configuration: Configuration.new(validate_tool_call_arguments: true),
|
||||||
|
|
@ -1718,10 +1756,33 @@ module MCP
|
||||||
|
|
||||||
assert_equal "2.0", response[:jsonrpc]
|
assert_equal "2.0", response[:jsonrpc]
|
||||||
assert_equal 1, response[:id]
|
assert_equal 1, response[:id]
|
||||||
|
assert_nil response[:error]
|
||||||
|
assert(response[:result][:isError])
|
||||||
|
assert_equal "text", response[:result][:content][0][:type]
|
||||||
|
assert_includes response[:result][:content][0][:text], "Invalid arguments"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "tools/call returns JSON-RPC -32602 protocol error when tool is not found" do
|
||||||
|
server = Server.new(
|
||||||
|
tools: [TestTool],
|
||||||
|
)
|
||||||
|
|
||||||
|
response = server.handle(
|
||||||
|
{
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: 1,
|
||||||
|
method: "tools/call",
|
||||||
|
params: {
|
||||||
|
name: "unknown_tool",
|
||||||
|
arguments: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
assert_nil response[:result]
|
assert_nil response[:result]
|
||||||
assert_equal(-32602, response[:error][:code])
|
assert_equal(-32602, response[:error][:code])
|
||||||
assert_equal "Invalid params", response[:error][:message]
|
assert_equal "Invalid params", response[:error][:message]
|
||||||
assert_includes response[:error][:data], "Invalid arguments"
|
assert_includes response[:error][:data], "Tool not found: unknown_tool"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "#handle completion/complete returns default completion result" do
|
test "#handle completion/complete returns default completion result" do
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue