Skip to content

Commit 6917897

Browse files
committed
added notification spec for handling unknown notification types and log ignore unknown types
1 parent b1f0eb1 commit 6917897

File tree

6 files changed

+83
-8
lines changed

6 files changed

+83
-8
lines changed

lib/ruby_llm/mcp/notification_handler.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ def execute(notification)
2727
when "notifications/cancelled"
2828
# TODO: - do nothing at the moment until we support client operations
2929
else
30-
message = "Unknown notification type: #{notification.type} params:#{notification.params.to_h}"
31-
raise Errors::UnknownNotification.new(message: message)
30+
process_unknown_notification(notification)
3231
end
3332
end
3433

@@ -75,6 +74,11 @@ def default_process_logging_message(notification, logger: RubyLLM::MCP.logger)
7574
logger.fatal(message["message"])
7675
end
7776
end
77+
78+
def process_unknown_notification(notification)
79+
message = "Unknown notification type: #{notification.type} params: #{notification.params.to_h}"
80+
RubyLLM::MCP.logger.error(message)
81+
end
7882
end
7983
end
8084
end

lib/ruby_llm/mcp/response_handler.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def initialize(coordinator)
1010
@client = coordinator.client
1111
end
1212

13-
def execute(result)
13+
def execute(result) # rubocop:disable Metrics/PredicateMethod
1414
if result.ping?
1515
coordinator.ping_response(id: result.id)
1616
true
@@ -21,9 +21,9 @@ def execute(result)
2121
handle_sampling_response(result)
2222
true
2323
else
24-
# Handle server-initiated requests
25-
# Currently, we do not support any client operations but will
26-
raise RubyLLM::MCP::Errors::UnknownRequest.new(message: "Unknown request type: #{result.inspect}")
24+
handle_unknown_request(result)
25+
RubyLLM::MCP.logger.error("MCP client was sent unknown method type and could not respond: #{result.inspect}")
26+
false
2727
end
2828
end
2929

@@ -47,6 +47,12 @@ def handle_sampling_response(result)
4747
RubyLLM::MCP.logger.info("Sampling response: #{result.inspect}")
4848
Sample.new(result, coordinator).execute
4949
end
50+
51+
def handle_unknown_request(result)
52+
coordinator.error_response(id: result.id,
53+
message: "Unknown method and could not respond: #{result.method}",
54+
code: -32_000)
55+
end
5056
end
5157
end
5258
end

lib/ruby_llm/mcp/result.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def initialize(response)
1212
end
1313

1414
class Result
15-
attr_reader :result, :error, :params, :id, :response, :session_id
15+
attr_reader :result, :error, :params, :id, :response, :session_id, :method
1616

1717
REQUEST_METHODS = {
1818
ping: "ping",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# frozen_string_literal: true
2+
3+
class FakeLogger
4+
def error(message)
5+
@error_message = message
6+
end
7+
8+
attr_reader :error_message
9+
end
10+
11+
RSpec.describe RubyLLM::MCP::NotificationHandler do
12+
let(:coordinator) { instance_double(RubyLLM::MCP::Coordinator) }
13+
let(:notification_handler) { RubyLLM::MCP::NotificationHandler.new(coordinator) }
14+
15+
before do
16+
allow(coordinator).to receive(:client)
17+
end
18+
19+
after do
20+
MCPTestConfiguration.reset_config!
21+
end
22+
23+
it "calling cancelled at the moment will do nothing" do
24+
notification = RubyLLM::MCP::Notification.new(
25+
{ "type" => "notifications/cancelled" }
26+
)
27+
28+
expect { notification_handler.execute(notification) }.not_to raise_error
29+
end
30+
31+
it "calling an unknown notification will log an error and do nothing else" do
32+
logger = FakeLogger.new
33+
RubyLLM::MCP.configure do |config|
34+
config.logger = logger
35+
end
36+
37+
notification = RubyLLM::MCP::Notification.new(
38+
{ "method" => "notifications/unknown", "params" => {} }
39+
)
40+
41+
notification_handler.execute(notification)
42+
expect(logger.error_message).to eq("Unknown notification type: notifications/unknown params: {}")
43+
end
44+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RubyLLM::MCP::ResponseHandler do
4+
let(:coordinator) { instance_double(RubyLLM::MCP::Coordinator) }
5+
let(:request_handler) { RubyLLM::MCP::ResponseHandler.new(coordinator) }
6+
7+
before do
8+
allow(coordinator).to receive(:client)
9+
allow(coordinator).to receive(:error_response).and_return(true)
10+
end
11+
12+
it "response with an error code if the request is unknown" do
13+
result = RubyLLM::MCP::Result.new(
14+
{ "id" => "123", "method" => "unknown/request", "params" => {} }
15+
)
16+
17+
request_handler.execute(result)
18+
error_message = "Unknown method and could not respond: #{result.method}"
19+
expect(coordinator).to have_received(:error_response).with(id: "123", message: error_message, code: -32_000)
20+
end
21+
end

spec/ruby_llm/mcp/sample_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@
178178
expect(model_preferences).to be_a(RubyLLM::MCP::Sample::Hint)
179179
end
180180

181-
it "client calls a block to determine the preferred model, and raises an error it will send an error message back" do
181+
it "client calls a block to determine the preferred model, and raises an error it will send an error back" do
182182
RubyLLM::MCP.configure do |config|
183183
config.sampling.enabled = true
184184

0 commit comments

Comments
 (0)