Skip to content

Commit 85a3492

Browse files
niechenclaude
andauthored
feat: change mcpm edit arguments from CSV to space-separated format (#213)
* feat: change mcpm edit arguments from CSV to space-separated format - Updated argument parsing from comma-separated to space-separated - Changed prompts from "Arguments (comma-separated)" to "Arguments (space-separated)" - Modified parsing logic from .split(",") to .split() - Updated table display format to show space-separated arguments - Updated tests to expect space-separated output Fixes #212 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: use shlex.split() for proper argument parsing with spaces - Import shlex module for proper shell-like argument parsing - Replace .split() with shlex.split() for argument parsing - Update prompts to indicate quote support for arguments with spaces - Add tests for arguments containing spaces - Add unit tests to verify shlex parsing behavior This addresses the review feedback about handling arguments with spaces correctly. Arguments like "path with spaces" are now properly parsed as single arguments. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 5d015df commit 85a3492

File tree

2 files changed

+63
-14
lines changed

2 files changed

+63
-14
lines changed

src/mcpm/commands/edit.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import os
6+
import shlex
67
import subprocess
78
import sys
89
from typing import Any, Dict, Optional
@@ -80,7 +81,7 @@ def edit(server_name, new, editor):
8081

8182
if isinstance(server_config, STDIOServerConfig):
8283
table.add_row("Command", server_config.command)
83-
table.add_row("Arguments", ", ".join(server_config.args) if server_config.args else "[dim]None[/]")
84+
table.add_row("Arguments", " ".join(server_config.args) if server_config.args else "[dim]None[/]")
8485
table.add_row(
8586
"Environment",
8687
", ".join(f"{k}={v}" for k, v in server_config.env.items()) if server_config.env else "[dim]None[/]",
@@ -187,12 +188,12 @@ def interactive_server_edit(server_config) -> Optional[Dict[str, Any]]:
187188
keybindings={"interrupt": [{"key": "escape"}]},
188189
).execute()
189190

190-
# Arguments as comma-separated string
191-
current_args = ", ".join(server_config.args) if server_config.args else ""
191+
# Arguments as space-separated string
192+
current_args = " ".join(server_config.args) if server_config.args else ""
192193
answers["args"] = inquirer.text(
193-
message="Arguments (comma-separated):",
194+
message="Arguments (space-separated, quotes supported):",
194195
default=current_args,
195-
instruction="(Leave empty for no arguments)",
196+
instruction="(Leave empty for no arguments, use quotes for args with spaces)",
196197
keybindings={"interrupt": [{"key": "escape"}]},
197198
).execute()
198199

@@ -237,7 +238,7 @@ def interactive_server_edit(server_config) -> Optional[Dict[str, Any]]:
237238

238239
if isinstance(server_config, STDIOServerConfig):
239240
console.print(f"Command: [cyan]{server_config.command}[/] → [cyan]{answers['command']}[/]")
240-
new_args = [arg.strip() for arg in answers["args"].split(",") if arg.strip()] if answers["args"] else []
241+
new_args = shlex.split(answers["args"]) if answers["args"] else []
241242
console.print(f"Arguments: [cyan]{server_config.args}[/] → [cyan]{new_args}[/]")
242243

243244
new_env = {}
@@ -298,7 +299,7 @@ def apply_interactive_changes(server_config, interactive_result):
298299

299300
# Parse arguments
300301
if answers["args"].strip():
301-
server_config.args = [arg.strip() for arg in answers["args"].split(",") if arg.strip()]
302+
server_config.args = shlex.split(answers["args"])
302303
else:
303304
server_config.args = []
304305

@@ -381,9 +382,7 @@ def _create_new_server():
381382
server_config = STDIOServerConfig(
382383
name=server_name,
383384
command=result["answers"]["command"],
384-
args=[arg.strip() for arg in result["answers"]["args"].split(",") if arg.strip()]
385-
if result["answers"]["args"]
386-
else [],
385+
args=shlex.split(result["answers"]["args"]) if result["answers"]["args"] else [],
387386
env={},
388387
)
389388

@@ -462,8 +461,8 @@ def _interactive_new_server_form() -> Optional[Dict[str, Any]]:
462461
).execute()
463462

464463
answers["args"] = inquirer.text(
465-
message="Arguments (comma-separated):",
466-
instruction="(Leave empty for no arguments)",
464+
message="Arguments (space-separated, quotes supported):",
465+
instruction="(Leave empty for no arguments, use quotes for args with spaces)",
467466
keybindings={"interrupt": [{"key": "escape"}]},
468467
).execute()
469468

@@ -497,7 +496,7 @@ def _interactive_new_server_form() -> Optional[Dict[str, Any]]:
497496

498497
if answers["type"] == "stdio":
499498
console.print(f"Command: [cyan]{answers['command']}[/]")
500-
new_args = [arg.strip() for arg in answers["args"].split(",") if arg.strip()] if answers["args"] else []
499+
new_args = shlex.split(answers["args"]) if answers["args"] else []
501500
console.print(f"Arguments: [cyan]{new_args}[/]")
502501

503502
new_env = {}

tests/test_edit.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Tests for the edit command
33
"""
44

5+
import shlex
56
from unittest.mock import Mock
67

78
from click.testing import CliRunner
@@ -44,13 +45,39 @@ def test_edit_server_interactive_fallback(monkeypatch):
4445
assert result.exit_code == 0 # CliRunner may not properly handle our return codes
4546
assert "Current Configuration for 'test-server'" in result.output
4647
assert "test-cmd" in result.output
47-
assert "arg1, arg2" in result.output
48+
assert "arg1 arg2" in result.output
4849
assert "KEY=value" in result.output
4950
assert "test-profile" in result.output
5051
assert "Interactive editing not available" in result.output
5152
assert "This command requires a terminal for interactive input" in result.output
5253

5354

55+
def test_edit_server_with_spaces_in_args(monkeypatch):
56+
"""Test display of arguments with spaces in the fallback view."""
57+
test_server = STDIOServerConfig(
58+
name="test-server",
59+
command="test-cmd",
60+
args=["arg with spaces", "another arg", "--flag=value with spaces"],
61+
env={"KEY": "value"},
62+
profile_tags=["test-profile"],
63+
)
64+
65+
mock_global_config = Mock()
66+
mock_global_config.get_server.return_value = test_server
67+
monkeypatch.setattr("mcpm.commands.edit.global_config_manager", mock_global_config)
68+
69+
runner = CliRunner()
70+
result = runner.invoke(edit, ["test-server"])
71+
72+
# In test environment, interactive mode falls back and shows message
73+
assert result.exit_code == 0
74+
assert "Current Configuration for 'test-server'" in result.output
75+
assert "test-cmd" in result.output
76+
# Check that arguments with spaces are displayed correctly
77+
assert "arg with spaces another arg --flag=value with spaces" in result.output
78+
assert "Interactive editing not available" in result.output
79+
80+
5481
def test_edit_command_help():
5582
"""Test the edit command help output."""
5683
runner = CliRunner()
@@ -94,3 +121,26 @@ def test_edit_editor_flag(monkeypatch):
94121

95122
# Verify subprocess.run was called with correct arguments
96123
mock_subprocess.assert_called_once_with(["open", "/tmp/test_servers.json"])
124+
125+
126+
def test_shlex_argument_parsing():
127+
"""Test that shlex correctly parses arguments with spaces."""
128+
# Test basic space-separated arguments
129+
result = shlex.split("arg1 arg2 arg3")
130+
assert result == ["arg1", "arg2", "arg3"]
131+
132+
# Test quoted arguments with spaces
133+
result = shlex.split('arg1 "arg with spaces" arg3')
134+
assert result == ["arg1", "arg with spaces", "arg3"]
135+
136+
# Test mixed quotes
137+
result = shlex.split("arg1 'arg with spaces' --flag=\"value with spaces\"")
138+
assert result == ["arg1", "arg with spaces", "--flag=value with spaces"]
139+
140+
# Test empty string
141+
result = shlex.split("")
142+
assert result == []
143+
144+
# Test single argument with spaces
145+
result = shlex.split('"single arg with spaces"')
146+
assert result == ["single arg with spaces"]

0 commit comments

Comments
 (0)