Agent frameworks / OpenAI Agents SDK

OpenAI Agents SDK

Connect an agent built on the OpenAI Agents SDK, in Python or TypeScript, to AIX through the MCP server, the hosted MCP tool, or two function tools defined in code.

The OpenAI Agents SDK runs agents with tools, handoffs and guardrails, in Python and in TypeScript. An AIX integration gives the agent two operations: submit_intent after any turn that states or changes what the shopper wants to buy, and report_handoff before the shopper is sent to a store. Both run on your server with your key.

Through the MCP server (Python)

MCPServerStreamableHttp connects to a remote server and passes the key as a header. Cache the tool list so each run does not re-fetch it. The MCP submit_intent tool takes one argument, intent, with the same body as POST /intents (its turn input is reserved for Brambles-managed publishers; the model must send exactly one of the two).

python
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def run(user_message: str, session_id: str) -> str:
async with MCPServerStreamableHttp(
name="aix",
params={
"url": "https://aix.brambles.ai/api/aix/mcp",
"headers": {"Authorization": f"Bearer {os.environ['AIX_PUBLISHER_KEY']}"},
"timeout": 10,
},
cache_tools_list=True,
) as server:
agent = Agent(
name="Shopping assistant",
instructions=AIX_RULES, # see below
mcp_servers=[server],
)
result = await Runner.run(agent, user_message)
return result.final_output

Through the MCP server (TypeScript)

ts
import { Agent, MCPServerStreamableHttp, run } from "@openai/agents";
const aix = new MCPServerStreamableHttp({
name: "aix",
url: "https://aix.brambles.ai/api/aix/mcp",
requestInit: { headers: { Authorization: `Bearer ${process.env.AIX_PUBLISHER_KEY}` } },
});
await aix.connect();
try {
const agent = new Agent({ name: "Shopping assistant", instructions: AIX_RULES, mcpServers: [aix] });
const result = await run(agent, userMessage);
console.log(result.finalOutput);
} finally {
await aix.close();
}

Hosted MCP tool and the Responses API

If you would rather let OpenAI's backend call AIX, pass the server to the hosted MCP tool with the key as the authorization. The same shape works on the Responses API without the Agents SDK.

ts
import { Agent, hostedMcpTool } from "@openai/agents";
const agent = new Agent({
name: "Shopping assistant",
instructions: AIX_RULES,
tools: [
hostedMcpTool({
serverLabel: "aix",
serverUrl: "https://aix.brambles.ai/api/aix/mcp",
authorization: process.env.AIX_PUBLISHER_KEY,
requireApproval: "never",
}),
],
});
json
{
"model": "gpt-5.5",
"tools": [
{
"type": "mcp",
"server_label": "aix",
"server_url": "https://aix.brambles.ai/api/aix/mcp",
"headers": { "Authorization": "Bearer <AIX_PUBLISHER_KEY>" },
"require_approval": "never"
}
],
"input": "..."
}

Defined in code (Python)

Two function tools and one request helper with a three-second timeout. Every failure returns a skipped result so AIX can never slow down or break the shopper's experience. submit_intent takes the same intent argument as the MCP tool, so one prompt works whichever way the tools arrive.

python
import os
from typing import Literal, NotRequired, TypedDict
import httpx
from agents import Agent, function_tool
AIX = "https://aix.brambles.ai/api/aix"
class Budget(TypedDict):
currency: str
max_cents: int
class Fact(TypedDict):
attribute: Literal["brand", "width", "size", "department", "use_case", "feature", "merchant", "deal"]
value: str
strength: Literal["required", "preferred", "excluded"]
class Task(TypedDict):
category: str
summary: str
budget: NotRequired[Budget]
facts: list[Fact]
class Intent(TypedDict):
session_id: str
country: str
task: Task
async def _aix(path: str, body: dict) -> dict:
key = os.environ.get("AIX_PUBLISHER_KEY")
if not key:
return {"status": "skipped", "reason": "no_key"}
try:
async with httpx.AsyncClient(timeout=3) as client:
r = await client.post(f"{AIX}/{path}", json=body, headers={"Authorization": f"Bearer {key}"})
return r.json() if r.is_success else {"status": "skipped", "reason": f"http_{r.status_code}"}
except httpx.HTTPError:
return {"status": "skipped", "reason": "timeout"} # never block the shopper
@function_tool
async def submit_intent(intent: Intent) -> dict:
"""Report the shopper's current shopping task to AIX. Call after any turn that states or changes
what they want to buy, with intent: session_id, country and the task. Shopping requirements only,
never personal data."""
return await _aix("intents", dict(intent))
@function_tool
async def report_handoff(session_id: str, destination_url: str, consent: str | None = None) -> dict:
"""Report that the shopper is being sent to a store or product page (the store's own URL).
Use the returned landing_url as that page's URL, inside your affiliate link if you use one."""
body: dict = {"session_id": session_id, "destination_url": destination_url}
if consent:
body["consent"] = consent
return await _aix("handoffs", body)
agent = Agent(name="Shopping assistant", instructions=AIX_RULES, tools=[submit_intent, report_handoff])

The rules the model needs

Put this in instructions:

text
You can send shopping intent to the Agentic Intent Exchange (AIX).
- After any user turn that states or changes what they want to buy, call submit_intent with one
argument, intent: an object holding the conversation's session_id, the shopper's two-letter
country, and the task: a category, a one-line summary, the budget when they gave one, and facts
with attribute, value and strength (required, preferred or excluded). Send only shopping
requirements: never names, emails, phone numbers, addresses, account or order numbers, or the
conversation itself.
- Before you give the user a link to a store or product page, call report_handoff with the same
session_id and the store's page URL, then use the returned landing_url as that page's URL (inside
your own affiliate or ad link, if you use one).
- If either tool fails or is slow, carry on without it. Never ask the user for a key.

Set country on the server from your host's geolocation header rather than asking the shopper. Shoppers in the EEA, the UK or Switzerland are shared only with consent: show one line under your assistant, "Clicking a store link shares your shopping request with that store", and pass consent: "granted" on handoffs from those clicks. Sensitive topics are suppressed by AIX on arrival; never reword a task to get it through.

Receiving side

A store's agent uses the same connection with AIX_RECEIVER_KEY; the tools then list deliveries (subscribe_intents), read one receipt (get_intent_context), acknowledge it (acknowledge_delivery) and redeem a handoff id from a landing or a checkout (redeem_handoff; with a sandbox key it returns a free test delivery for any well-formed id). Verification and acknowledgment belong on your server; see Receiving agents.

Test it

  1. Create a free sending agent in the AIX console and store the sandbox key as AIX_PUBLISHER_KEY.
  2. Run one conversation that names a product and ends with a store link. The intent answers accepted; the handoff answers test_delivered with a receiver_preview.
  3. GET /activity with the same key, or the AIX console, shows the test. Create the production key there and set it in production.
Was this helpful?