OpenAI Agents SDK Integration#
Govern an OpenAI Agents SDK agent with FirstOps — across the model, your function tools, and MCP servers.
What FirstOps governs#
| Surface | How it's wired | What you get |
|---|---|---|
| LLM calls | point the Agents SDK's default OpenAI client at the FirstOps sidecar | prompt/response inspection, PII detection, audit |
| Function tools | a FirstOps tool guardrail on each @function_tool | block a tool call + audit |
| MCP servers | point the MCP server at the FirstOps proxy | server-side policy + credential brokering (the agent never holds the upstream token) |
A note specific to OpenAI Agents: tool guardrails are block-only — they can deny a tool call but cannot rewrite its arguments. If you need to scrub arguments (not just block), wrap the function with the base-API @firstops.tool decorator. And guardrails attach per function tool, not on the Agent.
Setup#
1. Create an agent principal#
In the FirstOps dashboard, create an agent — you'll get an agent ID and a DPoP private-key PEM.
2. Install#
pip install "firstops[openai]"
3. Initialize FirstOps and route the LLM#
import firstops
from agents import set_default_openai_client, set_tracing_disabled
from openai import AsyncOpenAI
fo = firstops.init(
agent_id="<agent-uuid>",
private_key_pem=open("agent-key.pem").read(),
)
# The Agents SDK's model calls now flow through the FirstOps sidecar.
set_default_openai_client(AsyncOpenAI(
base_url=firstops.llm_base_url("openai"),
api_key="sk-...", # passes through to OpenAI; FirstOps never stores it
))
set_tracing_disabled(True) # so traces don't bypass governance
4. Govern function tools#
Attach the FirstOps guardrail to each @function_tool:
from agents import Agent, Runner, function_tool
from firstops.integrations.openai_agents import firstops_tool_input_guardrail
guard = firstops_tool_input_guardrail(fo)
@function_tool(tool_input_guardrails=[guard])
def send_email(to: str, body: str) -> str:
...
agent = Agent(name="assistant", tools=[send_email])
result = await Runner.run(agent, "email alice@example.com the Q3 summary")
5. (Optional) Add MCP servers through the FirstOps proxy#
from agents.mcp import MCPServerStreamableHttp
async with MCPServerStreamableHttp(
name="notion",
params={"url": firstops.mcp_url("<notion-connection-id>")},
) as notion:
agent = Agent(name="assistant", mcp_servers=[notion], tools=[send_email])
result = await Runner.run(agent, "Fetch the customer records from Notion.")
Full example — LLM + tool guardrail + MCP#
import asyncio
import firstops
from agents import (
Agent, Runner, function_tool, set_default_openai_client, set_tracing_disabled,
)
from agents.mcp import MCPServerStreamableHttp
from firstops.integrations.openai_agents import firstops_tool_input_guardrail
from openai import AsyncOpenAI
async def main():
fo = firstops.init(
agent_id="<agent-uuid>",
private_key_pem=open("agent-key.pem").read(),
)
try:
set_default_openai_client(AsyncOpenAI(
base_url=firstops.llm_base_url("openai"), api_key="sk-...",
))
set_tracing_disabled(True)
guard = firstops_tool_input_guardrail(fo)
@function_tool(tool_input_guardrails=[guard])
def write_to_file(filename: str, content: str) -> str:
"""Write text content to a local file."""
with open(filename, "w") as f:
f.write(content)
return f"wrote {len(content)} bytes to {filename}"
async with MCPServerStreamableHttp(
name="notion", params={"url": firstops.mcp_url("<notion-connection-id>")},
) as notion:
agent = Agent(name="assistant", mcp_servers=[notion], tools=[write_to_file])
result = await Runner.run(
agent,
"Fetch the customer records from Notion and write them to customers.txt.",
)
print(result.final_output)
finally:
firstops.shutdown()
asyncio.run(main())
Multi-agent workflows#
For multi-agent handoffs, give each agent its own FirstOps principal by running it in its own process with its own init() and port:
# Researcher (process 1)
fo = firstops.init(agent_id="<research-uuid>", private_key_pem=research_key, port=9322)
# Writer (process 2)
fo = firstops.init(agent_id="<writer-uuid>", private_key_pem=writer_key, port=9323)
Each agent gets independent identity, permissions, and an independent audit trail.