Skip to content

Commit 1154a5f

Browse files
committed
Refactor clients
1 parent a9f0ec6 commit 1154a5f

File tree

17 files changed

+176
-49
lines changed

17 files changed

+176
-49
lines changed

src/mcpm/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def main(ctx, help_flag):
4747
console.print("Available clients:")
4848

4949
# Show available clients
50-
from mcpm.utils.client_registry import ClientRegistry
50+
from mcpm.clients.client_registry import ClientRegistry
5151

5252
for client in ClientRegistry.get_supported_clients():
5353
console.print(f" - {client}")
@@ -90,7 +90,7 @@ def main(ctx, help_flag):
9090
console.print(panel)
9191

9292
# Get information about installed clients
93-
from mcpm.utils.client_registry import ClientRegistry
93+
from mcpm.clients.client_registry import ClientRegistry
9494

9595
installed_clients = ClientRegistry.detect_installed_clients()
9696

src/mcpm/clients/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@
55
"""
66

77
from mcpm.clients.base import BaseClientManager
8-
from mcpm.clients.claude_desktop import ClaudeDesktopManager
98
from mcpm.clients.client_config import ClientConfigManager
10-
from mcpm.clients.cursor import CursorManager
11-
from mcpm.clients.windsurf import WindsurfManager
9+
from mcpm.clients.client_registry import ClientRegistry
10+
from mcpm.clients.managers.claude_desktop import ClaudeDesktopManager
11+
from mcpm.clients.managers.cursor import CursorManager
12+
from mcpm.clients.managers.windsurf import WindsurfManager
1213

1314
__all__ = [
1415
"BaseClientManager",
1516
"ClaudeDesktopManager",
1617
"WindsurfManager",
1718
"CursorManager",
1819
"ClientConfigManager",
20+
"ClientRegistry",
1921
]

src/mcpm/clients/base.py

Lines changed: 123 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22
Base client manager module that defines the interface for all client managers.
33
"""
44

5-
import os
5+
import abc
66
import json
77
import logging
8+
import os
89
import platform
9-
from typing import Dict, Optional, Any, List, Union
10+
from typing import Any, Dict, List, Optional, Union
1011

1112
from mcpm.utils.server_config import ServerConfig
1213

1314
logger = logging.getLogger(__name__)
1415

1516

16-
class BaseClientManager:
17-
"""Base class for all client managers providing a common interface"""
17+
class BaseClientManager(abc.ABC):
18+
"""
19+
Abstract base class that defines the interface for all client managers.
20+
21+
This class establishes the contract that all client managers must fulfill,
22+
but does not provide implementations.
23+
"""
1824

1925
# Client information properties
2026
client_key = "" # Client identifier (e.g., "claude-desktop")
@@ -24,9 +30,120 @@ class BaseClientManager:
2430
def __init__(self):
2531
"""Initialize the client manager"""
2632
self.config_path = None # To be set by subclasses
27-
self._config = None
2833
self._system = platform.system()
2934

35+
@abc.abstractmethod
36+
def get_servers(self) -> Dict[str, Any]:
37+
"""Get all MCP servers configured for this client
38+
39+
Returns:
40+
Dict of server configurations by name
41+
"""
42+
pass
43+
44+
@abc.abstractmethod
45+
def get_server(self, server_name: str) -> Optional[ServerConfig]:
46+
"""Get a server configuration
47+
48+
Args:
49+
server_name: Name of the server
50+
51+
Returns:
52+
ServerConfig object if found, None otherwise
53+
"""
54+
pass
55+
56+
@abc.abstractmethod
57+
def add_server(self, server_config: Union[ServerConfig, Dict[str, Any]], name: Optional[str] = None) -> bool:
58+
"""Add or update a server in the client config
59+
60+
Args:
61+
server_config: ServerConfig object or dictionary in client format
62+
name: Required server name when using dictionary format
63+
64+
Returns:
65+
bool: Success or failure
66+
"""
67+
pass
68+
69+
@abc.abstractmethod
70+
def to_client_format(self, server_config: ServerConfig) -> Dict[str, Any]:
71+
"""Convert ServerConfig to client-specific format
72+
73+
Args:
74+
server_config: ServerConfig object
75+
76+
Returns:
77+
Dict containing client-specific configuration
78+
"""
79+
pass
80+
81+
@abc.abstractmethod
82+
def from_client_format(self, server_name: str, client_config: Dict[str, Any]) -> ServerConfig:
83+
"""Convert client format to ServerConfig
84+
85+
Args:
86+
server_name: Name of the server
87+
client_config: Client-specific configuration
88+
89+
Returns:
90+
ServerConfig object
91+
"""
92+
pass
93+
94+
@abc.abstractmethod
95+
def list_servers(self) -> List[str]:
96+
"""List all MCP servers in client config
97+
98+
Returns:
99+
List of server names
100+
"""
101+
pass
102+
103+
@abc.abstractmethod
104+
def remove_server(self, server_name: str) -> bool:
105+
"""Remove an MCP server from client config
106+
107+
Args:
108+
server_name: Name of the server to remove
109+
110+
Returns:
111+
bool: Success or failure
112+
"""
113+
pass
114+
115+
@abc.abstractmethod
116+
def get_client_info(self) -> Dict[str, str]:
117+
"""Get information about this client
118+
119+
Returns:
120+
Dict: Information about the client including display name, download URL, and config path
121+
"""
122+
pass
123+
124+
@abc.abstractmethod
125+
def is_client_installed(self) -> bool:
126+
"""Check if this client is installed
127+
128+
Returns:
129+
bool: True if client is installed, False otherwise
130+
"""
131+
pass
132+
133+
134+
class JSONClientManager(BaseClientManager):
135+
"""
136+
JSON-based implementation of the client manager interface.
137+
138+
This class implements the BaseClientManager interface using JSON files
139+
for configuration storage.
140+
"""
141+
142+
def __init__(self):
143+
"""Initialize the JSON client manager"""
144+
super().__init__()
145+
self._config = None
146+
30147
def _load_config(self) -> Dict[str, Any]:
31148
"""Load client configuration file
32149
@@ -88,7 +205,6 @@ def get_servers(self) -> Dict[str, Any]:
88205
Returns:
89206
Dict of server configurations by name
90207
"""
91-
# To be overridden by subclasses
92208
config = self._load_config()
93209
return config.get("mcpServers", {})
94210

@@ -145,7 +261,7 @@ def add_server(self, server_config: Union[ServerConfig, Dict[str, Any]], name: O
145261
def to_client_format(self, server_config: ServerConfig) -> Dict[str, Any]:
146262
"""Convert ServerConfig to client-specific format with common core fields
147263
148-
This base implementation provides the common core fields (command, args, env)
264+
This implementation provides the common core fields (command, args, env)
149265
that are used by all client managers. Subclasses can override this method
150266
if they need to add additional client-specific fields.
151267

src/mcpm/clients/client_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def set_active_client(self, client_name: Optional[str]) -> bool:
4343
return result
4444

4545
# Get supported clients
46-
from mcpm.utils.client_registry import ClientRegistry
46+
from mcpm.clients.client_registry import ClientRegistry
4747

4848
supported_clients = ClientRegistry.get_supported_clients()
4949

@@ -59,7 +59,7 @@ def set_active_client(self, client_name: Optional[str]) -> bool:
5959
def get_supported_clients(self) -> List[str]:
6060
"""Get a list of supported client names"""
6161
# Import here to avoid circular imports
62-
from mcpm.utils.client_registry import ClientRegistry
62+
from mcpm.clients.client_registry import ClientRegistry
6363

6464
return ClientRegistry.get_supported_clients()
6565

@@ -73,7 +73,7 @@ def get_client_manager(self, client_name: str):
7373
BaseClientManager or None if client not supported
7474
"""
7575
# Import here to avoid circular imports
76-
from mcpm.utils.client_registry import ClientRegistry
76+
from mcpm.clients.client_registry import ClientRegistry
7777

7878
return ClientRegistry.get_client_manager(client_name)
7979

src/mcpm/utils/client_registry.py renamed to src/mcpm/clients/client_registry.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
from typing import Dict, List, Optional
88

99
from mcpm.clients.base import BaseClientManager
10+
from mcpm.clients.client_config import ClientConfigManager
1011

1112
# Import all client managers
12-
from mcpm.clients.claude_desktop import ClaudeDesktopManager
13-
from mcpm.clients.client_config import ClientConfigManager
14-
from mcpm.clients.cursor import CursorManager
15-
from mcpm.clients.windsurf import WindsurfManager
13+
from mcpm.clients.managers.claude_desktop import ClaudeDesktopManager
14+
from mcpm.clients.managers.cursor import CursorManager
15+
from mcpm.clients.managers.windsurf import WindsurfManager
1616

1717
logger = logging.getLogger(__name__)
1818

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""
2+
Client manager implementations for various MCP clients
3+
4+
This package contains specific implementations of client managers for MCP clients.
5+
"""
6+
7+
from mcpm.clients.managers.claude_desktop import ClaudeDesktopManager
8+
from mcpm.clients.managers.cursor import CursorManager
9+
from mcpm.clients.managers.windsurf import WindsurfManager
10+
11+
__all__ = [
12+
"ClaudeDesktopManager",
13+
"CursorManager",
14+
"WindsurfManager",
15+
]

src/mcpm/clients/claude_desktop.py renamed to src/mcpm/clients/managers/claude_desktop.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
Claude Desktop integration utilities for MCP
33
"""
44

5-
import os
65
import logging
7-
import platform
8-
from typing import Dict, Any
6+
import os
7+
from typing import Any, Dict
98

10-
from mcpm.clients.base import BaseClientManager
9+
from mcpm.clients.base import JSONClientManager
1110

1211
logger = logging.getLogger(__name__)
1312

1413

15-
class ClaudeDesktopManager(BaseClientManager):
14+
class ClaudeDesktopManager(JSONClientManager):
1615
"""Manages Claude Desktop MCP server configurations"""
1716

1817
# Client information

src/mcpm/clients/cursor.py renamed to src/mcpm/clients/managers/cursor.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
Cursor integration utilities for MCP
33
"""
44

5-
import os
65
import logging
7-
import platform
8-
from typing import Dict, Any
6+
import os
7+
from typing import Any, Dict
98

10-
from mcpm.clients.base import BaseClientManager
9+
from mcpm.clients.base import JSONClientManager
1110

1211
logger = logging.getLogger(__name__)
1312

1413

15-
class CursorManager(BaseClientManager):
14+
class CursorManager(JSONClientManager):
1615
"""Manages Cursor MCP server configurations"""
1716

1817
# Client information

src/mcpm/clients/windsurf.py renamed to src/mcpm/clients/managers/windsurf.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@
22
Windsurf integration utilities for MCP
33
"""
44

5-
import os
65
import logging
7-
import platform
8-
from typing import Dict, Any, Union, Optional
9-
10-
from mcpm.utils.server_config import ServerConfig
6+
import os
117

12-
from mcpm.clients.base import BaseClientManager
8+
from mcpm.clients.base import JSONClientManager
139

1410
logger = logging.getLogger(__name__)
1511

1612

17-
class WindsurfManager(BaseClientManager):
13+
class WindsurfManager(JSONClientManager):
1814
"""Manages Windsurf MCP server configurations"""
1915

2016
# Client information

src/mcpm/commands/add.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
Add command for adding MCP servers directly to client configurations
33
"""
44

5-
import os
65
import json
6+
import os
77

88
import click
99
from rich.console import Console
1010
from rich.progress import Progress, SpinnerColumn, TextColumn
1111
from rich.prompt import Confirm
1212

13+
from mcpm.clients.client_registry import ClientRegistry
1314
from mcpm.utils.repository import RepositoryManager
1415
from mcpm.utils.server_config import ServerConfig
15-
from mcpm.utils.client_registry import ClientRegistry
1616

1717
console = Console()
1818
repo_manager = RepositoryManager()

0 commit comments

Comments
 (0)