@@ -34,6 +34,8 @@ export class Harness {
3434 private tui ! : TuiApp ;
3535 private baseSystemPrompt = "" ;
3636 private lastMcpProgress = new Map < string , { progress ?: number ; total ?: number ; message ?: string } > ( ) ;
37+ private mcpEventUnsubscribe ?: ( ) => void ;
38+ private shuttingDown = false ;
3739
3840 constructor ( config : HarnessConfig ) {
3941 this . config = config ;
@@ -143,57 +145,60 @@ export class Harness {
143145
144146 await this . tui . start ( ) ;
145147
146- if ( this . config . mcp . length > 0 ) {
147- this . tui . addInfo ( `Connecting ${ this . config . mcp . length } MCP server(s)...` ) ;
148- this . mcpManager = new MCPManager ( this . config . mcp ) ;
149- this . tui . setMcpManager ( this . mcpManager ) ;
150- await this . mcpManager . initialize ( ) ;
151- this . mcpManager . onEvent ( ( event ) => this . handleMcpEvent ( event ) ) ;
152- await this . mcpManager . registerDrivers ( this . driverRegistry ) ;
148+ try {
149+ if ( this . config . mcp . length > 0 ) {
150+ this . tui . addInfo ( `Connecting ${ this . config . mcp . length } MCP server(s)...` ) ;
151+ this . mcpManager = new MCPManager ( this . config . mcp ) ;
152+ this . tui . setMcpManager ( this . mcpManager ) ;
153+ await this . mcpManager . initialize ( ) ;
154+ this . mcpEventUnsubscribe = this . mcpManager . onEvent ( ( event ) => this . handleMcpEvent ( event ) ) ;
155+ await this . mcpManager . registerDrivers ( this . driverRegistry ) ;
156+
157+ if ( this . appHostManager ) {
158+ this . appHostManager . setMcpManager ( this . mcpManager ) ;
159+ }
153160
154- if ( this . appHostManager ) {
155- this . appHostManager . setMcpManager ( this . mcpManager ) ;
161+ const appOnlyNames = new Set ( this . mcpManager . getAppOnlyToolNames ( ) ) ;
162+ this . toolRegistry . initialize (
163+ this . makeSkillTool ( ) ,
164+ this . mcpManager . getAlwaysLoadToolNames ( ) ,
165+ appOnlyNames ,
166+ ) ;
167+ this . agent . state . tools = this . toolRegistry . buildToolsForRequest ( ) ;
168+ const connected = this . mcpManager . getStates ( ) . filter ( ( s ) => s . status === "connected" ) . length ;
169+ const total = this . config . mcp . length ;
170+ this . tui . addInfo ( `MCP: ${ connected } /${ total } connected` ) ;
171+ if ( connected < total ) {
172+ const errors = this . mcpManager . getStates ( ) . filter ( ( s ) => s . status === "error" ) ;
173+ for ( const err of errors ) {
174+ this . tui . addInfo ( `MCP '${ err . config . name } ' failed: ${ err . error ?? "unknown" } ` ) ;
175+ }
176+ }
177+ this . tui . focusEditor ( ) ;
178+ } else {
179+ this . toolRegistry . initialize ( this . makeSkillTool ( ) ) ;
180+ this . agent . state . tools = this . toolRegistry . buildToolsForRequest ( ) ;
156181 }
157182
158- const appOnlyNames = new Set ( this . mcpManager . getAppOnlyToolNames ( ) ) ;
159- this . toolRegistry . initialize (
160- this . makeSkillTool ( ) ,
161- this . mcpManager . getAlwaysLoadToolNames ( ) ,
162- appOnlyNames ,
163- ) ;
164- this . agent . state . tools = this . toolRegistry . buildToolsForRequest ( ) ;
165- const connected = this . mcpManager . getStates ( ) . filter ( ( s ) => s . status === "connected" ) . length ;
166- const total = this . config . mcp . length ;
167- this . tui . addInfo ( `MCP: ${ connected } /${ total } connected` ) ;
168- if ( connected < total ) {
169- const errors = this . mcpManager . getStates ( ) . filter ( ( s ) => s . status === "error" ) ;
170- for ( const err of errors ) {
171- this . tui . addInfo ( `MCP '${ err . config . name } ' failed: ${ err . error ?? "unknown" } ` ) ;
172- }
183+ if ( ! this . config . apiKey ) {
184+ this . tui . addInfo ( [
185+ "Welcome to DSCode! To get started, configure your API key:" ,
186+ "" ,
187+ " /config key sk-your-deepseek-api-key" ,
188+ "" ,
189+ "Then set your preferred model:" ,
190+ "" ,
191+ " /config model deepseek-v4-pro" ,
192+ "" ,
193+ "Type /config to see all settings." ,
194+ ] . join ( "\n" ) ) ;
173195 }
174- this . tui . focusEditor ( ) ;
175- } else {
176- this . toolRegistry . initialize ( this . makeSkillTool ( ) ) ;
177- this . agent . state . tools = this . toolRegistry . buildToolsForRequest ( ) ;
178- }
179196
180- if ( ! this . config . apiKey ) {
181- this . tui . addInfo ( [
182- "Welcome to DSCode! To get started, configure your API key:" ,
183- "" ,
184- " /config key sk-your-deepseek-api-key" ,
185- "" ,
186- "Then set your preferred model:" ,
187- "" ,
188- " /config model deepseek-v4-pro" ,
189- "" ,
190- "Type /config to see all settings." ,
191- ] . join ( "\n" ) ) ;
197+ this . tui . focusEditor ( ) ;
198+ await this . tui . waitForExit ( ) ;
199+ } finally {
200+ await this . shutdown ( ) ;
192201 }
193-
194- this . tui . focusEditor ( ) ;
195- await this . tui . waitForExit ( ) ;
196- await this . shutdown ( ) ;
197202 }
198203
199204 setModel ( modelId : string ) : void {
@@ -217,6 +222,11 @@ export class Harness {
217222 }
218223
219224 private async shutdown ( ) : Promise < void > {
225+ if ( this . shuttingDown ) return ;
226+ this . shuttingDown = true ;
227+
228+ this . mcpEventUnsubscribe ?.( ) ;
229+ this . mcpEventUnsubscribe = undefined ;
220230 this . sessionManager . saveSession ( this . agent ) ;
221231 if ( this . appHostManager ) {
222232 await this . appHostManager . shutdown ( ) ;
0 commit comments