MailFlatDocs
Documentation/AI agents & assistants/OpenAI Agents SDK

Using MailFlat with the OpenAI Agents SDK

There is no MailFlat package for the OpenAI Agents SDK and none is needed: attach the MCP server, or wrap the Python client in two function tools.

How it fits together

We ship official integrations for LangChain and the Vercel AI SDK, and nothing specific to the OpenAI Agents SDK. That is not a gap you have to work around: the SDK already speaks MCP, which is the whole MailFlat tool surface, and its function-tool decorator turns any Python function into a tool in one line. Pick by how much control you want over what the agent can do.

Route one: attach the MCP server

All seven tools at once, no wrapper code. The server is a subprocess the SDK starts and stops around the run.
Shell
pip install openai-agents mailflat-mcp

The agent

Everything inside the context manager can call MailFlat tools; when the block exits the server shuts down with it.
Python
import asyncio, os
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
 
async def main() -> None:
async with MCPServerStdio(
params={
"command": "uvx",
"args": ["mailflat-mcp"],
"env": {"MAILFLAT_API_KEY": os.environ["MAILFLAT_API_KEY"]},
},
) as mailflat:
agent = Agent(
name="Signup bot",
instructions="Use MailFlat inboxes for any address you need.",
mcp_servers=[mailflat],
)
result = await Runner.run(
agent,
"Open an inbox labelled agents-demo and tell me the address.",
)
print(result.final_output)
 
asyncio.run(main())

Route two: two function tools

Narrower on purpose. The agent gets exactly what the task needs, the docstrings become the tool descriptions the model reads, and nothing can delete an inbox.
Python
from agents import Agent, Runner, function_tool
from mailflat import MailFlat, OTPTimeoutError
 
mf = MailFlat() # reads MAILFLAT_API_KEY
 
@function_tool
def open_inbox(label: str) -> str:
"""Open a disposable inbox and return its email address."""
return mf.create(label=label, retention_hours=2).address
 
@function_tool
def read_code(address: str, timeout: int = 60) -> str:
"""Wait for a one-time code sent to this address and return it."""
try:
return mf.inbox(address).wait_for_otp(timeout=timeout)
except OTPTimeoutError:
return "no code arrived before the timeout"
 
agent = Agent(
name="Signup bot",
instructions="Use open_inbox for any address you need, then read_code.",
tools=[open_inbox, read_code],
)
retention_hours=2 is the cleanup: the inbox empties itself, so the agent never needs a delete tool at all.

Run it

Runner.run drives the loop — tool call, result, next step — until the agent has an answer.
Python
import asyncio
 
result = asyncio.run(Runner.run(
agent,
"Open an inbox for the signup test, then report the first code it receives.",
))
print(result.final_output)

Worth knowing

Route one hands the agent every tool including delete_inbox; route two hands it only what you wrapped. Choose route two when the same key can reach inboxes you care about.
wait_for_otp blocks the run while it polls, so keep the timeout near what the mail really takes. An encrypted inbox raises EncryptedInboxError instead of returning a code — the server cannot read it.
Both routes read MAILFLAT_API_KEY from the environment. Give the agent its own key rather than the one your dashboard session uses, so revoking it costs you nothing.