integrate-arcjet-guard-genkit
Integrate Arcjet security into a Genkit JS agent using @arcjet/guard — wrap ai.defineTool, put guardMiddleware on generate({ use }) for unwrapped / MCP / filesystem tools, and read a caller-owned id from generate({ context }). Use when asked to add Arcjet to genkit, rate limit its tools, screen inbound messages, or block prompt injection / PII.
git clone --depth 1 https://github.com/arcjet/arcjet-js /tmp/integrate-arcjet-guard-genkit && cp -r /tmp/integrate-arcjet-guard-genkit/arcjet-guard/skills/integrate-arcjet-guard-genkit ~/.claude/skills/integrate-arcjet-guard-genkitSKILL.md
# Integrate Arcjet Guard into a Genkit app
`@arcjet/guard`'s Genkit v1 namespace wraps the agent's existing Arcjet
client. It never talks to the Arcjet API itself. Three surfaces, one
decision rule:
- **An authored tool** (`ai.defineTool(config, handler)`) → `guardTool()`.
After `defineTool` the object is a `ToolAction`; `generate()` calls it
as a function. DENY returns a structured `ArcjetDenialResult`. Do not
throw. Do not call `interrupt()`. Do not throw `ToolInterruptError`.
- **Filesystem / MCP / unwrapped tools** → `guardMiddleware()`. A
`generate({ use })` middleware whose `tool` hook is the generate()-wide
gate. It denies by returning a completed `ToolResponsePart` without
calling `next()`. Already-branded tools are skipped when they can be
looked up. Requires the `generateMiddleware` `tool` hook (Genkit >=
1.33).
- **Correlation** → `genkitContext()` reads a field the integrator put
on `generate({ context })` / the tool handler's `{ context }`
(`correlationId`, then `sessionId`, then `conversationId`, then a
caller-owned flow / run id). It never mints a new id. It never reads
`traceId`. It never treats `interrupt` / `resumed` as correlation.
This namespace is JS **`genkit()` + `ai.defineTool` + `ai.generate`**.
Not Go / Python Genkit. Do not also wrap the same tool with
`@arcjet/guard/vercel-ai/v7`. Zod is Genkit's, not ours.
## Screen user text before `generate()` — there is no inbound hook. Middleware `model` is not Guard.
There is no first-class inbound channel, so there is no `guardInbound`.
Put prompt-injection (and other inbound rules) in the application before
`ai.generate()` / `chat.send()`. The middleware `model` hook intercepts
the model call, not user text. It is not this policy gate.
## `interrupt()` / `defineInterrupt` / `toolApproval` are HITL, not a policy gate.
`interrupt()` / `defineInterrupt` / `@genkit-ai/middleware`
`toolApproval` / `restartTool` / `finishReason === "interrupted"` is
human-in-the-loop. Same trap as Mastra `requireApproval`, Claude
`canUseTool`, LangGraph `interrupt()`, and OpenAI Agents
`needsApproval`. There is no `guardApproval`. Do not wrap them as Guard.
## Deny inside `defineTool` (and `guardMiddleware`'s `tool` hook). MCP and filesystem-injected tools skip an unwrapped handler.
The authored `defineTool` handler is the deny point for tools you own.
Filesystem middleware tools, MCP tools, and anything not wrapped with
`guardTool` skip that handler. `guardMiddleware` is the generate()-wide
gate for those. `returnToolRequests: true` means the app calls the tool
itself — `guardTool` on the defineTool handler still gates that;
`guardMiddleware` does not run if they never `generate()` the tool.
`guardMiddleware` **can deny**. Genkit's `resolveToolRequest` treats a
`ToolResponsePart` returned without calling `next()` as a completed tool
result. Throwing `ToolInterruptError` sets `finishReason: "interrupted"`
(HITL — do not do this). Returning `undefined` drops the tool request
(do not do this).
`generate({ use })` must receive a **plain object `{ name, instantiate }`**.
A raw function becomes a *model* hook only. A function with `instantiate`
+ `plugin` throws “must be called with ()”.
## Questions to ask the human first
Ask only what you cannot infer from the code; suggest defaults.
1. Which tools are **risky** (external side effects, irreversible, spends
money, sends messages)? Those get `guardTool`. MCP / filesystem /
tools you did not author get `guardMiddleware`.
2. What **limits**? (e.g. "10 lookups/min per order" → `tokenBucket`.)
3. Who is the **user** for metadata — an opaque user/tenant ID (never PII)?
Default: none. Pass it via `metadata` on the policy. Put the
conversation / session id you already have on
`ai.generate({ context: { sessionId } })` *and* on
`guardMiddleware({ sessionId })` — the tool hook does not receive ALS
context today. That id is the correlation id, not the user.
4. Is an Arcjet outage unacceptable? Every helper defaults to
`onGuardError: "deny"`. Ask explicitly about inbound screening before
`generate()`: failing closed there means the agent does not run for
the duration of the outage, so `"allow"` is a routine and legitimate
choice at that one call site.
## The six things readers get wrong
1. **There is no `guardInbound`.** Screen prompt injection before
`ai.generate()` / `chat.send()`. Middleware `model` is not Guard.
2. **`interrupt()` is not a policy gate.** It is HITL. Use `guardTool`
or `guardMiddleware`. A denial is a completed `toolResponse`, not
`finishReason: "interrupted"`.
3. **The import path is versioned and there is no alias.**
`@arcjet/guard/genkit/v1`. `@arcjet/guard/genkit` does not resolve.
4. **Correlation is read, never minted.** Do not call `createAgentContext`
inside a generate / tool callback — that generates a second id and
splits the Sequence. Put the id you already chose on
`generate({ context })`. Do not read `Session.sessionId` from a Session
constructed without an id — that class mints a UUID. Do not use
`traceId` (OTel / Genkit mints one). Do not treat `interrupt` /
`resumed` as correlation.
5. **Do not double-wrap with `@arcjet/guard/vercel-ai/v7`.** `guardTool`
throws if the tool already carries the Arcjet protection brand.
6. **A denial from `guardTool` is a structured object, not a throw.**
Wrap the returned `ToolAction` (the callable `generate()` invokes),
not the inner handler. `outputSchema` validation runs *inside*
`action()`. Wrapping outside means DENY returns `ArcjetDenialResult`
without schema check, so the model still sees a completed tool
result. Wrapping the inner handler would throw on schema mismatch
and fail `generate()`.
## Step 1: Install and find the guard client
Install `@arcjet/guard` (required), plus `genkit` (optional peer, needed
for `@arcjet/guard/genkit/v1`). Always use the versioned path:
`@arcjet/guard/genkit/v1` resolves; `@arIntegrate Arcjet security into a Vercel AI SDK (v7) application using @arcjet/guard — wrap agent tools with guard checks, enforce rules on risky app actions, and emit audit events joined by one correlation ID. Use when asked to add Arcjet to an AI SDK app, protect or rate limit agent tool calls, guard AI agent actions, or audit what an agent did.
Integrate Arcjet security into a Claude Agent SDK agent using @arcjet/guard — wrap tool() handlers, screen inbound prompts with UserPromptSubmit, and deny unwrapped built-in/MCP tools with PreToolUse. Use when asked to add Arcjet to a Claude Agent SDK or Claude Code agent, rate limit its tools, screen inbound messages, or block prompt injection / PII.
Integrate Arcjet security into a Vercel Eve agent using @arcjet/guard — add guard gates to tools and connections, screen inbound messages, and record agent lifecycle events correlated to the session. Use when asked to add Arcjet to an Eve agent, rate limit its tools, guard connection access, or screen inbound messages.
Integrate Arcjet security into a LangChain JS createAgent using @arcjet/guard — wrap tool() / StructuredTool, put guardMiddleware on createAgent({ middleware }) for MCP / unwrapped tools, and read configurable.thread_id for correlation. Use when asked to add Arcjet to langchain createAgent, rate limit its tools, screen inbound messages, or block prompt injection / PII. This is LangChain JS, not the Python page.
Integrate Arcjet security into a LangGraph Graph API agent using @arcjet/guard — wrap tool() / StructuredTool, wrap ToolNode for unwrapped MCP tools, and read thread_id for correlation. Use when asked to add Arcjet to a LangGraph StateGraph / ToolNode agent, rate limit its tools, screen inbound messages, or block prompt injection / PII.
Integrate Arcjet security into a Mastra agent using @arcjet/guard — wrap createTool execute, screen input/output with a Processor tripwire, and gate unwrapped MCP/workspace tools with hooks. Use when asked to add Arcjet to a Mastra agent, rate limit its tools, screen inbound messages, or block prompt injection / PII.
Integrate Arcjet security into an OpenAI Agents text Agent using @arcjet/guard — wrap tool({ execute }), screen inbound before run(), and read a caller-owned id from runContext.context. Use when asked to add Arcjet to @openai/agents, rate limit its tools, screen inbound messages, or block prompt injection / PII.
Integrate Arcjet security into a Strands Agents JS app using @arcjet/guard — wrap tool({ callback }), put guardHooks on Agent({ plugins }) for unwrapped / MCP / vended tools, and read a caller-owned id from invocationState. Use when asked to add Arcjet to strands-agents, rate limit its tools, screen inbound messages, or block prompt injection / PII.