@@ -26,6 +26,7 @@ const fakeWorkspace = {
2626 mkdir : ( _ : string , __ : any ) => Promise . resolve ( ) ,
2727 } ,
2828 getUserHomeDir : ( ) => '' ,
29+ getAllWorkspaceFolders : ( ) => [ { uri : '/fake/workspace' } ] ,
2930}
3031const features = { logging : fakeLogging , workspace : fakeWorkspace , lsp : { } } as any
3132
@@ -234,10 +235,26 @@ describe('addServer()', () => {
234235describe ( 'removeServer()' , ( ) => {
235236 let loadStub : sinon . SinonStub
236237 let saveAgentConfigStub : sinon . SinonStub
238+ let existsStub : sinon . SinonStub
239+ let readFileStub : sinon . SinonStub
240+ let writeFileStub : sinon . SinonStub
241+ let mkdirStub : sinon . SinonStub
242+ let getWorkspaceMcpConfigPathsStub : sinon . SinonStub
243+ let getGlobalMcpConfigPathStub : sinon . SinonStub
237244
238245 beforeEach ( ( ) => {
239246 loadStub = stubAgentConfig ( )
240247 saveAgentConfigStub = sinon . stub ( mcpUtils , 'saveAgentConfig' ) . resolves ( )
248+ existsStub = sinon . stub ( fakeWorkspace . fs , 'exists' ) . resolves ( true )
249+ readFileStub = sinon
250+ . stub ( fakeWorkspace . fs , 'readFile' )
251+ . resolves ( Buffer . from ( JSON . stringify ( { mcpServers : { x : { } } } ) ) )
252+ writeFileStub = sinon . stub ( fakeWorkspace . fs , 'writeFile' ) . resolves ( )
253+ mkdirStub = sinon . stub ( fakeWorkspace . fs , 'mkdir' ) . resolves ( )
254+ getWorkspaceMcpConfigPathsStub = sinon
255+ . stub ( mcpUtils , 'getWorkspaceMcpConfigPaths' )
256+ . returns ( [ 'ws1/config.json' , 'ws2/config.json' ] )
257+ getGlobalMcpConfigPathStub = sinon . stub ( mcpUtils , 'getGlobalMcpConfigPath' ) . returns ( 'global/config.json' )
241258 } )
242259
243260 afterEach ( async ( ) => {
@@ -275,6 +292,106 @@ describe('removeServer()', () => {
275292 expect ( saveAgentConfigStub . calledOnce ) . to . be . true
276293 expect ( ( mgr as any ) . clients . has ( 'x' ) ) . to . be . false
277294 } )
295+
296+ it ( 'removes server from all config files' , async ( ) => {
297+ const mgr = await McpManager . init ( [ ] , features )
298+ const dummy = new Client ( { name : 'c' , version : 'v' } )
299+ ; ( mgr as any ) . clients . set ( 'x' , dummy )
300+ ; ( mgr as any ) . mcpServers . set ( 'x' , {
301+ command : '' ,
302+ args : [ ] ,
303+ env : { } ,
304+ timeout : 0 ,
305+ __configPath__ : 'c.json' ,
306+ } as MCPServerConfig )
307+ ; ( mgr as any ) . serverNameMapping . set ( 'x' , 'x' )
308+ ; ( mgr as any ) . agentConfig = {
309+ name : 'test-agent' ,
310+ version : '1.0.0' ,
311+ description : 'Test agent' ,
312+ mcpServers : { x : { } } ,
313+ tools : [ '@x' ] ,
314+ allowedTools : [ ] ,
315+ toolsSettings : { } ,
316+ includedFiles : [ ] ,
317+ resources : [ ] ,
318+ }
319+
320+ await mgr . removeServer ( 'x' )
321+
322+ // Verify that writeFile was called for each config path (2 workspace + 1 global)
323+ expect ( writeFileStub . callCount ) . to . equal ( 3 )
324+
325+ // Verify the content of the writes (should have removed the server)
326+ writeFileStub . getCalls ( ) . forEach ( call => {
327+ const content = JSON . parse ( call . args [ 1 ] )
328+ expect ( content . mcpServers ) . to . not . have . property ( 'x' )
329+ } )
330+ } )
331+ } )
332+
333+ describe ( 'mutateConfigFile()' , ( ) => {
334+ let existsStub : sinon . SinonStub
335+ let readFileStub : sinon . SinonStub
336+ let writeFileStub : sinon . SinonStub
337+ let mkdirStub : sinon . SinonStub
338+ let mgr : McpManager
339+
340+ beforeEach ( async ( ) => {
341+ sinon . restore ( )
342+ stubAgentConfig ( )
343+ existsStub = sinon . stub ( fakeWorkspace . fs , 'exists' ) . resolves ( true )
344+ readFileStub = sinon
345+ . stub ( fakeWorkspace . fs , 'readFile' )
346+ . resolves ( Buffer . from ( JSON . stringify ( { mcpServers : { test : { } } } ) ) )
347+ writeFileStub = sinon . stub ( fakeWorkspace . fs , 'writeFile' ) . resolves ( )
348+ mkdirStub = sinon . stub ( fakeWorkspace . fs , 'mkdir' ) . resolves ( )
349+ mgr = await McpManager . init ( [ ] , features )
350+ } )
351+
352+ afterEach ( async ( ) => {
353+ sinon . restore ( )
354+ try {
355+ await McpManager . instance . close ( )
356+ } catch { }
357+ } )
358+
359+ it ( 'reads, mutates, and writes config file' , async ( ) => {
360+ // Access the private method using type assertion
361+ const mutateConfigFile = ( mgr as any ) . mutateConfigFile . bind ( mgr )
362+
363+ await mutateConfigFile ( 'test/path.json' , ( json : any ) => {
364+ json . mcpServers . newServer = { command : 'test' }
365+ delete json . mcpServers . test
366+ } )
367+
368+ expect ( readFileStub . calledOnce ) . to . be . true
369+ expect ( writeFileStub . calledOnce ) . to . be . true
370+
371+ // Verify the content was modified correctly
372+ const writtenContent = JSON . parse ( writeFileStub . firstCall . args [ 1 ] )
373+ expect ( writtenContent . mcpServers ) . to . have . property ( 'newServer' )
374+ expect ( writtenContent . mcpServers ) . to . not . have . property ( 'test' )
375+ } )
376+
377+ it ( 'creates new config file if it does not exist' , async ( ) => {
378+ existsStub . resolves ( false )
379+ readFileStub . rejects ( { code : 'ENOENT' } )
380+
381+ // Access the private method using type assertion
382+ const mutateConfigFile = ( mgr as any ) . mutateConfigFile . bind ( mgr )
383+
384+ await mutateConfigFile ( 'test/path.json' , ( json : any ) => {
385+ json . mcpServers . newServer = { command : 'test' }
386+ } )
387+
388+ expect ( mkdirStub . calledOnce ) . to . be . true
389+ expect ( writeFileStub . calledOnce ) . to . be . true
390+
391+ // Verify the content was created correctly
392+ const writtenContent = JSON . parse ( writeFileStub . firstCall . args [ 1 ] )
393+ expect ( writtenContent . mcpServers ) . to . have . property ( 'newServer' )
394+ } )
278395} )
279396
280397describe ( 'updateServer()' , ( ) => {
0 commit comments