OpenAI Agents SDK tool guardrails: how to use them on MCP tools, and what they skip

The OpenAI Agents SDK can check every tool call before it runs. That check covers function tools and local MCP servers, and skips hosted MCP, the shell tool and handoffs. Here is the map.

The short answer

In the OpenAI Agents SDK for Python, a tool input guardrail runs before a function tool executes and can skip the call, replace its output with a message, or raise a tripwire. A tool output guardrail runs after the tool and can replace the output or raise a tripwire.1 You attach them with tool_input_guardrails and tool_output_guardrails, on a single tool or on a local MCP server, where they apply to every tool the server exposes.1

They don’t cover everything. The docs list what they skip: handoffs, hosted tools including HostedMCPTool, built-in execution tools including ShellTool and ComputerTool, and agents used as tools.1 If your agent can run shell commands or use a hosted MCP server, those calls need a different control.

Every MCP tool call goes through your rules first. Here a delete that doesn’t say which rows is stopped before it reaches the database. These are the rules deedglass init starts with; the call is made up.

Three kinds of guardrail

The SDK uses the word guardrail for three different checks, and they run at different moments.

Guardrail Runs on When Can stop a tool call?
Agent input guardrail The user’s input, first agent only In parallel with the agent by default Not reliably: tools may already have run1
Agent output guardrail The final output, last agent only After the agent finishes No; the tools already ran1
Tool input guardrail One tool call’s arguments Before that call executes Yes1
Tool output guardrail One tool call’s result After that call No, but it can replace what the model sees1

The first row is the trap. By default, agent input guardrails run concurrently with the agent, so the agent may already have consumed tokens or run tools before a tripwire cancels it. Setting run_in_parallel=False makes them finish before the agent starts.1 If a guardrail is meant to stop something, it must finish before the thing it stops.

For tool calls, the tool input guardrail is the one that matters. It sees the tool’s name and arguments and runs before the side effect.

A tool input guardrail on an MCP server

Local MCP servers (MCPServerStdio, MCPServerStreamableHttp, and the older SSE transport) accept guardrails directly, and the SDK attaches them to every MCP tool that remains after filtering.2 Here is a guardrail that refuses SQL that drops or truncates, attached to a database MCP server:

Code Program code, for developers. The text around it says what it does.

import json, re
from agents import Agent, Runner, ToolGuardrailFunctionOutput, tool_input_guardrail
from agents.mcp import MCPServerStdio

DESTRUCTIVE = re.compile(r"\b(drop|truncate)\b", re.I)

@tool_input_guardrail
def no_drop_or_truncate(data):
    args = json.loads(data.context.tool_arguments or "{}")
    sql = str(args.get("sql") or args.get("query") or "")
    if DESTRUCTIVE.search(sql):
        return ToolGuardrailFunctionOutput.reject_content(
            "DROP and TRUNCATE are not allowed from this agent. Ask the user to run it."
        )
    return ToolGuardrailFunctionOutput.allow()

async def main():
    async with MCPServerStdio(
        name="db",
        params={"command": "npx", "args": ["-y", "your-postgres-mcp-server"]},
        tool_input_guardrails=[no_drop_or_truncate],
    ) as server:
        agent = Agent(name="orders", mcp_servers=[server])
        print((await Runner.run(agent, "Clean up the orders table")).final_output)

The shape follows the SDK’s own examples: parse data.context.tool_arguments, return reject_content(...) to refuse with a message the model reads, or allow().2

Two things to know about this pattern. The regular expression is a placeholder: DROP inside a comment trips it, and SQL split across separate calls passes it. A real check parses the SQL. And reject_content returns a message to the model, which will usually try something else. That is what you want for a refusal, but log it, because a model that keeps trying is worth knowing about.

The tools guardrails don’t see

This list, from the SDK’s guardrails page, is the most useful thing to check against your own agent.1

Tool or path Tool guardrails apply? Why
Function tools (@tool, function_tool) Yes The guarded pipeline
Local MCP server tools, with server-wide guardrails Yes Attached to every remaining tool2
HostedMCPTool No The Responses API executes it2
WebSearchTool, FileSearchTool, CodeInterpreterTool, ImageGenerationTool No Hosted tools
ShellTool, LocalShellTool, ComputerTool, ApplyPatchTool No Built-in execution tools
Handoffs No A separate pipeline
Agent.as_tool() No No guardrail options today

The shell row deserves attention. An agent with ShellTool can run psql with any SQL in it, and no tool guardrail sees that call. The same goes for ApplyPatchTool writing files. If you give an agent those tools, the control has to live outside the SDK: in the sandbox, the operating system user, or the credentials the shell can reach.

Hosted MCP is the other gap. Because OpenAI’s infrastructure makes the call, your process never gets a chance to inspect it. What you do get is approval: HostedMCPTool takes require_approval in its tool_config, with an on_approval_request callback for deciding in Python.2

Guardrails, filters and approvals together

The SDK offers three controls for MCP tools, and they combine well.

  1. Filters decide which tools exist. create_static_tool_filter takes an allow list and a block list, applying the allow list first. A callable filter can decide per agent.2 A tool that isn’t listed can’t be called, which beats any check on its arguments.
  2. Approvals decide whether a person must say yes. Local servers take require_approval: always, never, a per-tool map, or a callable. The callable doesn’t receive the call’s arguments, so it can only decide by tool name.2
  3. Guardrails decide on the arguments. They are the only one of the three that sees what the call will actually do.

Order matters too. For a tool that needs approval, input guardrails normally run after the approval, just before execution. ToolExecutionConfig(pre_approval_tool_input_guardrails=True) runs them before the approval prompt, and the call is still rechecked after.1 Running them first saves a person from approving a call that would have been refused anyway.

A checklist for an Agents SDK app

  1. List every tool the agent has, and mark which rows of the table above they fall in.
  2. Filter MCP tools down to the ones the task needs.2
  3. Put tool input guardrails on each local MCP server that can write.2
  4. Require approval for writes, by tool name, and run guardrails before approval.1
  5. Set run_in_parallel=False on any agent input guardrail meant to block.1
  6. Treat ShellTool and hosted tools as unguarded and limit them some other way.1
  7. Keep a record of refusals and approvals outside the model’s context, so you can review them later.

Where an outside gate fits

Guardrails live inside your Python process, next to the agent. That is convenient, and it means anything that runs outside that pipeline, or a second agent built without your guardrails, goes unchecked. An outside gate sits between the agent and the system it writes to, so every client meets the same rules.

We build one, Deedglass. Its docs carry recipes for nine agent frameworks, including the OpenAI Agents SDK, each a runnable file of 15 lines or fewer with pinned packages; no live model run backs them.3 The OpenAI recipe hands the agent Deedglass’s MCP server through MCPServerStdio, so its calls meet a tool-call policy and risky ones wait for a person’s approval. It is the same local-MCP path as the guardrail above, so it has the same gaps: it doesn’t see ShellTool or hosted MCP calls. For approvals on MCP tool calls in general, see our approvals post; for checks on the tool list itself, tool poisoning. To try the checks without installing anything, open the playground; to grade what an agent can reach on your own machine, run npx deedglass scan (the grade, explained).

Written with AI assistance and checked against our claims register.

The takeaway

OpenAI Agents SDK tool guardrails cover function tools and local MCP servers, not HostedMCPTool, ShellTool or handoffs; agent input guardrails run in parallel.

Read nextRequire approval for MCP tool calls: Claude Code, Cursor, then a gateway Try it: install in 7 s

Related on this site

More posts

Sources

  1. 1OpenAI Agents SDK (Python): Guardrails · openai.github.io · checked 26 October 2026
  2. 2OpenAI Agents SDK (Python): Model context protocol (MCP) · openai.github.io · checked 26 October 2026
  3. 3Deedglass docs: OpenAI Agents SDK · docs.deedglass.com · checked 26 October 2026

Published 26 October 2026, sources last checked 26 October 2026. If something here is out of date, write to hello@deedglass.com.

Try it on your own setup

npx deedglass scan

Grades only your own setup. Nothing is uploaded.

Pricing