@@ -19,6 +19,7 @@ import { process } from '@modelcontextprotocol/server/_shims';
1919export class StdioServerTransport implements Transport {
2020 private _readBuffer : ReadBuffer = new ReadBuffer ( ) ;
2121 private _started = false ;
22+ private _closed = false ;
2223
2324 constructor (
2425 private _stdin : Readable = process . stdin ,
@@ -37,6 +38,12 @@ export class StdioServerTransport implements Transport {
3738 _onerror = ( error : Error ) => {
3839 this . onerror ?.( error ) ;
3940 } ;
41+ _onstdouterror = ( error : Error ) => {
42+ this . onerror ?.( error ) ;
43+ this . close ( ) . catch ( ( ) => {
44+ // Ignore errors during close — we're already in an error path
45+ } ) ;
46+ } ;
4047
4148 /**
4249 * Starts listening for messages on `stdin`.
@@ -51,6 +58,7 @@ export class StdioServerTransport implements Transport {
5158 this . _started = true ;
5259 this . _stdin . on ( 'data' , this . _ondata ) ;
5360 this . _stdin . on ( 'error' , this . _onerror ) ;
61+ this . _stdout . on ( 'error' , this . _onstdouterror ) ;
5462 }
5563
5664 private processReadBuffer ( ) {
@@ -69,9 +77,15 @@ export class StdioServerTransport implements Transport {
6977 }
7078
7179 async close ( ) : Promise < void > {
80+ if ( this . _closed ) {
81+ return ;
82+ }
83+ this . _closed = true ;
84+
7285 // Remove our event listeners first
7386 this . _stdin . off ( 'data' , this . _ondata ) ;
7487 this . _stdin . off ( 'error' , this . _onerror ) ;
88+ this . _stdout . off ( 'error' , this . _onstdouterror ) ;
7589
7690 // Check if we were the only data listener
7791 const remainingDataListeners = this . _stdin . listenerCount ( 'data' ) ;
@@ -87,12 +101,37 @@ export class StdioServerTransport implements Transport {
87101 }
88102
89103 send ( message : JSONRPCMessage ) : Promise < void > {
90- return new Promise ( resolve => {
104+ if ( this . _closed ) {
105+ return Promise . reject ( new Error ( 'StdioServerTransport is closed' ) ) ;
106+ }
107+ return new Promise ( ( resolve , reject ) => {
91108 const json = serializeMessage ( message ) ;
109+
110+ let settled = false ;
111+ const onError = ( error : Error ) => {
112+ if ( settled ) return ;
113+ settled = true ;
114+ this . _stdout . off ( 'error' , onError ) ;
115+ this . _stdout . off ( 'drain' , onDrain ) ;
116+ reject ( error ) ;
117+ } ;
118+ const onDrain = ( ) => {
119+ if ( settled ) return ;
120+ settled = true ;
121+ this . _stdout . off ( 'error' , onError ) ;
122+ this . _stdout . off ( 'drain' , onDrain ) ;
123+ resolve ( ) ;
124+ } ;
125+
126+ this . _stdout . once ( 'error' , onError ) ;
127+
92128 if ( this . _stdout . write ( json ) ) {
129+ if ( settled ) return ;
130+ settled = true ;
131+ this . _stdout . off ( 'error' , onError ) ;
93132 resolve ( ) ;
94- } else {
95- this . _stdout . once ( 'drain' , resolve ) ;
133+ } else if ( ! settled ) {
134+ this . _stdout . once ( 'drain' , onDrain ) ;
96135 }
97136 } ) ;
98137 }
0 commit comments