Skip to content

Agent configuration

Agents are defined by JSON files in the agents folder, /etc/ambrogio/agents/ unless AMBROGIO_AGENTS_DIR says otherwise (see Backend configuration). The files hold data only. They never contain secrets: an API key, token or client secret is named by the environment variable that holds it, and the backend reads that variable at startup. The folder is read once at startup and every file is validated before the backend starts.

File Meaning
orchestrator.json The orchestrator. Required.
<id>.json A specialised agent with identifier <id>. At least one is required.
anything not ending in .json Ignored.

<id> must match ^[a-z][a-z0-9-]*$: a lowercase letter, then lowercase letters, digits or hyphens.

Every file must be valid JSON with only the fields listed on this page; any other field, such as a literal apiKey or token, makes the file invalid. An invalid file stops the backend with Invalid agent configuration: <id>. There is no field to disable an agent: remove or rename its file.

Fields ending in Env name an environment variable. The name must match ^[A-Z][A-Z0-9_]*$. If the variable is unset or empty at startup, the backend does not start, with Missing environment variable <NAME> for agent <id>. Several agents may name the same variable.

Field Holds
llm.apiKeyEnv The provider API key.
mcp.auth.tokenEnv The static Bearer token of the MCP server.
mcp.auth.clientSecretEnv The OAuth client secret.
Field Type Required Meaning
instructions string yes Instructions for choosing an agent. Ambrogio appends the list of agents (identifier and description) and a request to pick one.
llm object yes See llm.

description, mcp and maxToolCalls are not allowed in this file.

The orchestrator never writes the answer. Its only output is the identifier of the agent that will answer.

Field Type Required Meaning
description string yes What the agent is for. The orchestrator reads it to choose. Its instructions and credentials are never shown to the orchestrator.
instructions string yes The agent’s own instructions. At every turn Ambrogio adds the current date and time and the selected response language; see Current date and time.
llm object yes See llm.
mcp object no Connection to an MCP server. See mcp.
maxToolCalls positive integer with mcp only Most MCP tool calls the agent may make for one turn. Required when mcp is present; not allowed when it is absent.

When the limit is reached, the next tool call is not made and the turn fails with the standard error message. The count restarts at every turn.

At every turn handled by a specialised agent, Ambrogio adds this line right after the agent’s instructions, before the server prompts:

Current date and time: Monday, 19 October 2026 at 09:41 (Europe/Rome).

The time zone is AMBROGIO_TIME_ZONE (see Backend configuration), UTC when it is not set. The orchestrator does not get the line. The line is not stored in the conversation.

The backend also tells each specialised agent to answer in the language selected by AMBROGIO_LANGUAGE, Italian or English. This applies to agents with and without MCP. The orchestrator does not receive that instruction because it returns only an agent identifier. Agent files can be written in English without repeating the response language.

Field Type Required Meaning
provider string yes Who runs the model. See Providers.
model string yes Model identifier as the provider names it, for example "mistral-medium-latest".
apiKeyEnv string yes Name of the environment variable that holds the API key, for example "MISTRAL_API_KEY". Required with every provider, even when the server does not check the key.
baseURL string with openai-compatible only Absolute http:// or https:// address of the API, for example "http://localhost:11434/v1". Required with openai-compatible; not allowed with the other providers.
options object no Model options. See below.

No other field is allowed in llm.

Each agent, the orchestrator included, has its own llm, so agents can use different providers.

provider Service Endpoint
mistral Mistral The provider’s own
openai OpenAI The provider’s own
anthropic Anthropic The provider’s own
google Google Gemini API (Google AI Studio) The provider’s own
openai-compatible Any API compatible with OpenAI’s, such as a local model server or a model aggregator baseURL

Ambrogio talks to each one through its official Vercel AI SDK package. Providers that need credentials other than an API key, such as Amazon Bedrock, Google Vertex AI or Azure OpenAI, are not supported.

The orchestrator’s model must support structured output with a JSON schema: it chooses the agent, and it decides whether an answer with a question expects a reply. The model of every agent with an mcp section must support tool calling; an agent without mcp is offered no tools. With openai-compatible, the orchestrator’s requests carry their JSON schema as a response_format of type json_schema, so the server must accept it. If the provider refuses a request because the model lacks a feature, the turn fails with the standard error message.

options may contain only:

Key Type Meaning
temperature number ≥ 0 Sampling temperature.
max_tokens integer ≥ 1 Maximum length of the generated output, in tokens.

The two keys mean the same with every provider.

Requests to the model are not retried. For the orchestrator, mistral-medium-latest is the model the project recommends. It is an alias that Mistral AI points to the latest Mistral Medium version, so the model behind it changes when Mistral AI moves the alias.

Field Type Required Meaning
url string yes Absolute http:// or https:// URL of the MCP endpoint. The transport is Streamable HTTP.
auth object no Credentials. Without it, no Authorization header is sent.
tools array of strings no Names of the server’s tools the agent may use, as the server declares them. Without it, every tool is offered. The array must not be empty, and names must be non-empty strings without repeats.

No other field is allowed in mcp, transport included. Local (stdio) servers and the older HTTP+SSE transport are not supported.

The backend connects to the server once, right after it starts listening, without delaying the conversations, and lists its tools. If the server declares prompts, it also lists the prompts that have no required arguments. If the connection or either list fails, or a name in tools is not among the server’s tools, the agent stays unavailable until the backend restarts and the log has an agent_mcp_unavailable line; the backend and the other agents keep working, and a request given to that agent gets the standard error answer. A request given to the agent while it is still connecting waits for the connection.

Behaviour Value
Tools shown to the model All the server’s tools, or with tools only the listed ones, in the server’s order. A call to any other tool never reaches the server.
Tool names shown to the model The server’s tool name when it matches ^[A-Za-z_][A-Za-z0-9_-]{0,63}$, a rule that every supported provider accepts; otherwise mcp_<index>, the tool’s position in the list, from 0. Names of the form mcp_<digits> always get the positional name.
Server prompts Read again at every turn handled by the agent, before the model is called. Their text is added after the agent’s instructions, under the heading Instructions and context from the connected MCP server:.
Prompt read fails during a turn The turn fails with the standard error message; the log cause is MCP prompt retrieval failed.
Prompt reads and maxToolCalls Not counted.
Rules added to instructions The agent writes its answer only after its last tool call, when it knows how it went; it never says that something was done unless a tool did it in that answer; after an action it says briefly what it did, and when an action fails, what did not work. Text written before a tool call anyway is sent as part of the answer, followed by a line break before the text written after the call. If the answer has no words when the model stops calling tools, the agent is asked once more to write it, without tools; if it is still empty, the turn fails with the standard message.
History seen by the agent The latest AMBROGIO_HISTORY_TURNS turns. For the turns this agent answered, also its tool calls with their arguments and results, before its answer. Answers of other agents appear as text only; answers without words are left out. Tool calls are never sent to clients or shown by the HTTP API.
Positional names with tools Unchanged: the index is the position in the server’s full list.
Field Type Meaning
type "bearer"
tokenEnv string Name of the environment variable that holds the token. The token is sent as Authorization: Bearer <token> to this agent’s url only.
Field Type Required Meaning
type "oauth_client_credentials" yes
clientId string yes Client registered with the authorization server.
clientSecretEnv string yes Name of the environment variable that holds the client secret.
issuer string yes Absolute http:// or https:// URL of the authorization server. Ambrogio checks that the server advertised by the MCP endpoint matches it before sending the secret.
scope string no Scope to request.

Tokens are obtained with the OAuth client_credentials flow and renewed automatically while the backend runs.

The backend repository provides these files in examples/agents/.

Orchestrator, orchestrator.json:

{
"instructions": "Choose the agent that should answer the family's latest message. Route general questions, conversation and unclear requests to generic. If the message answers a question from an agent, choose the same agent. Return only the selected agent identifier.",
"llm": {
"provider": "mistral",
"model": "mistral-medium-latest",
"apiKeyEnv": "MISTRAL_API_KEY"
}
}

Agent without tools, generic.json:

{
"description": "Answers general questions and makes conversation.",
"instructions": "You are Ambrogio, the family assistant. Answer briefly and plainly. Requests may come from speech recognition and contain misheard words; interpret them in context. If you cannot understand a request, ask the family to repeat it.",
"llm": {
"provider": "mistral",
"model": "mistral-medium-latest",
"apiKeyEnv": "MISTRAL_API_KEY"
}
}

Agent with an MCP server, home-assistant.json:

{
"description": "Reads the state of the home's lights, switches and sensors through Home Assistant and turns devices on or off.",
"instructions": "You control the family's home through Home Assistant. Answer briefly. Choose freely among the available tools and use the ones the request needs. To answer a question about the state of a device or a sensor, read its current state and do not turn on, turn off or change anything. Change a device only when the user asks for a change. When the user refers to several devices, for example all the lights or the lights of a room, act on all of them, using the area or the device type when a tool allows it. Say that an action happened only after a tool has confirmed it; if a tool fails or a device is not available, say so.",
"llm": {
"provider": "mistral",
"model": "mistral-medium-latest",
"apiKeyEnv": "MISTRAL_API_KEY"
},
"maxToolCalls": 5,
"mcp": {
"url": "http://homeassistant.local:8123/api/mcp",
"auth": {
"type": "bearer",
"tokenEnv": "HOME_ASSISTANT_LONG_LIVED_ACCESS_TOKEN"
}
}
}

Every file above names MISTRAL_API_KEY, and the Home Assistant file also names HOME_ASSISTANT_LONG_LIVED_ACCESS_TOKEN; set those variables in the backend environment. Replace the mcp.url placeholder with the address of your server.