Tool runtime
Your tool runs inside Stevai. It never asks the user to set anything up, and it never touches their credentials — Stevai hands it an already‑authenticated context.
Zero setup, by design
When a user gets your tool, it just works. You do not collect an API key, a token, or any credential from the user. At run time Stevai injects a ToolContext that is already signed in as the current user and scoped to their budget. Your tool calls that context; Stevai does the authenticating, the budget metering, and the permission enforcement.
- You never see the user's secrets. Every AI and connector call is mediated by Stevai — the token stays server‑side.
- Local‑first.
ai.chatruns on the user's own device model where it can (free, private); network use draws from the user's budget. - Contained. Your tool can only do what its manifest declared. Anything more is blocked at review and at run time.
The context
typescript
interface ToolContext {
user: { id: string };
ai: {
// runs the user's model on their budget, metered by Stevai
chat(messages: { role: "system" | "user" | "assistant"; content: string }[],
opts?: { region?: string }): Promise<{ reply: string }>;
};
connections: {
has(connectorId: string): boolean;
// an authorized call through a connector the user granted (no token exposed)
call(connectorId: string, req: { method: string; path: string; body?: unknown }): Promise<unknown>;
};
storage: {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
};
}A tool
Export a function; Stevai invokes it with the input and the injected context.
tool.ts
export default async function run(input: { text: string }, ctx: ToolContext) {
// Uses the user's model — no API key, no setup, metered to their budget.
const { reply } = await ctx.ai.chat([
{ role: "system", content: "Summarise the note in 3 bullet points." },
{ role: "user", content: input.text },
]);
return { summary: reply };
}Using a connector
tool.ts
export default async function run(_input: unknown, ctx: ToolContext) {
if (!ctx.connections.has("gmail")) {
// The user grants Gmail ONCE via OAuth; the store prompts them automatically.
return { needsConnection: "gmail" };
}
// Stevai attaches the user's authorized token — the tool never sees it.
const inbox = await ctx.connections.call("gmail", { method: "GET", path: "/messages?limit=20" });
const { reply } = await ctx.ai.chat([
{ role: "system", content: "Summarise these emails and suggest labels." },
{ role: "user", content: JSON.stringify(inbox) },
]);
return { digest: reply };
}See
for how the one‑time OAuth grant and the encrypted vault work.