Skip to content

Commit f9dff9a

Browse files
feat: activate profile for client
1 parent 0571fec commit f9dff9a

File tree

8 files changed

+275
-66
lines changed

8 files changed

+275
-66
lines changed

src/mcpm/clients/base.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from ruamel.yaml import YAML
1414

1515
from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig
16+
from mcpm.utils.config import ROUTER_SERVER_NAME
17+
from mcpm.utils.router_server import format_server_url
1618

1719
logger = logging.getLogger(__name__)
1820

@@ -132,6 +134,30 @@ def is_client_installed(self) -> bool:
132134
"""
133135
pass
134136

137+
@abc.abstractmethod
138+
def activate_profile(self, profile_name: str, router_config: Dict[str, Any]) -> bool:
139+
"""
140+
Activate a profile in the client config
141+
142+
Args:
143+
profile_name: Name of the profile
144+
router_config: Router configuration
145+
146+
Returns:
147+
bool: Success or failure
148+
"""
149+
pass
150+
151+
@abc.abstractmethod
152+
def deactivate_profile(self) -> bool:
153+
"""
154+
Deactivate a profile in the client config
155+
156+
Returns:
157+
bool: Success or failure
158+
"""
159+
pass
160+
135161

136162
class JSONClientManager(BaseClientManager):
137163
"""
@@ -350,6 +376,33 @@ def is_client_installed(self) -> bool:
350376
# Can be overridden by subclasses
351377
return os.path.isdir(os.path.dirname(self.config_path))
352378

379+
def activate_profile(self, profile_name: str, router_config: Dict[str, Any]) -> bool:
380+
"""Activate a profile in the client config
381+
382+
Args:
383+
profile_name: Name of the profile
384+
385+
Returns:
386+
bool: Success or failure
387+
"""
388+
host = router_config["host"]
389+
port = router_config["port"]
390+
default_base_url = f"http://{host}:{port}/sse"
391+
392+
server_config = self._format_router_server(profile_name, default_base_url)
393+
return self.add_server(server_config)
394+
395+
def _format_router_server(self, profile_name, base_url) -> ServerConfig:
396+
return format_server_url(self.client_key, profile_name, base_url)
397+
398+
def deactivate_profile(self) -> bool:
399+
"""Deactivate a profile in the client config
400+
401+
Returns:
402+
bool: Success or failure
403+
"""
404+
return self.remove_server(ROUTER_SERVER_NAME)
405+
353406

354407
class YAMLClientManager(BaseClientManager):
355408
"""
@@ -589,3 +642,30 @@ def is_client_installed(self) -> bool:
589642
"""
590643
# Check if the config directory exists
591644
return os.path.isdir(os.path.dirname(self.config_path))
645+
646+
def activate_profile(self, profile_name: str, router_config: Dict[str, Any]) -> bool:
647+
"""Activate a profile in the client config
648+
649+
Args:
650+
profile_name: Name of the profile
651+
652+
Returns:
653+
bool: Success or failure
654+
"""
655+
host = router_config["host"]
656+
port = router_config["port"]
657+
default_base_url = f"http://{host}:{port}/sse"
658+
659+
server_config = self._format_router_server(profile_name, default_base_url)
660+
return self.add_server(server_config)
661+
662+
def _format_router_server(self, profile_name, base_url) -> ServerConfig:
663+
return format_server_url(self.client_key, profile_name, base_url)
664+
665+
def deactivate_profile(self) -> bool:
666+
"""Deactivate a profile in the client config
667+
668+
Returns:
669+
bool: Success or failure
670+
"""
671+
return self.remove_server(ROUTER_SERVER_NAME)

src/mcpm/clients/client_registry.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from mcpm.clients.managers.fiveire import FiveireManager
1818
from mcpm.clients.managers.goose import GooseClientManager
1919
from mcpm.clients.managers.windsurf import WindsurfManager
20+
from mcpm.utils.config import ConfigManager
2021
from mcpm.utils.scope import CLIENT_PREFIX, PROFILE_PREFIX
2122

2223
logger = logging.getLogger(__name__)
@@ -205,3 +206,37 @@ def determine_active_scope(cls) -> str | None:
205206
if client:
206207
return f"{CLIENT_PREFIX}{client}"
207208
return None
209+
210+
@classmethod
211+
def activate_profile(cls, client_name: str, profile_name: str) -> bool:
212+
"""
213+
Activate a profile in the client config
214+
215+
Args:
216+
client_name: Name of the client
217+
profile_name: Name of the profile
218+
219+
Returns:
220+
bool: Success or failure
221+
"""
222+
router_config = ConfigManager().get_router_config()
223+
client = cls.get_client_manager(client_name)
224+
if client is None:
225+
return False
226+
return client.activate_profile(profile_name, router_config)
227+
228+
@classmethod
229+
def deactivate_profile(cls, client_name: str) -> bool:
230+
"""
231+
Deactivate a profile in the client config
232+
233+
Args:
234+
profile_name: Name of the profile
235+
236+
Returns:
237+
bool: Success or failure
238+
"""
239+
client = cls.get_client_manager(client_name)
240+
if client is None:
241+
return False
242+
return client.deactivate_profile()

src/mcpm/clients/managers/claude_desktop.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from typing import Any, Dict
88

99
from mcpm.clients.base import JSONClientManager
10+
from mcpm.schemas.server_config import ServerConfig
11+
from mcpm.utils.router_server import format_server_url_with_proxy_param
1012

1113
logger = logging.getLogger(__name__)
1214

@@ -111,6 +113,9 @@ def is_server_disabled(self, server_name: str) -> bool:
111113
config = self._load_config()
112114
return "disabledServers" in config and server_name in config["disabledServers"]
113115

116+
def _format_router_server(self, profile_name, base_url) -> ServerConfig:
117+
return format_server_url_with_proxy_param(self.client_key, profile_name, base_url)
118+
114119
# Uses base class implementation of remove_server
115120

116121
# Uses base class implementation of get_server

src/mcpm/clients/managers/continue_extension.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from mcpm.clients.base import YAMLClientManager
1212
from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig
13+
from mcpm.utils.router_server import format_server_url_with_proxy_headers
1314

1415
logger = logging.getLogger(__name__)
1516

@@ -203,3 +204,6 @@ def from_client_format(self, server_name: str, client_config: Dict[str, Any]) ->
203204
}
204205
server_data.update(client_config)
205206
return TypeAdapter(ServerConfig).validate_python(server_data)
207+
208+
def _format_router_server(self, profile_name, base_url) -> ServerConfig:
209+
return format_server_url_with_proxy_headers(self.client_key, profile_name, base_url)

src/mcpm/commands/profile.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from mcpm.clients.client_registry import ClientRegistry
66
from mcpm.profile.profile_config import ProfileConfigManager
77
from mcpm.schemas.server_config import STDIOServerConfig
8+
from mcpm.utils.config import ConfigManager
89

910
profile_config_manager = ProfileConfigManager()
1011
console = Console()
@@ -19,9 +20,9 @@ def profile():
1920

2021
@click.command()
2122
@click.argument("profile_name")
22-
@click.option("--client", "-c", default="client", help="Client of the profile")
23+
@click.option("--client", "-c", help="Client of the profile")
2324
@click.help_option("-h", "--help")
24-
def activate(profile_name, client):
25+
def activate(profile_name, client=None):
2526
"""Activate a profile.
2627
2728
Sets the specified profile as the active profile.
@@ -33,18 +34,37 @@ def activate(profile_name, client):
3334

3435
# Set the active profile
3536
client_registry = ClientRegistry()
36-
if client_registry.set_active_profile(profile_name):
37+
config_manager = ConfigManager()
38+
39+
if client:
40+
console.print(f"[bold cyan]Activating profile '{profile_name}' in client '{client}'...[/]")
41+
client_manager = ClientRegistry.get_client_manager(client)
42+
if client_manager is None:
43+
console.print(f"[bold red]Error:[/] Client '{client}' not found.")
44+
return
45+
success = client_manager.activate_profile(profile_name, config_manager.get_router_config())
46+
else:
47+
client = ClientRegistry.get_active_client()
48+
if client is None:
49+
console.print("[bold yellow]No active client found.[/]\n")
50+
return
51+
console.print(f"[bold cyan]Activating profile '{profile_name}' in active client '{client}'...[/]")
52+
client_manager = ClientRegistry.get_client_manager(client)
53+
if client_manager is None:
54+
console.print(f"[bold red]Error:[/] Client '{client}' not found.")
55+
return
56+
success = client_manager.activate_profile(profile_name, config_manager.get_router_config())
57+
if success:
58+
client_registry.set_active_profile(profile_name)
3759
console.print(f"\n[green]Profile '{profile_name}' activated successfully.[/]\n")
3860
else:
3961
console.print(f"[bold red]Error:[/] Failed to activate profile '{profile_name}'.")
4062

41-
# TODO: add url to the client config
42-
4363

4464
@click.command()
45-
@click.option("--client", "-c", default="client", help="Client of the profile")
65+
@click.option("--client", "-c", help="Client of the profile")
4666
@click.help_option("-h", "--help")
47-
def deactivate(client):
67+
def deactivate(client=None):
4868
"""Deactivate a profile.
4969
5070
Unsets the active profile.
@@ -56,12 +76,30 @@ def deactivate(client):
5676
return
5777
console.print(f"\n[green]Deactivating profile '{active_profile}'...[/]")
5878
client_registry = ClientRegistry()
59-
if client_registry.set_active_profile(None):
60-
console.print(f"\n[green]Profile '{active_profile}' deactivated successfully.[/]\n")
61-
else:
62-
console.print(f"[bold red]Error:[/] Failed to deactivate profile '{active_profile}'.")
6379

64-
# TODO: remove url from the client config
80+
if client:
81+
console.print(f"[bold cyan]Deactivating profile '{active_profile}' in client '{client}'...[/]")
82+
client_manager = ClientRegistry.get_client_manager(client)
83+
if client_manager is None:
84+
console.print(f"[bold red]Error:[/] Client '{client}' not found.")
85+
return
86+
success = client_manager.deactivate_profile()
87+
else:
88+
client = ClientRegistry.get_active_client()
89+
if client is None:
90+
console.print("[bold yellow]No active client found.[/]\n")
91+
return
92+
console.print(f"[bold cyan]Deactivating profile '{active_profile}' in active client '{client}'...[/]")
93+
client_manager = ClientRegistry.get_client_manager(client)
94+
if client_manager is None:
95+
console.print(f"[bold red]Error:[/] Client '{client}' not found.")
96+
return
97+
success = client_manager.deactivate_profile()
98+
if success:
99+
client_registry.set_active_profile(None)
100+
console.print(f"\n[yellow]Profile '{active_profile}' deactivated successfully.[/]\n")
101+
else:
102+
console.print(f"[bold red]Error:[/] Failed to deactivate profile '{active_profile}' in client '{client}'.")
65103

66104

67105
@profile.command(name="ls")

0 commit comments

Comments
 (0)