@@ -9,164 +9,164 @@ import struct Foundation.Data
99#endif
1010
1111// Import for specific low-level operations not yet in Swift System
12- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS )
12+ #if canImport(Darwin )
1313 import Darwin. POSIX
14- #elseif os(Linux )
14+ #elseif canImport(Glibc )
1515 import Glibc
1616#endif
1717
18- /// Standard input/output transport implementation
19- public actor StdioTransport : Transport {
20- private let input : FileDescriptor
21- private let output : FileDescriptor
22- public nonisolated let logger : Logger
23-
24- private var isConnected = false
25- private let messageStream : AsyncStream < Data >
26- private let messageContinuation : AsyncStream < Data > . Continuation
27-
28- public init (
29- input: FileDescriptor = FileDescriptor . standardInput,
30- output: FileDescriptor = FileDescriptor . standardOutput,
31- logger: Logger ? = nil
32- ) {
33- self . input = input
34- self . output = output
35- self . logger =
36- logger
37- ?? Logger (
38- label: " mcp.transport.stdio " ,
39- factory: { _ in SwiftLogNoOpLogHandler ( ) } )
40-
41- // Create message stream
42- var continuation : AsyncStream < Data > . Continuation !
43- self . messageStream = AsyncStream { continuation = $0 }
44- self . messageContinuation = continuation
45- }
18+ #if canImport(Darwin) || canImport(Glibc)
19+ /// Standard input/output transport implementation
20+ public actor StdioTransport : Transport {
21+ private let input : FileDescriptor
22+ private let output : FileDescriptor
23+ public nonisolated let logger : Logger
24+
25+ private var isConnected = false
26+ private let messageStream : AsyncStream < Data >
27+ private let messageContinuation : AsyncStream < Data > . Continuation
28+
29+ public init (
30+ input: FileDescriptor = FileDescriptor . standardInput,
31+ output: FileDescriptor = FileDescriptor . standardOutput,
32+ logger: Logger ? = nil
33+ ) {
34+ self . input = input
35+ self . output = output
36+ self . logger =
37+ logger
38+ ?? Logger (
39+ label: " mcp.transport.stdio " ,
40+ factory: { _ in SwiftLogNoOpLogHandler ( ) } )
41+
42+ // Create message stream
43+ var continuation : AsyncStream < Data > . Continuation !
44+ self . messageStream = AsyncStream { continuation = $0 }
45+ self . messageContinuation = continuation
46+ }
4647
47- public func connect( ) async throws {
48- guard !isConnected else { return }
48+ public func connect( ) async throws {
49+ guard !isConnected else { return }
4950
50- // Set non-blocking mode
51- try setNonBlocking ( fileDescriptor: input)
52- try setNonBlocking ( fileDescriptor: output)
51+ // Set non-blocking mode
52+ try setNonBlocking ( fileDescriptor: input)
53+ try setNonBlocking ( fileDescriptor: output)
5354
54- isConnected = true
55- logger. info ( " Transport connected successfully " )
55+ isConnected = true
56+ logger. info ( " Transport connected successfully " )
5657
57- // Start reading loop in background
58- Task {
59- await readLoop ( )
58+ // Start reading loop in background
59+ Task {
60+ await readLoop ( )
61+ }
6062 }
61- }
6263
63- private func setNonBlocking( fileDescriptor: FileDescriptor ) throws {
64- #if os(macOS) || os(iOS) || os(tvOS ) || os(watchOS) || os(Linux )
65- // Get current flags
66- let flags = fcntl ( fileDescriptor. rawValue, F_GETFL)
67- guard flags >= 0 else {
68- throw MCPError . transportError ( Errno ( rawValue: CInt ( errno) ) )
69- }
64+ private func setNonBlocking( fileDescriptor: FileDescriptor ) throws {
65+ #if canImport(Darwin ) || canImport(Glibc )
66+ // Get current flags
67+ let flags = fcntl ( fileDescriptor. rawValue, F_GETFL)
68+ guard flags >= 0 else {
69+ throw MCPError . transportError ( Errno ( rawValue: CInt ( errno) ) )
70+ }
7071
71- // Set non-blocking flag
72- let result = fcntl ( fileDescriptor. rawValue, F_SETFL, flags | O_NONBLOCK)
73- guard result >= 0 else {
74- throw MCPError . transportError ( Errno ( rawValue: CInt ( errno) ) )
75- }
76- #else
77- // For platforms where non-blocking operations aren't supported
78- throw MCPError . internalError ( " Setting non-blocking mode not supported on this platform " )
79- #endif
80- }
72+ // Set non-blocking flag
73+ let result = fcntl ( fileDescriptor. rawValue, F_SETFL, flags | O_NONBLOCK)
74+ guard result >= 0 else {
75+ throw MCPError . transportError ( Errno ( rawValue: CInt ( errno) ) )
76+ }
77+ #else
78+ // For platforms where non-blocking operations aren't supported
79+ throw MCPError . internalError (
80+ " Setting non-blocking mode not supported on this platform " )
81+ #endif
82+ }
8183
82- private func readLoop( ) async {
83- let bufferSize = 4096
84- var buffer = [ UInt8] ( repeating: 0 , count: bufferSize)
85- var pendingData = Data ( )
84+ private func readLoop( ) async {
85+ let bufferSize = 4096
86+ var buffer = [ UInt8] ( repeating: 0 , count: bufferSize)
87+ var pendingData = Data ( )
8688
87- while isConnected && !Task. isCancelled {
88- do {
89- let bytesRead = try buffer. withUnsafeMutableBufferPointer { pointer in
90- try input. read ( into: UnsafeMutableRawBufferPointer ( pointer) )
91- }
89+ while isConnected && !Task. isCancelled {
90+ do {
91+ let bytesRead = try buffer. withUnsafeMutableBufferPointer { pointer in
92+ try input. read ( into: UnsafeMutableRawBufferPointer ( pointer) )
93+ }
9294
93- if bytesRead == 0 {
94- logger. notice ( " EOF received " )
95- break
96- }
95+ if bytesRead == 0 {
96+ logger. notice ( " EOF received " )
97+ break
98+ }
9799
98- pendingData. append ( Data ( buffer [ ..< bytesRead] ) )
100+ pendingData. append ( Data ( buffer [ ..< bytesRead] ) )
99101
100- // Process complete messages
101- while let newlineIndex = pendingData. firstIndex ( of: UInt8 ( ascii: " \n " ) ) {
102- let messageData = pendingData [ ..< newlineIndex]
103- pendingData = pendingData [ ( newlineIndex + 1 ) ... ]
102+ // Process complete messages
103+ while let newlineIndex = pendingData. firstIndex ( of: UInt8 ( ascii: " \n " ) ) {
104+ let messageData = pendingData [ ..< newlineIndex]
105+ pendingData = pendingData [ ( newlineIndex + 1 ) ... ]
104106
105- if !messageData. isEmpty {
106- logger. debug ( " Message received " , metadata: [ " size " : " \( messageData. count) " ] )
107- messageContinuation. yield ( Data ( messageData) )
107+ if !messageData. isEmpty {
108+ logger. debug (
109+ " Message received " , metadata: [ " size " : " \( messageData. count) " ] )
110+ messageContinuation. yield ( Data ( messageData) )
111+ }
108112 }
113+ } catch let error where MCPError . isResourceTemporarilyUnavailable ( error) {
114+ try ? await Task . sleep ( for: . milliseconds( 10 ) )
115+ continue
116+ } catch {
117+ if !Task. isCancelled {
118+ logger. error ( " Read error occurred " , metadata: [ " error " : " \( error) " ] )
119+ }
120+ break
109121 }
110- } catch let error where MCPError . isResourceTemporarilyUnavailable ( error) {
111- try ? await Task . sleep ( for: . milliseconds( 10 ) )
112- continue
113- } catch {
114- if !Task. isCancelled {
115- logger. error ( " Read error occurred " , metadata: [ " error " : " \( error) " ] )
116- }
117- break
118122 }
119- }
120123
121- messageContinuation. finish ( )
122- }
124+ messageContinuation. finish ( )
125+ }
123126
124- public func disconnect( ) async {
125- guard isConnected else { return }
126- isConnected = false
127- messageContinuation. finish ( )
128- logger. info ( " Transport disconnected " )
129- }
127+ public func disconnect( ) async {
128+ guard isConnected else { return }
129+ isConnected = false
130+ messageContinuation. finish ( )
131+ logger. info ( " Transport disconnected " )
132+ }
130133
131- public func send( _ message: Data ) async throws {
132- guard isConnected else {
133- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Linux)
134+ public func send( _ message: Data ) async throws {
135+ guard isConnected else {
134136 throw MCPError . transportError ( Errno ( rawValue: ENOTCONN) )
135- #else
136- throw MCPError . internalError ( " Transport not connected " )
137- #endif
138- }
137+ }
139138
140- // Add newline as delimiter
141- var messageWithNewline = message
142- messageWithNewline. append ( UInt8 ( ascii: " \n " ) )
139+ // Add newline as delimiter
140+ var messageWithNewline = message
141+ messageWithNewline. append ( UInt8 ( ascii: " \n " ) )
143142
144- var remaining = messageWithNewline
145- while !remaining. isEmpty {
146- do {
147- let written = try remaining. withUnsafeBytes { buffer in
148- try output. write ( UnsafeRawBufferPointer ( buffer) )
149- }
150- if written > 0 {
151- remaining = remaining. dropFirst ( written)
143+ var remaining = messageWithNewline
144+ while !remaining. isEmpty {
145+ do {
146+ let written = try remaining. withUnsafeBytes { buffer in
147+ try output. write ( UnsafeRawBufferPointer ( buffer) )
148+ }
149+ if written > 0 {
150+ remaining = remaining. dropFirst ( written)
151+ }
152+ } catch let error where MCPError . isResourceTemporarilyUnavailable ( error) {
153+ try await Task . sleep ( for: . milliseconds( 10 ) )
154+ continue
155+ } catch {
156+ throw MCPError . transportError ( error)
152157 }
153- } catch let error where MCPError . isResourceTemporarilyUnavailable ( error) {
154- try await Task . sleep ( for: . milliseconds( 10 ) )
155- continue
156- } catch {
157- throw MCPError . transportError ( error)
158158 }
159159 }
160- }
161160
162- public func receive( ) -> AsyncThrowingStream < Data , Swift . Error > {
163- return AsyncThrowingStream { continuation in
164- Task {
165- for await message in messageStream {
166- continuation. yield ( message)
161+ public func receive( ) -> AsyncThrowingStream < Data , Swift . Error > {
162+ return AsyncThrowingStream { continuation in
163+ Task {
164+ for await message in messageStream {
165+ continuation. yield ( message)
166+ }
167+ continuation. finish ( )
167168 }
168- continuation. finish ( )
169169 }
170170 }
171171 }
172- }
172+ #endif
0 commit comments