@@ -131,47 +131,8 @@ public final actor MCPServerProxy: Sendable {
131131 return cachedTools
132132 }
133133
134- let requestId = nextRequestID ( )
135- let request = JSONRPCMessage . request ( id: requestId, method: " tools/list " )
136- let response = try await send ( request)
137-
138- guard case let . response( respData) = response, let result = respData. result else {
139- throw MCPServerProxyError . communicationError ( " Invalid response type for tools/list " )
140- }
141-
142- if let isError = result [ " isError " ] ? . value as? Bool , isError {
143- throw MCPServerProxyError . communicationError ( " Server does not provide any tools " )
144- }
145-
146- guard let toolsData = result [ " tools " ] ? . value as? [ [ String : Any ] ] else {
147- throw MCPServerProxyError . communicationError ( " Invalid response format for tools/list " )
148- }
149-
150- let tools = try toolsData. compactMap { toolData -> MCPTool ? in
151- guard let name = toolData [ " name " ] as? String ,
152- let inputSchema = toolData [ " inputSchema " ] as? [ String : Any ] else {
153- return nil
154- }
155-
156- let description = toolData [ " description " ] as? String
157- let schema = try JSONDecoder ( ) . decode ( JSONSchema . self, from: JSONSerialization . data ( withJSONObject: inputSchema) )
158- let outputSchema : JSONSchema ?
159- if let outputSchemaData = toolData [ " outputSchema " ] as? [ String : Any ] {
160- outputSchema = try JSONDecoder ( ) . decode ( JSONSchema . self, from: JSONSerialization . data ( withJSONObject: outputSchemaData) )
161- } else {
162- outputSchema = nil
163- }
164-
165- // Parse annotations if present
166- let annotations : MCPToolAnnotations ?
167- if let annotationsData = toolData [ " annotations " ] as? [ String : Any ] {
168- annotations = try JSONDecoder ( ) . decode ( MCPToolAnnotations . self, from: JSONSerialization . data ( withJSONObject: annotationsData) )
169- } else {
170- annotations = nil
171- }
172-
173- return MCPTool ( name: name, description: description, inputSchema: schema, outputSchema: outputSchema, annotations: annotations)
174- }
134+ let result = try await requestResult ( method: " tools/list " )
135+ let tools : [ MCPTool ] = try decodeResultField ( " tools " , from: result, method: " tools/list " )
175136
176137 if cacheToolsList {
177138 cachedTools = tools
@@ -180,6 +141,48 @@ public final actor MCPServerProxy: Sendable {
180141 return tools
181142 }
182143
144+ /// Lists all static resources available from the server.
145+ public func listResources( ) async throws -> [ SimpleResource ] {
146+ let result = try await requestResult ( method: " resources/list " )
147+ return try decodeResultField ( " resources " , from: result, method: " resources/list " )
148+ }
149+
150+ /// Lists all resource templates available from the server.
151+ public func listResourceTemplates( ) async throws -> [ SimpleResourceTemplate ] {
152+ let result = try await requestResult ( method: " resources/templates/list " )
153+ return try decodeResultField ( " resourceTemplates " , from: result, method: " resources/templates/list " )
154+ }
155+
156+ /// Reads a resource at the specified URI.
157+ public func readResource( uri: URL ) async throws -> [ GenericResourceContent ] {
158+ let result = try await requestResult (
159+ method: " resources/read " ,
160+ params: [ " uri " : AnyCodable ( uri. absoluteString) ]
161+ )
162+ return try decodeResultField ( " contents " , from: result, method: " resources/read " )
163+ }
164+
165+ /// Lists all prompts available from the server.
166+ public func listPrompts( ) async throws -> [ Prompt ] {
167+ let result = try await requestResult ( method: " prompts/list " )
168+ return try decodeResultField ( " prompts " , from: result, method: " prompts/list " )
169+ }
170+
171+ /// Gets a prompt by name with optional arguments.
172+ public func getPrompt(
173+ name: String ,
174+ arguments: [ String : any Sendable ] = [ : ]
175+ ) async throws -> PromptResult {
176+ let result = try await requestResult (
177+ method: " prompts/get " ,
178+ params: [
179+ " name " : AnyCodable ( name) ,
180+ " arguments " : AnyCodable ( arguments. mapValues ( AnyCodable . init) )
181+ ]
182+ )
183+ return try decodeResultField ( " self " , from: result, method: " prompts/get " , as: PromptResult . self)
184+ }
185+
183186 /// Calls a tool by name on the connected MCP server with the provided arguments.
184187 public func callTool(
185188 _ name: String ,
@@ -205,18 +208,21 @@ public final actor MCPServerProxy: Sendable {
205208 let request = JSONRPCMessage . request ( id: requestId, method: " tools/call " , params: params)
206209 let responseMessage = try await send ( request)
207210
208- guard case let . response( responseData) = responseMessage, let result = responseData. result else {
211+ let result : [ String : AnyCodable ]
212+ switch responseMessage {
213+ case . response( let responseData) :
214+ guard let responseResult = responseData. result else {
215+ throw MCPServerProxyError . communicationError ( " Invalid response type for tools/call, expected JSONRPCResponse " )
216+ }
217+ result = responseResult
218+ case . errorResponse( let errorResponse) :
219+ throw MCPServerProxyError . toolError ( errorResponse. error. message)
220+ default :
209221 throw MCPServerProxyError . communicationError ( " Invalid response type for tools/call, expected JSONRPCResponse " )
210222 }
211223
212224 if let isError = result [ " isError " ] ? . value as? Bool , isError {
213- var errorMessage = " Tool call failed with an unspecified error. "
214- if let contentArray = result [ " content " ] ? . value as? [ Any ] ,
215- let firstContent = contentArray. first as? [ String : Any ] ,
216- let text = firstContent [ " text " ] as? String {
217- errorMessage = text
218- }
219- throw MCPServerProxyError . toolError ( errorMessage)
225+ throw MCPServerProxyError . toolError ( errorMessage ( from: result) ?? " Tool call failed with an unspecified error. " )
220226 }
221227
222228 guard let contentValue = result [ " content " ] ? . value else {
@@ -714,6 +720,72 @@ public final actor MCPServerProxy: Sendable {
714720 return nil
715721 }
716722
723+ private func requestResult(
724+ method: String ,
725+ params: [ String : AnyCodable ] ? = nil
726+ ) async throws -> [ String : AnyCodable ] {
727+ let requestId = nextRequestID ( )
728+ let request = JSONRPCMessage . request ( id: requestId, method: method, params: params)
729+ let response = try await send ( request)
730+
731+ switch response {
732+ case . response( let responseData) :
733+ guard let result = responseData. result else {
734+ throw MCPServerProxyError . communicationError ( " Invalid response type for \( method) " )
735+ }
736+ if let isError = result [ " isError " ] ? . value as? Bool , isError {
737+ throw MCPServerProxyError . communicationError ( errorMessage ( from: result) ?? " Request failed for \( method) " )
738+ }
739+ return result
740+ case . errorResponse( let errorResponse) :
741+ throw MCPServerProxyError . communicationError ( errorResponse. error. message)
742+ default :
743+ throw MCPServerProxyError . communicationError ( " Invalid response type for \( method) " )
744+ }
745+ }
746+
747+ private func decodeResultField< T: Decodable > (
748+ _ field: String ,
749+ from result: [ String : AnyCodable ] ,
750+ method: String ,
751+ as type: T . Type = T . self
752+ ) throws -> T {
753+ let value : AnyCodable
754+ if field == " self " {
755+ value = AnyCodable ( result)
756+ } else if let fieldValue = result [ field] {
757+ value = fieldValue
758+ } else {
759+ throw MCPServerProxyError . communicationError ( " Invalid response format for \( method) " )
760+ }
761+
762+ let encoder = JSONEncoder ( )
763+ encoder. dateEncodingStrategy = . iso8601WithTimeZone
764+ let data = try encoder. encode ( value)
765+
766+ let decoder = JSONDecoder ( )
767+ decoder. dateDecodingStrategy = . iso8601WithTimeZone
768+ return try decoder. decode ( type, from: data)
769+ }
770+
771+ private func errorMessage( from result: [ String : AnyCodable ] ) -> String ? {
772+ if let message = stringValue ( result [ " message " ] ) {
773+ return message
774+ }
775+ if let contentValue = result [ " content " ] ? . value {
776+ let contentArray : [ Any ]
777+ if let array = contentValue as? [ Any ] {
778+ contentArray = array
779+ } else if let array = contentValue as? [ AnyCodable ] {
780+ contentArray = array. map ( \. value)
781+ } else {
782+ contentArray = [ ]
783+ }
784+ return extractTextPayload ( from: contentArray)
785+ }
786+ return nil
787+ }
788+
717789
718790 private func extractServerDescription( from result: [ String : AnyCodable ] ) -> String ? {
719791 guard let serverInfoValue = result [ " serverInfo " ] ? . value else {
0 commit comments