Physical MCP GitHub

Meta Global AI Developer Hackathon · Indie track

This timer has no API. So we gave it one.

An AI that sees a control panel, presses the buttons, and asks a human before anything risky. A webcam watches the panel, a servo finger does the pressing, and every action goes through MCP tools with an approval gate in front of anything risky.

Brain
Muse Spark plans, Muse Spark Open scores risk
Eyes
SAM 3.1 finds the controls, Spark reads the screen
Gut
Muse Spark Open scores risk, a human makes the call
Finger
Two servos on the rig: H and ST/SP
02:00 00 T1 · STANDBY H M S RESET ST/SP two fingers · H + ST/SP
Two-finger rig on the kitchen timer. The incubator below runs the same pipeline.

01

Meet the rig

Drag it to turn it and pull it apart. Pick a layer to see what it does.

A webcam over the panel, two servo fingers on H and ST/SP, and the screen where a human approves. Pull it apart to see the four layers.

02

Watch it press buttons

Full demo video

Filmed on the rig: H, H, then the hold-to-approve on ST/SP.

03

Try it yourself

Two replays, one pipeline. Same brain, different machine. Press play, watch the finger work, and be the human on the risky step. The timer is the real rig device; the incubator is a demo profile showing the same code driving a second machine.

Request

…

queued 0.0 s

Screen read

…

Log

    Replays run from fixed scripts in this page, built from the real fixtures and a recorded risk score. The full stack — planner, vision, scorer, finger — runs in the repo below.

    04

    How it works

    1. Eyes. A webcam watches the panel. SAM 3.1 finds the buttons and the screens; Muse Spark reads what the screens say, voting across reads.
    2. Brain. Muse Spark plans the presses from the live screen read, then the plan is checked in code against that same read before anything moves.
    3. Finger. An ESP32 drives a servo that presses the real button. Two fingers on the rig: H and ST/SP.
    4. Gut check. Muse Spark Open scores each risky step with a tier and a one-line reason. It can only add, never remove an approval.
    5. The call. A human holds approve. Steps that turn something on always need the hold, whatever the score says.
    One process owns the finger
    web app  →  backend (HTTP + WebSocket)  →  MCP tools: get_controls, press, read_screen, get_frame, stop
                              ↑                              ↓ press holds the approval gate
                  Muse Spark (plan) + SAM 3.1 (find)      ESP32 + servo → machine
                  Muse Spark Open on Baseten (risk)       webcam → screen reads

    05

    Which Meta model does what

    ModelJob
    sam-3.1Finds the buttons and the screens on the panel.
    muse-spark-1.3Plans the presses and reads the screens, voting across reads for confidence.
    meta-models/muse-spark-open-1026Muse Spark Open on Baseten: scores each risky step with a tier and a one-line reason. It can only add, never remove an approval.
    Muse CodeBuilt this site.

    Two real risk cards, one per replay

    Timer, step 3: Start T1 (press ST/SP)

    medium Starts T1 countdown from 2 hours as requested.

    Scored by meta-models/muse-spark-open-1026. Recorded score from the run data.

    Incubator, step 6: Start the run (press START)

    medium Starts incubator at 37 °C for 2 hours as requested.

    Scored by meta-models/muse-spark-open-1026. Real scorer output, risk/out/incubator_start_37.scored.json.

    06

    Safety

    Every rule below lives in the code, not just on this page.

    • The approval gate lives inside the press tool, so the agent can’t skip it. (backend/mcp_server.py)
    • Steps that turn on output always need hold-to-approve, whatever the risk score. The backend refuses a tap. (backend/api.py)
    • The risk scorer can only add a tier and a reason, never remove an approval. If it fails, the step scores high. (risk/scorer.py)
    • Reads are voted: two reads agree, or a third breaks the tie. A low-confidence read pauses the job instead of pressing blind.
    • Press budget: 30 presses per job. Going over is refused, nothing pressed.
    • Plans that don’t add up get refused with a plain reason. (backend/planner.py checks the plan against the screen)
    • A press that didn’t register gets retried — never on an approval step, where a double-press would undo the job.
    • Buttons with no finger are never planned. STOP is always on screen and lifts the finger right away, even mid-hold.

    07

    The numbers

    Only numbers with a source are here. Everything else says so. Full table, sources and check dates: docs/costs.md.

    ≈$20

    Rig parts

    Servos, webcam, ESP32, wiring. Entered prices, listings not attached yet.

    $0.14

    API cost per job

    Planner, screen reads, scorer, at list price. From the one run so far with token usage recorded. Hackathon credits paid for the calls.

    6/7

    Full jobs worked on the rig

    Nov 3 runs, not counting jobs stopped by hand. All 23 recorded runs, debugging included, are in runs/.

    Replace the machine, or give it an API

    Instead of replacing…Replacement costsSource, checked Nov 2
    Lab benchtop incubator$798.59–$1,185.00TEquipment listings
    Manufacturing HMI touch panel$1,718.00AutomationDirect
    Touch screen test robot (hardware QA)$12,243.75–$12,288.75igus RBTX
    Temperature controller (building ops)$540.52SupplyHouse
    Greenhouse environment kit$8,745.00Growers Supply
    Commercial fryer, programmable$6,765.00WebstaurantStore
    Warehouse parcel scale$945.00Uline
    Home electric range$649.00–$829.00Home Depot
    IoT retrofit, drill press (sensors + gateway)about $2,000Bosch Rexroth via Design Development Today
    IoT retrofit, legacy equipment, typicala couple thousand, up to $10,000–$20,000Automate.org

    08

    Built during the hackathon

    • Pam (@PamAntonio): the idea, the first contract, the first planner and device profiles, the ESP32 firmware, the serial protocol, and the rig, including the press angles.
    • Bhavi (@Bhavishaahuja): the MCP server, the planner hookup and later planner work, the backend runner, vision, the risk scorer, the web app, calibration, and the live camera.
    Everything in this repo was written during the hackathon, from Oct 27, except the very first commit: the README was created on Oct 26 (b06e0dc, Pam). web/ started from create-next-app.

    We used Claude Code as a coding assistant. The Co-Authored-By trailers in the commit history show which commits. This site was built with Muse Code. Commit history

    09

    Run it yourself

    No hardware needed. This runs the backend on FakeHardware plus the web app. You need your own Meta Model API key and Baseten key in a repo-root .env file (MODEL_API_KEY and BASETEN_API_KEY).

    Mac / Linux

    python3 -m venv .venv
    .venv/bin/python -m pip install -r backend/requirements.txt
    FAKE_CONFIDENCE=high FAKE_LOW_READ=2 FAKE_MISS_PRESS=btn_h .venv/bin/python backend/api.py

    Then the web app, in another terminal:

    cd web
    npm install
    NEXT_PUBLIC_USE_MOCK=false NEXT_PUBLIC_BACKEND_URL=http://localhost:8000 NEXT_PUBLIC_DEVICE_ID=timer-01 npm run dev

    Windows Command Prompt

    python -m venv .venv
    .venv\Scripts\python -m pip install -r backend\requirements.txt
    set FAKE_CONFIDENCE=high
    set FAKE_LOW_READ=2
    set FAKE_MISS_PRESS=btn_h
    .venv\Scripts\python backend\api.py

    Then the web app, in another terminal:

    cd web
    npm install
    set NEXT_PUBLIC_USE_MOCK=false
    set NEXT_PUBLIC_BACKEND_URL=http://localhost:8000
    set NEXT_PUBLIC_DEVICE_ID=timer-01
    npm run dev

    Open http://localhost:3000. The fake flags replay the low read and the missed press from the timer replay above. Restart the backend before every take, each flag fires once per start. Full flags and rig setup are in the repo README.