@@ -56,6 +56,7 @@ export const McpCommand = cmd({
5656 builder : ( yargs ) =>
5757 yargs
5858 . command ( McpAddCommand )
59+ . command ( McpRemoveCommand )
5960 . command ( McpListCommand )
6061 . command ( McpAuthCommand )
6162 . command ( McpLogoutCommand )
@@ -398,6 +399,25 @@ async function resolveConfigPath(baseDir: string, global = false) {
398399 return candidates [ 0 ]
399400}
400401
402+ async function removeMcpFromConfig ( name : string , configPath : string ) {
403+ if ( ! ( await Filesystem . exists ( configPath ) ) ) {
404+ return false
405+ }
406+
407+ const text = await Filesystem . readText ( configPath )
408+ const edits = modify ( text , [ "mcp" , name ] , undefined , {
409+ formattingOptions : { tabSize : 2 , insertSpaces : true } ,
410+ } )
411+
412+ if ( edits . length === 0 ) {
413+ return false
414+ }
415+
416+ const result = applyEdits ( text , edits )
417+ await Filesystem . write ( configPath , result )
418+ return true
419+ }
420+
401421async function addMcpToConfig ( name : string , mcpConfig : Config . Mcp , configPath : string ) {
402422 let text = "{}"
403423 if ( await Filesystem . exists ( configPath ) ) {
@@ -418,10 +438,79 @@ async function addMcpToConfig(name: string, mcpConfig: Config.Mcp, configPath: s
418438export const McpAddCommand = cmd ( {
419439 command : "add" ,
420440 describe : "add an MCP server" ,
421- async handler ( ) {
441+ builder : ( yargs ) =>
442+ yargs
443+ . option ( "name" , { type : "string" , describe : "MCP server name" } )
444+ . option ( "type" , { type : "string" , describe : "Server type" , choices : [ "local" , "remote" ] } )
445+ . option ( "url" , { type : "string" , describe : "Server URL (for remote type)" } )
446+ . option ( "command" , { type : "string" , describe : "Command to run (for local type)" } )
447+ . option ( "header" , { type : "array" , string : true , describe : "HTTP headers as key=value (repeatable)" } )
448+ . option ( "oauth" , { type : "boolean" , describe : "Enable OAuth" , default : true } )
449+ . option ( "global" , { type : "boolean" , describe : "Add to global config" , default : false } ) ,
450+ async handler ( args ) {
422451 await Instance . provide ( {
423452 directory : process . cwd ( ) ,
424453 async fn ( ) {
454+ // Non-interactive mode: all required args provided via flags
455+ if ( args . name && args . type ) {
456+ if ( ! args . name . trim ( ) ) {
457+ console . error ( "MCP server name cannot be empty" )
458+ process . exit ( 1 )
459+ }
460+
461+ const useGlobal = args . global || Instance . project . vcs !== "git"
462+ const configPath = await resolveConfigPath (
463+ useGlobal ? Global . Path . config : Instance . worktree ,
464+ useGlobal ,
465+ )
466+
467+ let mcpConfig : Config . Mcp
468+
469+ if ( args . type === "local" ) {
470+ if ( ! args . command ?. trim ( ) ) {
471+ console . error ( "--command is required for local type" )
472+ process . exit ( 1 )
473+ }
474+ mcpConfig = {
475+ type : "local" ,
476+ command : args . command . trim ( ) . split ( / \s + / ) . filter ( Boolean ) ,
477+ }
478+ } else {
479+ if ( ! args . url ) {
480+ console . error ( "--url is required for remote type" )
481+ process . exit ( 1 )
482+ }
483+ if ( ! URL . canParse ( args . url ) ) {
484+ console . error ( `Invalid URL: ${ args . url } ` )
485+ process . exit ( 1 )
486+ }
487+
488+ const headers : Record < string , string > = { }
489+ if ( args . header ) {
490+ for ( const h of args . header ) {
491+ const eq = h . indexOf ( "=" )
492+ if ( eq === - 1 ) {
493+ console . error ( `Invalid header format: ${ h } (expected key=value)` )
494+ process . exit ( 1 )
495+ }
496+ headers [ h . substring ( 0 , eq ) ] = h . substring ( eq + 1 )
497+ }
498+ }
499+
500+ mcpConfig = {
501+ type : "remote" ,
502+ url : args . url ,
503+ ...( ! args . oauth ? { oauth : false as const } : { } ) ,
504+ ...( Object . keys ( headers ) . length > 0 ? { headers } : { } ) ,
505+ }
506+ }
507+
508+ await addMcpToConfig ( args . name , mcpConfig , configPath )
509+ console . log ( `MCP server "${ args . name } " added to ${ configPath } ` )
510+ return
511+ }
512+
513+ // Interactive mode: existing behavior
425514 UI . empty ( )
426515 prompts . intro ( "Add MCP server" )
427516
@@ -579,6 +668,60 @@ export const McpAddCommand = cmd({
579668 } ,
580669} )
581670
671+ export const McpRemoveCommand = cmd ( {
672+ command : "remove <name>" ,
673+ aliases : [ "rm" ] ,
674+ describe : "remove an MCP server" ,
675+ builder : ( yargs ) =>
676+ yargs
677+ . positional ( "name" , {
678+ describe : "name of the MCP server to remove" ,
679+ type : "string" ,
680+ demandOption : true ,
681+ } )
682+ . option ( "global" , { type : "boolean" , describe : "Remove from global config" , default : false } ) ,
683+ async handler ( args ) {
684+ await Instance . provide ( {
685+ directory : process . cwd ( ) ,
686+ async fn ( ) {
687+ const useGlobal = args . global || Instance . project . vcs !== "git"
688+ const configPath = await resolveConfigPath (
689+ useGlobal ? Global . Path . config : Instance . worktree ,
690+ useGlobal ,
691+ )
692+
693+ const removed = await removeMcpFromConfig ( args . name , configPath )
694+ if ( removed ) {
695+ console . log ( `MCP server "${ args . name } " removed from ${ configPath } ` )
696+ } else if ( Instance . project . vcs === "git" && ! args . global ) {
697+ // Try global scope as fallback only when in a git repo
698+ const globalPath = await resolveConfigPath ( Global . Path . config , true )
699+ const removedGlobal = await removeMcpFromConfig ( args . name , globalPath )
700+ if ( removedGlobal ) {
701+ console . log ( `MCP server "${ args . name } " removed from ${ globalPath } ` )
702+ } else {
703+ console . error ( `MCP server "${ args . name } " not found in any config` )
704+ process . exit ( 1 )
705+ }
706+ } else if ( args . global && Instance . project . vcs === "git" ) {
707+ // Try local scope as fallback when --global was explicit and we're in a git repo
708+ const localPath = await resolveConfigPath ( Instance . worktree , false )
709+ const removedLocal = await removeMcpFromConfig ( args . name , localPath )
710+ if ( removedLocal ) {
711+ console . log ( `MCP server "${ args . name } " removed from ${ localPath } ` )
712+ } else {
713+ console . error ( `MCP server "${ args . name } " not found in any config` )
714+ process . exit ( 1 )
715+ }
716+ } else {
717+ console . error ( `MCP server "${ args . name } " not found in any config` )
718+ process . exit ( 1 )
719+ }
720+ } ,
721+ } )
722+ } ,
723+ } )
724+
582725export const McpDebugCommand = cmd ( {
583726 command : "debug <name>" ,
584727 describe : "debug OAuth connection for an MCP server" ,
0 commit comments