MCP

Give an AI agent read/setup access to Argos — connecting hardware, browsing the model catalog, checking status — without ever handing it control of the arm.

Overview

The Argos MCP server is the same kind of thin client the CLI is — every tool call is just an HTTP request to the daemon (http://127.0.0.1:8000), which has to already be running. It exists so an AI agent can help a user set up and inspect their robot — connecting a serial port, discovering cameras, pulling a model — the parts of the workflow that are just setup, not actuation.

Installation

The same install that sets up argos also puts argos-mcp on your PATH — nothing extra to build.

$ curl -fsSL https://www.xn--args-ira.com/install | bash

It's a standard stdio MCP server, so it works with any MCP-compatible agent or editor — not just one vendor. Most hosts (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, …) read the same mcpServers config shape:

{
"mcpServers": {
"argos": { "command": "argos-mcp" }
}
}

Where that config lives depends on your host — check its docs for the exact file path. Some hosts also offer a CLI shortcut instead of editing a file directly; Claude Code, for example:

$ claude mcp add argos -- argos-mcp

Either way, the daemon needs to be running before any tool call here will work:

$ sudo systemctl start argos

Tools

Every tool below maps to one of the daemon's /cli/* endpoints — the same ones the CLI uses. Responses are the raw JSON the daemon returns, not the CLI's formatted terminal output.

connect_actuator

connect_actuator(port?, servo_ids?)

Connects the robot arm over the Feetech servo protocol. Called with no arguments, it discovers available serial ports instead of connecting to anything. Same underlying daemon call as `argos connect actuator` — the agent doesn't get a separate, looser code path.

port
Serial port, e.g. /dev/ttyUSB0. Omit to list available ports instead.
servo_ids
Servo IDs to ping. Omit to use the default set for the reference arm.
$ connect_actuator()
{"ports": [{"port": "/dev/ttyUSB0", "description": "USB-Serial Controller"}]}
$ connect_actuator(port="/dev/ttyUSB0")
{"port": "/dev/ttyUSB0", "connected": true, "responded": [...], "unresponsive": []}

connect_sensor

connect_sensor(index?, view_name?)

Connects a camera under a named view. Called with no arguments, it discovers available cameras. `view_name` has to match one of the target model's expected camera_views keys — check the model catalog first if you're not sure which.

index
Camera index. Omit to discover available cameras.
view_name
View name — must match the model's expected camera_views key.
$ connect_sensor()
{"cameras": [{"index": 0, "preview_jpeg_b64": "..."}], "required_views": 2}
$ connect_sensor(index=0, view_name="observation.images.image")
{"index": 0, "view_name": "observation.images.image", "connected": true, ...}

devices

devices(kind?)

Lists whatever's currently connected. Filter to just one kind, or omit it to see both.

kind
Optional. Filter to 'actuator' or 'sensor'. Omit to show both.
$ devices()
{"actuator": {"port": "/dev/ttyUSB0", "servo_ids": [1,2,3,4,5,6]}, "sensors": [...]}

list_models

list_models()

Lists the model catalog and shows which ones are already pulled locally.

$ list_models()
{"models": {"smolvla": {"repo_id": "...", "pulled": false}, ...}}

pull_model

pull_model(model)

Downloads a model's weights from Hugging Face into the daemon's local cache.

model
Required. Model name from the catalog, e.g. smolvla.
$ pull_model("smolvla")
{"model": "smolvla", "path": ".../services/models/smolvla-libero.gguf"}

status

status()

Reports what model is loaded, which devices are connected, and how long the daemon and any active run have been up.

$ status()
{"daemon_uptime_s": 142.3, "run": {"active": false, ...}, "engine": {...}}

logs

logs(source?, limit?)

Shows recent log lines from the inference engine and/or the active run.

source
Optional. Filter to 'engine' or 'run'. Omit to show both.
limit
Optional. Number of most-recent log lines to show. Defaults to 50.
$ logs(source="engine", limit=3)
{"engine": ["loaded checkpoint smolvla-libero.gguf", "bound to tcp://127.0.0.1:5555, ready"]}

stop

stop()

Safely ends the active run: stops the control loop, drives the arm back to a home position, and shuts down the inference engine. The one tool here that moves the arm — only ever to a fixed home position as part of shutting a run down, never to start or steer one.

$ stop()
{"stopped": true, "home_reached": true}