Skip to main content
ClaudeWave
Skill682 repo starsupdated 3d ago

integrate-arcjet-guard-eve

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.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/arcjet/arcjet-js /tmp/integrate-arcjet-guard-eve && cp -r /tmp/integrate-arcjet-guard-eve/arcjet-guard/skills/integrate-arcjet-guard-eve ~/.claude/skills/integrate-arcjet-guard-eve
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Integrate Arcjet Guard into a Vercel Eve agent

`@arcjet/guard`'s Vercel Eve v0 namespace wraps the agent's existing Arcjet
client. It never talks to the Arcjet API itself. Four surfaces, one decision
rule:

- **An authored tool** (`agent/tools/*.ts`) → `guardTool()` if you need its
  execution outcome at the call site, or `guardApproval()` if you only need to
  gate it. Only `guardTool` observes success or failure.
- **A connection's operations** (`agent/connections/*.ts`) → `guardApproval()`
  on the connection's `approval` field. There is no local `execute`; nothing
  else can gate these.
- **An inbound message** (`agent/channels/*.ts`) → `guardInbound()` to screen
  text before the agent sees it. This is the only place a turn can be declined
  before it starts.
- **Everything else** → `arcjetHooks()` to observe agent lifecycle events.
  Hooks are observe-only by design and cannot block.

The three in-session helpers correlate by session id, so their decisions land
on one Sequence. `guardInbound` runs before the session exists and correlates
by whatever identity the channel has, so its decision lands on a _second_
Sequence. `arcjetHooks` emits an `eve.session-started` record carrying both, which
is what lets you pivot from one to the other.

## Questions to ask the human first

Ask only what you cannot infer from the code; suggest defaults.

1. Which tools and connections are **risky** (external side effects,
   irreversible, spends money, sends messages)? Those get gates. Purely
   informational tools can be left unguarded or gated with no `rules`.
2. What **limits**? (e.g. "10 lookups/min per order" → `tokenBucket`;
   "5 integrations/hour" → `slidingWindow`.)
3. Who is the **user** for metadata — an opaque user/tenant/installation ID
   (never PII)? Default: the Eve principal from the session context.
4. Is an Arcjet outage unacceptable? Should the agent be blocked if the guard
   is unavailable? Every helper defaults to `onGuardError: "deny"`, including
   the channel. Ask explicitly about the channel anyway: failing closed there
   means the agent stops answering entirely 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

State plainly why each applies to Eve, not other frameworks:

1. **Hooks cannot reject a turn.** Their handlers return `void`. If the request
   is "block prompt injection", the answer is `guardInbound` at the channel, not
   a hook. Hooks are for audit trails, not enforcement.

2. **The import path is versioned and there is no alias.** `@arcjet/guard/vercel-eve/v0`.
   `@arcjet/guard/vercel-eve` does not resolve, and neither does `/v1`. The segment
   tracks Eve's major, and Eve is pre-1.0, so it gets `v0`. When Eve ships 1.0,
   a `/v1` path will be added alongside this one.

3. **Correlation is not passed; it is read from the session.** Never call
   `createAgentContext` inside an Eve callback — the session id already is the
   run identity, and generating a second one splits the Sequence. `eveAgentContext`
   is exported for callers who need the context explicitly. Three of the four
   helpers call it themselves; `guardInbound` runs before the session exists,
   so it takes an explicit `correlationId` instead.

4. **`approval` is one field per tool or connection.** It can be a function
   (request-time only) or `{ request, response }`. You still cannot assign
   `always()`/`once()`/`never()` from `eve/tools/approval` *alongside*
   `guardApproval` — the slot holds one value. `onAllow: "user-approval"` is
   how you require a human after the request-time gate. The optional `response`
   policy is how you authorize who may approve the parked request. A rejected
   response leaves the approval pending; it does not deny the tool. HITL
   clients answer with `cancel`, not `deny`. Request-time denials are still
   `{ type: "denied" }`.

5. **`defineDynamic` tools are not covered.** Eve's compiler hoists a dynamic
   tool's inline `execute` to a module-scope step function, so a wrapper is not
   visible to it. Gate those with `guardApproval()` instead — the approval gate
   runs at decision time.

6. **A denial from `guardTool` throws** (Eve projects it as a failed `action.result`),
   whereas a denial from `guardApproval` is a `denied` status carrying a reason the
   model reads. Prefer the gate when you want the model to adapt; use `guardTool`
   when you need the outcome.

## Step 1: Install and find the guard client

Install `@arcjet/guard` (required), plus `eve` (optional peer, needed for
`@arcjet/guard/vercel-eve/v0` and must be on Node 24+). Every agent helper lives
on that one path. Always use explicit versions: `@arcjet/guard/vercel-eve/v0`
resolves, but `@arcjet/guard/vercel-eve` does not — omitting the version is
deliberate (it prevents silent API breaking changes when a new major version is
supported). Attempting to import from an unversioned path throws
`ERR_PACKAGE_PATH_NOT_EXPORTED`.

```sh
npm install @arcjet/guard eve
```

**Note:** Eve requires Node.js >= 24. `@arcjet/guard` supports Node >= 22, but
the Eve integration does not. Verify the agent's `engines` declares `">=24"` or
note the floor in deployment docs.

If the agent has no guard client yet, launch one **once at module scope**:

```ts
import { launchArcjet } from "@arcjet/guard";

export const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
```

## Step 2: Gate authored tools

```ts
import { defineTool } from "eve/tools";
import { z } from "zod";
import { guardTool } from "@arcjet/guard/vercel-eve/v0";
import { tokenBucket } from "@arcjet/guard";

import { arcjet } from "../arcjet.js";

const lookupLimit = tokenBucket({
  bucket: "lookups",
  refillRate: 10,
  intervalSeconds: 60,
  maxTokens: 10,
});

export default guardTool(
  arcjet,
  defineTool({
    description: "Look up an order by ID",
    inputSchema: z.object({ orderId: z.string() }),
    async execute(input) {
      return
integrate-arcjet-guard-agentsSkill

Integrate 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-guard-claude-agent-sdkSkill

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-guard-genkitSkill

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.

integrate-arcjet-guard-langchainSkill

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-guard-langgraphSkill

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-guard-mastraSkill

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-guard-openai-agentsSkill

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-guard-strands-agentsSkill

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.