How-tos

How do I secure AI agents in production?

Screen inbound HTTP and channel text, gate every tool and MCP call before the side effect, and treat observe-only hooks as a diary. Then add budgets, local PII detection, and prompt-injection checks at those same boundaries.

4 min read
In short: Screen inbound HTTP and channel text, gate every tool and MCP call before the side effect, and treat observe-only hooks as a diary. Then add budgets, local PII detection, and prompt-injection checks at those same boundaries.

How do I secure AI agents in production?

That's AI agent runtime security and runtime security for LLM applications under a production question. The following pages are the framework recipes. Wire the helper that they name, on the surface that they name.

How do I screen inbound traffic?

A public chat route is still an HTTP endpoint. Hostile instructions, pasted cards, and automated clients arrive on that request. Screen the most recent user message before the provider call. Keep denied responses generic. Don't name the detector.

The HTTP screen sees the request. It doesn't see sendEmail or linear__create_issue later in the loop. A clean inbound score isn't authorization for a tool.

Channel-specific screens sit on the host, after signature checks and before the agent starts. Eve screens the Slack or GitHub body. Mastra screens processInput. Claude screens UserPromptSubmit. The preceding list links to those recipes.

How do I gate tools and MCP calls?

Most agent side effects never touch the original HTTP request. A tool handler receives function arguments. An MCP server injects a token. A queue job has no Request. The gate belongs immediately before the side effect.

One SDK that you can call from a tool handler looks like this. Inspect content that the tool is about to feed back to the model, and return a placeholder instead of the injected page.

import { launchArcjet, detectPromptInjection } from "@arcjet/guard";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
export async function fetchTool({ url }: { url: string }) {
const content = await fetch(url).then((r) => r.text());
const decision = await arcjet.guard({
label: "tools.fetch",
rules: [detectPromptInjection()(content)],
});
if (decision.conclusion === "DENY") {
return { content: "[Content blocked]" };
}
return { content };
}

MCP and OpenAPI connections often have no local execute to wrap. Eve puts the gate on the connection's approval field. Mastra denies unwrapped MCP, workspace, and toolset tools in a before-tool hook. Claude denies unwrapped built-ins on PreToolUse. Use the recipe for the host that you actually run.

Bot detection is an HTTP client problem. It doesn't sit on a tool.

Why aren't observe-only hooks a security policy?

A hook that logs session.started or "prompt injection" after the turn began is a diary of the miss. Eve hooks return void. They can't reject a turn.

If the request is to block inbound text, the answer is the inbound screen. If the request is to stop a send or an MCP create, the answer is the tool or connection gate. The hook doesn't get a chance to refuse.

Human approval isn't a policy gate either. Parking every call for a person trains reviewers to click through. Keep approval on the small irreversible set. Human approval is not a security policy is that argument.

How do I add budgets, PII checks, and prompt-injection detection?

Those three controls sit on the same two boundaries: the inbound text, and the action. They aren't a third product.

Prompt injection. Run it on inbound text before the provider call, and again on tool output that re-enters context. A clean score isn't authorization. The hardest cases look like a plausible business request. The layer that stops the outcome is the action gate.

PII. Classify the prompt in your own process so the raw body doesn't have to visit a second vendor to get labeled. Cards, emails, phones, and IPs are the usual first list. Names and addresses need a stronger local model. Detect and redact PII and keeping inspection local are the deeper pages.

Budgets. Put the limit at the tool call, keyed on an identity that you control, drawn down by cost. An HTTP rate limit counts workflow starts. One start can fan out 50 tools. Enforce token spend budgets is the recipe.

Start detection in dry-run, measure against real traffic, then switch to live. For the small set of irreversible writes, require a human in your own workflow.

Frequently asked questions

How do I secure AI agents in production?

Screen inbound HTTP and channel text before a turn starts, gate every tool and MCP call before the side effect, and treat observe-only hooks as audit, not a deny. Add token budgets, local PII detection, and prompt-injection checks at those same boundaries.

Is inbound screening enough to secure an AI agent?

No. An inbound screen sees the request or channel body. It does not see a later send or MCP create. A clean inbound score is not authorization for a tool.

Can observe-only hooks enforce a deny?

No. A hook that logs after the turn began cannot reject the turn. Screen inbound text before the agent starts, and gate tools or connections before they run. See the Eve, Mastra, and Claude how-tos for the helper on each host.

Does a clean prompt-injection score authorize a tool call?

No. A clean score means this text was not flagged. It does not authorize sendEmail or a CRM write. The action gate is a different control, and it belongs inside the tool.

Where should budgets and PII checks run?

On the same two boundaries: inbound text, and the action. An HTTP rate limit counts workflow starts, not tool spend. PII classification should run in your process so the raw body does not visit a second vendor to get labeled.

AI runtime security in your code

Protect your AI agent workflows with Arcjet

Inbound screening, tool gates, budgets, and local PII in the path of the action.