diff --git a/README.md b/README.md index a1a8ec84..d09972bc 100644 --- a/README.md +++ b/README.md @@ -51,11 +51,11 @@ let server = Server( name: "MyServer", version: "1.0.0", capabilities: .init( + prompts: .init(), resources: .init( - list: true, - read: true, subscribe: true - ) + ), + tools: .init() ) ) diff --git a/Sources/MCP/Client/Client.swift b/Sources/MCP/Client/Client.swift index be32df76..e910ac8f 100644 --- a/Sources/MCP/Client/Client.swift +++ b/Sources/MCP/Client/Client.swift @@ -313,7 +313,7 @@ public actor Client { // MARK: - Resources public func readResource(uri: String) async throws -> [Resource.Content] { - _ = try checkCapability(\.resources?.read, "Resource reading") + _ = try checkCapability(\.resources, "Resources") let request = ReadResource.request(.init(uri: uri)) let result = try await send(request) return result.contents @@ -322,7 +322,7 @@ public actor Client { public func listResources(cursor: String? = nil) async throws -> ( resources: [Resource], nextCursor: String? ) { - _ = try checkCapability(\.resources?.list, "Resource listing") + _ = try checkCapability(\.resources, "Resources") let request = ListResources.request(.init(cursor: cursor)) let result = try await send(request) return (resources: result.resources, nextCursor: result.nextCursor) diff --git a/Sources/MCP/Server/Server.swift b/Sources/MCP/Server/Server.swift index 495d7680..168b0625 100644 --- a/Sources/MCP/Server/Server.swift +++ b/Sources/MCP/Server/Server.swift @@ -44,23 +44,15 @@ public actor Server { public struct Capabilities: Hashable, Codable, Sendable { /// Resources capabilities public struct Resources: Hashable, Codable, Sendable { - /// Whether the list of resources has changed - public var list: Bool? - /// Whether the resource can be read - public var read: Bool? /// Whether the resource can be subscribed to public var subscribe: Bool? /// Whether the list of resources has changed public var listChanged: Bool? public init( - list: Bool? = nil, - read: Bool? = nil, subscribe: Bool? = nil, listChanged: Bool? = nil ) { - self.list = list - self.read = read self.subscribe = subscribe self.listChanged = listChanged } diff --git a/Tests/MCPTests/RoundtripTests.swift b/Tests/MCPTests/RoundtripTests.swift index 014ca85b..681c77e9 100644 --- a/Tests/MCPTests/RoundtripTests.swift +++ b/Tests/MCPTests/RoundtripTests.swift @@ -33,7 +33,11 @@ struct RoundtripTests { let server = Server( name: "TestServer", version: "1.0.0", - capabilities: .init(prompts: .init(), tools: .init()) + capabilities: .init( + prompts: .init(), + resources: .init(), + tools: .init() + ) ) await server.withMethodHandler(ListTools.self) { _ in return ListTools.Result(tools: [ @@ -61,6 +65,32 @@ struct RoundtripTests { return CallTool.Result(content: [.text("\(a + b)")]) } + // Add resource handlers to server + await server.withMethodHandler(ListResources.self) { _ in + return ListResources.Result(resources: [ + Resource( + name: "Example Text", + uri: "test://example.txt", + description: "A test resource", + mimeType: "text/plain" + ), + Resource( + name: "Test Data", + uri: "test://data.json", + description: "JSON test data", + mimeType: "application/json" + ), + ]) + } + + await server.withMethodHandler(ReadResource.self) { request in + guard request.uri == "test://example.txt" else { + return ReadResource.Result(contents: [.text("Resource not found", uri: request.uri)] + ) + } + return ReadResource.Result(contents: [.text("Hello, World!", uri: request.uri)]) + } + let client = Client(name: "TestClient", version: "1.0") try await server.start(transport: serverTransport) @@ -104,8 +134,12 @@ struct RoundtripTests { group.addTask { try await Task.sleep(for: .seconds(1)) listToolsTask.cancel() + callToolTask.cancel() throw CancellationError() } + group.addTask { + try await listToolsTask.value + } group.addTask { try await callToolTask.value } @@ -113,6 +147,40 @@ struct RoundtripTests { group.cancelAll() } + // Test listing resources + let listResourcesTask = Task { + let result = try await client.listResources() + #expect(result.resources.count == 2) + #expect(result.resources[0].uri == "test://example.txt") + #expect(result.resources[0].name == "Example Text") + #expect(result.resources[1].uri == "test://data.json") + #expect(result.resources[1].name == "Test Data") + } + + // Test reading a resource + let readResourceTask = Task { + let result = try await client.readResource(uri: "test://example.txt") + #expect(result.count == 1) + #expect(result[0] == .text("Hello, World!", uri: "test://example.txt")) + } + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await Task.sleep(for: .seconds(1)) + listResourcesTask.cancel() + readResourceTask.cancel() + throw CancellationError() + } + group.addTask { + try await listResourcesTask.value + } + group.addTask { + try await readResourceTask.value + } + try await group.next() + group.cancelAll() + } + await server.stop() await client.disconnect() try? clientToServerRead.close()