Skip to content
Conduit

mcp server

lynx

Open-source pan/tilt camera for the XIAO ESP32-S3 Sense

alexnodeland/lynxcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 25, 2026

lynx

An open-source pan/tilt camera you can tell where to look. AI agents drive it over MCP, Home Assistant finds it on its own, and it tracks faces and learns your own objects without sending a frame off the device.

It runs on a Seeed XIAO ESP32-S3 Sense with two MG90S servos and eight 3D-printed parts. One USB-C cable powers it, and one screw type holds it together. About $43 in parts (cost breakdown).

Site: https://alexnodeland.github.io/lynx/ · Web flasher · Build guide (3D) · Docs

What it does

Agents: an MCP server on the camera

The firmware is an MCP server (Streamable HTTP) and a REST API. Give an agent the URL and the device token, and it can aim the head, take a snapshot with the detections drawn on it, find faces, save presets, run a patrol, and read the microSD recordings.

claude mcp add --transport http lynx http://lynx-xxxx.local/mcp --header "Authorization: Bearer <token>"

you: look at the desk and take a picture → look(pan: -40, tilt: 30) → snapshot(annotate: true) agent: A laptop, a mug and a plant. One person, seated on the left.

Tools: look · center · sweep · snapshot · detect · follow · preset · patrol · recordings · set_camera · stream_url · status. Full list and the REST API: firmware/lynx.

Home Assistant: discovered, not configured

Add this repo to HACS as a custom integration and lynx shows up under Discovered (zeroconf). Paste the token and you get the camera with live view, pan and tilt sliders, occupancy and a person count, follow and detection switches, presets and patrol, microSD recording, and a firmware update entity that installs GitHub releases over Wi-Fi.

# look at the front door when it opens
triggers:
  - trigger: state
    entity_id: binary_sensor.front_door
    to: "on"
actions:
  - action: lynx.look
    target: { entity_id: camera.lynx_42e0 }
    data: { pan: 60, tilt: 50 }

Entities and more automations: custom_components/lynx.

Face tracking, on the device

ESP-DL finds human faces or cat faces at about 4 fps on a scaled copy of each frame, so the stream keeps its 20 fps. Follow mode keeps the nearest face centred with small no-overshoot moves, holds where it last saw one, then glides back to rest. It's detection, not recognition, and nothing leaves the camera unless you ask for it over the API.

Your own detector

Anything else (a package, your dog, a whole person) comes from Edge Impulse. lynx collects its own training set by scanning the room, and one command installs the export:

just capture mug              # lynx pans the room and saves 30 shots to datasets/mug/
# label and train in Edge Impulse (FOMO boxes or a classifier), export as an Arduino library
just model-add export.zip     # now `model: custom` in the API, MCP and Home Assistant

Walkthrough: docs/custom-models.md.

Also

Over-the-air updates that roll themselves back if a new build fails · up to 4 viewers at once and pushed status events · 8 named presets and a patrol tour · snapshots on detection and a timelapse on microSD · a first-boot Wi-Fi hotspot with a setup page · a per-device token.

Repository

PathContents
firmware/lynxPlatformIO firmware: motion, camera and MJPEG stream, REST API, MCP server, detection and follow, OTA, setup hotspot
custom_components/lynxHome Assistant integration (HACS)
hardware/productionPrint-ready STL/STEP, assembly STEP, BOM
hardware/electronicsCircuit, nets, parts, wiring harness, carrier PCB (KiCad)
webthree.js landing page, build guide and web flasher (Vite + GSAP)
tools/mlTraining-image capture for custom models
mediaRenders and screenshots

Specs

ControllerSeeed XIAO ESP32-S3 Sense (camera, microSD, Wi-Fi)
Actuators2 × MG90S micro servo — pan on D0 (GPIO1), tilt on D1 (GPIO2), 220 Ω series resistors
Travelpan ±90°, tilt ±60° mechanical; firmware soft limits pan ±88°, tilt ±58°
Motioneased "glance" moves, default max speed 300 °/s, PWM detached when idle
Power + dataone USB-C cable via a panel socket in the base (Adafruit 6069); supply ≥ 1 A
Decoupling470 µF 6.3 V bulk + 100 nF across the servo 5 V rail
Fasteners9 × M2 × 8 thread-forming, pan head — the only screw
Printed parts8 (base, lid, battery tray, yoke, pan base, pan cap, servo shroud, light pipe)
Controlon your network: REST API, MCP server for AI agents, MJPEG stream (port 81), dashboard; per-device token; serial console
Setupfirst-boot Wi-Fi hotspot lynx-xxxx with a captive setup page; lynx-xxxx.local via mDNS
Detectionon-device faces or cat faces (ESP-DL), ~4 fps; custom Edge Impulse models; follow mode
Rest posepan 0°, tilt 45°: the lens leans toward the front (tilt 0° points it straight up)
Batteryoptional 150 mAh LiPo keeps the camera up; servos run on USB power only

System

flowchart LR
  USB["USB-C supply (≥ 1 A)"] --> SOCK["Panel socket<br/>Adafruit 6069"]
  SOCK --> XIAO["XIAO ESP32-S3 Sense"]
  BAT["LiPo 150 mAh<br/>(optional)"] -.-> XIAO
  XIAO -- "5V / GND" --> RAIL["Servo rail<br/>470 µF + 100 nF"]
  XIAO -- "D0 → 220 Ω" --> PAN["Pan servo<br/>MG90S"]
  XIAO -- "D1 → 220 Ω" --> TILT["Tilt servo<br/>MG90S"]
  RAIL --> PAN
  RAIL --> TILT
  XIAO -. "Wi-Fi: MCP" .- AGENT["AI agent"]
  XIAO -. "Wi-Fi: REST + events" .- HA["Home Assistant"]
  XIAO -. "Wi-Fi: dashboard + MJPEG" .- CLIENT["Browser"]

Gallery

Exploded: eight parts, one screw typeAssembled on the pan base
One USB-C cable into the basemicroSD slot above the USB-C port
The landing pageIts motion section

Parts

About $43 per camera, $55 with the battery and a microSD card, and about $80 for your first build because the small parts come in packs (breakdown). Full list with suppliers in hardware/production/BOM.md.

QtyPart
8printed parts (hardware/production/*.stl)
1Seeed XIAO ESP32-S3 Sense
2MG90S micro servo (Waveshare 15024) or Adafruit SG92R
1panel-mount USB-C extension, Adafruit 6069
1right-angle USB-C to USB-C, thin, data-capable, 15–20 cm
1470 µF 6.3 V electrolytic (Panasonic EEU-FR0J471) + 100 nF 0603
2220 Ω resistor
9M2 × 8 thread-forming screw, pan head
112.7 × 3.17 mm N42 disc magnet (K&J D82)
—optional: 150 mAh LiPo (Adafruit 1317), microSD card

Quick start

1. Print

Print the three test-*.stl fit coupons first, then the eight parts in hardware/production. Orientation, supports and materials per part are in the BOM; defaults are 0.2 mm layers and 3 walls.

2. Assemble

The interactive build guide walks all 13 steps in 3D: parts fly in, the rest of the camera stays ghosted, and you can orbit the model at every step. The same steps as text:

Follow the ten steps in the BOM's assembly section. Wire the servos as in hardware/electronics — carrier PCB or soldered harness, same nets.

3. Flash

The easiest way is from a browser: open the web flasher in Chrome or Edge, plug in over USB-C, and click Install. Or with PlatformIO:

cd firmware/lynx
pio run -e xiao -t upload       # build and flash over USB-C
pio device monitor -b 115200    # serial console: try `info`, `center`, `sweep`

Calibrate before fixing the horns: see firmware/lynx.

4. Connect

On first boot lynx opens a Wi-Fi hotspot, lynx-xxxx. Join it from a phone, pick your network, and save the token the setup page shows. Then open http://lynx-xxxx.local/ for the live view and controls, or hook up an AI agent:

claude mcp add --transport http lynx http://lynx-xxxx.local/mcp --header "Authorization: Bearer <token>"

The full API and MCP tool list are in firmware/lynx.

Home Assistant: add this repo to HACS as a custom integration, restart, and lynx shows up under Discovered. Paste the token and you're done. See custom_components/lynx.

5. Run the site locally

cd web
npm ci
npm run dev

Develop

just setup   # once: PlatformIO + HA test harness in .venv, web deps, git hooks
just check   # lint, 71 firmware tests, XIAO build, 35 Home Assistant tests, site build
just         # every other recipe: fw-flash, fw-monitor, web-dev, hass-test, look, snap, mcp-add, release …

Contributing · Architecture · Troubleshooting · Changelog

Licences

  • Hardware: CERN-OHL-S-2.0
  • Firmware and web: MIT
  • Docs and media: CC-BY-SA-4.0

The licence texts are in LICENSES/. The root LICENSE is the MIT text for the software, so GitHub and HACS can detect it; NOTICE maps each folder to its licence.

↑↓ navigate openesc close