Skip to content
Merged
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: 3 additions & 4 deletions Sources/MCP/Base/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,11 @@ final class TypedRequestHandler<M: Method>: RequestHandlerBox, @unchecked Sendab
let decoder = JSONDecoder()

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

// Handle with concrete type
let response = try await _handle(typedRequest)
let response = try await _handle(request)

// Convert result to AnyMethod response
switch response.result {
Expand Down
37 changes: 35 additions & 2 deletions Tests/MCPTests/ToolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ 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
Expand All @@ -126,7 +126,7 @@ struct ToolTests {
#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
Expand Down Expand Up @@ -205,4 +205,37 @@ struct ToolTests {
func testToolListChangedNotification() throws {
#expect(ToolListChangedNotification.name == "notifications/tools/list_changed")
}

@Test("ListTools handler invocation without params")
func testListToolsHandlerWithoutParams() async throws {
let jsonString = """
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
"""
let jsonData = jsonString.data(using: .utf8)!

let anyRequest = try JSONDecoder().decode(AnyRequest.self, from: jsonData)

let handler = TypedRequestHandler<ListTools> { request in
#expect(request.method == ListTools.name)
#expect(request.id == 1)
#expect(request.params.cursor == nil)

let testTool = Tool(name: "test_tool", description: "Test tool for verification")
return ListTools.response(id: request.id, result: ListTools.Result(tools: [testTool]))
}

let response = try await handler(anyRequest)

if case .success(let value) = response.result {
let encoder = JSONEncoder()
let decoder = JSONDecoder()
let data = try encoder.encode(value)
let result = try decoder.decode(ListTools.Result.self, from: data)

#expect(result.tools.count == 1)
#expect(result.tools[0].name == "test_tool")
} else {
#expect(Bool(false), "Expected success result")
}
}
}