Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.truefoundry.com/llms.txt

Use this file to discover all available pages before exploring further.

Agent Platform is currently in beta. The API may introduce breaking changes.

Overview

The Agent Harness ships with a Python and TypeScript SDK for interacting with agents and consuming the event stream. We also provide a React component for building a chat interface for an agent.

Install and initialize

pip install -U truefoundry-gateway-sdk
Initialize the client with your gateway URL and API key.
import os
from truefoundry_gateway_sdk import TrueFoundryGateway

client = TrueFoundryGateway(
    # For TrueFoundry SaaS: "https://gateway.truefoundry.ai/<tenant_name>"
    # For self-hosted: the URL of your gateway installation followed by your tenant name
    base_url=os.environ["GATEWAY_BASE_URL"],  # e.g. "https://gateway.truefoundry.ai/my-tenant"
    api_key=os.environ["TFY_API_KEY"],
)

How interaction works

Interaction with the agent is turn-based. Each turn, you send input and receive a stream of Server-Sent Events (SSE). The stream closes when the agent emits a terminal event. Every stream opens with a response.created event that carries a response_id. Pass this as previous_response_id on the next turn to continue the conversation. Some terminal events require you to respond before the agent can continue. tool.approval_required pauses the agent for a human approval decision; tool.response_required waits for a client-side tool result. Collect those responses and send them as input on the next turn.

Calling the agent

Use client.agents.responses.create to start or continue an agent run. There are two modes:
ModeWhen to use
Saved agentProduction. Agent is saved in the Agent Registry with versioning, RBAC, and audit.
Inline agentPrototyping, scripts, ad-hoc workflows. Define configuration in code; nothing is persisted.
Reference a saved agent by name. The agent configuration (model, instructions, tools) is loaded from the registry.
The YAML below shows a complete saved agent spec. See AgentManifest for a description of every field.
name: support-bot
description: A helpful support assistant
model:
  name: anthropic/claude-sonnet-4-6
  params:
    max_tokens: 4096
    temperature: 1.0
    reasoning_effort: low
# Instructions are the system prompt for the agent.
instructions: |
  You are a helpful support assistant that helps customers file issues.
  Current customer is {{customer_name}}. Their support tier is {{support_tier}}.
# Seed messages sent after the system prompt but before `input`.
messages:
  - role: user
    content: Hello. What can you help me with?
# Default values for variables used in `messages` and `instructions`.
variables:
  customer_name:
    default_value: ""
    description: "The name of the customer"
  support_tier:
    default_value: ""
    description: "The support tier of the customer"
# Skills are mounted from the Skills Registry when the agent runs.
skills:
  - fqn: truefoundry/skills/web-search:1
    preload: true
  - fqn: truefoundry/skills/code-interpreter:2
    preload: false
mcp_servers:
  - name: zendesk
    enable_tools: ["@read-only", "delete_ticket"]
    disable_tools: ["get_users", "list_users"]
    preload_tools: ["list_tickets"]
    require_approval_for_tools: ["@write", "@destructive", "delete_ticket"]
response_format:
  type: text
config:
  server_timeout_seconds: 900
  iteration_limit: 25
  sandbox:
    enabled: true
    file_downloads: true
  dynamic_sub_agents:
    enabled: true
  context_management:
    compaction:
      enabled: true
      compaction_threshold_tokens: 60000
    large_tool_response:
      enabled: true
      individual_tool_response_token_threshold: 4000
      total_tool_response_token_threshold: 8000
      preview_number_of_characters: 500
  generative_ui:
    enabled: false
  ask_user_questions:
    enabled: true
import os
from truefoundry_gateway_sdk import (
    AgentResponsesSavedAgent,
    AgentInputUserMessage,
    TrueFoundryGateway,
)

client = TrueFoundryGateway(
    base_url=os.environ["GATEWAY_BASE_URL"],
    api_key=os.environ["TFY_API_KEY"],
)

stream = client.agents.responses.create(
    request=AgentResponsesSavedAgent(
        agent_name="support-bot",
        input=[AgentInputUserMessage(role="user", content="I would like to file a support ticket.")],
        # previous_response_id="..."  # pass on subsequent turns
        # variables={"customer_name": "Alice", "support_tier": "pro"}  # override saved defaults
    )
)

for event in stream:
    print(event)

Input items

Each turn’s input is a list of one of these types.
User messages (AgentInputUserMessage) cannot be mixed with tool approval decisions or client-side tool responses in the same input list.

AgentInputUserMessage

Start a new conversation or send the next user message.
{ "role": "user", "content": "I would like to file a support ticket." }
FieldTypeRequiredDescription
role"user"Yes
contentstringYesThe message text.

AgentApprovalDecisionMessage

Sent to resume a stream paused by tool.approval_required. One item per pending tool call.
{
    "type": "tool.approval",
    "execution_id": "root",
    "tool_call_id": "call_restart_billing",
    "approval": { "status": "allow" }
}
FieldTypeRequiredDescription
type"tool.approval"Yes
execution_idstringYesexecution_id from the tool.approval_required event.
tool_call_idstringYesID of the tool call being approved or denied.
approvalAgentApprovalDecisionAllow | AgentApprovalDecisionDenyYesUse {"status": "allow"} to permit the call, or {"status": "deny", "reason": "..."} to block it. reason is optional.

AgentClientSideToolResponseMessage

Sent to resume a stream paused by tool.response_required. One item per pending tool call.
{
    "type": "tool.client_side_response",
    "execution_id": "exec-1",
    "tool_call_id": "call_a1b2",
    "content": "tfy-prod-us (production)"
}
FieldTypeRequiredDescription
type"tool.client_side_response"Yes
execution_idstringYesexecution_id from the tool.response_required event.
tool_call_idstringYesID of the tool call whose result is being supplied.
contentstringYesThe result to return to the agent.

Output events

The agent streams Server-Sent Events (SSE). Each event is a JSON object with a type field.
  • The stream always opens with response.created and closes after a terminal event.
  • Events tied to an execution carry an execution_id. The root agent always has execution_id: "root". Sub-agents have unique IDs.
  • agent.created and agent.done are only emitted for dynamically spawned sub-agents - the root agent’s lifecycle is represented by response.created and response.done.
  • All events carry a sequence_id for ordering.
typeTied to executionTerminalDescription
response.createdNoNoFirst event. Carries response_id for multi-turn continuations.
agent.createdYesNoA sub-agent execution started. Not emitted for the root agent.
agent.messageYesNoStreaming assistant text delta, tool call, or tool result. Most frequent event.
agent.doneYesNoA sub-agent execution completed. Not emitted for the root agent.
agent.errorYesNoAn execution failed. Does not close the stream.
mcp.initializeYesNoOne or more MCP server sessions were initialized.
sandbox.createdNoNoA sandbox was provisioned for the run.
mcp.auth_requiredNoYesMCP server(s) need OAuth before proceeding. Resume after the user authorizes.
tool.approval_requiredYesYesA tool call is awaiting human approval. Resume with an approval decision.
tool.response_requiredYesYesA client-side tool was called and needs a result. Resume with the tool response.
response.doneNoYesLast event on every stream. Carries the final status.

ResponseCreated

Tied to execution: No | Terminal: No Emitted once at the start of every stream. Carries the response_id needed to continue this conversation in a future turn via previous_response_id.
FieldTypeRequiredDescription
type"response.created"Yes
response_idstringYesUnique ID for this response. Pass as previous_response_id to continue the conversation.
created_atstringYesISO-8601 timestamp when the response was created.

AgentExecutionCreated

Tied to execution: Yes | Terminal: No Emitted when a dynamically spawned sub-agent execution starts. Not emitted for the root agent - use response.created to detect the start of the root execution.
FieldTypeRequiredDescription
type"agent.created"Yes
execution_idstringYesUnique ID for this sub-agent execution. Never "root".
parentAgentParentYesParent execution and tool call that spawned this sub-agent.
agent_infoAgentInfoYesName and input of the sub-agent as defined by the parent.
created_atstringNo

AgentMessage

Tied to execution: Yes | Terminal: No The most frequent event type. Carries incremental assistant text, tool calls, or tool results. Assistant deltas must be accumulated client-side; a delta with a non-null finish_reason signals the message is complete. Use is_assistant_delta(event) and merge_assistant_message() from truefoundry_gateway_sdk.helpers to handle delta accumulation.
FieldTypeRequiredDescription
type"agent.message"Yes
execution_idstringYesExecution this message belongs to. "root" for the root agent; a unique ID for sub-agents.
role"assistant" | "tool"Yes
contentstringNoIncremental text content for assistant deltas; full content for tool messages.
finish_reasonstringNoSet on the final assistant delta. Signals the accumulated message is complete.
tool_callsAgentExtendedDeltaToolCall[]NoTool call chunks to accumulate on assistant deltas; fully formed on the final delta.
tool_call_idstringNoPresent on tool messages. Links the result to its tool call.

AgentExecutionDone

Tied to execution: Yes | Terminal: No Emitted when a dynamically spawned sub-agent execution completes successfully. Not emitted for the root agent - use response.done to detect root completion.
FieldTypeRequiredDescription
type"agent.done"Yes
execution_idstringYes
outputAgentAssistantMessageYesFinal accumulated assistant message for this execution.
parentAgentParentNoPresent on sub-agent completions.

AgentExecutionError

Tied to execution: Yes | Terminal: No Emitted when an agent execution fails. Does not terminate the overall response stream.
FieldTypeRequiredDescription
type"agent.error"Yes
execution_idstringYes
messagestringYesHuman-readable error description.
parentAgentParentNo
outputAgentAssistantMessageNoPartial output accumulated before the error, if any.

AgentMCPInitialize

Tied to execution: Yes | Terminal: No Emitted when one or more MCP server sessions are initialized for an execution.
FieldTypeRequiredDescription
type"mcp.initialize"Yes
execution_idstringYes
contentAgentMcpInitializationInfo[]YesList of initialized MCP servers with their session IDs.

AgentSandboxCreated

Tied to execution: No | Terminal: No Emitted once when a sandbox is provisioned for the run. The sandbox is reused across turns via previous_response_id.
FieldTypeRequiredDescription
type"sandbox.created"Yes
sandbox_idstringYesUnique identifier for the provisioned sandbox.

AgentMCPAuthRequired

Tied to execution: No | Terminal: Yes Emitted when one or more MCP servers require OAuth authorization before the agent can proceed. The stream ends after this event. Resume by calling responses.create again with the same previous_response_id after the user completes the OAuth flow.
FieldTypeRequiredDescription
type"mcp.auth_required"Yes
serversAgentMcpServerAuthInfo[]YesList of servers needing authorization, each with mcp_server_name, auth_url, and execution_ids.

AgentApprovalRequired

Tied to execution: Yes | Terminal: Yes Emitted when a tool call requires explicit user approval before it can run. The stream ends after this event. Resume by sending AgentApprovalDecisionMessage items in the next turn.
FieldTypeRequiredDescription
type"tool.approval_required"Yes
execution_idstringYes
tool_callsAgentToolCallRef[]YesThe tool calls awaiting approval, each with an id.

AgentToolResponseRequired

Tied to execution: Yes | Terminal: Yes Emitted when the agent has called a client-side tool and is waiting for the result. The stream ends after this event. Resume by sending AgentClientSideToolResponseMessage items in the next turn.
FieldTypeRequiredDescription
type"tool.response_required"Yes
execution_idstringYes
tool_callsAgentToolCallRef[]YesThe tool calls awaiting a client-supplied result, each with an id.

ResponseDone

Tied to execution: No | Terminal: Yes Final event on every stream. The status field distinguishes normal completion from cancellation or error.
FieldTypeRequiredDescription
type"response.done"Yes
status"done" | "cancelled" | "error"Yes
cancellation_reasonstringNoPresent when status is "cancelled". Values: "client-cancelled", "server-execution-timeout".
messagestringNoPresent when status is "error". Human-readable error description.

Complete usage example

This is a complete, runnable terminal chat client built on the SDK. It handles every event the harness can emit:
  • streams assistant tokens in real time,
  • shows tool calls and tool results inline,
  • prompts the user to answer ask_user_question calls,
  • prompts the user to allow or deny tool approvals,
  • shows in-chat MCP OAuth prompts,
  • demuxes parallel subagents by execution_id,
  • continues the conversation with previous_response_id across turns.
Python
"""Terminal chat client for the TrueFoundry Agent Harness SDK."""

import argparse, json, os, sys
from typing import Any, List, Optional, Union

from truefoundry_gateway_sdk import (
    AgentApprovalDecisionAllow,
    AgentApprovalDecisionDeny,
    AgentApprovalDecisionMessage,
    AgentApprovalOrToolResponseMessage,
    AgentApprovalRequired,
    AgentClientSideToolResponseMessage,
    AgentInputUserMessage,
    AgentResponsesInput,
    AgentResponsesSavedAgent,
    AgentToolResponseRequired,
    TrueFoundryGateway,
)
from truefoundry_gateway_sdk.helpers import (
    EnrichedAssistantMessage,
    EnrichedToolCall,
    is_assistant_delta,
    merge_assistant_message,
)

PendingNextTurnRequest = Union[AgentApprovalRequired, AgentToolResponseRequired]


class ChatSession:
    """Holds turn-to-turn state for one conversation."""

    def __init__(
        self,
        client: TrueFoundryGateway,
        request_template: AgentResponsesSavedAgent,
    ) -> None:
        self.client = client
        self.request_template = request_template
        self.previous_response_id: Optional[str] = None
        self.open_subagents: set[str] = set()
        self.execution_labels: dict[str, str] = {"root": "root"}
        self.assistant_buffers: dict[str, Optional[EnrichedAssistantMessage]] = {}
        self.open_tool_calls: dict[str, EnrichedToolCall] = {}
        self.pending_next_turn_requests: list[PendingNextTurnRequest] = []
        self._root_streaming = False

    def label(self, execution_id: str) -> str:
        return self.execution_labels.get(execution_id, "root")
    
    def _handle_assistant_delta(self, chunk: Any) -> None:
        # Tool calls stream across many deltas: first chunk carries id/name/tool_info,
        # later chunks append function.arguments by index, then a finish-only chunk sets
        # finish_reason (often with no role or tool_calls). merge_assistant_message folds
        # all of that; we only print and register tool calls after finish_reason is set.
        execution_id = chunk.execution_id
        self.assistant_buffers[execution_id] = merge_assistant_message(
            self.assistant_buffers.get(execution_id),
            chunk,
        )
        msg = self.assistant_buffers[execution_id]
        assert msg is not None

        if execution_id == "root" and chunk.content:
            if not self._root_streaming:
                sys.stdout.write("\nassistant: ")
                self._root_streaming = True
            sys.stdout.write(chunk.content)
            sys.stdout.flush()

        if msg.finish_reason is not None:
            self._flush_assistant(execution_id, msg)
            self.assistant_buffers[execution_id] = None

    def _flush_assistant(self, execution_id: str, msg: EnrichedAssistantMessage) -> None:
        label = self.label(execution_id)

        if execution_id == "root":
            if self._root_streaming:
                print()
                self._root_streaming = False
            elif msg.content:
                print(f"\nassistant: {msg.content}")
        elif execution_id in self.open_subagents and msg.content:
            print(f"\n[{label}] {msg.content}")

        for tool_call in msg.tool_calls or []:
            if not self._is_complete_tool_call(tool_call):
                continue
            self.open_tool_calls[tool_call.id] = tool_call
            self._print_tool_call(execution_id, tool_call)

    @staticmethod
    def _is_complete_tool_call(tool_call: EnrichedToolCall) -> bool:
        return bool(tool_call.id and tool_call.function.name)

    def _print_tool_call(self, execution_id: str, tool_call: EnrichedToolCall) -> None:
        label = self.label(execution_id)
        tool_info = tool_call.tool_info
        fn = tool_call.function
        tool_name = fn.name
        if tool_info and tool_info.original_tool_name:
            tool_name = tool_info.original_tool_name

        try:
            args = json.loads(fn.arguments or "{}")
        except json.JSONDecodeError:
            args = fn.arguments

        if tool_info and tool_info.is_client_side and fn.name == "ask_user_question":
            return
        if tool_info and tool_info.is_approval_required:
            return

        args_preview = json.dumps(args)[:160] if isinstance(args, dict) else str(args)[:160]
        prefix = "root" if execution_id == "root" else label
        print(f"\n[{prefix} -> {tool_name}] {args_preview}")

    def _handle_tool_message(self, event: Any) -> None:
        tool_call_id = getattr(event, "tool_call_id", None)
        if tool_call_id:
            self.open_tool_calls.pop(tool_call_id, None)

        label = self.label(event.execution_id)
        prefix = "root" if event.execution_id == "root" else label
        content = getattr(event, "content", None) or ""
        preview = content[:200].replace("\n", " ")
        print(f"\n[{prefix} tool result] {preview}")

    def _prompt_client_side_response(
        self,
        pending_event: AgentToolResponseRequired,
        tool_call_id: str,
        tool_call: Optional[EnrichedToolCall],
    ) -> AgentClientSideToolResponseMessage:
        content = ""
        fn_name = None
        if tool_call is not None:
            fn_name = tool_call.function.name
            args = json.loads(tool_call.function.arguments or "{}")
            tool_info = tool_call.tool_info
            if fn_name == "ask_user_question" or (tool_info and tool_info.is_client_side):
                question = args.get("question", "Answer:")
                options = args.get("options") or []
                print(f"\nassistant asks: {question}")
                for idx, option in enumerate(options, 1):
                    print(f"  {idx}. {option}")
                if options:
                    print("  Or type a free-form answer.")
                answer = input("you: ").strip()
                if options and answer.isdigit() and 1 <= int(answer) <= len(options):
                    content = options[int(answer) - 1]
                else:
                    content = answer
            else:
                print(f"\nclient-side tool response required: {fn_name}")
                content = input("you: ").strip()
        else:
            content = input("you: ").strip()

        return AgentClientSideToolResponseMessage(
            execution_id=pending_event.execution_id,
            tool_call_id=tool_call_id,
            content=content,
        )

    def _prompt_approval(
        self,
        pending_event: AgentApprovalRequired,
        tool_call_id: str,
        tool_call: Optional[EnrichedToolCall],
    ) -> AgentApprovalDecisionMessage:
        tool_name = tool_call_id
        arguments: Any = {}
        if tool_call is not None:
            tool_info = tool_call.tool_info
            tool_name = (
                tool_info.original_tool_name
                if tool_info
                else tool_call.function.name
            ) or tool_call.function.name
            try:
                arguments = json.loads(tool_call.function.arguments or "{}")
            except json.JSONDecodeError:
                arguments = tool_call.function.arguments

        print(f"\napproval needed: {tool_name}")
        print(f"  arguments: {json.dumps(arguments, indent=2)}")
        choice = input("  allow / deny / reason for denial: ").strip().lower()
        if choice in {"a", "allow", "yes", "y"}:
            approval = AgentApprovalDecisionAllow()
        elif choice in {"d", "deny", "no", "n"}:
            approval = AgentApprovalDecisionDeny()
        else:
            approval = AgentApprovalDecisionDeny(reason=choice)

        return AgentApprovalDecisionMessage(
            execution_id=pending_event.execution_id,
            tool_call_id=tool_call_id,
            approval=approval,
        )

    def build_next_turn_input(self) -> List[AgentApprovalOrToolResponseMessage]:
        next_turn: List[AgentApprovalOrToolResponseMessage] = []
        pending = self.pending_next_turn_requests[:]
        self.pending_next_turn_requests.clear()

        for pending_event in pending:
            for tool_ref in pending_event.tool_calls:
                tool_call = self.open_tool_calls.get(tool_ref.id)
                if pending_event.type == "tool.response_required":
                    next_turn.append(self._prompt_client_side_response(pending_event, tool_ref.id, tool_call))
                elif pending_event.type == "tool.approval_required":
                    next_turn.append(self._prompt_approval(pending_event, tool_ref.id, tool_call))

        return next_turn

    def _handle_event(self, event: Any) -> None:
        t = event.type

        if t == "response.created":
            self.previous_response_id = event.response_id
            return

        elif t == "agent.created":
            self.open_subagents.add(event.execution_id)
            name = event.agent_info.name
            self.execution_labels[event.execution_id] = name
            print(f"\n[subagent {name} started]")
            return

        elif t == "agent.done":
            self.open_subagents.discard(event.execution_id)
            label = self.label(event.execution_id)
            print(f"[subagent {label} finished]")
            return

        elif t == "agent.error":
            label = self.label(event.execution_id)
            print(f"\n[{label} error] {event.message}")
            return

        elif t == "mcp.initialize":
            for server in event.content:
                print(f"[mcp] connected to {server.mcp_server_name}")
            return

        elif t == "mcp.auth_required":
            print("\n[auth] MCP authorization required. Complete OAuth for each server:")
            for server in event.servers:
                print(f"        {server.mcp_server_name}: {server.auth_url}")
            print("        Press Enter once you've finished the OAuth flow.")
            input()
            self.run_turn(None)
            return

        elif t == "sandbox.created":
            print(f"[sandbox] provisioned ({event.sandbox_id})")
            return

        elif is_assistant_delta(event):
            self._handle_assistant_delta(event)
            return

        elif t == "agent.message" and getattr(event, "role", None) == "tool":
            self._handle_tool_message(event)
            return

        elif t == "tool.response_required":
            self.pending_next_turn_requests.append(event)
            return

        elif t == "tool.approval_required":
            self.pending_next_turn_requests.append(event)
            return

        elif t == "response.done" and event.status == "error":
            message = getattr(event, "message", None)
            if message:
                print(f"\n[error] {message}")
        
        else:
            print(f"\n[unknown event] {event}")

    def run_turn(self, input_items: Optional[AgentResponsesInput]) -> str:
        request = self.request_template.model_copy(
            update={
                "previous_response_id": self.previous_response_id,
                "input": input_items,
            }
        )

        last_status = "done"
        for event in self.client.agents.responses.create(request=request):
            self._handle_event(event)
            if event.type == "response.done":
                last_status = event.status
        return last_status


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Terminal chat client for the TrueFoundry Agent Harness.",
    )
    parser.add_argument(
        "agent_name",
        nargs="?",
        default=os.environ.get("AGENT_NAME"),
        help="Registered agent name (default: AGENT_NAME environment variable)",
    )
    args = parser.parse_args()
    if not args.agent_name:
        parser.error("agent_name is required (pass as argument or set AGENT_NAME)")
    return args


def main() -> None:
    args = parse_args()

    base_url = os.environ.get("GATEWAY_BASE_URL")
    api_key = os.environ.get("TFY_API_KEY")
    if not base_url or not api_key:
        print("Set GATEWAY_BASE_URL and TFY_API_KEY environment variables.")
        sys.exit(1)

    client = TrueFoundryGateway(
        base_url=base_url,
        api_key=api_key
    )
    session = ChatSession(
        client,
        AgentResponsesSavedAgent(agent_name=args.agent_name),
    )

    print("Type a message, or Ctrl-D to exit.")
    while True:
        while session.pending_next_turn_requests:
            tool_input = session.build_next_turn_input()
            if tool_input:
                session.run_turn(tool_input)

        try:
            text = input("\nyou: ").strip()
        except EOFError:
            print()
            break
        if not text:
            continue
        session.run_turn(
            [AgentInputUserMessage(role="user", content=text)]
        )


if __name__ == "__main__":
    main()

Sample run

$ python example.py support-bot
Type a message, or Ctrl-D to exit.

you: Refund invoice INV-2031 for tenant acme.
[mcp] connected to truefoundry-mcp
[sandbox] provisioned (my-tenant.a1b2c3d4)

assistant asks: Which refund reason should I record?
  1. Customer churn
  2. Billing error
  3. Goodwill credit
  Or type a free-form answer.
you: 2

assistant: Found invoice INV-2031 for $1,240.00 on 2026-04-12.

approval needed: process_refund
  arguments: {
  "invoice_id": "INV-2031",
  "amount": 1240.0,
  "reason": "Billing error"
}
  allow / deny / reason for denial: allow

[root tool result] {"refund_id":"RF-9914","status":"completed"}

assistant: Refund RF-9914 of $1,240.00 was processed for INV-2031.

you: ^D

Adapting the flow

A few common variations on top of the same skeleton:
  • Persisted history. Save session.previous_response_id to disk after every turn. On the next process start, set it before calling run_turn to resume the conversation.
  • JSON output for piping. Replace the print calls inside _handle_event with json.dumps(event.model_dump()) to emit one event per line for downstream tools.
  • Generative UI. When you detect a fenced ```openui block in agent.message.content, hand the block to the OpenUI React renderer instead of printing it. Everything else stays the same.
  • Browser / web UI. The same event shape is delivered over Server-Sent Events on POST /agent/responses. Replace client.agents.responses.create with an SSE consumer; the handlers above are unchanged.

Reference

AgentResponsesSavedAgent

Request type for calling a saved agent by name. All agent configuration is loaded from the registry.
FieldTypeRequiredDescription
agent_namestringYesName of the saved agent to invoke.
previous_response_idstringNoresponse_id from the previous turn’s response.created event. Omit on the first turn.
inputAgentResponsesInputNoInput items for this turn. See Input items.
variablesmap<string, string>NoVariable overrides for this invocation. Replaces {{variable}} placeholders defined in the agent’s instructions and messages. These override the default_value set in the saved agent spec.

AgentResponsesInlineAgent

Request type for defining the full agent configuration inline. Nothing is saved to the registry.
FieldTypeRequiredDescription
modelobjectYesLLM to use. See model.
instructionsstringNoSystem prompt for the agent. Supports {{variable}} template placeholders resolved from variables.
messagesarrayNoSeed messages injected after the system prompt and before input. See messages[].
skillsarrayNoSkills to mount from the Skills Registry. See skills[].
mcp_serversarrayNoMCP servers whose tools are exposed to the agent. See mcp_servers[].
response_formatobjectNoOutput format of the final agent response. See response_format.
configobjectNoAgent behavior and runtime settings. See config.
previous_response_idstringNoresponse_id from the previous turn’s response.created event. Omit on the first turn.
inputAgentResponsesInputNoInput items for this turn. See Input items.
variablesmap<string, string>NoValues for {{variable}} placeholders in instructions and messages.

Agent configuration

The fields below are shared between AgentResponsesInlineAgent and the AgentManifest YAML spec.

model

FieldTypeRequiredDescription
namestringYesModel identifier in provider/model-name format (e.g. anthropic/claude-sonnet-4-6, openai/gpt-4o).
paramsmap<string, any>NoKey-value pairs passed directly to the model API (e.g. max_tokens, temperature, top_p, reasoning_effort). Accepted keys depend on the model provider.

messages[]

Seed messages injected after the system prompt and before the user’s input. Each entry has:
FieldTypeRequiredDescription
rolestringYesRole of the message sender. One of user or assistant.
contentstringYesText content of the message. Supports {{variable}} template placeholders.

skills[]

Each entry mounts a skill from the Skills Registry.
FieldTypeRequiredDescription
fqnstringYesFully qualified name of the skill including version (e.g. truefoundry/skills/web-search:1).
preloadbooleanNoIf true, the skill’s tools are loaded at agent startup. If false, tools are loaded lazily on first use. Defaults to false.

mcp_servers[]

Each entry configures an MCP server whose tools are made available to the agent. Tool selectors support special tags that use MCP tool annotations:
TagMatches
@allEvery tool exposed by the server.
@read-onlyTools with readOnlyHint: true explicitly set.
@destructiveTools with destructiveHint: true explicitly set (implies readOnlyHint: false).
@writeTools that do not have readOnlyHint: true - including all @destructive tools. @write is a superset of @destructive.
Tags and explicit tool names can be combined in the same list.
FieldTypeRequiredDescription
namestringYesName of the MCP server as registered in the platform.
enable_toolsarray of stringsNoAllowlist of tools (or tags) to expose to the agent. Defaults to ["@all"].
disable_toolsarray of stringsNoBlocklist of tools (or tags) to hide from the agent even if matched by enable_tools. Defaults to [].
preload_toolsarray of stringsNoTools to eagerly load at agent startup instead of on first use. Defaults to [].
require_approval_for_toolsarray of stringsNoTools (or tags) that require explicit human approval before the agent can call them. Defaults to ["@write", "@destructive"].

response_format

FieldTypeRequiredDescription
typestringYesOutput format for the final response. Use text for plain text or json_schema for structured JSON output.

config

FieldTypeRequiredDescription
server_timeout_secondsintegerNoMaximum wall-clock time in seconds the agent is allowed to run before the server forcibly terminates the execution.
iteration_limitintegerNoMaximum number of reasoning iterations (tool call + model response cycles) the agent may perform before it is forced to stop.
sandboxobjectNoSandboxed code execution settings. See config.sandbox.
dynamic_sub_agentsobjectNoControls whether the agent can spawn sub-agents dynamically. See config.dynamic_sub_agents.
context_managementobjectNoControls how the agent manages its context window. See config.context_management.
generative_uiobjectNoControls whether the agent can emit generative UI components. See config.generative_ui.
ask_user_questionsobjectNoControls whether the agent can pause to ask the user clarifying questions. See config.ask_user_questions.

config.sandbox

FieldTypeRequiredDescription
enabledbooleanNoEnable an isolated sandbox environment for code execution. Defaults to false.
file_downloadsbooleanNoAllow files generated inside the sandbox to be downloaded by the caller. Defaults to false.

config.dynamic_sub_agents

FieldTypeRequiredDescription
enabledbooleanNoAllow the agent to spawn sub-agents dynamically to parallelize or delegate subtasks. Defaults to false.

config.context_management

FieldTypeRequiredDescription
compactionobjectNoAutomatic context compaction settings. See config.context_management.compaction.
large_tool_responseobjectNoSettings for handling tool responses that exceed token limits. See config.context_management.large_tool_response.

config.context_management.compaction

FieldTypeRequiredDescription
enabledbooleanNoEnable automatic compaction of conversation history when approaching the context limit. Defaults to false.
compaction_threshold_tokensintegerNoToken count at which compaction is triggered.

config.context_management.large_tool_response

FieldTypeRequiredDescription
enabledbooleanNoEnable truncation of tool responses that exceed token thresholds. Defaults to false.
individual_tool_response_token_thresholdintegerNoToken limit for a single tool response before it is truncated.
total_tool_response_token_thresholdintegerNoCombined token limit across all tool responses in a single iteration before truncation is applied.
preview_number_of_charactersintegerNoNumber of characters to retain as a preview when a tool response is truncated.

config.generative_ui

FieldTypeRequiredDescription
enabledbooleanNoAllow the agent to emit structured UI components (e.g. cards, forms) as part of its response stream. Defaults to false.

config.ask_user_questions

FieldTypeRequiredDescription
enabledbooleanNoAllow the agent to pause mid-run and ask the user a clarifying question before continuing. Defaults to false.

AgentManifest

AgentManifest is the shape of the YAML spec saved in the Agent Registry. It shares all agent configuration fields with AgentResponsesInlineAgent, with two differences:
  • It has name and description top-level fields that identify the agent in the registry.
  • variables is a map of objects with default_value and description, not plain string values. Callers can override these at invocation time via the variables field on AgentResponsesSavedAgent.
FieldTypeRequiredDescription
namestringYesUnique identifier for the agent. Used when referencing the agent via the SDK or API.
descriptionstringNoHuman-readable description of what the agent does.
modelobjectYesLLM to use. See model.
instructionsstringNoSystem prompt. Supports {{variable}} placeholders resolved from variables at runtime.
messagesarrayNoSeed messages. See messages[].
variablesmap<string, VariableDef>NoVariable definitions. Each entry has default_value (string) and description (string). Callers override with plain string values at invocation time.
skillsarrayNoSee skills[].
mcp_serversarrayNoSee mcp_servers[].
response_formatobjectNoSee response_format.
configobjectNoSee config.