Agent API
Function Calling
Built-in server tools + your own client function tools on the Agent API, with both handoff continuation styles.
The Agent API runs two kinds of tools:
- Built-in server tools — the platform’s health-data tools. They run server-side; you never execute them. Their trace is reported, not delegated.
- Client function tools — tools you declare on the request. When the model wants one, the response hands off with a
function_calloutput item; you execute it and continue the run.
Built-in server tools
Section titled “Built-in server tools”The agent always has its platform toolset over the Subject’s data (same catalog as the Answers API):
query_health_data— search and aggregate the Subject’s recordslist_family_members— resolve care-circle members the Subject is allowed to querysearch_medical_evidence— search literature, guidelines/consensus, and registered trials (feedscitations)read_source— read one search result by ref; arbitrary URL fetching is not supported
The /v1 agent’s domain tools are read-only. It also runs internal planning and analysis tools. Every server-tool run lands in the response object’s top-level tool_steps extension — never as an output item, which official SDKs would mis-parse — and in streaming as the response.mirobody_tool_call side-channel event.
Declaring client tools
Section titled “Declaring client tools”{ "model": "mirobody-flash", "input": "Check my recent glucose and book a follow-up if it is trending up.", "user": "alice", "tools": [ { "type": "function", "name": "book_appointment", "description": "Book a clinic appointment for the end user.", "parameters": { "type": "object", "properties": { "date": { "type": "string", "description": "ISO date" } }, "required": ["date"] } } ]}Rules (violations are explicit 400s, never silently dropped):
| Rule | Detail |
|---|---|
| Type | "type": "function" (client handoff) and "type": "mcp" (server-side remote tools) are supported. Both the flat Responses form and the completions-nested {"type":"function","function":{...}} form are accepted. {"type": "mcp"} attaches your remote MCP servers (server-side execution, no handoff) — see MCP servers. |
| Count | Max 64 tools per request. |
| Names | Must match [a-zA-Z0-9_-]{1,64}; must be unique; must not shadow a built-in tool name (query_health_data, read_file, task, …). |
parameters | A JSON Schema object (defaults to {"type":"object","properties":{}}). |
tool_choice | "auto" (default), "none" (disables all tools for the turn, built-ins included — see backbone mode), or a named client tool. Anything else → 400 unsupported_parameter. |
The handoff
Section titled “The handoff”When the model calls your tool, the response completes with a function_call output item (status: "completed" — the response is done; the conversation is waiting on you):
{ "id": "resp_abc...", "object": "response", "status": "completed", "output": [ { "type": "function_call", "id": "fc_0", "call_id": "call_9f2...", "status": "completed", "name": "book_appointment", "arguments": "{\"date\": \"2026-07-14\"}" } ], "output_text": "", ...}In streaming, the same handoff arrives as a response.output_item.added → response.function_call_arguments.delta / .done → response.output_item.done event group (Streaming).
Execute the tool, then continue the run in either of two ways:
Path 1 — stateful resume (previous_response_id)
Section titled “Path 1 — stateful resume (previous_response_id)”Send only function_call_output items, referencing the handoff response. The server resumes the paused agent thread — no history resend:
curl https://api.mirobody.ai/v1/responses \ -H "Authorization: Bearer $MIROBODY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "mirobody-flash", "previous_response_id": "resp_abc...", "input": [ { "type": "function_call_output", "call_id": "call_9f2...", "output": "Booked: Mon 2026-07-14 09:30, Dr. Chen" } ], "user": "alice" }'Requirements: outputs must cover exactly the pending call_ids (parallel calls → one function_call_output each); you may not mix message items into a resume; a handoff can be resumed once (a duplicate resume fails loudly). The handoff response must have been stored (store=true, the default).
Path 2 — stateless full replay
Section titled “Path 2 — stateless full replay”What openai-agents does by default: resend the entire item transcript in input — including the function_call / function_call_output pairs — with no previous_response_id:
{ "model": "mirobody-flash", "input": [ { "role": "user", "content": "Check my recent glucose and book a follow-up if it is trending up." }, { "type": "function_call", "call_id": "call_9f2...", "name": "book_appointment", "arguments": "{\"date\": \"2026-07-14\"}" }, { "type": "function_call_output", "call_id": "call_9f2...", "output": "Booked: Mon 2026-07-14 09:30, Dr. Chen" } ], "tools": [ ... ], "user": "alice"}The pairs are reconstructed as conversation history and the run continues as a fresh turn. Works with store=false end to end.
End-to-end with openai-agents
Section titled “End-to-end with openai-agents”The SDK handles the whole loop — declaration, handoff, execution, replay:
from agents import Agent, ModelSettings, Runner, function_tool, set_default_openai_client, set_tracing_disabledfrom openai import AsyncOpenAI
set_default_openai_client(AsyncOpenAI( base_url="https://api.mirobody.ai/v1", api_key="mb_live_..."))set_tracing_disabled(True)
@function_tooldef book_appointment(date: str) -> str: """Book a clinic appointment for the end user (ISO date).""" return f"Booked: {date} 09:30, Dr. Chen"
agent = Agent( name="Health assistant", model="mirobody-flash", instructions="Check real health data before acting.", tools=[book_appointment], model_settings=ModelSettings(extra_body={"user": "alice"}), # tenant isolation — REQUIRED)result = Runner.run_sync(agent, "Check my recent glucose and book a follow-up if it's trending up.")print(result.final_output)The model reads the Subject’s real glucose data with built-in server tools, then hands off to your book_appointment — the SDK executes it locally and replays the transcript automatically.
Continuation errors
Section titled “Continuation errors”HTTP 400 message | Cause |
|---|---|
previous response has no pending function calls | Resuming a response that wasn’t a handoff (or was already resumed). |
cannot mix message items with function_call_output when resuming via previous_response_id | A resume must contain only tool outputs. |
unknown call_id(s): [...] / missing function_call_output for call_id(s): [...] | Outputs must match the pending calls exactly. |
previous response has pending function call(s) — provide function_call_output items for: ... | Continuing a handoff conversation without supplying the outputs. |
function_call_output without matching function_call items — replay the full transcript, or resume via previous_response_id | A stateless replay must include the function_call items too. |
See also
Section titled “See also”- Agent API (Responses) — the endpoint these tools run on.
- MCP Servers — attaching a remote MCP server instead of a local tool.
- Structured Output — constraining the final answer to a schema.