AI / Automation 27 avril 2026

2026 : OpenClaw PATH, préfixes Homebrew et échec d'exécution de serveurs MCP sous launchd sur un Mac mini ProxyMac

Équipe d'ingénierie ProxyMac 27 avril 2026 ~12 min de lecture

Les équipes qui exécutent OpenClaw sur des Mac mini M4 loués à Hong Kong, au Japon, en Corée, à Singapour ou aux États-Unis partagent des journaux où les outils MCP affichent env: node: No such file or directory ou uvx: command not found alors que le même binaire réussit dans Terminal.app. Ce texte formalise le contrat de PATH entre shell de connexion et launchd : pourquoi brew --prefix diffère sur Apple Silicon, comment lire une plist LaunchAgent sans deviner, et une échelle de cinq vérifications alignée sur les serveurs MCP, le déploiement et l'installation, plus une matrice, un extrait EnvironmentVariables à valider, des appels vers l'aide et le hub OpenClaw. Documentez hachage et PATH par locataire sur l'hôte partagé, sinon les artefacts CI se mélangent aux mises à jour Homebrew interactives pendant que l'agent silencieux tourne la nuit. Versionnez les chemins d'écriture userspace avant qu'un socle sécurité ne casse les liens ~/.local.

Terminal et launchd : deux univers

Les shells interactifs chargent /etc/zprofile, ~/.zprofile et ~/.zshrc, souvent avec eval "$(/opt/homebrew/bin/brew shellenv)" en tête. Les agents launchd héritent d'un environnement minimal : PATH retombe sur /usr/bin:/bin:/usr/sbin:/sbin tant que la plist n'étend pas. Les passerelles OpenClaw qui lancent des MCP ne voient donc pas npx ni pnpm, tandis que which npx côté Terminal affiche /opt/homebrew/bin/npx. Ce n'est pas « OpenClaw a perdu le MCP » : c'est ENOENT de execve. Les builds headless calibrés en session ssh mais déployés sans EnvironmentVariables s'écartent dès qu'un brew nocturne installe un autre secondaire Node alors que le JSON pointe toujours vers l'ancien shim.

  • Prouver l'écart : comparer printenv PATH en session ssh mini 'launchctl print gui/…' et en shell de connexion.
  • Conserver l'exécutable MCP en chemin absolu pendant que le PATH d'aide se stabilise.
  • Versionner (voir gitops de config) pour ne pas subir de rotation silencieuse de racine Homebrew.
N'importez jamais le PATH graphique pour des automates : le partage d'écran injecte d'autres tables d'environnement qu'un LaunchAgent headless, le VNC sert d'aide humaine, pas de vérité d'orchestration. Vérifiez qu'une règle sudo parallèle ne supprime pas votre plist parce qu'une session VNC a duré plus longtemps.

Matrice de préfixe Homebrew (trois colonnes)

GénérationPréfixe brewSymptôme usuel
Mac mini M4 Apple Silicon/opt/homebrewnode manquant, /opt/homebrew/bin/node -v v22.x
Mac mini Intel hérité/usr/localconfig MCP contient encore /opt/homebrew/bin/uvx d'un clone portable
Flotte mixtedeux disquesordre de shims erroné si /usr/local/bin précède /opt/homebrew/bin

Ne mélangez pas interpréteurs x86 et arm64 dans la même plist sans y attacher toutes les sondes d'intégrité. Rosette peut brouiller la lecture. Fournissez file sur chaque binaire MCP, comme un journal de compilation : le mini en datacentre n'émet pas le même sifflement que l'ultraportable qui a écrit le JSON de démonstration.

Plist EnvironmentVariables : survivre au reboot

Ajoutez un dictionnaire EnvironmentVariables au LaunchAgent. D'abord Homebrew, ensuite les chemins Apple, enfin p. ex. ~/.local/bin pour uv. Après édition : launchctl bootout gui/$(id -u)/domain plist et kickstart (éviter launchctl unload seul en macOS 14+). Joignez des sondes de supervision pour alerte en 3 minutes dès qu'un PATH s'effiloche. Citez ID de ticket et instantané de retour en arrière avant l'audit de sécurité.

Exemple (valider en préprod)

<key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string> </dict>

Sécurité : ouvrir le PATH vaut mieux qu'assouplir le SIP, mais bannir les répertoires en écriture globale en tête de PATH, voir l'hygiène des secrets pour binaires user-writables. Vérifiez umask des LaunchAgents contre le ticket qui autorisait le staging.

Cinq vérifications avant de réécrire tout le MCP

  1. Hachage : shasum -a 256 $(which uvx) côté Terminal, comparer au chemin absolu intégré au JSON.
  2. Environnement launchd : launchctl print user/$(id -u)/limit et domaine d'agent ; confirmer le PATH chargé.
  3. Non-login : env -i PATH=/usr/bin:/bin /opt/homebrew/bin/npx --version prouve que npx survit au minimum.
  4. JSONL : rechercher ENOENT, lire le diagnostic.
  5. Rollback : congeler la plist signée, selon retour d'upgrade, avant un déploiement PATH massif sur HK / JP / KR / SG / US, canary par région plutôt qu'un redémarrage de tous les gateways à l'heure de pointe.

Node, uv et disposition des shims : pourquoi les gestionnaires de versions éclatent avec launchd

On aime fnm, mise et asdf pour jongler entre dizaines de Node par dépôt, mais ces outils placent le plus souvent leurs ajustements de PATH dans ~/.zshrc, que launchd n’exécute jamais. Lorsqu’un JSON MCP appelle npx @scope/server, c’est d’abord npx qu’il faut trouver en chemin absolu ; seulement ensuite interviennent ~/.npm et le reste. Sur un mini ProxyMac neuf, copier la config d’un portable donne volontiers ce scénario : corepack enable a mis pnpm dans PATH côté shell interactif, tandis que l’agent pointe un shebang #!/usr/bin/env node qui aboutit au binaire système (parfois v18 sur l’image) au lieu de la v22 exigée par le fichier de verrou. L’échec n’apparaît pas proprement comme « binaire manquant » : on récolte ERR_PNPM_UNSUPPORTED_ENGINE perdu au milieu des relances OpenClaw.

uv obéit au même principe : uvx atterrit en général dans ~/.local/bin ou ~/.cargo/bin après le script curl, répertoires visibles au Terminal mais souvent absents du PATH hérité par launchd. Plutôt qu’allonger indéfiniment la pliste, beaucoup d’équipes posent un script enveloppe minimal, par ex. /usr/local/bin/mcp-env.sh (propriétaire root:wheel, pas d’emplacement inscriptible par le monde en tête) qui exporte PATH une fois et exec l’entrée réelle. Les auditeurs préfèrent un fichier relu qu’une ribambelle de bouts de plistes. Tenez le wrapper sous Git avec la même rigueur que les extraits de redémarrage du gateway, launchctl et reprise afin d’isoler mardi de jeudi.

RuntimeOù l’on installe d’ordinaireCe que launchd voit sans correctifs
Node via Homebrew/opt/homebrew/bin/nodeOn ne va souvent qu’à /usr/bin/env ; shim Homebrew manquant → ENOENT
Chaînes uv~/.local/bin/uvxLe tilde n’est pas étendu dans une chaîne de pliste si vous ne le faites pas
pnpm géré par Corepackshim à côté de nodeAligné seulement si le même répertoire précède /usr/bin dans PATH
Conseil d’exploitation : ajoutez un LaunchAgent de contrôle qui exécute toutes les 15 minutes /opt/homebrew/bin/node -e "console.log(process.version)" et pousse la ligne de sortie standard dans votre canal JSONL ; lorsqu’un brew upgrade remplace le binaire, la chaîne de version bondit avant l’usager.

FAQ

Tout envelopper dans /bin/zsh -lc ? Possible, masque les erreurs et gêne les signaux. Préférez un PATH explicite, sauf besoin de fonctions de connexion.

Rosetta casse-t-elle brew pour MCP ? Lorsqu'un binaire est x86_64 et l'agent attend arm64, croiser file $(which node) et l'architecture cible d'OpenClaw.

Et les blocages stdio ? PATH répare l'exec, les tampons réparent les tuyaux : lire stdio buffer quand l'exécution démarre mais l'échange sature.

Pourquoi le Mac mini ProxyMac fige les contrats de PATH

Un Mac mini M4 dédié cimente /opt/homebrew sur la NVMe à côté d'OpenClaw, sans brew uninstall d'un outillage partagé. arm64 natif évite Rosetta, mémoire unifiée tient des travailleurs MCP en parallèle, cinq régions clonent le même modèle de plist. Quand le PATH se stabilise, montez le parallélisme via le guide d'agents parallèles, réservez des nœuds sur les tarifs, pointez les opérateurs vers l'aide et VNC dès qu'une boîte de dialogue exige l'écran. Couplage recommandé avant qu'un autre SRE, fuseau voisin, ne duplique le cluster sans lire le même screen-wrapper.

Fixer le PATH, déployer OpenClaw partout

HK / JP / KR / SG / US · Apple Silicon M4