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.chat runs 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.