@@ -19,11 +19,11 @@ public protocol Transport: Actor {
19
19
/// Disconnects from the transport
20
20
func disconnect( ) async
21
21
22
- /// Sends a message string
23
- func send( _ message : String ) async throws
22
+ /// Sends data
23
+ func send( _ data : Data ) async throws
24
24
25
- /// Receives message strings as an async sequence
26
- func receive( ) -> AsyncThrowingStream < String , Swift . Error >
25
+ /// Receives data in an async sequence
26
+ func receive( ) -> AsyncThrowingStream < Data , Swift . Error >
27
27
}
28
28
29
29
/// Standard input/output transport implementation
@@ -33,8 +33,8 @@ public actor StdioTransport: Transport {
33
33
public nonisolated let logger : Logger
34
34
35
35
private var isConnected = false
36
- private let messageStream : AsyncStream < String >
37
- private let messageContinuation : AsyncStream < String > . Continuation
36
+ private let messageStream : AsyncStream < Data >
37
+ private let messageContinuation : AsyncStream < Data > . Continuation
38
38
39
39
public init (
40
40
input: FileDescriptor = FileDescriptor . standardInput,
@@ -50,7 +50,7 @@ public actor StdioTransport: Transport {
50
50
factory: { _ in SwiftLogNoOpLogHandler ( ) } )
51
51
52
52
// Create message stream
53
- var continuation : AsyncStream < String > . Continuation !
53
+ var continuation : AsyncStream < Data > . Continuation !
54
54
self . messageStream = AsyncStream { continuation = $0 }
55
55
self . messageContinuation = continuation
56
56
}
@@ -105,15 +105,13 @@ public actor StdioTransport: Transport {
105
105
let messageData = pendingData [ ..< newlineIndex]
106
106
pendingData = pendingData [ ( newlineIndex + 1 ) ... ]
107
107
108
- if let message = String ( data: messageData, encoding: . utf8) ,
109
- !message. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty
110
- {
111
- logger. debug ( " Message received " , metadata: [ " message " : " \( message) " ] )
112
- messageContinuation. yield ( message)
108
+ if !messageData. isEmpty {
109
+ logger. debug ( " Message received " , metadata: [ " size " : " \( messageData. count) " ] )
110
+ messageContinuation. yield ( Data ( messageData) )
113
111
}
114
112
}
115
113
} catch let error where Error . isResourceTemporarilyUnavailable ( error) {
116
- try ? await Task . sleep ( nanoseconds : 10_000_000 ) // 10ms backoff
114
+ try ? await Task . sleep ( for : . milliseconds ( 10 ) )
117
115
continue
118
116
} catch {
119
117
if !Task. isCancelled {
@@ -133,17 +131,16 @@ public actor StdioTransport: Transport {
133
131
logger. info ( " Transport disconnected " )
134
132
}
135
133
136
- public func send( _ message: String ) async throws {
134
+ public func send( _ message: Data ) async throws {
137
135
guard isConnected else {
138
136
throw Error . transportError ( Errno . socketNotConnected)
139
137
}
140
138
141
- let message = message + " \n "
142
- guard let data = message. data ( using: . utf8) else {
143
- throw Error . transportError ( Errno . invalidArgument)
144
- }
139
+ // Add newline as delimiter
140
+ var messageWithNewline = message
141
+ messageWithNewline. append ( UInt8 ( ascii: " \n " ) )
145
142
146
- var remaining = data
143
+ var remaining = messageWithNewline
147
144
while !remaining. isEmpty {
148
145
do {
149
146
let written = try remaining. withUnsafeBytes { buffer in
@@ -153,15 +150,15 @@ public actor StdioTransport: Transport {
153
150
remaining = remaining. dropFirst ( written)
154
151
}
155
152
} catch let error where Error . isResourceTemporarilyUnavailable ( error) {
156
- try await Task . sleep ( nanoseconds : 10_000_000 ) // 10ms backoff
153
+ try await Task . sleep ( for : . milliseconds ( 10 ) )
157
154
continue
158
155
} catch {
159
156
throw Error . transportError ( error)
160
157
}
161
158
}
162
159
}
163
160
164
- public func receive( ) -> AsyncThrowingStream < String , Swift . Error > {
161
+ public func receive( ) -> AsyncThrowingStream < Data , Swift . Error > {
165
162
return AsyncThrowingStream { continuation in
166
163
Task {
167
164
for await message in messageStream {
@@ -182,8 +179,8 @@ public actor StdioTransport: Transport {
182
179
public nonisolated let logger : Logger
183
180
184
181
private var isConnected = false
185
- private let messageStream : AsyncThrowingStream < String , Swift . Error >
186
- private let messageContinuation : AsyncThrowingStream < String , Swift . Error > . Continuation
182
+ private let messageStream : AsyncThrowingStream < Data , Swift . Error >
183
+ private let messageContinuation : AsyncThrowingStream < Data , Swift . Error > . Continuation
187
184
188
185
// Track connection state for continuations
189
186
private var connectionContinuationResumed = false
@@ -198,7 +195,7 @@ public actor StdioTransport: Transport {
198
195
)
199
196
200
197
// Create message stream
201
- var continuation : AsyncThrowingStream < String , Swift . Error > . Continuation !
198
+ var continuation : AsyncThrowingStream < Data , Swift . Error > . Continuation !
202
199
self . messageStream = AsyncThrowingStream { continuation = $0 }
203
200
self . messageContinuation = continuation
204
201
}
@@ -289,14 +286,14 @@ public actor StdioTransport: Transport {
289
286
logger. info ( " Network transport disconnected " )
290
287
}
291
288
292
- public func send( _ message: String ) async throws {
289
+ public func send( _ message: Data ) async throws {
293
290
guard isConnected else {
294
291
throw MCP . Error. internalError ( " Transport not connected " )
295
292
}
296
293
297
- guard let data = ( message + " \n " ) . data ( using : . utf8 ) else {
298
- throw MCP . Error . internalError ( " Failed to encode message" )
299
- }
294
+ // Add newline as delimiter
295
+ var messageWithNewline = message
296
+ messageWithNewline . append ( UInt8 ( ascii : " \n " ) )
300
297
301
298
// Use a local actor-isolated variable to track continuation state
302
299
var sendContinuationResumed = false
@@ -309,7 +306,7 @@ public actor StdioTransport: Transport {
309
306
}
310
307
311
308
connection. send (
312
- content: data ,
309
+ content: messageWithNewline ,
313
310
completion: . contentProcessed { [ weak self] error in
314
311
guard let self = self else { return }
315
312
@@ -329,7 +326,7 @@ public actor StdioTransport: Transport {
329
326
}
330
327
}
331
328
332
- public func receive( ) -> AsyncThrowingStream < String , Swift . Error > {
329
+ public func receive( ) -> AsyncThrowingStream < Data , Swift . Error > {
333
330
return AsyncThrowingStream { continuation in
334
331
Task {
335
332
do {
@@ -357,11 +354,10 @@ public actor StdioTransport: Transport {
357
354
let messageData = buffer [ ..< newlineIndex]
358
355
buffer = buffer [ ( newlineIndex + 1 ) ... ]
359
356
360
- if let message = String ( data: messageData, encoding: . utf8) ,
361
- !message. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty
362
- {
363
- logger. debug ( " Message received " , metadata: [ " message " : " \( message) " ] )
364
- messageContinuation. yield ( message)
357
+ if !messageData. isEmpty {
358
+ logger. debug (
359
+ " Message received " , metadata: [ " size " : " \( messageData. count) " ] )
360
+ messageContinuation. yield ( Data ( messageData) )
365
361
}
366
362
}
367
363
} catch let error as NWError {
0 commit comments