Skip to content
Draft
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
7 changes: 7 additions & 0 deletions lib/mcp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@ def handle_request(request, method, session: nil, related_request_id: nil)
when Methods::INITIALIZE
init(params, session: session)
when Methods::RESOURCES_READ
validate_resource_read_params!(params)
contents = read_resource_contents(params, session: session, related_request_id: related_request_id, cancellation: cancellation, envelope: envelope)

# An SEP-2322 `input_required` result must not be wrapped as `contents` or stamped with SEP-2549 cache hints.
Expand Down Expand Up @@ -1117,6 +1118,12 @@ def init(params, session: nil)
}.compact
end

def validate_resource_read_params!(params)
unless params.is_a?(Hash) && params[:uri].is_a?(String)
raise RequestHandlerError.new("Missing or invalid uri", params, error_type: :invalid_params)
end
end

def validate_resource_subscription_params!(params)
unless params.is_a?(Hash) && params[:uri].is_a?(String)
raise RequestHandlerError.new("Missing or invalid uri", params, error_type: :invalid_params)
Expand Down
55 changes: 55 additions & 0 deletions test/mcp/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,61 @@ def read_resource_request(uri)
assert_equal "handler wins", response[:result][:contents].first[:text]
end

test "#handle resources/read without uri returns -32602" do
response = @server.handle({ jsonrpc: "2.0", method: "resources/read", id: 1, params: {} })

assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
assert_includes response[:error][:data], "uri"
end

test "#handle resources/read without params returns -32602" do
response = @server.handle({ jsonrpc: "2.0", method: "resources/read", id: 1 })

assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
end

test "#handle resources/read with a non-string uri returns -32602 when class-based resources are registered" do
server = Server.new(name: "test_server", resources: [GreetingResource])

response = server.handle(read_resource_request(123))

assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
assert_includes response[:error][:data], "uri"
end

test "#handle resources/read without uri does not invoke a custom handler" do
handler_called = false
@server.resources_read_handler do |_request|
handler_called = true
[]
end

response = @server.handle({ jsonrpc: "2.0", method: "resources/read", id: 1, params: {} })

assert_equal(-32602, response[:error][:code])
refute handler_called
end

test "#handle resources/read with a non-string uri returns -32602 when class-based templates are registered" do
server = Server.new(name: "test_server", resource_templates: [UserProfileTemplate])

response = server.handle(read_resource_request(123))

assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
assert_includes response[:error][:data], "uri"
end

test "#handle resources/read with params that are not an object returns -32602" do
response = @server.handle({ jsonrpc: "2.0", method: "resources/read", id: 1, params: [] })

assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
end

test "#handle resources/list and resources/templates/list render class-based and instance-based entries together" do
server = Server.new(
name: "test_server",
Expand Down