The AI, bots and capabilities
The builder AI (ready) #
The AI chat on the dashboard builds the space. It can do anything HQ can, and it never changes anything by itself: it proposes, a person approves, every version can be undone. Code: convex/ai.ts; how to build, in words for any AI, is convex/lib/guide.ts; its tools are described once in convex/lib/capabilities/tools.ts and shared with the MCP server.
- Someone with
managesends a message (ai.send). - The server sends Claude the build guide (blocks, kits, collections, games, ledger, styles, rules), the current blueprint and recent messages. The guide part stays the same between requests, so it's cached and cheap.
- Claude calls tools. Changes are applied to a copy and run through every check; errors go back to Claude to fix, up to 6 rounds.
- A valid change is stored as a pending proposal; the person taps Approve in the chat or in HQ.
| Tool | Does |
|---|---|
propose_change({ summary, ops }) | Proposes JSON Patch ops on the blueprint |
add_kit | Proposes adding a kit (taking one out is done in HQ) |
list_records | Reads a collection's records |
list_connections / call_connection | Uses the space's outside services (Capabilities) |
list_photos({ feed?, tag?, limit? }) | Reads photos (ref, caption, #tags, shape, date) to build photo books; read-only, manage (MCP scope data.read) |
search_miniapps / view_miniapp / add_miniapp | Searches the public mini-app library, reads one (with its HTML if asked), and proposes adding or remixing it (Mini-apps) |
list_reviews / answer_review / moderate_review | Reads a board's or subject's reviews (data.read), writes the space's public answer, and approves, hides or keeps a review (data.write); all need manage. No tool writes a review, so an AI or a key can never fake one (Engines) |
get_ai_usage({ day? }) | Today's AI use by person, the space and bots, against the limits, and whose key pays; manage (AI limits) |
search_templates / view_template / add_template / remove_template / save_template | Searches the template and kit library (mine for your own), reads one (source), proposes adding or taking one out, and saves pages or the space privately (preview first). No tool publishes (Templates) |
search({ query, sources?, collection?, stream?, feed?, limit? }) | Searches the space (pages, records, messages, posts, people, files) and says where each result lives; read-only, data.read. Over MCP it searches as the token's person; the in-app AI never sees direct chats or private groups (docs/SEARCH.md) |
list_notes / remember_note / forget_note | The caller's AI notes in this space (only where /ai/remember is on) (AI jobs) |
list_ai_jobs / ask_space_ai / cancel_ai_job | MCP only: the space's latest AI jobs, hand a request to the space's own AI, stop a job (AI jobs) |
list_invites / create_invite / revoke_invite / list_referrals / recheck_referrals | Scope people: invite links and who invited whom (People) |
my_notifications / change_my_notifications / undo_my_notifications / my_inbox / send_notification | Scope me: the caller's own notification choices and inbox; send_notification (scope notify) is managers only, 10 an hour, outside AIs only (Notifications) |
list_reports / check_text / act_on_report / report / block_person / list_blocked / safety_admin | The review queue, the word filter, acting on a report (new scope moderate), reporting and blocking. The in-app AI reads the queue and tries the filter, but only a person moderates (docs/SAFETY.md) |
get_web / check_handle / check_web_domain / preview_web_page | The space's web address and pages, whether a handle is free, an own domain's DNS, and what a visitor sees at a path (docs/WEB.md) |
account_overview / account_request_deletion / account_confirm_deletion | Scope account: the emailed code is the person's approval; email and password changes stay in the app (Account) |
read_conversation / post_message / notify / give / run_automation / ask_bot / list_runs / list_waiting / decide_action | Bots and automations: read and post in a conversation, notify, give Ledger units, start an automation, ask a bot, see runs and what's waiting, decide a waiting action (Bots and automations) |
web_search / web_fetch | Searches the web and reads pages, only in spaces with the Web research kit |
It never approves its own proposals and never writes records itself.
Which key pays: each space connects its own Claude key, saved in the vault with use ai, from the ai.setup block at the top of HQ or the Connections page. Until then the AI chat asks the owner to connect one. The space's own limits per person and role still apply (AI limits).
How it runs: every reply is an AI job that streams in, shows a progress line, can be stopped, and keeps going in the background for long builds; /ai/model picks auto, small (fast) or large (careful), and the AI can keep short notes on how each person likes things built. Detail: AI jobs. Keys never reach a device. Safety detail: Security.
What it knows about feeds: FEEDS_GUIDE (convex/kits/social/guide.ts, part of the kits section of the guide) covers the /feeds settings, the feed blocks and the switcher, the feed.<key> words, the feed recipes, how to add a feed and its page, and how a whole social app is put together.
What it knows about mini-apps: the guide's "apps" part (convex/lib/miniapps/guide.ts, built from the bridge's call list) covers the /apps shape, needs, the sandbox and every bridge call. It can write, place and remix mini-apps through proposals, but it can't publish one to the public library; only a manager can, and an admin reviews it.
What the AI is told it must do: build abstract, reusable pieces; keep looks in styles; give every block a unique id; never remove the HQ page; put games in game definitions or mini-apps with no network calls; keep proposals small; keep kids spaces kid-safe.
How to ask well #
- Say what it's for, not how to build it: "a page where my class signs up for a time slot".
- One thing at a time; approve, then ask for the next.
- Name the look in words ("warm, paper, serif") and let it write the style.
- If a proposal is wrong, say what's wrong; it changes the proposal. Undo is always there.
Planned for the builder AI #
- One tool per engine (pages, data, apps, media, notifications, automations), all still through propose and approve.
- Analytics totals (never raw personal data) so it can suggest improvements.
- Making images and video (needs a second provider, added as a capability).
Bots and automations #
Bots are AI teammates in the space and automations are rules: when this happens, if these checks pass, do these steps. Both are blueprint entries (/bots, /automations) made by chat, by hand or over MCP, and every change is a proposal a person approves. Full detail: docs/BOTS.md; the engine is convex/kits/bots/ (its README.md, rules in model.ts).
{
"bots": [{ "id": "helper", "name": "Helper", "role": "bot", "instructions": "Answer briefly.",
"tools": ["read_conversation", "post_message", "list_records"], "approval": "ask" }],
"automations": [{ "id": "helper-answers", "name": "Helper answers mentions",
"when": { "event": "post", "mentions": "helper" },
"do": [{ "do": "ask", "bot": "helper", "prompt": "{{person.name}} asked: {{text}}" },
{ "do": "post", "reply": true, "text": "{{last}}", "as": "helper" }] }]
}
- Bot fields:
id,name,about,icon,role,instructions,tools,approval(askorauto),askFor,approvers,model(smallorlarge),perDay,on. Thebuilderentry is the AI chat itself: only its name, instructions and model can be set. - Tools: any tool from the one tool list (the same tools MCP clients and the AI chat use), except approving. A tool runs as the bot's own account, so its role applies. Tools that change something follow the approval rules, and proposals always wait for a person. In kids spaces bots always ask a grown-up.
- Triggers (
when.event):record,record.changed,post,join,booking,play,review, any event a kit reports throughcount(), plusschedule(every 15m, 30m, hour, day, week or month, withat,dayandtimezone),webhookandmanual. Other keys filter on the event's fields (collection,feed,board…);mentionsnames a bot;fromBotshears bots too. Kit authors: pass the event's fields ascount()'s optionalaboutargument so automations can filter on them. - Checks (
if):[{ path, is | not | contains | gt | lt | exists }]. - Steps (
do):post,add_record,update_record,notify,give(Ledger units, never money),ask(a bot thinks; its answer is{{last}}),wait,call(a connection),tool(any tool, as a bot). Text is a template:{{text}},{{person.name}},{{data.x}},{{last}},{{date}},{{space.name}}. - Approval: words written into an automation go as written; words a bot wrote follow its approval;
as: "approver"makes a draft the approver sends as themselves;approval: "ask"on an automation makes every step wait. - Safety limits: an automation never starts itself again, chains stop after 3 rounds, each bot has a daily cap (50 by default) and each automation one too (200), and webhooks take 60 a minute and 16 KB.
- Webhook:
POST {CONVEX_SITE_URL}/hooks/<secret>with a JSON body starts an automation; it answers 202, 404, 409, 413 or 429. The address is made with abots.hooksblock and shown once.
Blocks: bots.list, bots.waiting, bots.runs, bots.run, bots.ask, bots.hooks. The Bots kit adds a Bots page; recipes: Helper bot, Welcome message, Reply drafts, Daily digest, Coins for posts. Bot jobs run on the AI jobs engine and count against the space's AI limits.
Capabilities #
Ready. Full detail and setup: docs/CAPABILITIES.md. People get the screens with the Connections kit (hq.connections).
Build from anywhere. Zvessa is an MCP server at {CONVEX_SITE_URL}/mcp. Any MCP client (Claude Desktop, Claude Code, Cursor, another app's AI) builds a space with the same tools as the in-app AI, using an access token made on the Connections page:
claude mcp add --transport http zvessa https://your-backend/mcp --header "Authorization: Bearer mbk_…"
A token belongs to one person in one space and can do what both its scopes (read, propose, approve, data.read, data.write, connections) and the person's current role allow. New tokens get read + propose: the client proposes, a person approves in the app.
Your own keys. A space's keys live in a vault on the server, sealed with AES-256-GCM, each limited to the hosts it may go to. No query returns a value; the AI, bots and mini-apps see a key's name, never the key.
Outside services. blueprint.connections lists remote MCP servers (kind: "mcp") and HTTPS APIs (kind: "http") with the vault key each uses. The AI can propose one; a person approves. Only read-only tools or GET calls run unless the person lists more. Addresses must be public HTTPS, so a connection can't reach the server's own network. Local (stdio) MCP servers can't run on the backend; connect the remote version.
Planned: auto-post to social accounts, scheduled jobs that call connections, and the Automations kit.