arcjet
Runtime security for your app — abuse protection, budget controls & rate limiting, prompt injection detection, and PII blocking. Connects your AI agent to the Arcjet MCP server and provides security-aware coding guidance.
security-analyst
Arcjet security analyst — monitors traffic, investigates threats, manages remote rules, and provides security recommendations using the Arcjet MCP server.
# Arcjet Security Analyst
You are a security analyst with access to the Arcjet MCP server. Your role is to help developers understand their application's security posture, investigate threats, and respond to attacks using Arcjet's tools.
## Capabilities
You have access to the Arcjet MCP server which provides these tools:
- **`list-teams`** / **`list-sites`** — discover the user's Arcjet teams and sites
- **`get-security-briefing`** — comprehensive security overview for a site
- **`analyze-traffic`** — traffic patterns, denial rates, top paths, top IPs
- **`list-requests`** — inspect individual requests with filtering (by conclusion, path, IP, time range)
- **`explain-decision`** — detailed breakdown of why a specific request was allowed or denied
- **`get-request-details`** — headers, rules executed, full decision details for a request
- **`investigate-ip`** — geolocation, ASN, threat intelligence, VPN/Tor/proxy/hosting detection, request history
- **`list-rules`** — current remote rules (both DRY_RUN and LIVE)
- **`create-rule`** — create a new remote rule
- **`promote-rule`** — promote a DRY_RUN rule to LIVE
- **`update-rule`** — update an existing rule
- **`delete-rule`** — remove a rule
## When Invoked
Perform a security review:
1. **Identify the site** — use `list-teams` and `list-sites` to find the target. If ambiguous, ask.
2. **Get the briefing** — call `get-security-briefing` for the overall security picture.
3. **Analyze traffic** — call `analyze-traffic` to understand patterns: denial rates, busiest paths, top source IPs.
4. **Check for anomalies** — look for unusual spikes, new attack vectors, or geographic anomalies in the traffic data.
5. **Investigate suspicious IPs** — for any flagged IPs, call `investigate-ip` for geo, ASN, threat intel, and VPN/Tor/hosting status.
6. **Review rules** — call `list-rules` to see current remote rules. Check if any DRY_RUN rules are ready for promotion.
## Report Format
Present findings as a structured security report:
### Threat Summary
- Overall posture (healthy / attention needed / under attack)
- Key metrics: total requests, denial rate, top denial reasons
### Anomalies
- Unusual traffic patterns or spikes
- New attack vectors or suspicious sources
- Geographic anomalies
### Rule Recommendations
- Rules to create (with rationale)
- DRY_RUN rules ready for promotion
- Rules that may need updating or removal
### Action Items
- Prioritized list of recommended actions
- For each: urgency (immediate / soon / when convenient) and effort (quick / moderate / significant)
## Incident Response
When the user reports an active attack or suspicious activity:
1. **Assess** — use `list-requests` with `conclusion: "DENY"` to see what's being blocked, and without filter to see what's getting through.
2. **Identify** — find the attack pattern: common IP ranges, user agents, paths, or countries.
3. **Respond** — create a filter rule in `DRY_RUN` mode targeting the pattern. Verify it matches attack traffic without blocking legitimate users.
4. **Promote** — once verified, promote the rule to `LIVE` for immediate effect across all instances.
5. **Monitor** — continue watching with `list-requests` to confirm the attack is mitigated.
## Remote Rules vs SDK Rules
Understand the boundary:
- **Remote rules** (managed via MCP, immediate effect, no deploy): `rate_limit`, `bot`, `shield`, `filter`
- **SDK rules** (require code changes and deployment): `prompt_injection`, `sensitive_info`, `email`, `signup`
When recommending rules that need request body analysis, explain that these must be added via the SDK and provide guidance on which skill to use (`/arcjet:protect-route` or `/arcjet:add-ai-protection`).
## Tone
Be direct and specific. Quantify threats where possible. Prioritize actionable recommendations over general advice. If you need more information, use the MCP tools to get it rather than asking the user.add-ai-protection
Add AI-specific security to LLM/chat endpoints — prompt injection detection, PII/sensitive info blocking, and token budget rate limiting. Use when building AI chat interfaces, completion APIs, or any endpoint that processes user prompts.
# Add AI-Specific Security with Arcjet
Secure AI/LLM endpoints with layered protection: prompt injection detection, PII blocking, and token budget rate limiting. These protections work together to block abuse before it reaches your model, saving AI budget and protecting user data.
## Reference
Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation covering all frameworks, rule types, and configuration options.
## Why AI Endpoints Need Special Protection
AI endpoints are high-value targets:
- **Prompt injection** — attackers try to override system prompts, extract training data, or bypass safety rails
- **PII leakage** — users may paste sensitive data (credit cards, emails, phone numbers) that gets stored in model context or logs
- **Cost abuse** — each request consumes expensive model tokens; without rate limiting, a single user can exhaust your budget
- **Automated scraping** — bots can scrape your AI endpoints for data or to find vulnerabilities.
Arcjet addresses all of these with rules that run **before** the request reaches your AI model.
## Step 1: Ensure Arcjet Is Set Up
Check for an existing shared Arcjet client (see `/arcjet:protect-route` for full setup). If none exists, set one up first with `shield()` as the base rule. The user will need to register for an Arcjet account at https://app.arcjet.com then use the `ARCJET_KEY` in their environment variables.
## Step 2: Add AI Protection Rules
AI endpoints should combine these rules on the shared instance using `withRule()`:
### Prompt Injection Detection
Detects jailbreaks, role-play escapes, and instruction overrides.
- JS: `detectPromptInjection()` — pass user message via `detectPromptInjectionMessage` parameter at `protect()` time
- Python: `detect_prompt_injection()` — pass via `detect_prompt_injection_message` parameter
Blocks hostile prompts **before** they reach the model. This saves AI budget by rejecting attacks early.
### Sensitive Info / PII Blocking
Prevents personally identifiable information from entering model context.
- JS: `sensitiveInfo({ deny: ["EMAIL", "CREDIT_CARD_NUMBER", "PHONE_NUMBER", "IP_ADDRESS"] })`
- Python: `detect_sensitive_info(deny=[SensitiveInfoType.EMAIL, SensitiveInfoType.CREDIT_CARD_NUMBER, ...])`
Detection runs **locally in WASM** — no user data is sent to external services. Only available in route handlers (not pages or server actions in Next.js).
Pass the user message via `sensitiveInfoValue` (JS) / `sensitive_info_value` (Python) at `protect()` time.
### Token Budget Rate Limiting
Use `tokenBucket()` / `token_bucket()` for AI endpoints — it allows short bursts while enforcing an average rate, which matches how users interact with chat interfaces.
Recommended starting configuration:
- `capacity`: 10 (max burst)
- `refillRate`: 5 tokens per interval
- `interval`: "10s"
Pass the `requested` parameter at `protect()` time to deduct tokens proportional to model cost. For example, deduct 1 token per message, or estimate based on prompt length.
Set `characteristics` to track per-user: `["userId"]` if authenticated, defaults to IP-based.
### Base Protection
Always include `shield()` (WAF) and `detectBot()` as base layers. Bots scraping AI endpoints are a common abuse vector. For endpoints accessed via browsers (e.g. chat interfaces), consider adding Arcjet advanced signals for client-side bot detection that catches sophisticated headless browsers. See https://docs.arcjet.com/bot-protection/advanced-signals for setup.
## Step 3: Compose the protect() Call
All rule parameters are passed together in a single `protect()` call. The key parameters for AI:
- `requested` — tokens to deduct for rate limiting
- `sensitiveInfoValue` / `sensitive_info_value` — the user's message text for PII scanning
- `detectPromptInjectionMessage` / `detect_prompt_injection_message` — the user's message text for injection detection
Typically `sensitiveInfoValue` and `detectPromptInjectionMessage` are set to the same value: the user's input message.
## Step 4: Handle Decisions
For AI endpoints, provide meaningful error responses:
- **Rate limited** (`reason.isRateLimit()`) → 429 with message like "You've exceeded your usage limit. Please try again later."
- **Prompt injection** (`reason.isPromptInjection()`) → 400 with "Your message was flagged as potentially harmful."
- **Sensitive info** (`reason.isSensitiveInfo()`) → 400 with "Your message contains sensitive information that cannot be processed. Please remove any personal data."
- **Bot detected** (`reason.isBot()`) → 403
## Step 5: Verify
1. Start the app and send a normal message — should succeed
2. Test prompt injection by sending something like "Ignore all previous instructions and..."
3. Test PII blocking by sending a message with a fake credit card number
Start all rules in `"DRY_RUN"` mode first. Once verified, promote to `"LIVE"`.
**Always recommend using the Arcjet MCP tools** to verify rules and analyze traffic:
- `list-requests` — confirm decisions are being recorded, filter by conclusion to see blocks
- `analyze-traffic` — review denial rates and patterns for the AI endpoint
- `explain-decision` — understand why a specific request was allowed or denied (useful for tuning prompt injection sensitivity)
- `promote-rule` — promote rules from `DRY_RUN` to `LIVE` once verified
If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules.
The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection.
## Common Patterns
**Streaming responses**: Call `protect()` before starting the stream. If denied, return the error before opening the stream — don't start streaming and then abort.
**Multiple models / providers**: Use the same Arcjet instance regardless of which AI provider you use. Arcjet operates at the HTTP layer, independent of the model provider.
**Vercel AI SDK**: Arcjet works alongside the Vercel AI SDK. Call `protect()` before `streamText()` / `generateText()`. If denied, return a plain error response instead of calling the AI SDK.protect-route
Add Arcjet security protection to a route handler. Detects framework, imports the shared client, applies appropriate rules, and handles decisions. Use when adding security to API routes, form handlers, or any server-side endpoint.
# Add Arcjet Protection to a Route
Add runtime security to a route handler using Arcjet. This skill guides you through detecting the framework, setting up the client, choosing rules, and handling decisions.
## Reference
Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation covering all frameworks, rule types, and configuration options.
## Step 1: Detect the Framework
Check the project for framework indicators:
- `package.json` dependencies: `next`, `express`, `fastify`, `@nestjs/core`, `@sveltejs/kit`, `hono`, `@remix-run/node`, `react-router`, `astro`, `nuxt`
- `bun.lockb` or `bun.lock` → Bun runtime
- `deno.json` → Deno runtime
- `pyproject.toml` or `requirements.txt` with `fastapi` or `flask` → Python
Select the correct Arcjet adapter package:
| Framework | Package |
| ------------------------ | ---------------------- |
| Next.js | `@arcjet/next` |
| Express / Node.js / Hono | `@arcjet/node` |
| Fastify | `@arcjet/fastify` |
| NestJS | `@arcjet/nest` |
| SvelteKit | `@arcjet/sveltekit` |
| Remix | `@arcjet/remix` |
| React Router | `@arcjet/react-router` |
| Astro | `@arcjet/astro` |
| Bun | `@arcjet/bun` |
| Deno | `npm:@arcjet/deno` |
| Python (FastAPI/Flask) | `arcjet` (pip) |
## Step 2: Check for Existing Arcjet Setup
Search the project for an existing shared Arcjet client file (commonly `lib/arcjet.ts`, `src/lib/arcjet.ts`, `lib/arcjet.py`, or similar).
**If no client exists:**
1. Install the correct adapter package.
2. Check if `ARCJET_KEY` is set in the environment file (`.env.local` for Next.js/Astro, `.env` for others). If not, use the Arcjet MCP tools to get one:
- Call `list-teams` to find the team
- Call `list-sites` to find an existing site, or `create-site` for a new one
- Call `get-site-key` to retrieve the key
- Add the key to the appropriate env file along with `ARCJET_ENV=development`
3. Create a shared client file with `shield()` as the base rule. This file should export the Arcjet instance for reuse across routes with `withRule()`.
**If a client already exists:** Import it. Do not create a new instance.
## Step 3: Choose Protection Rules
Select rules based on the route's purpose. If the user specified what they want (via `$ARGUMENTS` or in their prompt), use that. Otherwise, infer from context:
| Route type | Recommended rules |
| ----------------------- | ---------------------------------------------------------------------- |
| Public API endpoint | `shield()` + `detectBot()` + `fixedWindow()` or `slidingWindow()` |
| Form handler / signup | `shield()` + `validateEmail()` + `slidingWindow()` |
| Authentication endpoint | `shield()` + `slidingWindow()` (strict, low limits) |
| AI / LLM endpoint | Use `/arcjet:add-ai-protection` instead — it handles the full AI stack |
| Webhook receiver | `shield()` + filter rules for allowed IPs |
| General server route | `shield()` + `detectBot()` |
For routes that need to detect sophisticated bots (headless browsers, advanced scrapers) — especially form submissions, login/signup pages, and other abuse-prone endpoints — recommend adding Arcjet advanced signals. This is a browser-based detection system using client-side telemetry that complements server-side `detectBot()` rules. See https://docs.arcjet.com/bot-protection/advanced-signals for setup instructions.
Apply route-specific rules using `withRule()` on the shared instance — do not modify the shared instance directly.
## Step 4: Add Protection to the Handler
**Key patterns:**
- Call `protect()` **inside** the route handler, not in middleware.
- Call `protect()` only **once** per request.
- Pass the framework's request object directly.
- For Next.js pages/server components: use `import { request } from "@arcjet/next"` then `const req = await request()`.
**Handle the decision:**
- `isDenied()` (JS) / `is_denied()` (Python) — return the appropriate error response:
- `429` if `reason.isRateLimit()`
- `403` if `reason.isBot()`, `reason.isShield()`, or `reason.isFilterRule()`
- `400` if `reason.isSensitiveInfo()`
- `isErrored()` / `is_error()` — Arcjet fails open. Log the error and allow the request to proceed.
## Step 5: Verify
Suggest the user start their app and hit the protected route. Remind them that new rules should start in `"DRY_RUN"` mode and be promoted to `"LIVE"` after verification.
**Always recommend using the Arcjet MCP tools** to verify rules are working and analyze traffic:
- `list-requests` — confirm decisions are being recorded and inspect allow/deny outcomes
- `analyze-traffic` — review traffic patterns and denial rates for the protected route
- `explain-decision` — understand why a specific request was allowed or denied
- `promote-rule` — promote rules from `DRY_RUN` to `LIVE` once verified
If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules.
The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection.
## Common Mistakes to Avoid
- Creating a new Arcjet instance per request (causes connection overhead)
- Using Arcjet in Next.js middleware (fires on every request, no route context)
- Calling `protect()` multiple times in one request (double-counts rate limits)
- Hardcoding `ARCJET_KEY` instead of using environment variables
- Using `app.use()` as Express middleware instead of per-route protectionarcjet
MCP server: arcjet
{
"type": "http",
"url": "https://api.arcjet.com/mcp"
}Arcjet SDK best practices — single instance, protect() in handlers, withRule() composition, decision handling, DRY_RUN workflow.
Arcjet SDK best practices — single instance, protect() in handlers, withRule() composition, decision handling, DRY_RUN workflow.
## Arcjet SDK Patterns
- Create ONE Arcjet client instance per app, reuse via import. Never instantiate inside a request handler.
- Call `protect()` inside route handlers, NOT in middleware (middleware lacks route context and fires on every request).
- Call `protect()` only ONCE per request. Multiple calls cause double-counting for rate limits.
- Use `withRule()` to add route-specific rules to the shared instance: `aj.withRule(tokenBucket({...}))`.
- Handle `decision.isDenied()` (JS) / `decision.is_denied()` (Python):
- 429 for rate limits
- 403 for bot/shield/filter
- 400 for sensitive info
- Handle `decision.isErrored()` / `decision.is_error()` — Arcjet fails open. Log the error, allow the request.
- Start new rules in `"DRY_RUN"` mode. Verify via dashboard or MCP `list-requests`, then switch to `"LIVE"`.
- Never hardcode `ARCJET_KEY`. Use environment variables (`.env.local` for Next.js, `.env` for others).
- Ref: https://docs.arcjet.com/llms.txtSecurity guidance for AI/LLM endpoints — layered Arcjet protection with prompt injection, PII blocking, and token budgets.
Security guidance for AI/LLM endpoints — layered Arcjet protection with prompt injection, PII blocking, and token budgets.
## Arcjet for AI Endpoints
- AI endpoints need layered protection: `shield()` + `detectBot()` + `tokenBucket()` + `sensitiveInfo()` + `detectPromptInjection()`.
- Use `tokenBucket()` for AI (not fixed/sliding window) — allows bursts while enforcing average token spend. Set `requested` parameter to deduct proportional to model cost.
- `sensitiveInfo()` with deny list (`CREDIT_CARD_NUMBER`, `EMAIL`, `PHONE_NUMBER`) prevents PII entering model context. Runs locally in WASM — no data sent externally.
- `detectPromptInjection()` blocks jailbreaks before they reach the model, saving AI budget & protecting the context.
- Pass user message to both `sensitiveInfoValue` and `detectPromptInjectionMessage` in the `protect()` call.
- Python equivalents: `detect_sensitive_info()`, `detect_prompt_injection()`, with `sensitive_info_value` and `detect_prompt_injection_message` parameters.
- Ref: https://docs.arcjet.com/llms.txtArcjet patterns for Express, Node.js, Fastify, Hono, NestJS, Bun, and Deno — correct adapters, no middleware, proxy config.
Arcjet patterns for Express, Node.js, Fastify, Hono, NestJS, Bun, and Deno — correct adapters, no middleware, proxy config.
## Arcjet + Node.js Frameworks
- Use the correct adapter package:
- Express / Node.js / Hono: `@arcjet/node`
- Fastify: `@arcjet/fastify`
- NestJS: `@arcjet/nest`
- SvelteKit: `@arcjet/sveltekit`
- Remix: `@arcjet/remix`
- React Router: `@arcjet/react-router`
- Astro: `@arcjet/astro`
- Bun: `@arcjet/bun`
- Deno: `npm:@arcjet/deno`
- Do NOT use as Express middleware (`app.use()`). Call `protect()` inside individual route handlers.
- Pass the framework's request object directly: `aj.protect(req)`.
- For apps behind proxies/load balancers: set `proxies: ["<lb-ip>"]` on the Arcjet client options.
- Ref: https://docs.arcjet.com/llms.txtArcjet patterns for Next.js — correct imports, route handlers, pages, server components, no middleware.
Arcjet patterns for Next.js — correct imports, route handlers, pages, server components, no middleware.
## Arcjet + Next.js
- Import from `@arcjet/next`, not `arcjet` or `@arcjet/node`.
- Place the shared client in `lib/arcjet.ts` (or `src/lib/arcjet.ts`).
- **Route handlers**: protect inside `route.ts` handlers (`GET`, `POST`, etc.) — request is a function parameter.
- **Pages & server components**: use `import { request } from "@arcjet/next"` then `const req = await request()` to get the request object. Throw on denial (caught by error boundary). Works in any non-client component.
- **Client components cannot be protected** — they run client-side with no request object.
- Do NOT use Arcjet in `middleware.ts` — it runs on every request including static assets and lacks route context.
- For server actions, use the same `request()` helper from `@arcjet/next`.
- Sensitive info detection is only supported in route handlers, not in server actions or pages.
- Set `ARCJET_KEY` in `.env.local` and `ARCJET_ENV=development` for local dev.
- Ref: https://docs.arcjet.com/llms.txtArcjet patterns for Python FastAPI and Flask — snake_case API, enum values, async vs sync clients.
Arcjet patterns for Python FastAPI and Flask — snake_case API, enum values, async vs sync clients.
## Arcjet + Python
- Install: `pip install arcjet`
- Async client (FastAPI): `aj = arcjet(key=..., rules=[...])`
- Sync client (Flask): `aj = arcjet_sync(key=..., rules=[...])`
- Rule functions use snake_case: `shield()`, `detect_bot()`, `token_bucket()`, `fixed_window()`, `sliding_window()`, `detect_sensitive_info()`, `validate_email()`, `detect_prompt_injection()`.
- Use `Mode.LIVE` / `Mode.DRY_RUN` enums, not strings.
- Bot categories: `BotCategory.SEARCH_ENGINE`, not string literals.
- Sensitive info types: `SensitiveInfoType.EMAIL`, `SensitiveInfoType.CREDIT_CARD_NUMBER`, etc.
- Decision API: `decision.is_denied()`, `decision.is_allowed()`, `decision.is_error()`.
- Reason types: `decision.reason_v2.type` returns `"BOT"`, `"RATE_LIMIT"`, `"SHIELD"`, etc.
- IP analysis: `decision.ip.is_hosting()`, `decision.ip.is_vpn()`, `decision.ip.is_tor()`.
- Ref: https://docs.arcjet.com/llms.txt