Skip to content

Commit c5591be

Browse files
atesgoraltopherbullock
authored andcommitted
Internal Release 0.2.4
- remove activesupport as a runtime dependency - use alternative to activesupport/inflector methods - use `json_rpc_handler` gem - add json_rpc_handler to gem dependencies
1 parent 6004f42 commit c5591be

File tree

18 files changed

+145
-507
lines changed

18 files changed

+145
-507
lines changed

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,5 @@ gem "minitest", "~> 5.1", require: false
1010
gem "rake", "~> 13.0"
1111
gem "rubocop-shopify", require: false
1212

13-
gem "activesupport"
1413
gem "minitest-reporters"
1514
gem "mocha"

Gemfile.lock

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
PATH
22
remote: .
33
specs:
4-
model_context_protocol (0.2.2)
5-
activesupport
4+
model_context_protocol (0.2.4)
5+
json_rpc_handler (~> 0.1)
66

77
GEM
88
remote: https://rubygems.org/
@@ -32,6 +32,7 @@ GEM
3232
i18n (1.14.7)
3333
concurrent-ruby (~> 1.0)
3434
json (2.11.3)
35+
json_rpc_handler (0.1.1)
3536
language_server-protocol (3.17.0.4)
3637
lint_roller (1.1.0)
3738
logger (1.7.0)

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ MCP spec includes [Tools](https://modelcontextprotocol.io/docs/concepts/tools) w
6262
This gem provides a `ModelContextProtocol::Tool` class that can be used to create tools in two ways:
6363

6464
1. As a class definition:
65+
6566
```ruby
6667
class MyTool < ModelContextProtocol::Tool
6768
tool_name "my_tool"
@@ -77,6 +78,7 @@ tool = MyTool.new
7778
```
7879

7980
2. By using the `ModelContextProtocol::Tool.define` method with a block:
81+
8082
```ruby
8183
tool = ModelContextProtocol::Tool.define(name: "my_tool", description: "This tool performs specific functionality...") do |args|
8284
Tool::Response.new([{ type: "text", content: "OK" }])
@@ -90,6 +92,7 @@ MCP spec includes [Prompts](https://modelcontextprotocol.io/docs/concepts/prompt
9092
The `ModelContextProtocol::Prompt` class provides two ways to create prompts:
9193

9294
1. As a class definition with metadata:
95+
9396
```ruby
9497
class MyPrompt < ModelContextProtocol::Prompt
9598
prompt_name "my_prompt" # Optional - defaults to underscored class name
@@ -121,6 +124,7 @@ end
121124
```
122125

123126
2. Using the `ModelContextProtocol::Prompt.define` method:
127+
124128
```ruby
125129
prompt = ModelContextProtocol::Prompt.define(
126130
name: "my_prompt",
@@ -168,6 +172,7 @@ server = ModelContextProtocol::Server.new(
168172
```
169173

170174
The server will handle prompt listing and execution through the MCP protocol methods:
175+
171176
- `prompts/list` - Lists all registered prompts and their schemas
172177
- `prompts/get` - Retrieves and executes a specific prompt with arguments
173178

lib/model_context_protocol.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4-
require "model_context_protocol/json_rpc"
54
require "model_context_protocol/server"
5+
require "model_context_protocol/string_utils"
66
require "model_context_protocol/tool"
77
require "model_context_protocol/content"
88
require "model_context_protocol/resource"

lib/model_context_protocol/json_rpc/error.rb

Lines changed: 0 additions & 58 deletions
This file was deleted.

lib/model_context_protocol/json_rpc/request.rb

Lines changed: 0 additions & 50 deletions
This file was deleted.

lib/model_context_protocol/json_rpc/response.rb

Lines changed: 0 additions & 29 deletions
This file was deleted.

lib/model_context_protocol/prompt.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def prompt_name(value)
6060
end
6161

6262
def name_value
63-
@name_value || name.demodulize.underscore
63+
@name_value || StringUtils.handle_from_class_name(name)
6464
end
6565

6666
def description(value)

lib/model_context_protocol/server.rb

Lines changed: 28 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require "json_rpc_handler"
4+
35
module ModelContextProtocol
46
class Server
57
PROTOCOL_VERSION = "2024-11-05"
@@ -26,20 +28,20 @@ def initialize(name: "model_context_protocol", tools: [], prompts: [], resources
2628
end
2729

2830
def handle(request)
29-
response = begin
30-
parsed_request = JsonRPC::Request.parse(request)
31-
parsed_request.validate!
32-
33-
if parsed_request.notification?
34-
handle_notification(parsed_request)
31+
JsonRpcHandler.handle(request) do |method|
32+
handler = case method
33+
when "tools/list"
34+
->(params) { { tools: @handlers["tools/list"].call(params) } }
35+
when "prompts/list"
36+
->(params) { { prompts: @handlers["prompts/list"].call(params) } }
37+
when "resources/list"
38+
->(params) { { resources: @handlers["resources/list"].call(params) } }
3539
else
36-
handle_method(parsed_request)
40+
@handlers[method]
3741
end
38-
rescue JsonRPC::Error => e
39-
JsonRPC::Response.new(id: parsed_request&.id, error: e)
40-
end
4142

42-
response
43+
handler
44+
end
4345
end
4446

4547
def resources_list_handler(&block)
@@ -86,29 +88,6 @@ def server_info
8688
}
8789
end
8890

89-
def handle_notification(request)
90-
nil
91-
end
92-
93-
def handle_method(request)
94-
request_handler = @handlers[request.method]
95-
raise JsonRPC::MethodNotFoundError.new(message: "Method not found #{request.method}") unless request_handler
96-
97-
result = request_handler.call(request)
98-
wrapped_result = case request.method
99-
when "tools/list"
100-
{ tools: result }
101-
when "prompts/list"
102-
{ prompts: result }
103-
when "resources/list"
104-
{ resources: result }
105-
else
106-
result
107-
end
108-
109-
JsonRPC::Response.new(id: request.id, result: wrapped_result)
110-
end
111-
11291
def init(request)
11392
{
11493
protocolVersion: PROTOCOL_VERSION,
@@ -121,43 +100,33 @@ def ping(request)
121100
"pong"
122101
end
123102

103+
def list_tools(request)
104+
@tools.map { |_, tool| tool.to_h }
105+
end
106+
124107
def call_tool(request)
125-
tool_name = request.params&.dig("name")
108+
tool_name = request[:name]
126109
tool = tools[tool_name]
127-
raise JsonRPC::MethodNotFoundError.new(message: "Tool not found #{tool_name}") unless tool
128-
129-
tool_args = request.params&.dig("arguments")
130-
131-
result = begin
132-
tool.call(**tool_args)
133-
rescue => e
134-
raise JsonRPC::InternalError.new(message: e.message)
135-
end
110+
raise "Tool not found #{tool_name}" unless tool
136111

112+
result = tool.call(**request[:arguments])
137113
result.to_h
138114
end
139115

140-
def list_tools(request)
141-
@tools.map { |_, tool| tool.to_h }
142-
end
143-
144116
def list_prompts(request)
145117
@prompts.map { |_, prompt| prompt.to_h }
146118
end
147119

148120
def get_prompt(request)
149-
prompt_name = request.params&.dig("name")
121+
prompt_name = request[:name]
150122
prompt = @prompts[prompt_name]
151-
raise JsonRPC::MethodNotFoundError.new(message: "Prompt not found #{prompt_name}") unless prompt
152123

153-
begin
154-
prompt_args = request.params&.dig("arguments")
155-
prompt.validate_arguments!(prompt_args)
124+
raise "Prompt not found #{prompt_name}" unless prompt
156125

157-
result = prompt.template(prompt_args)
158-
rescue ArgumentError => e
159-
raise JsonRPC::InvalidParamsError.new(message: e.message)
160-
end
126+
prompt_args = request[:arguments]
127+
prompt.validate_arguments!(prompt_args)
128+
129+
result = prompt.template(prompt_args)
161130

162131
result.to_h
163132
end
@@ -167,10 +136,10 @@ def list_resources(request)
167136
end
168137

169138
def read_resource(request)
170-
resource_uri = request.params&.dig("uri")
139+
resource_uri = request[:uri]
171140

172141
resource = @resource_index[resource_uri]
173-
raise JsonRPC::MethodNotFoundError.new(message: "Resource not found #{resource_uri}") unless resource
142+
raise "Resource not found #{resource_uri}" unless resource
174143

175144
resource.to_h
176145
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module ModelContextProtocol
5+
module StringUtils
6+
extend self
7+
8+
def handle_from_class_name(class_name)
9+
underscore(demodulize(class_name))
10+
end
11+
12+
private
13+
14+
def demodulize(path)
15+
path.to_s.split("::").last || path.to_s
16+
end
17+
18+
def underscore(camel_cased_word)
19+
camel_cased_word.dup
20+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
21+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
22+
.tr("-", "_")
23+
.downcase
24+
end
25+
end
26+
end

0 commit comments

Comments
 (0)