diff --git a/CHANGELOG.md b/CHANGELOG.md index 71fa786..2eab845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Remove dead code across the codebase. - (Fix) Fix logdev references in server logger. - (Fix) Ensure context hash keys are symbolized when retrieved from session. +- (Fix) Fix `resource_link` content helper to return a Content object like all other content helpers. ## [0.6.0] - 2026-01-26 diff --git a/lib/model_context_protocol/server/content_helpers.rb b/lib/model_context_protocol/server/content_helpers.rb index 5325ab1..17546df 100644 --- a/lib/model_context_protocol/server/content_helpers.rb +++ b/lib/model_context_protocol/server/content_helpers.rb @@ -78,7 +78,7 @@ def resource_link(name:, uri:, meta: nil, annotations: {}, description: nil, mim size:, title:, uri: - ].serialized + ] end end end diff --git a/spec/lib/model_context_protocol/server/content_helpers_spec.rb b/spec/lib/model_context_protocol/server/content_helpers_spec.rb index fd6294c..a7116c2 100644 --- a/spec/lib/model_context_protocol/server/content_helpers_spec.rb +++ b/spec/lib/model_context_protocol/server/content_helpers_spec.rb @@ -217,30 +217,30 @@ describe "#resource_link" do context "with valid data" do - it "returns a serialized ResourceLink hash with required parameters" do + it "returns a ResourceLink content object with required parameters" do result = helper.resource_link(name: "test-file", uri: "https://example.com/test.txt") - expect(result).to be_a(Hash) - expect(result[:name]).to eq("test-file") - expect(result[:uri]).to eq("https://example.com/test.txt") - expect(result[:type]).to eq("resource_link") + expect(result).to be_a(ModelContextProtocol::Server::Content::ResourceLink) + expect(result.name).to eq("test-file") + expect(result.uri).to eq("https://example.com/test.txt") + expect(result.meta).to be_nil + expect(result.annotations).to be_nil end - it "returns a serialized ResourceLink hash with meta" do + it "returns a ResourceLink content object with meta" do result = helper.resource_link( name: "test-file", uri: "https://example.com/test.txt", meta: {api_version: "v1"} ) - expect(result).to be_a(Hash) - expect(result[:name]).to eq("test-file") - expect(result[:uri]).to eq("https://example.com/test.txt") - expect(result[:type]).to eq("resource_link") - expect(result[:_meta]).to eq({api_version: "v1"}) + expect(result).to be_a(ModelContextProtocol::Server::Content::ResourceLink) + expect(result.name).to eq("test-file") + expect(result.uri).to eq("https://example.com/test.txt") + expect(result.meta).to eq({api_version: "v1"}) end - it "returns a serialized ResourceLink hash with annotations" do + it "returns a ResourceLink content object with annotations" do annotations = {audience: "user", priority: 0.8} result = helper.resource_link( name: "test-file", @@ -248,14 +248,13 @@ annotations: annotations ) - expect(result).to be_a(Hash) - expect(result[:name]).to eq("test-file") - expect(result[:uri]).to eq("https://example.com/test.txt") - expect(result[:type]).to eq("resource_link") - expect(result[:annotations]).to eq({audience: "user", priority: 0.8}) + expect(result).to be_a(ModelContextProtocol::Server::Content::ResourceLink) + expect(result.name).to eq("test-file") + expect(result.uri).to eq("https://example.com/test.txt") + expect(result.annotations).to eq({audience: "user", priority: 0.8}) end - it "returns a serialized ResourceLink hash with all optional parameters" do + it "returns a ResourceLink content object with all optional parameters" do annotations = {audience: ["user", "assistant"], last_modified: "2025-01-12T15:00:58Z", priority: 1.0} result = helper.resource_link( name: "test-file", @@ -268,23 +267,22 @@ title: "Test File" ) - expect(result).to be_a(Hash) - expect(result[:name]).to eq("test-file") - expect(result[:uri]).to eq("https://example.com/test.txt") - expect(result[:type]).to eq("resource_link") - expect(result[:_meta]).to eq({external: true}) - expect(result[:annotations]).to eq({ + expect(result).to be_a(ModelContextProtocol::Server::Content::ResourceLink) + expect(result.name).to eq("test-file") + expect(result.uri).to eq("https://example.com/test.txt") + expect(result.meta).to eq({external: true}) + expect(result.annotations).to eq({ audience: ["user", "assistant"], lastModified: "2025-01-12T15:00:58Z", priority: 1.0 }) - expect(result[:description]).to eq("A test file") - expect(result[:mimeType]).to eq("text/plain") - expect(result[:size]).to eq(1024) - expect(result[:title]).to eq("Test File") + expect(result.description).to eq("A test file") + expect(result.mime_type).to eq("text/plain") + expect(result.size).to eq(1024) + expect(result.title).to eq("Test File") end - it "returns a serialized ResourceLink hash with only some optional parameters" do + it "returns a ResourceLink content object with only some optional parameters" do result = helper.resource_link( name: "test-file", uri: "https://example.com/test.txt", @@ -292,16 +290,25 @@ size: 2048 ) - expect(result).to be_a(Hash) - expect(result[:name]).to eq("test-file") - expect(result[:uri]).to eq("https://example.com/test.txt") - expect(result[:type]).to eq("resource_link") - expect(result[:description]).to eq("A test file") - expect(result[:size]).to eq(2048) - expect(result).not_to have_key(:_meta) - expect(result).not_to have_key(:annotations) - expect(result).not_to have_key(:mimeType) - expect(result).not_to have_key(:title) + expect(result).to be_a(ModelContextProtocol::Server::Content::ResourceLink) + expect(result.name).to eq("test-file") + expect(result.uri).to eq("https://example.com/test.txt") + expect(result.description).to eq("A test file") + expect(result.size).to eq(2048) + expect(result.meta).to be_nil + expect(result.annotations).to be_nil + expect(result.mime_type).to be_nil + expect(result.title).to be_nil + end + + it "serializes correctly" do + result = helper.resource_link(name: "test-file", uri: "https://example.com/test.txt") + serialized = result.serialized + + expect(serialized).to be_a(Hash) + expect(serialized[:name]).to eq("test-file") + expect(serialized[:uri]).to eq("https://example.com/test.txt") + expect(serialized[:type]).to eq("resource_link") end end end diff --git a/spec/support/tools/test_tool_with_resource_link_response.rb b/spec/support/tools/test_tool_with_resource_link_response.rb index ce97b37..ff7ce46 100644 --- a/spec/support/tools/test_tool_with_resource_link_response.rb +++ b/spec/support/tools/test_tool_with_resource_link_response.rb @@ -19,18 +19,12 @@ class TestToolWithResourceLinkResponse < ModelContextProtocol::Server::Tool def call name = arguments[:name] - # Create ResourceLink content object directly since the helper returns serialized form - link = ModelContextProtocol::Server::Content::ResourceLink[ - meta: nil, - annotations: nil, + respond_with content: resource_link( + name: name, + uri: "file:///docs/#{name}.md", description: "A document named #{name}", mime_type: "text/markdown", - name: name, - size: nil, - title: "Document: #{name}", - uri: "file:///docs/#{name}.md" - ] - - respond_with content: link + title: "Document: #{name}" + ) end end diff --git a/tasks/templates/dev-http-puma.erb b/tasks/templates/dev-http-puma.erb index ab0547d..1f10102 100644 --- a/tasks/templates/dev-http-puma.erb +++ b/tasks/templates/dev-http-puma.erb @@ -84,6 +84,7 @@ ModelContextProtocol::Server.with_streamable_http_transport do |config| register TestToolWithProgressableAndCancellable register TestToolWithAnnotations register TestToolWithSecuritySchemes + register TestToolWithResourceLinkResponse register TestToolWithAudioResponse if flag_enabled?('extra_tools') end end diff --git a/tasks/templates/dev-http.erb b/tasks/templates/dev-http.erb index 722ac2b..e7e68d8 100644 --- a/tasks/templates/dev-http.erb +++ b/tasks/templates/dev-http.erb @@ -84,6 +84,7 @@ ModelContextProtocol::Server.with_streamable_http_transport do |config| register TestToolWithProgressableAndCancellable register TestToolWithAnnotations register TestToolWithSecuritySchemes + register TestToolWithResourceLinkResponse register TestToolWithAudioResponse if flag_enabled?('extra_tools') end end diff --git a/tasks/templates/dev.erb b/tasks/templates/dev.erb index 2d5996c..80ac28c 100644 --- a/tasks/templates/dev.erb +++ b/tasks/templates/dev.erb @@ -66,6 +66,7 @@ server = ModelContextProtocol::Server.with_stdio_transport do |config| register TestToolWithProgressableAndCancellable register TestToolWithAnnotations register TestToolWithSecuritySchemes + register TestToolWithResourceLinkResponse end end end