diff --git a/Sources/MCP/Server/Prompts.swift b/Sources/MCP/Server/Prompts.swift index 2ebd3ae0..24e5a007 100644 --- a/Sources/MCP/Server/Prompts.swift +++ b/Sources/MCP/Server/Prompts.swift @@ -64,6 +64,8 @@ public struct Prompt: Hashable, Codable, Sendable { case text(text: String) /// Image content case image(data: String, mimeType: String) + /// Audio content + case audio(data: String, mimeType: String) /// Embedded resource content case resource(uri: String, mimeType: String, text: String?, blob: String?) @@ -82,6 +84,10 @@ public struct Prompt: Hashable, Codable, Sendable { try container.encode("image", forKey: .type) try container.encode(data, forKey: .data) try container.encode(mimeType, forKey: .mimeType) + case .audio(let data, let mimeType): + try container.encode("audio", forKey: .type) + try container.encode(data, forKey: .data) + try container.encode(mimeType, forKey: .mimeType) case .resource(let uri, let mimeType, let text, let blob): try container.encode("resource", forKey: .type) try container.encode(uri, forKey: .uri) @@ -103,6 +109,10 @@ public struct Prompt: Hashable, Codable, Sendable { let data = try container.decode(String.self, forKey: .data) let mimeType = try container.decode(String.self, forKey: .mimeType) self = .image(data: data, mimeType: mimeType) + case "audio": + let data = try container.decode(String.self, forKey: .data) + let mimeType = try container.decode(String.self, forKey: .mimeType) + self = .audio(data: data, mimeType: mimeType) case "resource": let uri = try container.decode(String.self, forKey: .uri) let mimeType = try container.decode(String.self, forKey: .mimeType) @@ -155,7 +165,7 @@ public enum ListPrompts: Method { public struct Parameters: NotRequired, Hashable, Codable, Sendable { public let cursor: String? - + public init() { self.cursor = nil } diff --git a/Sources/MCP/Server/Tools.swift b/Sources/MCP/Server/Tools.swift index 74755574..58981a47 100644 --- a/Sources/MCP/Server/Tools.swift +++ b/Sources/MCP/Server/Tools.swift @@ -28,6 +28,8 @@ public struct Tool: Hashable, Codable, Sendable { case text(String) /// Image content case image(data: String, mimeType: String, metadata: [String: String]?) + /// Audio content + case audio(data: String, mimeType: String) /// Embedded resource content case resource(uri: String, mimeType: String, text: String?) @@ -36,6 +38,7 @@ public struct Tool: Hashable, Codable, Sendable { case text case image case resource + case audio case uri case mimeType case data @@ -56,6 +59,10 @@ public struct Tool: Hashable, Codable, Sendable { let metadata = try container.decodeIfPresent( [String: String].self, forKey: .metadata) self = .image(data: data, mimeType: mimeType, metadata: metadata) + case "audio": + let data = try container.decode(String.self, forKey: .data) + let mimeType = try container.decode(String.self, forKey: .mimeType) + self = .audio(data: data, mimeType: mimeType) case "resource": let uri = try container.decode(String.self, forKey: .uri) let mimeType = try container.decode(String.self, forKey: .mimeType) @@ -79,6 +86,10 @@ public struct Tool: Hashable, Codable, Sendable { try container.encode(data, forKey: .data) try container.encode(mimeType, forKey: .mimeType) try container.encodeIfPresent(metadata, forKey: .metadata) + case .audio(let data, let mimeType): + try container.encode("audio", forKey: .type) + try container.encode(data, forKey: .data) + try container.encode(mimeType, forKey: .mimeType) case .resource(let uri, let mimeType, let text): try container.encode("resource", forKey: .type) try container.encode(uri, forKey: .uri) @@ -120,7 +131,7 @@ public enum ListTools: Method { public struct Parameters: NotRequired, Hashable, Codable, Sendable { public let cursor: String? - + public init() { self.cursor = nil } diff --git a/Tests/MCPTests/PromptTests.swift b/Tests/MCPTests/PromptTests.swift index 083d4636..d0d7f112 100644 --- a/Tests/MCPTests/PromptTests.swift +++ b/Tests/MCPTests/PromptTests.swift @@ -63,6 +63,18 @@ struct PromptTests { #expect(Bool(false), "Expected text content") } + // Test audio content + let audioContent = Prompt.Message.Content.audio( + data: "base64audiodata", mimeType: "audio/wav") + let audioData = try encoder.encode(audioContent) + let decodedAudio = try decoder.decode(Prompt.Message.Content.self, from: audioData) + if case .audio(let data, let mimeType) = decodedAudio { + #expect(data == "base64audiodata") + #expect(mimeType == "audio/wav") + } else { + #expect(Bool(false), "Expected audio content") + } + // Test image content let imageContent = Prompt.Message.Content.image(data: "base64data", mimeType: "image/png") let imageData = try encoder.encode(imageContent) @@ -142,7 +154,7 @@ 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 @@ -157,7 +169,7 @@ struct PromptTests { #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 diff --git a/Tests/MCPTests/ToolTests.swift b/Tests/MCPTests/ToolTests.swift index 0ab0c503..22e744e5 100644 --- a/Tests/MCPTests/ToolTests.swift +++ b/Tests/MCPTests/ToolTests.swift @@ -103,6 +103,26 @@ struct ToolTests { } } + @Test("Audio content encoding and decoding") + func testToolContentAudioEncoding() throws { + let content = Tool.Content.audio( + data: "base64audiodata", + mimeType: "audio/wav" + ) + let encoder = JSONEncoder() + let decoder = JSONDecoder() + + let data = try encoder.encode(content) + let decoded = try decoder.decode(Tool.Content.self, from: data) + + if case .audio(let data, let mimeType) = decoded { + #expect(data == "base64audiodata") + #expect(mimeType == "audio/wav") + } else { + #expect(Bool(false), "Expected audio content") + } + } + @Test("ListTools parameters validation") func testListToolsParameters() throws { let params = ListTools.Parameters(cursor: "next_page")