Skip to content

Commit 19857af

Browse files
authored
Fix/decode tool call (#36)
* Adding unit test * Decode entire request to strongly typed Method call. This allows NotRequired parameters to properly decode from null, which wouldn't be possible when decoding from the 'null' JSON encoding directly.
1 parent d55140b commit 19857af

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

Sources/MCP/Base/Messages.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,11 @@ final class TypedRequestHandler<M: Method>: RequestHandlerBox, @unchecked Sendab
161161
let decoder = JSONDecoder()
162162

163163
// Create a concrete request from the type-erased one
164-
let data = try encoder.encode(request.params)
165-
let params = try decoder.decode(M.Parameters.self, from: data)
166-
let typedRequest = Request<M>(id: request.id, method: M.name, params: params)
164+
let data = try encoder.encode(request)
165+
let request = try decoder.decode(Request<M>.self, from: data)
167166

168167
// Handle with concrete type
169-
let response = try await _handle(typedRequest)
168+
let response = try await _handle(request)
170169

171170
// Convert result to AnyMethod response
172171
switch response.result {

Tests/MCPTests/ToolTests.swift

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ struct ToolTests {
111111
let emptyParams = ListTools.Parameters()
112112
#expect(emptyParams.cursor == nil)
113113
}
114-
114+
115115
@Test("ListTools request decoding with omitted params")
116116
func testListToolsRequestDecodingWithOmittedParams() throws {
117117
// Test decoding when params field is omitted
@@ -126,7 +126,7 @@ struct ToolTests {
126126
#expect(decoded.id == "test-id")
127127
#expect(decoded.method == ListTools.name)
128128
}
129-
129+
130130
@Test("ListTools request decoding with null params")
131131
func testListToolsRequestDecodingWithNullParams() throws {
132132
// Test decoding when params field is null
@@ -205,4 +205,37 @@ struct ToolTests {
205205
func testToolListChangedNotification() throws {
206206
#expect(ToolListChangedNotification.name == "notifications/tools/list_changed")
207207
}
208+
209+
@Test("ListTools handler invocation without params")
210+
func testListToolsHandlerWithoutParams() async throws {
211+
let jsonString = """
212+
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
213+
"""
214+
let jsonData = jsonString.data(using: .utf8)!
215+
216+
let anyRequest = try JSONDecoder().decode(AnyRequest.self, from: jsonData)
217+
218+
let handler = TypedRequestHandler<ListTools> { request in
219+
#expect(request.method == ListTools.name)
220+
#expect(request.id == 1)
221+
#expect(request.params.cursor == nil)
222+
223+
let testTool = Tool(name: "test_tool", description: "Test tool for verification")
224+
return ListTools.response(id: request.id, result: ListTools.Result(tools: [testTool]))
225+
}
226+
227+
let response = try await handler(anyRequest)
228+
229+
if case .success(let value) = response.result {
230+
let encoder = JSONEncoder()
231+
let decoder = JSONDecoder()
232+
let data = try encoder.encode(value)
233+
let result = try decoder.decode(ListTools.Result.self, from: data)
234+
235+
#expect(result.tools.count == 1)
236+
#expect(result.tools[0].name == "test_tool")
237+
} else {
238+
#expect(Bool(false), "Expected success result")
239+
}
240+
}
208241
}

0 commit comments

Comments
 (0)