App-gestützte MCP-Server: warum Hermes jetzt zuerst prüft, ob die App installiert ist

Du installierst Blender. Du aktivierst darin das offizielle MCP-Add-on. Dann folgst du einem Tutorial, lässt Hermes ein Blender-Plugin installieren, und die Installation meldet „fertig“ — und tatsächlich taucht get_objects_summary in der Tool-Liste auf. Also fragst du: „Welche Objekte sind gerade in meiner Szene?“ Die Antwort kommt zurück: „Tool nicht verbunden.“
Du öffnest mcp.json. Die Adresse stimmt. Du startest Blender neu. Immer noch nichts. Das eigentliche Problem ist, dass der Port nie in mcp.json stand — Blender wählt beim Start einen aus und schreibt ihn in eine eigene Runtime-Datei. Die Adresse, die du von Hand in die Konfiguration geschrieben hast, war vom ersten Tag an veraltet.
Drei Änderungen sind kürzlich gelandet, um genau diese Klasse von „die Konfiguration sieht richtig aus, sie verbindet einfach nicht“ zu schließen: Plugins können deklarieren, welche Anwendung sie brauchen (#119049), die Runtime vertraut nur der Endpoint-Datei, die die App schreibt (#119050), und der Plugin-Katalog hat seinen ersten echten Fall bekommen, Blender Lab (#119365).
Warum „die Adresse in die Konfiguration zu schreiben“ nie funktionieren konnte
Lokale Apps im Tool-Stil sprechen über eine Loopback-Bridge mit Hermes. Um nicht um Ports zu kämpfen, vergibt eine Desktop-App üblicherweise beim Start dynamisch einen und schreibt dann ihre aktuelle http-Adresse, ein temporäres token und ihre Prozess-pid in eine Runtime-Datei — bei Blender ist das die Bridge auf Port 9876. Daraus folgen zwei Konsequenzen:
- Eine fest kodierte URL veraltet irgendwann, und sie veraltet lautlos. Die Konfiguration ist syntaktisch perfekt; nur die Verbindung ist tot.
- Ein Tool, das sich nicht verbindet, hatte früher genau eine Meldung: „nicht verbunden.“ Du konntest nicht erkennen, ob die App geschlossen war, der Port gewandert ist oder die Version zu alt war.
Deshalb wurde das Design umgedreht: nicht raten — die Anwendung fragen.
Ein Plugin kann in plugin.json deklarieren, welche Anwendung es braucht
Portable Plugins (die Sorte, die eine plugin.json plus Dateien mitbringt, ohne nativen Code) haben einen Deklarationsblock unter extensions."com.nousresearch.hermes".servers bekommen. Der Servername muss mit einem Eintrag in der mcp.json des Plugins übereinstimmen:
"extensions": {
"com.nousresearch.hermes": {
"servers": {
"blender": {
"app": {
"darwin": {
"presence": "bundle",
"location": "/Applications/Blender.app",
"version": { "kind": "plist" }
},
"win32": {
"presence": "executable",
"location": "%ProgramFiles%/Blender Foundation/Blender/blender.exe",
"version": { "kind": "uninstall_registry", "display_name_prefix": "Blender" }
},
"linux": {
"presence": "executable",
"location": "/usr/bin/blender",
"version": { "kind": "none" }
}
},
"requires": { "app": true, "min_version": "5.1" },
"liveness": {
"kind": "server_json",
"path": "%LOCALAPPDATA%/Blender/server.json",
"fields": { "url": "http", "token": "token", "pid": "pid" }
}
}
}
}
}
Der Parser ist bei jeder einzelnen dieser Zeilen streng, was man wissen sollte, bevor man selbst eine schreibt:
appist nach Betriebssystem gegliedert und akzeptiert nurdarwin,win32undlinux, wobei mindestens ein Block erforderlich ist.locationmuss absolut sein oder mit~,%VAR%oder$VARbeginnen; alles, was..oder ein URL-Schema enthält, wird rundheraus abgelehnt.presencehat zwei Werte:bundle(ein macOS-App-Bundle) undexecutable.- Eine Versionsquelle (
version.kind) ist eine vonplist(nur darwin),uninstall_registry(nur win32, und sie verlangt eindisplay_name_prefix),pe_resource(win32) odernone. Wenn durequires.min_versionsetzt, muss jeder OS-Block tatsächlich eine Version lesen können — einnone-Block scheitert an der Validierung. liveness.kindistserver_jsonodernone. Die Wahl vonserver_jsonerfordertpath; optionale Schlüssel sindendpoint_path(Standard/mcp) und die Feldnamen-Überschreibungenpid_key,url_key,token_key(Standardpid,http,token).
Beachte die Trennung zwischen den beiden Blöcken: app sind Daten („wo die Anwendung liegt“), requires ist Policy („ob ich darauf bestehe, dass sie da ist“). Diese Trennung ist Absicht.
Das Installations-Gate: nichts installieren, was nicht funktionieren kann
Vorher war eine fehlende Anwendung rein ein Runtime-Problem — das Plugin landete in seinem Verzeichnis, seine Tools registrierten sich, und du hast die Leere erst bemerkt, als du eines davon aufgerufen hast. Jetzt prüft der Installer jeden deklarierten Server vor dem atomaren Swap und verweigert bei einem der folgenden Fälle:
| Verweigerung | Auslöser |
|---|---|
missing_app |
Die deklarierte Anwendung ist nicht auf dieser Maschine |
version_too_old |
Die App ist da, aber älter als requires.min_version |
unsupported_os |
Dieser Host wird von keinem OS-Block abgedeckt |
| Plattform-Mismatch | Die platforms-Liste eines Katalog-Eintrags passt nicht zur OS-Familie des Hosts (windows / macos werden als Aliase für win32 / darwin akzeptiert) |
Bei Katalog-Installationen erfolgt die Prüfung, bevor geklont oder das Plugin-Ziel geschrieben wird, sodass du nie mit einem halb installierten Verzeichnis dastehst. Die Validierung meldet den Verfügbarkeitsstatus zusätzlich als informative Zeile:
hermes plugins validate --install-deps
Zur Runtime ist nur die Datei maßgeblich, die die App schreibt
Das ist der härteste Teil der Änderung. Jeder Verbindungsversuch zu einem deklarierten Server liest zuerst diese Runtime-Datei erneut und verwendet den darin stehenden Endpoint:
- Die Runtime-Datei ist maßgeblich. Eine fehlende Datei, ein fehlerhafter Endpoint oder eine tote
pidlösenLiveEndpointUnavailableaus — und Hermes fällt nicht auf die statisch konfigurierte URL zurück. Ein Test hält diese Linie:test_missing_runtime_file_never_falls_back. - Das Token wird registriert, bevor es verwendet wird. Ein in der Runtime-Datei gefundenes temporäres Token wird dem Redactor übergeben, bevor Hermes daraus einen
Authorization-Header baut, sodass es nicht durchsickert, wenn du später Logs einfügst oder ein Transkript liest. - Anwendungsanforderungen steuern auch die Tool-Aufnahme. Ein Server mit App-Anforderungen bekommt seine Tools nicht in das verfügbare Set angeboten, solange der Host sie nicht erfüllen kann.
Wenn ein Tool weg ist, sag warum
Die schlimmste Variante dieser Erfahrung ist nicht „das Tool fehlt“ — sondern „das Tool fehlt und niemand sagt dir, warum.“ Deklarierte, aber nicht verfügbare Tools liefern jetzt einen strukturierten Zustand: den aktuellen Status, die Anwendungsdetails, eine konkrete Nutzeraktion und eine Retry-Policy. Der Katalog verzögerter Tools und die Zusammenfassung einer leeren Suche behalten außerdem eine stabile Zeile für jeden versteckten deklarierten Server.
Fünf Zustände sind unterscheidbar:
| Zustand | Bedeutung | Ungefähr, was zu tun ist |
|---|---|---|
missing_app |
Nicht installiert | Die App installieren, dann das Plugin installieren/aktivieren |
version_too_old |
Installiert, aber zu alt | Die App aktualisieren |
app_not_running |
Installiert, läuft nicht | Sie starten und die Bridge hochkommen lassen |
endpoint_unavailable |
Runtime-Datei fehlt oder Endpoint ungültig | Die Bridge aus der App heraus neu starten |
no_interactive_session |
Braucht eine grafische Login-Sitzung | In einer Desktop-Sitzung ausführen, nicht in einem headless Service |
Beachte außerdem: Die Retry-Policy ist immer nur after_user_action (erledige das oben Genannte, dann versuche es erneut) oder never_here (dieser Host wird es nie erfüllen, etwa weil das OS nicht unterstützt wird). „Versuch es noch einmal“ und „hör auf, es zu versuchen“ sind verschiedene Antworten, und das Tool gibt dir jetzt die richtige.
Der echte Fall, der schon im Katalog steht: Blender Lab
Der offizielle Plugin-Katalog führt jetzt einen blender-Eintrag, der dieses öffentliche Repository umhüllt: hermes-plugin-blender.
# installiert den gepinnten sha des Katalog-Eintrags, nicht die Spitze eines Branches
hermes plugins install NousResearch/hermes-plugin-blender
hermes plugins enable blender
Was es tut, ist unspektakulär: Es startet den offiziellen Blender Lab MCP-Server über stdio (Upstream-Quelle gepinnt auf Commit 2cea8d56, v1.0.3), und dieser Server verbindet sich mit der Bridge des Blender-Add-ons auf 127.0.0.1:9876. Ein originärer Workflow-Skill wird mitgeliefert.
Drei Dinge solltest du wissen, bevor du dich darauf verlässt:
- Die Voraussetzungen bringst du mit. Blender 5.1+, das offizielle MCP-Add-on (installiert, aktiviert, Online-Zugriff erlaubt, Bridge gestartet), plus Git und uv auf der Backend-Maschine. Das Installieren des Pakets installiert nicht Blender, ändert nicht dessen Einstellungen, installiert nicht das Add-on und öffnet keine Szene.
- Wärme die uv-Umgebung vor der ersten Verbindung auf. Der erste Upstream-Build dauert Minuten; das wurde unter Windows gemessen, und ein normales Verbindungs-Timeout von MCP bricht ihn ab:
uv tool run --from "git+https://projects.blender.org/lab/blender_mcp.git@2cea8d566dde07fbac28a61d698909d69724e853#subdirectory=mcp" blender-mcp --help
- Dieser Wrapper nutzt das App-Gate noch nicht. In seinem README steht unverblümt, dass die automatische Erkennung von Anwendung, Version und Liveness vom Wrapper weder implementiert noch verifiziert ist und dass die Voraussetzungen manuell sind. Mit anderen Worten: Der
app/requires/liveness-Vertrag ist im Core live, und dieses spezielle Plugin hat ihn noch nicht übernommen — die übliche Reihenfolge „erst die Fähigkeit, dann die Plugins“.
Noch etwas gehört offen ausgesprochen: Blender Lab selbst warnt, dass seine Tools beliebigen Python-Code innerhalb von Blender ausführen, ohne Schutz gegen Dateilöschung oder Datenexfiltration. Halte die Bridge auf Loopback beschränkt, nutze sie in vertrauenswürdigen Workflows und bevorzuge eine wegwerfbare Szene. Das ist keine Sandbox.
Plattform-Asymmetrien und die Ecken, die noch offen sind
- Die Host-Prüfungen sind über Plattformen hinweg nicht symmetrisch. Windows verlangt eine Prozesssitzung ungleich null (entweder die aktive Konsolensitzung oder eine Prozess-Window-Station). Linux verlangt eine lesbare Prozess-Sitzungs-ID und das Runtime-Verzeichnis des aktuellen Benutzers. macOS gibt true zurück — die Standardbibliothek hat keinen subprozessfreien Weg, die aktuelle grafische Sitzung abzufragen.
- Anwendungs-Builds mit deaktivierter Auth schreiben die Runtime-Datei möglicherweise nie. Der PR führt das als offene Frage, die auf eine Hardware-Verifikation wartet; ein solcher Build würde dauerhaft in
endpoint_unavailablehängen. - Die Aktivierungsgrenze hat sich nicht verschoben. Wenn ein Plugin mitten im Lauf geladen wird, verdrahten sich seine gateway-seitigen Handler (Slash-Befehle, Button-Callbacks, eingehende Transforms) sofort — aber Tools und System-Prompt-Abschnitte warten weiterhin auf die nächste Session (die Prompt-Cache-Invariante), und portable MCP-Server brauchen ein explizites
mcp.reload.
Zum Schluss
Wenn dein Plugin mit Desktop-Software spricht, war das Problem nie, dass die Konfiguration falsch geschrieben war — sondern dass ein Mensch diese Adresse überhaupt nicht schreiben sollte. Jetzt deklariert ein Plugin seine Anwendungsabhängigkeit, der Installer verweigert, bevor er dein Profile anfasst, die Runtime vertraut nur dem Endpoint, den die App selbst veröffentlicht, und ein totes Tool sagt dir endlich, welches Glied in der Kette gebrochen ist.
Für das umgebende Plugin-Bild siehe das Vertrauensmodell des Plugin-Katalogs und den offiziellen Remote-MCP-Katalog; wenn dich die Sicherheitsgrenze lokaler Tools wie dieses interessiert, ist MCP-Katalog-Sicherheit die nähere Lektüre. Für die repo-vendored Variante behandelt der Leitfaden zu projekt-lokalen Skills .hermes/skills/ und dessen Trust-Gate.