Skip to content

Commit c54c131

Browse files
fix(gateway): include user-local bin paths in launchd plist PATH
Port PR #3527's systemd fix to launchd (macOS): generate_launchd_plist() now calls _build_user_local_paths() so ~/.local/bin, ~/.cargo/bin, ~/go/bin, and ~/.npm-global/bin are always reachable, not just whatever happened to be in the captured shell PATH at install time. Fixes uv/uvx MCP servers being unreachable under the launchd-managed gateway. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c624934 commit c54c131

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

hermes_cli/gateway.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3333,6 +3333,11 @@ def generate_launchd_plist() -> str:
33333333
resolved_node_dir = str(Path(resolved_node).resolve().parent)
33343334
if resolved_node_dir not in priority_dirs:
33353335
priority_dirs.append(resolved_node_dir)
3336+
# Explicitly ensure user-local bin dirs (uv/uvx, pipx, cargo, go, npm
3337+
# global) are reachable even if the captured shell PATH below happens to
3338+
# lack them (e.g. plist generated from a non-interactive/minimal
3339+
# environment). Mirrors the same safety net used for systemd units.
3340+
priority_dirs.extend(_build_user_local_paths(Path.home(), priority_dirs))
33363341
sane_path = ":".join(
33373342
dict.fromkeys(
33383343
priority_dirs + [p for p in os.environ.get("PATH", "").split(":") if p]

tests/hermes_cli/test_gateway_service.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,27 @@ def test_system_unit_includes_local_bin_in_path(self, monkeypatch):
17291729
assert "/.local/bin" in unit
17301730

17311731

1732+
class TestGeneratedLaunchdPlistIncludesLocalBin:
1733+
"""~/.local/bin must be in the launchd plist PATH so uvx/pipx MCP tools
1734+
are discoverable, even when it's absent from the captured shell PATH."""
1735+
1736+
def test_launchd_plist_includes_local_bin_in_path(self, monkeypatch):
1737+
home = Path.home()
1738+
local_bin = str(home / ".local" / "bin")
1739+
# Simulate a minimal environment where the captured shell PATH does
1740+
# NOT contain ~/.local/bin.
1741+
monkeypatch.setenv("PATH", "/usr/bin:/bin")
1742+
monkeypatch.setattr(
1743+
gateway_cli,
1744+
"_build_user_local_paths",
1745+
lambda home_path, existing: [local_bin],
1746+
)
1747+
1748+
plist = gateway_cli.generate_launchd_plist()
1749+
1750+
assert local_bin in plist
1751+
1752+
17321753
class TestSystemServiceIdentityRootHandling:
17331754
"""Root user handling in _system_service_identity()."""
17341755

0 commit comments

Comments
 (0)