@@ -68,15 +68,77 @@ pub async fn remove_mcp_server(ctx: &Context, args: McpRemove) -> Result<()> {
6868}
6969
7070pub async fn list_mcp_server ( ctx : & Context , args : McpList ) -> Result < ( ) > {
71- todo ! ( )
71+ let configs = get_mcp_server_configs ( ctx, args. scope , args. profile ) . await ?;
72+ if configs. is_empty ( ) {
73+ println ! ( "No MCP server configurations found.\n " ) ;
74+ return Ok ( ( ) ) ;
75+ }
76+
77+ for ( scope, profile, path, cfg_opt) in configs {
78+ println ! ( "{}:" , scope_display( & scope, & profile) ) ;
79+ println ! ( " {}" , path. display( ) ) ;
80+ match cfg_opt {
81+ Some ( cfg) if !cfg. mcp_servers . is_empty ( ) => {
82+ for ( name, tool_cfg) in & cfg. mcp_servers {
83+ println ! ( " • {name:<12} {}" , tool_cfg. command) ;
84+ }
85+ } ,
86+ _ => {
87+ println ! ( " null" ) ;
88+ } ,
89+ }
90+ }
91+ Ok ( ( ) )
7292}
7393
7494async fn get_mcp_server_configs (
7595 ctx : & Context ,
7696 scope : Option < Scope > ,
7797 profile : Option < String > ,
78- ) -> Result < Vec < ( PathBuf , McpServerConfig ) > > {
79- todo ! ( )
98+ ) -> Result < Vec < ( Scope , Option < String > , PathBuf , Option < McpServerConfig > ) > > {
99+ let targets: Vec < ( Scope , Option < String > ) > = match ( scope, profile) {
100+ ( Some ( Scope :: Workspace ) , _) => vec ! [ ( Scope :: Workspace , None ) ] ,
101+ ( Some ( Scope :: Global ) , _) => vec ! [ ( Scope :: Global , None ) ] ,
102+ ( Some ( Scope :: Profile ) , Some ( p) ) => vec ! [ ( Scope :: Profile , Some ( p) ) ] ,
103+ ( Some ( Scope :: Profile ) , None ) => vec ! [ ( Scope :: Profile , Some ( "default" . to_string( ) ) ) ] ,
104+
105+ // no scope but have profile ⇒ search profile
106+ ( None , Some ( p) ) => vec ! [ ( Scope :: Profile , Some ( p) ) ] ,
107+
108+ // give nothing ⇒ default priority:workspace → global
109+ ( None , None ) => vec ! [
110+ ( Scope :: Workspace , None ) ,
111+ ( Scope :: Global , None ) ,
112+ ( Scope :: Profile , Some ( "default" . to_string( ) ) ) ,
113+ ] ,
114+ } ;
115+
116+ let mut results = Vec :: new ( ) ;
117+ for ( sc, prof) in targets {
118+ let path = resolve_scope_profile ( ctx, Some ( sc) , prof. as_ref ( ) ) ?;
119+
120+ let cfg_opt = if ctx. fs ( ) . exists ( & path) {
121+ match McpServerConfig :: load_from_file ( ctx, & path) . await {
122+ Ok ( cfg) => Some ( cfg) ,
123+ Err ( e) => {
124+ warn ! ( ?path, error = %e, "Invalid MCP config file—ignored, treated as null" ) ;
125+ None
126+ } ,
127+ }
128+ } else {
129+ None
130+ } ;
131+ results. push ( ( sc, prof. clone ( ) , path, cfg_opt) ) ;
132+ }
133+ Ok ( results)
134+ }
135+
136+ fn scope_display ( scope : & Scope , profile : & Option < String > ) -> String {
137+ match scope {
138+ Scope :: Workspace => "\n 📄 workspace" . into ( ) ,
139+ Scope :: Global => "\n 🌍 global" . into ( ) ,
140+ Scope :: Profile => format ! ( "\n 👤 profile({})" , profile. as_deref( ) . unwrap_or( "default" ) ) ,
141+ }
80142}
81143
82144pub async fn import_mcp_server ( ctx : & Context , args : McpImport ) -> Result < ( ) > {
0 commit comments