ruby-sdk/lib/model_context_protocol/tool.rb
Topher Bullock 7175d49108 Internal Release 0.5.0
Bump protocol version to 2025-03-26
Provide fallback value for tool_name
Make configuration per server
Add support for tool annotations
document tool annotations
2025-05-08 17:38:02 -04:00

118 lines
3 KiB
Ruby

# frozen_string_literal: true
module ModelContextProtocol
class Tool
class Response
attr_reader :content, :is_error
def initialize(content, is_error = false)
@content = content
@is_error = is_error
end
def to_h
{ content:, isError: is_error }.compact
end
end
class Annotations
attr_reader :title, :read_only_hint, :destructive_hint, :idempotent_hint, :open_world_hint
def initialize(title: nil, read_only_hint: nil, destructive_hint: nil, idempotent_hint: nil, open_world_hint: nil)
@title = title
@read_only_hint = read_only_hint
@destructive_hint = destructive_hint
@idempotent_hint = idempotent_hint
@open_world_hint = open_world_hint
end
def to_h
{
title:,
readOnlyHint: read_only_hint,
destructiveHint: destructive_hint,
idempotentHint: idempotent_hint,
openWorldHint: open_world_hint,
}.compact
end
end
class << self
NOT_SET = Object.new
attr_reader :description_value
attr_reader :input_schema_value
attr_reader :annotations_value
def call(*args, context:)
raise NotImplementedError, "Subclasses must implement call"
end
def to_h
result = {
name: name_value,
description: description_value,
inputSchema: input_schema_value,
}
result[:annotations] = annotations_value.to_h if annotations_value
result
end
def inherited(subclass)
super
subclass.instance_variable_set(:@name_value, nil)
subclass.instance_variable_set(:@description_value, nil)
subclass.instance_variable_set(:@input_schema_value, nil)
subclass.instance_variable_set(:@annotations_value, nil)
end
def tool_name(value = NOT_SET)
if value == NOT_SET
name_value
else
@name_value = value
end
end
def name_value
@name_value || StringUtils.handle_from_class_name(name)
end
def description(value = NOT_SET)
if value == NOT_SET
@description_value
else
@description_value = value
end
end
def input_schema(value = NOT_SET)
if value == NOT_SET
@input_schema_value
else
@input_schema_value = value
end
end
def annotations(hash = NOT_SET)
if hash == NOT_SET
@annotations_value
else
@annotations_value = Annotations.new(**hash)
end
end
def define(name: nil, description: nil, input_schema: nil, annotations: nil, &block)
Class.new(self) do
tool_name name
description description
input_schema input_schema
self.annotations(annotations) if annotations
define_singleton_method(:call) do |*args, context:|
instance_exec(*args, context:, &block)
end
end
end
end
end
end