Tiny Engineer is a Wi-Fi desk robot. Drive it from any tool that can make HTTP requests, or use the Cursor-specific helper that maps agent hook events to poses.
Robot must be on the same network. Base URL: http://tiny-engineer.local (or the IP shown on the OLED). Full HTTP reference: api.md.
Two integration paths:
| Path | Best for | How |
|---|---|---|
| REST API | Any AI IDE, script, CI, custom agent | POST /anim?name=… |
| Cursor CLI | Cursor project hooks | npx → tiny-engineer-cursor |
flowchart TB
subgraph anyIde [Any AI IDE or script]
HookOrScript[Agent event or your code]
HookOrScript --> Post["POST /anim?name=typing|reading|…"]
end
subgraph cursorPath [Cursor]
CursorHook[Cursor hooks.json]
CursorHook --> Npx["npx tiny-engineer-cursor"]
Npx --> Post
end
Post --> Robot[Tiny Engineer on Wi-Fi]
Call the board directly. Works with Claude Code, Windsurf, Continue, custom plugins, shell hooks, or anything that can POST over HTTP.
curl -X POST "http://tiny-engineer.local/anim?name=typing"name |
Typical use |
|---|---|
typing |
Agent writing / editing / running tools |
reading |
Agent reading files / context |
thinking |
Agent reasoning / waiting on a long step |
ring |
Turn finished (attention ping) |
welcome |
Session start / greeting |
attention |
Needs user input |
error / abort |
Failure / cancel |
none |
Idle / clear pose |
Firmware holds each pose ≥1s and keeps only the latest pending switch — spam-safe. Auth is optional: if you set an access token on the device, send Authorization: Bearer <token> (check GET /auth for required). Prefer short timeouts (e.g. 2s) and ignore network errors so the agent never stalls if the robot is offline.
Shell
curl -4 -sS -m 2 -X POST "http://tiny-engineer.local/anim?name=reading" >/dev/null || trueJavaScript (Node 18+)
await fetch("http://tiny-engineer.local/anim?name=thinking", {
method: "POST",
signal: AbortSignal.timeout(2000),
}).catch(() => {});Python
import urllib.request
urllib.request.urlopen(
urllib.request.Request(
"http://tiny-engineer.local/anim?name=typing",
method="POST",
),
timeout=2,
)Wire these into your IDE’s hook / plugin / lifecycle events (prompt submitted → reading, tool use → typing, turn end → ring, etc.). Mapping is yours; the robot only cares about name.
Health check (no motion):
curl http://tiny-engineer.local/healthMore routes (tests, servo, web UI): api.md.
For Cursor only: a small Node CLI reads Cursor hook JSON on stdin, picks an animation, and POSTs /anim. No need to implement the event map yourself.
npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-cursor- HTTPS tarball (not
github:…SSH shorthand — that often fails in Cursor hooks with no SSH agent). - Bin name
tiny-engineer-cursorrequired after--package=…. - Optional:
--url http://192.168.x.x(defaulthttp://tiny-engineer.local). - Auth: if the device has an
access_token, setTINY_ENGINEER_TOKENin the process env or a project-root.envfile (same value). The CLI sendsAuthorization: Bearer …. No token → no header (auth disabled on device). --helpfor usage and event map.
npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-cursor --help
echo '{"hook_event_name":"stop"}' | npx -y --package=https://github.com/jamro/tiny-engineer/archive/refs/heads/main.tar.gz tiny-engineer-cursorIn that project’s .cursor/hooks.json, use the same command for each anim hook. Set timeout ≥ 30 (cold npx can exceed 2s). Full sample + event table: hooks.md.
Inside this firmware repo you can instead run the local bin while developing the CLI:
node packages/tiny-engineer-cursor/bin/tiny-engineer-cursor.js- Building for one IDE / custom agent → REST. One
POST, zero Node dependency. - Using Cursor and want zero mapping code → Cursor CLI + hooks.
- Both are fine together: CLI is just a thin client of the same
/animAPI.
Prerequisites for either path: flash firmware, join 2.4 GHz Wi-Fi, confirm http://tiny-engineer.local/health (or the OLED IP) responds.