Frameworks
OpenAI Agents SDK: Hello World, Tools and Handoffs
Checked 2026-10-31
Loading poll...
The OpenAI Agents SDK is a small framework for building agents in your own application, in Python or JavaScript. You define an agent with instructions, tools and optional handoffs to other agents, then run it. It is MIT-licensed and free; you pay OpenAI for model use. Checked 31 October 2026.
At a glance
OpenAI Agents SDK | |
|---|---|
Vendor | OpenAI |
Languages | Python ( |
License | MIT (both repositories) |
Core ideas | Agent, tools, handoffs, sessions, guardrails, tracing |
Where it runs | Inside your application |
Cost | The SDK is free; model and hosted-tool usage is billed by OpenAI; GPT-6.1 Sol is $2 and $10 per million input and output tokens |
Repo activity | Python: about 30,400 stars, pushed 30 October 2026. JavaScript: about 3,950 stars, pushed 30 October 2026 |
Related | Agents API (managed), Responses API (lower level), ChatKit (chat UI) |
What it is
The SDK is the "control the loop yourself" option in OpenAI's agent stack. OpenAI's own guide splits the stack into four starting points.
You want to | Start with |
|---|---|
Run an agent on the Codex harness managed by OpenAI | Agents API |
Control the agent loop in your application with reusable agents, tools and handoffs | Agents SDK |
Work directly with model responses | Responses API |
Add an embedded chat experience | ChatKit |
The comparison table in the guide is useful. With the Agents API, OpenAI runs the harness and saves the session; integration effort is low. With the Agents SDK, the SDK runs inside your application; effort is medium, and you own storage, approvals and runtime. With the Responses API you build the agent from scratch; effort is high.
Choose the SDK when you want orchestration in your own code, your own state store and your own deployment. Choose the Agents API when you want long-running tasks managed for you. If you are leaving Agent Builder, see OpenAI Agent Builder, because the SDK is the main code path out of it.
Install
# JavaScript
npm install @openai/agents zod
# Python
pip install openai-agents
export OPENAI_API_KEY=sk-...The JavaScript package asks for zod because tool parameters are described with Zod schemas.
Hello world
This is the quickstart, with one focused agent and one turn. The SDK makes the model call and returns a result object with the final output and the run history.
import asyncio
from agents import Agent, Runner
agent = Agent(
name="History tutor",
instructions="You answer history questions clearly and concisely.",
model="gpt-6-astra",
)
async def main() -> None:
result = await Runner.run(agent, "When did the Roman Empire fall?")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())import { Agent, run } from "@openai/agents";
const agent = new Agent({
name: "History tutor",
instructions: "You answer history questions clearly and concisely.",
model: "gpt-6-astra",
});
const result = await run(agent, "When did the Roman Empire fall?");
console.log(result.finalOutput);The model name in the docs' example is gpt-6-astra. Model names change, so check OpenAI's model list before copying it. The docs' advice is to get this loop working first and then add capability step by step, "rather than starting with a large multi-agent design."
Add a tool
The first capability you usually add is a function tool or a hosted OpenAI tool such as web search or file search. A Python function tool is a decorated function:
from agents import Agent, Runner, function_tool
@function_tool
def history_fun_fact() -> str:
"""Return a short history fact."""
return "Sharks are older than trees."
agent = Agent(
name="History tutor",
instructions="Answer history questions clearly. Use history_fun_fact when it helps.",
tools=[history_fun_fact],
)In JavaScript you build the tool with tool({ name, description, parameters, execute }), where parameters is a Zod object. The docstring (Python) or description (JavaScript) is what the model reads, so write it for the model, not for yourself.
Add specialist agents with handoffs
A common next step is to split the work into specialists and let a router delegate:
history_tutor = Agent(
name="History tutor",
handoff_description="Specialist for history questions.",
instructions="Answer history questions clearly and concisely.",
)
math_tutor = Agent(
name="Math tutor",
handoff_description="Specialist for math questions.",
instructions="Explain math step by step and include worked examples.",
)
triage_agent = Agent(
name="Homework triage",
instructions="Route each homework question to the right specialist.",
handoffs=[history_tutor, math_tutor],
)result.last_agent (Python) or result.lastAgent (JavaScript) tells you which specialist ended up answering. After a handoff, reuse that agent for the next turn when the specialist should stay in control. This is the same pattern Agent Builder drew as a graph of agent nodes.
State between turns
The docs give four options for carrying state into the next turn.
If you want | Use |
|---|---|
The full history kept in your application |
|
The SDK to load and save history | A session |
OpenAI to manage continuation state | A server-managed continuation ID |
To resume a run paused for approval or interruption |
|
The last row is how human approval works in SDK code: a run can pause and be resumed after a person decides.
Tracing
The normal server-side path includes tracing. The docs say to open the Traces dashboard as soon as the first run works, to inspect model calls, tool calls, handoffs and guardrails before tuning prompts. Tracing is also what the trace graders in OpenAI's evaluation tooling read. If you send code or prompts you consider sensitive, check what your tracing settings upload. The Python docs say tracing is on by default and give three ways to turn it off: set the environment variable OPENAI_AGENTS_DISABLE_TRACING=1, call set_tracing_disabled(True), or set tracing_disabled on the RunConfig of a single run. Model and function spans can hold sensitive inputs and outputs, and RunConfig.trace_include_sensitive_data controls whether they are captured. One caveat from the docs: disabling tracing stops new traces but does not discard data a processor already buffered.
MCP, tools and guardrails
The agents guide has sections on tools, skills and prompt caching, and the Agents API mentions support for MCP servers. The Agent Builder node reference describes MCP, file search and guardrails as tool nodes, and the SDK is where that functionality lives when you write it in code. The Python docs list four MCP routes, covered in the sections below, and two guardrail kinds plus tool guardrails.
Browse the MCP servers that agents commonly use in our MCP server list.
Models and vendor lock-in
The quickstart uses OpenAI models and OpenAI's hosted tools. The Python models page names two built-in OpenAI model classes, one for the Responses API (recommended) and one for Chat Completions. Whether you can use a non-OpenAI model depends on adapters we did not confirm. Treat it as OpenAI-first. If you need a framework that is model-neutral by design, see LangGraph or CrewAI. If your agent is a coding agent built on Claude, see the Claude Agent SDK.
Connect MCP servers
The Python docs offer four ways to use MCP, and the choice is about who holds the connection.
You want | Use |
|---|---|
OpenAI's Responses API to call a publicly reachable MCP server for the model |
|
To connect to a Streamable HTTP server you run or reach yourself |
|
To launch a local process and talk over stdin and stdout |
|
An older server-sent events transport |
|
Hosted tools push the whole round trip into OpenAI's infrastructure, so your code never lists or calls the tools. They currently work with OpenAI models that support hosted MCP in the Responses API. For a server you control, the Streamable HTTP route keeps the connection in your process. This example follows the docs, with the token read from an environment variable and the tool list cached:
import asyncio
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def main() -> None:
token = os.environ["MCP_SERVER_TOKEN"]
async with MCPServerStreamableHttp(
name="Docs server",
params={
"url": "http://localhost:8000/mcp",
"headers": {"Authorization": f"Bearer {token}"},
"timeout": 10,
},
cache_tools_list=True,
max_retry_attempts=3,
) as server:
agent = Agent(
name="Assistant",
instructions="Use the MCP tools to answer the questions.",
mcp_servers=[server],
)
result = await Runner.run(agent, "Add 7 and 22.")
print(result.final_output)
asyncio.run(main())Useful options from the docs: tool_filter exposes only a subset of a server's tools, require_approval adds a human approval policy for local MCP tools, and max_retry_attempts retries tool listing and calls with backoff. Filter and approve before you connect a server with write access, since the model can call anything you expose. The docs also note that the local transports depend on the mcp package, and that a major-version change of that package affects some options, so pin it. To build a server of your own, see how to build an MCP server.
Guardrails
Guardrails check input and output around an agent. The docs describe two kinds: input guardrails run on the initial user input, and output guardrails run on the final agent output. Where they run matters. Input guardrails run only for the first agent in a chain, and output guardrails only for the agent that produces the final answer. If you need a check around every custom function-tool call, including in handoffs, use tool guardrails, which run on each guarded call.
An input guardrail returns a result with a tripwire_triggered flag. When it is true, the SDK raises an exception and the run stops. This sketch follows the docs' pattern, with a simple check in place of a classifier:
from agents import (
Agent,
GuardrailFunctionOutput,
InputGuardrailTripwireTriggered,
RunContextWrapper,
Runner,
TResponseInputItem,
)
from agents.decorators import input_guardrail
@input_guardrail(run_in_parallel=False)
async def no_card_numbers(
ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
) -> GuardrailFunctionOutput:
text = input if isinstance(input, str) else str(input)
digits = "".join(ch for ch in text if ch.isdigit())
return GuardrailFunctionOutput(
output_info={"digits": len(digits)},
tripwire_triggered=len(digits) >= 13,
)
agent = Agent(
name="Support",
instructions="Answer support questions.",
input_guardrails=[no_card_numbers],
)
async def main() -> None:
try:
await Runner.run(agent, "My card is 4111 1111 1111 1111, please fix billing")
except InputGuardrailTripwireTriggered:
print("Blocked before the agent ran")Input guardrails have two modes. The default runs in parallel with the agent for the best latency, but if the tripwire fires the agent may already have used tokens. With run_in_parallel=False the guardrail finishes first, so a blocked request never reaches the model or any tool. Use blocking mode for checks that protect money or data. A digit counter is a crude rule, and a real guardrail often uses a small model as a classifier. The docs show that pattern, and the threshold you pick should come from labeled examples of your own traffic.
A worked cost at three volumes
The SDK is free, so the bill is model use. OpenAI's API prices from our pricing research are GPT-6.1 Sol at $2 input and $10 output per million tokens, GPT-6.1 Terra at $1 and $5, and GPT-6 Luna at $0.10 and $0.50. These sums use example token counts: one agent run that makes five model calls, reading 60,000 input tokens and writing 3,000 output tokens in total.
Model | One run | 100 runs | 5,000 runs | 50,000 runs |
|---|---|---|---|---|
GPT-6.1 Sol | $0.12 + $0.03 = $0.15 | $15 | $750 | $7,500 |
GPT-6.1 Terra | $0.06 + $0.015 = about $0.075 | $7.50 | $375 | $3,750 |
GPT-6 Luna | $0.006 + $0.0015 = about $0.0075 | $0.75 | $37.50 | $375 |
Prompts over 272,000 tokens bill at higher rates, and batch or Flex processing is half price, so check OpenAI API pricing before you plan a large job. The table shows why a router pattern pays: a triage agent on a small model that hands hard cases to a larger one costs far less than sending everything to the biggest. Handoffs are model decisions, though, so measure the routing accuracy on your own questions before you trust the saving.
From prototype to something you can run
- Pin the model name in config. Names change, and a code sample is the worst place to hold one.
- Put every tool behind a narrow function. Validate arguments in code, as with any tool the model calls.
- Decide where history lives before you have users, using the state table above.
- Turn on tracing in development and decide for production. Traces are how you debug a bad handoff. Switch off sensitive data capture if prompts contain personal data.
- Add approval before side effects. Pausing a run for approval and resuming it is built in.
- Keep a small regression set. Ten saved inputs with expected behaviour, run after each prompt change, catch most breakage.
- Set a spend limit on the API project, and alert on daily usage.
Limits and gotchas
- You own the runtime. Storage, approvals, deployment and sandboxing are your job, which is the trade for control.
- Model names move. Pin them in config, not in code samples.
- Two libraries. Python and JavaScript are separate repositories with different star counts and release rhythms; check the one you use.
- Handoffs are model decisions. A router can send a query to the wrong specialist. Test it with ambiguous inputs.
- Safety. OpenAI's safety guidance for agents applies: keep untrusted text out of developer messages, constrain data flow with structured outputs and put human approval before actions that matter.
SDK, Agents API or Responses API
This is the decision most newcomers get stuck on, so here it is in detail. The three sit at different heights.
Agents API | Agents SDK | Responses API | |
|---|---|---|---|
Use for | Long-running tasks where OpenAI manages the agent and saves its progress | Agents with custom tools and workflows in your application | Calling models directly or building from scratch |
Where the agent runs | OpenAI runs a managed Codex harness | Inside your application | Your application, with optional hosted orchestration |
State between tasks | Saved session configuration, turns and items | Your storage and SDK sessions, or Responses conversation state | Manual history, response chaining or Conversations |
Tool execution | Service-connected tools, application function handlers and an optional sandbox | Tools and integrations you configure | Hosted tools and tools your application runs |
Execution environment | OpenAI hosted sandbox, self-hosted sandbox or none | Your runtime and sandbox integrations | Your own |
Integration effort | Low | Medium | High |
OpenAI says the Agents API includes automatic context compaction, multi-agent orchestration, programmatic tool calling and MCP server support. The SDK "gives your application control over deployment, storage, approvals, and runtime integration," and its runner handles the loop and handoffs.
A way to choose: if the task runs for a long time and you do not want to build storage and a sandbox, look at the Agents API. If you need the logic in your own code and your own cloud, use the SDK. If you want to write the loop yourself, drop to the Responses API.
A sensible project layout
The SDK does not force a structure, so pick one that keeps agents testable. This is our suggestion, not OpenAI's.
File or folder | Holds |
|---|---|
| One module per agent: name, instructions, tools, handoffs |
| Plain functions with clear names and docstrings |
| Model names and the API key lookup, in one place |
| The entry point that calls |
| Saved inputs and expected outputs for regression checks |
Keep instructions in version control next to the code. When an agent misbehaves, the first thing to check is the prompt, and having it in a diff makes that quick.
Questions people ask
Do I need ChatKit? No. ChatKit is a chat UI you can put in front of an agent. The SDK works from a script, a worker or an API.
Can I use it with a coding agent? The SDK builds your own agent. For a ready-made coding agent, see Codex CLI or the other pages under AI coding agents.
Is it production ready? The repositories are active, with pushes on the day we checked. The docs still tell you to validate runtime configuration, tools, authentication, permissions and deployment yourself, which is a fair summary of what production readiness means with any framework.
Where do I learn more? The guide has pages on agent definitions, tools, running agents and tracing. Start at the quickstart and follow its "next steps".
Who it suits
- Developers who want a small, readable framework and already use OpenAI models.
- Teams migrating off Agent Builder who want the closest match in code.
- Anyone who wants tracing and evaluation tooling next to the framework.
It suits less well if you need many model vendors on equal footing, or a no-code builder. For a visual tool look at n8n or Flowise.
Compared with the other frameworks
Style | Best for | |
|---|---|---|
OpenAI Agents SDK | Small agent, tools and handoffs | Staying close to OpenAI's platform |
State graph | Long-running, stateful flows | |
Role-based crews | Teams of agents with roles | |
Claude Code's tools as a library | Coding and file-based agents on Claude |
See AI agent frameworks and agentic AI frameworks for the whole field.
Our take
This comes from OpenAI's guide, quickstart and the two repositories. We did not build an agent for this page.
The SDK's strength is that it is small. Agent, tool, handoff, run: you can read the whole quickstart in ten minutes and understand the model. That is also what makes it a good landing place for Agent Builder users, since the concepts map across.
The weakness is that it is OpenAI's. The docs, examples and tooling all assume OpenAI models and hosted tools. That is fine if you are committed to OpenAI and a risk if you are not. Keep the model name and the tools behind a thin layer of your own so you can move.
Start with the hello world, add one tool, then add one handoff, and open the trace after each. If that feels like the right amount of framework, stay. If you feel the need for graph state or role teams, compare the others.
Checklist
- Install the package for your language and set
OPENAI_API_KEY - Run the hello world with a model you have access to
- Add one tool and read its trace
- Add a handoff and test ambiguous questions
- Decide where history lives: your store, a session or a continuation ID
- Put approval in front of any action with side effects
Questions
How do I install the OpenAI Agents SDK?
For Python run pip install openai-agents. For JavaScript run npm install @openai/agents zod. Set OPENAI_API_KEY.
Is it open source?
Yes. Both the Python and JavaScript repositories are MIT-licensed.
What is the difference between the Agents SDK and the Agents API?
With the SDK the agent loop runs inside your application. With the Agents API, OpenAI runs a managed Codex harness and saves progress for long-running tasks.
Does the SDK replace Agent Builder?
OpenAI's migration guide offers it as one of two paths for Agent Builder workflows, which are scheduled to shut down on November 30, 2026.
Loading poll...
Sources
- github.com/openai/openai-agents-python/blob/main/docs/mcp.md
- github.com/openai/openai-agents-python/blob/main/docs/guardrails.md
- github.com/openai/openai-agents-python/blob/main/docs/tracing.md
- developers.openai.com/api/docs/guides/agents
- developers.openai.com/api/docs/guides/agents/quickstart
- github.com/openai/openai-agents-python
- github.com/openai/openai-agents-js
- developers.openai.com/api/docs/guides/agent-builder/migrate-from-agent-builder