@interopio/io-assist-react
v1.0.1
Published
io.Intelligence React client library
Downloads
231
Readme
@interopio/io-assist-react
A full-featured AI assistant component for React applications. Drop a single <IoAssist /> component into your app and get a conversational AI chat interface backed by any AG-UI Protocol compatible agent server — with streaming responses, persistent threads, a prompt library, MCP tool access, working context awareness, and built-in sampling/elicitation handling.
Built on io.Connect and @interopio/ai-web.
Features
- Conversational AI chat — Streaming responses, markdown rendering, code highlighting
- Persistent conversation threads — Thread history with resume, rename, and delete
- Prompt library — Categorized pre-written prompts with favorites (persisted via io.Connect preferences)
- MCP tool access — Built-in io.Intelligence MCP server, extensible with remote and third-party servers
- MCP Apps — Custom HTML applications rendered inline in chat or in io.Connect workspace windows
- Sampling & elicitation — Built-in confirmation/input UI for MCP server requests (replaceable with custom handlers)
- Working context — Live data from io.Connect contexts passed to the agent with every message
- Theming — Automatic dark/light mode sync with io.Connect
Prerequisites
- React 18+
- Node.js 20.19+ (< 22)
- npm 10+
- An AG-UI Protocol compatible agent server (e.g. Mastra)
Installation
npm install @interopio/io-assist-reactreact and react-dom are the only peer dependencies — your app already provides them:
npm install react react-domEverything else is declared as a direct dependency and installed automatically, including the io.Connect platform packages (@interopio/browser, @interopio/desktop), the core intelligence library (@interopio/ai-web), working context (@interopio/working-context), and the UI runtime (zustand, react-markdown, remark-gfm, prism-react-renderer, @fontsource-variable/inter). These are resolved from your node_modules at runtime, not inlined into the package.
You always need to import a platform factory and pass it in your connectConfig — pick the one that matches your environment:
// io.Connect Browser (most common)
import IOBrowser from '@interopio/browser';
// io.Connect Desktop
import IODesktop from '@interopio/desktop';Agent Server
io.Assist requires an AG-UI Protocol compatible agent server to handle chat requests. The recommended way to get started is with Mastra.
Quick setup with Mastra
npx create-mastra@latestThis scaffolds a fully working Mastra project. Follow the prompts, then start the server:
npm run devBy default the server runs on http://localhost:4111 — the same URL used in the Quickstart examples below.
Required environment variables:
# At least one LLM provider key
OPENAI_API_KEY=your-key
ANTHROPIC_API_KEY=your-keyFor full Mastra documentation — including custom agents, tools, memory, and deployment — see mastra.ai/docs.
Quickstart
1. Add the stylesheet
@import "@interopio/io-assist-react/styles";2. Render the component
<IoAssist /> takes two props: a staticConfig (infrastructure settings, known at bootstrap) and a dynamicConfig (the active user's identity and per-request headers, often known only after login).
import { IoAssist, IoAssistStaticConfig, IoAssistDynamicConfig } from '@interopio/io-assist-react';
import IOBrowser from '@interopio/browser';
const staticConfig: IoAssistStaticConfig = {
connectConfig: {
browser: {
factory: IOBrowser,
},
},
aiWebConfig: {
agentServer: {
baseUrl: 'http://localhost:4111',
},
},
};
const dynamicConfig: IoAssistDynamicConfig = {
// id: Your access/auth token of choice — scopes conversation threads
// name: UI name that will be displayed
user: { id: 'yourIdOfChoice', name: 'Jane Doe' },
agentServer: {
headers: {
Authorization: `Bearer ${import.meta.env.VITE_AUTH_TOKEN}`,
},
},
};
export function App() {
return <IoAssist staticConfig={staticConfig} dynamicConfig={dynamicConfig} />;
}3. Run
npm run devio.Assist is live.
Quickstart with Login
This pattern shows how to pair io.Assist with a login flow — the user's identity is only passed to the assistant after authentication. Build the dynamicConfig from your auth state and render <IoAssist /> only once a user is present.
import { useMemo } from 'react';
import { Navigate } from 'react-router-dom';
import { IoAssist, IoAssistDynamicConfig } from '@interopio/io-assist-react';
import { staticConfig } from './configs';
import { useAuth } from './auth';
export function AssistantShell() {
const { userId } = useAuth();
const dynamicConfig = useMemo<IoAssistDynamicConfig | null>(
() => (userId ? { user: { id: userId } } : null),
[userId],
);
if (!dynamicConfig) {
return <Navigate to="/login" replace />;
}
return <IoAssist staticConfig={staticConfig} dynamicConfig={dynamicConfig} />;
}Once the user logs in, dynamicConfig is populated and io.Assist loads threads scoped to that user.
What you get with the minimal config
With just the required fields (staticConfig.connectConfig, staticConfig.aiWebConfig.agentServer.baseUrl, and dynamicConfig.user), io.Assist provides:
- Full conversational AI chat with streaming markdown responses
- Persistent conversation threads (scoped to
user.id) - Automatic agent selection (first available agent from your server)
- Built-in sampling and elicitation UI (confirmation dialogs when MCP servers request them)
- Built-in io.Intelligence MCP server connection
- Dark/light theme sync with io.Connect
- Auto-injected fonts and syntax highlighting
No prompts, no working context, and no MCP Apps — those require additional configuration below.
Configuration
io.Assist uses two separate configuration objects, passed as two distinct props:
IoAssistStaticConfig— infrastructure settings that don't change at runtime (platform factory, agent server, MCP, prompts). Pass it as thestaticConfigprop.IoAssistDynamicConfig— the active user's identity and per-request headers, which may only be known after login. Pass it as thedynamicConfigprop.
Static configuration (IoAssistStaticConfig)
Passed as the staticConfig prop.
Required fields
| Field | Type | Description |
|-------|------|-------------|
| connectConfig | { browser?, desktop? } | io.Connect platform settings. Must include a browser and/or desktop entry, each with a factory (and optional config). |
| aiWebConfig.agentServer.baseUrl | string | Base URL of your AG-UI compatible agent server. |
Optional fields
| Field | Type | Description |
|-------|------|-------------|
| aiWebConfig.agentServer.* | IoAiWeb.AgentServerConfig | Additional agent-server options (retries, backoff, credentials, etc.). |
| aiWebConfig.mcp | IoAiWeb.MCPConfig | MCP configuration — remote servers, MCP Apps, sampling/elicitation overrides. |
| defaultAgentName | string | Agent to select on startup. Falls back to the first available agent if not found. |
| defaultPrompts | IoAssistPromptCategory[] | Categorized prompt library entries. |
| workingContext | IoAiWeb.WorkingContextConfig | Live context collection from io.Connect. |
Dynamic configuration (IoAssistDynamicConfig)
Passed as the dynamicConfig prop. Typically derived from your authentication state.
| Field | Type | Description |
|-------|------|-------------|
| user.id | string | Required. Unique user identifier. Scopes conversation threads — each user sees only their own threads. |
| user.name | string? | Display name shown in the chat UI and thread history. |
| agentServer.headers | Record<string, string>? | Request headers sent with every agent call. This is the only source of request headers — use it for auth tokens or per-user context. |
Configuration Examples
Standard — with prompts and a named agent
import { IoAssistStaticConfig } from '@interopio/io-assist-react';
import IOBrowser from '@interopio/browser';
export const staticConfig: IoAssistStaticConfig = {
connectConfig: {
browser: { factory: IOBrowser },
},
aiWebConfig: {
agentServer: {
baseUrl: 'http://localhost:4111',
},
},
defaultAgentName: 'my-agent',
defaultPrompts: [
{
category: 'General',
prompts: [
{ name: 'Summarize', prompt: 'Please summarize the following content:' },
{ name: 'Explain', prompt: 'Please explain this in simple terms:' },
],
},
{
category: 'Code',
prompts: [
{ name: 'Review Code', prompt: 'Please review this code and suggest improvements:' },
{
name: 'Add Comments',
prompt: 'Annotate this code with detailed comments:',
iconResource: {
type: 'svg',
data: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="..."/></svg>',
},
},
],
},
],
};What this adds over minimal:
- Named agent pre-selected at startup
- Prompt library with two categories — users can browse, search, and favorite prompts
Advanced — custom handlers, MCP Apps, working context
import { IoAssistStaticConfig } from '@interopio/io-assist-react';
import { IoAiWeb } from '@interopio/ai-web';
import IOBrowser from '@interopio/browser';
import { IoIntelWorkingContextFactory, IoIntelWorkingContext } from '@interopio/working-context';
export const staticConfig: IoAssistStaticConfig = {
connectConfig: {
browser: { factory: IOBrowser },
},
aiWebConfig: {
agentServer: {
baseUrl: 'http://localhost:4111',
},
mcp: {
// Custom sampling handler — replaces the built-in confirmation dialog
clientsConfig: {
enforceStrictCapabilities: false,
capabilities: {
sampling: {
handler: async (
serverId: string,
params: IoAiWeb.SamplingRequestParams,
): Promise<IoAiWeb.SamplingSuccessResponse> => {
// Your custom logic — show your own UI, call your own APIs, etc.
return {
model: 'gpt-4',
role: 'assistant',
content: { type: 'text', text: 'Custom sampling response' },
stopReason: 'endTurn',
};
},
},
elicitation: {
handler: async (
serverId: string,
params: IoAiWeb.ElicitationRequestParams,
): Promise<IoAiWeb.ElicitationResponse> => {
// Your custom logic — build a form, collect user input, etc.
return { action: 'accept', content: { confirmed: true } };
},
},
// Required for MCP Apps
extensions: {
'io.modelcontextprotocol/ui': {
mimeTypes: ['text/html;profile=mcp-app'],
},
},
},
},
// MCP Apps — interactive UI panels in chat or workspace windows
mcpApps: {
sandboxProxyUrl: 'http://localhost:6565/index.html',
displayMode: 'workspace', // or 'inline'
},
// Remote MCP servers
ioIntel: {
remote: {
streamableHttp: {
url: 'http://localhost:8989/mcp',
name: 'remote-io-mcp-server',
},
},
},
remoteServers: [
{
streamableHttp: {
url: 'http://localhost:8081/mcp',
name: 'third-party-server',
},
},
],
},
},
defaultAgentName: 'my-agent',
defaultPrompts: [
{
category: 'General',
prompts: [
{ name: 'Summarize', prompt: 'Please summarize the following content:' },
],
},
],
workingContext: {
factory: IoIntelWorkingContextFactory,
config: {
schema: {
userId: {
type: 'string',
description: "Current user's identifier",
source: {
context: {
location: { global: { names: ['UserSession'] } },
path: 'user.id',
},
},
},
selectedClient: {
type: 'string',
description: 'Client currently selected in the workspace',
source: {
context: {
location: { workspace: {} },
path: 'client.name',
},
},
},
},
} as IoIntelWorkingContext.Config,
},
};What this adds over standard:
- Custom sampling handler — replaces the built-in confirmation dialog. When an MCP server makes a sampling request, your handler runs instead.
- Custom elicitation handler — replaces the built-in elicitation dialog. When an MCP server requests user input, your handler runs instead.
- MCP Apps — interactive HTML panels that MCP tools can render inline in chat or in io.Connect workspace windows. Requires both
mcpAppsconfig and theio.modelcontextprotocol/uiextension. - Remote MCP servers — switch the io.Intelligence MCP to a remote endpoint and/or add third-party MCP servers.
- Working context — io.Assist reads live data from io.Connect contexts and passes it to the agent with every message.
Component usage: In all examples above, pass the config object as the
staticConfigprop and the user identity as thedynamicConfigprop:<IoAssist staticConfig={staticConfig} dynamicConfig={dynamicConfig} />. See Quickstart with Login for a full component example.
MCP Apps
MCP Apps are fully interactive UI applications that MCP tools can display directly inside the assistant. Instead of returning only text, a tool can ship a complete HTML application that the user interacts with in context.
Requirements — two config sections must both be present under aiWebConfig.mcp:
mcpApps— enables the MCP Apps runtime:mcpApps: { sandboxProxyUrl: 'http://localhost:6565/index.html', displayMode: 'workspace', // 'workspace' | 'inline' (optional, auto-detects if omitted) },extensions— tells MCP servers that this client supports UI:capabilities: { extensions: { 'io.modelcontextprotocol/ui': { mimeTypes: ['text/html;profile=mcp-app'], }, }, },
Without both, MCP servers will not expose UI metadata on tool definitions and no apps will be created.
Display modes:
| Mode | Behavior |
|------|----------|
| 'inline' | App renders inside the chat message flow |
| 'workspace' | App opens in a separate io.Connect workspace window (falls back to inline if workspaces are unavailable) |
Sandbox proxy — MCP Apps run inside a sandboxed iframe. The sandboxProxyUrl points to an HTML file that bridges communication between the host and the app. This proxy page must be served alongside your application.
Sampling & Elicitation
When an MCP server sends a sampling or elicitation request:
| Scenario | Behavior | |----------|----------| | No custom handler | io.Assist shows a built-in confirmation dialog (uses an io.Connect modal if available, otherwise an in-app overlay) | | Custom handler provided | Your handler function runs instead — full control over UI and response | | Request from a background thread | Automatically rejected (the user must be on the active thread the request targets) |
Sampling — the MCP server asks the client to generate a model response. The built-in handler shows a "Continue / Cancel" dialog. On accept, it calls the selected agent's generate() and returns the result as a SamplingSuccessResponse. On decline, it returns an error response.
Elicitation — the MCP server asks the client to collect user input. The built-in handler presents accept/decline/cancel options.
Custom handlers are configured under aiWebConfig.mcp.clientsConfig.capabilities — see the advanced configuration example for the handler signatures.
Prompt Icons
Each prompt can have an optional iconResource. Three formats are supported:
| Type | Example | Notes |
|------|---------|-------|
| 'svg' | '<svg xmlns="..." viewBox="0 0 24 24">...</svg>' | Sanitized: fill, width, height stripped; hardcoded colors → currentColor |
| 'url' | '/icons/summarize.svg' | Must be an absolute URL or absolute path from the document root |
| 'data-url' | 'data:image/svg+xml;base64,...' | Base64-encoded SVG, PNG, or JPEG |
Prompts without an icon show a default icon.
Public API
The package exposes a deliberately small surface — one component and the two config types it consumes:
import { IoAssist } from '@interopio/io-assist-react';
import type { IoAssistStaticConfig, IoAssistDynamicConfig } from '@interopio/io-assist-react';| Export | Kind | Description |
|--------|------|-------------|
| IoAssist | Component | The root component. Takes staticConfig and dynamicConfig props. |
| IoAssistStaticConfig | Type | Static configuration shape for the staticConfig prop. |
| IoAssistDynamicConfig | Type | Runtime configuration shape for the dynamicConfig prop. |
The prompt, icon, and working-context shapes referenced in the tables above are structural — TypeScript infers them from the config-object literals you write, so you don't need to import them separately.
The stylesheet is exported from the ./styles subpath:
@import "@interopio/io-assist-react/styles";Related Packages
- @interopio/ai-web — Core intelligence library
- @interopio/io-assist-ng — The Angular equivalent of this component
- @interopio/browser — io.Connect Browser platform
- @interopio/desktop — io.Connect Desktop platform
- @interopio/working-context — Working context collection
License
MIT
