Skip to content

Projects and deploys

A Maitento project is a folder. Each kind of definition has its own sub-folder, and the file name is the definition’s name: agents/greeter.mtr defines the agent greeter.

A maitento project folder, where the file name is the definition's name: agents/greeter.mtr is the agent "greeter". Its folders: agents/; interactions/ with one-shot/, round-robin/ or managed/; connectors/open-api/ for the HTTP endpoints your tools call; external-operations/open-api/ for the tools agents see; apps/ with a folder per app holding name.mtc and an optional .mtr; capsules/; looms/ for memory; code-pipelines/ with a folder per pipeline holding pipeline-definition.json; prompts/ for long text read with @prompt("name"); json/ for JSON Schemas read with @json("name").

Sub-folders inside these are fine. Anything else in the project folder, such as a README, is ignored.

Definitions are written in .mtr files: one name = value per line.

agents/greeter.mtr
// The model this agent runs on. See the names you can use with `mt aimodel-list`.
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a friendly assistant. Keep every answer to one or two sentences."
summary = "A friendly assistant"

What you can write as a value:

ValueExample
Text"You are a friendly assistant."
Number or true/false3, true
A block of propertiesagent = { name = "Greeter" agent = @ref("greeter") }
A list["git", "curl"] (commas between items)
Another definition@ref("greeter"), or @ref("greeter", "other-namespace")
A file from prompts/@prompt("poet") reads prompts/poet.md, as in tutorial 2
A file from json/@json("recipe") reads json/recipe.json
A comment// anything after two slashes

Rules that catch people out:

  • Put spaces around =. task="x" does not parse; task = "x" does.
  • Text in .mtr files is taken literally. \n stays as a backslash and an n. Put anything longer than a sentence in a prompts/*.md file and use @prompt(...).
  • A list can’t be empty. Leave the property out instead.
  • Prompt and JSON files are found by name, wherever they sit under prompts/ and json/, so keep their names unique.

Every kind of file and every property it can set is listed in the project-file reference, and the VS Code extension completes and checks .mtr files as you type.

Sending a project to Maitento is a build. A build compiles every file, checks every @ref, and then either throws the result away (validate) or saves it (deploy). Nothing is half-saved: if any file has an error, nothing changes.

You can build from three places:

import { Maitento } from "@maitento/sdk";
const mt = new Maitento({ namespace: "hello-maitento" });
await mt.validate("./maitento"); // check only
await mt.deploy("./maitento"); // save

The third place is the portal’s Build studio, which has an editor with the same folder layout, and Validate and Deploy buttons. Errors link to the file and line.

See it in tutorial 1, Hello, agent, which deploys its first project and then breaks a reference on purpose: the deploy names the file and saves nothing.

Every definition keeps its history. A deploy that changes a definition adds a new version, and a deploy where nothing changed reports it as Unchanged. A new run uses the newest version.

To go back to an earlier version, deploy its files again. The deploy adds them as a new version, so new runs use them, and the versions in between stay in the history.

When an interaction uses an agent, it is tied to the agent version that existed when the interaction was deployed. Deploy the whole folder after you change an agent, and the interaction picks up the new version.

A deploy creates and updates. It never deletes: removing a file from your folder does not remove the definition. Delete definitions in the portal, with the SDK, or with the mt *-delete commands.

A few things hold data rather than describe behaviour, so you create them directly through the portal, the SDK or the CLI, not with a deploy: