Skip to content

Commit 89a8eb7

Browse files
committed
Support Sorbet typed tools
1 parent 784b8b8 commit 89a8eb7

File tree

5 files changed

+59
-1
lines changed

5 files changed

+59
-1
lines changed

Gemfile.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ GEM
9191
ruby-progressbar (1.13.0)
9292
ruby2_keywords (0.0.5)
9393
securerandom (0.4.1)
94+
sorbet (0.5.11495)
95+
sorbet-static (= 0.5.11495)
96+
sorbet-runtime (0.5.11495)
97+
sorbet-static (0.5.11495-universal-darwin)
98+
sorbet-static-and-runtime (0.5.11495)
99+
sorbet (= 0.5.11495)
100+
sorbet-runtime (= 0.5.11495)
94101
stringio (3.1.7)
95102
tzinfo (2.0.6)
96103
concurrent-ruby (~> 1.0)
@@ -112,6 +119,7 @@ DEPENDENCIES
112119
model_context_protocol!
113120
rake (~> 13.0)
114121
rubocop-shopify
122+
sorbet-static-and-runtime
115123

116124
BUNDLED WITH
117125
2.5.9

lib/model_context_protocol/server.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ def call_tool(request)
193193
end
194194

195195
begin
196-
call_params = tool.method(:call).parameters.flatten
196+
call_params = method_parameters(tool.method(:call))
197+
197198
if call_params.include?(:server_context)
198199
tool.call(**arguments.transform_keys(&:to_sym), server_context:).to_h
199200
else
@@ -254,5 +255,21 @@ def index_resources_by_uri(resources)
254255
hash[resource.uri] = resource
255256
end
256257
end
258+
259+
def method_parameters(method)
260+
default_value = method.parameters.flatten
261+
262+
if defined?(T::Utils) && T::Utils.respond_to?(:signature_for_method)
263+
method_sig = T::Utils.signature_for_method(method)
264+
265+
if method_sig
266+
method_sig.parameters.flatten
267+
else
268+
default_value
269+
end
270+
else
271+
default_value
272+
end
273+
end
257274
end
258275
end

model_context_protocol.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ Gem::Specification.new do |spec|
2929

3030
spec.add_dependency("json_rpc_handler", "~> 0.1")
3131
spec.add_development_dependency("activesupport")
32+
spec.add_development_dependency("sorbet-static-and-runtime")
3233
end

test/model_context_protocol/tool_test.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# typed: true
12
# frozen_string_literal: true
23

34
require "test_helper"
@@ -203,5 +204,34 @@ class UpdatableAnnotationsTool < Tool
203204
tool.annotations(title: "Updated")
204205
assert_equal tool.annotations_value.title, "Updated"
205206
end
207+
208+
test "#call with Sorbet typed tools invokes the tool block and returns the response" do
209+
class TypedTestTool < Tool
210+
tool_name "test_tool"
211+
description "a test tool for testing"
212+
input_schema({ properties: { message: { type: "string" } }, required: ["message"] })
213+
annotations(
214+
title: "Test Tool",
215+
read_only_hint: true,
216+
destructive_hint: false,
217+
idempotent_hint: true,
218+
open_world_hint: false,
219+
)
220+
221+
class << self
222+
extend T::Sig
223+
224+
sig { params(message: String, server_context: T.nilable(T.untyped)).returns(Tool::Response) }
225+
def call(message, server_context: nil)
226+
Tool::Response.new([{ type: "text", content: "OK" }])
227+
end
228+
end
229+
end
230+
231+
tool = TypedTestTool
232+
response = tool.call("test")
233+
assert_equal response.content, [{ type: "text", content: "OK" }]
234+
assert_equal response.is_error, false
235+
end
206236
end
207237
end

test/test_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
require "active_support"
1414
require "active_support/test_case"
1515

16+
require "sorbet-runtime"
17+
1618
require_relative "instrumentation_test_helper"
1719

1820
Minitest::Reporters.use!(Minitest::Reporters::ProgressReporter.new)

0 commit comments

Comments
 (0)