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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,10 @@ e.g. around authentication state.

Tools can include annotations that provide additional metadata about their behavior. The following annotations are supported:

- `destructive_hint`: Indicates if the tool performs destructive operations
- `idempotent_hint`: Indicates if the tool's operations are idempotent
- `open_world_hint`: Indicates if the tool operates in an open world context
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state)
- `destructive_hint`: Indicates if the tool performs destructive operations. Defaults to true
- `idempotent_hint`: Indicates if the tool's operations are idempotent. Defaults to false
- `open_world_hint`: Indicates if the tool operates in an open world context. Defaults to true
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state). Defaults to false
- `title`: A human-readable title for the tool

Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method.
Expand Down
8 changes: 4 additions & 4 deletions lib/mcp/tool/annotations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
module MCP
class Tool
class Annotations
attr_reader :title, :read_only_hint, :destructive_hint, :idempotent_hint, :open_world_hint
attr_reader :destructive_hint, :idempotent_hint, :open_world_hint, :read_only_hint, :title

def initialize(title: nil, read_only_hint: nil, destructive_hint: nil, idempotent_hint: nil, open_world_hint: nil)
def initialize(destructive_hint: true, idempotent_hint: false, open_world_hint: true, read_only_hint: false, title: nil)
@title = title
@read_only_hint = read_only_hint
@destructive_hint = destructive_hint
Expand All @@ -15,11 +15,11 @@ def initialize(title: nil, read_only_hint: nil, destructive_hint: nil, idempoten

def to_h
{
title:,
readOnlyHint: read_only_hint,
destructiveHint: destructive_hint,
idempotentHint: idempotent_hint,
openWorldHint: open_world_hint,
readOnlyHint: read_only_hint,
title:,
}.compact
end
end
Expand Down
35 changes: 19 additions & 16 deletions test/mcp/tool/annotations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ class Tool
class AnnotationsTest < ActiveSupport::TestCase
test "Tool::Annotations initializes with all properties" do
annotations = Tool::Annotations.new(
title: "Test Tool",
read_only_hint: true,
destructive_hint: false,
idempotent_hint: true,
open_world_hint: false,
read_only_hint: true,
title: "Test Tool",
)

assert_equal "Test Tool", annotations.title
assert annotations.read_only_hint
refute annotations.destructive_hint
assert annotations.idempotent_hint
refute annotations.open_world_hint
assert annotations.read_only_hint
assert_equal "Test Tool", annotations.title
end

test "Tool::Annotations initializes with partial properties" do
annotations = Tool::Annotations.new(
title: "Test Tool",
read_only_hint: true,
title: "Test Tool",
)

assert_equal "Test Tool", annotations.title
assert annotations.destructive_hint
refute annotations.idempotent_hint
assert annotations.open_world_hint
assert annotations.read_only_hint
assert_nil annotations.destructive_hint
assert_nil annotations.idempotent_hint
assert_nil annotations.open_world_hint
assert_equal "Test Tool", annotations.title
end

test "Tool::Annotations#to_h omits nil values" do
Expand All @@ -41,34 +41,37 @@ class AnnotationsTest < ActiveSupport::TestCase
)

expected = {
title: "Test Tool",
destructiveHint: true,
idempotentHint: false,
openWorldHint: true,
readOnlyHint: true,
title: "Test Tool",
}
assert_equal expected, annotations.to_h
end

test "Tool::Annotations#to_h handles all properties" do
annotations = Tool::Annotations.new(
title: "Test Tool",
read_only_hint: true,
destructive_hint: false,
idempotent_hint: true,
open_world_hint: false,
read_only_hint: true,
title: "Test Tool",
)

expected = {
title: "Test Tool",
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
readOnlyHint: true,
title: "Test Tool",
}
assert_equal expected, annotations.to_h
end

test "Tool::Annotations#to_h returns empty hash when all values are nil" do
test "Tool::Annotations#to_h returns hash with default hint values" do
annotations = Tool::Annotations.new
assert_empty annotations.to_h
assert_equal({ destructiveHint: true, idempotentHint: false, openWorldHint: true, readOnlyHint: false }, annotations.to_h)
end
end
end
Expand Down
12 changes: 6 additions & 6 deletions test/mcp/tool_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class TestTool < Tool
description "a test tool for testing"
input_schema({ properties: { message: { type: "string" } }, required: ["message"] })
annotations(
title: "Test Tool",
read_only_hint: true,
destructive_hint: false,
idempotent_hint: true,
open_world_hint: false,
read_only_hint: true,
title: "Test Tool",
)

class << self
Expand All @@ -36,11 +36,11 @@ def call(message:, server_context: nil)
test "#to_h includes annotations when present" do
tool = TestTool
expected_annotations = {
title: "Test Tool",
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
readOnlyHint: true,
title: "Test Tool",
}
assert_equal expected_annotations, tool.to_h[:annotations]
end
Expand Down Expand Up @@ -142,15 +142,15 @@ class InputSchemaTool < Tool
assert_equal "Mock Tool", tool.title
assert_equal "a mock tool for testing", tool.description
assert_equal tool.input_schema, Tool::InputSchema.new
assert_equal({ readOnlyHint: true, title: "Mock Tool" }, tool.annotations_value.to_h)
assert_equal({ destructiveHint: true, idempotentHint: false, openWorldHint: true, readOnlyHint: true, title: "Mock Tool" }, tool.annotations_value.to_h)
end

test "Tool class method annotations can be set and retrieved" do
class AnnotationsTestTool < Tool
tool_name "annotations_test"
annotations(
title: "Annotations Test",
read_only_hint: true,
title: "Annotations Test",
)
end

Expand Down