Skip to content

Commit 1b1d404

Browse files
fix: handle Windows drive letters in update_claude_config
Before this fix, `update_claude_config` would incorrectly split Windows paths like `C:\Users\foo\server.py` on the drive letter colon, treating `\Users\foo\server.py` as an object suffix. This would generate broken config entries that made MCP servers unlaunchable from Claude Desktop on Windows. Apply the same Windows drive-letter detection logic already present in `_parse_file_path` (cli.py:97-102): check if position 1 is a colon, and if so, only consider colons after position 2 as object-suffix separators. Add test coverage for Windows paths with and without `:object` suffixes. Co-authored-by: Felix Weinberger <felixweinberger@users.noreply.github.com>
1 parent 058a5b3 commit 1b1d404

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

src/mcp/cli/claude.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ def update_claude_config(
115115

116116
# Convert file path to absolute before adding to command
117117
# Split off any :object suffix first
118-
if ":" in file_spec:
118+
# First check if we have a Windows path (e.g., C:\...)
119+
has_windows_drive = len(file_spec) > 1 and file_spec[1] == ":"
120+
121+
# Split on the last colon, but only if it's not part of the Windows drive letter
122+
if ":" in (file_spec[2:] if has_windows_drive else file_spec):
119123
file_path, server_object = file_spec.rsplit(":", 1)
120124
file_spec = f"{Path(file_path).resolve()}:{server_object}"
121125
else:

tests/cli/test_claude.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,39 @@ def fake_which(cmd: str) -> str | None:
115115

116116
monkeypatch.setattr("shutil.which", fake_which)
117117
assert get_uv_path() == expected
118+
119+
120+
def test_windows_drive_letter_without_object(config_dir: Path, monkeypatch: pytest.MonkeyPatch):
121+
"""Windows paths like C:\\path\\server.py without :object should not split on drive letter."""
122+
# Mock Path.resolve to return a fake Windows-style path
123+
original_resolve = Path.resolve
124+
125+
def fake_resolve(self: Path) -> Path:
126+
if str(self) == "C:\\Users\\foo\\server.py":
127+
# Return the same path as if it were already resolved
128+
return self
129+
return original_resolve(self)
130+
131+
monkeypatch.setattr(Path, "resolve", fake_resolve)
132+
133+
assert update_claude_config(file_spec="C:\\Users\\foo\\server.py", server_name="s")
134+
135+
# Should use the full path without splitting on the drive letter colon
136+
assert _read_server(config_dir, "s")["args"][-1] == "C:\\Users\\foo\\server.py"
137+
138+
139+
def test_windows_drive_letter_with_object(config_dir: Path, monkeypatch: pytest.MonkeyPatch):
140+
"""Windows paths like C:\\path\\server.py:app should only split on the :app suffix."""
141+
original_resolve = Path.resolve
142+
143+
def fake_resolve(self: Path) -> Path:
144+
if str(self) == "C:\\Users\\foo\\server.py":
145+
return self
146+
return original_resolve(self)
147+
148+
monkeypatch.setattr(Path, "resolve", fake_resolve)
149+
150+
assert update_claude_config(file_spec="C:\\Users\\foo\\server.py:app", server_name="s")
151+
152+
# Should split on :app but not on C:
153+
assert _read_server(config_dir, "s")["args"][-1] == "C:\\Users\\foo\\server.py:app"

0 commit comments

Comments
 (0)