> ## Documentation Index
> Fetch the complete documentation index at: https://vendo-mintlify-9465070f.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# AI SDK

> Spread Vendo's guarded tool pack into a Vercel AI SDK loop you already run, and render what it returns in your own chat.

Keep your loop. Vendo adds the guarded tools and renders what they return.

<Steps>
  <Step title="Install and run init">
    <CodeGroup>
      ```bash npm theme={null}
      npm install @vendoai/vendo
      npx vendo init
      ```

      ```bash pnpm theme={null}
      pnpm add @vendoai/vendo
      pnpm exec vendo init
      ```
    </CodeGroup>

    Answer the first question with **Through my own agent loop (AI SDK / Mastra)**, and take **Vendo Cloud** on the model one. Init reads your repo, writes the wire route, lands a `VENDO_API_KEY` in `.env.local`, and adds Vendo's `serverExternalPackages` entries to your Next config.

    Init wrote `lib/vendo.ts` for you (`src/lib/vendo.ts` when your app lives under `src/`) — the `createVendo` call, and the caller resolver beside it, so your chat route, your wire route, and the pack all share one instance.

    ```ts lib/vendo.ts focus={6} theme={null}
    import { authJs } from "@vendoai/vendo/auth/auth-js";
    import { createVendo } from "@vendoai/vendo/server";

    const auth = authJs();
    export const vendo = createVendo({ auth });
    export const resolvePrincipal = (req: Request) => auth.principal(req);
    ```

    Init wires whichever provider it detects — swap `authJs()` for another preset any time ([Auth](/production/auth) lists all five).

    With no provider yet, init wrote a demo principal in place of a preset, and the resolver beside it hands your chat route that same subject:

    ```ts lib/vendo.ts theme={null}
    import { createVendo } from "@vendoai/vendo/server";

    // init writes both — swap them for
    // your real session lookup.
    const principal = async () => ({
      kind: "user" as const,
      subject: "demo-user",
    });
    export const vendo = createVendo({ principal });
    export const resolvePrincipal = (_req: Request) => principal();
    ```

    Either way, your loop and your wire route have to resolve the same subject — a mismatch has no error; the embed just polls a screen it will never be shown ([Auth](/production/auth)).
  </Step>

  <Step title="Spread the tools in">
    ```ts app/api/chat/route.ts theme={null}
    import { convertToModelMessages, stepCountIs, streamText } from "ai";
    import { vendoTools } from "@vendoai/vendo/ai-sdk";
    import { resolvePrincipal, vendo } from "@/lib/vendo";

    export async function POST(req: Request) {
      const { messages } = await req.json();
      const caller = await resolvePrincipal(req);
      if (!caller) return new Response("Unauthorized", { status: 401 });
      return streamText({
        model: yourModel,            // your model, unchanged
        messages: await convertToModelMessages(messages),
        stopWhen: stepCountIs(5),    // the AI SDK stops after one step by default
        system: "…your system prompt as it is",
        tools: { ...yourTools, ...(await vendoTools(vendo, { principal: caller })) },
      }).toUIMessageStreamResponse();
    }
    ```

    `vendoTools` returns a Promise, so the pack is built per request, inside the handler. Every tool lands under a `vendo_` prefix — one per registered host action, plus `vendo_make` (a live view) and `vendo_delegate` (a whole task for Vendo's own agent); trim the pack with `include` or `exclude` by final tool name.
  </Step>

  <Step title="Render the embeds">
    ```tsx app/page.tsx theme={null}
    import { VendoToolResult } from "@vendoai/vendo/react";

    // in your message-part renderer, for each finished tool part:
    if (part.type === "dynamic-tool" && part.state === "output-available") {
      return <VendoToolResult key={key} output={part.output} />;
    }
    ```

    Nothing to wrap: the embed finds the wire at `/api/vendo` and rides your host session cookie. The shim declares every tool with `dynamicTool`, so they stream as `dynamic-tool` parts, and one component covers plain data, app refs, and approval refs. Full contract: [Embeds in your chat](/existing-agent/embeds).
  </Step>

  <Step title="See it live">
    Run your own chat and ask for something behind your API. The answer comes back as a working card, inside your own bubble.

    Init wrote `VENDO_BASE_URL` into `.env.local`; deployments set the same variable to the public URL, path prefix included ([environment variables](/reference/environment-variables)).

    <Frame>
      <img src="https://mintcdn.com/vendo-mintlify-9465070f/oYTEzUuScYkXt6Yp/images/existing-agents/ai-sdk-dashboard.png?fit=max&auto=format&n=oYTEzUuScYkXt6Yp&q=85&s=3c3b4ca4b2e6e3d838e23dffae980d64" alt="The AI SDK example chat answering a dashboard request with a generated weather comparison screen rendered inline in the assistant message" width="448" height="800" data-path="images/existing-agents/ai-sdk-dashboard.png" />
    </Frame>
  </Step>
</Steps>

## Apps in your product

Your agent can build apps now. Where they land in your product, and how to teach your model when to build one, is one more quickstart: [Apps in your product](/generated/quickstart).

## The full example

[`examples/ai-sdk-agent`](https://github.com/runvendo/vendo/tree/main/examples/ai-sdk-agent) is the stock [AI SDK Next.js chatbot](https://ai-sdk.dev/docs/getting-started/nextjs-app-router) with this diff applied. Every added line sits between `--- vendo` and `--- /vendo` markers, so `grep` shows you the whole integration.
