@@ -459,7 +459,14 @@ import Logging
459459 content: Heartbeat ( ) . data,
460460 contentContext: . defaultMessage,
461461 isComplete: true ,
462- completion: . contentProcessed { [ weak self] error in
462+ completion: . contentProcessed { [ weak self, continuation] error in
463+ guard let self = self else {
464+ continuation. resume (
465+ throwing: MCPError . internalError (
466+ " Transport deallocated during heartbeat " ) )
467+ return
468+ }
469+
463470 if let error = error {
464471 continuation. resume ( throwing: error)
465472 } else {
@@ -525,6 +532,86 @@ import Logging
525532 completion: . contentProcessed { [ weak self] error in
526533 guard let self = self else { return }
527534
535+ if let error = error {
536+ self . logger. error ( " Send error: \( error) " )
537+
538+ // Schedule reconnection check on a separate task
539+ Task { [ weak self] in
540+ guard let self = self else { return }
541+ let isStopping = await self . isStopping
542+ if !isStopping && self . reconnectionConfig. enabled {
543+ let isConnected = await self . isConnected
544+ if isConnected && error. isConnectionLost {
545+ self . logger. warning (
546+ " Connection appears broken, will attempt to reconnect... "
547+ )
548+ await self . setIsConnected ( false )
549+ try ? await Task . sleep ( for: . milliseconds( 500 ) )
550+
551+ let currentIsStopping = await self . isStopping
552+ if !currentIsStopping {
553+ self . connection. cancel ( )
554+ try ? await self . connect ( )
555+ Task { @MainActor in
556+ if !sendContinuationResumed {
557+ sendContinuationResumed = true
558+ if let error = error {
559+ self . logger. error ( " Send error: \( error) " )
560+
561+ // Check if we should attempt to reconnect on send failure
562+ let isStopping = await self . isStopping // Await actor-isolated property
563+ if !isStopping && self . reconnectionConfig. enabled {
564+ let isConnected = await self . isConnected
565+ if isConnected {
566+ if error. isConnectionLost {
567+ self . logger. warning (
568+ " Connection appears broken, will attempt to reconnect... "
569+ )
570+
571+ // Schedule connection restart
572+ Task { [ weak self] in // Operate on self's executor
573+ guard let self = self else { return }
574+
575+ await self . setIsConnected ( false )
576+
577+ try ? await Task . sleep ( for: . milliseconds( 500 ) )
578+
579+ let currentIsStopping = await self . isStopping
580+ if !currentIsStopping {
581+ // Cancel the connection, then attempt to reconnect fully.
582+ self . connection. cancel ( )
583+ try ? await self . connect ( )
584+ }
585+ }
586+ }
587+ if let error = error {
588+ self . logger. error ( " Send error: \( error) " )
589+
590+ // Schedule reconnection check on a separate task
591+ Task { [ weak self] in
592+ guard let self = self else { return }
593+ let isStopping = await self . isStopping
594+ if !isStopping && self . reconnectionConfig. enabled {
595+ let isConnected = await self . isConnected
596+ if isConnected && error. isConnectionLost {
597+ self . logger. warning (
598+ " Connection appears broken, will attempt to reconnect... "
599+ )
600+ await self . setIsConnected ( false )
601+ try ? await Task . sleep ( for: . milliseconds( 500 ) )
602+
603+ let currentIsStopping = await self . isStopping
604+ if !currentIsStopping {
605+ self . connection. cancel ( )
606+ try ? await self . connect ( )
607+ completion: . contentProcessed { [ weak self, continuation] error in
608+ guard let self = self else {
609+ continuation. resume (
610+ throwing: MCPError . internalError (
611+ " Transport deallocated during send " ) )
612+ return
613+ }
614+
528615 if let error = error {
529616 self . logger. error ( " Send error: \( error) " )
530617
@@ -751,6 +838,40 @@ import Logging
751838 continuation. resume ( throwing: MCPError . connectionClosed)
752839 } else {
753840 continuation. resume ( returning: Data ( ) )
841+ Task { @MainActor in
842+ if !receiveContinuationResumed {
843+ receiveContinuationResumed = true
844+ if let error = error {
845+ continuation. resume ( throwing: MCPError . transportError ( error) )
846+ } else if let content = content {
847+ continuation. resume ( returning: content)
848+ } else if isComplete {
849+ self . logger. trace ( " Connection completed by peer " )
850+ continuation. resume ( throwing: MCPError . connectionClosed)
851+ } else {
852+ // EOF: Resume with empty data instead of throwing an error
853+ continuation. resume ( returning: Data ( ) )
854+ }
855+ }
856+ if let error = error {
857+ continuation. resume ( throwing: MCPError . transportError ( error) )
858+ } else if let content = content {
859+ continuation. resume ( returning: content)
860+ } else if isComplete {
861+ self . logger. trace ( " Connection completed by peer " )
862+ continuation. resume ( throwing: MCPError . connectionClosed)
863+ } else {
864+ continuation. resume ( returning: Data ( ) )
865+ [ weak self, continuation] content, _, isComplete , error in
866+ if let error = error {
867+ continuation. resume ( throwing: MCPError . transportError ( error) )
868+ } else if let content = content {
869+ continuation. resume ( returning: content)
870+ } else if isComplete {
871+ self ? . logger. trace ( " Connection completed by peer " )
872+ continuation. resume ( throwing: MCPError . connectionClosed)
873+ } else {
874+ continuation. resume ( returning: Data ( ) )
754875 }
755876 }
756877 }
0 commit comments