Skip to Content
TensorPass
Quickstart

React

Add the connect window and a chat that runs on the visitor's AI to a React app.

This page builds a small React app, served by Vite, with an "AI mode" button and a chat that searches the site's catalog through its tools. It follows the bookshop demo in apps/demo of the AI Connect repository.

You need React 19 or later, and Chrome with AI Wallet installed and one account connected to try it.

Install the packages

npm install @aiconnect/core @aiconnect/react

@aiconnect/react depends on @aiconnect/design, which holds the CSS tokens, so you do not install it yourself.

Wrap the app in the provider

Create one AiConnect client for the page and give it to AiConnectProvider. Import the connect window's stylesheet once.

src/main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { AiConnect } from '@aiconnect/core';
import { AiConnectProvider } from '@aiconnect/react';
import '@aiconnect/react/styles.css';
import { App } from './App.tsx';

// The visitor's AI, through the AI Wallet extension. The client looks for the wallet as soon as it is built.
const client = new AiConnect();

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <AiConnectProvider client={client}>
      <App />
    </AiConnectProvider>
  </StrictMode>,
);

A visitor who approved your site on an earlier visit is connected a moment after the page loads, with no click.

Add the entry point

useAiConnect gives you the connection state and openPicker, which shows the connect window. The window's button calls client.connect(), and the window closes on its own once the wallet connects the site.

src/ConnectButton.tsx
import { useAiConnect } from '@aiconnect/react';

export function ConnectButton() {
  const { state, client, openPicker } = useAiConnect();
  if (state.status === 'connected') {
    return (
      <p>
        Connected with {state.providerName}{state.model ? ` (${state.model})` : ''}{' '}
        <button type="button" onClick={() => client.disconnect()}>Disconnect</button>
      </p>
    );
  }
  return <button type="button" onClick={openPicker}>AI mode</button>;
}

client.disconnect() removes your site from the visitor's approved sites in the wallet. The next connect asks the visitor again.

Chat with your tools

useAiChat runs the conversation on the visitor's account. Give it a system prompt and a tool source. McpToolSource reads the tools from your MCP server; the MCP guide shows how to serve one.

src/Chat.tsx
import { useState } from 'react';
import type { FormEvent } from 'react';
import { McpToolSource } from '@aiconnect/core';
import { useAiChat } from '@aiconnect/react';

const SYSTEM = 'You recommend books from this site\'s catalog. Always search the catalog before recommending.';

// Built once: the source keeps its MCP connection and the tool list it read.
const tools = new McpToolSource('/api/mcp', 'Bookshop');

export function Chat() {
  const { turns, send, busy, error, connected } = useAiChat({ system: SYSTEM, tools });
  const [question, setQuestion] = useState('');

  const submit = (event: FormEvent) => {
    event.preventDefault();
    send(question);
    setQuestion('');
  };

  if (!connected) {
    return <p>Connect your AI to ask for a book that fits your mood.</p>;
  }
  return (
    <section>
      {turns.map((turn, index) => (
        <article key={index}>
          <p>{turn.question}</p>
          {turn.tools.map(({ call, result }) => (
            <pre key={call.id}>{call.name}({JSON.stringify(call.arguments)}) {result ?? '…'}</pre>
          ))}
          <p>{turn.answer}</p>
        </article>
      ))}
      {error ? <p role="alert">{error}</p> : null}
      <form onSubmit={submit}>
        <input value={question} onChange={(event) => setQuestion(event.target.value)} placeholder="A calm book for tonight" />
        <button type="submit" disabled={busy}>Ask</button>
      </form>
    </section>
  );
}

Each turn holds the visitor's question, the answer as it streams, and every tool call with its raw result. Render those results as your own cards when your tools return structured data, as the MCP guide shows.

send does not queue questions. Disable your send button while busy is true.

Try it without a server

Any object with listTools and callTool is a tool source. The demo serves a tiny catalog from memory, which lets you try the chat before your MCP server exists:

src/demoTools.ts
import type { ToolDefinition, ToolSource } from '@aiconnect/core';

const BOOKS = [
  { title: 'Piranesi', author: 'Susanna Clarke', mood: ['dreamy', 'calm', 'mysterious'], pages: 272 },
  { title: 'Gilead', author: 'Marilynne Robinson', mood: ['calm', 'thoughtful', 'hopeful'], pages: 247 },
];

const DEFINITIONS: ToolDefinition[] = [
  {
    name: 'search_books',
    description: 'Find books in the catalog by mood and maximum length.',
    inputSchema: {
      type: 'object',
      properties: {
        mood: { type: 'string', description: 'One mood word, such as calm, funny or adventurous' },
        maxPages: { type: 'number' },
      },
    },
  },
];

export const demoTools: ToolSource = {
  listTools: async () => DEFINITIONS,
  async callTool(name, args) {
    if (name !== 'search_books') {
      throw new Error(`Unknown tool ${name}`);
    }
    const mood = typeof args.mood === 'string' ? args.mood.toLowerCase() : null;
    const maxPages = typeof args.maxPages === 'number' ? args.maxPages : Infinity;
    return JSON.stringify(BOOKS.filter((book) => (!mood || book.mood.includes(mood)) && book.pages <= maxPages));
  },
};

Pass tools: demoTools to useAiChat in place of the McpToolSource.

Run it

  1. Start your dev server on localhost. AI Wallet serves pages on HTTPS and on http://localhost or http://127.0.0.1.
  2. Open the page in Chrome with AI Wallet installed and an account connected in the wallet.
  3. Click AI mode, then Connect AI Wallet. Approve your site in the window the wallet opens.
  4. Ask a question. The answer streams from the account the wallet chose for your site.

Already using an AI SDK

A site that already calls a model through the OpenAI SDK, the Anthropic SDK, Google Gen AI, the Vercel AI SDK, LangChain or LlamaIndex keeps its code. It builds the SDK's client from the same AiConnect client it passes to AiConnectProvider:

import OpenAI from 'openai';
import { AiConnect } from '@aiconnect/core';

const client = new AiConnect();
const openai = new OpenAI(client.sdkOptions);

The AI SDK guide shows the change for each SDK.

Next steps

On this page