From 2c9690f9785663333f6205fcef8d551a5e4c49d5 Mon Sep 17 00:00:00 2001 From: Mattt Zmuda Date: Thu, 29 May 2025 06:22:47 -0700 Subject: [PATCH] Make Tool.inputSchema nonoptional --- Sources/MCP/Server/Tools.swift | 10 ++++------ Tests/MCPTests/ToolTests.swift | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Sources/MCP/Server/Tools.swift b/Sources/MCP/Server/Tools.swift index 9c08b72..edfe112 100644 --- a/Sources/MCP/Server/Tools.swift +++ b/Sources/MCP/Server/Tools.swift @@ -14,7 +14,7 @@ public struct Tool: Hashable, Codable, Sendable { /// The tool description public let description: String /// The tool input schema - public let inputSchema: Value? + public let inputSchema: Value /// Annotations that provide display-facing and operational information for a Tool. /// @@ -86,7 +86,7 @@ public struct Tool: Hashable, Codable, Sendable { public init( name: String, description: String, - inputSchema: Value? = nil, + inputSchema: Value, annotations: Annotations = nil ) { self.name = name @@ -183,7 +183,7 @@ public struct Tool: Hashable, Codable, Sendable { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) description = try container.decode(String.self, forKey: .description) - inputSchema = try container.decodeIfPresent(Value.self, forKey: .inputSchema) + inputSchema = try container.decode(Value.self, forKey: .inputSchema) annotations = try container.decodeIfPresent(Tool.Annotations.self, forKey: .annotations) ?? .init() } @@ -192,9 +192,7 @@ public struct Tool: Hashable, Codable, Sendable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(description, forKey: .description) - if let schema = inputSchema { - try container.encode(schema, forKey: .inputSchema) - } + try container.encode(inputSchema, forKey: .inputSchema) if !annotations.isEmpty { try container.encode(annotations, forKey: .annotations) } diff --git a/Tests/MCPTests/ToolTests.swift b/Tests/MCPTests/ToolTests.swift index 8486d9e..76be721 100644 --- a/Tests/MCPTests/ToolTests.swift +++ b/Tests/MCPTests/ToolTests.swift @@ -126,28 +126,29 @@ struct ToolTests { func testToolWithEmptyAnnotations() throws { var tool = Tool( name: "test_tool", - description: "Test tool description" + description: "Test tool description", + inputSchema: [:] ) do { #expect(tool.annotations.isEmpty) - + let encoder = JSONEncoder() let data = try encoder.encode(tool) - + // Verify that empty annotations are not included in the JSON let jsonString = String(data: data, encoding: .utf8)! #expect(!jsonString.contains("\"annotations\"")) } - + do { tool.annotations.title = "Test" #expect(!tool.annotations.isEmpty) - + let encoder = JSONEncoder() let data = try encoder.encode(tool) - + // Verify that empty annotations are not included in the JSON let jsonString = String(data: data, encoding: .utf8)! #expect(jsonString.contains("\"annotations\"")) @@ -159,7 +160,7 @@ struct ToolTests { let tool = Tool( name: "test_tool", description: "Test tool description", - inputSchema: nil, + inputSchema: [:], annotations: nil ) @@ -318,8 +319,8 @@ struct ToolTests { @Test("ListTools result validation") func testListToolsResult() throws { let tools = [ - Tool(name: "tool1", description: "First tool", inputSchema: nil), - Tool(name: "tool2", description: "Second tool", inputSchema: nil), + Tool(name: "tool1", description: "First tool", inputSchema: [:]), + Tool(name: "tool2", description: "Second tool", inputSchema: [:]), ] let result = ListTools.Result(tools: tools, nextCursor: "next_page") @@ -393,7 +394,11 @@ struct ToolTests { #expect(request.id == 1) #expect(request.params.cursor == nil) - let testTool = Tool(name: "test_tool", description: "Test tool for verification") + let testTool = Tool( + name: "test_tool", + description: "Test tool for verification", + inputSchema: [:] + ) return ListTools.response(id: request.id, result: ListTools.Result(tools: [testTool])) }