@@ -7,6 +7,7 @@ use eyre::{
77 bail,
88} ;
99use fig_os_shim:: Context ;
10+ use futures:: future:: ok;
1011use tracing:: warn;
1112
1213use crate :: cli:: {
@@ -44,9 +45,18 @@ pub async fn execute_mcp(args: Mcp) -> Result<ExitCode> {
4445pub async fn add_mcp_server ( ctx : & Context , args : McpAdd ) -> Result < ( ) > {
4546 let config_path = resolve_scope_profile ( ctx, args. scope , args. profile . as_ref ( ) ) ?;
4647 let mut config: McpServerConfig = serde_json:: from_str ( & ctx. fs ( ) . read_to_string ( & config_path) . await ?) ?;
48+ let merged_env = if args. env . is_empty ( ) {
49+ None
50+ } else {
51+ let mut map = HashMap :: new ( ) ;
52+ for m in args. env {
53+ map. extend ( m) ;
54+ }
55+ Some ( map)
56+ } ;
4757 let val: CustomToolConfig = serde_json:: from_value ( serde_json:: json!( {
4858 "command" : args. command,
49- "env" : args . env ,
59+ "env" : merged_env ,
5060 "timeout" : args. timeout,
5161 } ) ) ?;
5262 config. mcp_servers . insert ( args. name , val) ;
@@ -58,8 +68,17 @@ pub async fn add_mcp_server(ctx: &Context, args: McpAdd) -> Result<()> {
5868pub async fn remove_mcp_server ( ctx : & Context , args : McpRemove ) -> Result < ( ) > {
5969 let config_path = resolve_scope_profile ( ctx, args. scope , args. profile . as_ref ( ) ) ?;
6070 let mut config = McpServerConfig :: load_from_file ( ctx, & config_path) . await ?;
71+
72+ let scope = args. scope . unwrap_or ( Scope :: Workspace ) ;
6173 match config. mcp_servers . remove ( & args. name ) {
62- Some ( _) => ( ) ,
74+ Some ( _) => {
75+ config. save_to_file ( ctx, & config_path) . await ?;
76+ println ! (
77+ "✓ Removed MCP server '{}' from {}" ,
78+ args. name,
79+ scope_display( & scope, & args. profile)
80+ ) ;
81+ } ,
6382 None => {
6483 warn ! ( ?args, "No MCP server found" ) ;
6584 } ,
@@ -135,19 +154,74 @@ async fn get_mcp_server_configs(
135154
136155fn scope_display ( scope : & Scope , profile : & Option < String > ) -> String {
137156 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" ) ) ,
157+ Scope :: Workspace => "📄 workspace" . into ( ) ,
158+ Scope :: Global => "🌍 global" . into ( ) ,
159+ Scope :: Profile => format ! ( "👤 profile({})" , profile. as_deref( ) . unwrap_or( "default" ) ) ,
141160 }
142161}
143162
144163pub async fn import_mcp_server ( ctx : & Context , args : McpImport ) -> Result < ( ) > {
145164 let config_path = resolve_scope_profile ( ctx, args. scope , args. profile . as_ref ( ) ) ?;
146- todo ! ( )
165+ let mut dst_cfg = if ctx. fs ( ) . exists ( & config_path) {
166+ McpServerConfig :: load_from_file ( ctx, & config_path) . await ?
167+ } else {
168+ McpServerConfig :: default ( )
169+ } ;
170+ let expanded = shellexpand:: tilde ( & args. file ) ;
171+ let mut src_path = std:: path:: PathBuf :: from ( expanded. as_ref ( ) ) ;
172+ if src_path. is_relative ( ) {
173+ src_path = ctx. env ( ) . current_dir ( ) ?. join ( src_path) ;
174+ }
175+
176+ let src_content = ctx
177+ . fs ( )
178+ . read_to_string ( & src_path)
179+ . await
180+ . map_err ( |e| eyre:: eyre!( "Failed to read source file '{}': {e}" , src_path. display( ) ) ) ?;
181+ let src_cfg: McpServerConfig = serde_json:: from_str ( & src_content)
182+ . map_err ( |e| eyre:: eyre!( "Invalid MCP JSON in '{}': {e}" , src_path. display( ) ) ) ?;
183+
184+ let before = dst_cfg. mcp_servers . len ( ) ;
185+ for ( name, cfg) in src_cfg. mcp_servers {
186+ if dst_cfg. mcp_servers . insert ( name. clone ( ) , cfg) . is_some ( ) {
187+ warn ! ( server = %name, "Overwriting existing MCP server configuration" ) ;
188+ }
189+ }
190+ let added = dst_cfg. mcp_servers . len ( ) - before;
191+
192+ dst_cfg. save_to_file ( ctx, & config_path) . await ?;
193+
194+ let scope = args. scope . unwrap_or ( Scope :: Workspace ) ;
195+ println ! (
196+ "✓ Imported {added} MCP server(s) into {:?} scope" ,
197+ scope_display( & scope, & args. profile)
198+ ) ;
199+ Ok ( ( ) )
147200}
148201
149202pub async fn get_mcp_server_status ( ctx : & Context , name : String ) -> Result < ( ) > {
150- todo ! ( )
203+ let configs = get_mcp_server_configs ( ctx, None , None ) . await ?;
204+ for ( _, _, _, cfg_opt) in configs {
205+ if let Some ( cfg) = cfg_opt {
206+ if let Some ( tool_cfg) = cfg. mcp_servers . get ( & name) {
207+ println ! ( "MCP Server: {name}" ) ;
208+ println ! ( "Command : {}" , tool_cfg. command) ;
209+ println ! ( "Timeout : {} ms" , tool_cfg. timeout) ;
210+ println ! (
211+ "Env Vars : {}" ,
212+ tool_cfg
213+ . env
214+ . as_ref( )
215+ . map( |e| e. keys( ) . cloned( ) . collect:: <Vec <_>>( ) . join( ", " ) )
216+ . unwrap_or_else( || "(none)" . into( ) )
217+ ) ;
218+ // todo yifan how can I know the server status
219+ println ! ( "Status : " ) ;
220+ return Ok ( ( ) ) ;
221+ }
222+ }
223+ }
224+ bail ! ( "No MCP server named '{name}' found\n " )
151225}
152226
153227fn resolve_scope_profile ( ctx : & Context , scope : Option < Scope > , profile : Option < & impl AsRef < str > > ) -> Result < PathBuf > {
0 commit comments