Skip to content

Commit 97f2973

Browse files
niechenclaude
andcommitted
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]>
1 parent 4c14c1b commit 97f2973

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

src/mcpm/commands/edit.py

Lines changed: 9 additions & 8 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
@@ -190,9 +191,9 @@ def interactive_server_edit(server_config) -> Optional[Dict[str, Any]]:
190191
# Arguments as space-separated string
191192
current_args = " ".join(server_config.args) if server_config.args else ""
192193
answers["args"] = inquirer.text(
193-
message="Arguments (space-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 = answers["args"].split() 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 = answers["args"].split()
302+
server_config.args = shlex.split(answers["args"])
302303
else:
303304
server_config.args = []
304305

@@ -381,7 +382,7 @@ def _create_new_server():
381382
server_config = STDIOServerConfig(
382383
name=server_name,
383384
command=result["answers"]["command"],
384-
args=result["answers"]["args"].split() if result["answers"]["args"] else [],
385+
args=shlex.split(result["answers"]["args"]) if result["answers"]["args"] else [],
385386
env={},
386387
)
387388

@@ -460,8 +461,8 @@ def _interactive_new_server_form() -> Optional[Dict[str, Any]]:
460461
).execute()
461462

462463
answers["args"] = inquirer.text(
463-
message="Arguments (space-separated):",
464-
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)",
465466
keybindings={"interrupt": [{"key": "escape"}]},
466467
).execute()
467468

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

496497
if answers["type"] == "stdio":
497498
console.print(f"Command: [cyan]{answers['command']}[/]")
498-
new_args = answers["args"].split() if answers["args"] else []
499+
new_args = shlex.split(answers["args"]) if answers["args"] else []
499500
console.print(f"Arguments: [cyan]{new_args}[/]")
500501

501502
new_env = {}

tests/test_edit.py

Lines changed: 50 additions & 0 deletions
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
@@ -51,6 +52,32 @@ def test_edit_server_interactive_fallback(monkeypatch):
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)