Zum Inhalt

Code ausführen (Tool js_execute, integrierter MCP-Server „JS“)

Der integrierte MCP-Server JS erweitert Assistenten um zwei zentrale Fähigkeiten:

  • Automationen ausführen: wiederkehrende Abläufe zuverlässig nach Ereignissen oder Zeitplänen anstoßen.
  • Code-Tool: kleine TypeScript/JavaScript-Skripte sicher in einer isolierten Umgebung ausführen, um Daten zu verarbeiten oder Aktionen zu orchestrieren.

Diese Seite erklärt, was du damit als Endnutzer tun kannst, und welche Funktionen dir im Code-Tool zur Verfügung stehen.

Wann ist der JS-Server sinnvoll?

Nutze ihn, wenn du:

  • Daten aus Dateien/Ergebnissen transformieren willst (z. B. Liste bereinigen, Felder normieren, JSON erzeugen).
  • Tools kombinieren willst (z. B. erst suchen, dann Ergebnisse filtern, dann als Datei speichern).
  • aus einem Trigger heraus Folgeschritte durchführen willst (z. B. „Wenn X passiert, lege Einträge an, schreibe eine Zusammenfassung, speichere Artefakte“).

Wenn du nur eine normale Frage beantwortet haben willst, ist der JS-Server meist nicht nötig.

Gerade für effiziente Automationen ist der Server wichtig, weil du mit Code erst die billigen Schritte erledigen kannst: prüfen, filtern, deduplizieren, Felder normalisieren, Relevanz entscheiden. Ein LLM-Aufruf sollte erst danach kommen, wenn wirklich ein inhaltlicher KI-Schritt nötig ist.

Beziehung zu Automation Actions

Der Name ist etwas irreführend: Das Code-Tool läuft über den integrierten MCP-Server JS ist nicht nur für klassische Trigger-Automationen relevant, sondern auch selbst eine Quelle für Tools, die als Aktion in Automationen auftauchen können.

Das heißt praktisch:

  • In einer Automation kann als Aktion ein Tool aus dem Server JS gewählt werden.
  • Dazu gehören vor allem Such-, Patch- und Code-Ausführungswerkzeuge.
  • Daneben können auch viele andere integrierte MCP-Server als Aktion dienen, zum Beispiel chat/send_message, event/event_dispatch, email/email_send oder task_management/task_create.

Mehr dazu:

Das Tool js_execute (JS/TS in der Sandbox ausführen)

js_execute führt TypeScript/JavaScript in einer Sandbox aus:

  • keine Shell-Befehle, kein direkter Systemzugriff
  • Virtuelles Dateisystem (fs) und mcp/help nur im Chat- und Playground-Profil (siehe Zwei Modi)
  • MCP im klassischen Automations-Hintergrund z. B. über import { callMcpTool } from "clye.ai" (oder über tools.*, je nach Setup)
  • Hilfsfunktionen für Events, Wissens-/Chunk-Operationen und (profilabhängig) fetch; Datum/JSON wie gewohnt über Standard-JavaScript (new Date(), JSON.*) bzw. über die in help() gelisteten Projekt-APIs

Zwei Modi: Chat vs. Automation (wichtig)

Je nachdem, wo du Code ausführst, gelten unterschiedliche Regeln:

  • Im Chat (interaktiver Lauf, z. B. Tool „JS/TS im Chat ausführen“)
  • fs, mcp und help stehen als Globals zur Verfügung (virtuelles Dateisystem).
  • Dateien liegen im Chat-Kontext (Uploads + Workspace).
  • HTTP/fetch ist bewusst deaktiviert (damit kein unkontrollierter Netzwerkzugriff aus der Sandbox passiert).
  • Bei addChunks: die Variante url: { fetch: "…" } (Host lädt eine URL nach) ist im gleichen Profil ebenfalls deaktiviert. Normale URL-Strings, data:-URLs sowie Blob/ArrayBuffer/Uint8Array funktionieren weiterhin.
  • Im Space-JS-Playground
  • Wie Chat: fs, mcp, help; kein freies fetch; addChunks ohne { fetch: … }.
  • In einer klassischen Automation (Hintergrundlauf ohne Chat-Kontext)
  • fs, mcp und help gibt es hier nicht als Globals (kein Chat-Dateisystem in diesem Profil).
  • fetch ist möglich, wenn eure Umgebung das zulässt.
  • addChunks darf url: { fetch: "…", … } nutzen (Host lädt die URL und legt die Datei ab).
  • Typisch nutzt du tools.*, Events (lastEvent / getEvents), Chunk-Helfer, import … from "clye.ai", console und das Objekt automation (siehe unten).

Dateisystem (fs)

Im Chat- und Playground-Profil steht dir das Objekt fs zur Verfügung. Im klassischen Automation-Hintergrund (ohne Chat-Kontext) ist fs kein Global — nutze dort tools.*, Events, Chunks und clye.ai, oder führe Dateizugriffe über andere Tools aus.

Chat-Kontext (typische Ordner)

Im Chat arbeitest du in einem Root wie:

  • /chats/<chatId>/assets (Uploads, nur lesen)
  • /chats/<chatId>/workspace (dein Arbeitsbereich, schreiben erlaubt)
  • /chats/<chatId>/workspace/out (Outputs)
  • /chats/<chatId>/workspace/tmp (temporär)
  • /chats/<chatId>/.tool-results (Tool-Artefakte)

Wichtig: Das aktuelle Arbeitsverzeichnis startet bereits in .../workspace. Nutze deshalb bevorzugt relative Pfade wie out/result.json statt workspace/out/result.json.

Verfügbare fs-Funktionen

  • fs.pwd() – aktuelles Verzeichnis
  • await fs.cd(path) – Verzeichnis wechseln
  • await fs.ls(dir?, { glob?, limit?, offset?, withStat? }) – Dateien/Ordner listen (liefert u. a. entries; das Ergebnis ist auch iterierbar)
  • await fs.readdir(dir?) – nur Dateinamen (einfache Liste, intern über ls mit Limit)
  • await fs.stat(path) – Metadaten zu einer Datei oder einem Ordner (type, size, mtime, isFile() / isDirectory())
  • await fs.exists(path)
  • await fs.mkdir(path, { recursive? })
  • await fs.rm(path, { recursive? })
  • await fs.readText(path, { maxBytes? })
  • await fs.writeText(path, content)
  • await fs.readJson(path)
  • await fs.writeJson(path, value, { pretty? })
  • await fs.mv(src, dst, { overwrite? })
  • await fs.cp(src, dst, { recursive?, overwrite? })

API im Editor durchsuchen (help)

Nur im Chat- und Playground-Profil (wenn fs/mcp als Globals gesetzt sind):

  • await help() – Überblick und Beispiele zu den Runtime-APIs
  • await help("addChunks") – gezielt Signaturen und Kurzbeschreibungen (u. a. fs, mcp, Chunk-Funktionen, Event-Typen)

MCP-Tools nutzen (mcp)

Im Chat- und Playground-Profil: Wenn in eurem Space MCP-Server verbunden sind, kannst du sie über das Global mcp verwenden:

  • await mcp.tools() listet verfügbare Tools (Name + Beschreibung).
  • await mcp.call("serverName/toolName", { ...args }) ruft ein Tool auf. Erlaubt ist auch die Schreibweise serverName:toolName (Doppelpunkt statt Slash).

So kannst du z. B. „Suche → Filter → Export“ automatisieren, ohne alles manuell zu klicken.

Events abfragen (lastEvent, getEvents)

Für Automationen ist es oft wichtig, Ereignisse nachzuschlagen:

  • await lastEvent({ type?, objectId?, causedBy? }) – gibt das letzte passende Event zurück (oder null)
  • await getEvents({ type?, objectId?, causedBy?, limit?, cursor?, direction? }) – paginierte Liste
  • limit: optional, Standard 50, maximal 500
  • cursor: Sequenznummer (string) des letzten Events der vorherigen Seite (wie nextCursor in der Antwort)
  • direction: "desc" (Standard, neueste zuerst) oder "asc"

Das ist praktisch, um z. B. „Was ist zuletzt passiert?“ in einer Automation zu prüfen.

Wissen/Chunks bearbeiten (addChunks, deleteChunk, deleteChunks)

Du kannst Inhalte als Chunks in den Space schreiben oder entfernen. Die Logik entspricht grob dem Schreibpfad des RAG-Patch-Tools: bestehende Chunks mit derselben id werden überschrieben bzw. neu indexiert.

await addChunks(chunks)

Parameter: Array von Objekten (ChunkInput):

Feld Bedeutung
id Eindeutige ID des Chunks (wird intern in eine UUID übernommen).
text Textinhalt für die Indexierung. Wenn weggelassen, wird kein neuer Text geschrieben, sondern ein Re-Index des bestehenden Chunks ausgelöst (z. B. nach Aktualisierung der url).
url Optional: Referenz oder Binärdaten. Unterstützt u. a.: normale URL-Strings (https://…, interne Pfade wie /f/…), data:-URLs (werden automatisch hochgeladen und durch dauerhafte /f/…-Links ersetzt), Blob / ArrayBuffer / Uint8Array (Upload), Response-ähnliche Objekte (Bytes werden übernommen), sowie { fetch: string, init?: RequestInit, filename?: string, mime?: string }nur im Automation-Profil (Host lädt die URL; im Chat/Playground deaktiviert).
metadata Optionale Schlüssel/Werte (z. B. Dateiname/MIME-Hinweise bei Binärimport).
addedAutomatically Ob der Chunk als automatisch angelegt gilt (Standard: true).

Rückgabe: { chunkIds: string[] } — UUIDs der angelegten bzw. verarbeiteten Chunks.

await deleteChunk(chunkId) / await deleteChunks(chunkIds)

Löscht einen bzw. mehrere Chunks dauerhaft. Rückgabe jeweils mit deletedCount.

Typische Nutzung:

  • Ergebnisse als neue Wissenskarte/Chunk ablegen
  • veraltete automatische Chunks aufräumen
  • Inhalte nach erneuter Verarbeitung re-indexieren (text weglassen oder url/text aktualisieren)

Tools direkt aufrufen (tools.NAME)

Im Code gibt es ein tools-Objekt. Jedes verfügbare Tool kann wie eine Funktion aufgerufen werden, z. B.:

const res = await tools.rag_search({ query: "NDA", filter: undefined });

Welche Tool-Namen bei euch verfügbar sind, hängt von eurem Setup (Assistent/Space) ab.

Datum, Zeit und JSON

In der Sandbox steht dir normales JavaScript zur Verfügung — typischerweise new Date(), JSON.stringify / JSON.parse und die üblichen eingebauten Objekte der QuickJS-/Node-Kompatibilitätsschicht.

Zusätzlich pflegt der Bot in den TypeScript-Deklarationen (Autocomplete im Code-Editor) und in await help("…") (nur Chat/Playground) weitere Namen wie now, addDays, formatDate, parseJson, coalesce, usw. Welche davon zur Laufzeit wirklich als globale Funktion gebunden sind, kann vom Ausführungspfad und der Bot-Version abhängen — für portable Skripte sind die Standard-APIs die zuverlässigste Wahl.

Automation-Kontext (automation)

Im Sandbox-Code existiert die Konstante automation mit u. a.:

  • id — ID der Automation (z. B. für Abfragen früherer Läufe derselben Automation)
  • spaceId — Space
  • userId / actorId — auslösender Nutzer bzw. Actor (falls gesetzt)

Modul clye.ai (Import)

Neben den Globals kannst du aus "clye.ai" importieren (QuickJS-Bundling im Bot):

  • dispatchEvent(event) — Event im Space auslösen (type, optional objectId, data, metadata, causedBy, timestamp, id für Idempotenz)
  • callMcpTool(serverName, toolName, input) — MCP-Tool aufrufen
  • getMcpResource(serverName, uri) — MCP-Ressource lesen (text/blob/Metadaten)
  • lastEvent / getEvents — dieselben Event-Helfer wie global (wenn du sie explizit aus dem Modul beziehen willst)

Effizient bauen: erst Code, dann LLM

Ein wichtiger Grundsatz für produktive Automationen ist: Regeln und Code zuerst, LLM nur wenn nötig.

Das bedeutet typischerweise:

  • zuerst prüfen, ob sich überhaupt etwas geändert hat
  • zuerst filtern, ob ein Treffer fachlich relevant ist
  • zuerst einfache Entscheidungen mit Code treffen
  • erst danach chat/send_message oder einen anderen LLM-basierten Schritt aufrufen

Beispiel:

  • Ein Postfach wird regelmäßig geprüft.
  • Code erkennt zuerst, ob es neue E-Mails gibt und ob darunter überhaupt interessante Nachrichten sind.
  • Nur diese relevanten Nachrichten werden anschließend an einen Assistenten zur Zusammenfassung oder Antwortvorbereitung gegeben.

So bleibt die Automation effizient, ohne auf Qualität zu verzichten.

dispatchEvent (fortgeschritten)

In manchen Setups kann ein Skript auch Events auslösen — per import { dispatchEvent } from "clye.ai" (siehe Abschnitt Modul clye.ai (Import) weiter oben). Wenn ihr das nutzt, macht das am besten nur in klar definierten Automationen.

Ein sehr gutes Muster ist dabei:

  • Code erkennt zuerst eine Änderung oder einen fachlich relevanten Fall.
  • Danach wird ein eigenes Event dispatcht.
  • Eine andere Automation reagiert auf dieses Event.

Beispiel:

  • Ein Code-Schritt erkennt drei geänderte Dateien.
  • Er dispatcht drei getrennte Events.
  • Weitere Automationen können diese Änderungen unabhängig voneinander verarbeiten.

Das macht Abläufe meist einfacher, retrybarer und besser erweiterbar als eine einzige große Automation mit vielen direkten Folgeschritten.

Beispiele

Beispiel: Cross-Assistant-Dateizugriff (Ordner aus einem anderen Space listen und indexieren)

Manchmal liegt ein Dateisystem-Zugriff (z. B. über einen Filesystem-Connector) in einem anderen Assistenten/Space. Wenn du trotzdem gezielt einzelne Ordner in deinem aktuellen Assistenten indexieren möchtest, kannst du MCP-Tools raumübergreifend aufrufen.

Begriffe

  • Base-Assistent: Assistent/Space, der Zugriff auf das Dateisystem hat.
  • Specific-Assistent: Assistent/Space, der nur einen Teil davon (z. B. bestimmte Ordner) nutzen bzw. indexieren soll.

Voraussetzungen

  1. Öffne im Specific-Assistenten: Einstellungen → Berechtigungen.
  2. Lade den Base-Assistenten ein (damit der Specific-Assistent dessen MCP-Server/Tools verwenden darf).

Optional (Best Practice): Wenn im Base-Assistenten bisher „alles“ indexiert wird, kannst du in dessen Indexierungs-Automationen die Inhalte, die künftig „spezifisch“ verarbeitet werden sollen, ausklammern (z. B. Ordner exkludieren), damit nichts doppelt läuft.

MCP-Server-ID ermitteln

Die Server-ID findest du im Specific-Assistenten unter Einstellungen → MCP-Server:

  1. MCP-Server auswählen
  2. Auf das Zahnrad klicken
  3. Den Wert bei Server-Id kopieren

Tool-Aufruf im Automation-Code

Im Automation-Code rufst du das Tool über callMcpTool auf und gibst dabei zusätzlich die spaceId des Base-Assistenten an.

Parameter:

  • serverName: Name/ID des MCP-Servers (wie in den Einstellungen angezeigt)
  • toolName: Name des Tools auf diesem MCP-Server (z. B. list_directory)
  • input: Parameter-Objekt für das Tool
  • baseSpaceId: spaceId des Base-Assistenten (z. B. aus der URL des Base-Spaces)
const res = await process.env.callMcpTool(
  "Filesystem Connector",
  "list_directory",
  {
    include_hidden: false,
    path: "Vertrieb",
    recursive: true,
  },
  "123abc456-7899-56ec-bcdb-ef11801750b1"
);

Mit dem Ergebnis kannst du anschließend wie gewohnt Events dispatchen und diese in einer zweiten Automation indexieren lassen.

Empfehlung: Idempotenz beim Dispatch & Indexing

Wenn du pro Datei (oder pro gefundenem Objekt) ein Event auslöst und danach indexierst, achte darauf, dass du:

  • eine eindeutige objectId für das Event verwendest, damit Wiederholungen nicht zu Fehlern/Dubletten führen
  • beim Indexieren eine eindeutige uri verwendest

Praktisches Muster: einen Prefix voranstellen, z. B. "company-" + uri.

Beispiel: Upload lesen und Ergebnis als JSON speichern (Chat)

export default async function () {
  const input = await fs.readText("assets/input.txt");
  const lines = input.split("\n").map((l) => l.trim()).filter(Boolean);
  await fs.writeJson("out/result.json", { count: lines.length, lines });
  return { ok: true };
}

Beispiel: Tools kombinieren (Suche → Ergebnis speichern, Chat)

export default async function () {
  const result = await tools.rag_search({ query: "Vertrag", filter: undefined });
  await fs.writeJson("out/rag-result.json", result, { pretty: true });
  return { saved: true, items: result?.chunks?.length ?? 0 };
}

Kurz gesagt

Der integrierte MCP-Server JS ist der richtige Baustein, wenn ihr über reine Chat-Antworten hinaus wollt: wiederholbar, nachvollziehbar, mit Outputs, und mit der Möglichkeit, Tools und Datenverarbeitung in einem sicheren Code-Lauf zu kombinieren.