Skip to content

Commit a838927

Browse files
authored
fix: embedded resource format per MCP spec (#168)
type: "resource" now wraps content in nested resource object Release-As: 0.102.0
1 parent 030d9d3 commit a838927

3 files changed

Lines changed: 20 additions & 15 deletions

File tree

lib/action_mcp/content/resource.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,16 @@ def initialize(uri, mime_type = "text/plain", text: nil, blob: nil, annotations:
2828
end
2929

3030
# Returns a hash representation of the resource content.
31+
# Per MCP spec, embedded resources have type "resource" with a nested resource object.
3132
#
3233
# @return [Hash] The hash representation of the resource content.
3334
def to_h
34-
resource_data = super.merge(uri: @uri, mimeType: @mime_type)
35-
resource_data[:text] = @text if @text
36-
resource_data[:blob] = @blob if @blob
37-
resource_data
35+
inner = { uri: @uri, mimeType: @mime_type }
36+
inner[:text] = @text if @text
37+
inner[:blob] = @blob if @blob
38+
inner[:annotations] = @annotations if @annotations
39+
40+
{ type: @type, resource: inner }
3841
end
3942
end
4043
end

test/action_mcp/content/resource_test.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,22 @@ class ResourceTest < ActiveSupport::TestCase
5050

5151
test "#to_h returns correct hash with uri and mime_type" do
5252
resource = Resource.new("gemfile://test", "text/plain", annotations: nil)
53-
expected = { type: "resource", uri: "gemfile://test", mimeType: "text/plain" }
53+
expected = { type: "resource", resource: { uri: "gemfile://test", mimeType: "text/plain" } }
5454

5555
assert_equal expected, resource.to_h
5656
end
5757

5858
test "#to_h includes text when present" do
5959
resource = Resource.new("gemfile://test", "text/plain", text: "sample text", annotations: nil)
60-
expected = { type: "resource", uri: "gemfile://test", mimeType: "text/plain", text: "sample text" }
60+
expected = { type: "resource", resource: { uri: "gemfile://test", mimeType: "text/plain", text: "sample text" } }
6161

6262
assert_equal expected, resource.to_h
6363
end
6464

6565
test "#to_h includes blob when present" do
6666
blob = Base64.strict_encode64("sample blob")
6767
resource = Resource.new("gemfile://test", "application/octet-stream", blob: blob, annotations: nil)
68-
expected = { type: "resource", uri: "gemfile://test", mimeType: "application/octet-stream", blob: blob }
68+
expected = { type: "resource", resource: { uri: "gemfile://test", mimeType: "application/octet-stream", blob: blob } }
6969

7070
assert_equal expected, resource.to_h
7171
end
@@ -82,9 +82,11 @@ class ResourceTest < ActiveSupport::TestCase
8282

8383
expected = {
8484
type: "resource",
85-
uri: "gemfile://test",
86-
mimeType: "application/json",
87-
text: gemfile_json
85+
resource: {
86+
uri: "gemfile://test",
87+
mimeType: "application/json",
88+
text: gemfile_json
89+
}
8890
}
8991

9092
assert_equal expected, resource.to_h

test/action_mcp/content_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,24 +61,24 @@ class ContentTest < ActiveSupport::TestCase
6161

6262
# Without optional text or blob
6363
resource = Resource.new(uri, mime_type)
64-
expected = { type: "resource", uri: uri, mimeType: mime_type }
64+
expected = { type: "resource", resource: { uri: uri, mimeType: mime_type } }
6565
assert_equal expected, resource.to_h
6666

6767
# With text only
6868
text_content = "Optional text"
6969
resource_with_text = Resource.new(uri, mime_type, text: text_content)
70-
expected_with_text = { type: "resource", uri: uri, mimeType: mime_type, text: text_content }
70+
expected_with_text = { type: "resource", resource: { uri: uri, mimeType: mime_type, text: text_content } }
7171
assert_equal expected_with_text, resource_with_text.to_h
7272

7373
# With blob only
7474
blob_content = "base64encodedblob"
7575
resource_with_blob = Resource.new(uri, mime_type, blob: blob_content)
76-
expected_with_blob = { type: "resource", uri: uri, mimeType: mime_type, blob: blob_content }
76+
expected_with_blob = { type: "resource", resource: { uri: uri, mimeType: mime_type, blob: blob_content } }
7777
assert_equal expected_with_blob, resource_with_blob.to_h
7878

7979
# With both text and blob
8080
resource_full = Resource.new(uri, mime_type, text: text_content, blob: blob_content)
81-
expected_full = { type: "resource", uri: uri, mimeType: mime_type, text: text_content, blob: blob_content }
81+
expected_full = { type: "resource", resource: { uri: uri, mimeType: mime_type, text: text_content, blob: blob_content } }
8282
assert_equal expected_full, resource_full.to_h
8383
end
8484

@@ -87,7 +87,7 @@ class ContentTest < ActiveSupport::TestCase
8787
mime_type = "application/pdf"
8888
annotations = { "audience" => [ "user" ], "priority" => 1 }
8989
resource = Resource.new(uri, mime_type, annotations: annotations)
90-
expected = { type: "resource", uri: uri, mimeType: mime_type, annotations: annotations }
90+
expected = { type: "resource", resource: { uri: uri, mimeType: mime_type, annotations: annotations } }
9191
assert_equal annotations, resource.annotations
9292
assert_equal expected, resource.to_h
9393
end

0 commit comments

Comments
 (0)