servomotor-mcp
This server lets you control Gearotons M17 servomotors through natural language via MCP, from discovery to motion and firmware-level commands.
Discover and inspect serial ports, including USB adapter metadata.
Connect/disconnect to a serial RS-485 bus and auto-detect motors on it.
List detected motors with live position, voltage, temperature, and status.
Move motors absolutely or relatively in degrees, with configurable speed, and wait for completion.
Stop one or all motors and get detailed motor status with decoded errors.
Run choreographed multi-step sequences, including raw firmware commands.
Access the full firmware command catalog as tools: enable/disable MOSFETs, go-to-position, velocity/acceleration moves, homing, zero position, PID tuning, safety limits, calibration, vibration, identify, system reset, firmware upgrade, diagnostics, and more.
Works without hardware via a mock backend, or with real hardware over a USB-to-RS-485 adapter.
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., "@servomotor-mcp Home all motors"
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.
servomotor-mcp
Drive open-source Gearotons M17 servomotors from natural language.
An MCP server that exposes the M17 — a NEMA-17 integrated, closed-loop, RS-485 servomotor — to Claude Desktop, Claude Code, or any MCP client. Control real motors by just asking:
"Find my motors and rotate the one on the bench two full turns, slowly."
Nothing is hardcoded: the server discovers your serial ports, auto-detects the
motors on the bus (the firmware's "Detect devices" command), and exposes the entire
firmware command set — every command in the servomotor library's catalog becomes an
MCP tool automatically (48 commands as of library 0.10.0), plus a few high-level tools
for everyday moves. It ships with a mock backend, so you can try the whole thing with
no hardware.
The first servomotor with an official MCP server. Open hardware, open firmware, open software — and now an open, AI-native control interface.
Quickstart (no hardware, ~2 minutes)
# Run the server directly with uv (recommended). This runs the SIMULATOR —
# for real motors see "Drive real motors" below:
uvx --from servomotor-mcp servomotor-mcp
# or install it:
pip install servomotor-mcp
servomotor-mcpThen add it to Claude Desktop — copy the block from
examples/claude_desktop_config.json into your
claude_desktop_config.json, restart Claude Desktop, and ask:
"What serial ports do you see? Connect and find my motors."
See examples/demo_prompts.md for a scripted demo.
Related MCP server: reachy-mini-mcp
Drive real motors
Plug an M17 (or a daisy-chain of them) into a USB↔RS-485 adapter and install the
[serial] extra — that's it, no configuration:
pip install 'servomotor-mcp[serial]' # pulls in the Gearotons servomotor library
servomotor-mcpWith the servomotor library installed the server uses the real serial backend
automatically. Without it, the server runs its simulator and says so: list_serial_ports
and connect return a notice field, and a warning goes to stderr. With uv, use
uvx --from 'servomotor-mcp[serial]' servomotor-mcp. In a session, the model then:
list_serial_ports— enumerates the machine's ports (macOS/dev/cu.*, WindowsCOM*, Linux/dev/ttyUSB*), flagging USB serial adapters;connect— opens the port you (or it) picked, at 230400 baud;auto-detects every motor on that bus (unique ID + alias) — no address maps to write;
drives them. Tell it in plain English which adapter to use if you have several.
Tools
High-level (discovery + everyday motion):
Tool | What it does |
| Enumerate serial ports with USB metadata (call first). |
| Open a port and auto-detect the motors on that bus. |
| Re-scan the bus (reboots the motors on it). |
| Detected motors with live position/voltage/temperature/status. |
| Absolute / relative moves in degrees; waits for completion. |
| Emergency-stop one or all motors. |
| One motor's snapshot, fatal errors decoded to plain English. |
| Choreographed steps ("draw a square"), incl. raw command steps. |
Plus one tool per firmware command, generated from the library's command catalog:
enable_mosfets, go_to_position, move_with_velocity, move_with_acceleration,
multimove, homing, zero_position, get_position, get_temperature,
set_device_alias, set_pid_constants, system_reset, vibrate, ping, … — anything
the motor can do, the model can do. Motors are addressed by their alias number, their
16-hex-digit unique ID, or "all" (broadcast). Values are in friendly units (degrees,
seconds, degrees/s, volts, °C); the server converts to firmware units.
How it works
natural language → Claude → MCP tool calls → this server → RS-485 → M17 motorsThe server is a thin layer over the Gearotons servomotor Python library. The library is
data-driven — motor_commands.json defines every firmware command — and the server turns
that same catalog into MCP tools, so new library commands appear automatically. Tool calls
are forwarded straight to the hardware — no software clamping; full multi-turn travel, any
speed. The motor's own firmware protections (over-current / over-voltage /
over-temperature) still apply. The same tools run against the mock backend
(GEAROTONS_MOTOR_BACKEND=mock) for development and CI.
Environment variables (all optional):
GEAROTONS_MOTOR_BACKEND—auto(default: serial when theservomotorlibrary is installed, else mock),serial, ormock.GEAROTONS_SERIAL_PORT— default port forconnectwhen the model doesn't pass one.GEAROTONS_DEFAULT_SPEED_DPS— default speed formove_to/move_relative(180).
Develop / test
pip install -e '.[dev]'
GEAROTONS_MOTOR_BACKEND=mock pytest # catalog + mock-bus + server-tool testshardware_tests/ contains scripts that exercise the real serial path end to end
(port sweep, full command suite, stdio MCP session) against a bench motor.
Status
✅ Full firmware command surface (48 commands), serial-port discovery, bus auto-detection — verified on a physical M17 (fw 0.15.3.0) over a real stdio MCP session and via
uvx, on all four test adapters (motor found only where it truly is).✅ Mock backend + 39 unit tests, no hardware needed.
✅ Cross-platform port handling (macOS / Windows / Linux) via pyserial enumeration.
License
MIT. Hardware, firmware, and software for the M17 are open-source — see github.com/tomrodinger/servomotor.
Available Tools
57 toolscapture_hall_sensor_dataB
Start sending hall sensor data (work in progress; don't send this command) [Firmware command "Capture hall sensor data", group: Other.] Parameters:
captureType: Indicates the type of data to capture. Currently 1 to 3 are valid.
nPointsToRead: Number of points to read back from the device
channelsToCaptureBitmask: Channels to capture bitmask. The first three bits are valid, which will turn on (0) or turn off (0) that hall sensor channel
timeStepsPerSample: Aquire a sample every this number of time steps. Time steps happen at the update frequency, which can be read with the Get product specs command
nSamplesToSum: Number of samples to sum together to make one point to transmit back
divisionFactor: Division factor to apply to the sum of the samples to scale it down before transmitting it so that it fits into the returned data type, which is a 16-bit number per each hall sensor Returns:
data: The data of the hall sensors after suming and averaging motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), captureType (required), nPointsToRead (required), nSamplesToSum (required), +3 more.
connectA
Open a serial port (230400 baud) and auto-detect the motors on that RS-485 bus.
port is a device name from list_serial_ports (e.g. "/dev/cu.usbserial-210",
"COM3", "/dev/ttyUSB0"). If omitted: uses $GEAROTONS_SERIAL_PORT if set, else the
single USB serial adapter if there is exactly one, else asks you to choose.
Detection reboots every motor on the bus (positions re-zero at the current shaft location, MOSFETs turn off) and takes ~3 s per attempt. If no motors are found, the adapter may be the wrong one — try another port. Connecting to a different port replaces the previous connection, but only once the new port opens successfully — a failed attempt leaves the old connection intact.
Inputs: port, detect, detect_attempts.
control_hall_sensor_statisticsA
Turn on or off the gathering of statistics for the hall sensors and reset the statistics [Firmware command "Control hall sensor statistics", group: Status & Monitoring.] Parameters:
control: 0 = turn off statistics gathering, 1 = reset statistics and turn on gathering. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), control (required).
crc32_controlA
Enable or disable CRC32 checking for commands [Firmware command "CRC32 control", group: Configuration.] NOTE: Changes protocol framing for ALL subsequent commands; only disable CRC32 if you know why. Parameters:
enableCrc32: Control value (1 to enable, 0 to disable CRC32 checking) Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), enableCrc32 (required).
detect_devicesA
Re-scan the connected bus for motors using the firmware's "Detect devices" command.
Use after plugging in / powering on motors, or when a motor seems missing. Runs
attempts merged detection rounds (more rounds = more reliable with many motors
on one bus). CAUTION: this reboots every motor on the bus — positions re-zero at the
current shaft location and MOSFETs turn off.
Inputs: attempts.
disable_mosfetsA
Disables the MOSFETS (note that MOSFETs are disabled after initial power on). [Firmware command "Disable MOSFETs", group: Basic Control.] Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
disconnectA
Close the serial port (e.g. to free it for another program, or before unplugging).
Inputs: no named parameters.
emergency_stopA
Emergency stop (stop all movement, disable MOSFETS, clear the queue) [Firmware command "Emergency stop", group: Basic Control.] Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
enable_mosfetsA
Enables the MOSFETS. [Firmware command "Enable MOSFETs", group: Basic Control.] Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
firmware_upgradeA
This command will upgrade the flash memory of the servo motor. Before issuing a firmware upgrade command, you must do some calculations as shown in the examples. [Firmware command "Firmware upgrade", group: Device Management.] NOTE: Flashes a firmware page (hex-encoded bytes). Wrong data can brick the motor. Only use with a valid firmware file and the user's explicit go-ahead. Parameters:
firmwarePage: The data to upgrade one page of flash memory. Contents includes the product model code (8 bytes), firmware compatibility code (1 byte), page number (1 byte), and the page data itself (2048 bytes). Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), firmwarePage (required).
get_communication_statisticsA
Get and optionally reset the CRC32 error counter [Firmware command "Get communication statistics", group: Status & Monitoring.] Parameters:
resetCounter: Reset flag (1 to reset the counter after reading, 0 to just read) Returns:
crc32ErrorCount: Number of CRC32 errors detected
packetDecodeErrorCount: Number of packet decode errors detected
firstBitErrorCount: Number of times that the first bit in the first byte of a packet was not 1 as expected
framingErrorCount: Number of framing errors detected during reception from the RS485 interface
overrunErrorCount: Number of overrun errors detected during reception from the RS485 interface
noiseErrorCount: Number of noise errors detected during reception from the RS485 interface motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), resetCounter (required).
get_comprehensive_positionA
Get the desired motor position, hall sensor position, and external encoder position all in one shot [Firmware command "Get comprehensive position", group: Status & Monitoring.] Returns:
commandedPosition: The commanded position (which may differ from actual)
hallSensorPosition: The hall sensor position (or you could say the actual measured position)
externalEncoderPosition: The external encoder position. This needs special hardware attached to the motor to work motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_current_timeA
Gets the current absolute time [Firmware command "Get current time", group: Status & Monitoring.] Returns:
currentTime: The current absolute time in microseconds motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_debug_valuesB
Get debug values including motor control parameters, profiler times, hall sensor data, and other diagnostic information. [Firmware command "Get debug values", group: Status & Monitoring.] Returns:
maxAcceleration: Maximum acceleration setting
maxVelocity: Maximum velocity setting
currentVelocity: Current velocity
measuredVelocity: Measured velocity
nTimeSteps: Number of time steps left in the current move
debugValue1: Debug value 1
debugValue2: Debug value 2
debugValue3: Debug value 3
debugValue4: Debug value 4
allMotorControlCalculationsProfilerTime: All motor control calculations profiler time
allMotorControlCalculationsProfilerMaxTime: All motor control calculations profiler maximum time
getSensorPositionProfilerTime: Get sensor position profiler time
getSensorPositionProfilerMaxTime: Get sensor position profiler maximum time
computeVelocityProfilerTime: Compute velocity profiler time
computeVelocityProfilerMaxTime: Compute velocity profiler maximum time
motorMovementCalculationsProfilerTime: Motor movement calculations profiler time
motorMovementCalculationsProfilerMaxTime: Motor movement calculations profiler maximum time
motorPhaseCalculationsProfilerTime: Motor phase calculations profiler time
motorPhaseCalculationsProfilerMaxTime: Motor phase calculations profiler maximum time
motorControlLoopPeriodProfilerTime: Motor control loop period profiler time
motorControlLoopPeriodProfilerMaxTime: Motor control loop period profiler maximum time
hallSensor1Voltage: Hall sensor 1 voltage
hallSensor2Voltage: Hall sensor 2 voltage
hallSensor3Voltage: Hall sensor 3 voltage
commutationPositionOffset: Commutation position offset
motorPhasesReversed: Motor phases reversed flag
maxHallPositionDelta: Maximum hall position delta
minHallPositionDelta: Minimum hall position delta
averageHallPositionDelta: Average hall position delta
motorPwmVoltage: Motor PWM voltage motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_firmware_versionA
Get the firmware version or the bootloader version depending on what mode we are in. This command also returns the status bits, where the least significan bit teels us if we are currently in the bootloader (=1) or the main firmware (=0) [Firmware command "Get firmware version", group: Device Management.] Returns:
firmwareVersion: The firmware version stored as 4 bytes. The first byte is the development number, then patch version, followed by the minor and major versions.
inBootloader: A flag that tells us if we are in the bootloader (=1) or in the reguslar firmware (=0) motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_hall_sensor_positionB
Get the position as measured by the hall sensors (this should be the actual position of the motor and if everything is ok then it will be about the same as the desired position) [Firmware command "Get hall sensor position", group: Status & Monitoring.] Returns:
hallSensorPosition: The current position as determined by the hall sensors motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_hall_sensor_statisticsA
Read back the statistics gathered from the hall sensors. Useful for checking the hall sensor health and noise in the system. [Firmware command "Get hall sensor statistics", group: Status & Monitoring.] Returns:
maxHall1: The maximum value of hall sensor 1 encoutered since the last statistics reset.
maxHall2: The maximum value of hall sensor 2 encoutered since the last statistics reset.
maxHall3: The maximum value of hall sensor 3 encoutered since the last statistics reset.
minHall1: The minimum value of hall sensor 1 encoutered since the last statistics reset.
minHall2: The minimum value of hall sensor 2 encoutered since the last statistics reset.
minHall3: The minimum value of hall sensor 3 encoutered since the last statistics reset.
sumHall1: The sum of hall sensor 1 values collected since the last statistics reset.
sumHall2: The sum of hall sensor 2 values collected since the last statistics reset.
sumHall3: The sum of hall sensor 3 values collected since the last statistics reset.
measurementCount: The number of times the hall sensors were measured since the last statistics reset. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_max_pid_errorA
Get the minimum and maximum error value ovserved in the PID control loop since the last read. [Firmware command "Get max PID error", group: Status & Monitoring.] Returns:
minPidError: The minimum PID error value.
maxPidError: The maximum PID error value. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_motor_statusA
One motor's live snapshot: position, supply voltage, temperature, decoded status.
Call after moves to CONFIRM completion, or when the user asks "where is it / is it
okay". Includes any fatal error decoded to plain English with suggested fixes
(clear faults with system_reset).
Inputs: motor (required).
get_n_queued_itemsA
Get the number of items currently in the movement queue (if this gets too large, don't queue any more movement commands) [Firmware command "Get n queued items", group: Status & Monitoring.] Returns:
queueSize: The number of items in the movement queue. This command will return between 0 and 32. If less than 32, you can add more items to the queue to continue the movements in order without stopping. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_positionA
Get the current desired position (which may differ a bit from the actual position as measured by the hall sensors) [Firmware command "Get position", group: Status & Monitoring.] Returns:
position: The current desired position motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_product_descriptionB
Get the product description. [Firmware command "Get product description", group: Device Management.] Returns:
productDescription: This is a brief description of the product. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_product_infoC
Get product information [Firmware command "Get product info", group: Device Management.] Returns:
productCode: The product code / model number (when doing a firmware upgrade, this must match between the firmware file and the target device).
firmwareCompatibility: A firmware compatibility code (when doing a firmware upgrade, this must match between the firmware file and the target device).
hardwareVersion: The hardware version stored as 3 bytes. The first byte is the patch version, followed by the minor and major versions.
serialNumber: The serial number.
uniqueId: The unique ID for the product.
reserved: Not currently used. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_product_specsA
Get the update frequency (reciprocal of the time step) [Firmware command "Get product specs", group: Device Management.] Returns:
updateFrequency: Update frequency in Hz. This is how often the motor executes all calculations for hall sensor position, movement, PID loop, safety, etc.
countsPerRotation: Counts per rotation. When commanding the motor or when reading back position, this is the number of counts per one shaft rotation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_statusA
Gets the status of the motor [Firmware command "Get status", group: Status & Monitoring.] Returns:
statusFlags: A series of flags which are 1 bit each
fatalErrorCode: The fatal error code. If 0 then there is no fatal error. Once a fatal error happens, the motor becomes disabled and cannot do much anymore until reset. You can press the reset button on the motor or you can execute the System reset command to get out of the fatal error state. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_supply_voltageA
Get the measured voltage of the power supply. [Firmware command "Get supply voltage", group: Status & Monitoring.] Returns:
supplyVoltage: The voltage. Divide this number by 10 to get the actual voltage in volts. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
get_temperatureA
Get the measured temperature of the motor. [Firmware command "Get temperature", group: Status & Monitoring.] Returns:
temperature: The temperature in degrees celcius. The accuracy is about +/- 3 degrees celcius and is measured at the motor driver PCB. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
go_to_closed_loopB
Go to closed loop position control mode [Firmware command "Go to closed loop", group: Motion Control.] Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
go_to_positionA
Move to this new given position in the amount of time specified. Acceleration and deceleration will be applied to make the move smooth. [Firmware command "Go to position", group: Motion Control.] This QUEUES the move and returns immediately; poll get_n_queued_items for completion, or use move_to/move_relative which wait. Requires enable_mosfets first. Parameters:
position (in degrees): New absolute position value.
duration (in seconds): Time allowed for executing the move. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), duration (required), position (required).
homingA
Homing (or in other words, move until a crash and then stop immediately) [Firmware command "Homing", group: Motion Control.] NOTE: The motor moves until it hits a physical obstruction, then zeroes there. Ensure a hard stop exists within maxDistance. Parameters:
maxDistance (in degrees): The maximum distance to move (if a crash does not occur). This can be positive or negative. the sign determines the direction of movement.
maxDuration (in seconds): The maximum time to allow for homing. Make sure to give enough time for the motor to cover the maximum distance or the motor may move too fast or throw a fatal error. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), maxDistance (required), maxDuration (required).
identifyA
Identify your motor by sending this command. The motor's green LED will flash rapidly for 3 seconds. [Firmware command "Identify", group: Device Management.] Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
list_motorsA
List the detected motors with live position, voltage, temperature, and status.
Call after connect to see what is on the bus, or any time the user asks "what
motors are connected / where are they". If this is empty but hardware is plugged in,
run detect_devices or try another serial port.
Inputs: no named parameters.
list_serial_portsA
List the serial ports on this computer so one can be chosen for connect.
Call this FIRST in any session. USB-RS485 adapters show up with a USB VID:PID and usually a telling description/manufacturer (FTDI, CH340, CP210x...). If several adapters are present, connect to the most likely one and check whether motors are detected; it is cheap to connect to another port and look again. Relay the options to the user in plain English if it is ambiguous.
Inputs: no named parameters.
move_relativeA
Move one motor by a RELATIVE amount in degrees (+/-) and wait for it to finish.
Use for "turn a bit more", "back off 10 degrees", or incremental jogging. Any
magnitude is allowed, including multiple full turns. speed_dps is degrees/second
(default 180). Enables the MOSFETs automatically and returns the settled position.
motor: an alias number (e.g. 88) or a 16-hex-digit unique ID from list_motors.
Inputs: motor (required), degrees (required), speed_dps.
move_toA
Move one motor to an ABSOLUTE angle in degrees and wait for it to finish.
Use when the user names a target position ("go to 90 degrees", "one full turn" =
360). Angles are unbounded multi-turn (720 = two full turns). speed_dps is
degrees/second (default 180). Enables the motor's MOSFETs automatically and returns
the settled position. For very long moves (>30 s) prefer go_to_position plus
get_n_queued_items polling so the tool call doesn't time out.
motor: an alias number (e.g. 88) or a 16-hex-digit unique ID from list_motors.
Inputs: motor (required), degrees (required), speed_dps.
move_with_accelerationA
Rotates the motor with the specified acceleration [Firmware command "Move with acceleration", group: Motion Control.] This QUEUES the move and returns immediately; poll get_n_queued_items for completion, or use move_to/move_relative which wait. Requires enable_mosfets first. Parameters:
acceleration (in degrees_per_second_squared): The acceleration (the unit is microsteps per time step per time step * 2^24).
timeSteps (in seconds): The number of time steps to apply this acceleration. Use command 18 to get the frequency of the time steps. After this many time steps, the acceleration will go to zero and velocity will be maintained. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), timeSteps (required), acceleration (required).
move_with_velocityA
Rotates the motor with the specified velocity. [Firmware command "Move with velocity", group: Motion Control.] This QUEUES the move and returns immediately; poll get_n_queued_items for completion, or use move_to/move_relative which wait. Requires enable_mosfets first. Parameters:
velocity (in degrees_per_second): The velocity (the unit is microsteps per time step * 2^20).
duration (in seconds): The time to maintain this velocity. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), duration (required), velocity (required).
multimoveA
The multimove command allows you to compose multiple moves one after another. Please note that when the queue becomes empty after all the moves are executed and the motor is not at a standstill then a fatal error will be triggered. [Firmware command "Multimove", group: Motion Control.] NOTE: moveList is a list of [value, duration_seconds] pairs (max 32): value is degrees/s^2 for acceleration moves (moveTypes bit = 0) or degrees/s for velocity moves (bit = 1); this server converts to firmware units. The final move must bring the motor to a standstill or the firmware raises a fatal error when the queue empties. This QUEUES the move and returns immediately; poll get_n_queued_items for completion, or use move_to/move_relative which wait. Requires enable_mosfets first. Parameters:
moveCount: Specify how many moves are being communicated in this one shot.
moveTypes: Each bit specifies if the move is a (bit = 0) MOVE_WITH_ACCELERATION_COMMAND or a (bit = 1) MOVE_WITH_VELOCITY_COMMAND.
moveList: A 2D list in Python format (list of lists). Each item in the list is of type [i32, u32] representing a series of move commands. Each move command specifies the acceleration to move at or the velocity to instantly change to (according to the bits above) and the number of time steps over which this command is to be executed. For example: '[[100, 30000], [-200, 60000]]'. There is a limit of 32 move commands that can be listed in this one multi-move command. Each of the moves takes up one queue spot, so make sure there is enough space in the queue to store all of the commands. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), moveList (required), moveCount (required), moveTypes (required).
pingB
Send a payload containing any data and the device will respond with the same data back [Firmware command "Ping", group: Device Management.] Parameters:
pingData: Any binary data payload to send to the device. Returns:
responsePayload: The same data that was sent to the device will be returned if all went well. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), pingData (required).
read_multipurpose_bufferA
Read whatever is in the multipurpose buffer (the buffer is used for data generated during calibration, going to closed loop mode, and when capturing hall sensor data) [Firmware command "Read multipurpose buffer", group: Other.] Returns:
bufferData: The data in the buffer (the format and length of the data depends on what was put in the buffer) motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
reset_timeA
Resets the absolute time to zero (call this first before issuing any movement commands) [Firmware command "Reset time", group: Basic Control.] Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
run_sequenceA
Execute a choreographed sequence of steps, e.g. "draw a square" or "wave".
Each step is one of: {"action": "move_to", "motor": "88", "degrees": 90, "speed_dps": 120} {"action": "move_relative", "motor": "88", "degrees": -30} {"action": "stop"} # motor optional -> all {"action": "wait", "seconds": 0.5} {"action": "command", "motor": "88", "name": "vibrate", "params": [1]}
Steps run in order; "command" runs any raw catalog command by tool name. Returns the final state of every detected motor.
Inputs: steps (required).
set_device_aliasA
Sets device alias [Firmware command "Set device alias", group: Device Management.] NOTE: Changes how the motor is addressed (takes effect immediately). Re-run detect_devices afterwards. Parameters:
alias: The alias (which is a one byte ID) ranging from 0 to 251. It cannot be 252 to 254 because those are reserved. You can set it to 255, which will remove the alias. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: alias (required), motor (required).
set_max_allowable_position_deviationA
Set the amount of microsteps that the actual motor position (as measured by the hall sensors) is allowed to deviate from the desired position. Throw a fatal error if this is exceeded. [Firmware command "Set max allowable position deviation", group: Configuration.] Parameters:
maxAllowablePositionDeviation (in degrees): The new maximum allowable position deviation setting Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), maxAllowablePositionDeviation (required).
set_maximum_accelerationA
Sets max acceleration [Firmware command "Set maximum acceleration", group: Configuration.] Parameters:
maximumAcceleration (in degrees_per_second_squared): The maximum acceleration. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), maximumAcceleration (required).
set_maximum_motor_currentA
Set the maximum motor current and maximum regeneration current. The values take effect immediately but are not saved to non-volatile memory, so they revert to the firmware default after a reset. [Firmware command "Set maximum motor current", group: Configuration.] NOTE: Higher current = more torque AND more heat; excessive settings can overheat the motor. Parameters:
motorCurrent (in milliamps): The motor current. The units are some arbitrary units and not amps. A value of 150 or 200 is suitable.
regenerationCurrent (in milliamps): The motor regeneration current (while it is braking). This parameter is currently not used for anything. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), motorCurrent (required), regenerationCurrent (required).
set_maximum_velocityB
Sets maximum velocity (this is not used at this time) [Firmware command "Set maximum velocity", group: Configuration.] Parameters:
maximumVelocity (in degrees_per_second): Maximum velocity. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), maximumVelocity (required).
set_pid_constantsB
Set PID constants for the control loop that will try to maintain the motion trajectory. [Firmware command "Set PID constants", group: Configuration.] NOTE: Bad gains can make the motor oscillate violently. Parameters:
kP: The proportional term constant (P)
kI: The integral term constant (I)
kD: The differential term constant (D) Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: kD (required), kI (required), kP (required), motor (required).
set_safety_limitsB
Set safety limits (to prevent motion from exceeding set bounds) [Firmware command "Set safety limits", group: Configuration.] NOTE: Sets firmware position limits; moves beyond them fault the motor. Parameters:
lowerLimit (in degrees): The lower limit in microsteps.
upperLimit (in degrees): The upper limit in microsteps. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), lowerLimit (required), upperLimit (required).
start_calibrationA
Starts a calibration, which will determine the average values of the hall sensors and will determine if they are working correctly [Firmware command "Start calibration", group: Configuration.] NOTE: The motor spins through a calibration routine; the shaft must be free to rotate. Takes some seconds; the motor won't respond until done. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
stopA
Immediately halt one motor, or ALL motors if motor is omitted.
Use for "stop", "halt", or any sign something is wrong. Sends the firmware's
emergency stop: motion halts and the move queue empties; holding torque remains.
Use disable_mosfets afterwards to let the shaft spin freely.
Inputs: motor.
system_resetA
System reset / go to the bootloader. The motor will reset immediately and will enter the bootloader. If there is no command sent within a short time, the motor will exit the bootloader and run the application from the beginning. [Firmware command "System reset", group: Basic Control.] NOTE: Reboots the motor (~2 s): position re-zeroes at the current shaft location, MOSFETs disable, fatal errors clear. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
test_modeA
Set or trigger a certain test mode. This is a bit undocumented at the moment. Don't use this unless you are a developer working on test cases. Notable modes: 0 = clear all test modes; 1-9 = motor test modes; 10-13 = LED test modes (locks up the device); 14-73 = trigger fatal errors 0-59; 74 = set the overvoltage-protection threshold to 22 V (production test, should trip on a 24 V supply); 75 = set the overvoltage-protection threshold to 26 V (production test, should not trip on a 24 V supply). A system reset restores the default overvoltage threshold. [Firmware command "Test mode", group: Configuration.] NOTE: Developer/diagnostic modes; behavior depends on firmware internals. Parameters:
testMode: The test mode to use or trigger Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), testMode (required).
time_syncC
Sends the master time to the motor so that it can sync its own clock (do this 10 times per second). [Firmware command "Time sync", group: Device Management.] Parameters:
masterTime (in seconds): The motor absolute time that the motor should sync to (in microseconds). Returns:
timeError: The error in the motor's time compared to the master time.
rccIcscr: The contents of the RCC-ICSCR register (holds the HSICAL and HSITRIM settings). motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), masterTime (required).
trapezoid_moveA
Move immediately to the given position using the currently set speed (the speed is set by a separate command) [Firmware command "Trapezoid move", group: Motion Control.] NOTE: The displacement is RELATIVE to the current position. This QUEUES the move and returns immediately; poll get_n_queued_items for completion, or use move_to/move_relative which wait. Requires enable_mosfets first. Parameters:
displacement (in degrees): The displacement to travel. Can be positive or negative.
duration (in seconds): The time over which to do the move. Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), duration (required), displacement (required).
vibrateB
Cause the motor to start to vary the voltage quickly and therefore to vibrate (or stop). [Firmware command "Vibrate", group: Device Management.] Parameters:
vibrationLevel: Vibration level (0 = turn off, 1 = turn on). Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required), vibrationLevel (required).
zero_positionA
Make the current position the position zero (origin) [Firmware command "Zero position", group: Basic Control.] NOTE: Redefines the current shaft location as 0 degrees (no movement). Returns: success confirmation. motor: an alias number (e.g. 88), a 16-hex-digit unique ID (e.g. "0123456789ABCDEF"), or "all" to broadcast to every motor on the bus (broadcasts get no responses back).
Inputs: motor (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 57 tools
The tool set has significant overlap among movement commands (move_to, go_to_position, move_relative, trapezoid_move, move_with_velocity, move_with_acceleration, multimove) and position getters (get_position, get_hall_sensor_position, get_comprehensive_position). Although descriptions clarify queue-vs-wait and absolute-vs-relative behavior, an agent could easily select the wrong tool. Many status/device-management tools are distinct, but the same action is exposed through multiple similar command names.
Most tools follow a consistent verb_noun snake_case pattern (get_, set_, enable_, disable_, move_, etc.). There are minor deviations like 'multimove' instead of 'multi_move', 'trapezoid_move' instead of 'move_trapezoid', and bare verbs ('stop', 'ping', 'identify'}), but the overall style is readable and predictable enough.
57 tools is far more than an agent can efficiently handle for a motor control domain. Many are low-level firmware directives (e.g. capture_hall_sensor_data, test_mode, crc32_control, get_debug_values) that add little to typical use cases and bloat the API surface. The count feels like a raw protocol dump rather than a curated MCP surface.
The tool set thoroughly covers the motor lifecycle: connection, detection, high-level moves, queued low-level moves, homing, calibration, status/telemetry, safety limits, error recovery, and device management. It is reasonably complete for a motor-control server, with minor gaps like a dedicated set-saved-configuration command and high-level helper for complex motion expressed through raw commands.
Maintenance
Related MCP Connectors
MCP server for generating rough-draft project plans from natural-language prompts.
A full backend your AI coding agent can set up over MCP
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables natural language control of ElephantRobotics MyCobot series robotic arms (especially ultraArmP340) through MCP protocol, with simulation mode and safety features.38MIT
- AlicenseNot gradedqualityFmaintenanceMCP server for Reachy Mini robot that exposes robot state and motion control to LLM frontends like Claude Desktop.MIT

Robonine MCP Serverofficial
AlicenseAqualityDmaintenanceA local MCP server that connects Claude Code or any MCP-compatible AI assistant to a Robonine robot arm.1MIT- FlicenseNot gradedqualityCmaintenanceControls surgical bed and C-arm through natural language commands via MCP protocol, with a mock backend and 3D visualization for real-time feedback.-