99import platform
1010from typing import Any , Dict , List , Optional , Union
1111
12+ from pydantic import TypeAdapter
1213from ruamel .yaml import YAML
1314
14- from mcpm .utils .server_config import ServerConfig
15+ from mcpm .schemas .server_config import ServerConfig , STDIOServerConfig
1516
1617logger = logging .getLogger (__name__ )
1718
@@ -28,10 +29,10 @@ class BaseClientManager(abc.ABC):
2829 client_key = "" # Client identifier (e.g., "claude-desktop")
2930 display_name = "" # Human-readable name (e.g., "Claude Desktop")
3031 download_url = "" # URL to download the client
32+ config_path : str
3133
3234 def __init__ (self ):
3335 """Initialize the client manager"""
34- self .config_path = None # To be set by subclasses
3536 self ._system = platform .system ()
3637
3738 @abc .abstractmethod
@@ -56,12 +57,11 @@ def get_server(self, server_name: str) -> Optional[ServerConfig]:
5657 pass
5758
5859 @abc .abstractmethod
59- def add_server (self , server_config : Union [ ServerConfig , Dict [ str , Any ]], name : Optional [ str ] = None ) -> bool :
60+ def add_server (self , server_config : ServerConfig ) -> bool :
6061 """Add or update a server in the client config
6162
6263 Args:
63- server_config: ServerConfig object or dictionary in client format
64- name: Required server name when using dictionary format
64+ server_config: ServerConfig object
6565
6666 Returns:
6767 bool: Success or failure
@@ -140,6 +140,7 @@ class JSONClientManager(BaseClientManager):
140140 This class implements the BaseClientManager interface using JSON files
141141 for configuration storage.
142142 """
143+
143144 configure_key_name : str = "mcpServers"
144145
145146 def __init__ (self ):
@@ -231,29 +232,21 @@ def get_server(self, server_name: str) -> Optional[ServerConfig]:
231232 client_config = servers [server_name ]
232233 return self .from_client_format (server_name , client_config )
233234
234- def add_server (self , server_config : Union [ ServerConfig , Dict [ str , Any ]], name : Optional [ str ] = None ) -> bool :
235+ def add_server (self , server_config : ServerConfig ) -> bool :
235236 """Add or update a server in the client config
236237
237238 Can accept either a ServerConfig object or a raw dictionary in client format.
238239 When using a dictionary, a name must be provided.
239240
240241 Args:
241242 server_config: ServerConfig object or dictionary in client format
242- name: Required server name when using dictionary format
243243
244244 Returns:
245245 bool: Success or failure
246246 """
247- # Handle direct dictionary input
248- if isinstance (server_config , dict ):
249- if name is None :
250- raise ValueError ("Name must be provided when using dictionary format" )
251- server_name = name
252- client_config = server_config # Already in client format
253247 # Handle ServerConfig objects
254- else :
255- server_name = server_config .name
256- client_config = self .to_client_format (server_config )
248+ server_name = server_config .name
249+ client_config = self .to_client_format (server_config )
257250
258251 # Update config directly
259252 config = self ._load_config ()
@@ -275,15 +268,18 @@ def to_client_format(self, server_config: ServerConfig) -> Dict[str, Any]:
275268 Dict containing client-specific configuration with core fields
276269 """
277270 # Base result containing only essential execution information
278- result = {
279- "command" : server_config .command ,
280- "args" : server_config .args ,
281- }
282-
283- # Add filtered environment variables if present
284- non_empty_env = server_config .get_filtered_env_vars (os .environ )
285- if non_empty_env :
286- result ["env" ] = non_empty_env
271+ if isinstance (server_config , STDIOServerConfig ):
272+ result = {
273+ "command" : server_config .command ,
274+ "args" : server_config .args ,
275+ }
276+
277+ # Add filtered environment variables if present
278+ non_empty_env = server_config .get_filtered_env_vars (os .environ )
279+ if non_empty_env :
280+ result ["env" ] = non_empty_env
281+ else :
282+ result = server_config .to_dict ()
287283
288284 return result
289285
@@ -300,18 +296,11 @@ def from_client_format(cls, server_name: str, client_config: Dict[str, Any]) ->
300296 Returns:
301297 ServerConfig object
302298 """
303- # Create a dictionary that ServerConfig.from_dict can work with
304299 server_data = {
305300 "name" : server_name ,
306- "command" : client_config .get ("command" , "" ),
307- "args" : client_config .get ("args" , []),
308301 }
309-
310- # Add environment variables if present
311- if "env" in client_config :
312- server_data ["env" ] = client_config ["env" ]
313-
314- return ServerConfig .from_dict (server_data )
302+ server_data .update (client_config )
303+ return TypeAdapter (ServerConfig ).validate_python (server_data )
315304
316305 def list_servers (self ) -> List [str ]:
317306 """List all MCP servers in client config
@@ -374,7 +363,6 @@ class YAMLClientManager(BaseClientManager):
374363 def __init__ (self ):
375364 """Initialize the YAML client manager"""
376365 super ().__init__ ()
377- self .config_path = None # To be set by subclasses
378366 self .yaml_handler : YAML = YAML ()
379367
380368 def _load_config (self ) -> Dict [str , Any ]:
@@ -454,7 +442,9 @@ def _get_all_server_names(self, config: Dict[str, Any]) -> List[str]:
454442 pass
455443
456444 @abc .abstractmethod
457- def _add_server_to_config (self , config : Dict [str , Any ], server_name : str , server_config : Dict [str , Any ]) -> Dict [str , Any ]:
445+ def _add_server_to_config (
446+ self , config : Dict [str , Any ], server_name : str , server_config : Dict [str , Any ]
447+ ) -> Dict [str , Any ]:
458448 """Add or update a server in the config
459449
460450 Args:
@@ -589,11 +579,7 @@ def get_client_info(self) -> Dict[str, str]:
589579 Returns:
590580 Dict: Information about the client including display name, download URL, and config path
591581 """
592- return {
593- "name" : self .display_name ,
594- "download_url" : self .download_url ,
595- "config_file" : self .config_path
596- }
582+ return {"name" : self .display_name , "download_url" : self .download_url , "config_file" : self .config_path }
597583
598584 def is_client_installed (self ) -> bool :
599585 """Check if this client is installed
0 commit comments