Recurse docs / 14
agent.yaml
Your coding agent writes agent.yaml as the contract for a Recurse agent. Use this reference to review the identity exposed to callers, runtime, prompt, input and output schemas, and registered tools. Field names are case-sensitive, and unknown fields in the manifest's structural mappings are rejected.
The manifest has eight required top-level fields: apiVersion, kind, metadata, runtime, agent, inputs, outputs, and tools.
Complete example
This manifest shows the complete shape. The prompt, Python tools, project metadata, and lockfile are separate project files.
apiVersion: recurse.run/v1alpha1
kind: Agent
metadata:
name: tiny-tuner
title: Tiny Model Tuner
summary: Tunes a tiny classifier across feature and training configurations.
runtime:
python: "3.14"
lockfile: uv.lock
agent:
prompt: prompt.md
model: gpt-5.6-luna
inputs:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
required: [target_f1, max_trials]
properties:
target_f1:
type: number
minimum: 0
maximum: 1
max_trials:
type: integer
minimum: 1
task:
type: string
default: |
Find a model with validation F1 >= {{ input.target_f1 }}.
Run no more than {{ input.max_trials }} trials.
additionalProperties: false
outputs:
$schema: https://json-schema.org/draft/2020-12/schema
type: object
required: [best_f1, target_reached]
properties:
best_f1:
type: number
minimum: 0
maximum: 1
target_reached:
type: boolean
additionalProperties: false
tools:
source: tools.py
built_in: true
register:
extract_features: ~
train_model:
volatile: true
validate_model: ~
save_best_model:
storable: falseQuote values that YAML could interpret as numbers or booleans. ~ is YAML null and has a specific meaning for tool registration.
Before approving the manifest, review:
- Whether the name, summary, inputs, and outputs form a clear interface for callers.
- Whether the prompt, model, runtime, and lockfile identify the intended revision.
- Whether every public tool is registered with settings its implementation guarantees.
- Whether paths stay inside the packaged project.
- Whether defaults and optional fields behave correctly when omitted.
Define the agent identity
| Field | Type | Required | Contract |
|---|---|---|---|
apiVersion | String literal | Yes | Exactly recurse.run/v1alpha1. |
kind | String literal | Yes | Exactly Agent. |
metadata | Mapping | Yes | Contains name, title, and summary; no other fields. |
metadata.name | Non-empty string | Yes | Identifier for the Recurse agent and deployed MCP tool. |
metadata.title | Non-empty string | Yes | Human-readable title. |
metadata.summary | Non-empty string | Yes | Description presented to callers of a deployed tool. |
Review metadata.summary as tool-selection guidance. A calling agent should be able to tell when the tool is relevant without reading the project.
Pin the runtime
| Field | Type | Required | Contract |
|---|---|---|---|
runtime | Mapping | Yes | Contains python and lockfile; no other fields. |
runtime.python | String choice | Yes | "3.14" is the current supported value. |
runtime.lockfile | String literal | Yes | Exactly uv.lock. |
CPU, memory, and secret bindings are run or deployment options, not fields under runtime. See Project files and dependencies for the package and exact lockfile.
Select the prompt and model
| Field | Type | Required | Contract and omission behavior |
|---|---|---|---|
agent | Mapping | Yes | Contains the prompt and optional model selection. |
agent.prompt | Non-empty relative path | Yes | Prompt file inside the project. |
agent.model | Non-empty string | No | Choose a model listed on the pricing section, or omit it to use the current service default. |
Preparation checks the model against the service's enabled models and pricing, then records the resolved selection. An existing deployment keeps that selection when the service default changes. Unsupported or unavailable models are rejected without substitution.
Changing the model requires a new Recurse agent revision and deployment. Stable instructions belong in the prompt file. Caller-specific values belong in inputs.
Define the caller interface
inputs is a required mapping containing a valid JSON Schema Draft 2020-12 document. For an object-shaped interface, review:
propertiesfor every caller-supplied value.requiredfor values callers must provide.defaultfor values resolved when omitted.- Constraints such as
minimum,maximum, and string or array limits. additionalProperties: falsewhen unknown caller keys should be rejected.
Declaring a property does not make it required. Manifest loading validates the schema itself; caller values are validated and defaults are applied when a run is resolved.
Declare inputs.properties.task as a string with either a non-empty default or an entry in inputs.required. A caller must supply a required task; an omitted task uses its default. The resolved string becomes the Recurse agent's task, not a top-level manifest field. {{ input.NAME }} inserts another resolved caller value into that task. The task is reserved for the agent and is absent from the read-only recurse.context().inputs mapping seen by tools; other resolved values remain there. Missing, empty, non-string, or optional task declarations without a usable default are rejected before admission.
Define the result contract
outputs is a required, self-contained Draft 2020-12 JSON Schema with type: object. A successful final JSON value must match it, and a deployment exposes it as the MCP tool's output schema.
Review required fields and additionalProperties just as you would for inputs. Internal fragment references such as #/$defs/Receipt are allowed; external document references are rejected.
A valid output is not necessarily a successful quality outcome. For example, target_reached: false can be schema-valid. Artifact files are separate from the result: a filename in JSON does not create a file, and fields describing artifacts should agree with what the completion tool saved.
Register the tool module
| Field | Type | Required | Contract and omission behavior |
|---|---|---|---|
tools | Mapping | Yes | Contains source, register, and optional built_in and defaults. |
tools.source | Non-empty relative path | Yes | Python source file that defines the tools. |
tools.built_in | Boolean | No | Defaults to true; enables harness built-ins. |
tools.defaults | Settings mapping | No | Shared settings; per-tool values override them. |
tools.register | Non-empty mapping | Yes | Exact public function names mapped to ~ or settings. |
Every public top-level function in the source file must be registered, and every registration must name a public function. Helpers begin with _. Type annotations and Google-style docstrings define the interface the Recurse agent sees.
tool_name: ~ means the tool has no per-tool overrides. It does not disable the tool. Disabling built-in tools does not remove registered custom tools.
Configure tool behavior
The following settings are valid under tools.defaults and an individual registration:
| Setting | Accepted value | Behavior when omitted | Consequence |
|---|---|---|---|
storable | Boolean | Inferred from the return annotation when not inherited. | Allows the return value to be retained and used by later tools. It does not create a file artifact. |
no_storage | List of parameter names | Empty list when not inherited. | Listed parameters must receive inline values rather than stored-object references. |
volatile | Boolean | false when not inherited. | Identical arguments may produce different results, so repeated calls are not evidence of a stuck loop. |
pure_args | List of parameter names or null | Empty list when not inherited. | Listed parameters are not mutated. null declares every parameter pure. |
Precedence is an explicit per-tool value, then tools.defaults, then the harness default or inference. tool_name: ~ and pure_args: null use null for different purposes.
Only declare purity the implementation guarantees. An incorrect declaration can allow unsafe concurrent work. Shared parameter-name settings must refer to parameters that exist on every affected tool.
Review paths and validation stages
Declared paths are portable paths relative to the project directory. Do not use absolute paths, backslashes, empty segments, . or .. segments, or control characters.
Different checks establish different things:
| Validation point | What it checks | What it does not prove |
|---|---|---|
| Manifest loading | YAML structure, required and unknown fields, literal values, types, and JSON Schema validity. | File existence, packaging, execution, or result quality. |
| Packaging | Declared files, public tool registrations, annotations, docstrings, build backend, and exact lockfile in the archive. | Successful serverless preparation or a correct run. |
| Serverless preparation and execution | Model availability, runtime tool contracts, input resolution, and final output conformance. | That a valid result meets your substantive objective. |
Common corrections
| Invalid declaration | Correction |
|---|---|
Snake-case API version field or tools.built-in | Use apiVersion and tools.built_in. |
runtime.python: 3.14 | Use the string "3.14". |
Missing outputs, or outputs.type: array | Declare an object result schema. |
tools.register: {} or a list of names | Supply a non-empty mapping from names to null or settings. |
pure_args: true | Supply a list, [], or null. |
| A registration whose function is absent | Match the public function's exact name. |
| A prompt path outside the project | Package the file and use a relative path. |
See Write prompts and Python tools for tool design and Troubleshooting for recovery from a rejected manifest or package.