Allow params field to be omitted in requests for methods that don't require them (#17)

* Allow `params` field to be omitted in requests for methods with empty parameters

* Add ping request to roundtrip test

* Generalize decoding of non-required parameters
This commit is contained in:
Mattt 2025-03-17 05:26:47 -07:00 • committed by GitHub
commit 6f1f65a392
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 206 additions and 25 deletions

View file

@ -142,6 +142,36 @@ struct PromptTests {
let emptyParams = ListPrompts.Parameters()
#expect(emptyParams.cursor == nil)
}
@Test("ListPrompts request decoding with omitted params")
func testListPromptsRequestDecodingWithOmittedParams() throws {
// Test decoding when params field is omitted
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"prompts/list"}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<ListPrompts>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == ListPrompts.name)
}
@Test("ListPrompts request decoding with null params")
func testListPromptsRequestDecodingWithNullParams() throws {
// Test decoding when params field is null
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"prompts/list","params":null}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<ListPrompts>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == ListPrompts.name)
}
@Test("ListPrompts result validation")
func testListPromptsResult() throws {

View file

@ -76,4 +76,49 @@ struct RequestTests {
#expect(decoded.id == "test-id")
#expect(decoded.method == EmptyMethod.name)
}
@Test("NotRequired parameters request decoding - with params")
func testNotRequiredParametersRequestDecodingWithParams() throws {
// Test decoding when params field is present
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"ping","params":{}}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<Ping>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == Ping.name)
}
@Test("NotRequired parameters request decoding - without params")
func testNotRequiredParametersRequestDecodingWithoutParams() throws {
// Test decoding when params field is missing
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"ping"}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<Ping>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == Ping.name)
}
@Test("NotRequired parameters request decoding - with null params")
func testNotRequiredParametersRequestDecodingWithNullParams() throws {
// Test decoding when params field is null
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"ping","params":null}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<Ping>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == Ping.name)
}
}

View file

@ -86,6 +86,36 @@ struct ResourceTests {
let emptyParams = ListResources.Parameters()
#expect(emptyParams.cursor == nil)
}
@Test("ListResources request decoding with omitted params")
func testListResourcesRequestDecodingWithOmittedParams() throws {
// Test decoding when params field is omitted
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"resources/list"}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<ListResources>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == ListResources.name)
}
@Test("ListResources request decoding with null params")
func testListResourcesRequestDecodingWithNullParams() throws {
// Test decoding when params field is null
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"resources/list","params":null}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<ListResources>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == ListResources.name)
}
@Test("ListResources result validation")
func testListResourcesResult() throws {

View file

@ -118,6 +118,26 @@ struct RoundtripTests {
group.cancelAll()
}
// Test ping
let pingTask = Task {
try await client.ping()
// Ping doesn't return anything, so just getting here without throwing is success
#expect(true) // Test passed if we reach this point
}
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
try await Task.sleep(for: .seconds(1))
pingTask.cancel()
throw CancellationError()
}
group.addTask {
try await pingTask.value
}
try await group.next()
group.cancelAll()
}
let listToolsTask = Task {
let result = try await client.listTools()
#expect(result.count == 1)

View file

@ -111,6 +111,36 @@ struct ToolTests {
let emptyParams = ListTools.Parameters()
#expect(emptyParams.cursor == nil)
}
@Test("ListTools request decoding with omitted params")
func testListToolsRequestDecodingWithOmittedParams() throws {
// Test decoding when params field is omitted
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"tools/list"}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<ListTools>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == ListTools.name)
}
@Test("ListTools request decoding with null params")
func testListToolsRequestDecodingWithNullParams() throws {
// Test decoding when params field is null
let jsonString = """
{"jsonrpc":"2.0","id":"test-id","method":"tools/list","params":null}
"""
let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decoded = try decoder.decode(Request<ListTools>.self, from: data)
#expect(decoded.id == "test-id")
#expect(decoded.method == ListTools.name)
}
@Test("ListTools result validation")
func testListToolsResult() throws {