Skip to content

feat(guard): add Claude Agent SDK support as @arcjet/guard/claude-agent-sdk/v0 - #6229

Open
davidmytton wants to merge 4 commits into
mainfrom
david/cursor/guard-claude-agent-sdk-v0-16a6
Open

feat(guard): add Claude Agent SDK support as @arcjet/guard/claude-agent-sdk/v0#6229
davidmytton wants to merge 4 commits into
mainfrom
david/cursor/guard-claude-agent-sdk-v0-16a6

Conversation

@davidmytton

@davidmytton davidmytton commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Adds @arcjet/guard/claude-agent-sdk/v0 for the Claude Agent SDK: wrap authored tool() handlers, screen inbound prompts, and deny unwrapped / built-in tools. Correlation is read from session_id / options.sessionId — never minted. canUseTool is not a policy gate.

Full working demo: arcjet/examples#193.

import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";
import { guardTool, guardHooks } from "@arcjet/guard/claude-agent-sdk/v0";
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";

const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const limit = tokenBucket({ refillRate: 10, intervalSeconds: 60, maxTokens: 10 });

const lookupOrder = guardTool(
  arcjet,
  tool("lookup_order", "Look up an order", { orderNumber: z.string() }, async ({ orderNumber }) => ({
    content: [{ type: "text", text: `${orderNumber}: shipped` }],
  })),
  {
    action: "order.looked-up",
    rules: (input) => [limit({ key: input.orderNumber, requested: 1 })],
  },
);

const sessionId = conversationId;

for await (const message of query({
  prompt: userText,
  options: {
    sessionId,
    mcpServers: { app: createSdkMcpServer({ name: "app", tools: [lookupOrder] }) },
    hooks: guardHooks(arcjet, {
      sessionId,
      inbound: {
        action: "message.received",
        rules: ({ prompt }) => [detectPromptInjection()(prompt)],
      },
    }),
  },
})) {
  void message;
}
Open in Web Open in Cursor 

…nt-sdk/v0

Add a versioned @arcjet/guard subpath for the Claude Agent SDK: guardTool
for authored tool() handlers, guardHooks for UserPromptSubmit / PreToolUse /
PostToolUse, and claudeAgentContext that reads session_id and never mints
a correlation id. The SDK is an optional type-only peer so CI passes when
it is absent from node_modules.

Co-authored-by: David Mytton <davidmytton@users.noreply.github.com>
@cursor

cursor Bot commented Aug 15, 2026

Copy link
Copy Markdown

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@davidmytton
davidmytton requested a review from a team as a code owner August 15, 2026 12:38
@socket-security

socket-security Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​anthropic-ai/​claude-agent-sdk@​0.3.22180100929970

View full report

@arcjet-review arcjet-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arcjet Review — 🟡 Medium Risk

Decision: Needs Review

Rationale: New Claude Agent SDK namespace added following the exact pattern of existing mastra/v1 and vercel-eve/v0 namespaces. Additive-only: new export path, new source files, new tests. Optional peer dependency is correctly declared, CI verifies the peer is only used for types, renovate config restricts to <1 with automerge disabled. Fail-closed by default. Correlation IDs are validated and never minted. Double-wrap defense via shared symbol. Extensive test coverage including unavailability, denial shape, hook wiring, type-only imports, and package export map. Dependency and CI escalation triggers fired but the changes are well-scoped and consistent with prior integrations, so approving despite Medium risk.

Summary of Changes

Adds a new @arcjet/guard/claude-agent-sdk/v0 namespace exporting guardTool, guardHooks, and claudeAgentContext for integrating Arcjet security policy into Claude Agent SDK agents. Includes an integration skill, updated docs, CI job to verify type-only peer usage, and renovate config for the new optional peer.

PR Title & Description

These do not match the changes on the branch. They did not change this review's decision, but they will withhold approval once the other findings are resolved. Update them, then add the ai-review label to re-run the review.

  • description (missing): The PR description contains only the Cursor Cloud Agent boilerplate template and no description of what the branch changes. For a ~2000-line feature adding a new namespace, exports, docs, skill, CI job, and dependency, a future reader gets nothing beyond the title.
Suggested description
Adds `@arcjet/guard/claude-agent-sdk/v0`, a new vendor namespace that integrates Arcjet Guard with the Claude Agent SDK.

### What's exported

- `guardTool(client, tool, policy)` — wraps an authored `tool()` handler. On DENY the handler is not called and the model receives a `CallToolResult` with `isError: true` (never throws).
- `guardHooks(client, policy)` — Claude Agent SDK hooks:
  - `UserPromptSubmit` screens inbound prompts (DENY → `{ decision: "block" }`). This is the only place to attach inbound rules; there is no `guardInbound`.
  - `PreToolUse` gates built-ins and unwrapped MCP tools (DENY → `permissionDecision: "deny"`). `canUseTool` is intentionally not used — Claude skips it under `allowedTools`, allow rules, and `bypassPermissions` / `acceptEdits`.
  - `PostToolUse` is capture-only.
- `claudeAgentContext(source, init)` — reads correlation from hook `session_id` or `options.sessionId`; never mints an id.

### Package & peer

- New export map entry `./claude-agent-sdk/v0` (no unversioned alias, no wildcard).
- `@anthropic-ai/claude-agent-sdk` added as an optional peer (`>=0.1.0 <1`) and a devDependency. Only type-only imports reach the SDK; a new CI job (`Unit tests with claude-agent-sdk absent`) enforces that by removing the peer and running the namespace tests.
- Renovate pinned to `<1` with automerge disabled — the SDK is pre-1.0 and tracks Claude Code patch-for-patch.

### Docs & skill

- README section documenting the namespace, fail-closed defaults, and peer install.
- New `integrate-arcjet-guard-claude-agent-sdk` skill for Claude Code integration sessions.
- CONTRIBUTING.md updated to describe the Claude Agent SDK's decision points.

Escalation Triggers

  • Dependency Changes: arcjet-guard/package.json adds @anthropic-ai/claude-agent-sdk as devDependency and optional peer
  • CI/CD Pipeline: .github/workflows/guard.yml adds a new test job for the claude-agent-sdk-absent scenario

Notes

PR size is roughly 2000 lines — above the 1000-line threshold — but the shape is heavily test/docs (denial, gate, hooks, guard-tool, context, index, type-only, peer, assignability tests plus a 250-line SKILL.md), and each unit closely mirrors the existing mastra/v1 namespace, so it remained reviewable.

Path filtering: 1 file excluded by ignore paths. 24 of 25 files included in review.

Approval withheld: The PR title or description does not match the branch — see the "PR Title & Description" section. Update them, then add the ai-review label to re-run the review.

Review: 8a3bb568 | Model: anthropic/claude-opus-4-7 | Powered by Arcjet Review

Comment thread arcjet-guard/src/claude-agent-sdk/v0/guard-tool.ts
Comment thread arcjet-guard/src/claude-agent-sdk/v0/guard-tool.ts Outdated
CI runs `npm ci` with the pinned npm 12.0.1. The previous lockfile was
written with npm 10 and failed sync checks for the @sveltejs/kit override
and the new @anthropic-ai/claude-agent-sdk entry.

Co-authored-by: David Mytton <davidmytton@users.noreply.github.com>
@arcjet-review arcjet-review Bot added needs review Awaiting human review and removed needs review Awaiting human review labels Aug 15, 2026
…fineProperty

A throwing rules/metadata/sessionId factory now follows onGuardError instead
of crashing the tool. The wrapped handler is installed with defineProperty so
a non-writable original descriptor cannot throw at wrap time.

Co-authored-by: David Mytton <davidmytton@users.noreply.github.com>
@arcjet-review arcjet-review Bot added needs review Awaiting human review and removed needs review Awaiting human review labels Aug 15, 2026
Remove the repo-internal "do not add examples here" line from the README,
and drop the Eve/Mastra approval comparison from the Claude Agent SDK skill.

Co-authored-by: David Mytton <davidmytton@users.noreply.github.com>
@arcjet-review arcjet-review Bot added needs review Awaiting human review and removed needs review Awaiting human review labels Aug 15, 2026
@davidmytton
davidmytton enabled auto-merge August 15, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs review Awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants