How-tos

How do I secure an OpenAI Agents SDK agent?

Wiring recipe for OpenAI Agents: screen text before run(), wrap authored tool(), and leave hosted tools off the deny list. Work through the full checklist in the OpenAI Agents security guide.

5 min read
In short: Wiring recipe for OpenAI Agents: screen text before run(), wrap authored tool(), and leave hosted tools off the deny list. Work through the full checklist in the OpenAI Agents security guide.

How do I secure an OpenAI Agents SDK agent?

Screen user text with a direct guard() call before run(). Wrap an authored tool() with guardTool so the check sits on FunctionTool.invoke. needsApproval and hosted requireApproval park a call for a person. They are not a policy.

The full checklist is the OpenAI Agents security guide. This page is the wiring recipe: install, inbound placement, and what this adapter can't deny.

This page is the OpenAI Agents SDK (Agent plus run() plus authored tool()). It isn't the Assistants API. OpenAI sunset Assistants on 2026-08-26. Search that still says "AI security for OpenAI Assistants and function calling" belongs here: the runtime that replaced Assistants.

protect() is the HTTP check on a route. The OpenAI Agents helpers are a direct guard() before run(), and guardTool on an authored tool. The product map is the OpenAI Agents agent guard.

What do I install and import?

Install @arcjet/guard and the OpenAI Agents SDK, then import the helpers from the versioned path:

Terminal window
npm install @arcjet/guard @openai/agents

Import from @arcjet/guard/openai-agents/v0. There is no unversioned alias, so @arcjet/guard/openai-agents doesn't resolve. The SDK is pre-1.0, so the segment is v0. @openai/agents (>=0.17.0 <1) is an optional peer, and the integration needs Node.js 22 or later. Launch one client at module scope with launchArcjet.

This adapter covers a text Agent plus run() plus authored tool(). It doesn't cover Realtime, Sandbox, hosted tools, MCP, agent.asTool(), or computer/shell. An integration skill ships in the package, so cp -r node_modules/@arcjet/guard/skills/integrate-arcjet-guard-openai-agents ~/.claude/skills/ gives a coding agent the same recipe.

How do I screen user text before run()?

OpenAI Agents has no inbound hook, so there is no guardInbound. Put prompt-injection rules in the application before run(agent, input). On deny, don't call run(). See How do I screen inbound prompts in OpenAI Agents SDK?.

inputGuardrails and outputGuardrails are OpenAI tripwires. They aren't Arcjet. Direct guard() fails open: gate on decision.hasFailedOpen() when this call site must fail closed.

Why isn't needsApproval a security policy?

needsApproval pauses the run for a person. Hosted MCP requireApproval is the same class of control. Neither is a remote allow or deny. See needsApproval and LangGraph interrupt() are not a security policy.

There's no guardApproval for OpenAI Agents. Don't wrap needsApproval as Guard.

What can guardTool stop?

guardTool wraps FunctionTool.invoke after tool({ execute }). On deny the original invoke never runs. The helper returns a plain ArcjetDenialResult. Don't throw. See the wrap sample in the OpenAI Agents security guide.

Pass wrapped tools on the Agent tools array, then call run(). guardTool warns when invoke is handed neither a string nor an object: the runner passes a JSON string, so a different shape means no arguments were scanned. Treat that warning as a wiring bug.

Hosted tools (webSearchTool, fileSearchTool, codeInterpreterTool), handoff, agent.asTool(), MCP servers (mcpServers), and computer/shell tools don't go through that authored invoke path. Runner agent_tool_start and agent_tool_end are observe-only. They aren't a deny point. There is no guardToolNode and no guardHooks for those paths. Don't also wrap these tools with @arcjet/guard/vercel-ai/v7.

An OpenAI Agents SDK agent that only wraps lookup_order still has an open send if the model can call webSearchTool or a mounted MCP server. The adapter cannot refuse those hosts. Plan for that gap: keep hosted tools off the agent, or accept that the deny stops at authored execute. For more information about the first-party compare, see OpenAI Agents SDK vs Claude Agent SDK. Claude PreToolUse can still deny Bash and MCP that you didn't wrap.

RunContext has no conversation or session id, so openaiAgentsContext() reads a field that you put on runContext.context: correlationId, then sessionId, then conversationId, then groupId. It then reads envelope copies (conversationId, groupId, and an already-resolved sessionId). A bare app object ({ sessionId }) and { context: appContext, conversationId } are both valid sources. It never mints an id, never reads traceId, and never calls session.getSessionId().

Put the id you already have on run(..., { context }). If nothing is a valid 1-256 printable-ASCII string, the call is uncorrelated rather than joined to a generated id. That is the safer failure: a generated MemorySession UUID is not a conversation that you will search for later. MemorySession mints a UUID when it's constructed without sessionId, so don't pass a Session and expect getSessionId() to run.

Is this page about OpenAI Assistants?

The OpenAI Agents SDK is the runtime this article covers. The Assistants API sunset on 2026-08-26. Function calling on Assistants is a different runtime, and this adapter doesn't wrap it.

A search for "AI security for OpenAI Assistants and function calling" still lands people here because Agents is the replacement. Screen the inbound string before run(). Wrap authored tool() with guardTool. Leave hosted tools, MCP, handoffs, and agent.asTool() off the deny list until the host exposes a local invoke. For more information about the stack map, see agent framework security.

Frequently asked questions

How do I secure an OpenAI Agents SDK agent?

Screen user text with a direct guard() call before run(). Wrap authored tool() with guardTool. This page is the wiring recipe. Work through the full checklist in the OpenAI Agents security guide.

Is this page about the OpenAI Assistants API?

No. This page is the OpenAI Agents SDK. The Assistants API sunset on 2026-08-26. Search for AI security for OpenAI Assistants and function calling still belongs here because Agents replaced Assistants.

Can guardTool deny hosted tools or MCP?

No. guardTool wraps FunctionTool.invoke after tool({ execute }). Hosted tools, MCP servers, handoffs, agent.asTool(), and computer/shell don't go through that path. Runner agent_tool_start is observe-only.

How do I install the OpenAI Agents helpers?

Run npm install @arcjet/guard @openai/agents and import from @arcjet/guard/openai-agents/v0. There is no unversioned alias. @openai/agents (>=0.17.0 <1) is an optional peer and the integration needs Node.js 22 or later.

Does a direct guard() call fail closed?

No. Direct guard() fails open. An ALLOW isn't proof the rules ran. Gate on decision.hasFailedOpen() when the inbound site must fail closed. guardTool already defaults to deny.

Do OpenAI output guardrails see an Arcjet denial?

Yes. guardTool returns the denial as the tool's output instead of throwing, so outputGuardrails and customDataExtractor receive the ArcjetDenialResult. timeoutMs also races the guard round trip as well as execute.

AI runtime security in your code

Protect your AI agent workflows with Arcjet

Screen user text with a direct guard call before run(), and wrap authored tools so the check sits on the invoke.