Skip to content

Commit 71346ef

Browse files
committed
[tools] add support for structured responses
1 parent efeef28 commit 71346ef

File tree

4 files changed

+51
-1
lines changed

4 files changed

+51
-1
lines changed

lib/mcp.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
require_relative "mcp/tool"
2020
require_relative "mcp/tool/input_schema"
2121
require_relative "mcp/tool/response"
22+
require_relative "mcp/tool/structured_response"
2223
require_relative "mcp/tool/annotations"
2324
require_relative "mcp/transport"
2425
require_relative "mcp/version"

lib/mcp/tool/structured_response.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module MCP
4+
class Tool
5+
class StructuredResponse
6+
attr_reader :content, :is_error
7+
8+
# @param structured_content [Hash] The structured content of the response, must be provided.
9+
# @param content [String, nil] The content array of the response, can be nil. If nil will generate a single element with structured content converted to JSON string.
10+
# @param is_error [Boolean] Indicates if the response is an error.
11+
def initialize(structured_content, content: nil, is_error: false)
12+
raise ArgumentError, "structured_content must be provided" if structured_content.nil?
13+
14+
@structured_content = structured_content
15+
@content = content || [{ type: :text, text: @structured_content.to_json }]
16+
@is_error = is_error
17+
end
18+
19+
def to_h
20+
{ content:, structuredContent: @structured_content, isError: is_error }.compact
21+
end
22+
end
23+
end
24+
end

test/mcp/server_test.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,31 @@ class ServerTest < ActiveSupport::TestCase
257257
assert_instrumentation_data({ method: "tools/call", tool_name: })
258258
end
259259

260+
test "#handle_json tools/call executes tool and returns structured response result" do
261+
tool_name = "test_tool"
262+
tool_args = { arg: "value" }
263+
tool_response = Tool::StructuredResponse.new({ result: "success" })
264+
265+
@tool.expects(:call).with(arg: "value", server_context: nil).returns(tool_response)
266+
267+
request = JSON.generate({
268+
jsonrpc: "2.0",
269+
method: "tools/call",
270+
params: { name: tool_name, arguments: tool_args },
271+
id: 1,
272+
})
273+
expected_response = {
274+
content: [{ type: "text", text: "{\"result\":\"success\"}" }],
275+
structuredContent: { result: "success" },
276+
isError: false,
277+
}
278+
279+
raw_response = @server.handle_json(request)
280+
response = JSON.parse(raw_response, symbolize_names: true) if raw_response
281+
assert_equal expected_response, response[:result] if response
282+
assert_instrumentation_data({ method: "tools/call", tool_name: })
283+
end
284+
260285
test "#handle_json tools/call executes tool and returns result, when the tool is typed with Sorbet" do
261286
class TypedTestTool < Tool
262287
tool_name "test_tool"

test/test_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
require_relative "instrumentation_test_helper"
1919

20-
Minitest::Reporters.use!(Minitest::Reporters::ProgressReporter.new)
20+
Minitest::Reporters.use!(Minitest::Reporters::ProgressReporter.new) unless ENV["RM_INFO"]
2121

2222
module ActiveSupport
2323
class TestCase

0 commit comments

Comments
 (0)