TokenMonitor is live on Kickstarter back the campaign while it's running

Setup

From box to desk
in about three minutes.

Install the MCP plugin on your AI agent, power the device, join its captive portal, pair from your agent. Live usage flows within ~60 seconds.

  1. Step 01
    Install the MCP plugin
  2. Step 02
    Power the device
  3. Step 03
    Give it Wi-Fi
  4. Step 04
    Pair from your AI
  5. Step 05
    You're done
  6. Step 06 · optional
    Your own panel
01

Install the MCP plugin on your AI agent.

The tokenmonitor plugin ships through Fractal Manifold's MCP marketplace. Installing it registers the local broker (tokenmonitor-mcp) plus the configure / theme / settings / firmware skills — same surface across every supported CLI.

# Add the marketplace, then install the plugin > /plugin marketplace add fractal-manifold/mcp-marketplace > /plugin install tokenmonitor@fractalmanifold-mcp-marketplace
# Same marketplace as Claude Code — from your shell, not the Codex prompt $ codex plugin marketplace add fractal-manifold/mcp-marketplace $ codex plugin add tokenmonitor@fractalmanifold-mcp-marketplace # then start a new Codex thread so it loads
# Antigravity (agy) installs the plugin straight from the GitHub subpath $ agy plugin install https://github.com/fractal-manifold/mcp-marketplace/plugins/tokenmonitor
Verify it's loaded Ask your agent "What's the status of my TokenMonitor broker?" — it should call tokenmonitor_status and report the broker role + request count.
claude · 80×24
> /plugin marketplace add fractal-manifold/mcp-marketplace
Added marketplace fractalmanifold-mcp-marketplace
 
> /plugin install tokenmonitor@fractalmanifold-mcp-marketplace
Fetching plugin tokenmonitor
Loaded skills: configure, theme, settings, firmware
MCP server tokenmonitor ready (17 tools)
[ok] plugin installed · waiting for devices
02

Power the device.

USB-C, 5 V / 1 A. Any wall charger or USB hub will do — the device draws < 4 W peak with the backlight at full.

On first boot, with no Wi-Fi yet stored, the device shows a setup screen offering three ways in: join its own access point and use the captive portal, type the Wi-Fi straight into the touchscreen, or push everything over a USB cable. Pick whichever suits you — the device listens on all three at once.

Power consumption ≈ 3 W with backlight at 80%, ≈ 0.4 W with backlight off in standby. Battery (where fitted) holds ~3 hours of operation for power cuts.
03

Give it Wi-Fi. Three routes.

The first screen is a chooser, not an instruction. All three routes are live at the same time — the device is running its own access point, listening on the touchscreen and watching the USB cable simultaneously, so you can start down one and switch to another without rebooting. Tap a card and it opens the detail view for that route.

1

Join the setup WiFi

The classic route: another device does the typing. The unit raises its own access point and serves a captive portal at 192.168.4.1.

  • SSID TokenMonitor-XXXX — last 4 = MAC suffix
  • Password shown on the screen, next to a QR for the portal address
  • The portal asks for your Wi-Fi SSID + password, nothing else

Best when the device is on a wall wart rather than plugged into a computer.

2

Enter WiFi here

No phone involved. The panel is 480×480 capacitive — it scans, you tap your network and type the password on an on-screen keyboard.

  • Sorted strongest-first, open networks flagged
  • Manual for a hidden SSID, Rescan if yours hasn't appeared
  • Same picker Settings opens later to change networks

Fastest when you are already standing in front of the unit.

3

Set up over USB

The cable it already draws power from is also a data link. One command from your agent pushes Wi-Fi, broker URL and key together — steps 3 and 4 collapse into one.

  • No pairing code. Plugging it in is the proof you are standing in front of it
  • Works with no shared network at all — before the device has ever seen Wi-Fi
  • Also how you move a configured device onto a new network later

Linux hosts only for now; on macOS or Windows use route 1 or 2.

192.168.4.1

TokenMonitor

Setup

After saving, the device reboots and shows its IP and a 6-digit pairing code on screen. Then run /tokenmonitor:configure from your AI agent on the same LAN and enter the code to finish setup (broker URL, key and city).

Route 1 — what the portal looks like

Joining TokenMonitor-XXXX pops this page up on its own; if it doesn't, open 192.168.4.1 by hand.

It asks for two things and no more. Broker URL, key and city are not here — they come from your agent in step 4, over a signed channel, so nothing sensitive is ever typed into a page anyone on that access point could read.

Is the setup network secure? It is WPA2, with a password unique to your unit and shown only on its screen — so a neighbour cannot join it. The password is derived from the device's own MAC address rather than being a secret, so treat the portal page as readable by anyone already on that network; nothing sensitive is pre-filled there. The broker channel that follows is HMAC-signed, with the config blob AES-CTR encrypted under your passphrase-derived PSK.

Whichever route you take, the device reboots once and lands on Waiting for setup — except route 3, which already carried the broker URL and key across the cable and goes straight to the dashboard.

04

Pair from your AI agent. The magic moment.

Install the plugin (step 01 above, or the plugin page for the full per-CLI walkthrough), then ask Claude Code, Codex or Antigravity to configure your device:

> /tokenmonitor:configure

The agent does the rest:

  • Discovers your device on the LAN via mDNS
  • Asks for the 6-digit code shown on screen
  • Generates a passphrase, derives the PSK locally
  • Pushes broker URL + key, device reboots
What about Codex / Antigravity? Same skill, same UX, different CLI. See plugin install for the per-CLI snippet.
Took route 3? You are already done. The cable carried Wi-Fi, broker URL and key in one push, so this step already happened — there is nothing to discover and no code to type. The 6-digit code on this page gates the LAN path only; over USB, plugging it in is the proof you are standing in front of the device. That also makes the cable the way out of guest networks and VLANs that block mDNS. See route 3 above.
05

You're done.

The device lands on the dashboard. Within ~60 seconds the broker's first poll completes and live usage starts flowing, then refreshes about every 90 seconds.

Forget it. Glance at it.

86 × 86 mm footprint, sits on your desk with one USB-C cable. Standby kicks in after 20 min idle; tap anywhere to wake.

06

Your own panel. Optional.

Swipe up on the dashboard to reach a screen that's entirely yours — a line, bar, pie, key/value table or plain-text widget, drawn from data you produce. A small script on your laptop writes a JSON file; the broker serves it to the device over the same signed LAN link; the device draws it. It's opt-in, off by default, and nothing is ever flashed — edit the file and the screen catches up within one poll.

on your laptopYour script writes a small JSON file, whenever your data changes
a filepanel.json any path you choose — the broker just reads it
brokerServes it verbatim, over the signed GET /device/<id>/panel
on your deskThe device swipe up to view · redraws on every poll
1

Turn it on

The panel is off until you enable it. On the device: Settings → Display → Custom panel. Or from your agent, on any of the three CLIs:

> /tokenmonitor:settings panel_enabled=true
2

Point the broker at a file

You decide where the file lives — the broker only reads the path you give it. Add a [panel] section to ~/.config/tokenmonitor/tokenmonitor.toml:

# one file, shared by every device you own [panel] file = "~/.local/state/tokenmonitor/panel.json" # …or a folder of per-device files (optional) # dir = "~/.config/tokenmonitor/panels"

Restart the broker once after adding the section. From then on the file is re-read on every poll — rewrite it as often as you like, no restart needed.

Which file does device <id> get?
  1. dir/<id>.json — per-device, wins if present
  2. dir/default.json — shared fallback in the folder
  3. file — the single global one

Your device's 8-hex ID is on Settings → About (or ask your agent to list devices). If none of these resolve, the panel screen simply shows an empty-state message — that's normal, not an error.

3

Write the file

Any language works. Write it atomically — a temp file, then a rename — so the broker never catches a half-written file. Give any element a color, and set poll_s (3–60 s) when you want a near-live refresh:

{ "version": 1, "poll_s": 5, "title": "Session", "title_color": "#e0894a", "tiles": [{ "type": "line", "title": "Tokens/min", "series": [ { "name": "in", "color": "#c15f3c", "points": [12,18,9,24,31] }, { "name": "out", "color": "#e07a4a", "points": [4,7,3,11,15] } ] }] }
‹ Back Session
Tokens/min
in out
A ~20-line Python generator you can steal →
#!/usr/bin/env python3 import json, os, time, collections OUT = os.path.expanduser("~/.local/state/tokenmonitor/panel.json") hist = collections.deque(maxlen=64) while True: hist.append(round(sample_metric())) # your number: tokens/min, RAM %, $ today… doc = { "version": 1, "poll_s": 5, "title": "Live", "tiles": [{ "type": "line", "title": "metric/min", "series": [{"name": "m", "color": "#c15f3c", "points": list(hist)}], }], } tmp = OUT + ".tmp" # write a temp file first… with open(tmp, "w") as f: json.dump(doc, f) os.replace(tmp, OUT) # …then rename — atomic, never half-read time.sleep(30)

Five widgets, your data & colors

Pick a shape for whatever you measure.

↑ Tap a shape · here's the file that draws it ↓

{ "version": 1, "poll_s": 5, "title": "Session", "title_color": "#e0894a", "tiles": [{ "type": "line", "title": "Tokens/min", "title_color": "#c15f3c", "series": [ { "name": "in", "color": "#c15f3c", "points": [12,18,9,24,31] }, { "name": "out", "color": "#e07a4a", "points": [4,7,3,11,15] } ] }] }
{ "version": 1, "title": "Per day", "title_color": "#10a37f", "tiles": [{ "type": "bar", "title": "Tokens/day", "series": [ { "color": "#10a37f", "points": [23,19,28,12,40,6,4] } ] }] }
{ "version": 1, "title": "Split", "tiles": [{ "type": "pie", "title": "By provider", "slices": [ { "label": "Claude", "value": 61, "color": "#c15f3c" }, { "label": "Codex", "value": 27, "color": "#10a37f" }, { "label": "Antigravity", "value": 12, "color": "#3186ff" } ] }] }
{ "version": 1, "title": "System", "title_color": "#c15f3c", "tiles": [{ "type": "table", "title": "Metrics", "title_color": "#e07a4a", "rows": [ { "k": "RAM", "v": "62%", "color": "#10a37f" }, { "k": "$ today", "v": "4.21", "color": "#e07a4a" }, { "k": "session", "v": "12.4k" }, { "k": "uptime", "v": "3h12m" } ] }] }
{ "version": 1, "title": "Hello", "title_color": "#c15f3c", "tiles": [{ "type": "text", "title": "Note", "bg": "#141210", "text": "ship it", "color": "#e07a4a" }] }

One panel — or a grid

Add more tiles, up to four.

Put more objects in tiles[] and the device lays them out for you: one fills the screen, two stack in rows, three or four fall into a 2×2 grid.

Session
Tokens/min
1 tile · full screen
Panel
Tokens/min
Metrics
RAM62%
$ today4.21
uptime3h12m
2 tiles · stacked
Panel
Tokens
Metrics
RAM62%
$ today4.21
Split
Note
ship it
3–4 tiles · 2×2 grid

Colour anything

Every colour is an optional solid #rrggbb. Omit it and the element falls back to the active day / night theme:

  • Screen & tile titles — title_color
  • Tile background — bg
  • Line / bar series & pie slices — color
  • Table value / key cells — color / kcolor
  • Text tile — color

Limits

The device parses into fixed buffers — anything past these caps is simply ignored, never a crash:

Tiles per document4
Series · points each4 · 64
X-axis labels12
Pie slices8
Table rows12
Any text string32 chars
Whole file8 KB
poll_s range3–60 s
A fast poll_s costs the broker nothing extra Serving the panel is a plain file read, and provider usage is answered from the broker's ~30 s cache — so polling every few seconds never multiplies calls to Claude, Codex or Antigravity. The full field reference lives in the broker docs.