OpenAI Agents SDK tutorial for Python
Checked against the official docs:
The OpenAI Agents SDK (openai-agents on PyPI) is OpenAI's open-source Python framework for building agents: a model with instructions, tools it can call, and the option to hand a conversation to another agent. This tutorial goes from an empty folder to an agent that uses your own Python functions, routes questions between specialists, remembers earlier turns and calls an MCP server. Every snippet follows the SDK's official docs, and the imports were checked against openai-agents 0.24.0 (the current release on 4 November 2026).
Quick answer
- Use Python 3.10 or newer. Make a virtual environment and run
pip install openai-agents. - Set
OPENAI_API_KEYin your environment. - Create an
Agent(name=..., instructions=...)and run it withRunner.run(agent, "your question")insideasyncio.run(...), orRunner.run_sync(...)in a plain script. - Read the answer from
result.final_output.
Free: MCP Starter Checklist
One page: Inspector smoke-test, Claude Code + Cursor connect steps, and the stdout gotcha. Join the list and download it free.
1. Install the SDK
The package needs Python 3.10 or newer. Make a project folder with its own virtual environment so the SDK and its dependencies stay out of your system Python:
mkdir my_project
cd my_project
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install openai-agentsIf you use uv, uv init and then uv add openai-agents does the same thing. The package is called openai-agents, but you import it as agents. Don't install the agents package from PyPI by mistake: that is a different project (TensorFlow Agents).
2. Set your API key
The SDK reads your key from the OPENAI_API_KEY environment variable. Create a key in the OpenAI dashboard, then set it in the terminal you run the script from:
export OPENAI_API_KEY=sk-...$env:OPENAI_API_KEY = "sk-..."That only lasts for the current terminal session. Keep the key out of your code and out of git. One option is a .env file that you load yourself and list in .gitignore.
3. Your first agent
An agent is a model plus instructions. Runner runs the loop: it calls the model, runs any tools the model asks for, and stops when the model gives a final answer. Save this as main.py:
import asyncio
from agents import Agent, Runner
agent = Agent(
name="History Tutor",
instructions="You answer history questions clearly and concisely.",
)
async def main():
result = await Runner.run(agent, "When did the Roman Empire fall?")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())python main.pyNo model is set here, so the SDK uses its default model. To choose one, pass model="..." to Agent, or set OPENAI_DEFAULT_MODEL for every agent that doesn't name one. The models page in the SDK docs lists the current default.
run, run_sync or run_streamed?
await Runner.run(...)is async. Use it in async code, web servers and anything that runs several agents at once.Runner.run_sync(...)wraps the same thing for plain scripts. It won't work inside an event loop that is already running, such as a Jupyter notebook. Useawait Runner.run(...)there.Runner.run_streamed(...)returns straight away and gives you events as they happen, so you can print text as it is written.
from openai.types.responses import ResponseTextDeltaEvent
result = Runner.run_streamed(agent, input="Tell me three facts about Rome.")
async for event in result.stream_events():
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
print(event.data.delta, end="", flush=True)4. Give the agent tools
A function tool is an ordinary Python function the model can decide to call. The SDK builds the tool's schema from the function name, the type hints and the docstring, so write the docstring for the model: say what the tool does and when to use it.
import asyncio
from agents import Agent, Runner
from agents.decorators import tool
@tool
def get_order_status(order_id: str) -> str:
"""Look up the shipping status of an order by its ID."""
# Replace this with a real database or API call.
return f"Order {order_id} shipped yesterday."
agent = Agent(
name="Support Agent",
instructions="Help customers with their orders. Use the tools to look things up.",
tools=[get_order_status],
)
async def main():
result = await Runner.run(agent, "Where is order 1042?")
print(result.final_output)
asyncio.run(main())The quickstart imports tool from agents.decorators. In 0.24.0, tool is an alias for function_tool, so code that uses from agents import function_tool and @function_tool works the same way.
The SDK also has hosted tools that run on OpenAI's side, such as WebSearchTool() and FileSearchTool(...), which you add to tools the same way. The docs list them as built-in tools for OpenAI's Responses models.
A tool can do anything your Python code can do. If it deletes, sends or pays for something, check the inputs inside the function and think about requiring a human approval step. The SDK's human-in-the-loop guide shows how to pause a run for approval.
5. Hand off between agents
A handoff lets one agent pass the conversation to another that is better suited to it. Give each specialist a handoff_description so the routing agent knows when to use it:
from agents import Agent, Runner
history_tutor = Agent(
name="History Tutor",
handoff_description="Specialist agent for historical questions",
instructions="You answer history questions clearly and concisely.",
)
math_tutor = Agent(
name="Math Tutor",
handoff_description="Specialist agent for math questions",
instructions="You explain math step by step.",
)
triage_agent = Agent(
name="Triage Agent",
instructions="Route each homework question to the right specialist.",
handoffs=[history_tutor, math_tutor],
)
result = Runner.run_sync(triage_agent, "What is 15% of 80?")
print(result.last_agent.name) # which agent answered
print(result.final_output)After a handoff, the specialist owns the rest of the conversation. If you want a manager agent that stays in charge and calls specialists like functions, use agents as tools instead:
manager = Agent(
name="Manager",
instructions="Answer the question. Use the tutors when you need them.",
tools=[
history_tutor.as_tool(tool_name="history_tutor", tool_description="Answers history questions"),
math_tutor.as_tool(tool_name="math_tutor", tool_description="Solves math problems"),
],
)6. Get structured output
Set output_type to a Pydantic model and final_output becomes an instance of it rather than plain text. That is much easier to use in code than parsing a sentence:
from pydantic import BaseModel
from agents import Agent, Runner
class Ticket(BaseModel):
category: str
urgent: bool
summary: str
classifier = Agent(
name="Ticket Classifier",
instructions="Classify the support message.",
output_type=Ticket,
)
result = Runner.run_sync(classifier, "My card was charged twice and I need it fixed today.")
ticket = result.final_output # a Ticket object
print(ticket.category, ticket.urgent)7. Remember earlier turns
Each Runner.run call starts fresh unless you pass the history back in. The simplest way is a session, which stores the conversation and adds it to the next run for you:
from agents import Agent, Runner, SQLiteSession
agent = Agent(name="Assistant", instructions="Reply very concisely.")
session = SQLiteSession("conversation_123") # in memory
# session = SQLiteSession("user_123", "conversations.db") # saved to a file
result = Runner.run_sync(agent, "What city is the Golden Gate Bridge in?", session=session)
print(result.final_output)
result = Runner.run_sync(agent, "What state is it in?", session=session)
print(result.final_output) # knows "it" means the bridgeUse one session ID per conversation (per user, per chat thread). Without a database file the history is lost when the program exits. If you want to manage history yourself, pass result.to_input_list() plus the new message into the next run. You can also let OpenAI keep the state with previous_response_id or conversation_id. The SDK's running-agents guide compares the options.
8. Connect an MCP server
MCP (Model Context Protocol) servers give an agent ready-made tools. The SDK can start a local server and expose its tools to the agent. This example uses the official Filesystem MCP server, limited to one folder. It needs Node.js 24 or newer for npx:
import asyncio
from pathlib import Path
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
notes_dir = Path(__file__).parent / "notes"
async def main():
async with MCPServerStdio(
name="Filesystem",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", str(notes_dir)],
},
client_session_timeout_seconds=30,
) as server:
agent = Agent(
name="Notes Assistant",
instructions="Answer questions using the files in the notes folder.",
mcp_servers=[server],
)
result = await Runner.run(agent, "List the files you can see and summarize the newest one.")
print(result.final_output)
asyncio.run(main())client_session_timeout_seconds defaults to 5 seconds. The example raises it to 30, as the example in the SDK's MCP docs does. npx -y may download the package before the server starts, so the SDK docs recommend installing the package before connecting. One way is npm install -g @modelcontextprotocol/server-filesystem and then "command": "mcp-server-filesystem" with just the folder in args. To give the agent only some of a server's tools, pass tool_filter=create_static_tool_filter(allowed_tool_names=[...]) (import it from agents.mcp). For Filesystem, ["read_text_file", "list_directory", "search_files"] makes it read-only. The SDK docs' example uses read_file. That name still works, but the server marks it deprecated in favor of read_text_file. More on this server: Filesystem MCP server setup.
For a server that runs on the web, use MCPServerStreamableHttp(name=..., params={"url": "https://...", "headers": {...}}) the same way. With OpenAI's Responses models there is also HostedMCPTool, where the Responses API connects to a remote MCP server and your code never connects to it. Only connect servers you trust: whatever a tool returns goes straight into the model's context.
9. See what happened with tracing
Tracing is on by default. Each run records the model calls, tool calls and handoffs, and you can look through them in the OpenAI Traces dashboard. That shows you why an agent picked a tool or where it got stuck.
To switch it off, set OPENAI_AGENTS_DISABLE_TRACING=1 in the environment, or call set_tracing_disabled(True) (from agents) at the start of your program. Tracing is unavailable for organizations that use OpenAI's APIs under a Zero Data Retention (ZDR) policy.
Common errors
- `ModuleNotFoundError: No module named 'agents'`: the virtual environment isn't active, or you installed into a different Python. Activate
.venvand runpip install openai-agentsagain. - `OpenAIError: Missing credentials. Please pass an `api_key`...`:
OPENAI_API_KEYisn't set in the terminal that runs the script. Set it again after opening a new terminal. - `MaxTurnsExceeded`: the agent kept calling tools without finishing. The default limit is 10 turns. Tighten the instructions or tool descriptions, or pass
max_turns=toRunner.runif the task really needs more steps. - `RuntimeError: AgentRunner.run_sync() cannot be called when an event loop is already running.`: you called
Runner.run_syncinside Jupyter or another async program. Useawait Runner.run(...)instead. - The MCP server times out on startup: install the package before connecting, as the SDK docs recommend, and check
node --version(the Filesystem server has declared Node 24 or newer since v2.0.0).
Where to go next
The SDK repo has runnable examples worth reading next: examples/basic/hello_world.py, examples/basic/tools.py and examples/agent_patterns/routing.py. Guardrails (checks that run on input or output), human approval and voice agents each have their own page in the docs. For a shorter overview of the SDK, how it differs from the regular OpenAI SDK and the JavaScript version, see the OpenAI Agents SDK listing. To give your agent more tools through MCP, the best MCP servers for Claude Code covers popular servers that work with any MCP client.
FAQ
Is the OpenAI Agents SDK free?
The SDK is open source under the MIT license and free to install. You pay for the model calls it makes as normal API usage.
Can I use it with models from other providers?
Yes. The models page in the docs covers non-OpenAI providers, including OpenAI-compatible endpoints and the LiteLLM and Any-LLM adapters (both marked beta). Hosted tools such as web search and file search are built-in tools for OpenAI's Responses models.
What is the difference between the Agents SDK and the OpenAI Python SDK?
The openai package sends single requests to the API. The Agents SDK is built on top of it and adds the agent loop: running tools, handoffs, sessions, guardrails and tracing. The listing has a short comparison.
Is there a JavaScript or TypeScript version?
Yes, @openai/agents on npm, with similar concepts. This tutorial covers the Python package only.
Sources
Commands and config above come from these official docs. Tools change often, so check them for the latest steps.
- OpenAI Agents SDK docs: Quickstart
- OpenAI Agents SDK docs: Running agents
- OpenAI Agents SDK docs: Tools
- OpenAI Agents SDK docs: Sessions
- OpenAI Agents SDK docs: MCP
- OpenAI Agents SDK docs: Streaming
- OpenAI Agents SDK docs: Tracing
- OpenAI Agents SDK docs: Models
- openai-agents on PyPI (version and Python requirement)
- openai/openai-agents-python on GitHub (examples)
PluginStack is independent and not affiliated with or endorsed by the companies mentioned. Spotted something out of date? Email pluginstackai@gmail.com.
Related guides
Tools in this guide
- Agent Frameworks · OpenAI
OpenAI Agents SDK
OpenAI's lightweight, open-source framework for building agents and multi-agent workflows, in Python (openai-agents) and JavaScript/TypeScript (@openai/agents). It covers agents with tools (functions, MCP servers, hosted tools), handoffs, guardrails, human-in-the-loop, sessions, tracing, sandbox agents and realtime/voice agents. Per the README it is provider-agnostic: it supports the OpenAI Responses and Chat Completions APIs and 100+ other LLMs. MIT licensed.
- MCP Servers · Model Context Protocol
Filesystem MCP Server
Reference MCP server that lets an AI read, write, edit, move and search files and directories, restricted to the folders you pass it. Version 2.0.1 (25 Oct 2026) includes security fixes for how it checks paths.
- MCP Dev Tools · Model Context Protocol
MCP Inspector
The official developer tool for testing MCP servers. It connects to a local or remote server and lets you list and call its tools, resources and prompts from a web UI, a scriptable CLI or a terminal UI. Version 2 needs Node 22.19 or newer.