# Output Gate: JS-Policies für MCP-Tool-Calls

> Output Gate führt goja-JS-Policies pre und post Execution aus — validiert Inputs, filtert Outputs. ctx-Referenz, PII-Redaktion-Beispiele, 5s-Timeout.

Canonical: https://www.toolmesh.io/de/output-gate/

Das Output Gate ist ToolMeshs Inhaltskontrollschicht. JavaScript-Policies laufen vor (pre) und nach (post) jeder Tool-Ausführung und ermöglichen Eingabevalidierung, Ausgabefilterung und PII-Schwärzung.

## Funktionsweise

Gate-Policies sind Top-Level-JavaScript-Dateien im `policies/`-Verzeichnis. Sie werden von [goja](https://github.com/dop251/goja) ausgeführt, einer Go-nativen JavaScript-Engine, mit einem Timeout von 5 Sekunden pro Policy.

Jede Policy erhält eine einzige globale Variable `ctx` und läuft in einer von zwei Phasen:

- **`pre`** — vor der Tool-Ausführung. `ctx.params` modifizieren, um den Aufruf umzuschreiben, oder `throw`, um ihn abzulehnen.
- **`post`** — nach der Tool-Ausführung. `ctx.response.content` modifizieren, um die Antwort zu filtern.

Wirft das Skript einen String oder `Error`, wird der Aufruf abgelehnt. Läuft es ohne Throw durch, wird der Aufruf zugelassen.

### `ctx`-Referenz

| Feld | Beschreibung |
|------|--------------|
| `ctx.phase` | `"pre"` oder `"post"` |
| `ctx.tool` | Tool-Name mit Backend-Präfix (z. B. `github_merge_pull`) |
| `ctx.toolAccess` | DADL-Access-Tag: `"read"`, `"write"`, `"admin"`, `"dangerous"` oder leer |
| `ctx.params` | Tool-Parameter (in `pre` mutierbar) |
| `ctx.response.content` | Tool-Antwort (in `post` mutierbar; nur in `post` befüllt) |
| `ctx.user.userID` | Authentifizierte Nutzerkennung |
| `ctx.user.callerId` | Caller-Identität (z. B. `cli`, `claude-desktop`) |
| `ctx.user.callerClass` | `"trusted"`, `"standard"` oder `"untrusted"` |
| `ctx.user.roles` | Array mit Nutzerrollen |
| `ctx.rateLimitExceeded(n)` | Funktion — liefert `true`, wenn der Nutzer `n` Aufrufe/Stunde überschritten hat |

## Beispiele

### Gefährliche Parameter ablehnen (pre)

```javascript
// policies/block-force-delete.js
if (ctx.phase === "pre" && ctx.params.force_delete === true) {
  throw "force_delete ist per Policy gesperrt";
}
```

### PII aus Antworten schwärzen (post)

```javascript
// policies/redact-email.js
if (ctx.phase === "post" && ctx.response && ctx.response.content) {
  for (var i = 0; i < ctx.response.content.length; i++) {
    var block = ctx.response.content[i];
    if (block && block.type === "text" && block.text) {
      block.text = block.text.replace(
        /[\w.-]+@[\w.-]+\.\w+/g,
        "[REDACTED]"
      );
    }
  }
}
```

Mutationen an `ctx.response.content` werden in die Live-Antwort zurückpropagiert — nachgelagerte Evaluatoren und der Executor sehen den geschwärzten Inhalt.

### Co-Authored-By aus GitHub-PRs entfernen (pre)

Viele AI-Coding-Agents hängen `Co-Authored-By`-Trailer an Commit-Nachrichten und PR-Beschreibungen. Eine Pre-Gate-Policy entfernt diese vor dem GitHub-API-Aufruf:

```javascript
// policies/strip-co-authored-by.js
if (ctx.phase === "pre") {
  var coAuthorPattern = /^[Cc]o-[Aa]uthored-[Bb]y:.*$/gm;

  function stripTrailer(text) {
    if (!text) return text;
    return text
      .replace(coAuthorPattern, "")
      .replace(/\n{3,}/g, "\n\n")
      .replace(/\n+$/, "\n");
  }

  if (ctx.tool === "github_create_pull" || ctx.tool === "github_update_pull") {
    if (ctx.params.body) {
      ctx.params.body = stripTrailer(ctx.params.body);
    }
  }
  if (ctx.tool === "github_merge_pull" && ctx.params.commit_message) {
    ctx.params.commit_message = stripTrailer(ctx.params.commit_message);
  }
  if (ctx.tool === "github_create_git_commit" && ctx.params.message) {
    ctx.params.message = stripTrailer(ctx.params.message);
  }
}
```

Der AI-Agent merkt nichts davon, und keine `Co-Authored-By`-Zeile erreicht das Repository.

### Read-only-Durchsetzung für untrusted Caller (pre)

```javascript
// policies/untrusted-readonly.js
if (ctx.phase === "pre" &&
    ctx.user.callerClass === "untrusted" &&
    ctx.toolAccess !== "read") {
  throw "CallerClass 'untrusted' darf nur Tools mit Access-Tag 'read' aufrufen";
}
```

Das setzt auf den DADL-`access`-Tag (`read`, `write`, `admin`, `dangerous`), der pro Tool deklariert wird — siehe [DADL-Spezifikation](https://dadl.ai/spec/dadl-spec-v0.1).

## CallerClass-basierte Filterung

Das Gate erhält die CallerClass und ermöglicht abgestufte Policies:

| CallerClass | Typische Filterung |
|-------------|--------------------|
| `trusted` | Nur Credentials |
| `standard` | Hochrisiko-PII + Credentials |
| `untrusted` | Alle PII-Muster, nur Read-Tools |

## Rate Limiting

Policies können ein Sliding-Window-Rate-Limit pro Nutzer durchsetzen:

```javascript
if (ctx.phase === "pre" && ctx.rateLimitExceeded(100)) {
  throw "Rate Limit überschritten (100/Stunde)";
}
```

## Konfiguration

```bash
GATE_EVALUATORS=goja           # goja-Evaluator aktivieren (Standard)
```

Policy-Dateien im `policies/`-Verzeichnis ablegen. ToolMesh lädt sie beim Start.

## Enterprise: Compliance-LLM

Die Enterprise-Erweiterung fügt einen LLM-basierten Gate-Evaluator hinzu, der Inhalte gegen Compliance-Regeln klassifiziert. Das ermöglicht Policies wie „Antworten mit Finanzberatung blockieren", ohne Regex-Muster schreiben zu müssen.
