a very small agent
mote is a terminal agent in one file of stdlib Python. No SDK, no pip,
no wheels, no Node — just mote.py, an API key, and any machine with
Python ≥ 3.7 on it.
It was born on a revived 2009 Dell Inspiron Mini 1012 (Intel Atom N450,
1GB RAM, 32-bit antiX Linux). Current agentic CLI tools ship as x64/arm64
native binaries on top of heavyweight runtimes — that netbook can run none
of them. It turns out the agent itself doesn't need any of that: the wire
protocol is just HTTPS and server-sent events, and the whole loop fits in
~900 lines of urllib. If a machine can open a TLS connection, it can
host an agent.
The model backend is the Anthropic Messages API — bring your own key.
$ python3 mote.py
mote · claude-opus-4-8 · /home/user
a very small agent. /help for commands. acting without approval prompts (--safe to change).
❯ why does wifi drop after suspend?
· thinking
· tool: bash (dmesg | grep -i -E 'wlan|brcm' | tail -20)
· tool: web_search (brcmsmac suspend resume wifi drop)
· 6 results
Your Broadcom BCM4313 uses brcmsmac, which is known to lose
firmware state on resume. Quick fix now: sudo modprobe -r brcmsmac
&& sudo modprobe brcmsmac. Permanent fix: add a systemd
suspend hook — want me to write it?
❯ yes
· tool: write_file (/etc/systemd/system/wifi-resume.service)
· tool: bash (systemctl enable wifi-resume.service)
Done — the module now reloads automatically after every resume.
- The agent loop: streaming responses, model reasoning rendered dim as it arrives, tool use, retries with backoff on 429/5xx/529, broken-stream recovery.
- Client tools that act on your machine:
bash,glob,grep,read_file,edit_file(exact-string replace),write_file. - Server-side web tools:
web_searchandweb_fetchexecute on the API provider's infrastructure — web lookup with zero local code and zero RAM cost, which matters when you have 1GB of it. - Prompt caching wired in properly: a stable cache breakpoint over tools+system and moving breakpoints over recent turns, so long sessions mostly bill at the ~0.1× cached-input rate.
- Context management: past ~150k input tokens, old tool outputs are
automatically elided (also
/trimon demand). - Cost visibility:
/usageshows tokens, cache hit rate, web searches, and an estimated dollar total, live. - Hardware-aware by default: at startup mote detects cores, RAM, arch,
and terminal width and tells the model what it's living in, so it
prefers
head/grepover dumping files, distrusts fat install paths on 32-bit userlands, and keeps output narrow.
curl -O https://raw.githubusercontent.com/asystemoffields/mote/main/mote.py
mkdir -p ~/.config/mote
echo 'sk-ant-...' > ~/.config/mote/key # key from platform.claude.com
chmod 600 ~/.config/mote/key
python3 mote.pyexport ANTHROPIC_API_KEY=... works instead of the key file. Note this
must be a pay-per-token API key from the developer console — a chat-app
subscription login can't authenticate against the raw API.
On a very old install, if you hit a TLS certificate error:
sudo apt-get install --reinstall ca-certificates.
python3 mote.py # interactive, acts freely
python3 mote.py --safe # ask before mutating anything
python3 mote.py -p "why is the fan on?" # one-shot prompt, then exit
python3 mote.py --model claude-haiku-4-5 # cheaper/faster model
python3 mote.py --no-web # disable web tools
| command | effect |
|---|---|
/help |
command list |
/auto |
toggle approval prompts (default: off) |
/web |
toggle web_search / web_fetch |
/model |
show or switch model (drops the prompt cache) |
/usage |
token + cost totals for the session |
/trim |
elide old tool outputs from context now |
/clear |
forget the conversation |
/quit |
exit (also ctrl-d) |
ctrl-c interrupts a running turn and rolls the conversation back to a
consistent state (no dangling tool calls).
Default model is claude-opus-4-8; claude-haiku-4-5 is the budget
choice for quick sysadmin questions (mote automatically selects the older
web-search variant it supports).
mote runs in yolo mode by default: every tool call — including rm,
package installs, config edits — executes immediately, as your user, with
no sandbox and no confirmation. The only guardrail is the system prompt,
which instructs the model to double-check destructive commands and prefer
reversible alternatives. That is a real mitigation but not a boundary.
If you want a boundary:
--safe(or/autoin-session) restores y/n approval prompts: read-only commands (ls,grep,df, …) run freely; anything mutating asks first. Answering with a sentence instead of y/n declines with feedback the model acts on.a= approve everything from here on.- Run mote as a dedicated low-privilege user, or in a container/VM, if you're pointing it at anything you care about.
Choose your own adventure — it's your machine.
You pay per token at standard API rates. The caching setup keeps long
sessions cheap (a mostly-cached turn costs cents); web search bills
~$10 per 1,000 searches on top. /usage keeps a running estimate.
One file, four moving parts:
- SSE streaming — a hand-rolled ~80-line parser over
urllibhandlescontent_block_start/delta/stop, accumulates split tool-input JSON, and captures reasoning blocks with their signatures so they replay correctly on later turns (required by the API). - The loop — send messages → if
stop_reason == "tool_use", execute the tool blocks locally, append results, repeat.pause_turn,refusal, andmax_tokensare handled; server-tool blocks pass through untouched. - Caching —
cache_controlbreakpoints placed on the system prompt and the last two user turns, moved each request. - Tools — client tools are plain functions dispatched by name;
web_search/web_fetchare declared in the request and run entirely server-side.
test/mock_api.py is a mock API server speaking the real wire format —
python3 test/mock_api.py in one terminal, point mote at it with
ANTHROPIC_BASE_URL=http://127.0.0.1:8399, and you can hack on the
harness without spending a token. It asserts the tricky invariants
(reasoning-block replay, tool-result pairing, cache-marker placement).
- Python ≥ 3.7, stdlib only. Developed against 3.7-era syntax on purpose.
- Any POSIX-ish OS with
bashandgrep(thebash/greptools shell out). Linux, BSD, macOS should all work; the target audience is old and odd Linux boxes. - 32-bit x86, ARM, whatever — if CPython runs and TLS works, mote runs.
MIT