feat: 2025-11-25 review fixes (#201)
* fix: review fixes of the latest changes * fix: updated unit tests * chore: more conformance compliance improvements * chore: reverted protocol changes * chore: more compliance improvements * chore: more conformance compliance improvements * chore: improvements to the backcompat and updated to Tool unit tests * chore: ci update
This commit is contained in:
parent
6112a3995a
commit
973b46b8f7
11 changed files with 692 additions and 507 deletions
|
|
@ -133,7 +133,7 @@ struct CancellationTests {
|
|||
}
|
||||
|
||||
await server.withMethodHandler(CallTool.self) { params in
|
||||
return .init(content: [.text("Result for \(params.name)")], isError: false)
|
||||
return .init(content: [.text(text: "Result for \(params.name)", annotations: nil, _meta: nil)], isError: false)
|
||||
}
|
||||
|
||||
// Start server and connect client
|
||||
|
|
@ -149,7 +149,7 @@ struct CancellationTests {
|
|||
|
||||
// Get the result
|
||||
let result = try await context.value
|
||||
#expect(result.content == [.text("Result for testTool")])
|
||||
#expect(result.content == [.text(text: "Result for testTool", annotations: nil, _meta: nil)])
|
||||
#expect(result.isError == false)
|
||||
|
||||
await client.disconnect()
|
||||
|
|
@ -174,7 +174,7 @@ struct CancellationTests {
|
|||
|
||||
await server.withMethodHandler(CallTool.self) { params in
|
||||
try await Task.sleep(for: .seconds(5))
|
||||
return .init(content: [.text("Should not reach here")], isError: false)
|
||||
return .init(content: [.text(text: "Should not reach here", annotations: nil, _meta: nil)], isError: false)
|
||||
}
|
||||
|
||||
// Start server and connect client
|
||||
|
|
|
|||
|
|
@ -128,12 +128,12 @@ struct CompletionTests {
|
|||
func testCompleteRequestWithContext() throws {
|
||||
let ref = CompletionReference.prompt(PromptReference(name: "code_review"))
|
||||
let argument = Complete.Parameters.Argument(name: "framework", value: "fla")
|
||||
let context = Complete.Parameters.Context(arguments: ["language": .string("python")])
|
||||
let context = Complete.Parameters.Context(arguments: ["language": "python"])
|
||||
|
||||
let request = Complete.request(.init(ref: ref, argument: argument, context: context))
|
||||
|
||||
#expect(request.params.context != nil)
|
||||
#expect(request.params.context?.arguments["language"] == .string("python"))
|
||||
#expect(request.params.context?.arguments["language"] == "python")
|
||||
}
|
||||
|
||||
@Test("Complete request encoding")
|
||||
|
|
@ -396,7 +396,7 @@ struct CompletionTests {
|
|||
#expect(params.argument.name == "framework")
|
||||
#expect(params.argument.value == "fla")
|
||||
#expect(params.context != nil)
|
||||
#expect(params.context?.arguments["language"] == .string("python"))
|
||||
#expect(params.context?.arguments["language"] == "python")
|
||||
|
||||
return .init(
|
||||
completion: .init(
|
||||
|
|
@ -415,7 +415,7 @@ struct CompletionTests {
|
|||
promptName: "code_review",
|
||||
argumentName: "framework",
|
||||
argumentValue: "fla",
|
||||
context: ["language": .string("python")]
|
||||
context: ["language": "python"]
|
||||
)
|
||||
|
||||
#expect(completion.values == ["flask"])
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ struct Elicitation2025_11_25Tests {
|
|||
let decoder = JSONDecoder()
|
||||
|
||||
let params = CreateElicitation.Parameters.form(
|
||||
.init(message: "Enter your name")
|
||||
.init(message: "Enter your name", requestedSchema: .init())
|
||||
)
|
||||
|
||||
let data = try encoder.encode(params)
|
||||
|
|
@ -262,7 +262,7 @@ struct ElicitationIntegrationTests {
|
|||
await client.withElicitationHandler { parameters in
|
||||
if case .form(let formParams) = parameters {
|
||||
#expect(formParams.message == "Please enter your details")
|
||||
#expect(formParams.requestedSchema?.properties["email"] != nil)
|
||||
#expect(formParams.requestedSchema.properties["email"] != nil)
|
||||
#expect(formParams._meta?["flow"]?.stringValue == "onboarding")
|
||||
|
||||
// Return accepted response
|
||||
|
|
@ -384,7 +384,8 @@ struct ElicitationIntegrationTests {
|
|||
try await client.connect(transport: clientTransport)
|
||||
|
||||
let result = try await server.requestElicitation(
|
||||
message: "Optional question"
|
||||
message: "Optional question",
|
||||
requestedSchema: .init()
|
||||
)
|
||||
|
||||
#expect(result.action == .decline)
|
||||
|
|
@ -414,7 +415,8 @@ struct ElicitationIntegrationTests {
|
|||
// Should throw an error because client doesn't have elicitation capability
|
||||
await #expect(throws: MCPError.self) {
|
||||
_ = try await server.requestElicitation(
|
||||
message: "Test message"
|
||||
message: "Test message",
|
||||
requestedSchema: .init()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -452,7 +454,8 @@ struct ElicitationIntegrationTests {
|
|||
|
||||
// Should succeed because client declares elicitation capability
|
||||
let result = try await server.requestElicitation(
|
||||
message: "Test message"
|
||||
message: "Test message",
|
||||
requestedSchema: .init()
|
||||
)
|
||||
|
||||
#expect(result.action == .accept)
|
||||
|
|
@ -486,7 +489,8 @@ struct ElicitationIntegrationTests {
|
|||
// Should fail because client doesn't declare elicitation capability in strict mode
|
||||
await #expect(throws: MCPError.self) {
|
||||
_ = try await server.requestElicitation(
|
||||
message: "Test message"
|
||||
message: "Test message",
|
||||
requestedSchema: .init()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -525,7 +529,8 @@ struct ElicitationIntegrationTests {
|
|||
|
||||
// Should succeed because server is in non-strict mode
|
||||
let result = try await server.requestElicitation(
|
||||
message: "Test message"
|
||||
message: "Test message",
|
||||
requestedSchema: .init()
|
||||
)
|
||||
|
||||
#expect(result.action == .accept)
|
||||
|
|
@ -608,15 +613,15 @@ struct ElicitationIntegrationTests {
|
|||
try await client.connect(transport: clientTransport)
|
||||
|
||||
// Make multiple sequential requests
|
||||
let result1 = try await server.requestElicitation(message: "First question")
|
||||
let result1 = try await server.requestElicitation(message: "First question", requestedSchema: .init())
|
||||
#expect(result1.action == .accept)
|
||||
#expect(result1.content?["echo"]?.stringValue == "First question")
|
||||
|
||||
let result2 = try await server.requestElicitation(message: "Second question")
|
||||
let result2 = try await server.requestElicitation(message: "Second question", requestedSchema: .init())
|
||||
#expect(result2.action == .accept)
|
||||
#expect(result2.content?["echo"]?.stringValue == "Second question")
|
||||
|
||||
let result3 = try await server.requestElicitation(message: "Third question")
|
||||
let result3 = try await server.requestElicitation(message: "Third question", requestedSchema: .init())
|
||||
#expect(result3.action == .accept)
|
||||
#expect(result3.content?["echo"]?.stringValue == "Third question")
|
||||
|
||||
|
|
|
|||
|
|
@ -637,7 +637,7 @@ import Testing
|
|||
#expect(toolName == "calculator")
|
||||
|
||||
let requestID = json["id"] as! String
|
||||
let result = CallTool.Result(content: [.text("42")])
|
||||
let result = CallTool.Result(content: [.text(text: "42", annotations: nil, _meta: nil)])
|
||||
let response = CallTool.response(id: .string(requestID), result: result)
|
||||
let responseData = try JSONEncoder().encode(response)
|
||||
|
||||
|
|
@ -669,7 +669,7 @@ import Testing
|
|||
// Step 2: Call a tool
|
||||
let toolResult = try await client.callTool(name: "calculator")
|
||||
#expect(toolResult.content.count == 1)
|
||||
if case let .text(text) = toolResult.content[0] {
|
||||
if case let .text(text, _, _) = toolResult.content[0] {
|
||||
#expect(text == "42")
|
||||
} else {
|
||||
#expect(Bool(false), "Expected text content")
|
||||
|
|
|
|||
|
|
@ -18,60 +18,6 @@ struct MetaFieldsTests {
|
|||
typealias Result = Payload
|
||||
}
|
||||
|
||||
@Test("Encoding includes meta and custom fields")
|
||||
func testEncodingGeneralFields() throws {
|
||||
let meta: Metadata = Metadata(additionalFields: ["vendor.example/request-id": .string("abc123")])
|
||||
|
||||
let request = Request<TestMethod>(
|
||||
id: 42,
|
||||
method: TestMethod.name,
|
||||
params: Payload(message: "hello"),
|
||||
_meta: meta
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(request)
|
||||
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
||||
|
||||
let metaObject = json?["_meta"] as? [String: Any]
|
||||
#expect(metaObject?["vendor.example/request-id"] as? String == "abc123")
|
||||
}
|
||||
|
||||
@Test("Decoding restores general fields")
|
||||
func testDecodingGeneralFields() throws {
|
||||
let payload: [String: Any] = [
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"method": TestMethod.name,
|
||||
"params": ["message": "hi"],
|
||||
"_meta": ["vendor.example/session": "s42"],
|
||||
"custom-data": ["value": 1],
|
||||
]
|
||||
|
||||
let data = try JSONSerialization.data(withJSONObject: payload)
|
||||
let decoded = try JSONDecoder().decode(Request<TestMethod>.self, from: data)
|
||||
|
||||
let metaValue = decoded._meta?["vendor.example/session"]
|
||||
#expect(metaValue == .string("s42"))
|
||||
}
|
||||
|
||||
@Test("Response encoding includes general fields")
|
||||
func testResponseGeneralFields() throws {
|
||||
let meta = Metadata(additionalFields: ["vendor.example/status": .string("partial")])
|
||||
let response = Response<TestMethod>(
|
||||
id: 99,
|
||||
result: .success(Payload(message: "ok")),
|
||||
_meta: meta
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(response)
|
||||
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
||||
let metaObject = json?["_meta"] as? [String: Any]
|
||||
#expect(metaObject?["vendor.example/status"] as? String == "partial")
|
||||
|
||||
let decoded = try JSONDecoder().decode(Response<TestMethod>.self, from: data)
|
||||
#expect(decoded._meta?["vendor.example/status"] == .string("partial"))
|
||||
}
|
||||
|
||||
@Test("Tool encoding and decoding with general fields")
|
||||
func testToolGeneralFields() throws {
|
||||
let meta = Metadata(additionalFields: [
|
||||
|
|
@ -199,7 +145,7 @@ struct MetaFieldsTests {
|
|||
let meta = Metadata(additionalFields: ["vendor.example/executionTime": .int(150)])
|
||||
|
||||
let result = CallTool.Result(
|
||||
content: [.text("Result data")],
|
||||
content: [.text(text: "Result data", annotations: nil, _meta: nil)],
|
||||
isError: false,
|
||||
_meta: meta
|
||||
)
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ struct ProgressTests {
|
|||
}
|
||||
|
||||
let server = Server(name: "testServer", version: "1")
|
||||
let expectedToolCallResult = CallTool.Result(content: [.text("success")])
|
||||
let expectedToolCallResult = CallTool.Result(content: [.text(text: "success", annotations: nil, _meta: nil)])
|
||||
await server.withMethodHandler(CallTool.self) { params in
|
||||
if let token = params._meta?.progressToken {
|
||||
for i in 1...5 {
|
||||
|
|
@ -375,7 +375,7 @@ struct ProgressTests {
|
|||
}
|
||||
}
|
||||
|
||||
return .init(content: [.text("success")])
|
||||
return .init(content: [.text(text: "success", annotations: nil, _meta: nil)])
|
||||
}
|
||||
|
||||
try await server.start(transport: pair.server)
|
||||
|
|
|
|||
|
|
@ -125,9 +125,9 @@ struct PromptTests {
|
|||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
let arguments: [String: Value] = [
|
||||
"param1": .string("value1"),
|
||||
"param2": .int(42),
|
||||
let arguments: [String: String] = [
|
||||
"param1": "value1",
|
||||
"param2": "42",
|
||||
]
|
||||
|
||||
let params = GetPrompt.Parameters(name: "test_prompt", arguments: arguments)
|
||||
|
|
@ -570,14 +570,14 @@ struct PromptIntegrationTests {
|
|||
)
|
||||
|
||||
let expectedMessages: [Prompt.Message] = [
|
||||
.user("Hello, I'd like to schedule an interview for the \(Value.string("Software Engineer")) position"),
|
||||
.user("Hello, I'd like to schedule an interview for the Software Engineer position"),
|
||||
.assistant("I'd be happy to help you prepare for the Software Engineer interview. Let's discuss your background."),
|
||||
]
|
||||
|
||||
// Register get prompt handler
|
||||
await server.withMethodHandler(GetPrompt.self) { params in
|
||||
#expect(params.name == "interview")
|
||||
#expect(params.arguments?["position"]?.stringValue == "Software Engineer")
|
||||
#expect(params.arguments?["position"] == "Software Engineer")
|
||||
|
||||
return GetPrompt.Result(
|
||||
description: "Interview preparation prompt",
|
||||
|
|
@ -595,7 +595,7 @@ struct PromptIntegrationTests {
|
|||
|
||||
let (description, messages) = try await client.getPrompt(
|
||||
name: "interview",
|
||||
arguments: ["position": .string("Software Engineer")]
|
||||
arguments: ["position": "Software Engineer"]
|
||||
)
|
||||
|
||||
#expect(description == "Interview preparation prompt")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ struct ResourceTests {
|
|||
title: "Test Resource Title",
|
||||
description: "A test resource",
|
||||
mimeType: "text/plain",
|
||||
metadata: ["key": "value"]
|
||||
size: 1024
|
||||
)
|
||||
|
||||
#expect(resource.name == "test_resource")
|
||||
|
|
@ -21,7 +21,7 @@ struct ResourceTests {
|
|||
#expect(resource.title == "Test Resource Title")
|
||||
#expect(resource.description == "A test resource")
|
||||
#expect(resource.mimeType == "text/plain")
|
||||
#expect(resource.metadata?["key"] == "value")
|
||||
#expect(resource.size == 1024)
|
||||
}
|
||||
|
||||
@Test("Resource encoding and decoding")
|
||||
|
|
@ -32,7 +32,7 @@ struct ResourceTests {
|
|||
title: "Test Resource Title",
|
||||
description: "Test resource description",
|
||||
mimeType: "text/plain",
|
||||
metadata: ["key1": "value1", "key2": "value2"]
|
||||
size: 512
|
||||
)
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
|
|
@ -46,7 +46,7 @@ struct ResourceTests {
|
|||
#expect(decoded.title == resource.title)
|
||||
#expect(decoded.description == resource.description)
|
||||
#expect(decoded.mimeType == resource.mimeType)
|
||||
#expect(decoded.metadata == resource.metadata)
|
||||
#expect(decoded.size == resource.size)
|
||||
}
|
||||
|
||||
@Test("Resource.Content text initialization and encoding")
|
||||
|
|
|
|||
|
|
@ -57,17 +57,17 @@ struct RoundtripTests {
|
|||
}
|
||||
await server.withMethodHandler(CallTool.self) { request in
|
||||
guard request.name == "add" else {
|
||||
return CallTool.Result(content: [.text("Invalid tool name")], isError: true)
|
||||
return CallTool.Result(content: [.text(text: "Invalid tool name", annotations: nil, _meta: nil)], isError: true)
|
||||
}
|
||||
|
||||
guard let a = request.arguments?["a"]?.intValue,
|
||||
let b = request.arguments?["b"]?.intValue
|
||||
else {
|
||||
return CallTool.Result(
|
||||
content: [.text("Did not receive valid arguments")], isError: true)
|
||||
content: [.text(text: "Did not receive valid arguments", annotations: nil, _meta: nil)], isError: true)
|
||||
}
|
||||
|
||||
return CallTool.Result(content: [.text("\(a + b)")])
|
||||
return CallTool.Result(content: [.text(text: "\(a + b)", annotations: nil, _meta: nil)])
|
||||
}
|
||||
|
||||
// Add resource handlers to server
|
||||
|
|
@ -151,7 +151,7 @@ struct RoundtripTests {
|
|||
let callToolTask = Task {
|
||||
let result = try await client.callTool(name: "add", arguments: ["a": 1, "b": 2])
|
||||
#expect(result.isError == nil)
|
||||
#expect(result.content == [.text("3")])
|
||||
#expect(result.content == [.text(text: "3", annotations: nil, _meta: nil)])
|
||||
}
|
||||
|
||||
try await withThrowingTaskGroup(of: Void.self) { group in
|
||||
|
|
|
|||
|
|
@ -183,23 +183,6 @@ struct SamplingTests {
|
|||
#expect(request.params.maxTokens == 100)
|
||||
}
|
||||
|
||||
@Test("Server capabilities include sampling")
|
||||
func testServerCapabilitiesIncludeSampling() throws {
|
||||
let capabilities = Server.Capabilities(
|
||||
sampling: .init()
|
||||
)
|
||||
|
||||
#expect(capabilities.sampling != nil)
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
let data = try encoder.encode(capabilities)
|
||||
let decoded = try decoder.decode(Server.Capabilities.self, from: data)
|
||||
|
||||
#expect(decoded.sampling != nil)
|
||||
}
|
||||
|
||||
@Test("Client capabilities include sampling")
|
||||
func testClientCapabilitiesIncludeSampling() throws {
|
||||
let capabilities = Client.Capabilities(
|
||||
|
|
@ -507,8 +490,7 @@ struct SamplingIntegrationTests {
|
|||
|
||||
let server = Server(
|
||||
name: "SamplingHandlerTestServer",
|
||||
version: "1.0",
|
||||
capabilities: .init(sampling: .init())
|
||||
version: "1.0"
|
||||
)
|
||||
|
||||
let client = Client(
|
||||
|
|
@ -553,8 +535,7 @@ struct SamplingIntegrationTests {
|
|||
|
||||
let server = Server(
|
||||
name: "SamplingRequestTestServer",
|
||||
version: "1.0",
|
||||
capabilities: .init(sampling: .init())
|
||||
version: "1.0"
|
||||
)
|
||||
|
||||
let client = Client(
|
||||
|
|
@ -721,8 +702,7 @@ struct SamplingIntegrationTests {
|
|||
|
||||
let server = Server(
|
||||
name: "ErrorTestServer",
|
||||
version: "1.0",
|
||||
capabilities: .init(sampling: .init())
|
||||
version: "1.0"
|
||||
)
|
||||
|
||||
// Client WITHOUT sampling capability
|
||||
|
|
@ -757,7 +737,6 @@ struct SamplingIntegrationTests {
|
|||
let server = Server(
|
||||
name: "StrictTestServer",
|
||||
version: "1.0",
|
||||
capabilities: .init(sampling: .init()),
|
||||
configuration: .strict
|
||||
)
|
||||
|
||||
|
|
@ -806,7 +785,6 @@ struct SamplingIntegrationTests {
|
|||
let server = Server(
|
||||
name: "StrictTestServer",
|
||||
version: "1.0",
|
||||
capabilities: .init(sampling: .init()),
|
||||
configuration: .strict
|
||||
)
|
||||
|
||||
|
|
@ -840,7 +818,6 @@ struct SamplingIntegrationTests {
|
|||
let server = Server(
|
||||
name: "NonStrictTestServer",
|
||||
version: "1.0",
|
||||
capabilities: .init(sampling: .init()),
|
||||
configuration: .default // Non-strict mode
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -240,14 +240,14 @@ struct ToolTests {
|
|||
|
||||
@Test("Text content encoding and decoding")
|
||||
func testToolContentTextEncoding() throws {
|
||||
let content = Tool.Content.text("Hello, world!")
|
||||
let content = Tool.Content.text(text: "Hello, world!", annotations: nil, _meta: nil)
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
let data = try encoder.encode(content)
|
||||
let decoded = try decoder.decode(Tool.Content.self, from: data)
|
||||
|
||||
if case .text(let text) = decoded {
|
||||
if case .text(let text, _, _) = decoded {
|
||||
#expect(text == "Hello, world!")
|
||||
} else {
|
||||
#expect(Bool(false), "Expected text content")
|
||||
|
|
@ -259,7 +259,8 @@ struct ToolTests {
|
|||
let content = Tool.Content.image(
|
||||
data: "base64data",
|
||||
mimeType: "image/png",
|
||||
metadata: .init(additionalFields: ["width": "100", "height": "100"])
|
||||
annotations: nil,
|
||||
_meta: nil
|
||||
)
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
|
@ -267,11 +268,9 @@ struct ToolTests {
|
|||
let data = try encoder.encode(content)
|
||||
let decoded = try decoder.decode(Tool.Content.self, from: data)
|
||||
|
||||
if case .image(let data, let mimeType, let metadata) = decoded {
|
||||
if case .image(let data, let mimeType, _, _) = decoded {
|
||||
#expect(data == "base64data")
|
||||
#expect(mimeType == "image/png")
|
||||
#expect(metadata?["width"] == "100")
|
||||
#expect(metadata?["height"] == "100")
|
||||
} else {
|
||||
#expect(Bool(false), "Expected image content")
|
||||
}
|
||||
|
|
@ -337,7 +336,9 @@ struct ToolTests {
|
|||
func testToolContentAudioEncoding() throws {
|
||||
let content = Tool.Content.audio(
|
||||
data: "base64audiodata",
|
||||
mimeType: "audio/wav"
|
||||
mimeType: "audio/wav",
|
||||
annotations: nil,
|
||||
_meta: nil
|
||||
)
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
|
@ -345,7 +346,7 @@ struct ToolTests {
|
|||
let data = try encoder.encode(content)
|
||||
let decoded = try decoder.decode(Tool.Content.self, from: data)
|
||||
|
||||
if case .audio(let data, let mimeType) = decoded {
|
||||
if case .audio(let data, let mimeType, _, _) = decoded {
|
||||
#expect(data == "base64audiodata")
|
||||
#expect(mimeType == "audio/wav")
|
||||
} else {
|
||||
|
|
@ -353,6 +354,71 @@ struct ToolTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test("Text content encoding includes annotations and meta fields")
|
||||
func testToolContentTextEncodingIncludesAnnotationsAndMeta() throws {
|
||||
let annotations = Resource.Annotations(
|
||||
audience: [.assistant],
|
||||
priority: 0.75,
|
||||
lastModified: "2026-01-01T00:00:00Z"
|
||||
)
|
||||
let meta = Metadata(additionalFields: [
|
||||
"vendor.example/source": .string("tool-text")
|
||||
])
|
||||
let expected = Tool.Content.text(
|
||||
text: "Hello with metadata",
|
||||
annotations: annotations,
|
||||
_meta: meta
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(expected)
|
||||
let decoded = try JSONDecoder().decode(Tool.Content.self, from: data)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
|
||||
@Test("Image content encoding includes annotations and meta fields")
|
||||
func testToolContentImageEncodingIncludesAnnotationsAndMeta() throws {
|
||||
let annotations = Resource.Annotations(
|
||||
audience: [.user],
|
||||
priority: 0.9,
|
||||
lastModified: "2026-01-02T00:00:00Z"
|
||||
)
|
||||
let meta = Metadata(additionalFields: [
|
||||
"vendor.example/source": .string("tool-image")
|
||||
])
|
||||
let expected = Tool.Content.image(
|
||||
data: "base64-image",
|
||||
mimeType: "image/jpeg",
|
||||
annotations: annotations,
|
||||
_meta: meta
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(expected)
|
||||
let decoded = try JSONDecoder().decode(Tool.Content.self, from: data)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
|
||||
@Test("Audio content encoding includes annotations and meta fields")
|
||||
func testToolContentAudioEncodingIncludesAnnotationsAndMeta() throws {
|
||||
let annotations = Resource.Annotations(
|
||||
audience: [.assistant],
|
||||
priority: 0.4,
|
||||
lastModified: "2026-01-03T00:00:00Z"
|
||||
)
|
||||
let meta = Metadata(additionalFields: [
|
||||
"vendor.example/source": .string("tool-audio")
|
||||
])
|
||||
let expected = Tool.Content.audio(
|
||||
data: "base64-audio",
|
||||
mimeType: "audio/mp3",
|
||||
annotations: annotations,
|
||||
_meta: meta
|
||||
)
|
||||
|
||||
let data = try JSONEncoder().encode(expected)
|
||||
let decoded = try JSONDecoder().decode(Tool.Content.self, from: data)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
|
||||
@Test("ListTools parameters validation")
|
||||
func testListToolsParameters() throws {
|
||||
let params = ListTools.Parameters(cursor: "next_page")
|
||||
|
|
@ -422,15 +488,15 @@ struct ToolTests {
|
|||
@Test("CallTool success result validation")
|
||||
func testCallToolResult() throws {
|
||||
let content = [
|
||||
Tool.Content.text("Result 1"),
|
||||
Tool.Content.text("Result 2"),
|
||||
Tool.Content.text(text: "Result 1", annotations: nil, _meta: nil),
|
||||
Tool.Content.text(text: "Result 2", annotations: nil, _meta: nil),
|
||||
]
|
||||
|
||||
let result = CallTool.Result(content: content)
|
||||
#expect(result.content.count == 2)
|
||||
#expect(result.isError == nil)
|
||||
|
||||
if case .text(let text) = result.content[0] {
|
||||
if case .text(let text, _, _) = result.content[0] {
|
||||
#expect(text == "Result 1")
|
||||
} else {
|
||||
#expect(Bool(false), "Expected text content")
|
||||
|
|
@ -439,12 +505,12 @@ struct ToolTests {
|
|||
|
||||
@Test("CallTool error result validation")
|
||||
func testCallToolErrorResult() throws {
|
||||
let errorContent = [Tool.Content.text("Error message")]
|
||||
let errorContent = [Tool.Content.text(text: "Error message", annotations: nil, _meta: nil)]
|
||||
let errorResult = CallTool.Result(content: errorContent, isError: true)
|
||||
#expect(errorResult.content.count == 1)
|
||||
#expect(errorResult.isError == true)
|
||||
|
||||
if case .text(let text) = errorResult.content[0] {
|
||||
if case .text(let text, _, _) = errorResult.content[0] {
|
||||
#expect(text == "Error message")
|
||||
} else {
|
||||
#expect(Bool(false), "Expected error text content")
|
||||
|
|
|
|||
Loading…
Reference in a new issue