Skip to content
Open
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: 5 additions & 5 deletions lib/mcp/string_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ module MCP
module StringUtils
extend self

NAMESPACE_SEPARATOR = "/"

def handle_from_class_name(class_name)
underscore(demodulize(class_name))
class_name.to_s.split("::").map do |name|
underscore(name)
end.join(NAMESPACE_SEPARATOR)
end

private

def demodulize(path)
path.to_s.split("::").last || path.to_s
end

def underscore(camel_cased_word)
camel_cased_word.dup
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
Expand Down
4 changes: 2 additions & 2 deletions test/mcp/prompt_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def template(args, server_context:)

prompt = DefaultNamePrompt

assert_equal "default_name_prompt", prompt.name_value
assert_equal "mcp/prompt_test/default_name_prompt", prompt.name_value
assert_equal "a mock prompt for testing", prompt.description
assert_equal "test_argument", prompt.arguments.first.name
end
Expand Down Expand Up @@ -184,7 +184,7 @@ class NoArgumentsPrompt < Prompt
prompt = NoArgumentsPrompt

expected = {
name: "no_arguments_prompt",
name: "mcp/prompt_test/no_arguments_prompt",
description: "No arguments prompt",
}

Expand Down
16 changes: 16 additions & 0 deletions test/mcp/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,22 @@ def call(message:, server_context: nil)
assert_instrumentation_data({ method: "tools/call", tool_name: "tool_that_raises" })
end

test "registers tools with the same class name in different namespaces" do
module Foo
class Example < Tool
end
end

class Bar
class Example < Tool
end
end

server = Server.new(tools: [Foo::Example, Bar::Example])

assert_equal(2, server.tools.count)
end

test "#handle_json returns error response with isError true if the tool raises an error" do
request = JSON.generate({
jsonrpc: "2.0",
Expand Down
8 changes: 4 additions & 4 deletions test/mcp/string_utils_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ def test_handle_from_class_name_returns_the_class_name_without_the_module_for_a_
end

def test_handle_from_class_name_returns_the_class_name_without_the_module_for_a_class_with_a_single_parent_module
assert_equal("test", StringUtils.handle_from_class_name("Module::Test"))
assert_equal("test_class", StringUtils.handle_from_class_name("Module::TestClass"))
assert_equal("module/test", StringUtils.handle_from_class_name("Module::Test"))
assert_equal("module/test_class", StringUtils.handle_from_class_name("Module::TestClass"))
end

def test_handle_from_class_name_returns_the_class_name_without_the_module_for_a_class_with_multiple_parent_modules
assert_equal("test", StringUtils.handle_from_class_name("Module::Submodule::Test"))
assert_equal("test_class", StringUtils.handle_from_class_name("Module::Submodule::TestClass"))
assert_equal("module/submodule/test", StringUtils.handle_from_class_name("Module::Submodule::Test"))
assert_equal("module/submodule/test_class", StringUtils.handle_from_class_name("Module::Submodule::TestClass"))
end
end
end
2 changes: 1 addition & 1 deletion test/mcp/tool_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class DefaultNameTool < Tool

tool = DefaultNameTool

assert_equal "default_name_tool", tool.tool_name
assert_equal "mcp/tool_test/default_name_tool", tool.tool_name
end

test "input schema defaults to an empty hash" do
Expand Down