Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions src/mcpm/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,10 @@ def main(ctx, help_flag):
commands_table.add_row("[yellow]server[/]")
commands_table.add_row(" [cyan]search[/]", "Search available MCP servers.")
commands_table.add_row(" [cyan]add[/]", "Add an MCP server directly to a client.")
commands_table.add_row(" [cyan]cp[/]")
commands_table.add_row(" [cyan]copy[/]", "Copy a server from one client/profile to another.")
commands_table.add_row(" [cyan]mv[/]")
commands_table.add_row(" [cyan]move[/]", "Move a server from one client/profile to another.")
commands_table.add_row(" [cyan]rm[/]")
commands_table.add_row(" [cyan]remove[/]", "Remove an installed MCP server.")
commands_table.add_row(" [cyan]ls[/]")
commands_table.add_row(" [cyan]list[/]", "List all installed MCP servers.")
commands_table.add_row(" [cyan]cp[/]", "Copy a server from one client/profile to another.")
commands_table.add_row(" [cyan]mv[/]", "Move a server from one client/profile to another.")
commands_table.add_row(" [cyan]rm[/]", "Remove an installed MCP server.")
commands_table.add_row(" [cyan]ls[/]", "List all installed MCP servers.")
commands_table.add_row(" [cyan]stash[/]", "Temporarily store a server configuration aside.")
commands_table.add_row(" [cyan]pop[/]", "Restore a previously stashed server configuration.")

Expand All @@ -167,15 +163,13 @@ def main(ctx, help_flag):

# Additional helpful information
console.print("")
console.print("[italic]Run [bold]mcpm -h[/] for more information on a command.[/]")
console.print("[italic]Run [bold]mcpm COMMAND -h[/] for more information on a command.[/]")


# Register commands
main.add_command(search.search)
main.add_command(remove.remove)
main.add_command(remove.remove, name="rm")
main.add_command(add.add)
main.add_command(list.list)
main.add_command(list.list, name="ls")

main.add_command(stash.stash)
Expand All @@ -185,9 +179,7 @@ def main(ctx, help_flag):
main.add_command(config.config)
main.add_command(inspector.inspector, name="inspector")
main.add_command(profile.profile, name="profile")
main.add_command(transfer.move)
main.add_command(transfer.move, name="mv")
main.add_command(transfer.copy)
main.add_command(transfer.copy, name="cp")
main.add_command(profile.activate)
main.add_command(profile.deactivate)
Expand Down
80 changes: 80 additions & 0 deletions src/mcpm/clients/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from ruamel.yaml import YAML

from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig
from mcpm.utils.config import ROUTER_SERVER_NAME
from mcpm.utils.router_server import format_server_url

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -132,6 +134,30 @@ def is_client_installed(self) -> bool:
"""
pass

@abc.abstractmethod
def activate_profile(self, profile_name: str, router_config: Dict[str, Any]) -> bool:
"""
Activate a profile in the client config

Args:
profile_name: Name of the profile
router_config: Router configuration

Returns:
bool: Success or failure
"""
pass

@abc.abstractmethod
def deactivate_profile(self) -> bool:
"""
Deactivate a profile in the client config

Returns:
bool: Success or failure
"""
pass


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

def activate_profile(self, profile_name: str, router_config: Dict[str, Any]) -> bool:
"""Activate a profile in the client config

Args:
profile_name: Name of the profile

Returns:
bool: Success or failure
"""
host = router_config["host"]
port = router_config["port"]
default_base_url = f"http://{host}:{port}/sse"

server_config = self._format_router_server(profile_name, default_base_url)
return self.add_server(server_config)

def _format_router_server(self, profile_name, base_url) -> ServerConfig:
return format_server_url(self.client_key, profile_name, base_url)

def deactivate_profile(self) -> bool:
"""Deactivate a profile in the client config

Returns:
bool: Success or failure
"""
return self.remove_server(ROUTER_SERVER_NAME)


class YAMLClientManager(BaseClientManager):
"""
Expand Down Expand Up @@ -589,3 +642,30 @@ def is_client_installed(self) -> bool:
"""
# Check if the config directory exists
return os.path.isdir(os.path.dirname(self.config_path))

def activate_profile(self, profile_name: str, router_config: Dict[str, Any]) -> bool:
"""Activate a profile in the client config

Args:
profile_name: Name of the profile

Returns:
bool: Success or failure
"""
host = router_config["host"]
port = router_config["port"]
default_base_url = f"http://{host}:{port}/sse"

server_config = self._format_router_server(profile_name, default_base_url)
return self.add_server(server_config)

def _format_router_server(self, profile_name, base_url) -> ServerConfig:
return format_server_url(self.client_key, profile_name, base_url)

def deactivate_profile(self) -> bool:
"""Deactivate a profile in the client config

Returns:
bool: Success or failure
"""
return self.remove_server(ROUTER_SERVER_NAME)
35 changes: 35 additions & 0 deletions src/mcpm/clients/client_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from mcpm.clients.managers.fiveire import FiveireManager
from mcpm.clients.managers.goose import GooseClientManager
from mcpm.clients.managers.windsurf import WindsurfManager
from mcpm.utils.config import ConfigManager
from mcpm.utils.scope import CLIENT_PREFIX, PROFILE_PREFIX

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -205,3 +206,37 @@ def determine_active_scope(cls) -> str | None:
if client:
return f"{CLIENT_PREFIX}{client}"
return None

@classmethod
def activate_profile(cls, client_name: str, profile_name: str) -> bool:
"""
Activate a profile in the client config

Args:
client_name: Name of the client
profile_name: Name of the profile

Returns:
bool: Success or failure
"""
router_config = ConfigManager().get_router_config()
client = cls.get_client_manager(client_name)
if client is None:
return False
return client.activate_profile(profile_name, router_config)

@classmethod
def deactivate_profile(cls, client_name: str) -> bool:
"""
Deactivate a profile in the client config

Args:
profile_name: Name of the profile

Returns:
bool: Success or failure
"""
client = cls.get_client_manager(client_name)
if client is None:
return False
return client.deactivate_profile()
5 changes: 5 additions & 0 deletions src/mcpm/clients/managers/claude_desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from typing import Any, Dict

from mcpm.clients.base import JSONClientManager
from mcpm.schemas.server_config import ServerConfig
from mcpm.utils.router_server import format_server_url_with_proxy_param

logger = logging.getLogger(__name__)

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

def _format_router_server(self, profile_name, base_url) -> ServerConfig:
return format_server_url_with_proxy_param(self.client_key, profile_name, base_url)

# Uses base class implementation of remove_server

# Uses base class implementation of get_server
Expand Down
4 changes: 4 additions & 0 deletions src/mcpm/clients/managers/continue_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from mcpm.clients.base import YAMLClientManager
from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig
from mcpm.utils.router_server import format_server_url_with_proxy_headers

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -203,3 +204,6 @@ def from_client_format(self, server_name: str, client_config: Dict[str, Any]) ->
}
server_data.update(client_config)
return TypeAdapter(ServerConfig).validate_python(server_data)

def _format_router_server(self, profile_name, base_url) -> ServerConfig:
return format_server_url_with_proxy_headers(self.client_key, profile_name, base_url)
2 changes: 2 additions & 0 deletions src/mcpm/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@


@click.group()
@click.help_option("-h", "--help")
def config():
"""Manage MCPM configuration.

Expand All @@ -21,6 +22,7 @@ def config():


@config.command()
@click.help_option("-h", "--help")
def clear_cache():
"""Clear the local repository cache.

Expand Down
5 changes: 4 additions & 1 deletion src/mcpm/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@

@click.command(name="list")
@click.option("--target", "-t", help="Target to list servers from")
@click.help_option("-h", "--help")
def list(target: str | None = None):
"""List all installed MCP servers.

Examples:

\b
mcpm list
mcpm list -t <scope>
mcpm list -t @cursor
"""
if target is None:
target = ClientRegistry.determine_active_scope()
Expand Down
Loading