@@ -9,164 +9,164 @@ import struct Foundation.Data
9
9
#endif
10
10
11
11
// 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 )
13
13
import Darwin. POSIX
14
- #elseif os(Linux )
14
+ #elseif canImport(Glibc )
15
15
import Glibc
16
16
#endif
17
17
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
+ }
46
47
47
- public func connect( ) async throws {
48
- guard !isConnected else { return }
48
+ public func connect( ) async throws {
49
+ guard !isConnected else { return }
49
50
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)
53
54
54
- isConnected = true
55
- logger. info ( " Transport connected successfully " )
55
+ isConnected = true
56
+ logger. info ( " Transport connected successfully " )
56
57
57
- // Start reading loop in background
58
- Task {
59
- await readLoop ( )
58
+ // Start reading loop in background
59
+ Task {
60
+ await readLoop ( )
61
+ }
60
62
}
61
- }
62
63
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
+ }
70
71
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
+ }
81
83
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 ( )
86
88
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
+ }
92
94
93
- if bytesRead == 0 {
94
- logger. notice ( " EOF received " )
95
- break
96
- }
95
+ if bytesRead == 0 {
96
+ logger. notice ( " EOF received " )
97
+ break
98
+ }
97
99
98
- pendingData. append ( Data ( buffer [ ..< bytesRead] ) )
100
+ pendingData. append ( Data ( buffer [ ..< bytesRead] ) )
99
101
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 ) ... ]
104
106
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
+ }
108
112
}
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
109
121
}
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
118
122
}
119
- }
120
123
121
- messageContinuation. finish ( )
122
- }
124
+ messageContinuation. finish ( )
125
+ }
123
126
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
+ }
130
133
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 {
134
136
throw MCPError . transportError ( Errno ( rawValue: ENOTCONN) )
135
- #else
136
- throw MCPError . internalError ( " Transport not connected " )
137
- #endif
138
- }
137
+ }
139
138
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 " ) )
143
142
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)
152
157
}
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)
158
158
}
159
159
}
160
- }
161
160
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 ( )
167
168
}
168
- continuation. finish ( )
169
169
}
170
170
}
171
171
}
172
- }
172
+ #endif
0 commit comments