Skip to main content

Overview

The OpenAI Agents SDK is OpenAI’s official framework for building autonomous agents. It provides async-first architecture, native tool support, and seamless Model Context Protocol (MCP) integration for external tool access.

When to Use OpenAI Agents SDK

  • OpenAI-First Projects: When using OpenAI models primarily
  • MCP Integration: Need to connect to external tools via Model Context Protocol
  • Async Applications: Building async-native agent systems
  • GitHub/Database Integration: Using MCP servers for GitHub, databases, etc.
  • Custom Tool Servers: Building agents that connect to custom MCP servers

Installation

pip install openai-agents
pip install openai
pip install python-dotenv

For MCP Support

# Install MCP dependencies
pip install openai-agents[mcp]

# Or install specific MCP servers via npm
npx -y @modelcontextprotocol/server-github
npx -y @modelcontextprotocol/server-sqlite

Core Concepts

Agent

The Agent class defines an autonomous agent with instructions, tools, and optional model configuration.
from agents import Agent, function_tool

@function_tool
def send_email(to: str, subject: str, body: str):
    """Send an email to a recipient."""
    print(f"Sending email to {to}")
    return {"status": "success"}

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant",
    tools=[send_email],
)

Runner

The Runner class executes agent interactions asynchronously:
import asyncio
from agents import Runner

async def main():
    result = await Runner.run(
        starting_agent=agent,
        input="Send an email to [email protected]"
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

Model Providers

Customize which models the agent uses:
from agents import Agent

# Uses OpenAI by default
agent = Agent(
    name="Assistant",
    instructions="You are helpful",
)
Source: starter_ai_agents/openai_agents_sdk/main.py:32-46

Tools

Define tools using the @function_tool decorator:
from agents import function_tool

@function_tool
def calculate_sum(a: int, b: int) -> int:
    """Add two numbers together.
    
    Args:
        a: First number
        b: Second number
    
    Returns:
        Sum of a and b
    """
    return a + b

agent = Agent(
    name="Calculator",
    tools=[calculate_sum],
    instructions="Help users with math"
)

MCP Integration

Model Context Protocol enables agents to use external tools and services:
import asyncio
import os
from agents import Agent, Runner, set_tracing_disabled
from agents.mcp import MCPServer, MCPServerStdio
from openai import AsyncOpenAI
from dotenv import load_dotenv

load_dotenv()
set_tracing_disabled(disabled=True)

async def main():
    # Connect to GitHub MCP server
    async with MCPServerStdio(
        cache_tools_list=True,
        params={
            "command": "npx",
            "args": [
                "-y",
                "@modelcontextprotocol/server-github"
            ],
            "env": {
                "GITHUB_PERSONAL_ACCESS_TOKEN": os.environ["GITHUB_TOKEN"],
            }
        },
    ) as server:
        # Create agent with MCP server access
        agent = Agent(
            name="GitHub Assistant",
            instructions="Analyze GitHub repositories",
            mcp_servers=[server],
        )
        
        result = await Runner.run(
            starting_agent=agent,
            input="List recent issues in owner/repo"
        )
        print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())
Based on: mcp_ai_agents/mcp_starter/main.py:19-88

Common Patterns

Pattern 1: Basic Async Agent

Standard pattern for simple tool-using agents:
from __future__ import annotations
import asyncio
import os
from agents import (
    Agent,
    Runner,
    function_tool,
    set_tracing_disabled,
)
from dotenv import load_dotenv

load_dotenv()
set_tracing_disabled(disabled=True)

@function_tool
def greet_user(name: str) -> str:
    """Greet a user by name."""
    return f"Hello, {name}!"

async def main():
    agent = Agent(
        name="Greeter",
        instructions="Greet users warmly",
        tools=[greet_user]
    )
    
    result = await Runner.run(
        agent,
        "Greet Alice"
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

Pattern 2: Custom Model Provider

Use alternative model providers (Nebius, Anthropic, etc.):
1

Create Custom Provider Class

from agents import ModelProvider, Model, OpenAIChatCompletionsModel
from openai import AsyncOpenAI

class CustomModelProvider(ModelProvider):
    def __init__(self, base_url: str, api_key: str):
        self.client = AsyncOpenAI(
            base_url=base_url,
            api_key=api_key
        )
    
    def get_model(self, model_name: str | None) -> Model:
        return OpenAIChatCompletionsModel(
            model=model_name,
            openai_client=self.client
        )
2

Initialize Provider

provider = CustomModelProvider(
    base_url="https://api.tokenfactory.nebius.com/v1",
    api_key=os.getenv("NEBIUS_API_KEY")
)
3

Use with RunConfig

from agents import RunConfig

result = await Runner.run(
    agent,
    "Your query",
    run_config=RunConfig(model_provider=provider)
)

Pattern 3: MCP Server Integration

Connect to external tools via MCP:
async with MCPServerStdio(
    params={
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-github"],
        "env": {
            "GITHUB_PERSONAL_ACCESS_TOKEN": os.environ["GITHUB_TOKEN"],
        }
    },
) as github_server:
    agent = Agent(
        name="GitHub Assistant",
        mcp_servers=[github_server],
        instructions="Analyze GitHub data"
    )
    
    result = await Runner.run(
        agent,
        "Find recent issues in owner/repo"
    )

Pattern 4: Multiple Queries

Run multiple agent queries with proper parameter handling:
async def run_query(agent: Agent, message: str):
    print("\n" + "-" * 40)
    print(f"Query: {message}")
    result = await Runner.run(starting_agent=agent, input=message)
    print(f"Result: {result.final_output}")

async def main():
    async with MCPServerStdio(...) as server:
        agent = Agent(
            name="GitHub Assistant",
            mcp_servers=[server],
            instructions="""
            When using numeric parameters like per_page,
            do not include quotes as they should be numbers.
            """
        )
        
        # Run multiple queries
        queries = [
            "List the most recent issue",
            "Show the latest commit",
            "Get repository stats",
        ]
        
        for query in queries:
            await run_query(agent, query)

if __name__ == "__main__":
    asyncio.run(main())
Based on: mcp_ai_agents/mcp_starter/main.py:44-69

Real Examples from Repository

Email Assistant

Haiku-speaking agent that sends emails via Resend API

GitHub MCP Agent

Analyze repositories using GitHub MCP server

Database MCP Agent

Query databases via SQLite MCP server

More MCP Examples

Explore more MCP integration examples

Configuration

Environment Variables

# .env file
OPENAI_API_KEY=your_openai_api_key

# Or for alternative providers
NEBIUS_API_KEY=your_nebius_api_key

# For MCP servers
GITHUB_PERSONAL_ACCESS_TOKEN=your_github_token

Agent Parameters

name
string
required
Name of the agent
instructions
string
required
System instructions for the agent
tools
list[Tool]
default:"[]"
List of function tools available to the agent
mcp_servers
list[MCPServer]
default:"[]"
MCP servers for external tool access
model
string
Model name to use (defaults to provider’s default)

RunConfig Parameters

model_provider
ModelProvider
Custom model provider for alternative LLM services
max_turns
int
Maximum conversation turns

Best Practices

The SDK is async-first. Use asyncio.run() for main execution:
async def main():
    result = await Runner.run(agent, "query")

if __name__ == "__main__":
    asyncio.run(main())
from agents import set_tracing_disabled

set_tracing_disabled(disabled=True)
Reduces noise in production logs.
The agent reads docstrings to understand tool usage:
@function_tool
def search(query: str, max_results: int = 10) -> list:
    """
    Search for information.
    
    Args:
        query: Search query string
        max_results: Maximum number of results (default 10)
    
    Returns:
        List of search results
    """
    ...
Use async context managers for MCP servers:
async with MCPServerStdio(...) as server:
    # Server is connected
    agent = Agent(mcp_servers=[server])
    result = await Runner.run(agent, "query")
# Server automatically disconnected
Include clear type hints in tool parameters:
@function_tool
def process_data(
    data: dict,  # Clear type
    count: int,  # Not string!
    enabled: bool
) -> str:
    ...
Note: Numeric parameters should be typed as int or float, not strings.

Troubleshooting

If you see “RuntimeError: asyncio.run() cannot be called from a running event loop”:
# Don't do this in Jupyter/async context
asyncio.run(main())

# Do this instead
await main()
Ensure npm packages are accessible:
# Test MCP server directly
npx -y @modelcontextprotocol/server-github

# If fails, install globally
npm install -g @modelcontextprotocol/server-github
  • Verify tool docstring is clear and descriptive
  • Check function signature has proper type hints
  • Ensure instructions mention when to use the tool
  • Add examples in the instructions
Verify OpenAI-compatible endpoint:
# Test client directly
client = AsyncOpenAI(base_url=..., api_key=...)
response = await client.chat.completions.create(
    model="model-name",
    messages=[{"role": "user", "content": "test"}]
)
print(response)

Next Steps

Explore MCP Servers

Browse MCP examples in mcp_ai_agents/ for GitHub, database, and custom server integration

Build Custom MCP Server

Create your own MCP server to expose custom tools and APIs to agents

Multi-Agent Systems

Combine multiple agents with different specializations

Add Custom Providers

Integrate with Anthropic, Nebius, or other OpenAI-compatible services

Build docs developers (and LLMs) love