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:

  • app ist nach Betriebssystem gegliedert und akzeptiert nur darwin, win32 und linux, wobei mindestens ein Block erforderlich ist. location muss absolut sein oder mit ~, %VAR% oder $VAR beginnen; alles, was .. oder ein URL-Schema enthält, wird rundheraus abgelehnt.
  • presence hat zwei Werte: bundle (ein macOS-App-Bundle) und executable.
  • Eine Versionsquelle (version.kind) ist eine von plist (nur darwin), uninstall_registry (nur win32, und sie verlangt ein display_name_prefix), pe_resource (win32) oder none. Wenn du requires.min_version setzt, muss jeder OS-Block tatsächlich eine Version lesen können — ein none-Block scheitert an der Validierung.
  • liveness.kind ist server_json oder none. Die Wahl von server_json erfordert path; optionale Schlüssel sind endpoint_path (Standard /mcp) und die Feldnamen-Überschreibungen pid_key, url_key, token_key (Standard pid, 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 pid lösen LiveEndpointUnavailable aus — 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:

  1. 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.
  2. 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
  1. 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_unavailable hä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.