Skip to content

Commit cdb2d09

Browse files
committed
fix: add should remove optional argument placeholder
1 parent a51e32c commit cdb2d09

File tree

3 files changed

+94
-53
lines changed

3 files changed

+94
-53
lines changed

src/mcpm/commands/server_operations/add.py

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -345,40 +345,11 @@ def add(server_name, force=False, alias=None, target: str | None = None):
345345

346346
# Now process the replacement of environment variables
347347
for key, value in env_vars.items():
348-
processed_env[key] = value
349-
350348
if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
351-
# For required arguments, prompt the user if not in environment
352-
env_value = os.environ.get(key, "")
353-
354-
if not env_value and key in required_args and key not in processed_variables:
355-
progress.stop()
356-
console.print(f"[yellow]Warning:[/] Required argument {key} is not set in environment")
357-
358-
# Prompt for the value
359-
arg_info = required_args.get(key, {})
360-
description = arg_info.get("description", "")
361-
try:
362-
user_value = prompt_with_default(
363-
f"Enter value for {key} ({description})",
364-
default="",
365-
hide_input=_should_hide_input(key),
366-
required=True,
367-
)
368-
processed_env[key] = user_value
369-
except click.Abort:
370-
console.print("[yellow]Will store the reference to environment variable instead.[/]")
371-
processed_env[key] = value # Store the reference as-is
372-
373-
# Resume progress
374-
progress = Progress(
375-
SpinnerColumn(), TextColumn("[bold green]{task.description}[/]"), console=console
376-
)
377-
progress.start()
378-
progress.add_task(f"Configuring {server_name}...", total=None)
379-
else:
380-
# Store reference to environment variable
381-
processed_env[key] = processed_variables.get(key, env_value)
349+
# Extract the variable name from ${VAR_NAME}
350+
env_var_name = value[2:-1]
351+
# Use empty string as default when key not found
352+
processed_env[key] = processed_variables.get(env_var_name, "")
382353
else:
383354
processed_env[key] = value
384355

@@ -389,11 +360,8 @@ def add(server_name, force=False, alias=None, target: str | None = None):
389360
matched = re.search(r"\$\{([^}]+)\}", arg)
390361
if matched:
391362
original, key = matched.group(0), matched.group(1)
392-
if key not in processed_variables:
393-
# Keep the original argument if variable not found
394-
processed_args.append(arg)
395-
continue
396-
replaced_arg = arg.replace(original, processed_variables.get(key, arg))
363+
# Use empty string as default when key not found
364+
replaced_arg = arg.replace(original, processed_variables.get(key, ""))
397365
processed_args.append(replaced_arg)
398366
else:
399367
processed_args.append(arg)

src/mcpm/schemas/server_config.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ def get_filtered_env_vars(self, env: Dict[str, str]) -> Dict[str, str]:
3434
# Use provided environment without falling back to os.environ
3535
environment = env
3636

37-
# Filter out empty environment variables
38-
non_empty_env = {}
37+
# Keep all environment variables, including empty strings
38+
filtered_env = {}
3939
for key, value in self.env.items():
4040
# For environment variable references like ${VAR_NAME}, check if the variable exists
4141
# and has a non-empty value. If it doesn't exist or is empty, exclude it.
@@ -44,14 +44,13 @@ def get_filtered_env_vars(self, env: Dict[str, str]) -> Dict[str, str]:
4444
# Extract the variable name from ${VAR_NAME}
4545
env_var_name = value[2:-1]
4646
env_value = environment.get(env_var_name, "")
47-
# Only include if the variable has a value in the environment
48-
if env_value.strip() != "":
49-
non_empty_env[key] = value
50-
# For regular values, only include if they're not empty
51-
elif value.strip() != "":
52-
non_empty_env[key] = value
53-
54-
return non_empty_env
47+
# Include all values, even empty ones
48+
filtered_env[key] = env_value
49+
else:
50+
# Include all values, even empty ones
51+
filtered_env[key] = value
52+
53+
return filtered_env
5554

5655

5756
class SSEServerConfig(BaseServerConfig):

tests/test_add.py

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def test_add_server(windsurf_manager, monkeypatch):
5050

5151

5252
def test_add_server_with_missing_arg(windsurf_manager, monkeypatch):
53-
"""Test add server with a missing argument that should remain in the args"""
53+
"""Test add server with a missing argument that should be replaced with empty string"""
5454
monkeypatch.setattr(ClientRegistry, "get_active_client", Mock(return_value="windsurf"))
5555
monkeypatch.setattr(ClientRegistry, "get_active_client_manager", Mock(return_value=windsurf_manager))
5656
monkeypatch.setattr(ClientRegistry, "get_client_manager", Mock(return_value=windsurf_manager))
@@ -78,7 +78,7 @@ def test_add_server_with_missing_arg(windsurf_manager, monkeypatch):
7878
"arguments": {
7979
"fmt": {"type": "string", "description": "Output format", "required": True},
8080
"API_KEY": {"type": "string", "description": "API key", "required": True},
81-
# Deliberately not including TZ to test the bug fix
81+
# Deliberately not including TZ to test empty string replacement
8282
},
8383
}
8484
}
@@ -104,10 +104,84 @@ def test_add_server_with_missing_arg(windsurf_manager, monkeypatch):
104104

105105
assert result.exit_code == 0
106106

107-
# Check that the server was added with alias and the missing argument is preserved
107+
# Check that the server was added with alias and the missing argument is replaced with empty string
108108
server = windsurf_manager.get_server("test-missing-arg")
109109
assert server is not None
110110
assert server.command == "npx"
111-
# The ${TZ} argument should remain intact since it's not in the processed variables
112-
assert server.args == ["-y", "@modelcontextprotocol/server-test", "--fmt", "json", "--timezone", "${TZ}"]
111+
# The ${TZ} argument should be replaced with empty string since it's not in processed variables
112+
assert server.args == ["-y", "@modelcontextprotocol/server-test", "--fmt", "json", "--timezone", ""]
113113
assert server.env["API_KEY"] == "test-api-key"
114+
115+
116+
def test_add_server_with_empty_args(windsurf_manager, monkeypatch):
117+
"""Test add server with missing arguments that should be replaced with empty strings"""
118+
monkeypatch.setattr(ClientRegistry, "get_active_client", Mock(return_value="windsurf"))
119+
monkeypatch.setattr(ClientRegistry, "get_active_client_manager", Mock(return_value=windsurf_manager))
120+
monkeypatch.setattr(ClientRegistry, "get_client_manager", Mock(return_value=windsurf_manager))
121+
monkeypatch.setattr(
122+
RepositoryManager,
123+
"_fetch_servers",
124+
Mock(
125+
return_value={
126+
"server-test": {
127+
"installations": {
128+
"npm": {
129+
"type": "npm",
130+
"command": "npx",
131+
"args": [
132+
"-y",
133+
"@modelcontextprotocol/server-test",
134+
"--fmt",
135+
"${fmt}",
136+
"--optional",
137+
"${OPTIONAL}", # Optional arg not in arguments list
138+
"--api-key",
139+
"${API_KEY}",
140+
],
141+
"env": {
142+
"API_KEY": "${API_KEY}",
143+
"OPTIONAL_ENV": "${OPTIONAL}", # Optional env var
144+
},
145+
}
146+
},
147+
"arguments": {
148+
"fmt": {"type": "string", "description": "Output format", "required": True},
149+
"API_KEY": {"type": "string", "description": "API key", "required": True},
150+
# OPTIONAL is not listed in arguments
151+
},
152+
}
153+
}
154+
),
155+
)
156+
157+
# Mock prompt responses for required arguments only
158+
with (
159+
patch("prompt_toolkit.PromptSession.prompt", side_effect=["json", "test-api-key"]),
160+
patch("rich.progress.Progress.start"),
161+
patch("rich.progress.Progress.stop"),
162+
patch("rich.progress.Progress.add_task"),
163+
):
164+
runner = CliRunner(mix_stderr=False)
165+
result = runner.invoke(add, ["server-test", "--force", "--alias", "test-empty-args"])
166+
167+
assert result.exit_code == 0
168+
169+
# Check that the server was added and optional arguments are empty
170+
server = windsurf_manager.get_server("test-empty-args")
171+
assert server is not None
172+
assert server.command == "npx"
173+
# Optional arguments should be replaced with empty strings
174+
assert server.args == [
175+
"-y",
176+
"@modelcontextprotocol/server-test",
177+
"--fmt",
178+
"json",
179+
"--optional",
180+
"", # ${OPTIONAL} replaced with empty string
181+
"--api-key",
182+
"test-api-key",
183+
]
184+
assert server.env == {
185+
"API_KEY": "test-api-key",
186+
"OPTIONAL_ENV": "", # Optional env var should be empty string
187+
}

0 commit comments

Comments
 (0)