VEX AIM MCP
Provides optional computer-vision object detection using a local YOLO model, adding a second opinion to the robot's own AI detections for balls, barrels, robots, and AprilTags.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@VEX AIM MCP Drive to the ball, take a photo, then kick it toward the goal"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.

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 | shA 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
Check it installs (the first run downloads it):
uvx --from git+https://github.com/dbbudd/VEX-AIM-MCP vex-aim-mcp --versionFind where
uvxlives. Apps opened from the Dock often can't find it by name, so give them the full path:which uvxAdd the server to your app. Most apps take an entry like this, with
/Users/you/.local/bin/uvxreplaced by whatwhich uvxprinted and192.168.1.50by 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 |
|
Claude Code, the |
|
Claude Desktop | Settings → Developer → Edit Config |
OpenAI Codex (app, command line, IDE) |
|
VS Code with GitHub Copilot |
|
Cursor |
|
Gemini CLI |
|
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 ofgit+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,uvxkeeps the version it first downloaded; add--refreshto 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.

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 arenaThis 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 |
|
| The robot's address (the default is its own hotspot) |
|
| Cap on driving and turning speed (100% is 200 mm/s or 180°/s) |
|
| Longest single move |
|
| Let go of the robot after this many idle minutes (0 = never) |
|
| The control panel's port |
|
| YOLO weights (downloaded on first use) |
| see below | Where the panel saves its set-up |
|
| HTTP mode: the port |
| 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--versioncommand 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
The robot's protocol follows VEX's AIM WebSocket library and documentation.
Ideas from earlier projects: flashzdw/VEX-AIM-MCP, touretzkyds/vex-aim-tools and robotmcp/ros-mcp-server.
This project isn't affiliated with or endorsed by VEX Robotics. VEX and VEX AIM are trademarks of Innovation First, Inc.
License
Available Tools
40 toolsadd_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?ARead-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 objectADestructive
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 objectsARead-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 arenaADestructive
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 objectADestructive
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 ballADestructive
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 spotADestructive
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 goalBDestructive
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.
kickKickADestructive
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 cameraARead-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.
moveMoveADestructive
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 spotADestructive
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 statusARead-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 surroundingsADestructive
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 goalADestructive
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 goalADestructive
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.
stopStopADestructive
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 listARead-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 kickADestructive
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 speedBDestructive
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.
turnTurnADestructive
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 headingADestructive
Spin on the spot to face an absolute heading.
Inputs: heading_deg (required), speed_percent.
wait_forWait for something to happenARead-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
Scored across 40 tools
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.
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.
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.
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
Related MCP Connectors
Turns a phone into a camera+Bluetooth remote so AI assistants can see and control any PC.
Connect Claude, Cursor, Codex, and other AI tools to your robotics mission data.
- DNS-DATA RemoteDeprecatedcom.dns-data
Connect AI assistants to authorized Windows and macOS devices for files, commands, and management.
Connect AI assistants to your approved computer for files, commands, browser and desktop tasks.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables 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.6GPL 3.0
- AlicenseNot gradedqualityDmaintenanceEnables 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
- AlicenseNot gradedqualityDmaintenanceEnables 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.87Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables 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