Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions Sources/MCP/Base/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,24 @@ import Foundation
@preconcurrency import SystemPackage
#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.
public enum Error: Sendable {
public enum MCPError: Error, Sendable {
// Standard JSON-RPC 2.0 errors (-32700 to -32603)
case parseError(String?) // -32700
case invalidRequest(String?) // -32600
Expand All @@ -20,7 +36,7 @@ public enum Error: Sendable {

// Transport specific errors
case connectionClosed
case transportError(Swift.Error)
case transportError(Error)

/// The JSON-RPC 2.0 error code
public var code: Int {
Expand All @@ -37,7 +53,7 @@ public enum Error: Sendable {
}

/// 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 let errno = error as? System.Errno, errno == .resourceTemporarilyUnavailable {
return true
Expand All @@ -53,7 +69,7 @@ public enum Error: Sendable {

// MARK: LocalizedError

extension Error: LocalizedError {
extension MCPError: LocalizedError {
public var errorDescription: String? {
switch self {
case .parseError(let detail):
Expand Down Expand Up @@ -116,7 +132,7 @@ extension Error: LocalizedError {

// MARK: CustomDebugStringConvertible

extension Error: CustomDebugStringConvertible {
extension MCPError: CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .transportError(let error):
Expand All @@ -131,7 +147,7 @@ extension Error: CustomDebugStringConvertible {

// MARK: Codable

extension Error: Codable {
extension MCPError: Codable {
private enum CodingKeys: String, CodingKey {
case code, message, data
}
Expand Down Expand Up @@ -199,15 +215,15 @@ extension Error: Codable {

// MARK: Equatable

extension Error: Equatable {
public static func == (lhs: Error, rhs: Error) -> Bool {
extension MCPError: Equatable {
public static func == (lhs: MCPError, rhs: MCPError) -> Bool {
lhs.code == rhs.code
}
}

// MARK: Hashable

extension Error: Hashable {
extension MCPError: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(code)
switch self {
Expand Down
8 changes: 4 additions & 4 deletions Sources/MCP/Base/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ extension Method {
}

/// 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)
}
}
Expand Down Expand Up @@ -186,14 +186,14 @@ public struct Response<M: Method>: Hashable, Identifiable, Codable, Sendable {
/// The response ID.
public let id: ID
/// 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) {
self.id = id
self.result = .success(result)
}

public init(id: ID, error: Error) {
public init(id: ID, error: MCPError) {
self.id = id
self.result = .failure(error)
}
Expand Down Expand Up @@ -224,7 +224,7 @@ public struct Response<M: Method>: Hashable, Identifiable, Codable, Sendable {
id = try container.decode(ID.self, forKey: .id)
if let result = try? container.decode(M.Result.self, forKey: .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)
} else {
throw DecodingError.dataCorrupted(
Expand Down
54 changes: 27 additions & 27 deletions Sources/MCP/Base/Transports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public protocol Transport: Actor {
func send(_ data: Data) async throws

/// Receives data in an async sequence
func receive() -> AsyncThrowingStream<Data, Swift.Error>
func receive() -> AsyncThrowingStream<Data, Error>
}

/// Standard input/output transport implementation
Expand Down Expand Up @@ -74,11 +74,11 @@ public actor StdioTransport: Transport {
private func setNonBlocking(fileDescriptor: FileDescriptor) throws {
let flags = fcntl(fileDescriptor.rawValue, F_GETFL)
guard flags >= 0 else {
throw Error.transportError(Errno.badFileDescriptor)
throw MCPError.transportError(Errno.badFileDescriptor)
}
let result = fcntl(fileDescriptor.rawValue, F_SETFL, flags | O_NONBLOCK)
guard result >= 0 else {
throw Error.transportError(Errno.badFileDescriptor)
throw MCPError.transportError(Errno.badFileDescriptor)
}
}

Expand Down Expand Up @@ -110,7 +110,7 @@ public actor StdioTransport: Transport {
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))
continue
} catch {
Expand All @@ -133,7 +133,7 @@ public actor StdioTransport: Transport {

public func send(_ message: Data) async throws {
guard isConnected else {
throw Error.transportError(Errno.socketNotConnected)
throw MCPError.transportError(Errno.socketNotConnected)
}

// Add newline as delimiter
Expand All @@ -149,16 +149,16 @@ public actor StdioTransport: Transport {
if written > 0 {
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))
continue
} 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
Task {
for await message in messageStream {
Expand All @@ -179,8 +179,8 @@ public actor StdioTransport: Transport {
public nonisolated let logger: Logger

private var isConnected = false
private let messageStream: AsyncThrowingStream<Data, Swift.Error>
private let messageContinuation: AsyncThrowingStream<Data, Swift.Error>.Continuation
private let messageStream: AsyncThrowingStream<Data, Error>
private let messageContinuation: AsyncThrowingStream<Data, Error>.Continuation

// Track connection state for continuations
private var connectionContinuationResumed = false
Expand All @@ -195,7 +195,7 @@ public actor StdioTransport: Transport {
)

// Create message stream
var continuation: AsyncThrowingStream<Data, Swift.Error>.Continuation!
var continuation: AsyncThrowingStream<Data, Error>.Continuation!
self.messageStream = AsyncThrowingStream { continuation = $0 }
self.messageContinuation = continuation
}
Expand All @@ -209,9 +209,9 @@ public actor StdioTransport: Transport {

// Wait for connection to be ready
try await withCheckedThrowingContinuation {
[weak self] (continuation: CheckedContinuation<Void, Swift.Error>) in
[weak self] (continuation: CheckedContinuation<Void, Error>) in
guard let self = self else {
continuation.resume(throwing: MCP.Error.internalError("Transport deallocated"))
continuation.resume(throwing: MCPError.internalError("Transport deallocated"))
return
}

Expand Down Expand Up @@ -245,7 +245,7 @@ public actor StdioTransport: Transport {
}
}

private func handleConnectionReady(continuation: CheckedContinuation<Void, Swift.Error>)
private func handleConnectionReady(continuation: CheckedContinuation<Void, Error>)
async
{
if !connectionContinuationResumed {
Expand All @@ -259,7 +259,7 @@ public actor StdioTransport: Transport {
}

private func handleConnectionFailed(
error: Swift.Error, continuation: CheckedContinuation<Void, Swift.Error>
error: Error, continuation: CheckedContinuation<Void, Error>
) async {
if !connectionContinuationResumed {
connectionContinuationResumed = true
Expand All @@ -268,13 +268,13 @@ public actor StdioTransport: Transport {
}
}

private func handleConnectionCancelled(continuation: CheckedContinuation<Void, Swift.Error>)
private func handleConnectionCancelled(continuation: CheckedContinuation<Void, Error>)
async
{
if !connectionContinuationResumed {
connectionContinuationResumed = true
logger.warning("Connection cancelled")
continuation.resume(throwing: MCP.Error.internalError("Connection cancelled"))
continuation.resume(throwing: MCPError.internalError("Connection cancelled"))
}
}

Expand All @@ -288,7 +288,7 @@ public actor StdioTransport: Transport {

public func send(_ message: Data) async throws {
guard isConnected else {
throw MCP.Error.internalError("Transport not connected")
throw MCPError.internalError("Transport not connected")
}

// Add newline as delimiter
Expand All @@ -299,9 +299,9 @@ public actor StdioTransport: Transport {
var sendContinuationResumed = false

try await withCheckedThrowingContinuation {
[weak self] (continuation: CheckedContinuation<Void, Swift.Error>) in
[weak self] (continuation: CheckedContinuation<Void, Error>) in
guard let self = self else {
continuation.resume(throwing: MCP.Error.internalError("Transport deallocated"))
continuation.resume(throwing: MCPError.internalError("Transport deallocated"))
return
}

Expand All @@ -316,7 +316,7 @@ public actor StdioTransport: Transport {
if let error = error {
self.logger.error("Send error: \(error)")
continuation.resume(
throwing: MCP.Error.internalError("Send error: \(error)"))
throwing: MCPError.internalError("Send error: \(error)"))
} else {
continuation.resume()
}
Expand All @@ -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
Task {
do {
Expand Down Expand Up @@ -363,7 +363,7 @@ public actor StdioTransport: Transport {
} catch let error as NWError {
if !Task.isCancelled {
logger.error("Network error occurred", metadata: ["error": "\(error)"])
messageContinuation.finish(throwing: MCP.Error.transportError(error))
messageContinuation.finish(throwing: MCPError.transportError(error))
}
break
} catch {
Expand All @@ -382,9 +382,9 @@ public actor StdioTransport: Transport {
var receiveContinuationResumed = false

return try await withCheckedThrowingContinuation {
[weak self] (continuation: CheckedContinuation<Data, Swift.Error>) in
[weak self] (continuation: CheckedContinuation<Data, Error>) in
guard let self = self else {
continuation.resume(throwing: MCP.Error.internalError("Transport deallocated"))
continuation.resume(throwing: MCPError.internalError("Transport deallocated"))
return
}

Expand All @@ -394,12 +394,12 @@ public actor StdioTransport: Transport {
if !receiveContinuationResumed {
receiveContinuationResumed = true
if let error = error {
continuation.resume(throwing: MCP.Error.transportError(error))
continuation.resume(throwing: MCPError.transportError(error))
} else if let content = content {
continuation.resume(returning: content)
} else {
continuation.resume(
throwing: MCP.Error.internalError("No data received"))
throwing: MCPError.internalError("No data received"))
}
}
}
Expand Down
Loading