Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/model_context_protocol/server/tool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def serialized
end

class << self
attr_reader :name, :description, :title, :input_schema, :output_schema
attr_reader :name, :description, :title, :input_schema, :output_schema, :annotations

def define(&block)
definition_dsl = DefinitionDSL.new
Expand All @@ -94,6 +94,7 @@ def define(&block)
@title = definition_dsl.title
@input_schema = definition_dsl.input_schema
@output_schema = definition_dsl.output_schema
@annotations = definition_dsl.annotations
end

def inherited(subclass)
Expand All @@ -102,6 +103,7 @@ def inherited(subclass)
subclass.instance_variable_set(:@title, @title)
subclass.instance_variable_set(:@input_schema, @input_schema)
subclass.instance_variable_set(:@output_schema, @output_schema)
subclass.instance_variable_set(:@annotations, @annotations)
end

def call(arguments, client_logger, server_logger, context = {})
Expand All @@ -120,6 +122,7 @@ def definition
result = {name: @name, description: @description, inputSchema: @input_schema}
result[:title] = @title if @title
result[:outputSchema] = @output_schema if @output_schema
result[:annotations] = @annotations if @annotations
result
end
end
Expand Down Expand Up @@ -149,6 +152,11 @@ def output_schema(&block)
@output_schema = instance_eval(&block) if block_given?
@output_schema
end

def annotations(&block)
@annotations = instance_eval(&block) if block_given?
@annotations
end
end
end
end
85 changes: 85 additions & 0 deletions spec/lib/model_context_protocol/server/tool_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,46 @@
)
end
end

it "sets annotations when provided" do
tool_with_annotations = Class.new(ModelContextProtocol::Server::Tool) do
define do
name "fetch"
description "Fetch the full contents of a single resource"
input_schema do
{
type: "object",
properties: {
id: {
type: "string",
description: "Unique identifier of the resource to fetch"
}
},
required: ["id"]
}
end
annotations do
{readOnlyHint: true}
end
end
end

expect(tool_with_annotations.annotations).to eq(readOnlyHint: true)
end

it "inherits annotations in subclasses" do
parent_tool = Class.new(ModelContextProtocol::Server::Tool) do
define do
name "fetch"
description "Fetch the full contents of a single resource"
input_schema { {type: "object", properties: {}, required: []} }
annotations { {readOnlyHint: true} }
end
end

child_tool = Class.new(parent_tool)
expect(child_tool.annotations).to eq({readOnlyHint: true})
end
end

describe "definition" do
Expand Down Expand Up @@ -350,6 +390,46 @@
}
)
end

it "includes annotations when provided" do
tool_with_annotations = Class.new(ModelContextProtocol::Server::Tool) do
define do
name "fetch"
description "Fetch the full contents of a single resource"
input_schema do
{
type: "object",
properties: {
id: {
type: "string",
description: "Unique identifier of the resource to fetch"
}
},
required: ["id"]
}
end
annotations do
{readOnlyHint: true}
end
end
end

expect(tool_with_annotations.definition).to eq(
name: "fetch",
description: "Fetch the full contents of a single resource",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Unique identifier of the resource to fetch"
}
},
required: ["id"]
},
annotations: {readOnlyHint: true}
)
end
end

describe "optional title field" do
Expand All @@ -373,6 +453,11 @@ def call
metadata = tool_without_title.definition
expect(metadata).not_to have_key(:title)
end

it "does not include annotations in definition when not provided" do
metadata = tool_without_title.definition
expect(metadata).not_to have_key(:annotations)
end
end

describe "server logger integration" do
Expand Down