AI Rover Studio

io.github.taewankungPublished by GitHub account taewankungv1.1.2Updated Oct 31, 2026

Let an AI agent drive a 2D robot rover simulator in the browser: sensors, camera, wiring and code.

VerifiedSTDIODesktop onlyDeveloper ToolsMedia & DesignAI & ML

Overview

AI-generated overview

Drives a 2D robot rover simulator in the browser: sensors, camera, body modules, programs, and generated wiring and firmware.

What you can do
  • Observe and drive the rover: read pose, velocity and sensor readings, drive or turn by exact angles, stop, and toggle simulation, autopilot and motor power
  • Run and inspect JavaScript programs on the robot, and send chat commands to the in-page parser
  • Configure the body: list, add, update and remove sensors, fit wheels, head, display or camera modules, and control the head pan servo
  • Use the face display: set emotions, show speech text, draw pictures, read back pixels, and switch display modules
  • Capture camera images with detections, read the sensor-built occupancy map, and edit the world's light, obstacles and floor tape
  • Generate wiring, bill of materials and firmware for ESP32, Arduino Uno…
When to use it
Useful for experimenting with robot behaviour, sensor placement and control loops in simulation, and for turning a simulated body into a real build plan with wiring and firmware. Also handy for teaching or demoing robotics concepts without hardware.
Requirements
Node.js 18 or newer, run locally over stdio (for example via npx). The simulator page must be open in a browser and its tab kept visible, since background tabs pause the simulation. Optional environment variables ROVER_BRIDGE_PORT, ROVER_SIMULATOR_URL and ROVER_ALLOWED_ORIGINS. No account or API key declared.
Before you install
Simulation only, nothing is sent to real hardware. The bridge listens on 127.0.0.1 and accepts only allowed origins, but ROVER_ALLOWED_ORIGINS widens which sites may connect, so add only origins you trust. run_program executes JavaScript you send in a Web Worker, replacing any running program. Tools change simulator state and the rover body configuration.

Installation

In SourceWeft

  1. Open AI Rover Studio in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

ai-rover-mcp

MCP server for AI Rover Studio, a 2D robot simulator that runs in the browser. It lets an AI agent (Claude Code, Claude Desktop or any MCP client) observe and drive the rover, change its body, run programs and read the real wiring for an ESP32 / Arduino Uno / Raspberry Pi. Simulation only: nothing is sent to hardware.

MCP client ──stdio──▶ ai-rover-mcp ──WebSocket 127.0.0.1:8765──▶ simulator page in your browser

The page must be open in a browser and the tab must stay visible, because browsers pause animation frames in background tabs, so the simulation stops there. The badge in the page header shows MCP ● connected while the bridge is up.

Setup

Needs Node.js 18 or newer.

Claude Code

bash
claude mcp add ai-rover -- npx -y ai-rover-mcp

Then ask Claude to "open the rover simulator". open_simulator opens the hosted simulator with ?mcp=on, which tells the page to connect to this server.

Claude Desktop / other clients

json
{  "mcpServers": {    "ai-rover": {      "command": "npx",      "args": ["-y", "ai-rover-mcp"]    }  }}

From a clone

bash
npm installclaude mcp add ai-rover -- node /path/to/ai-rover-mcp/server.js

Opening the simulator yourself

Open https://ai-rover.app/app/?mcp=on. Without ?mcp=on the hosted page does not try to reach a local MCP server; you can also press เชื่อมต่อ MCP on its Agent / MCP page. A copy opened from disk (file://) or from localhost connects on its own.

Tools

ToolWhat it does
open_simulatorOpens the simulator (hosted, or ROVER_SIMULATOR_URL) in the default browser and waits for it to connect
connection_statusWhether the page is connected
get_observationPose, velocity, sensor readings, face state and simulation status
read_sensorsFresh readings from the enabled sensors, with mounting info and units
driveforward / backward / left / right for 0.2–10 s; waits by default and returns the new observation
turnTurn on the spot by an exact angle
stopEmergency stop: cancels movement, turns autopilot off, pauses
set_simulationStart or pause the simulation
set_autopilotRule-based autopilot (seek light, avoid obstacles) on or off
set_powerMotor power, 10–100 %
set_head / get_headHead pan servo: turn to an angle, or track the light
press_button / get_button_eventsPress a push button on the rover / read presses made by a person
run_program / stop_program / get_programRun JavaScript that uses the rover library in the page, stop it, read its status and console
set_emotionFace expression on the display, with a message in the top line
display_textThe rover "says" a text: small face + speech bubble on the Face Display (Thai supported)
set_displayFit a display module: ssd1306 (128×64 mono OLED), ssd1331 (96×64 colour OLED) or st7789 (240×240 colour TFT)
draw_displayShow a picture on the Face Display from rows of characters
read_displayWhat the display shows: mode, stats and pixels as ASCII art
move_lightMove the light source
reset_worldRover and light back to their start positions
send_chatSend text to the in-page Agent Chat parser and get its reply
get_manifestRobot Capability Manifest (ai-cartridge/body-v1)
list_sensors / add_sensor / update_sensor / remove_sensorConfigure the sensors on the robot body
set_moduleFit or remove wheels, head, display or camera
capture_cameraPicture from the rover's forward camera (PNG image) plus detections with bearing and distance
get_sensor_mapMap the rover built from its own sensors: occupancy grid, floor colors, camera landmarks, bump points
get_wiringCircuit for the fitted body on esp32, uno or raspberrypi: every pin, power budget, wiring issues, pins.h / pins.py
get_firmwareComplete firmware for the fitted body: Arduino .ino or Raspberry Pi rover.py
get_bomParts list to build the fitted body for real, with quantities, approximate prices and shop links
list_worldWorld layout: light, obstacles, colored floor tape
add_obstacle / remove_obstaclePlace test objects: cylinder, ball, box or wall, with size and color
add_color_tape / remove_color_tapeColored floor tape for the color sensor, line following and the camera

Example prompts: "Open the rover simulator and drive it to the light without using the autopilot", "Add an IR sensor on each side angled ±45° and tell me the readings", "What parts do I need to build this rover on an ESP32?"

Configuration

  • ROVER_BRIDGE_PORT (default 8765). If you change it, the page needs ?mcpPort=<port> (open_simulator adds this for you).
  • ROVER_SIMULATOR_URL: the page open_simulator opens instead of the hosted one. A URL (http://localhost:5173 while developing) or a path to a local build (dist/index.html, relative to the working directory).
  • ROVER_ALLOWED_ORIGINS: extra comma-separated https:// origins the bridge accepts (for a self-hosted copy of the simulator).
  • On the page: ?mcp=on connects to the server, ?mcp=off never does.

Security

The WebSocket listens on 127.0.0.1 only. It accepts connections only from the page opened from disk (file://), from localhost, from the hosted simulator, or from the origin of ROVER_SIMULATOR_URL / ROVER_ALLOWED_ORIGINS, so other websites open in the same browser cannot pose as the rover. The page only runs the fixed set of methods listed above; it never evaluates code it receives from the server, except the program you send with run_program, which runs in a Web Worker.

License

MIT

Source: README.md at commit 2857a7d

Tools

41

open_simulator

Open simulator

Open the AI Rover Studio page in the default browser and wait (up to 15 s) for it to connect to this MCP server.

Takes no parameters.

connection_status

Connection status

Report whether the simulator page is connected to this MCP server.

Takes no parameters.

get_observation

Get observation

Current robot pose (x, y, heading_deg), velocity, every enabled sensor reading, face display state, plus simulation status (running, autopilot, power, lamp position).

Takes no parameters.

read_sensors

Read sensors

Fresh readings from all enabled sensors, with their mounting position, angle, range and unit.

Takes no parameters.

drive

Drive

Move the rover for a fixed time: forward / backward, or turn left / right in place. Turns autopilot off and starts the simulation. Rotation speed is about 2 rad/s (≈115°/s), so turning 90° takes ≈0.8 s. Forward speed is 65 % of max speed (≈61 px/s at the default 55 % power; backward ≈51 px/s). With wait=true (default) it returns the observation after the move finishes. The rover stops on collisions.

Parameterswaitsecondsdirectionrequired

turn

Turn by degrees

Turn the rover in place by an exact angle (+ = right / clockwise, − = left) at ≈115°/s, then return the observation. Unlike drive left/right there is no 0.2 s minimum, so small corrections (e.g. 7°) are exact.

Parametersdegreesrequired

run_program

Run a rover program

Run JavaScript on the robot with the `rover` library (same as the Code page; it replaces any running program and starts the simulation). Top-level await works. Motion (await): rover.forward(s), backward(s), turn(deg), left(s), right(s); rover.drive(speed −1…1, turn °/s) keeps going; rover.stop(); rover.power(%). Readings (sync, 50 ms snapshots): rover.read(id), sensors(), distance() (nearest sonar/IR cm), light() (%), bumped(), pose(), time(). Head/face: await rover.head(deg), trackLight(on), headAngle(); rover.face(emotion, text), say(text, s). Buttons: pressed(id), onButton([id], fn(event)), await waitButton([id]). Time/output: await wait(s), await rover.until(fn, timeout), log(...). Customise: await rover.module(name, on), addSensor({type,name,x,y,angle,range}), updateSensor(id, fields), removeSensor(id), sensorList(). Returns immediately; poll get_program for status and the console.

Parameterscoderequired

stop_program

Stop the program

Stop the running rover program (its worker is terminated, the wheels stop).

Takes no parameters.

get_program

Program status

The current/last rover program: code, status (idle, running, listening, done, stopped, error), error { message, line } and the last 50 console lines.

Takes no parameters.

stop

Stop

Emergency stop: cancel any movement, turn autopilot off and pause the simulation.

Takes no parameters.

set_simulation

Start / pause simulation

Start (running=true) or pause (running=false) the physics simulation.

Parametersrunningrequired

set_autopilot

Autopilot

Enable the built-in rule-based autopilot (seeks the light, backs away from obstacles; also starts the simulation) or disable it.

Parametersenabledrequired

set_power

Motor power

Set motor power in percent (10–100). Max speed is 170 px/s at 100 % (drive uses 65 % of it forward, 55 % backward).

Parameterspercentrequired

set_emotion

Face expression

Show an expression on the robot's face display (emulated SSD1306 128×64 OLED); the message appears in the top text line and scrolls if long. Held ~5 s before sensor-driven emotions resume.

Parametersemotionrequiredmessage

display_text

Show text on the display

Make the rover "say" a text on the Face Display module (128×64 OLED): a small rover face with a talking mouth and the text typed out in a speech bubble (Thai supported, ~11 characters per line, 2 lines per bubble, extra bubbles follow). Returns to the big face after `seconds`, or once every bubble has been shown if that takes longer.

Parameterstextrequiredseconds

set_display

Choose the display module

Fit a different Face Display module: "ssd1306" (0.96" 128×64 monochrome OLED, I²C; default), "ssd1331" (0.95" 96×64 colour RGB OLED, SPI) or "st7789" (1.3" 240×240 colour IPS TFT, SPI). Faces and text adapt to the resolution; colour modules show a colour per emotion. `panel` changes the SSD1306 glass colour. Returns read_display.

Parametersmodelpanel

draw_display

Draw a picture on the display

Show a picture on the Face Display module for `seconds`, then return to the face. Pass one string per pixel row, at most the module's resolution (SSD1306 128×64, SSD1331 96×64, ST7789 240×240; see read_display). Characters space . 0 - _ are dark. On colour modules (SSD1331, ST7789) these letters pick a colour: R red, O orange, Y yellow, G green, C cyan, B blue, P purple, M pink, W white, A grey, N brown, K black; any other character is white, and `palette` adds or overrides letters. On the monochrome SSD1306 every non-blank, non-black character is a lit pixel and colour comes from its panel variant (`panel`). The picture is centred; `scale` enlarges each pixel (e.g. 64×32 with scale 2 fills 128×64) and is lowered if it would not fit. Returns read_display, so the result shows what the module actually displays.

Parametersrowsrequiredpanelscalepaletteseconds

read_display

Read the display

Read back what the Face Display module is showing: mode (face/text), text lines, module stats (frames, fps, bytes) and the actual 128×64 pixels from the module's GDDRAM as ASCII art (█▀▄, 2 pixel rows per line).

Takes no parameters.

move_light

Move light source

Move the light source to world coordinates (clamped to 42–758 × 42–478).

Parametersxrequiredyrequired

reset_world

Reset world

Put the rover back at its start pose (120, 265, heading 0°) and the light at (690, 70). Turns autopilot off.

Takes no parameters.

send_chat

Send chat command

Send a message to the in-page Agent Chat (offline Thai/English command parser, e.g. "ไปหาแสง", "อ่านเซนเซอร์", "เดินหน้า 2 วินาที") and return the rover's reply. It shows up in the chat panel.

Parameterstextrequired

get_manifest

Get robot manifest

The Robot Capability Manifest (ai-cartridge/body-v1): motors, installed sensor modules and display.

Takes no parameters.

list_sensors

List sensors

All configured sensors (enabled or not) with id, type, mounting position, angle and range.

Takes no parameters.

add_sensor

Add sensor

Install a new sensor on the rover body. Unspecified fields use the type defaults (centre of the body, facing forward).

Parametersxynametyperequiredanglerangeenabled

update_sensor

Update sensor

Change a sensor's name, type, mounting position, angle, range or enabled flag. Use list_sensors to find ids.

Parametersxyidrequirednametypeanglerangeenabled

remove_sensor

Remove sensor

Uninstall a sensor by id.

Parametersidrequired

set_module

Install / remove a module

Install or remove a body module: "wheels" (drive motors + wheels; without them the rover cannot move), "head" (pan servo that turns the face and the light sensors), "display" (AI Face Display OLED) or "camera" (forward-facing camera).

Parametersmodulerequiredinstalledrequired

set_head

Turn the head

Control the Head module (pan servo, needs set_module head first). angle_deg turns the face to −90…90° relative to the body (+ = right, servo ≈180°/s) and switches to manual. track_light=true makes the head find and follow the light with its own light sensors: it sweeps the full range, settles on the brightest angle and keeps hill-climbing (rescans if the light is lost). Light sensors are directional (brightest when facing the lamp) and ride on the head. Starts the simulation.

Parametersangle_degtrack_light

press_button

Press a button

Press a push-button module (a sensor of type "button") for `seconds` and release it, like a person would. Held ≥ 0.8 s is a long press. The built-in program reacts: short press toggles the head's light tracking (happy face), long press stops everything (driving, autopilot, tracking; pauses the simulation). Returns the press event.

Parametersidrequiredseconds

get_button_events

Button events

Button presses since the last call (oldest first, then cleared): { id, name, press: "short" | "long", seconds, at (ms epoch) }, plus every button and whether it is held right now. People press buttons in the simulator page; poll this to react to them.

Takes no parameters.

get_head

Head status

Head module status: angle_deg, target_deg, mode (manual / track_light), tracking phase and the head light brightness.

Takes no parameters.

capture_camera

Capture camera image

Take a picture with the rover's forward-facing camera (needs the camera module). Returns a 320×240 first-person PNG (walls, colored obstacles, colored floor tape, the light source) and a JSON list of what is in view: type (obstacle / tape / light), id, color, bearing_deg (0 = straight ahead, + = right) and distance_cm.

Parametersfov_deg

get_sensor_map

Get sensor map

The map the rover has built from its own sensors only (not the true layout): an occupancy grid of 10 px cells as ASCII rows ("#" occupied by a sonar/IR echo or bumper hit, "." free, " " unknown, "@" rover, a capital letter = floor color read by a color sensor), camera landmarks with estimated world x/y, bumper hit points, the pose and exploration stats. clear=true wipes the map first.

Parametersclear

get_wiring

Get wiring

How the fitted rover body wires to a real microcontroller (the Wiring page): for each part (TB6612FNG motor driver, each enabled sensor, the display module, the camera) every pin and where it goes (MCU pin or 5V / 3V3 / GND / VBAT rail), the power budget per rail, issues found (pin conflicts, missing pins, 5 V signals into 3.3 V pins, duplicate I²C addresses, ESP32 strapping / ADC2 pins) and a ready-to-use pin file (Arduino pins.h, or pins.py with BCM numbers on the Pi). `board` switches the board first: "esp32" (ESP32 DevKit V1), "uno" (Arduino Uno R3) or "raspberrypi" (Raspberry Pi 4/5 40-pin header; analog sensors go through an ADS1115 on I²C, the camera on the CSI ribbon).

Parametersboard

get_bom

Get parts list

Bill of materials for building the fitted rover body for real (the parts tab on the Wiring page): the board, motor driver, every enabled sensor, display, camera and head servo, plus the parts the circuit needs around them (chassis, battery, buck converter, voltage-divider resistors, level shifter …), each with quantity, approximate price in THB and shop links when known, and a plain-text list to copy. `board` switches the board first: "esp32", "uno" or "raspberrypi".

Parametersboard

get_firmware

Get firmware

A complete program for the real robot, generated from the fitted body and the wiring pin map: an Arduino sketch (rover_firmware.ino) for "esp32" / "uno", or a Python script (rover.py) for "raspberrypi". It drives the TB6612FNG wheels, reads every enabled sensor, turns the head servo (with light tracking), draws faces on the display and runs the same auto emotions / rule-based autopilot as the simulator. Text commands over Serial 115200 / stdin: READ, F|B|L|R <s>, AUTO 0|1, POWER <%>, HEAD <deg>, TRACK 0|1, EMO <emotion> [text], SAY <text>, STOP. Returns { name, text }. `board` switches the board first.

Parametersboard

list_world

List world layout

World size, light position, all obstacles (id, kind, shape, position, size, color) and colored floor tapes.

Takes no parameters.

add_obstacle

Add obstacle

Place an object in the world to test sensors, collisions or the camera. Kinds: cylinder / ball (circle, radius r), box / wall (rectangle w × h, rotated by angle°). Unspecified size and color use the kind's defaults. Refused if it would overlap the rover.

Parametershrwxrequiredyrequiredkindrequiredanglecolor

remove_obstacle

Remove obstacle

Remove an obstacle by id (see list_world).

Parametersidrequired

add_color_tape

Add colored floor tape

Stick a strip of colored tape on the floor (for the color sensor, line following and the camera). A color sensor over the tape reads its color name.

Parametersxrequiredyrequiredanglecolorrequiredwidthlength

remove_color_tape

Remove colored floor tape

Remove a floor tape by id (see list_world).

Parametersidrequired

Version history

1
  1. v1.1.2LatestOct 31, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts