Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Integrate Arcjet Guard Cloudflare Think

BSecurity

Integrate Arcjet Guard into Cloudflare Think — delegate beforeToolCall to guardHooks so a DENY substitutes or blocks the tool, and read a caller-owned id via cloudflareThinkContext. Use when asked to add Arcjet to @cloudflare/think, Cloudflare Agents Think, rate limit those tools, screen inbound messages, or block prompt injection / PII. This is Cloudflare Think, not the Vercel AI SDK and not needsApproval HITL.

3 stars
0 votes
0 copies
0 views
Added 9/23/2026
ai-agentsjavascriptrustgojavabashnodenodejsgitapi

Works with

cursorcliapi

Security Analysis

B84/100
mediumInstalls packages at runtime which could introduce malicious dependencies
mediumInstalls packages at runtime which could introduce malicious dependencies

Scanned 9/23/2026

Install to Claude Code

$npx -y skills add arcjet/skills --skill integrate-arcjet-guard-cloudflare-think --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Integrate Arcjet Guard Cloudflare Think?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Integrate Arcjet Guard Cloudflare Think
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/arcjet-integrate-arcjet-guard-cloudflare-think/badge)](https://www.skillsdirectory.com/skills/arcjet-integrate-arcjet-guard-cloudflare-think)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: integrate-arcjet-guard-cloudflare-think
description: Integrate Arcjet Guard into Cloudflare Think — delegate beforeToolCall to guardHooks so a DENY substitutes or blocks the tool, and read a caller-owned id via cloudflareThinkContext. Use when asked to add Arcjet to @cloudflare/think, Cloudflare Agents Think, rate limit those tools, screen inbound messages, or block prompt injection / PII. This is Cloudflare Think, not the Vercel AI SDK and not needsApproval HITL.
license: Apache-2.0
compatibility: Requires official @cloudflare/think >=0.3.0 <1 as a peer of published @arcjet/guard. Install with npm install or pnpm add. Local Node.js follows the @arcjet/guard runtime floor (>=22.21.0 <23 || >=24.5.0). Cloudflare Workers need nodejs_compat and compat date 2025-09-01 or later (minimum, not an exact pin). Path is /v0. Do not use @arcjet/guard/vercel-ai/v7.
metadata:
  author: arcjet
  type: core
  library: "@arcjet/guard"
---

# Integrate Arcjet Guard into Cloudflare Think

`@arcjet/guard/cloudflare-think/v0` wraps the agent's existing Arcjet
client. It never talks to the Arcjet API itself. Shared Guard
fundamentals (client, rules, labels, decisions, capture, registration)
live in
[../arcjet/references/guards_javascript.md](../arcjet/references/guards_javascript.md).
Load that reference for anything that is not Think-specific.

Official `@cloudflare/think` `>=0.3.0 <1` only — that is the first
release whose `beforeToolCall` returns a functional `ToolCallDecision`
(`allow` / `block` / `substitute`) and wraps every server-side tool's
`execute` so the hook runs first. There is no `/v1` until Think ships
1.x. This is **not** the Vercel AI SDK (`ai` /
`@arcjet/guard/vercel-ai/v7`). Think owns `streamText` internally; do
not also wrap its tools with the AI SDK mix-in. Not TanStack Start HTTP
`protect()`. Not Cloudflare Workers HTTP `protect()` (`@arcjet/node` /
request-based).

Exports: `guardHooks`, `cloudflareThinkContext`. There is **no
`guardTool`** and no `guardThink` mixin. Skip is the
`beforeToolCall` return, not throw-from-execute. There is no
`guardInbound` and no `guardApproval`. No unversioned
`@arcjet/guard/cloudflare-think` alias.

Two surfaces, one decision rule:

- **Tool calls** → `guardHooks`. Returns a `{ beforeToolCall }`
  object the `Think` subclass **delegates to**. Delivery is a Think
  `ToolCallDecision`: **substitute** (`{ action: "substitute",
  output: ArcjetDenialResult }`) so the model sees the payload, or
  **block** (`{ action: "block", reason }`) so the model sees a
  reason string. `void` / `{ action: "allow" }` executes. Do not
  throw — a throw is a raw exception / `onChatError`, not a denial.
- **Correlation** → `cloudflareThinkContext` reads a caller-owned
  id. It never mints. It never reads Durable Object ids, `toolCallId`,
  `requestId`, or `traceId`.

Docs: https://docs.arcjet.com/guards/cloudflare-think/
Do not invent a second slug and do not overwrite any other
`/guards/...` page. Example (the `main` tree 404s; no example PR
is open, so cite this branch until
`examples/cloudflare-think-agent` exists on `main`):
[`examples/cloudflare-think-agent`](https://github.com/arcjet/examples/tree/david/cursor/cloudflare-think-agent-f8cc/examples/cloudflare-think-agent)
(no verify fixture). Do not invent a second example name.

## The gate is `beforeToolCall` block / substitute

Think wraps every server-side tool's `execute` and consults
`beforeToolCall` first (subclass hook, then extensions). Returning
`block` or `substitute` skips `execute`. Fail closed: always return
one of those on error — do not return `void` (that executes the
tool) and do not throw. Default DENY is **substitute** with
`ArcjetDenialResult` so the model sees `{ arcjetDenied: true, … }`.
`onDeny: "block"` uses `{ action: "block", reason }` instead and
drops the structured fields. `onDeny: "block"` applies to **real
DENY only**; unavailable still fail-closes with **substitute**.
Core `guard()` still fails open (`hasFailedOpen()`).

Client tools (no server `execute`) never enter this hook. Workspace
tools that Think auto-merges do, if they have `execute`.

## `needsApproval` is not a policy gate

AI SDK `needsApproval` / confirmation-style tools / human resume
are human-in-the-loop. After a human yes, Guard still runs on the
tool call. Same trap as TanStack `needsApproval`, Mastra
`requireApproval`, Claude `canUseTool`, LangGraph `interrupt()`,
OpenAI Agents `needsApproval`, Genkit `interrupt()`, and Google ADK
`requireConfirmation`. There is no `guardApproval`.

## Screen inbound before `chat()` / `submitMessages()`

There is no `guardInbound`. `beforeTurn` / `beforeStep` tune the
model call; they are not this policy gate. Call `arcjet.guard()` in
the application (or at the start of `beforeTurn` **and act on the
decision**) before the turn starts. Core `guard()` fails open:
`ALLOW` is not proof the rules ran. Gate on
`decision.hasFailedOpen()` if this call site must fail closed;
`guardHooks` already defaults to that.

## `actor` / `inputs` — omit and remote rules never fire

A remote policy that declares `actor` or typed `inputs` only
evaluates those values when this call sends them. Pass `actor` and
`inputs` on `guardHooks` (and on inbound `arcjet.guard()`). Import
`policyInput` from `@arcjet/guard`, not from the adapter path.
Check installed types — do not pass fields a helper does not
declare.

- **Omit `actor` / `inputs`** and a remote policy that requires
  them never fires. The guard call still happens; the remote rules
  just have nothing to read.
- **A resolver that throws fail-closes** under the default
  `onGuardError: "deny"` (unavailable substitute — never `void`).
- **`actor` is the authenticated caller**, never a model-produced
  tool argument. `input` on the Think envelope is model-controlled.
  Derive `actor` from authenticated server-side context (a signed
  session, verified token, or Durable Object state you already
  trust) — not from `ctx.input`.

`rules` / `action` / `metadata` see `{ toolName, input }`. `actor`
/ `inputs` resolvers also receive the Think `beforeToolCall`
context as the second argument.

## 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 are gated by
   `guardHooks`. Client tools with no `execute` are out of
   scope.
2. What **limits**? (e.g. "10 lookups/min per user" →
   `tokenBucket`.)
3. Who is the **user** for metadata — an opaque user/tenant ID
   (never PII)? Default: none. That same authenticated id is
   `actor`. Put the conversation / session id you already have on
   `guardHooks({ sessionId })`. That id is the correlation id, not
   the user. Do not use the Durable Object id or `toolCallId`.
4. Is an Arcjet outage unacceptable? Every helper defaults to
   `onGuardError: "deny"`. Ask explicitly about inbound screening
   before the turn: failing closed there means the agent does not
   answer, so `"allow"` is a routine and legitimate choice at that
   one call site.

## The things readers get wrong

1. **This is not the Vercel AI SDK.** Do not import
   `@arcjet/guard/vercel-ai/v7` and do not wrap Think tools with
   `guardTool` from that namespace. Think already owns
   `streamText`.
2. **There is no `guardInbound`.** Screen with core `guard()`
   before `chat()` / `submitMessages()`.
3. **`needsApproval` is HITL, not policy.**
4. **Fail closed = always return `block` or `substitute` on
   error.** `void` executes the tool. A throw is not a denial.
   Unavailable fail-closes with **substitute**, even when
   `onDeny: "block"`.
5. **Default DENY is substitute** (structured `ArcjetDenialResult`).
   `block` is the reason-string path and drops the fields. It
   applies to real DENY only.
6. **`guardHooks` is not a mixin.** Delegate
   `beforeToolCall` to `hooks.beforeToolCall(ctx)`. There is no
   `guardThink(Think, …)`.
7. **Correlation is read, never minted.** Never Durable Object id,
   never `toolCallId`, never `requestId`, never `traceId`.
8. **The import path is versioned.**
   `@arcjet/guard/cloudflare-think/v0` resolves;
   `@arcjet/guard/cloudflare-think` does not.
9. **Key rate limits on the authenticated caller**, not a
   model-supplied order id. Same for `actor`.
10. **Omit `actor` / `inputs` and remote rules never fire.** A
    resolver throw fail-closes. Import `policyInput` from
    `@arcjet/guard`.
11. **Do not hand-wrap every Think tool with raw `guard()`.**

## Step 1: Install and find the guard client

Install published `@arcjet/guard` and the `@cloudflare/think` peer.
Use the package manager the project already uses — do not pin a
git SHA:

```bash
npm install @arcjet/guard @cloudflare/think
pnpm add @arcjet/guard @cloudflare/think
```

Peer range is `@cloudflare/think` `>=0.3.0 <1`. The versioned path
`@arcjet/guard/cloudflare-think/v0` resolves; the unversioned
`@arcjet/guard/cloudflare-think` path does not. 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 tool calls — delegate `beforeToolCall`

Pass the **current conversation's** authenticated caller into
`guardHooks`. A Durable Object is often one-per-conversation — still
do not use `this.ctx.id` / `this.name` as `actor` or correlation.

```ts
import { Think } from "@cloudflare/think";
import { launchArcjet, policyInput, tokenBucket } from "@arcjet/guard";
import { guardHooks } from "@arcjet/guard/cloudflare-think/v0";

const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({
  bucket: "lookups",
  refillRate: 10,
  intervalSeconds: 60,
  maxTokens: 10,
});
// From auth middleware or Durable Object state you already trust.
// Not this.ctx.id / this.name, and not a model-produced argument.
const userId = authenticatedUserId;
const conversationId = authenticatedConversationId;

const hooks = guardHooks(arcjet, {
  action: ({ toolName }) => `${toolName}.invoked`,
  // Authenticated caller — never a model-produced order id / ctx.input.
  actor: userId,
  inputs: ({ toolName }) => ({
    tool: policyInput.server.string(toolName),
  }),
  // Keyed on the authenticated caller, not the model-supplied order id.
  rules: () => [lookupLimit({ key: userId, requested: 1 })],
  sessionId: conversationId,
  onGuardError: "deny",
  // default DENY is substitute (ArcjetDenialResult). onDeny: "block"
  // uses { action: "block", reason } on real DENY only. Unavailable
  // still substitutes.
});

export class SupportAgent extends Think<Env> {
  // needsApproval on a tool is HITL — not this policy gate
  override beforeToolCall = hooks.beforeToolCall;
}
```

If the subclass already implements `beforeToolCall`, call
`hooks.beforeToolCall(ctx)` and return its `ToolCallDecision`. Do
not void past a DENY and do not also wrap with
`@arcjet/guard/vercel-ai/v7`.

Omit `rules` to submit none — the guard call still happens. ALLOW
captures `outcome: "success"` when the policy lets the tool run,
not when `execute` finishes.

## Step 3: Screen inbound before the turn

```ts
import { detectPromptInjection, policyInput } from "@arcjet/guard";
import { cloudflareThinkContext } from "@arcjet/guard/cloudflare-think/v0";

const inbound = detectPromptInjection();
const decision = await arcjet.guard({
  label: "message.received",
  actor: userId,
  inputs: { content: policyInput.server.string(userText) },
  rules: [inbound(userText)],
  ...cloudflareThinkContext({ context: { sessionId: conversationId } }),
});
if (decision.conclusion === "DENY") {
  throw new Error("message blocked");
}
if (decision.hasFailedOpen()) {
  throw new Error("inbound guard unavailable");
}

// Then chat() / submitMessages() / the WebSocket turn.
```

There is no `guardInbound`. `guard()` fails open — always check
`hasFailedOpen()`.

## Step 4: Correlation

`cloudflareThinkContext` reads a caller-owned id. Preference:
`correlationId`, then `sessionId`, then `conversationId` on a
caller-owned wrap (`cloudflareThinkContext({ context: appContext })`),
then copies on a bare app object, then `guardHooks({ sessionId })` /
`init.correlationId`. It never mints. It never reads the Durable
Object id / `this.ctx.id` / `this.name`. It never reads `toolCallId`,
`requestId`, `traceId`, or stream ids Think or the AI SDK mint. A
`beforeToolCall` context that has `toolCallId` and `toolName` is
Think's envelope — top-level `sessionId` on that object is ignored.
Do not invent a correlation id per turn. If nothing valid remains,
the call is uncorrelated rather than joined to a generated id.

## Verify the integration

1. `npm run typecheck` (or the project's type-check) passes.
2. Exercise inbound PI (before the turn, including
   `hasFailedOpen()`), a substitute-deny (model sees
   `ArcjetDenialResult`), a block-deny (reason string; real DENY
   only), unavailable + `onDeny: "block"` still substitute, void
   execute, no-throw, never-mint, omitted `actor` / `inputs`
   (remote rules silent), a resolver throw (fail-closed
   substitute), and fail-closed (an unreachable guard →
   substitute, never void). Confirm `needsApproval` is never
   treated as the gate and that tools are not also wrapped with
   `@arcjet/guard/vercel-ai/v7`.
3. Confirm in the Arcjet Console / CLI that decisions share the
   caller-owned session / conversation id — not a Durable Object id
   or `toolCallId` — and that `actor` is the authenticated caller.
4. Manual E2E with a real `ARCJET_KEY` is still-to-verify until you
   run it.

Worked example:
[`examples/cloudflare-think-agent`](https://github.com/arcjet/examples/tree/david/cursor/cloudflare-think-agent-f8cc/examples/cloudflare-think-agent)
(no verify fixture; `main` does not have this tree yet). Do not
invent a second example name. Do not add an example in this skills
repo.

Attribution

arcjetarcjet
View sourceMore from arcjet →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Ultra-compressed communication mode that cuts output tokens while keeping technical accuracy. Levels: lite, full, ultra and the wenyan variants. Use for /caveman, "caveman mode", "talk like caveman", "be brief" or "less tokens".

1074701 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

693621 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3351 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

691 votes

math-skill

A comprehensive mathematical reasoning skill for AI assistants — handles arithmetic to research-level problems with rigorous step-by-step reasoning, systematic verification, and transparent uncertainty handling

381 votes
View all in ai-agents →