Validate Required Params for initialize and resources/subscribe

## Motivation and Context

Fixes https://github.com/modelcontextprotocol/ruby-sdk/issues/450

The MCP schema requires `protocolVersion`, `capabilities`, and `clientInfo` on `initialize` params,
and `uri` on `resources/subscribe` and `resources/unsubscribe` params. The server accepted requests with
these fields absent: `initialize` silently negotiated the server's default protocol version,
and `resources/subscribe` returned an empty success result. Both now return `-32602` Invalid params,
matching the Python SDK, which rejects such requests during pydantic validation.
The `uri` check runs before dispatch so it also covers handlers registered via `resources_subscribe_handler`
and `resources_unsubscribe_handler`.

A `protocolVersion` value that is present but unsupported still negotiates the server's fallback version,
as the spec's lifecycle section requires.

## How Has This Been Tested?

Reproduced the issue's stdio repro steps: both malformed requests now return `-32602` instead of succeeding.

## Breaking Changes

Clients that send `initialize` without `protocolVersion`, `capabilities`, or `clientInfo`,
or `resources/subscribe` / `resources/unsubscribe` without `uri`, now receive `-32602` Invalid params
instead of a success response. Such requests violate the MCP schema, but any code relying on
the previous lenient behavior (for example, test harnesses sending bare `initialize` requests)
must now include the required fields.
This commit is contained in:
Koichi ITO 2026-07-11 22:30:25 +09:00
commit abd1070cfa
8 changed files with 369 additions and 219 deletions

View file

@ -531,6 +531,7 @@ module MCP
when Methods::RESOURCES_READ
build_read_resource_result(read_resource_contents(params, session: session, related_request_id: related_request_id, cancellation: cancellation))
when Methods::RESOURCES_SUBSCRIBE, Methods::RESOURCES_UNSUBSCRIBE
validate_resource_subscription_params!(params)
dispatch_optional_context_handler(@handlers[method], params, session: session, related_request_id: related_request_id, cancellation: cancellation)
{}
when Methods::TOOLS_CALL
@ -605,6 +606,8 @@ module MCP
end
def init(params, session: nil)
validate_initialize_params!(params)
# MCP spec: the initialization phase MUST be the first interaction between client and server.
# Reject duplicate `initialize` on an already-initialized session so the negotiated
# client identity and capabilities cannot be silently overwritten.
@ -612,15 +615,13 @@ module MCP
raise RequestHandlerError.new("Invalid Request: Server already initialized", params, error_type: :invalid_request)
end
if params
if session
session.store_client_info(client: params[:clientInfo], capabilities: params[:capabilities])
else
@client = params[:clientInfo]
@client_capabilities = params[:capabilities]
end
protocol_version = params[:protocolVersion]
if session
session.store_client_info(client: params[:clientInfo], capabilities: params[:capabilities])
else
@client = params[:clientInfo]
@client_capabilities = params[:capabilities]
end
protocol_version = params[:protocolVersion]
negotiated_version = if Configuration::SUPPORTED_STABLE_PROTOCOL_VERSIONS.include?(protocol_version)
protocol_version
@ -649,6 +650,31 @@ module MCP
}.compact
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)
end
end
def validate_initialize_params!(params)
unless params.is_a?(Hash)
raise RequestHandlerError.new("Invalid params", params, error_type: :invalid_params)
end
unless params[:protocolVersion].is_a?(String)
raise RequestHandlerError.new("Missing or invalid protocolVersion", params, error_type: :invalid_params)
end
unless params[:capabilities].is_a?(Hash)
raise RequestHandlerError.new("Missing or invalid capabilities", params, error_type: :invalid_params)
end
client_info = params[:clientInfo]
if !client_info.is_a?(Hash) || client_info[:name].nil? || client_info[:version].nil?
raise RequestHandlerError.new("Missing or invalid clientInfo", params, error_type: :invalid_params)
end
end
# Handles `server/discover` (MCP 2026-07-28 draft, SEP-2575): sessionless capability discovery.
# Unlike `init`, this is state-free and idempotent: it stores no client info, does not mark
# the session initialized, and responds regardless of capability declarations or initialization state,

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
module InitializeParamsTestHelper
# `initialize` params satisfying the fields required by the MCP schema
# (protocolVersion, capabilities, and clientInfo).
def initialize_params(**overrides)
{
protocolVersion: MCP::Configuration::LATEST_STABLE_PROTOCOL_VERSION,
capabilities: {},
clientInfo: { name: "test-client", version: "1.0.0" },
}.merge(overrides)
end
end

View file

@ -108,6 +108,7 @@ module MCP
id: "1",
params: {
protocolVersion: "2025-11-25",
capabilities: {},
clientInfo: { name: "stdio-client", version: "1.0" },
},
}
@ -140,6 +141,7 @@ module MCP
id: "first",
params: {
protocolVersion: "2025-11-25",
capabilities: {},
clientInfo: { name: "original", version: "1.0" },
},
}
@ -149,6 +151,7 @@ module MCP
id: "second",
params: {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "intruder", version: "9.9" },
},
}

View file

@ -7,6 +7,8 @@ module MCP
class Server
module Transports
class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
include InitializeParamsTestHelper
setup do
@server = Server.new(
name: "test_server",
@ -23,7 +25,7 @@ module MCP
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "initialize", id: "init" }.to_json,
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
)
init_response = @transport.handle_request(init_request)
session_id = init_response[1]["Mcp-Session-Id"]
@ -72,7 +74,7 @@ module MCP
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "initialize", id: "123" }.to_json,
{ jsonrpc: "2.0", method: "initialize", id: "123", params: initialize_params }.to_json,
)
init_response1 = @transport.handle_request(init_request1)
session_id1 = init_response1[1]["Mcp-Session-Id"]
@ -81,7 +83,7 @@ module MCP
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "initialize", id: "456" }.to_json,
{ jsonrpc: "2.0", method: "initialize", id: "456", params: initialize_params }.to_json,
)
init_response2 = @transport.handle_request(init_request2)
session_id2 = init_response2[1]["Mcp-Session-Id"]
@ -127,7 +129,7 @@ module MCP
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "initialize", id: "init" }.to_json,
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
)
init_response = @transport.handle_request(init_request)
session_id = init_response[1]["Mcp-Session-Id"]
@ -163,7 +165,7 @@ module MCP
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "initialize", id: "init" }.to_json,
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
)
init_response = @transport.handle_request(init_request)
session_id = init_response[1]["Mcp-Session-Id"]
@ -207,7 +209,7 @@ module MCP
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "initialize", id: "init" }.to_json,
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
)
init_response = @transport.handle_request(init_request)
session_id = init_response[1]["Mcp-Session-Id"]

File diff suppressed because it is too large Load diff

View file

@ -87,9 +87,9 @@ module MCP
assert_equal({ roots: { listChanged: true } }, server.client_capabilities)
end
test "init stores nil when client sends no capabilities" do
test "init rejects params without capabilities and stores nothing" do
server = Server.new(name: "test", version: "1.0")
server.handle({
response = server.handle({
jsonrpc: "2.0",
method: "initialize",
id: 1,
@ -99,6 +99,7 @@ module MCP
},
})
assert_equal(-32602, response[:error][:code])
assert_nil server.client_capabilities
end

View file

@ -5,6 +5,7 @@ require "test_helper"
module MCP
class ServerTest < ActiveSupport::TestCase
include InstrumentationTestHelper
include InitializeParamsTestHelper
setup do
@tool = Tool.define(
name: "test_tool",
@ -151,7 +152,7 @@ module MCP
refute_predicate session, :initialized?
# `initialize` must still succeed afterwards.
response = @server.handle(
{ jsonrpc: "2.0", method: "initialize", id: 2, params: { clientInfo: { name: "c", version: "1" } } },
{ jsonrpc: "2.0", method: "initialize", id: 2, params: initialize_params },
session: session,
)
refute_nil response[:result]
@ -176,6 +177,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = @server.handle(request)
@ -204,7 +206,7 @@ module MCP
}
assert_equal expected_result, response
assert_instrumentation_data({ method: "initialize" })
assert_instrumentation_data({ method: "initialize", client: { name: "test-client", version: "1.0.0" } })
end
test "#handle initialize result carries declared capability extensions" do
@ -216,7 +218,7 @@ module MCP
},
)
response = server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
response = server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
assert_equal(
{ "com.example/feature" => { enabled: true } },
@ -230,7 +232,7 @@ module MCP
capabilities.support_extensions("io.modelcontextprotocol/tasks" => {})
server = Server.new(name: "extensions_test", capabilities: capabilities)
response = server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
response = server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
assert_equal(
{
@ -247,10 +249,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
clientInfo: { name: "test_client", version: "1.0.0" },
capabilities: { extensions: extensions },
},
params: initialize_params(capabilities: { extensions: extensions }),
}
@server.handle(request)
@ -265,10 +264,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
clientInfo: { name: "test_client", version: "1.0.0" },
capabilities: { extensions: extensions },
},
params: initialize_params(capabilities: { extensions: extensions }),
}
@server.handle(request, session: session)
@ -282,9 +278,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
clientInfo: client_info,
},
params: initialize_params(clientInfo: client_info),
}
@server.handle(request)
@ -297,9 +291,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
clientInfo: client_info,
},
params: initialize_params(clientInfo: client_info),
}
@server.handle(initialize_request)
@ -319,7 +311,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: { clientInfo: { name: "original", version: "1.0" } },
params: initialize_params(clientInfo: { name: "original", version: "1.0" }),
}
first_response = @server.handle(first_request, session: session)
refute_nil first_response[:result]
@ -328,7 +320,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 2,
params: { clientInfo: { name: "intruder", version: "9.9" }, protocolVersion: "2024-11-05" },
params: initialize_params(clientInfo: { name: "intruder", version: "9.9" }, protocolVersion: "2024-11-05"),
}
second_response = @server.handle(second_request, session: session)
@ -337,6 +329,59 @@ module MCP
assert_equal({ name: "original", version: "1.0" }, session.client)
end
test "#handle initialize with empty params returns -32602 and does not initialize the session" do
session = ServerSession.new(server: @server, transport: mock)
response = @server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: {} }, session: session)
assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
refute_predicate session, :initialized?
end
test "#handle initialize without params returns -32602" do
response = @server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
end
test "#handle initialize with a missing required field returns -32602" do
[:protocolVersion, :capabilities, :clientInfo].each do |field|
params = initialize_params.tap { |hash| hash.delete(field) }
response = @server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: params })
assert_equal(-32602, response[:error][:code], "expected -32602 when #{field} is missing")
assert_includes response[:error][:data], field.to_s
end
end
test "#handle initialize with clientInfo missing name or version returns -32602" do
[{ name: "client-without-version" }, { version: "1.0.0" }].each do |client_info|
response = @server.handle({
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params(clientInfo: client_info),
})
assert_equal(-32602, response[:error][:code])
assert_includes response[:error][:data], "clientInfo"
end
end
test "#handle initialize with an unsupported protocolVersion still negotiates the fallback version" do
response = @server.handle({
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params(protocolVersion: "1999-01-01"),
})
assert_equal Configuration::LATEST_STABLE_PROTOCOL_VERSION, response[:result][:protocolVersion]
end
test "instrumentation data does not include client key when no clientInfo provided" do
request = {
jsonrpc: "2.0",
@ -1540,6 +1585,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = @server.handle(request)
@ -1552,6 +1598,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
@ -1567,6 +1614,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
@ -1580,6 +1628,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
@ -1592,6 +1641,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
@ -1607,6 +1657,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
expected_icons = [{ mimeType: "image/png", sizes: ["48x48"], src: "https://example.com", theme: "light" }]
@ -1620,6 +1671,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
@ -1632,6 +1684,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params,
}
response = server.handle(request)
@ -1905,6 +1958,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: initialize_params(protocolVersion: "1999-01-01"),
}
response = server.handle(request)
@ -1918,9 +1972,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
protocolVersion: "2025-06-18",
},
params: initialize_params(protocolVersion: "2025-06-18"),
}
response = server.handle(request)
@ -1934,9 +1986,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
protocolVersion: "1999-01-01",
},
params: initialize_params(protocolVersion: "1999-01-01"),
}
response = server.handle(request)
@ -1954,9 +2004,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
protocolVersion: "2025-06-18",
},
params: initialize_params(protocolVersion: "2025-06-18"),
}
response = server.handle(request)
@ -1972,9 +2020,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
protocolVersion: "2025-03-26",
},
params: initialize_params(protocolVersion: "2025-03-26"),
}
response = server.handle(request)
@ -1990,9 +2036,7 @@ module MCP
jsonrpc: "2.0",
method: "initialize",
id: 1,
params: {
protocolVersion: "2024-11-05",
},
params: initialize_params(protocolVersion: "2024-11-05"),
}
response = server.handle(request)
@ -2381,7 +2425,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2419,7 +2463,7 @@ module MCP
{ completion: { values: ["python", "pytorch", "pyside"], total: 10, hasMore: true } }
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2457,7 +2501,7 @@ module MCP
{ completion: { values: ["file:///src", "file:///spec"], hasMore: false } }
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2500,7 +2544,7 @@ module MCP
{ completion: { values: ["flask"], hasMore: false } }
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
server.handle({
@ -2529,7 +2573,7 @@ module MCP
{ completion: { values: (1..150).map(&:to_s), hasMore: false } }
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2556,7 +2600,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2578,7 +2622,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2600,7 +2644,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2624,7 +2668,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2646,7 +2690,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2677,7 +2721,7 @@ module MCP
{ completion: { values: ["file:///README.md"], hasMore: false } }
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2708,7 +2752,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2731,7 +2775,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2759,7 +2803,7 @@ module MCP
nil
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2794,7 +2838,7 @@ module MCP
{ "completion" => { "values" => ["alpha", "beta"], "hasMore" => true } }
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2818,7 +2862,7 @@ module MCP
capabilities: { completions: {} },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2837,7 +2881,7 @@ module MCP
prompts: [],
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2860,7 +2904,7 @@ module MCP
capabilities: { resources: { subscribe: true } },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2886,7 +2930,7 @@ module MCP
capabilities: { resources: { subscribe: true } },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2918,7 +2962,7 @@ module MCP
{}
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2951,7 +2995,7 @@ module MCP
{}
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1 })
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
@ -2972,6 +3016,73 @@ module MCP
assert_equal "https://example.com/resource", received_params[:uri]
end
test "#handle resources/subscribe without uri returns -32602" do
server = Server.new(
name: "test_server",
capabilities: { resources: { subscribe: true } },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
jsonrpc: "2.0",
id: 2,
method: "resources/subscribe",
params: {},
})
assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
assert_includes response[:error][:data], "uri"
end
test "#handle resources/unsubscribe without uri returns -32602" do
server = Server.new(
name: "test_server",
capabilities: { resources: { subscribe: true } },
)
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
jsonrpc: "2.0",
id: 2,
method: "resources/unsubscribe",
params: {},
})
assert_equal(-32602, response[:error][:code])
assert_equal "Invalid params", response[:error][:message]
end
test "#handle resources/subscribe without uri does not invoke a custom handler" do
server = Server.new(
name: "test_server",
capabilities: { resources: { subscribe: true } },
)
handler_called = false
server.resources_subscribe_handler do |_params|
handler_called = true
{}
end
server.handle({ jsonrpc: "2.0", method: "initialize", id: 1, params: initialize_params })
server.handle({ jsonrpc: "2.0", method: "notifications/initialized" })
response = server.handle({
jsonrpc: "2.0",
id: 2,
method: "resources/subscribe",
params: {},
})
assert_equal(-32602, response[:error][:code])
refute handler_called
end
test "tools/call with no args" do
server = Server.new(tools: [@tool_with_no_args])

View file

@ -22,3 +22,4 @@ require "active_support/test_case"
require "sorbet-runtime" if RUBY_VERSION >= "3.0"
require_relative "instrumentation_test_helper"
require_relative "initialize_params_test_helper"