Skip to main content
Glama

VEX AIM MCP

Let an AI assistant drive a VEX AIM coding robot. Ask in plain English, and the assistant drives it over Wi-Fi, sees through its camera, uses its lights, screen, speaker and kicker, and plays soccer. A live control panel in your browser shows everything the robot sees and lets you drive, set up teams and run matches.

It works with apps that support MCP, the open Model Context Protocol: Claude, ChatGPT, OpenAI Codex, GitHub Copilot in VS Code, Cursor, Gemini CLI and others.

The control panel: live camera with the robot's AI overlaid, the Explore/Teams/Strategy tabs and the map

Your AI app ──MCP──▶ vex-aim-mcp ──Wi-Fi──▶ VEX AIM robot
                         └── control panel at http://127.0.0.1:8765 (for you)

What it can do

  • See: photos with a bearing ruler, the robot's own AI detections (balls, barrels, robots, AprilTags), and an optional second opinion from a YOLO model on your computer.

  • Move: drive in any direction, turn, find and approach objects, kick. Moves are bounded and speed-capped, and motion starts locked.

  • Play: fetch the ball, score between two barrels, pass, guard the goal, and advise whether to pass, shoot or dribble, from measured kick distances and driving speed.

  • Express: a player card on the robot's screen (name, team colour and a face that celebrates or sulks), lights, sounds, notes and speech.

  • Control panel: the live camera with AI overlays, a map of everything seen, driving with a game controller, keyboard or on-screen pad, matches (autonomous, then driver), teams of several robots, and Wi-Fi set-up.

  • Simulators: try all of it without a robot.

Related MCP server: Robot Navigation MCP Server

What you need

  • A VEX AIM robot on Wi-Fi (see Set up the robot).

  • An AI app that supports MCP (see Install).

  • uv, which installs and runs the server. In Terminal:

    curl -LsSf https://astral.sh/uv/install.sh | sh
  • A Mac is best. The Wi-Fi tools and speech use macOS. The rest should work on Windows and Linux, but isn't tested there.

Install

  1. Check it installs (the first run downloads it):

    uvx --from git+https://github.com/dbbudd/VEX-AIM-MCP vex-aim-mcp --version
  2. Find where uvx lives. Apps opened from the Dock often can't find it by name, so give them the full path:

    which uvx
  3. Add the server to your app. Most apps take an entry like this, with /Users/you/.local/bin/uvx replaced by what which uvx printed and 192.168.1.50 by your robot's address:

    {
      "mcpServers": {
        "vex-aim": {
          "command": "/Users/you/.local/bin/uvx",
          "args": ["--from", "git+https://github.com/dbbudd/VEX-AIM-MCP", "vex-aim-mcp"],
          "env": { "AIM_HOST": "192.168.1.50" }
        }
      }
    }

Where that goes depends on the app:

App

Where

Claude Code, in the Claude desktop app's Code tab

.mcp.json in your project folder

Claude Code, the claude command

claude mcp add …

Claude Desktop

Settings → Developer → Edit Config

OpenAI Codex (app, command line, IDE)

~/.codex/config.toml

VS Code with GitHub Copilot

.vscode/mcp.json

Cursor

~/.cursor/mcp.json

Gemini CLI

~/.gemini/settings.json

ChatGPT

HTTP mode, through a tunnel

docs/clients.md has the exact steps for each, including ChatGPT, which runs in the cloud and so reaches the server through a tunnel.

Options:

  • YOLO (a bigger object detector, run on your computer; a large download, as it includes PyTorch): use "vex-aim-mcp[yolo] @ git+https://github.com/dbbudd/VEX-AIM-MCP" in place of git+https://github.com/dbbudd/VEX-AIM-MCP.

  • A fixed version: add a release tag, e.g. git+https://github.com/dbbudd/VEX-AIM-MCP@v0.1.0. Without one, uvx keeps the version it first downloaded; add --refresh to the arguments once to update.

Set up the robot

The robot has to join the same Wi-Fi network as your computer (Station mode). AIM robots only join 2.4 GHz networks, with a name and password of up to 20 characters each, and no sign-in pages.

  • Already on your Wi-Fi? On the robot, open Settings → Radio → Station to see its address, and put it in AIM_HOST.

  • New robot, or a new network? Put the robot on its own hotspot (Settings → Radio → Access Point on its screen), and join its AIM-… Wi-Fi on your Mac (the password is on the robot's screen). Then open the control panel's Wi-Fi menu, pick your network and press Switch…. While the Mac is on the robot's hotspot it has no internet, so your AI assistant can't reply, but the panel keeps working.

The Wi-Fi networks menu, with nearby networks found by a scan

docs/setup.md has the details: classrooms with many robots, competitions at other venues, and what macOS asks permission for.

First steps

Try asking your assistant:

  • "Connect to the robot and tell me its battery."

  • "Open the control panel."

  • "What can you see?"

  • "Put my name on the robot's screen in blue."

  • "The robot is on the floor with space around it. Scan around and tell me what's there."

  • "Fetch the ball and score in the blue goal."

The assistant only moves the robot after you tell it the robot is on the floor with space around it.

The control panel

Ask your assistant to open it, or run it on its own with vex-aim-panel --host 192.168.1.50. It shows:

  • the camera, with or without the robot's AI drawn over it;

  • a map;

  • three tabs, Explore, Teams and Strategy;

  • along the top: the robot's Wi-Fi, battery, team, match timer, motion lock and a STOP button.

Its buttons and log use your app's name, e.g. Point out to ChatGPT. docs/control-panel.md walks through it.

The assistant's tools

About forty tools, in groups: see, real time, move, games, plays, explore and strategy, teams, express and connection. docs/tools.md lists them, with the conventions the assistant follows (directions, bearings, headings) and how it uses what you do in the panel.

Safety

  • Motion starts locked. The assistant unlocks it only after you confirm the robot is on the floor with space around it. You can also unlock it in the panel.

  • Caps: speed is capped at 60% and a single move at 1 m, by default.

  • Every move is bounded by a distance or angle and a timeout, and stops on a bump. This matters, because the robot doesn't reliably stop by itself if Wi-Fi drops mid-move (VEX issue #20).

  • STOP in the panel, or asking the assistant to stop, ends any move, play or experiment.

  • Driver mode in the panel stops the assistant's movement tools while you drive.

  • No password: anyone on the same Wi-Fi can control an AIM robot. Use a network just for the robots where you can. In HTTP mode, anyone with the server's secret address can too: keep it private.

  • One program at a time: ask the assistant to disconnect before using VEXcode or another program with the robot.

Try it without a robot

uvx --from git+https://github.com/dbbudd/VEX-AIM-MCP vex-aim-sim --world arena

This simulates a robot on a 1.2 × 2.4 m soccer pitch, with goals, AprilTags and a ball that rolls and bounces. Set AIM_HOST to 127.0.0.1:8899 to use it. --world goals is a simpler fixed scene, and vex-aim-arena --robots 4 (run the same way) puts four robots on one pitch.

Settings

Set these in the env part of your app's configuration:

Variable

Default

Meaning

AIM_HOST

192.168.4.1

The robot's address (the default is its own hotspot)

AIM_MAX_SPEED_PERCENT

60

Cap on driving and turning speed (100% is 200 mm/s or 180°/s)

AIM_MAX_MOVE_MM

1000

Longest single move

AIM_IDLE_DISCONNECT_MIN

10

Let go of the robot after this many idle minutes (0 = never)

AIM_LIVE_VIEW_PORT

8765

The control panel's port

AIM_YOLO_MODEL

yolo26n.pt

YOLO weights (downloaded on first use)

AIM_PANEL_SETUP

see below

Where the panel saves its set-up

AIM_HTTP_PORT

8000

HTTP mode: the port

AIM_HTTP_SECRET

random

HTTP mode: the secret part of the address

The panel saves measurements, AprilTag meanings, the field, the team list and remembered network names (never passwords) in ~/Library/Application Support/VEX AIM Panel/ on a Mac. Remembered Wi-Fi passwords are kept in the Mac's keychain.

Troubleshooting

  • "Host is down" or it can't connect: the robot sleeps after a few idle minutes. Tap its screen to wake it, and check it's on the same network as your computer.

  • Its address changed: the router gives it one, and it can change. Check Settings → Radio → Station on the robot, or use 🔍 Find robots in the panel's Teams tab.

  • The tools don't appear in your app: check the path to uvx, then run the --version command above to see any error.

  • Another program is using the robot: only one program can drive it. Ask the assistant to disconnect first.

Contributing

AGENTS.md explains the code, how to run the tests, and the rules for changing it. It's written for people and for AI coding agents (Codex, Copilot, Cursor, Claude Code and others) alike. CLAUDE.md adds notes for Claude Code.

Credits

This project isn't affiliated with or endorsed by VEX Robotics. VEX and VEX AIM are trademarks of Innovation First, Inc.

License

MIT

Available Tools

40 tools
add_playerAdd a robot to the team listA

Put another AIM robot on the team list, by its player name and IP address (the robot shows its IP on its screen under Settings → Radio; the control panel's Network menu can find robots too).

Inputs: host (required), name (required), team.

adviseWhat should the robot do?A
Read-only

Strategy advice without moving: fetch, shoot, pass or dribble, and why, with the numbers behind it (distance to the goal, how far each measured kick rolls, how long the ball takes to roll there versus driving, whether the path is clear, where to dribble to). holding defaults to what the onboard AI sees, which often misses a held ball, so confirm with look(). teammates_mm: [[x, y], …] of teammates to pass to.

Inputs: holding, teammates_mm, speed_percent.

approach_objectApproach an objectA
Destructive

Drive up to an object the onboard AI can see, re-aiming before every short step, until it's in the kicker (whose magnet then holds balls and barrels). Give a target kind or the label of an object or taught colour. If it isn't in view, use face_object or scan_surroundings first. The onboard AI often loses a ball in the last ~10 cm, so the robot finishes that part on an estimate. Unless the AI confirms the object is in the kicker, the result includes a photo: check it before kicking.

Inputs: label, target, speed_percent, max_distance_mm.

connect_robotConnect to the robotA

Connect, or reconnect, to the robot. Other tools connect automatically, so this is only needed to switch robots or to recover after the connection dropped.

Inputs: host.

control_panelControl panelA

Open (or close) the control panel: a web page on this computer with the robot's live camera and detection boxes, team buttons, object labelling, colour teaching, a STOP button, a top-down map and a log of what the person, you and the robot do. If your app has a built-in browser, show the URL there; otherwise give the person the link, or set open_browser. What the person chooses there is shared with you: see robot_status().panel and wait_for("panel").

Inputs: enabled, open_browser, yolo_detection.

detect_objectsDetect objectsA
Read-only

List what the robot can see, with bearings, without fetching a photo.

Inputs: source, apriltags.

disconnect_robotDisconnect from the robotA

Release the robot so VEXcode or a Python script can use it. Tools reconnect automatically.

Inputs: no named parameters.

enable_motionEnable motionA

Unlock move, turn, turn_to_heading, face_object and kick for this connection. Motion starts locked every time the robot connects, because it may be sitting on a table. Ask the user first; never call this on your own initiative.

Inputs: confirmed_clear_floor (required).

explore_arenaExplore the arenaA
Destructive

Map the arena by driving around it. It scans a full circle, then visits spots across the field chosen in the control panel (a grid 30 cm in from the edges), scanning at each. It drives around obstacles and anything already on the map (skipping a spot only if there's no way round), and stops on a bump. Without a field it only scans where it is. Takes a few minutes; the person can press STOP in the panel. Afterwards, robot_status().panel.map lists what it found.

Inputs: speed_percent.

face_objectFace an objectA
Destructive

Find an object with the robot's onboard AI vision and turn to face it. Works for what the onboard AI knows (sports balls, blue and orange barrels, other AIM robots, AprilTags), labelled objects, and colours taught with teach_colour or the control panel.

Inputs: label, search, target, speed_percent.

fetch_ballFetch the ballA
Destructive

Get the ball into the kicker by itself: find it (in view, on the control panel's map, or by turning to look around), drive to just short of it around obstacles and anything on the map, then creep up on it with the camera. Unlike approach_object, the ball needn't be in view. The onboard AI often loses the ball in the last ~10 cm; if the result says it can't confirm the ball is held, check with look(). STOP ends it.

Inputs: speed_percent.

go_toGo to a spotA
Destructive

Drive to a spot (field mm when a field is chosen in the control panel, otherwise from where the robot connected) around obstacles and anything on the map, finishing within a few cm; optionally face heading_deg (0 = up the field) at the end. Refuses spots off the field or within 10 cm of its walls.

Inputs: x_mm (required), y_mm (required), heading_deg, speed_percent.

guard_goalGuard the goalB
Destructive

Play goalkeeper for a while: stand 25 cm in front of the goal the other team scores in (each team scores in its own colour's goal, so the blue team guards the orange goal), facing up the field, and slide sideways to stay between the ball and the goal's centre, never past a post. Ends early if it catches the ball.

Inputs: goal, seconds, speed_percent.

kickKickA
Destructive

Fire the kicker at whatever it's holding ("soft" gently pushes or places it), then back away. Aim at open space first, since balls bounce off things, and make sure no one is in the way. Use look() to check something is in the kicker.

Inputs: strength, back_away_mm.

lookLook through the cameraA
Read-only

Take a photo with the robot's front camera and see it yourself. Objects the robot's onboard AI recognises (sports balls, blue/orange barrels, AIM robots, AprilTags) are listed with their bearing. For anything else, read its bearing off the ruler along the top of the photo.

Inputs: overlay, yolo_detection.

moveMoveA
Destructive

Drive a set distance in any direction, then stop. Waits until it has finished. Stops early if the robot bumps into something.

Inputs: distance_mm (required), direction_deg, speed_percent.

pass_ballPass the ball to a spotA
Destructive

Kick the ball to a spot on the field (mm, as in robot_status().panel.map: on a field (0, 0) is its centre, x right, y up the field) with the gentlest measured kick that gets there, fetching it first if needed and dribbling closer if no measured kick reaches. Refuses if something on the map is in the ball's way.

Inputs: x_mm (required), y_mm (required), speed_percent.

play_notesPlay notesA

Play a tune on the robot's speaker and wait for it to finish (30 seconds at most).

Inputs: notes (required), volume.

play_soundPlay soundC

Play one of the robot's built-in sounds.

Inputs: sound, volume.

reactReact on the screenA

Show the player card on the robot's screen: a small face with this expression, the player's name and team colour (from the control panel), and its lights in the team colour. Use it in games: excited after a goal, sad after a miss, wink after a good pass, surprised after a bump. It goes back to the resting face after a few seconds.

Inputs: hold_s, caption, expression.

record_kick_distanceRecord a kick distanceB

Save a kick distance someone measured (e.g. after test_kick lost sight of the ball).

Inputs: strength (required), distance_cm (required).

reset_positionReset positionA

Make the current heading 0° and the current position (0, 0).

Inputs: no named parameters.

robot_statusRobot statusA
Read-only

Battery, heading, position, tilt, whether it's moving or playing sound, what the kicker seems to hold, and what the robot's onboard AI vision sees right now. Connects if needed. "holding" relies on the onboard AI, which often misses a held ball in dim light; use look().

Inputs: no named parameters.

saySayB

Speak out loud through the robot's speaker, using this Mac's text-to-speech.

Inputs: text (required), voice, volume.

scan_surroundingsScan surroundingsA
Destructive

Turn a full circle on the spot, noting the heading of every object the onboard AI recognises (sports balls, barrels, AIM robots, and AprilTags once switched on) and where each goal (a pair of same-coloured barrels) is centred, then face the starting direction again. Use the headings with turn_to_heading, approach_object or shoot_at_goal.

Inputs: speed_percent.

score_goalScore a goalA
Destructive

Score by itself from anywhere: fetch the ball if the camera sees it elsewhere, find the goal (two barrels of the team's colour, from the map or by looking around), dribble closer if no measured kick would score from here, line up between the posts, kick with the gentlest measured strength that scores, and back away. Only uses kick strengths measured with test_kick; with none, it dribbles to 30 cm and kicks soft. shoot_at_goal instead kicks from where the robot stands. Make sure nobody is near the goal.

Inputs: goal, speed_percent.

select_playerChoose which robot to controlA

Make another player's robot the one the tools (and the control panel) act on. Motion starts locked on each robot: ask the person to confirm that robot is on the floor before enable_motion.

Inputs: name (required).

set_lightsSet lightsB

Set the colour of the robot's ring of six lights.

Inputs: led, color.

set_teamSet teamA

Choose the robot's team at the start of a goal game. Like VEX alliances, a team scores in the goal of its own colour: between the two barrels of that colour. (The person can also pick the team in the control panel.)

Inputs: colour (required).

shoot_at_goalShoot at goalA
Destructive

Score with the ball the robot is holding: find the goal (two barrels of one colour), turn so the ball passes between them, kick, and back away. Get the ball into the kicker first (approach_object) and confirm with look(). Returns a photo taken after the kick.

Inputs: goal, strength, speed_percent.

show_emojiShow emojiB

Show one of the robot's animated faces on its screen ("none" hides it).

Inputs: look, emoji.

show_textShow textB

Write text on the robot's 240x240 screen, replacing whatever was there. Long lines wrap.

Inputs: text (required), size, color, background.

stopStopA
Destructive

Stop all motion immediately, including an exploration or experiment in progress. Always allowed, even while motion is locked.

Inputs: no named parameters.

teach_colourTeach a colourA

Teach the robot's onboard AI to detect a colour, sampled from part of the current camera view (for example a cup you spotted in a look() photo). The robot then tracks it by itself, many times a second, and its name works as a label in approach_object, face_object and wait_for. This is the colour signature from VEXcode's AI Vision Utility; the robot holds 7.

Inputs: label (required), box_xyxy (required), width_mm, tolerance.

team_listTeam listA
Read-only

The robots on the team list: each player's name, team, robot address, whether it's connected, battery, and where it is on the field. "selected" is the one the tools and the control panel act on; use select_player to switch. Also which other robots the selected one can see, and whether each is a teammate, an opponent or unknown.

Inputs: no named parameters.

test_kickTest a kickA
Destructive

Experiment: kick the ball in the kicker straight ahead and watch it roll, to measure how far this strength sends it. Point the robot at open floor first. The distance is saved when the ball stops in view. If it rolls out of the camera's range, ask the person to measure where it stopped and call record_kick_distance. Results build up in robot_status().panel.abilities.

Inputs: strength, ball_in_kicker.

test_speedTest driving speedB
Destructive

Experiment: drive straight ahead a measured distance and time it, to find the robot's top speed at this speed setting and how quickly it gets up to speed. Needs that much clear floor ahead.

Inputs: distance_mm, speed_percent, return_to_start.

turnTurnA
Destructive

Spin on the spot by an angle, then stop. To face something seen in look(), turn by its bearing.

Inputs: degrees (required), speed_percent.

turn_to_headingTurn to headingA
Destructive

Spin on the spot to face an absolute heading.

Inputs: heading_deg (required), speed_percent.

wait_forWait for something to happenA
Read-only

Watch the robot about 10 times a second and return the moment something happens, instead of checking over and over. Returns what happened (with bearings), or that it timed out.

Inputs: event (required), label, target, timeout_s.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 40 tools

    Disambiguation4/5

    The set has real clusters of overlap: kick/shoot_at_goal/score_goal/pass_ball/test_kick all concern kicking, and approach_object/fetch_ball both acquire balls, while look/detect_objects/robot_status all report vision. However, descriptions actively cross-reference each other (e.g. 'unlike approach_object', 'shoot_at_goal instead kicks from where the robot stands'), so an agent can usually choose correctly.

    Naming Consistency4/5

    Nearly everything is snake_case verb-first or verb_noun (move, turn, face_object, go_to, kick, pass_ball, set_team, teach_colour, add_player), which is predictable and readable. A few noun-first outliers (robot_status, team_list, control_panel) and bare imperatives (look, say, react, advise) deviate slightly without breaking comprehension.

    Tool Count3/5

    40 tools is heavy and exceeds the comfortable range, though the domain is genuinely broad (motion, vision, audio, display, team play, experiments, strategy). Several functions could plausibly be consolidated (test_kick/test_speed/record_kick_distance, the multiple display tools), so it sits at the crowded end of reasonable.

    Completeness5/5

    The surface covers the full lifecycle: connection, motion and aiming, vision, ball handling, scoring, passing, goalkeeping, mapping, experiments, team roster management, TTS/sound/lights/screen, plus strategy advice and event waiting. No obvious dead ends or missing operations are apparent for a robot-control agent.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to control Universal Robots through real-time connection management, status monitoring, and precise joint/linear motion control. Provides safe robot operation with built-in collision detection and simulation mode for development without physical hardware.
      6
      GPL 3.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables robot navigation control and monitoring through natural language. Provides tools for robot positioning, navigation to coordinates, device status monitoring, task management, and emergency controls.
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables LLM-based AI agents to control SO-ARM100 and SO-101 robots through natural language commands and camera feedback. It supports various transport protocols and provides tools for both autonomous robotic movement and manual keyboard operation.
      87
      Apache 2.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI agents to control SO-ARM100 and LeKiwi robots using natural language instructions and integrated camera vision. It supports multiple communication transports and includes a CLI agent compatible with Claude, Gemini, and GPT models.
      Apache 2.0