Quickstart
npx vendo init turns your app into an MCP server. Outside agents connect
through your own login; your own backend gets a token from one method.
Four steps, each with a checkpoint. Do not move on until the checkpoint passes.
1
Run init
Already ran Answer the first question — How will people use your agent? — with From
outside agents over MCP — Claude, ChatGPT, Cursor, or any MCP agent
(experimental). That option is labelled experimental; the door itself is what
this page proves.Next it confirms the auth preset it detected — Should the agent act as your
signed-in … user? — which is what wires
vendo init and answered From outside agents over MCP? You
have this step — skip to Open the door.actAs, and then asks where your model
key comes from.Then Where does this app run in dev? — the origin your dev server actually
prints, http://localhost:3000 or whatever port it names.After the posture question below, init asks a few more that are not this page’s
business — the AI judgment pass, and any theme slots it could not read
confidently. Their defaults are fine; press Enter through them.Init writes the wiring: mcp: true into the composition it creates, the
origin-root discovery route beside it, VENDO_BASE_URL into .env.local, and a
.vendo/ directory holding the tool catalog, judgments, policy, overrides and
theme. It adds two package scripts — predev and prebuild, both running
vendo sync so the catalog cannot drift from your code — and installs ai and
@ai-sdk/anthropic. The closing line counts the files it wrote.lib/vendo.ts
auth has to be a preset that carries the oauth half — clerk(), authJs(),
supabase() or auth0(). Plain jwt() does not, and init refuses to write the
door without one. Auth lists them.Checkpoint.
lib/vendo.ts says mcp: true,
app/.well-known/[...vendo]/route.ts exists, and .env.local has a
VENDO_BASE_URL line. If init refused instead, the message names the reason and
the way out — a missing auth preset, or the http-origin-with-Cloud pair above.2
Check the door answers
Start your app and ask the door who it is. It should refuse you, and say where
to go and sign in.That
response
401 is the door working. The www-authenticate header is how every MCP
client discovers your sign-in without being told about it.Checkpoint. A
401 with a www-authenticate header naming your own origin.
A 404 means the app is not serving /api/vendo/mcp — check that init’s wire
route landed. A 200 means something else is answering that path.If you are on the Cloud posture, two more answers are worth knowing. A 400
with "code":"validation" is Vendo Cloud refusing to provision your tenant, and
it repeats the console’s own reason and docs link — read the message, it names
the fix. A 503 with "code":"unavailable" is Cloud itself failing, not your
wiring.3
Connect an outside agent
Your users’ setup page ships with the door. Open it in a browser:It carries the copy-paste config for Claude, ChatGPT and Cursor. Paste one in,
sign in with your app’s own login when the client bounces you there, and ask it
to do something.For Claude Code, the plugin is one command:
Checkpoint. The client lists your tools, and a call runs and comes back with
your own data. Reads and writes both execute — nothing parks, and that is
correct on a fresh install. The policy init writes to
.vendo/policy.json asks
on one thing only, risk: destructive, and a young app often has no tool graded
that way. Approvals are a policy you opt into, not a default the door imposes:
edit that file when you want a call to wait for a person.If a tool answers not-implemented, that means a missing actAs — the door has
no auth material to call your API with. A Cloud problem never looks like this;
it answers 400 or 503 with its own reason, per Step 2.
How the door works explains actAs.4
Mint a token for your own backend
A backend agent has no browser to bounce through, so it asks your composition
for a token instead. One method, two ways to call it.It exchanges a service key to do that. On Vendo Cloud the key is provisioned
with your tenant and there is nothing to do. On the zero-key posture you declare
one — Headless — a cron job, a queue worker, anything with no request in hand — pass
the user id your own product spells:The token lasts ten minutes, is bound to that one person, and has no refresh
path. Every call made with it runs as that user, through the same guard,
policy and audit your own UI answers to. Under the hood it is an RFC 8693
One thing to pass on that first call. Your client abandons a
npx vendo init --service-key writes it, and
Service keys is the whole story.
Without one, tokenFor says so and names the variable to set.Inside a request, pass the request — tokenFor reads who is signed in from the
session cookie, the same way your own pages do:app/api/agent/route.ts
jobs/nightly.ts
urn:ietf:params:oauth:grant-type:token-exchange, which you never have to spell
yourself.A subject that is not a real id — blank, null, undefined, or not a string at
all — is refused at mint rather than handing you a token that dies on the first
tool call.Checkpoint.
tokenFor returns a token string. Its shape tells you which
posture minted it: vmat_… from your own door on the local posture, a raw JWT
(eyJhbG…) from the broker on Vendo Cloud. Both are correct — feed it to a stock MCP
client — Your own agent is the whole loop —
and the call lands in your product under the user you named.tools/call after
60 seconds by default, and generating a screen with vendo_make routinely runs
longer. The door beats notifications/progress every 15 seconds, but the SDK
extends the deadline only when you asked it to — so a client that receives every
frame still gives up at 60 seconds. Ask for both: onprogress is what puts the
progress token on the request, and resetTimeoutOnProgress is what makes the
frames count.Who runs the sign-in
The door needs an OAuth authorization server in front of it. There are two answers, and the door’s URL is the same either way — it derives fromVENDO_BASE_URL and never from a broker, so switching later invalidates nothing
your users already configured.
Local — your app serves its own OAuth (default, no key). Everything above
already did this. Your app is the authorization server, your login is the
sign-in, and nothing leaves your origin. Zero config, and it works on http, so
this is the posture for local development.
Vendo Cloud broker (one answer). With a VENDO_API_KEY in hand, init asks
one more question — How should outside agents sign in? — and answering Vendo
Cloud broker moves the OAuth surface off your domain to a stable
yourcompany.mcp.vendo.run tenant that Vendo provisions on first use. There is
nothing to copy: no console visit, no environment values, no keys. It needs an
https VENDO_BASE_URL, per the warning in Step 1.
To front the door with a broker you run yourself, set both values where you
deploy. An explicit override always wins over the Cloud default.
.env
VENDO_API_KEY and mcp: true moves
its door from local to Cloud-brokered — declare those two variables yourself, or
pass mcp.serviceAuth, to keep it where it is.
When you deploy
SetVENDO_BASE_URL on your platform to the public origin your app answers on.
Discovery, issuer and token audience all derive from it, and a door pointed at
the wrong origin surfaces hours later as “Claude can’t find my server”.
Where to go next
How the door works
Who is calling, how a call reaches your host tools, and what comes back.
mcp + oauth + actAsYour own agent
The stock MCP client, one session per conversation, and the whole loop.
tokenForService keys & broker
Where the key lives, the audit’s
svc: attribution, and rotation.svc:<hash8>