API docs / Cradle MCP
API · Cradle MCPCradle's MCP tools
Cradle speaks the Model Context Protocol, so an MCP connected entity can see and drive your Amigas within the limits you set.
26 tools in three groups
Cradle's MCP server lets an MCP connected entity read Cradle's state and, when you allow it, start instances, work inside an Amiga and build into lab copies. Read-only tools are always on; tools that change state are listed as such and are off until you switch them on in Cradle.
Cradle and instances
READ cradle_status
Cradle's version and build, how many instances are running, the health of its parts (discovery, ACKitchen worker, stoves, GitHub, patch manager), the host and the default runtime engine. Changes nothing.
READ list_instances
Every instance Cradle knows: number, folder name, friendly name, system, whether it runs, its runtime and AC-control ports, engine and machine. Archived ones are left out unless include_archived is true. Changes nothing.
include_archived(boolean): Also list archived instances.running_only(boolean): Only instances that are running now.
READ instance_status
One instance in detail: state, last start, CPU and machine, build it was created and last started with, and (when it runs) what its own runtime answers on /api/system/status. Changes nothing.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name.
READ instance_logs
The last lines of one of an instance's or a lab's log files in its logs/ folder: native-runtime (default), native-bridge or launcher-runtime. At most 500 lines, long lines cut. Changes nothing.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.log(string): Which log (default native-runtime).lines(integer): How many lines from the end (default 100).
READ profile
The runtime profile (AC_PROFILE=1) of an instance or lab, parsed from the last N 5-second reports of its native-runtime log, oldest first: CPU %, the emulation thread's time by category with the translated/interpreter split, the hottest host functions and guest blocks (with their first bytes when the runtime prints them: AC_PROFILE_BYTES=1 on a runtime that has it), the opcodes the JIT interpreted inline, the MMU and JIT counters; and the windows taken together. Changes nothing.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.windows(integer): How many 5-second reports from the end (default 3).
READ appliance_status
The Raspberry Pi card builder: whether an image build is running, done or failed, its steps with percentages, the result image and its checksum, the settings it was started with (never a password) and the tail of its log; also whether a write to an SD card or USB stick is running. Changes nothing.
log_lines(integer): Lines of the build log to include (default 40).
READ appliance_images
The card images already built (name, size, date) and the output folder, and the newer images published for this Cradle's build. Changes nothing.
READ media_repository
The Media Repository folder (the install media Cradle uses to install an OS from the person's own disks): its path and the files in it, two folders deep, at most 300. Changes nothing.
WRITE start_instance
CHANGES STATE: starts an instance's runtime (the Amiga boots), as the Start button does. No window is opened. Does nothing if it already runs. Cradle refuses an archived instance or one whose port another instance holds.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name.
WRITE stop_instance
CHANGES STATE: stops an instance's runtime, as the Stop button does; whatever the Amiga had not saved is lost. Does nothing if it is already stopped. Refused while a card build or a card write is running.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name.
WRITE start_card_build
CHANGES STATE and uses the host's disk and CPU for a long time: starts a Raspberry Pi card image build, with the settings the Cradle page sends. kind native (one instance boots on the card; give instance, or fresh) or cradle. Copies the named instance into the image. It takes no Wi-Fi or SSH password: set those in Cradle's window. Refused while a build or a card write is already running. It does not write to a card.
kind(string): native (default) or cradle.board(string): The board (default pi5).hostname(string): The card's host name (default amigachrome).instance(string): The instance to copy onto the card (number, folder or friendly name).fresh(object): Instead of an instance: {"personality": ..., "answers": {...}} for a fresh machine made from a recipe (native only).ssh(boolean): Turn SSH on (the password is set in Cradle's window).vnc(boolean): Turn VNC on (cradle kind only).country(string): Two-letter country code (default GB).swap_monitors(boolean): Native: swap the two HDMI monitors.primary_screen(string): Native: which screen is primary.only_screen(boolean): Native: show only the primary screen.
WRITE cancel_card_build
CHANGES STATE: cancels the running card image build and unmounts its image. Does nothing if no build is running.
Inside an Amiga
READ instance_screenshot
A picture of what an instance or lab shows now: the RTG screen (head rtg, the default) or the chipset screen (head chipset). It comes back as an image. Changes nothing.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.head(string): Which screen (default rtg).scale_percent(integer): RTG only: 100 full size, 50 half (default), 25 a quarter.
READ amiga_run_status
The agent's state on an instance or lab: idle, waiting (a script not picked up yet), running (with the script), or done, and what the last script printed. Changes nothing.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.
READ labs
The labs in /root/AmigaChrome-dev/labs (scratch copies made by lab_instance.py): name, source, whether each runs and on which ports, who started it and why; and the lab port locks. Changes nothing.
WRITE amiga_run
CHANGES STATE: runs an AmigaDOS script inside a running instance or lab and returns what it printed, through the agent (S:ACAgent; install_agent puts it there). Waits up to timeout seconds (default 30); a script still running then is reported, not stopped: read it later with amiga_run_status. Start test programs that might hang with Run >NIL: so the script itself returns. The script runs with a stack of stack bytes (default 262144), and so do the programs it starts. Refused while another script runs.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.script(string, required): AmigaDOS commands, one per line (Latin-1).timeout(integer): Seconds to wait for it (0: hand it over and return).stack(integer): Bytes of stack for the script's commands and the programs it starts, from 4096 (default 262144; the agent's Shell alone has 4096, too little for SDL2 and GL programs). 0 leaves the Shell's setting as it is.
WRITE amiga_input
CHANGES STATE: keys, typing and the mouse on a running instance or lab, as from its window. action key: keys pressed together, e.g. "ctrl lamiga ramiga" or "lamiga w" or hex raw codes "63 66"; type: text on a US keyboard; click: x, y on the RTG screen (button left, right or middle); move: dx, dy; aim: put the pointer at x, y on the RTG screen.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.action(string, required)keys(string): key: names or hex raw codes, held together in order.text(string): type: up to 2000 characters.x(integer)y(integer)dx(integer)dy(integer)button(string)
WRITE instance_reset
CHANGES STATE: a soft reset of a running instance or lab (Ctrl-Amiga-Amiga): whatever was unsaved is lost and the Amiga boots again. A script the agent was running is renamed run.crashed-TIME so it is not run again.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.
WRITE install_agent
CHANGES STATE: puts the agent (S:ACAgent, started from S:User-Startup) on a stopped instance or lab whose SYS: is a host folder, so amiga_run works there from its next start. S:User-Startup is backed up first into the instance's archive/ with a PUT-BACK.txt.
instance(string, required): The instance: its number (24), folder (Instance-24) or friendly name; or a lab as lab:NAME.
WRITE lab
CHANGES STATE: labs in /root/AmigaChrome-dev/labs through lab_instance.py. copy: a new lab NAME from source (an instance, never written to); it runs in the background, follow it with labs. start: run lab NAME muted on lab ports (who and purpose required; env for the runtime's debugging switches such as AC_PROFILE). stop. archive: stop it and move it into archive/ (nothing is deleted).
action(string, required)name(string, required): The lab's name (letters, digits, . _ -).source(string): copy: the instance to copy (number, folder or friendly name).who(string): copy/start: who the lab is for.purpose(string): start: what it is for (goes on the port lock).env(object): start: the runtime's debugging switches, e.g. {"AC_PROFILE": "1", "AC_PROFILE_BYTES": "1", "JIT_MMUVIEW": "1"}. Names AC_*, AC090_*, AC460_* or JIT_*, up to 16; values up to 64 characters with no path.
Build, labs and deploy
WRITE stove_build
CHANGES STATE: builds one module at one commit with a stove, in Cradle's own clean checkout (/root/AmigaChrome-dev/cradle-builds/<repo>), with the target directory's own build-amiga.sh (STOVE= and OUT= set). Runs in the background and returns a job id: follow it with stove_job. One job at a time per repo; 900 s at most.
stove(string, required)repo(string, required): owner/name, one of the repos in Cradle's settings (e.g. DalsinAI/amigachrome-guest).ref(string, required): A full 40-character commit SHA, or main (resolved to a SHA and reported).target(string, required): The module's directory in the repo (libraries/opendatatypes, devices/opencarddisk.device, or . for the top).
READ stove_job
A job's state (queued, running, done or failed), the last lines of its log (up to 500) and, when it has ended, its output files with sizes and SHA-256. A deploy_dev dry run's job carries its ticket once done. Changes nothing.
job(string, required): A job id from stove_build, repo_test or deploy_dev.lines(integer): Log lines from the end (default 40).
WRITE lab_put
CHANGES STATE (labs only): copies a finished job's files into a lab's SYS:. Each file it replaces is kept first in the lab's archive/ with a PUT-BACK.txt. The lab must be stopped, or restart true stops it, copies and starts it again (with the who and purpose it ran with).
lab(string, required): The lab, as lab:NAME (instances are refused).job(string, required): A job id from stove_build, repo_test or deploy_dev.files(array, required): {from, to} pairs: from a path in the job's out/ (as stove_job lists it), to an Amiga path under SYS: (SYS:Libs/datatypes.library). No .., absolute host paths or symlinks.restart(boolean): Stop the lab, copy, start it again.
READ lab_get
One file from a lab's SYS: (SYS:Libs/name.library): its size and SHA-256, and its contents when under 256 KiB (text, or base64 for binary). Changes nothing.
lab(string, required): The lab, as lab:NAME (instances are refused).path(string, required): An Amiga path under SYS:.
WRITE deploy_dev
CHANGES STATE, in two steps. mode dry_run: kind cradle (default) runs scripts/deploy_dev.py --dry-run for DalsinAI/amigachrome at ref into /root/AmigaChrome as a background job (stove_job gives its ticket when done); kind instance checks putting a done job's files (as lab_put) into a stopped instance's SYS: and gives the ticket at once. Either way Cradle asks the person on Daletop (ACNotify: Approve / Decline). mode apply takes the ticket only: refused unless it is within 60 minutes of its dry run, its arguments and checksums still match and it was approved on Daletop; waits up to wait seconds for the approval. A session can't approve: only the person can.
mode(string, required)kind(string): dry_run: what is deployed (default cradle).ref(string): A full 40-character commit SHA, or main (resolved to a SHA and reported).job(string): A job id from stove_build, repo_test or deploy_dev.files(array): {from, to} pairs: from a path in the job's out/ (as stove_job lists it), to an Amiga path under SYS: (SYS:Libs/datatypes.library). No .., absolute host paths or symlinks.instance(string): kind instance: the instance (number, folder or friendly name), stopped.ticket(string): apply: the dry run's ticket.wait(integer): apply: seconds to wait for the approval (default 600).
WRITE repo_test
CHANGES STATE (runs code): one of a repo's own test scripts (tests/<dir>/test_*.sh) in Cradle's clean checkout at ref, in the background (900 s at most): follow it with stove_job for its exit code and output.
repo(string, required): owner/name, one of the repos in Cradle's settings (e.g. DalsinAI/amigachrome-guest).ref(string, required): A full 40-character commit SHA, or main (resolved to a SHA and reported).script(string, required): tests/m68k/test_NAME.sh and the like.