Skip to content

Commit e7906cd

Browse files
committed
Add working
1 parent 9c07d62 commit e7906cd

File tree

8 files changed

+536
-533
lines changed

8 files changed

+536
-533
lines changed

src/mcpm/cli.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from mcpm import __version__
1212
from mcpm.commands import (
1313
search,
14-
install,
1514
remove,
1615
list_servers,
1716
edit,
@@ -108,7 +107,6 @@ def main(ctx, help_flag):
108107
commands_table.add_row(" [cyan]client[/]", "Manage the active MCPM client.")
109108
commands_table.add_row(" [cyan]edit[/]", "View or edit the active MCPM client's configuration file.")
110109
commands_table.add_row(" [cyan]inspector[/]", "Launch the MCPM Inspector UI to examine servers.")
111-
commands_table.add_row(" [cyan]install[/]", "[yellow][DEPRECATED][/] Install an MCP server (use add instead).")
112110
commands_table.add_row(" [cyan]list[/]", "List all installed MCP servers.")
113111
commands_table.add_row(" [cyan]remove[/]", "Remove an installed MCP server.")
114112
commands_table.add_row(" [cyan]search[/]", "Search available MCP servers.")
@@ -124,7 +122,6 @@ def main(ctx, help_flag):
124122

125123
# Register commands
126124
main.add_command(search.search)
127-
main.add_command(install.install)
128125
main.add_command(remove.remove)
129126
main.add_command(add.add)
130127
main.add_command(list_servers.list)

src/mcpm/clients/claude_desktop.py

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import os
66
import logging
77
import platform
8-
from typing import Dict, Any
8+
from typing import Dict, Any, Optional, List
99

1010
from mcpm.clients.base import BaseClientManager
1111
from mcpm.utils.server_config import ServerConfig
@@ -74,12 +74,60 @@ def _convert_to_client_format(self, server_config: ServerConfig) -> Dict[str, An
7474
"""Convert ServerConfig to Claude Desktop format
7575
7676
Args:
77-
server_config: StandardServer configuration
77+
server_config: ServerConfig object
7878
7979
Returns:
8080
Dict containing Claude Desktop-specific configuration
8181
"""
82-
return server_config.to_claude_desktop_format()
82+
# Base result containing essential execution information
83+
result = {
84+
"command": server_config.command,
85+
"args": server_config.args,
86+
}
87+
88+
# Add filtered environment variables if present
89+
non_empty_env = server_config.get_filtered_env_vars()
90+
if non_empty_env:
91+
result["env"] = non_empty_env
92+
93+
# Add additional metadata fields for display in Claude Desktop
94+
# Fields that are None will be automatically excluded by JSON serialization
95+
for field in ["name", "display_name", "description", "version", "status", "path", "install_date"]:
96+
value = getattr(server_config, field, None)
97+
if value is not None:
98+
result[field] = value
99+
100+
return result
101+
102+
@classmethod
103+
def from_claude_desktop_format(cls, server_name: str, client_config: Dict[str, Any]) -> ServerConfig:
104+
"""Convert Claude Desktop format to ServerConfig
105+
106+
Args:
107+
server_name: Name of the server
108+
client_config: Claude Desktop-specific configuration
109+
110+
Returns:
111+
ServerConfig object
112+
"""
113+
# Create a dictionary that ServerConfig.from_dict can work with
114+
server_data = {
115+
"name": server_name,
116+
"command": client_config.get("command", ""),
117+
"args": client_config.get("args", []),
118+
}
119+
120+
# Add environment variables if present
121+
if "env" in client_config:
122+
server_data["env_vars"] = client_config["env"]
123+
124+
# Add additional metadata fields if present
125+
for field in ["display_name", "description", "version", "status", "path", "install_date",
126+
"package", "installation_method", "installation_type"]:
127+
if field in client_config:
128+
server_data[field] = client_config[field]
129+
130+
return ServerConfig.from_dict(server_data)
83131

84132
def _convert_from_client_format(self, server_name: str, client_config: Dict[str, Any]) -> ServerConfig:
85133
"""Convert Claude Desktop format to ServerConfig
@@ -91,21 +139,69 @@ def _convert_from_client_format(self, server_name: str, client_config: Dict[str,
91139
Returns:
92140
ServerConfig object
93141
"""
94-
return ServerConfig.from_claude_desktop_format(server_name, client_config)
142+
return self.from_claude_desktop_format(server_name, client_config)
95143

96144
def remove_server(self, server_name: str) -> bool:
97-
"""Remove an MCP server from Claude Desktop config"""
145+
"""Remove an MCP server from Claude Desktop config
146+
147+
Args:
148+
server_name: Name of the server to remove
149+
150+
Returns:
151+
bool: Success or failure
152+
"""
98153
config = self._load_config()
99154

100-
if "mcpServers" not in config or server_name not in config["mcpServers"]:
101-
logger.warning(f"Server not found in Claude Desktop config: {server_name}")
155+
# Check if mcpServers exists
156+
if "mcpServers" not in config:
157+
logger.warning(f"Cannot remove server {server_name}: mcpServers section doesn't exist")
158+
return False
159+
160+
# Check if the server exists
161+
if server_name not in config["mcpServers"]:
162+
logger.warning(f"Server {server_name} not found in Claude Desktop config")
102163
return False
103164

104165
# Remove the server
105166
del config["mcpServers"][server_name]
106167

107168
return self._save_config(config)
108169

109-
def is_claude_desktop_installed(self) -> bool:
110-
"""Check if Claude Desktop is installed"""
111-
return self.is_client_installed()
170+
def get_server(self, server_name: str) -> Optional[ServerConfig]:
171+
"""Get a server configuration from Claude Desktop
172+
173+
Args:
174+
server_name: Name of the server
175+
176+
Returns:
177+
ServerConfig object if found, None otherwise
178+
"""
179+
config = self._load_config()
180+
181+
# Check if mcpServers exists
182+
if "mcpServers" not in config:
183+
logger.warning(f"Cannot get server {server_name}: mcpServers section doesn't exist")
184+
return None
185+
186+
# Check if the server exists
187+
if server_name not in config["mcpServers"]:
188+
logger.debug(f"Server {server_name} not found in Claude Desktop config")
189+
return None
190+
191+
# Get the server config and convert to StandardServer
192+
client_config = config["mcpServers"][server_name]
193+
return self._convert_from_client_format(server_name, client_config)
194+
195+
def list_servers(self) -> List[str]:
196+
"""List all MCP servers in Claude Desktop config
197+
198+
Returns:
199+
List of server names
200+
"""
201+
config = self._load_config()
202+
203+
# Check if mcpServers exists
204+
if "mcpServers" not in config:
205+
return []
206+
207+
return list(config["mcpServers"].keys())

src/mcpm/clients/cursor.py

Lines changed: 109 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,28 @@
11
"""
2-
Cursor client configuration for MCP
2+
Cursor integration utilities for MCP
33
"""
44

55
import os
66
import logging
7-
from typing import Dict, Any
7+
import platform
8+
from typing import Dict, Any, Optional, List
89

910
from mcpm.clients.base import BaseClientManager
1011
from mcpm.utils.server_config import ServerConfig
1112

12-
# Cursor stores MCP configuration in:
13-
# - Project config: .cursor/mcp.json in the project directory
14-
# - Global config: ~/.cursor/mcp.json in the home directory
15-
16-
# Global config path for Cursor
17-
HOME_DIR = os.path.expanduser("~")
18-
CURSOR_CONFIG_PATH = os.path.join(HOME_DIR, ".cursor", "mcp.json")
19-
2013
logger = logging.getLogger(__name__)
2114

22-
# Get the project config path for Cursor
23-
def get_project_config_path(project_dir: str) -> str:
24-
"""
25-
Get the project-specific MCP configuration path for Cursor
26-
27-
Args:
28-
project_dir (str): Project directory path
29-
30-
Returns:
31-
str: Path to the project-specific MCP configuration file
32-
"""
33-
return os.path.join(project_dir, ".cursor", "mcp.json")
34-
15+
# Cursor config paths based on platform
16+
if platform.system() == "Darwin": # macOS
17+
CURSOR_CONFIG_PATH = os.path.expanduser("~/Library/Application Support/Cursor/User/mcp_config.json")
18+
elif platform.system() == "Windows":
19+
CURSOR_CONFIG_PATH = os.path.join(os.environ.get("APPDATA", ""), "Cursor", "User", "mcp_config.json")
20+
else:
21+
# Linux
22+
CURSOR_CONFIG_PATH = os.path.expanduser("~/.config/Cursor/User/mcp_config.json")
3523

3624
class CursorManager(BaseClientManager):
37-
"""Manages Cursor client configuration for MCP"""
25+
"""Manages Cursor MCP server configurations"""
3826

3927
def __init__(self, config_path: str = CURSOR_CONFIG_PATH):
4028
super().__init__(config_path)
@@ -86,12 +74,60 @@ def _convert_to_client_format(self, server_config: ServerConfig) -> Dict[str, An
8674
"""Convert ServerConfig to Cursor format
8775
8876
Args:
89-
server_config: StandardServer configuration
77+
server_config: ServerConfig object
9078
9179
Returns:
9280
Dict containing Cursor-specific configuration
9381
"""
94-
return server_config.to_cursor_format()
82+
# Base result containing essential execution information
83+
result = {
84+
"command": server_config.command,
85+
"args": server_config.args,
86+
}
87+
88+
# Add filtered environment variables if present
89+
non_empty_env = server_config.get_filtered_env_vars()
90+
if non_empty_env:
91+
result["env"] = non_empty_env
92+
93+
# Add additional metadata fields for display in Cursor
94+
# Fields that are None will be automatically excluded by JSON serialization
95+
for field in ["name", "display_name", "description", "version", "status", "path", "install_date"]:
96+
value = getattr(server_config, field, None)
97+
if value is not None:
98+
result[field] = value
99+
100+
return result
101+
102+
@classmethod
103+
def from_cursor_format(cls, server_name: str, client_config: Dict[str, Any]) -> ServerConfig:
104+
"""Convert Cursor format to ServerConfig
105+
106+
Args:
107+
server_name: Name of the server
108+
client_config: Cursor-specific configuration
109+
110+
Returns:
111+
ServerConfig object
112+
"""
113+
# Create a dictionary that ServerConfig.from_dict can work with
114+
server_data = {
115+
"name": server_name,
116+
"command": client_config.get("command", ""),
117+
"args": client_config.get("args", []),
118+
}
119+
120+
# Add environment variables if present
121+
if "env" in client_config:
122+
server_data["env_vars"] = client_config["env"]
123+
124+
# Add additional metadata fields if present
125+
for field in ["display_name", "description", "version", "status", "path", "install_date",
126+
"package", "installation_method", "installation_type"]:
127+
if field in client_config:
128+
server_data[field] = client_config[field]
129+
130+
return ServerConfig.from_dict(server_data)
95131

96132
def _convert_from_client_format(self, server_name: str, client_config: Dict[str, Any]) -> ServerConfig:
97133
"""Convert Cursor format to ServerConfig
@@ -103,7 +139,7 @@ def _convert_from_client_format(self, server_name: str, client_config: Dict[str,
103139
Returns:
104140
ServerConfig object
105141
"""
106-
return ServerConfig.from_cursor_format(server_name, client_config)
142+
return self.from_cursor_format(server_name, client_config)
107143

108144
def remove_server(self, server_name: str) -> bool:
109145
"""Remove an MCP server from Cursor config
@@ -116,15 +152,56 @@ def remove_server(self, server_name: str) -> bool:
116152
"""
117153
config = self._load_config()
118154

119-
if "mcpServers" not in config or server_name not in config["mcpServers"]:
120-
logger.warning(f"Server not found in Cursor config: {server_name}")
155+
# Check if mcpServers exists
156+
if "mcpServers" not in config:
157+
logger.warning(f"Cannot remove server {server_name}: mcpServers section doesn't exist")
158+
return False
159+
160+
# Check if the server exists
161+
if server_name not in config["mcpServers"]:
162+
logger.warning(f"Server {server_name} not found in Cursor config")
121163
return False
122164

123165
# Remove the server
124166
del config["mcpServers"][server_name]
125167

126168
return self._save_config(config)
127169

128-
def is_cursor_installed(self) -> bool:
129-
"""Check if Cursor is installed"""
130-
return self.is_client_installed()
170+
def get_server(self, server_name: str) -> Optional[ServerConfig]:
171+
"""Get a server configuration from Cursor
172+
173+
Args:
174+
server_name: Name of the server
175+
176+
Returns:
177+
ServerConfig object if found, None otherwise
178+
"""
179+
config = self._load_config()
180+
181+
# Check if mcpServers exists
182+
if "mcpServers" not in config:
183+
logger.warning(f"Cannot get server {server_name}: mcpServers section doesn't exist")
184+
return None
185+
186+
# Check if the server exists
187+
if server_name not in config["mcpServers"]:
188+
logger.debug(f"Server {server_name} not found in Cursor config")
189+
return None
190+
191+
# Get the server config and convert to StandardServer
192+
client_config = config["mcpServers"][server_name]
193+
return self._convert_from_client_format(server_name, client_config)
194+
195+
def list_servers(self) -> List[str]:
196+
"""List all MCP servers in Cursor config
197+
198+
Returns:
199+
List of server names
200+
"""
201+
config = self._load_config()
202+
203+
# Check if mcpServers exists
204+
if "mcpServers" not in config:
205+
return []
206+
207+
return list(config["mcpServers"].keys())

0 commit comments

Comments
 (0)