#An AI agent for your Supabase app
To add an AI agent to an app whose users sign in with Supabase Auth, create the agent from a chat with the Vatio MCP server, then build it into the code with npx @vatio-ai/skills and /vatio-init (steps below). A Supabase Edge Function reads the user's Supabase session and signs a short token with your own key, which the widget passes to Vatio. The agent's private tools call other Edge Functions of yours, which verify that token and read only the signed-in user's rows. Nothing reaches your users until you publish.
The example is a dog-walking app where signed-in owners ask about their pets' walks.
#Add the agent in two steps
Step 1, from any chat. Add the remote MCP server https://vatio.ai/mcp as a connector in Claude, ChatGPT or Cursor, sign in with a Vatio account and ask for the agent. It is created in a preview you can try; nothing reaches your users until you publish (MCP).
Step 2, build it into the Supabase app. In the project, run:
npx @vatio-ai/skills/vatio-initIf you built the app in Lovable or Bolt, connect it to GitHub and open it in Claude Code or Cursor first. Run the first line in a terminal at the root of your app's project, and the second in Claude Code (in Cursor or Codex, ask it to use the vatio-init skill). /vatio-init looks at what already exists, then writes the agent and pushes a preview. The first time it pushes, your browser opens to sign in with email, Google or GitHub. Then ask your coding agent to put the widget where the app starts. You can say what you want after /vatio-init:
/vatio-init an agent that answers from our FAQ and tells signed-in owners when their pets' next walks are, from our Supabase tablesAfter launch, /vatio-improve is the daily routine (After launch).
#What vatio-init finds in a Supabase project
vatio-init reads the project and tells you what it found and what it recommends before writing anything. Then it asks only what the code doesn't answer, such as what the agent must never promise and when a person takes over.
- How your users sign in. A Supabase client and the project URL beside it: the project URL in a
.env, or thesrc/integrations/supabase/client.tsLovable generates. That means an Edge Function that signs the Vatio token. - What the agent could call. Your Edge Functions as
/functions/v1/<name>, or routes of your own server, which read your tables and database functions for the signed-in user. - The docs it could use. FAQ and help pages, policies and your README, as knowledge entries the agent searches before it answers. Vatio doesn't crawl your site.
#Put the agent in your app
Ask your coding agent to add this code; it is shown here so you can review it.
Mount the widget once, where your app starts. The user's token comes from your backend (next section):
import { mountVatioWidget } from "@vatio-ai/widget";
mountVatioWidget({ workspace: "pawpal", token: import.meta.env.VITE_VATIO_TOKEN });The script tag works the same way, before </body> in index.html, with data-workspace and data-token (Web widget). To tell the agent who is signed in, pass the visitor token as data-visitor-token or with identify() (Authentication).
The publishable key (vatpub_…) is meant for page source, and it picks the environment: preview's goes in .env.development, live's in .env.production. Each works only from its own origins, exact scheme://host[:port] values with no wildcards:
vatio keys create publishable --env preview --label dev --origin http://localhost:5173vatio keys create publishable --env live --label web --origin https://pawpal.exampleLovable, Replit, Bolt, v0 and Base44 domains, preview and published, are accepted without listing them; once the app has a domain of its own, list it. Change a key's origins with vatio keys origins PREFIX URL... or in Settings → API keys in the inbox. A page whose origin isn't on the key gets origin_not_allowed, and the browser console names the origin to add. Origins live on the key, never in vatio.yml.
#Let it act for signed-in users
Vatio holds only your public key, so it can check a token but never mint one. Your coding agent sets it up with vatio auth --new-key, which writes identity.pub into the workspace (auth: public_key: identity.pub in vatio.yml) and identity.pem, the private key. Store identity.pem as a Supabase secret for your Edge Functions, never in vatio secrets (Authentication).
Signing the token. Add an Edge Function, say vatio-token, that reads the user's Supabase session (the Authorization header supabase-js sends), and signs a short RS256 token with sub set to the user id and aud set to your workspace slug. The app calls it once the user is signed in and hands the result to the widget. Mechanics, claims and refresh are in Authentication and Sessions and channels.
Reading the user's rows. A tool marked access: private forwards that token as Authorization: Bearer $auth.token to another Edge Function. The function verifies it against identity.pub and queries your tables for $auth.subject, the user id claim. The token is not a Supabase session, so turn Verify JWT off on the functions Vatio calls and do the check yourself. The check that matters now lives in the function: filter every query by the verified sub.
// supabase/functions/my-pets/index.ts: after verifying the tokenconst { data } = await admin .from("pets") .select("name,breed,next_walk") .eq("owner_id", sub);Keep the service-role key inside the function, never in a tool. A private tool cannot take a user id as a parameter either, and the deploy fails if one tries, because the model fills parameters and could be talked into filling in someone else's.
#What your coding agent writes
These files live in your project, so you can review them like the rest of your code: an excerpt of vatio.yml, and the tool.
workspace: pawpalauth: public_key: identity.pubagent: instructions: | You help owners who use Pawpal. Answer questions about walks, prices and cancellations from knowledge_lookup. For an owner's own pets and walks, call my_pets. Never confirm a walk my_pets did not return. tools: [knowledge_lookup, my_pets] knowledge: [faq]
# tools/my_pets.ymldescription: The signed-in owner's pets, with each one's next walk.when_to_use: The owner asks about their own pets or their walks.access: privaterequest: method: GET base_url: https://abcdefghijklmnopqrst.supabase.co path: /functions/v1/my-pets headers: Authorization: "Bearer $auth.token"respond: data: { pets: "$" }To act as well as read, the same kind of file calls POST /functions/v1/book-walk with the same header. The tool has no owner id: the function reads it from the verified token, and returns nobody else's rows.
#Try it, then publish
vatio-init gives you a link to the inbox, with Try the agent open, where you try the preview on WhatsApp, Instagram or a web chat. The web chat is a visitor who isn't signed in, so it only reaches public tools. To try my_pets, run your dev build with preview's widget token and sign in as a test user. Preview can call a staging project's functions while live calls production; sign in to the one preview calls. Ask "when is Luna's next walk?", then for the pets of another owner by email: it should find nothing but your own. In the inbox, each reply shows the tool calls behind it, with their arguments and what your function answered.
When the agent does what you want, tell your coding agent to publish. Nothing reaches your users until you do.
#After launch
Your team reads real conversations in the inbox. On a wrong reply, Report this reply asks what should have happened, and that note becomes a test. Once a day, run /vatio-improve in your project: your coding agent fixes the cause where it lives (the instructions, a knowledge entry, a tool or an Edge Function) and pushes it to preview, and you review the results and approve the update (Improve). The same agent answers on WhatsApp and Instagram (Pricing). There, a sign_in_url under auth: gives private tools a Sign in button that opens your login page, with the widget on it, and the conversation picks up once the user has signed in (Sessions and channels).
#Frequently asked questions
#How do I add an AI agent to an app built on Supabase?
Step 1: add the MCP server https://vatio.ai/mcp as a connector in Claude, ChatGPT or Cursor, sign in and ask for the agent; it is created in a preview. Step 2: in the app's project run npx @vatio-ai/skills, then /vatio-init in Claude Code (in Cursor or Codex, ask it to use that skill). Your coding agent adds an Edge Function that signs the user's token and puts the widget in your app, with a publishable key that allows its origins.
#How does the agent know who is signed in?
A Supabase Edge Function reads the user's Supabase session and signs a short RS256 token with the private key from vatio auth --new-key; the app passes it to the widget as the visitor token. Vatio holds only the public key, so it can check the token but never create one. Details are in Vatio's Authentication docs.
#Can the agent read another user's rows in my Supabase database?
Not when its tools are private and your Edge Functions filter by the verified token. A private tool forwards the token, your function verifies it and reads the user from its sub, and a private tool cannot take a user id as a parameter. Keep the service-role key inside the function, never in a tool.
#Does this work for an app I built in Lovable?
Yes. Connect the Lovable project to GitHub and open it in Claude Code or Cursor. Your coding agent adds the Edge Function that signs the token, and every publishable key accepts Lovable's preview and published domains without listing them.
#Related guides
- Build an AI agent for your app: The two-step path with your coding agent, from the first chat to publishing.
- An AI chat for your web app or site: One script tag puts the agent in your web app or site, themed to your brand and able to recognize signed-in users.
- React apps: The widget mounted in an effect; your backend signs the user's token and your API verifies it.
- Give your Lovable app an agent: Open your Lovable project in Claude Code or Cursor, run the two lines and connect your users.