Output Gate
The Output Gate is ToolMesh’s content control layer. JavaScript policies run before (pre) and after (post) every tool execution, enabling input validation, output filtering, and PII redaction.
How It Works
Section titled “How It Works”Gate policies are top-level JavaScript files in the policies/ directory. They are executed by goja, a Go-native JavaScript engine, with a 5-second timeout per policy.
Each policy receives a single global variable named ctx and runs in one of two phases:
pre— before the tool executes. Mutatectx.paramsto rewrite the request, orthrowto reject it.post— after the tool executes. Mutatectx.response.contentto filter the response.
Throwing a string or Error rejects the call. If the script completes without throwing, the call is allowed.
ctx reference
Section titled “ctx reference”| Field | Description |
|---|---|
ctx.phase | "pre" or "post" |
ctx.tool | Tool name with backend prefix (e.g. github_merge_pull) |
ctx.toolAccess | DADL access tag: "read", "write", "admin", "dangerous", or empty |
ctx.params | Tool parameters (mutate in pre to rewrite) |
ctx.response.content | Tool response (mutate in post to filter; only populated in post) |
ctx.user.userID | Authenticated user identifier |
ctx.user.callerId | Caller identity (e.g. cli, claude-desktop) |
ctx.user.callerClass | "trusted", "standard", or "untrusted" |
ctx.user.roles | Array of user role strings |
ctx.rateLimitExceeded(n) | Function — returns true if the user exceeded n calls/hour |
Examples
Section titled “Examples”Reject dangerous parameters (pre)
Section titled “Reject dangerous parameters (pre)”if (ctx.phase === "pre" && ctx.params.force_delete === true) { throw "force_delete is blocked by policy";}Redact PII from responses (post)
Section titled “Redact PII from responses (post)”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]" ); } }}Mutations on ctx.response.content propagate back to the live response — downstream evaluators and the executor see the redacted content.
Strip Co-Authored-By from GitHub PRs (pre)
Section titled “Strip Co-Authored-By from GitHub PRs (pre)”Many AI coding agents append Co-Authored-By trailers to commit messages and PR descriptions. A pre-gate policy can strip them before the GitHub API call:
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); }}The AI agent never notices the change, and no Co-Authored-By line reaches your repository.
Read-only enforcement for untrusted callers (pre)
Section titled “Read-only enforcement for untrusted callers (pre)”if (ctx.phase === "pre" && ctx.user.callerClass === "untrusted" && ctx.toolAccess !== "read") { throw "Caller class 'untrusted' may only execute read-tagged tools";}This relies on the DADL access tag (read, write, admin, dangerous) declared per tool — see the DADL spec.
CallerClass-Based Filtering
Section titled “CallerClass-Based Filtering”The gate receives the CallerClass, enabling tiered policies:
| CallerClass | Typical Filtering |
|---|---|
trusted | Credentials only |
standard | High-risk PII + credentials |
untrusted | All PII patterns, read-only tools |
Rate Limiting
Section titled “Rate Limiting”Policies can enforce a sliding-window rate limit per user:
if (ctx.phase === "pre" && ctx.rateLimitExceeded(100)) { throw "Rate limit exceeded (100/hour)";}Configuration
Section titled “Configuration”GATE_EVALUATORS=goja # Enable goja evaluator (default)Place policy files in the policies/ directory. ToolMesh loads them at startup.
Enterprise: Compliance-LLM
Section titled “Enterprise: Compliance-LLM”The enterprise extension adds an LLM-based gate evaluator that classifies content against compliance rules. This enables policies like “block responses containing financial advice” without writing regex patterns.