Refactor towards simple data/config classes#56
Refactor towards simple data/config classes#56sambostock wants to merge 4 commits intomodelcontextprotocol:mainfrom
Conversation
Also, implement `Annotations#to_h`
tests stuff
|
@koic asked the following on #48, which is also relevant here, so I'm transcribing it:
|
My original motivation for this was that as a consumer I found the API confusing: why define a class that will never be instantiated and only have singleton methods? In addition, the existing approach has (IMO) a higher implementation complexity because of the various "macros" for setting/getting the configured values, as well as the unusual In this new approach, we maintain a clear separation between the "schema" and the behaviour (isolated in the handler block):
Technically, this gem's README says it follows Semantic Versioning, and since we're still on a 0.x version, we're "free to make breaking changes at any time", and perhaps we should consider if it makes more sense to rapidly iterate towards a feature complete 1.0 release. That said, if we want to go the deprecation warning route, I think we would do something like the following:
Footnotes
|
kfischer-okarin
left a comment
There was a problem hiding this comment.
One comment I had seeing this PR in general... I think it might be more idiomatic and simpler to just use actual Data classes offered by Ruby
|
@kfischer-okarin I flip-flopped on using I gave it a shot based on your feedback (sambostock/mcp-ruby-sdk@data...sambostock:mcp-ruby-sdk:data-define), and based on what I found, I think it's still preferable to stick to POROs:
To expand on the last point # frozen_string_literal: true
require "minitest/autorun"
class UsingPORO
attr_reader :required_positional, :optional_keyword
def initialize(required_positional, optional_keyword: nil)
@required_positional = required_positional
@optional_keyword = optional_keyword
end
end
UsingData = Data.define(:required_positional, :optional_keyword) do
def initialize(required_positional:, optional_keyword: nil) = super(required_positional:, optional_keyword:)
end
class MixedPositionalAndKeywordTest < Minitest::Test
[UsingPORO, UsingData].each do |klass|
define_method "test_#{klass.name}.new('positional') works" do
klass.new("positional")
end
define_method "test_#{klass.name}.new('positional', 'optional') raises ArgumentError" do
assert_raises(ArgumentError) { klass.new("positional", "optional") }
end
define_method "test_#{klass.name}.new('positional', optional_keyword: 'optional') works" do
klass.new("positional", optional_keyword: "optional")
end
end
end$ ruby data.rb
Run options: --seed 26182
# Running:
F.E...
Finished in 0.000440s, 13636.3644 runs/s, 4545.4548 assertions/s.
1) Failure:
MixedPositionalAndKeywordTest#test_UsingData.new('positional', 'optional') raises ArgumentError [data.rb:25]:
ArgumentError expected but nothing was raised.
2) Error:
MixedPositionalAndKeywordTest#test_UsingData.new('positional', optional_keyword: 'optional') works:
ArgumentError: wrong number of arguments (given 2, expected 0)
data.rb:29:in 'UsingData.new'
data.rb:29:in 'block (2 levels) in <class:MixedPositionalAndKeywordTest>'
6 runs, 2 assertions, 1 failures, 1 errors, 0 skipsIMO given the simplicity of the API we're trying to offer, I think it's simplest to stick to POROs. |
|
@sambostock Ok, good reasonable points about not choosing the Data class approach - I can get behind that 👍🏻 It would be nice if the examples/docs were also updated to reflect the new API I was wondering if it would also be a good idea to make resources and resource templates also take blocks as I also think it's okay to be not sooo careful about breaking changes since we're before v1.0.0 - the error message and the necessary rewrite are both straightforward enough. If you wanted to be a bit nicer, you could offer a little mini code rewrite example right inside the error message. Or give those examples in longer form in a Changelog entry and refer to that in the error message. |
|
I agree with all of the above. Incidentally, if merged, #55 would enforce the examples are updated. I'll explore the resource changes you mentioned next week. 👍 |
This PR is an alternative to #48, with the same underlying motivations. It explores the idea of having tools and prompts be defined as simple data/config/value objects, and drops support for the current inheritance based approach.
It is a work in progress, but I have opted to open a draft now to get feedback, especially as it relates to other PRs, such as #54.
Motivation and Context
How Has This Been Tested?
Breaking Changes
Types of changes
Checklist
Additional context