> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Web SDK

> Embed a governed agent in your React app with the drop-in AgentChat component

The web Assistant SDK is a set of npm packages under the `@metabindai` scope. The fastest path is `@metabindai/agent-ui`: a prebuilt React chat surface that streams from Metabind's Agent proxy, calls your project's tools, and renders Interactive Tool output in sandboxed iframes. Configure it with your project credentials and render `<AgentChat />` — or drop to the lower-level typed streaming client and Vercel AI SDK transport when you want to build your own UI.

<CardGroup cols={2}>
  <Card title="SDK on GitHub" icon="github" href="https://github.com/metabindai/metabind-web">
    `metabind-web` — the web Assistant SDK monorepo (Apache 2.0).
  </Card>

  <Card title="Example app" icon="github" href="https://github.com/metabindai/metabind-web/tree/main/examples/example-metabind-react-app">
    A minimal clone-and-run React chat app built on the published packages.
  </Card>
</CardGroup>

## Packages

| Package                    | What it gives you                                                                                                                                                     |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@metabindai/agent-ui`     | The drop-in chat: `<AgentChat />`, the assistant-ui runtime, the agent transport, and MCP-UI tool rendering. Works standalone on a route or embedded in a host shell. |
| `@metabindai/agent-shell`  | A host-side presentation shell — modal, sidebar, and pill — that embeds `<AgentChat />` in a same-origin iframe and surfaces its bridge signals as React callbacks.   |
| `@metabindai/agent-ai-sdk` | A Vercel AI SDK adapter: `MetabindAgentTransport` drops into `useChat`, with an assistant-ui variant at `@metabindai/agent-ai-sdk/assistant-ui`.                      |
| `@metabindai/agent-core`   | A framework-agnostic client that streams typed events from the Agent proxy. No React or AI SDK dependencies.                                                          |

## Requirements

* React 18.3+ (`react` and `react-dom` are peer dependencies)
* A Metabind project with published tools
* A project-scoped Metabind API key

`@metabindai/agent-ui` and `@metabindai/agent-shell` ship as TypeScript source, so your bundler compiles them alongside your app. The example app uses Vite.

## Start from the example app

The quickest way to a working chat is to copy the example app and point it at your project:

<Steps>
  <Step title="Copy the example app">
    ```bash theme={null}
    npx degit metabindai/metabind-web/examples/example-metabind-react-app my-metabind-chat
    cd my-metabind-chat
    pnpm install
    ```
  </Step>

  <Step title="Get your credentials">
    You need an org ID, a project ID, and a project-scoped API key. The fastest path is the `metabind` CLI:

    ```bash theme={null}
    metabind auth login          # authenticate against your Metabind account
    metabind use                 # bind to a project (or `metabind init` to create one)
    metabind status              # read the bound projectId + orgId
    metabind api-key create      # mint a project API key
    ```
  </Step>

  <Step title="Configure and run">
    Copy `.env.example` to `.env.local`, fill in `VITE_METABIND_ORG_ID`, `VITE_METABIND_PROJECT_ID`, and `VITE_METABIND_API_KEY`, then:

    ```bash theme={null}
    pnpm dev          # http://localhost:5173
    ```
  </Step>
</Steps>

There's no server-side code in the app — the Agent proxy is the backend.

## Add the chat to an existing app

### Install

<CodeGroup>
  ```bash pnpm theme={null}
  pnpm add @metabindai/agent-ui react react-dom
  ```

  ```bash npm theme={null}
  npm install @metabindai/agent-ui react react-dom
  ```
</CodeGroup>

### Serve the sandbox proxy

Copy `sandbox_proxy.html` from the package's `public/` folder into wherever your app serves static files. Interactive Tool UIs render inside it. If you serve it somewhere other than `/sandbox_proxy.html`, point `configureChat({ sandboxUrl })` at it.

### Configure and render

Call `configureChat` once, before `<AgentChat />` mounts, and import the prebuilt stylesheet at your app's entry point:

<CodeGroup>
  ```tsx app.tsx theme={null}
  import { AgentChat, configureChat, DefaultWelcome } from "@metabindai/agent-ui";

  configureChat({
    agent: {
      orgId: import.meta.env.VITE_METABIND_ORG_ID,
      projectId: import.meta.env.VITE_METABIND_PROJECT_ID,
      apiKey: import.meta.env.VITE_METABIND_API_KEY,
    },
    sandboxUrl: "/sandbox_proxy.html",
    welcome: () => (
      <DefaultWelcome
        title="How can I help?"
        subtitle="Ask me anything — I can use the tools wired to this project."
      />
    ),
  });

  export default function App() {
    return <AgentChat />;
  }
  ```

  ```tsx main.tsx theme={null}
  import { StrictMode } from "react";
  import { createRoot } from "react-dom/client";
  import "@metabindai/agent-ui/agent-ui.css";
  import App from "./app.tsx";

  createRoot(document.getElementById("root")!).render(
    <StrictMode>
      <App />
    </StrictMode>,
  );
  ```
</CodeGroup>

Prefer an imperative mount? `mountAgentChat(el, config)` calls `configureChat` for you and returns an unmount cleanup.

### `configureChat` options

| Option                                             | Default                       | Description                                                                                                                                    |
| -------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent.orgId` / `agent.projectId` / `agent.apiKey` | required                      | Your Metabind project and a project-scoped API key. `apiKey` is a string or a sync/async getter, so a backend can broker short-lived tokens.   |
| `agent.baseUrl`                                    | `https://agent.metabind.ai`   | The Agent proxy endpoint.                                                                                                                      |
| `agent.draft`                                      | `false`                       | Target the project's draft (unpublished) tools instead of the published ones.                                                                  |
| `mcp.baseUrl`                                      | `https://mcp.metabind.ai`     | The hosted MCP server endpoint.                                                                                                                |
| `sandboxUrl`                                       | `/sandbox_proxy.html`         | Path or URL to the sandbox proxy you serve.                                                                                                    |
| `welcome`                                          | built-in `<DefaultWelcome />` | Render function for the empty state. Can use `StarterTile` for clickable starter prompts.                                                      |
| `autoStart`                                        | `true`                        | Open the conversation with a hidden opener turn so the agent speaks first, driven by its system prompt. Set `false` to keep the welcome state. |
| `debug`                                            | `false`                       | Show the developer tool-call display (raw arguments and results). `?debug=1` overrides at runtime.                                             |
| `allowAttachments`                                 | `false`                       | Allow attaching media and files in the composer.                                                                                               |
| `tracer`                                           | —                             | Per-turn tracing hook for analytics.                                                                                                           |

## How Interactive Tools render on the web

When the agent calls an Interactive Tool, `@metabindai/agent-ui` fetches the tool's UI resource from your project's MCP server and renders it inside a sandboxed iframe through [MCP-UI](https://github.com/idosal/mcp-ui) (`@mcp-ui/client`), hosted by the `sandbox_proxy.html` you serve. This is the same isolation model MCP hosts like Claude and ChatGPT use on the web: tool UIs can't touch your page's DOM, storage, or credentials.

This differs from the [iOS SDK](/guides/assistant-sdk/ios-sdk) and [Android SDK](/guides/assistant-sdk/android-sdk), where the same tool definitions render as SwiftUI and Jetpack Compose. On the web, rendering is web technology in an isolated sandbox.

## Embed as a modal, sidebar, or pill

To present the chat over an existing page rather than on its own route, add `@metabindai/agent-shell`. It renders a single panel that morphs between a centered modal, a docked sidebar, and a minimized pill, and embeds `<AgentChat />` in a same-origin iframe so the chat's CSS stays isolated from your page.

```bash theme={null}
pnpm add @metabindai/agent-shell @metabindai/agent-ui react react-dom
```

Serve `<AgentChat />` at a same-origin route (for example `/chat`), then point the shell at it:

```tsx theme={null}
import { useAgentShell, AgentShell } from "@metabindai/agent-shell";

function Page() {
  const chat = useAgentShell({
    chatUrl: "/chat",
    context: { systemContext: "The user is on the pricing page." },
    onSignal: (s) => console.log("bridge", s),
  });

  return (
    <>
      <button ref={chat.launchRef} onClick={chat.launch}>
        Ask the assistant
      </button>
      <AgentShell {...chat.shellProps} />
    </>
  );
}
```

`useAgentShell` owns open/close state, the launch-element ref (the panel grows out of it), and the iframe bridge, so you don't wire `postMessage` by hand. The shell seeds conversation context into the chat on load and surfaces a tool's dock requests as React callbacks.

## Build your own UI

If `<AgentChat />` doesn't fit, the two lower-level packages give you the same streaming pipeline without the prebuilt chat.

### With the Vercel AI SDK

`MetabindAgentTransport` from `@metabindai/agent-ai-sdk` drops into `useChat` from `@ai-sdk/react`. It reshapes outbound messages to the proxy's request format and translates the inbound stream into the chunks the AI SDK expects — tools are marked as already executed by the proxy.

```tsx theme={null}
import { useChat } from "@ai-sdk/react";
import { MetabindAgentTransport } from "@metabindai/agent-ai-sdk";

const transport = new MetabindAgentTransport({
  orgId: "my-org",
  projectId: "oak-and-ivory",
  apiKey: projectToken,
});

function Chat() {
  const { messages, sendMessage } = useChat({ transport });
  // render messages; call sendMessage to send a turn
}
```

`ai` and `@ai-sdk/react` are peer dependencies you provide. For an assistant-ui runtime, import the transport from `@metabindai/agent-ai-sdk/assistant-ui` instead (which also needs `@assistant-ui/react-ai-sdk`).

### Framework-agnostic streaming

`@metabindai/agent-core` is a typed client for the Agent proxy with no React or AI SDK dependencies. `client.chat(...)` returns an async iterable of discriminated events — text deltas, tool calls, tool results, and turn boundaries:

```ts theme={null}
import { createAgentClient } from "@metabindai/agent-core";

const client = createAgentClient({ orgId, projectId, apiKey });

for await (const event of client.chat({
  messages: [{ role: "user", content: "What's in stock?" }],
})) {
  // event.type: "text_delta" | "tool_use" | "tool_result" | "message_stop" | ...
}
```

The client also exposes `chatText(...)` (collects a full text response) and `fetchChat(...)` (the raw `Response`, for custom parsers).

## Key custody

The web SDK talks only to the Agent proxy — there's no bring-your-own-key provider on the web. The proxy holds the LLM key and runs the tool loop server-side, so no LLM-provider credential ever reaches the browser. See [LLM provider configuration](/guides/assistant-sdk/llm-provider-configuration).

<Warning>
  Your Metabind API key is inlined into the browser bundle — the same exposure profile as a `Bearer` header on every request. That's acceptable for a **project-scoped** Metabind key, never for raw LLM-provider secrets. For tighter control, pass `agent.apiKey` as an async getter and have your backend mint short-lived tokens.
</Warning>

## Styling

The chat is styled with Tailwind v4, but your app doesn't need Tailwind: import the self-contained prebuilt stylesheet the package compiles at build time.

```ts theme={null}
import "@metabindai/agent-ui/agent-ui.css";
```

It includes a CSS reset, so load it only on the route that serves the chat — or embed the chat through `@metabindai/agent-shell`, which isolates it inside an iframe. If your app is on Tailwind v4 and you want to theme the chat with your own tokens, compile it from source instead — see [the `@metabindai/agent-ui` README](https://github.com/metabindai/metabind-web/tree/main/packages/agent-ui#styling).

## Related

<CardGroup cols={2}>
  <Card title="Assistant SDK overview" icon="rocket" href="/guides/getting-started/embed-an-assistant">
    Conceptual: when to embed vs. connect to an external host.
  </Card>

  <Card title="LLM provider configuration" icon="brain" href="/guides/assistant-sdk/llm-provider-configuration">
    How the Agent proxy holds keys and runs the tool loop.
  </Card>

  <Card title="iOS SDK" icon="apple" href="/guides/assistant-sdk/ios-sdk">
    The same agent rendered as SwiftUI.
  </Card>

  <Card title="Android SDK" icon="android" href="/guides/assistant-sdk/android-sdk">
    The same agent rendered as Jetpack Compose.
  </Card>
</CardGroup>
