How do you secure a Mastra agent?
Screen inbound messages with guardProcessor, wrap the execute function in an authored createTool with guardTool, and deny unwrapped MCP, workspace, and toolset tools with guardHooks.
A Mastra agent has three places where work can start: a message that reaches processInput, an authored createTool with a local execute function, and an MCP or workspace tool that has none. Those are different jobs. Treating them as one processor list is how createPullRequest still runs after you "blocked prompt injection."
protect() is the HTTP check on a route. The Mastra helpers are guardProcessor, guardTool, and guardHooks. mastraAgentContext() uses a thread, resource, or workflow.runId that the app already has. It never creates an ID. For more information about the helpers, see the Mastra agent guard documentation.
How do you screen inbound Mastra messages?
Put guardProcessor on inputProcessors. Mastra channels already call processInput. On DENY, processInput and processInputStep call abort(), and Mastra raises a tripwire. processInputStep screens later agentic steps so that a tool continuation can't skip the inbound gate.
That is a screen on untrusted content. For more information, see Screen inbound with guardProcessor.
import { Agent } from "@mastra/core/agent";import { guardProcessor } from "@arcjet/guard/mastra/v1";import { detectPromptInjection } from "@arcjet/guard";import { arcjet } from "./arcjet.js";
const inbound = guardProcessor(arcjet, { action: "message.received", rules: ({ text }) => [detectPromptInjection()(text)],});
export const agent = new Agent({ id: "support-agent", name: "support-agent", instructions: "Help the user.", model: "openai/gpt-4o", inputProcessors: [inbound],});Helpers default to onGuardError: "deny". "allow" is a legitimate choice on the inbound processor, because failing closed there would stop the agent from answering during an outage.
Why isn't requireApproval a security policy?
requireApproval parks the call until a person clicks. That stops the send if the reviewer sees enough context to refuse. It's a human hold, not a remote policy. For more information, see requireApproval is not a policy gate and human approval gates.
A person is a hold after the policy, or instead of one. Rubber-stamping every tool doesn't remove a lethal trifecta leg. The model has already seen the prompt.
How do you gate authored tools vs MCP and workspace tools?
Wrap createTool with guardTool. On DENY, the function never runs. The model gets a structured ArcjetDenialResult ({ arcjetDenied, reason, message, retryable }). It isn't a throw.
MCP, workspace, and toolset tools skip that wrap. Use guardHooks. beforeToolCall can return { proceed: false, output } so that those tools never execute. afterToolCall runs after Mastra has called the host. A log of the comment isn't a deny. Applying both helpers to the same authored tool double-calls the guard.
See Hooks can deny unwrapped tools. For MCP outside Mastra, see How to secure an MCP server or AI agent tool calls.
Are Mastra guardrails enough?
No. A processor reads the message. Mastra's PromptInjectionDetector and PIIDetector classify, redact, or abort text. They don't see the tool name. For more information about that split, see Mastra guardrails vs an action gate.
An action gate is the labeled allow or deny immediately before the side effect. For more information, see runtime security for LLM applications. If detectors are your only control, then createPullRequest has already run.
Frequently asked questions
How do I secure a Mastra agent?
Screen inbound messages with guardProcessor, wrap authored createTool execute with guardTool, and deny unwrapped MCP, workspace, and toolset tools with guardHooks. requireApproval is a human hold, not a policy gate.
Is requireApproval a policy gate?
No. requireApproval parks the call until a person clicks. It is human-in-the-loop, not a remote policy. Use guardTool for authored tools or guardHooks for unwrapped tools.
Where do I put prompt injection checks on Mastra?
On guardProcessor in inputProcessors. Mastra channels already hit processInput. On DENY, processInput and processInputStep call abort() and Mastra raises a tripwire.
What if Arcjet is down on the inbound processor?
Helpers default to onGuardError: "deny", which aborts the turn. "allow" is a legitimate choice on the inbound processor, because failing closed there would stop the agent answering during an outage.
If I wrap every createTool, are MCP tools covered?
No. MCP, workspace, and toolset tools have no local execute. Deny them with guardHooks on beforeToolCall. afterToolCall is observe-only.
AI runtime security in your code
Protect your AI agent workflows with Arcjet
Get allow, deny, and redact on agent actions before the side effect.