Skip to main content
An agent is an LLM loop you declare in the BRICKS Buttress server config. It runs inside the server process, uses local functions (and MCP servers) as its tools, and keeps its conversation history in session files on disk. The primary consumer is automation: a local function or daemon calls context.agents.run(...) to add multi-step reasoning to a server-side workflow. An interactive CLI drives and inspects the same agents.
Agents are experimental. The [[agents]] and [agents_options] config keys, the /agents endpoints, the SSE event shape, and the context.agents API may change between releases without a deprecation window. The server prints this notice at startup. Pin your bricks-buttress version if you build on it, and re-read this page after upgrading.

Define an agent

Agents are off until the config declares at least one [[agents]] table:
Any key not listed above passes through to generation, so temperature, top_p, and friends work as written. Agent definitions are validated at startup, not on first run: an unknown buttress/ model, a missing prompt file, a malformed tool name, or a duplicate agent name stops the server with an error rather than silently dropping the agent.

Global options

[agents_options] applies to every agent:

Models

model is provider/model-id, split on the first slash — so the model id itself may contain slashes. buttress/<repo_id> targets a model this server already hosts. The id must match the repo_id of a configured ggml-llm or mlx-llm [[generators]] entry, and the server checks that at startup. Traffic reaches the generator through an in-process loopback — no socket, and no extra configuration. Enabling [openai_compat] is not required; that key still governs only the external HTTP route.
Any other prefix is a hosted provider — anthropic/…, openai/…, google/… — authenticated by that provider’s usual environment variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, …). Set it in [env] or in the process environment.
OAuth-based provider logins are not supported. A hosted provider needs an API key in the environment.

Tools

tools lists local function names explicitly. Each tool call runs through the normal functions executor, so a tool gets the same lazy reload, scratch directory, spawn tracking, and meta.timeout deadline it would get over HTTP or MCP — and the function’s meta.parameters JSON Schema is what the model sees. Aborting a run aborts its in-flight tool calls and the processes those calls spawned. A listed function that does not exist fails the run rather than letting a headless automation improvise around a missing capability.

MCP servers

[agents.mcp_servers.<name>] adds an MCP server’s tools alongside the local functions. Give each server exactly one of url (Streamable HTTP) or command (stdio):
MCP tools are exposed under server-qualified names — mcp__github__create_issue — so they cannot collide with local function names. Servers connect lazily on the first run that needs them, and a server that will not connect fails the run unless it is marked optional = true, in which case the agent runs without its tools.

Run an agent

From a local function

This is the primary surface. Every configured agent is reachable through context.agents:
A run inherits the calling function’s lifetime and deadline: context.signal — the function’s meta.timeout expiring, or the caller disconnecting — aborts the run and its tool calls. Agent loops are slower than ordinary function calls, so raise meta.timeout, or move the work into a daemon, which has no deadline. Agent progress mirrors onto the function’s own SSE stream as agent events, so an SSE caller of the function sees the agent working without extra wiring. Every method throws when the server has no [[agents]] configured.

Over HTTP

A failed run returns AGENT_RUN_FAILED with the sessionId attached, so the transcript is still reachable. An aborted run answers 499; other failures answer 500. Unknown agents and sessions answer 404 NOT_FOUND. Streaming responses carry slimmed agent events: partial assistant messages and full tool results are omitted, because they would dwarf the stream. Fetch the transcript from the sessions endpoint when you need the complete record.

From the CLI

bricks-buttress agent is a streaming chat client for an agent on a running server. Point it at the same config the server uses and it finds the port and the local token itself:
The chat streams text, thinking, and tool calls as they happen. In the chat, /exit quits, /new starts a fresh session, and Ctrl+C aborts the current run — a second Ctrl+C quits.

Run result

context.agents.run and POST /agents/<name>/run both resolve to: stopReason is one of end_turn (the agent answered), max_turns, token_budget, aborted, or error. Treat anything but end_turn as an incomplete answer.

Sessions

Every run belongs to a session. Omit sessionId and the run starts a new one; pass it back to continue the conversation; add fork: true alongside it to branch into a fresh session that records its parent, leaving the original untouched. Sessions are JSONL files under sessions_dir, scoped by agent name, and written as the run streams — so a run that was aborted or timed out still leaves a continuable transcript. Runs on the same session queue behind each other; different sessions run in parallel. A retention sweep prunes sessions by age (session_max_age) and count (session_max_count, per agent).
Sessions are scoped by the agent’s name. Renaming an agent orphans its existing sessions — they stay on disk but the renamed agent will not list or continue them.

Security

/agents authenticates like the functions surface, not like the open inference endpoints:
  • A bound server requires a workspace access token. See workspace binding.
  • An unbound server rejects remote calls entirely, unless [agents_options] allow_unauthenticated = true.
  • Cross-site browser requests are always rejected.
At startup the server writes an ephemeral runtime token to a runtime-token file next to sessions_dir, with mode 0600. Same-host tools — the CLI above — read it and authenticate automatically, bound or not.
allow_unauthenticated = true lets anyone who can reach the port run every agent, and therefore every tool you gave it. Only do this on a trusted network.
Agent definitions are trusted input, exactly like the config file and function files. An agent is only as safe as the functions and MCP servers you hand it: a model is choosing when to call them, so give an agent the narrowest tool list that does its job. A bound server cannot mint workspace tokens for itself — the loopback to buttress/ models is authorized by an internal token instead, and never by workspace credentials.

Samples

The server package ships config/function-samples/run-agent.ts, a local function that drives a configured agent and returns its conclusion, session id, turn count, and stop reason. config/sample.toml carries a commented [[agents]] block. See local functions for the rest of the samples.

Next steps

Local functions

Write the functions an agent uses as its tools.

Configuration

The full TOML reference, including the generators agents run on.