Skip to content

Integrations

The gate is one function call between “the model produced something” and “the user’s phone buzzed”. Each framework already has a place reserved for that decision; the adapters put the gate there. This page shows the whole loop per framework and links the examples in the repository.

Two of the examples need neither the framework nor a network. They import the package by name, read a JSON policy and a day of candidates with the clock taken from each line, and print the same thing every run:

git clone https://github.com/Bubblegunn/proactive-gate && cd proactive-gate
npm ci && npm run build
node examples/mastra/run.mjs
node examples/ai-sdk/run.mjs

An output processor. Mastra calls it after the model has produced the message and before anything reaches the user; on a rejection the processor calls abort() with the gate’s reason, and the tripwire response tells the model why the message did not go out.

import { Agent } from "@mastra/core/agent";
import { createGate, defaultChecks, MemoryStore } from "proactive-gate";
import { gateProcessor } from "proactive-gate/mastra";
const gate = createGate({ store: new MemoryStore(), checks: defaultChecks({ dailyLimit: 3, quietHoursFloor: "high" }) });
export const assistant = new Agent({
id: "calendar-assistant",
instructions: "Say the one thing the user should be told right now, or answer NONE.",
model: "openai/gpt-4o-mini",
outputProcessors: [gateProcessor({ gate, toInput: () => ({ user, candidate }) })],
});

Offline: examples/mastra/ runs ten candidates for two users through a policy with weekly and daily budgets; five go out, and the output names the check that stopped each of the other five. With a model: examples/mastra.ts.

The send tool has needsApproval: true, and the gate answers every approval request. A denied call carries the reason, so the model can plan around it; an approved call has already consumed the budget.

import { gateToolApproval } from "proactive-gate/ai-sdk";
const approve = gateToolApproval({ gate, toInput: (call) => ({ user, candidate: fromCall(call) }) });
const { approved, reason } = await approve(call);
// addToolApprovalResponse({ id: call.approvalId, approved, reason })

Offline: examples/ai-sdk/ answers nine approval requests under a policy that includes the TCPA preset; one of them is a critical alert at 23:00 that the legal window still refuses, because presets carry no priority bypass. With a model: examples/vercel-ai-sdk.ts.

Middleware around the send tool (proactive-gate/langchain): a watched tool call is refused with the gate’s reason as the tool result; other tools pass through. In a graph, the notify node asks the gate before it fires the tool: examples/langgraph.ts.

A tool-input guardrail (proactive-gate/openai-agents): the tripwire fires on a rejection and outputInfo carries the decision, including the surfaces an allowed message may use.

A PreToolUse hook. npx proactive-gate hook --policy policy.json --tool send_message reads the event on stdin and prints a permissionDecision for the matching tool; other tools print nothing. examples/claude-code-hook.json.

A skill teaches the coding agent that is writing your code when to put the gate in front of a message, which calls to make, and how to read the answer. In Claude Code:

claude plugin marketplace add Bubblegunn/proactive-gate
claude plugin install proactive-gate@proactive-gate

The skill is skills/proactive-gate/SKILL.md, in the repository and in the npm package, so an agent that reads skill folders can use it from node_modules/proactive-gate/skills/proactive-gate. A test checks every API name, option, preset, check id, subcommand and flag in it against what the package exports, so the skill cannot describe a function that no longer exists.

npx proactive-gate mcp --policy policy.json serves the gate to any MCP client over stdio. It has two tools: decide takes { user, candidate, now?, commit? } and returns the decision, including rejectedBy, reason and nextAllowedAt; explain takes the same input and returns the decision as plain sentences. Neither spends a budget unless decide is called with commit: true, which is the call to make when you are about to send. The gate keeps its counters in memory for the life of the process.

claude mcp add proactive-gate -- npx proactive-gate mcp --policy policy.json

The server is about 150 lines of JSON-RPC over standard input and output, with no MCP SDK and no dependency, and it gives the same answers as the library on every fixture.

The same policy file runs in the Python package (Gate.from_policy), sync or async, and passes the same fixtures. See Python.

Listing the gate where these frameworks live

Section titled “Listing the gate where these frameworks live”

The texts for a Mastra integrations page and a Vercel AI SDK docs pointer are in docs/integrations/ in the repository. If you maintain a framework and want the gate in its docs, open an issue and the page is yours to review.