diff --git a/lib/mcp/client.rb b/lib/mcp/client.rb index e56366c4..6437dd68 100644 --- a/lib/mcp/client.rb +++ b/lib/mcp/client.rb @@ -92,12 +92,17 @@ def prompts # Calls a tool via the transport layer and returns the full response from the server. # + # @param name [String] The name of the tool to call. # @param tool [MCP::Client::Tool] The tool to be called. # @param arguments [Object, nil] The arguments to pass to the tool. # @param progress_token [String, Integer, nil] A token to request progress notifications from the server during tool execution. # @return [Hash] The full JSON-RPC response from the transport. # - # @example + # @example Call by name + # response = client.call_tool(name: "my_tool", arguments: { foo: "bar" }) + # content = response.dig("result", "content") + # + # @example Call with a tool object # tool = client.tools.first # response = client.call_tool(tool: tool, arguments: { foo: "bar" }) # structured_content = response.dig("result", "structuredContent") @@ -105,8 +110,11 @@ def prompts # @note # The exact requirements for `arguments` are determined by the transport layer in use. # Consult the documentation for your transport (e.g., MCP::Client::HTTP) for details. - def call_tool(tool:, arguments: nil, progress_token: nil) - params = { name: tool.name, arguments: arguments } + def call_tool(name: nil, tool: nil, arguments: nil, progress_token: nil) + tool_name = name || tool&.name + raise ArgumentError, "Either `name:` or `tool:` must be provided." unless tool_name + + params = { name: tool_name, arguments: arguments } if progress_token params[:_meta] = { progressToken: progress_token } end diff --git a/test/mcp/client_test.rb b/test/mcp/client_test.rb index 2806ef1e..bf83542e 100644 --- a/test/mcp/client_test.rb +++ b/test/mcp/client_test.rb @@ -67,6 +67,32 @@ def test_call_tool_sends_request_to_transport_and_returns_content assert_equal([{ type: "text", text: "Hello, world!" }], content) end + def test_call_tool_by_name + transport = mock + arguments = { foo: "bar" } + mock_response = { + "result" => { "content" => [{ "type": "text", "text": "Hello, world!" }] }, + } + + transport.expects(:send_request).with do |args| + args.dig(:request, :params, :name) == "tool1" && + args.dig(:request, :params, :arguments) == arguments + end.returns(mock_response).once + + client = Client.new(transport: transport) + result = client.call_tool(name: "tool1", arguments: arguments) + content = result.dig("result", "content") + + assert_equal([{ type: "text", text: "Hello, world!" }], content) + end + + def test_call_tool_raises_when_no_name_or_tool + client = Client.new(transport: mock) + + error = assert_raises(ArgumentError) { client.call_tool(arguments: { foo: "bar" }) } + assert_equal("Either `name:` or `tool:` must be provided.", error.message) + end + def test_resources_sends_request_to_transport_and_returns_resources_array transport = mock mock_response = {