Everything here is opt-in and behind its own extra. None of it changes the loop — a gateway moves text in and out, an integration is a tool the agent may call. The four pillars work with none of this installed.
Moved out of the README, which had grown to 569 lines for a project whose promise is that you can read it in an afternoon. Nothing was rewritten.
uv pip install -e '.[voice]'
tini voice # hands-free: always-listening for "tini tini"Hands-free by default. tini voice listens for the wake word "tini tini" — a tiny
Whisper model scans the mic; when it hears the phrase, the big model takes over for your
command and speaks the reply. Change or disable it:
TINI_WAKE_WORD="hey tini" tini voice # any phrase, no training
TINI_WAKE_WORD="" tini voice # push-to-talk instead (Enter, speak, Enter)The matcher is ~15 transparent lines with a deterministic eval; it accepts cross-script
variants ("tini tini,わくわく"). A trained openWakeWord model is the efficient v2 upgrade.
A beautiful voice. Out of the box it uses macOS say — and Tini auto-picks the nicest
voice you have, preferring a downloaded Premium/Enhanced one (System Settings ▸ Accessibility
▸ Spoken Content ▸ System Voice) over the robotic built-ins. For the real neural upgrade,
install Kokoro — a fully local, offline British-butler
voice that's picked up automatically, no env var needed:
uv pip install '.[voice-neural]' # neural Kokoro (bm_george); pulls torch (~2GB)Override either engine with TINI_VOICE (a say voice name, or a Kokoro voice like bf_emma).
pip install -e '.[telegram]'
# message @BotFather, /newbot, put the token in .env, then:
make telegramText your bot from anywhere and your laptop runs the turn — long-polling, so no
public URL or webhook. Set TELEGRAM_ALLOWED_USER to lock it to just you.
TINI_APPLE_TOOLS=1 make brief # macOS; grant the permission prompts onceTini reads your real Calendar.app (including events invited by email) and
recent Apple Mail, cross-references your memory, and writes a focus-first briefing
with clickable message:// links. Cron it for a morning greeting:
30 7 * * * cd ~/tini-agent && make brief
It runs through the normal harness, so it animates on the dashboard like any turn.
The local SQLite database and calendar.ics stay authoritative. To also write
create_event results to Google Calendar, install the opt-in extra and configure
Application Default Credentials:
pip install -e '.[gcal]'
# Keep the downloaded client file OUTSIDE the repo — it is only an input to
# gcloud, which stores the resulting credentials in ~/.config/gcloud/.
gcloud auth application-default login \
--client-id-file=~/.config/tini/gcal-client.json \
--scopes=https://www.googleapis.com/auth/calendar.events
TINI_GOOGLE_CALENDAR=1 tiniNothing secret ever needs to live in the repo: the client file is read once by
gcloud, and the credentials it mints land in ~/.config/gcloud/. (.gitignore
also blocks credentials.json and *token*.json as a second line of defence.)
The target defaults to the signed-in user's primary calendar; set
TINI_GOOGLE_CALENDAR_ID for another calendar. list_events still reads the
local database. Google failures never roll back the local event, and attendee
notifications are suppressed (sendUpdates=none).
pip install -e '.[mcp]'Create .tini/mcp.json and any Model Context Protocol server's tools appear to
the agent, namespaced <server>_<tool> (and in the dashboard's Tools ▸ MCP tab):
{"servers": [{"name": "fs", "command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]}]}Node-free demo — a tiny self-contained Python MCP server ships in the repo:
cp examples/mcp.demo.json .tini/mcp.json # points at examples/mcp_demo_server.py
make dashboard # demo_word_count / demo_reverse_text appear in ToolsSame pattern scales to any server, yours or a vendor's — no changes to Tini's code.