Rename Error to MCPError (#44)

* Rename Error to MCPError

* Conform MCPError to Swift.Error

* Use unqualified Error instead of Swift.Error

* Use unqualified MCPError instead of MCP.MCPError

* Rename file back to Error.swift

* Introduce top-level MCP namespace with nested Error typealias to provide deprecation notice for existing usage of MCP.Error

* Fix markup for MCP enum warning
This commit is contained in:
Mattt 2025-04-03 05:25:00 -07:00 • committed by GitHub
commit 09da2a1c01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 108 additions and 88 deletions

View file

@ -6,8 +6,24 @@ import Foundation
@preconcurrency import SystemPackage @preconcurrency import SystemPackage
#endif #endif
/// Top-level namespace for backward compatibility
///
/// This is provided to allow existing code that uses `MCP.Error` to continue
/// to work without modification.
///
/// The MCPError type is now the recommended way to handle errors in MCP.
///
/// - Warning: This namespace is deprecated and will be removed in a future version.
public enum MCP {
/// Deprecated type alias for MCPError
@available(*, deprecated, renamed: "MCPError", message: "Use MCPError instead of MCP.Error")
public typealias Error = MCPError
}
// MARK: -
/// A model context protocol error. /// A model context protocol error.
public enum Error: Sendable { public enum MCPError: Error, Sendable {
// Standard JSON-RPC 2.0 errors (-32700 to -32603) // Standard JSON-RPC 2.0 errors (-32700 to -32603)
case parseError(String?) // -32700 case parseError(String?) // -32700
case invalidRequest(String?) // -32600 case invalidRequest(String?) // -32600
@ -20,7 +36,7 @@ public enum Error: Sendable {
// Transport specific errors // Transport specific errors
case connectionClosed case connectionClosed
case transportError(Swift.Error) case transportError(Error)
/// The JSON-RPC 2.0 error code /// The JSON-RPC 2.0 error code
public var code: Int { public var code: Int {
@ -37,7 +53,7 @@ public enum Error: Sendable {
} }
/// Check if an error represents a "resource temporarily unavailable" condition /// Check if an error represents a "resource temporarily unavailable" condition
public static func isResourceTemporarilyUnavailable(_ error: Swift.Error) -> Bool { public static func isResourceTemporarilyUnavailable(_ error: Error) -> Bool {
#if canImport(System) #if canImport(System)
if let errno = error as? System.Errno, errno == .resourceTemporarilyUnavailable { if let errno = error as? System.Errno, errno == .resourceTemporarilyUnavailable {
return true return true
@ -53,7 +69,7 @@ public enum Error: Sendable {
// MARK: LocalizedError // MARK: LocalizedError
extension Error: LocalizedError { extension MCPError: LocalizedError {
public var errorDescription: String? { public var errorDescription: String? {
switch self { switch self {
case .parseError(let detail): case .parseError(let detail):
@ -116,7 +132,7 @@ extension Error: LocalizedError {
// MARK: CustomDebugStringConvertible // MARK: CustomDebugStringConvertible
extension Error: CustomDebugStringConvertible { extension MCPError: CustomDebugStringConvertible {
public var debugDescription: String { public var debugDescription: String {
switch self { switch self {
case .transportError(let error): case .transportError(let error):
@ -131,7 +147,7 @@ extension Error: CustomDebugStringConvertible {
// MARK: Codable // MARK: Codable
extension Error: Codable { extension MCPError: Codable {
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case code, message, data case code, message, data
} }
@ -199,15 +215,15 @@ extension Error: Codable {
// MARK: Equatable // MARK: Equatable
extension Error: Equatable { extension MCPError: Equatable {
public static func == (lhs: Error, rhs: Error) -> Bool { public static func == (lhs: MCPError, rhs: MCPError) -> Bool {
lhs.code == rhs.code lhs.code == rhs.code
} }
} }
// MARK: Hashable // MARK: Hashable
extension Error: Hashable { extension MCPError: Hashable {
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
hasher.combine(code) hasher.combine(code)
switch self { switch self {

View file

@ -60,7 +60,7 @@ extension Method {
} }
/// Create a response with the given error. /// Create a response with the given error.
public static func response(id: ID, error: Error) -> Response<Self> { public static func response(id: ID, error: MCPError) -> Response<Self> {
Response(id: id, error: error) Response(id: id, error: error)
} }
} }
@ -186,14 +186,14 @@ public struct Response<M: Method>: Hashable, Identifiable, Codable, Sendable {
/// The response ID. /// The response ID.
public let id: ID public let id: ID
/// The response result. /// The response result.
public let result: Swift.Result<M.Result, Error> public let result: Swift.Result<M.Result, MCPError>
public init(id: ID, result: M.Result) { public init(id: ID, result: M.Result) {
self.id = id self.id = id
self.result = .success(result) self.result = .success(result)
} }
public init(id: ID, error: Error) { public init(id: ID, error: MCPError) {
self.id = id self.id = id
self.result = .failure(error) self.result = .failure(error)
} }
@ -224,7 +224,7 @@ public struct Response<M: Method>: Hashable, Identifiable, Codable, Sendable {
id = try container.decode(ID.self, forKey: .id) id = try container.decode(ID.self, forKey: .id)
if let result = try? container.decode(M.Result.self, forKey: .result) { if let result = try? container.decode(M.Result.self, forKey: .result) {
self.result = .success(result) self.result = .success(result)
} else if let error = try? container.decode(Error.self, forKey: .error) { } else if let error = try? container.decode(MCPError.self, forKey: .error) {
self.result = .failure(error) self.result = .failure(error)
} else { } else {
throw DecodingError.dataCorrupted( throw DecodingError.dataCorrupted(

View file

@ -23,7 +23,7 @@ public protocol Transport: Actor {
func send(_ data: Data) async throws func send(_ data: Data) async throws
/// Receives data in an async sequence /// Receives data in an async sequence
func receive() -> AsyncThrowingStream<Data, Swift.Error> func receive() -> AsyncThrowingStream<Data, Error>
} }
/// Standard input/output transport implementation /// Standard input/output transport implementation
@ -74,11 +74,11 @@ public actor StdioTransport: Transport {
private func setNonBlocking(fileDescriptor: FileDescriptor) throws { private func setNonBlocking(fileDescriptor: FileDescriptor) throws {
let flags = fcntl(fileDescriptor.rawValue, F_GETFL) let flags = fcntl(fileDescriptor.rawValue, F_GETFL)
guard flags >= 0 else { guard flags >= 0 else {
throw Error.transportError(Errno.badFileDescriptor) throw MCPError.transportError(Errno.badFileDescriptor)
} }
let result = fcntl(fileDescriptor.rawValue, F_SETFL, flags | O_NONBLOCK) let result = fcntl(fileDescriptor.rawValue, F_SETFL, flags | O_NONBLOCK)
guard result >= 0 else { guard result >= 0 else {
throw Error.transportError(Errno.badFileDescriptor) throw MCPError.transportError(Errno.badFileDescriptor)
} }
} }
@ -110,7 +110,7 @@ public actor StdioTransport: Transport {
messageContinuation.yield(Data(messageData)) messageContinuation.yield(Data(messageData))
} }
} }
} catch let error where Error.isResourceTemporarilyUnavailable(error) { } catch let error where MCPError.isResourceTemporarilyUnavailable(error) {
try? await Task.sleep(for: .milliseconds(10)) try? await Task.sleep(for: .milliseconds(10))
continue continue
} catch { } catch {
@ -133,7 +133,7 @@ public actor StdioTransport: Transport {
public func send(_ message: Data) async throws { public func send(_ message: Data) async throws {
guard isConnected else { guard isConnected else {
throw Error.transportError(Errno.socketNotConnected) throw MCPError.transportError(Errno.socketNotConnected)
} }
// Add newline as delimiter // Add newline as delimiter
@ -149,16 +149,16 @@ public actor StdioTransport: Transport {
if written > 0 { if written > 0 {
remaining = remaining.dropFirst(written) remaining = remaining.dropFirst(written)
} }
} catch let error where Error.isResourceTemporarilyUnavailable(error) { } catch let error where MCPError.isResourceTemporarilyUnavailable(error) {
try await Task.sleep(for: .milliseconds(10)) try await Task.sleep(for: .milliseconds(10))
continue continue
} catch { } catch {
throw Error.transportError(error) throw MCPError.transportError(error)
} }
} }
} }
public func receive() -> AsyncThrowingStream<Data, Swift.Error> { public func receive() -> AsyncThrowingStream<Data, Error> {
return AsyncThrowingStream { continuation in return AsyncThrowingStream { continuation in
Task { Task {
for await message in messageStream { for await message in messageStream {
@ -179,8 +179,8 @@ public actor StdioTransport: Transport {
public nonisolated let logger: Logger public nonisolated let logger: Logger
private var isConnected = false private var isConnected = false
private let messageStream: AsyncThrowingStream<Data, Swift.Error> private let messageStream: AsyncThrowingStream<Data, Error>
private let messageContinuation: AsyncThrowingStream<Data, Swift.Error>.Continuation private let messageContinuation: AsyncThrowingStream<Data, Error>.Continuation
// Track connection state for continuations // Track connection state for continuations
private var connectionContinuationResumed = false private var connectionContinuationResumed = false
@ -195,7 +195,7 @@ public actor StdioTransport: Transport {
) )
// Create message stream // Create message stream
var continuation: AsyncThrowingStream<Data, Swift.Error>.Continuation! var continuation: AsyncThrowingStream<Data, Error>.Continuation!
self.messageStream = AsyncThrowingStream { continuation = $0 } self.messageStream = AsyncThrowingStream { continuation = $0 }
self.messageContinuation = continuation self.messageContinuation = continuation
} }
@ -209,9 +209,9 @@ public actor StdioTransport: Transport {
// Wait for connection to be ready // Wait for connection to be ready
try await withCheckedThrowingContinuation { try await withCheckedThrowingContinuation {
[weak self] (continuation: CheckedContinuation<Void, Swift.Error>) in [weak self] (continuation: CheckedContinuation<Void, Error>) in
guard let self = self else { guard let self = self else {
continuation.resume(throwing: MCP.Error.internalError("Transport deallocated")) continuation.resume(throwing: MCPError.internalError("Transport deallocated"))
return return
} }
@ -245,7 +245,7 @@ public actor StdioTransport: Transport {
} }
} }
private func handleConnectionReady(continuation: CheckedContinuation<Void, Swift.Error>) private func handleConnectionReady(continuation: CheckedContinuation<Void, Error>)
async async
{ {
if !connectionContinuationResumed { if !connectionContinuationResumed {
@ -259,7 +259,7 @@ public actor StdioTransport: Transport {
} }
private func handleConnectionFailed( private func handleConnectionFailed(
error: Swift.Error, continuation: CheckedContinuation<Void, Swift.Error> error: Error, continuation: CheckedContinuation<Void, Error>
) async { ) async {
if !connectionContinuationResumed { if !connectionContinuationResumed {
connectionContinuationResumed = true connectionContinuationResumed = true
@ -268,13 +268,13 @@ public actor StdioTransport: Transport {
} }
} }
private func handleConnectionCancelled(continuation: CheckedContinuation<Void, Swift.Error>) private func handleConnectionCancelled(continuation: CheckedContinuation<Void, Error>)
async async
{ {
if !connectionContinuationResumed { if !connectionContinuationResumed {
connectionContinuationResumed = true connectionContinuationResumed = true
logger.warning("Connection cancelled") logger.warning("Connection cancelled")
continuation.resume(throwing: MCP.Error.internalError("Connection cancelled")) continuation.resume(throwing: MCPError.internalError("Connection cancelled"))
} }
} }
@ -288,7 +288,7 @@ public actor StdioTransport: Transport {
public func send(_ message: Data) async throws { public func send(_ message: Data) async throws {
guard isConnected else { guard isConnected else {
throw MCP.Error.internalError("Transport not connected") throw MCPError.internalError("Transport not connected")
} }
// Add newline as delimiter // Add newline as delimiter
@ -299,9 +299,9 @@ public actor StdioTransport: Transport {
var sendContinuationResumed = false var sendContinuationResumed = false
try await withCheckedThrowingContinuation { try await withCheckedThrowingContinuation {
[weak self] (continuation: CheckedContinuation<Void, Swift.Error>) in [weak self] (continuation: CheckedContinuation<Void, Error>) in
guard let self = self else { guard let self = self else {
continuation.resume(throwing: MCP.Error.internalError("Transport deallocated")) continuation.resume(throwing: MCPError.internalError("Transport deallocated"))
return return
} }
@ -316,7 +316,7 @@ public actor StdioTransport: Transport {
if let error = error { if let error = error {
self.logger.error("Send error: \(error)") self.logger.error("Send error: \(error)")
continuation.resume( continuation.resume(
throwing: MCP.Error.internalError("Send error: \(error)")) throwing: MCPError.internalError("Send error: \(error)"))
} else { } else {
continuation.resume() continuation.resume()
} }
@ -326,7 +326,7 @@ public actor StdioTransport: Transport {
} }
} }
public func receive() -> AsyncThrowingStream<Data, Swift.Error> { public func receive() -> AsyncThrowingStream<Data, Error> {
return AsyncThrowingStream { continuation in return AsyncThrowingStream { continuation in
Task { Task {
do { do {
@ -363,7 +363,7 @@ public actor StdioTransport: Transport {
} catch let error as NWError { } catch let error as NWError {
if !Task.isCancelled { if !Task.isCancelled {
logger.error("Network error occurred", metadata: ["error": "\(error)"]) logger.error("Network error occurred", metadata: ["error": "\(error)"])
messageContinuation.finish(throwing: MCP.Error.transportError(error)) messageContinuation.finish(throwing: MCPError.transportError(error))
} }
break break
} catch { } catch {
@ -382,9 +382,9 @@ public actor StdioTransport: Transport {
var receiveContinuationResumed = false var receiveContinuationResumed = false
return try await withCheckedThrowingContinuation { return try await withCheckedThrowingContinuation {
[weak self] (continuation: CheckedContinuation<Data, Swift.Error>) in [weak self] (continuation: CheckedContinuation<Data, Error>) in
guard let self = self else { guard let self = self else {
continuation.resume(throwing: MCP.Error.internalError("Transport deallocated")) continuation.resume(throwing: MCPError.internalError("Transport deallocated"))
return return
} }
@ -394,12 +394,12 @@ public actor StdioTransport: Transport {
if !receiveContinuationResumed { if !receiveContinuationResumed {
receiveContinuationResumed = true receiveContinuationResumed = true
if let error = error { if let error = error {
continuation.resume(throwing: MCP.Error.transportError(error)) continuation.resume(throwing: MCPError.transportError(error))
} else if let content = content { } else if let content = content {
continuation.resume(returning: content) continuation.resume(returning: content)
} else { } else {
continuation.resume( continuation.resume(
throwing: MCP.Error.internalError("No data received")) throwing: MCPError.internalError("No data received"))
} }
} }
} }

View file

@ -112,16 +112,16 @@ public actor Client {
private var task: Task<Void, Never>? private var task: Task<Void, Never>?
/// An error indicating a type mismatch when decoding a pending request /// An error indicating a type mismatch when decoding a pending request
private struct TypeMismatchError: Swift.Error {} private struct TypeMismatchError: Error {}
/// A pending request with a continuation for the result /// A pending request with a continuation for the result
private struct PendingRequest<T> { private struct PendingRequest<T> {
let continuation: CheckedContinuation<T, Swift.Error> let continuation: CheckedContinuation<T, Error>
} }
/// A type-erased pending request /// A type-erased pending request
private struct AnyPendingRequest { private struct AnyPendingRequest {
private let _resume: (Result<Any, Swift.Error>) -> Void private let _resume: (Result<Any, Error>) -> Void
init<T: Sendable & Decodable>(_ request: PendingRequest<T>) { init<T: Sendable & Decodable>(_ request: PendingRequest<T>) {
_resume = { result in _resume = { result in
@ -146,7 +146,7 @@ public actor Client {
_resume(.success(value)) _resume(.success(value))
} }
func resume(throwing error: Swift.Error) { func resume(throwing error: Error) {
_resume(.failure(error)) _resume(.failure(error))
} }
} }
@ -201,7 +201,7 @@ public actor Client {
"Unexpected message received by client", metadata: metadata) "Unexpected message received by client", metadata: metadata)
} }
} }
} catch let error where Error.isResourceTemporarilyUnavailable(error) { } catch let error where MCPError.isResourceTemporarilyUnavailable(error) {
try? await Task.sleep(for: .milliseconds(10)) try? await Task.sleep(for: .milliseconds(10))
continue continue
} catch { } catch {
@ -217,7 +217,7 @@ public actor Client {
public func disconnect() async { public func disconnect() async {
// Cancel all pending requests // Cancel all pending requests
for (id, request) in pendingRequests { for (id, request) in pendingRequests {
request.resume(throwing: Error.internalError("Client disconnected")) request.resume(throwing: MCPError.internalError("Client disconnected"))
pendingRequests.removeValue(forKey: id) pendingRequests.removeValue(forKey: id)
} }
@ -247,7 +247,7 @@ public actor Client {
/// Send a request and receive its response /// Send a request and receive its response
public func send<M: Method>(_ request: Request<M>) async throws -> M.Result { public func send<M: Method>(_ request: Request<M>) async throws -> M.Result {
guard let connection = connection else { guard let connection = connection else {
throw Error.internalError("Client connection not initialized") throw MCPError.internalError("Client connection not initialized")
} }
let requestData = try JSONEncoder().encode(request) let requestData = try JSONEncoder().encode(request)
@ -274,7 +274,7 @@ public actor Client {
private func addPendingRequest<T: Sendable & Decodable>( private func addPendingRequest<T: Sendable & Decodable>(
id: ID, id: ID,
continuation: CheckedContinuation<T, Swift.Error>, continuation: CheckedContinuation<T, Error>,
type: T.Type type: T.Type
) { ) {
pendingRequests[id] = AnyPendingRequest(PendingRequest(continuation: continuation)) pendingRequests[id] = AnyPendingRequest(PendingRequest(continuation: continuation))
@ -439,10 +439,10 @@ public actor Client {
{ {
if configuration.strict { if configuration.strict {
guard let capabilities = serverCapabilities else { guard let capabilities = serverCapabilities else {
throw Error.methodNotFound("Server capabilities not initialized") throw MCPError.methodNotFound("Server capabilities not initialized")
} }
guard capabilities[keyPath: keyPath] != nil else { guard capabilities[keyPath: keyPath] != nil else {
throw Error.methodNotFound("\(name) is not supported by the server") throw MCPError.methodNotFound("\(name) is not supported by the server")
} }
} }
} }

View file

@ -195,9 +195,9 @@ public actor Server {
requestID = .number(intValue) requestID = .number(intValue)
} }
} }
throw Error.parseError("Invalid message format") throw MCPError.parseError("Invalid message format")
} }
} catch let error where Error.isResourceTemporarilyUnavailable(error) { } catch let error where MCPError.isResourceTemporarilyUnavailable(error) {
// Resource temporarily unavailable, retry after a short delay // Resource temporarily unavailable, retry after a short delay
try? await Task.sleep(for: .milliseconds(10)) try? await Task.sleep(for: .milliseconds(10))
continue continue
@ -206,8 +206,8 @@ public actor Server {
"Error processing message", metadata: ["error": "\(error)"]) "Error processing message", metadata: ["error": "\(error)"])
let response = AnyMethod.response( let response = AnyMethod.response(
id: requestID ?? .random, id: requestID ?? .random,
error: error as? Error error: error as? MCPError
?? Error.internalError(error.localizedDescription) ?? MCPError.internalError(error.localizedDescription)
) )
try? await send(response) try? await send(response)
} }
@ -265,7 +265,7 @@ public actor Server {
/// Send a response to a request /// Send a response to a request
public func send<M: Method>(_ response: Response<M>) async throws { public func send<M: Method>(_ response: Response<M>) async throws {
guard let connection = connection else { guard let connection = connection else {
throw Error.internalError("Server connection not initialized") throw MCPError.internalError("Server connection not initialized")
} }
let encoder = JSONEncoder() let encoder = JSONEncoder()
@ -278,7 +278,7 @@ public actor Server {
/// Send a notification to connected clients /// Send a notification to connected clients
public func notify<N: Notification>(_ notification: Message<N>) async throws { public func notify<N: Notification>(_ notification: Message<N>) async throws {
guard let connection = connection else { guard let connection = connection else {
throw Error.internalError("Server connection not initialized") throw MCPError.internalError("Server connection not initialized")
} }
let encoder = JSONEncoder() let encoder = JSONEncoder()
@ -311,7 +311,7 @@ public actor Server {
// Find handler for method name // Find handler for method name
guard let handler = methodHandlers[request.method] else { guard let handler = methodHandlers[request.method] else {
let error = Error.methodNotFound("Unknown method: \(request.method)") let error = MCPError.methodNotFound("Unknown method: \(request.method)")
let response = AnyMethod.response(id: request.id, error: error) let response = AnyMethod.response(id: request.id, error: error)
try await send(response) try await send(response)
throw error throw error
@ -322,7 +322,7 @@ public actor Server {
let response = try await handler(request) let response = try await handler(request)
try await send(response) try await send(response)
} catch { } catch {
let mcpError = error as? Error ?? Error.internalError(error.localizedDescription) let mcpError = error as? MCPError ?? MCPError.internalError(error.localizedDescription)
let response = AnyMethod.response(id: request.id, error: mcpError) let response = AnyMethod.response(id: request.id, error: mcpError)
try await send(response) try await send(response)
throw error throw error
@ -361,7 +361,7 @@ public actor Server {
private func checkInitialized() throws { private func checkInitialized() throws {
guard isInitialized else { guard isInitialized else {
throw Error.invalidRequest("Server is not initialized") throw MCPError.invalidRequest("Server is not initialized")
} }
} }
@ -371,16 +371,16 @@ public actor Server {
// Initialize // Initialize
withMethodHandler(Initialize.self) { [weak self] params in withMethodHandler(Initialize.self) { [weak self] params in
guard let self = self else { guard let self = self else {
throw Error.internalError("Server was deallocated") throw MCPError.internalError("Server was deallocated")
} }
guard await !self.isInitialized else { guard await !self.isInitialized else {
throw Error.invalidRequest("Server is already initialized") throw MCPError.invalidRequest("Server is already initialized")
} }
// Validate protocol version // Validate protocol version
guard Version.latest == params.protocolVersion else { guard Version.latest == params.protocolVersion else {
throw Error.invalidRequest( throw MCPError.invalidRequest(
"Unsupported protocol version: \(params.protocolVersion)") "Unsupported protocol version: \(params.protocolVersion)")
} }

View file

@ -90,8 +90,8 @@ struct ClientTests {
do { do {
try await client.connect(transport: transport) try await client.connect(transport: transport)
#expect(Bool(false), "Expected connection to fail") #expect(Bool(false), "Expected connection to fail")
} catch let error as Error { } catch let error as MCPError {
if case Error.transportError = error { if case MCPError.transportError = error {
#expect(Bool(true)) #expect(Bool(true))
} else { } else {
#expect(Bool(false), "Expected transport error") #expect(Bool(false), "Expected transport error")
@ -112,8 +112,8 @@ struct ClientTests {
do { do {
try await client.ping() try await client.ping()
#expect(Bool(false), "Expected ping to fail") #expect(Bool(false), "Expected ping to fail")
} catch let error as Error { } catch let error as MCPError {
if case Error.transportError = error { if case MCPError.transportError = error {
#expect(Bool(true)) #expect(Bool(true))
} else { } else {
#expect(Bool(false), "Expected transport error") #expect(Bool(false), "Expected transport error")
@ -132,12 +132,12 @@ struct ClientTests {
try await client.connect(transport: transport) try await client.connect(transport: transport)
// Create a task for listPrompts // Create a task for listPrompts
let promptsTask = Task<Void, Swift.Error> { let promptsTask = Task<Void, Error> {
do { do {
_ = try await client.listPrompts() _ = try await client.listPrompts()
#expect(Bool(false), "Expected listPrompts to fail in strict mode") #expect(Bool(false), "Expected listPrompts to fail in strict mode")
} catch let error as Error { } catch let error as MCPError {
if case Error.methodNotFound = error { if case MCPError.methodNotFound = error {
#expect(Bool(true)) #expect(Bool(true))
} else { } else {
#expect(Bool(false), "Expected methodNotFound error, got \(error)") #expect(Bool(false), "Expected methodNotFound error, got \(error)")
@ -185,7 +185,7 @@ struct ClientTests {
// Create an error response with the same ID // Create an error response with the same ID
let errorResponse = Response<ListPrompts>( let errorResponse = Response<ListPrompts>(
id: decodedRequest.id, id: decodedRequest.id,
error: Error.methodNotFound("Test: Prompts capability not available") error: MCPError.methodNotFound("Test: Prompts capability not available")
) )
try await transport.queue(response: errorResponse) try await transport.queue(response: errorResponse)
@ -193,8 +193,8 @@ struct ClientTests {
do { do {
_ = try await client.listPrompts() _ = try await client.listPrompts()
#expect(Bool(false), "Expected listPrompts to fail in non-strict mode") #expect(Bool(false), "Expected listPrompts to fail in non-strict mode")
} catch let error as Error { } catch let error as MCPError {
if case Error.methodNotFound = error { if case MCPError.methodNotFound = error {
#expect(Bool(true)) #expect(Bool(true))
} else { } else {
#expect(Bool(false), "Expected methodNotFound error, got \(error)") #expect(Bool(false), "Expected methodNotFound error, got \(error)")

View file

@ -1,4 +1,8 @@
import Foundation import class Foundation.JSONEncoder
import class Foundation.JSONDecoder
import struct Foundation.Data
import struct Foundation.POSIXError
import Logging import Logging
@testable import MCP @testable import MCP
@ -26,7 +30,7 @@ actor MockTransport: Transport {
private var dataToReceive: [Data] = [] private var dataToReceive: [Data] = []
private(set) var receivedMessages: [String] = [] private(set) var receivedMessages: [String] = []
private var dataStreamContinuation: AsyncThrowingStream<Data, Swift.Error>.Continuation? private var dataStreamContinuation: AsyncThrowingStream<Data, Error>.Continuation?
var shouldFailConnect = false var shouldFailConnect = false
var shouldFailSend = false var shouldFailSend = false
@ -37,7 +41,7 @@ actor MockTransport: Transport {
public func connect() async throws { public func connect() async throws {
if shouldFailConnect { if shouldFailConnect {
throw Error.transportError(POSIXError(.ECONNREFUSED)) throw MCPError.transportError(POSIXError(.ECONNREFUSED))
} }
isConnected = true isConnected = true
} }
@ -50,13 +54,13 @@ actor MockTransport: Transport {
public func send(_ message: Data) async throws { public func send(_ message: Data) async throws {
if shouldFailSend { if shouldFailSend {
throw Error.transportError(POSIXError(.EIO)) throw MCPError.transportError(POSIXError(.EIO))
} }
sentData.append(message) sentData.append(message)
} }
public func receive() -> AsyncThrowingStream<Data, Swift.Error> { public func receive() -> AsyncThrowingStream<Data, Error> {
return AsyncThrowingStream<Data, Swift.Error> { continuation in return AsyncThrowingStream<Data, Error> { continuation in
dataStreamContinuation = continuation dataStreamContinuation = continuation
for message in dataToReceive { for message in dataToReceive {
continuation.yield(message) continuation.yield(message)
@ -76,7 +80,7 @@ actor MockTransport: Transport {
shouldFailSend = shouldFail shouldFailSend = shouldFail
} }
func queue<M: MCP.Method>(request: Request<M>) throws { func queue<M: Method>(request: Request<M>) throws {
let data = try encoder.encode(request) let data = try encoder.encode(request)
if let continuation = dataStreamContinuation { if let continuation = dataStreamContinuation {
continuation.yield(data) continuation.yield(data)
@ -85,12 +89,12 @@ actor MockTransport: Transport {
} }
} }
func queue<M: MCP.Method>(response: Response<M>) throws { func queue<M: Method>(response: Response<M>) throws {
let data = try encoder.encode(response) let data = try encoder.encode(response)
dataToReceive.append(data) dataToReceive.append(data)
} }
func queue<N: MCP.Notification>(notification: Message<N>) throws { func queue<N: Notification>(notification: Message<N>) throws {
let data = try encoder.encode(notification) let data = try encoder.encode(notification)
dataToReceive.append(data) dataToReceive.append(data)
} }

View file

@ -43,7 +43,7 @@ struct ResponseTests {
@Test("Error response initialization and encoding") @Test("Error response initialization and encoding")
func testErrorResponse() throws { func testErrorResponse() throws {
let id: ID = "test-id" let id: ID = "test-id"
let error = MCP.Error.parseError(nil) let error = MCPError.parseError(nil)
let response = Response<TestMethod>(id: id, error: error) let response = Response<TestMethod>(id: id, error: error)
let encoder = JSONEncoder() let encoder = JSONEncoder()
@ -65,7 +65,7 @@ struct ResponseTests {
@Test("Error response with detail") @Test("Error response with detail")
func testErrorResponseWithDetail() throws { func testErrorResponseWithDetail() throws {
let id: ID = "test-id" let id: ID = "test-id"
let error = MCP.Error.parseError("Invalid syntax") let error = MCPError.parseError("Invalid syntax")
let response = Response<TestMethod>(id: id, error: error) let response = Response<TestMethod>(id: id, error: error)
let encoder = JSONEncoder() let encoder = JSONEncoder()

View file

@ -109,7 +109,7 @@ struct ServerTests {
try await server.start(transport: transport) { clientInfo, _ in try await server.start(transport: transport) { clientInfo, _ in
if clientInfo.name == "BlockedClient" { if clientInfo.name == "BlockedClient" {
throw Error.invalidRequest("Client not allowed") throw MCPError.invalidRequest("Client not allowed")
} }
} }

View file

@ -57,7 +57,7 @@ struct StdioTransportTests {
try writer.close() try writer.close()
// Start receiving messages // Start receiving messages
let stream: AsyncThrowingStream<Data, Swift.Error> = await transport.receive() let stream: AsyncThrowingStream<Data, Error> = await transport.receive()
var iterator = stream.makeAsyncIterator() var iterator = stream.makeAsyncIterator()
// Get first message // Get first message
@ -79,7 +79,7 @@ struct StdioTransportTests {
try writer.writeAll(invalidJSON.data(using: .utf8)!) try writer.writeAll(invalidJSON.data(using: .utf8)!)
try writer.close() try writer.close()
let stream: AsyncThrowingStream<Data, Swift.Error> = await transport.receive() let stream: AsyncThrowingStream<Data, Error> = await transport.receive()
var iterator = stream.makeAsyncIterator() var iterator = stream.makeAsyncIterator()
_ = try await iterator.next() _ = try await iterator.next()
@ -100,7 +100,7 @@ struct StdioTransportTests {
try await transport.connect() try await transport.connect()
#expect(Bool(false), "Expected connect to throw an error") #expect(Bool(false), "Expected connect to throw an error")
} catch { } catch {
#expect(error is MCP.Error) #expect(error is MCPError)
} }
await transport.disconnect() await transport.disconnect()
@ -119,7 +119,7 @@ struct StdioTransportTests {
try await transport.connect() try await transport.connect()
#expect(Bool(false), "Expected connect to throw an error") #expect(Bool(false), "Expected connect to throw an error")
} catch { } catch {
#expect(error is MCP.Error) #expect(error is MCPError)
} }
await transport.disconnect() await transport.disconnect()