@interopio/ai-web
v1.1.1
Published
io.Intelligence web service client library
Readme
io.Intelligence Web SDK
A TypeScript/JavaScript library for building AI Assistant and Co-Pilot web applications with support for Model Context Protocol (MCP) connections and the io.Intelligence server.
Table of Contents
Introduction
@interopio/ai-web is a library for building AI-powered web applications on the io.Connect platform. It provides:
- MCP Host Management: Manage multiple MCP connections to both io.Intelligence MCP Server and third-party MCP servers via the Streamable HTTP transport.
- Agent Execution: Stream or generate responses from configured AI agents, with automatic multi-turn tool execution.
- Thread Persistence: Create, list, and manage persistent conversation threads stored on the io.Intelligence server.
- Tool and Resource Discovery: Discover and invoke tools and resources exposed by connected MCP servers.
- MCP Apps: Render interactive web applications returned by MCP tool calls, in either inline iframe or io.Connect Workspace mode.
- Working Context Integration: Optionally collect live context from io.Connect (global, workspace, and channel contexts) and inject it into agent system prompts.
Target Audience
This library is designed for developers building:
- AI-powered web applications running inside io.Connect Browser or io.Connect Desktop
- Co-pilot and assistant interfaces
- Multi-agent conversational systems
- Applications that expose or consume MCP tools and resources
Installation
npm install @interopio/ai-webPeer Dependencies
The library requires one of the following io.Connect packages at runtime:
# For browser applications
npm install @interopio/browser
# For desktop applications
npm install @interopio/desktopOptionally, install the Working Context package to enable context harvesting from io.Connect:
npm install @interopio/working-contextQuick Start
import { IoAiWebFactory } from "@interopio/ai-web";
import IOBrowser from "@interopio/browser";
const io = await IOBrowser();
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://your-intel-server.com",
headers: {
Authorization: "Bearer YOUR_TOKEN",
},
},
mcp: {
clientsConfig: {
capabilities: {},
},
},
});
// List available agents
const agents = await intelWeb.agents.list();
const agent = agents[0];
// Create a conversation thread
const thread = await intelWeb.threads.create({
agentId: agent.id,
resourceId: "user-123",
title: "My First Chat",
});
// Stream a response
const runHandle = await agent.stream({
messages: "Hello! What can you help me with today?",
memory: { thread: thread.id },
resourceId: "user-123",
});
runHandle.subscribe({
next: (event) => {
if (event.type === "TEXT_MESSAGE_CONTENT") {
process.stdout.write((event as any).delta ?? "");
}
},
complete: () => console.log("\nDone."),
error: (err) => console.error("Error:", err),
});Core Concepts
MCP (Model Context Protocol)
MCP is an open protocol for connecting AI applications to context servers. @interopio/ai-web acts as an MCP host, managing one or more MCP client connections. The library supports two transport types:
- Web Client (
ioIntel.web): Connects to the io.Intelligence MCP server running inside the io.Connect environment using the@interopio/mcp-webclient. Requires@interopio/browserand the io.Intelligence application to be running. - Streamable HTTP (
ioIntel.remoteandremoteServers): Connects to any MCP server over HTTP using the MCP Streamable HTTP transport.
When both web and remote transports are configured for the io.Intelligence server, the hasPriority flag determines which is attempted first, with automatic fallback to the other. By default, the web client has priority (hasPriority: true).
Agents
Agents are AI models registered on the io.Intelligence server. You list available agents and call them directly:
stream(params): Starts a streaming run and returns aStreamResponsehandle. Subscribe to receive AG-UI events in real-time. The library handles multi-turn tool execution automatically.generate(params): Convenience wrapper overstream()that assembles the full text response and resolves a promise when the run completes.abortOperation(threadId): Cancels all active runs associated with the given thread.
Threads
Threads are persistent conversation sessions stored on the io.Intelligence server. Each thread:
- Belongs to a resource (identified by
resourceId, typically a user or session identifier) - Has an associated agent (identified by
agentIdwhen listed) - Stores message history
- Can carry an optional title and metadata
Tools
Tools are functions that agents can invoke during a run. Tools are discovered from connected MCP servers and automatically made available to agents. You can:
- List all available tools and their schemas
- Enable or disable individual tools
- Call tools directly from your application code
Tool deduplication is applied automatically: if multiple connected MCP servers expose a tool with the same name, only the first occurrence is kept and a warning is logged.
Resources
Resources are MCP-defined data sources exposed by connected MCP servers. Resources have a URI, name, and optional MIME type. You can:
- List all available resources
- Read the content of a specific resource by URI
MCP Apps
MCP Apps are interactive web applications embedded in AI responses. When an agent calls an MCP tool that returns an HTML UI resource, the library can render that UI as either:
- Inline: An iframe embedded in the host page
- Workspace: A window opened in an io.Connect Workspace (requires io.Connect Workspace support)
MCP Apps require a sandbox proxy HTML page to be served alongside your application and configured via mcp.mcpApps.sandboxProxyUrl. The sandbox proxy isolates the guest application for security.
Working Context
Working Context is an optional feature that collects live context data from the io.Connect environment (global contexts, workspace contexts, channel contexts) and automatically injects it into agent system prompts. This allows agents to be aware of what the user is currently viewing or working on.
API Reference
Factory Function
IoAiWebFactory(io, config)
Initializes the library and returns the API object.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| io | IOConnectBrowser.API \| IOConnectDesktop.API | Yes | The io.Connect API instance |
| config | IoAiWeb.WebConfig | Yes | Library configuration |
Returns: Promise<IoAiWeb.API>
Throws: Throws if config.agentServer.baseUrl is not a valid URL, if config.mcp.remoteServers contains duplicate names, or if MCP Apps configuration is invalid.
Example:
import { IoAiWebFactory } from "@interopio/ai-web";
import IOBrowser from "@interopio/browser";
const io = await IOBrowser();
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://your-intel-server.com",
},
mcp: {
clientsConfig: {
capabilities: {},
},
},
});Agents API
Accessed via intelWeb.agents.
agents.list()
Returns all agents registered on the io.Intelligence server.
Returns: Promise<Agent[]>
Example:
const agents = await intelWeb.agents.list();
agents.forEach((agent) => {
console.log(`${agent.name} — model: ${agent.modelId}, provider: ${agent.provider}`);
});Agent object
Each agent returned by list() has the following shape:
Properties:
| Property | Type | Description |
|----------|------|-------------|
| id | string | Unique agent identifier |
| name | string | Human-readable agent name |
| modelId | string | Underlying model identifier |
| modelVersion | string | Model version string |
| provider | string | Model provider (e.g., "openai", "anthropic") |
Methods:
agent.stream(params)
Starts a streaming run. The library sends the messages and tool schemas to the io.Intelligence server, streams AG-UI events back to subscribers in real-time, executes any tool calls locally, feeds results back to the server, and continues until the run finishes.
Parameters:
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| params.messages | string \| AgentMessage[] | Yes | — | Messages to send. A plain string is treated as a single user message. |
| params.memory | MemoryConfig | No | — | Memory options forwarded to the backend. Use memory.thread to enable conversation continuity. |
| params.resourceId | string | No | — | Resource identifier (user/session) for context scoping. |
| params.structuredOutput | { schema?: unknown } | No | — | When provided, guides the LLM to respond with JSON matching the given schema. |
| params.tools | { autoIncludeEnabled?: boolean } | No | { autoIncludeEnabled: true } | Set autoIncludeEnabled: false to run without any MCP tools. |
Returns: Promise<StreamResponse>
The StreamResponse handle has two methods:
subscribe(observer): Registers an observer. Returns aStreamSubscriptionwith anunsubscribe()method.abort(): Cancels the run immediately.
The observer receives the following callbacks:
| Callback | Description |
|----------|-------------|
| next(event) | Called for each AG-UI StreamEvent |
| error(err) | Called if the run encounters an error |
| abort() | Called if the run is aborted |
| complete() | Called when the run finishes successfully |
Stream event types (event.type):
| Type | Description |
|------|-------------|
| RUN_STARTED | The run has been accepted by the server |
| RUN_FINISHED | The run completed without error |
| RUN_ERROR | The run terminated with an error |
| TEXT_MESSAGE_START | A text message output started |
| TEXT_MESSAGE_CONTENT | A delta of text content (check event.delta) |
| TEXT_MESSAGE_END | A text message output finished |
| TOOL_CALL_START | An agent tool call started |
| TOOL_CALL_ARGS | Arguments for a tool call (streaming) |
| TOOL_CALL_END | A tool call finished |
| TOOL_CALL_RESULT | The result of a tool call |
| STEP_STARTED | An agent reasoning step started |
| STEP_FINISHED | An agent reasoning step finished |
Example:
const runHandle = await agent.stream({
messages: [
{ role: "user", content: "What is the weather in London?" },
],
memory: { thread: "thread-abc-123" },
resourceId: "user-456",
});
const subscription = runHandle.subscribe({
next: (event) => {
if (event.type === "TEXT_MESSAGE_CONTENT") {
process.stdout.write((event as any).delta ?? "");
}
},
complete: () => console.log("\nRun complete"),
error: (err) => console.error("Run error:", err.message),
abort: () => console.log("Run was aborted"),
});
// To cancel the run:
// runHandle.abort();
// To stop receiving events without cancelling:
// subscription.unsubscribe();agent.generate(params)
Convenience wrapper over stream() that collects all TEXT_MESSAGE_CONTENT deltas and resolves when the run completes. The same StreamParams type is accepted.
Parameters: Same as stream().
Returns: Promise<GenerateResponse>
interface GenerateResponse {
text: string; // the full assembled text response
}Throws: Rejects with a DOMException (AbortError) if the run is aborted before completion.
Example:
const response = await agent.generate({
messages: "Summarize the quarterly report.",
memory: { thread: "thread-abc-123" },
});
console.log(response.text);agent.abortOperation(threadId)
Cancels all active streaming runs associated with the given thread. Runs that have already completed are unaffected.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| threadId | string | Yes | Thread identifier |
Returns: void
Example:
agent.abortOperation("thread-abc-123");Threads API
Accessed via intelWeb.threads.
threads.list(params)
Lists all threads for a given resource and agent.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.resourceId | string | Yes | Resource identifier |
| params.agentId | string | Yes | Agent identifier |
Returns: Promise<Thread[]>
Example:
const threads = await intelWeb.threads.list({
resourceId: "user-123",
agentId: agent.id,
});threads.create(params)
Creates a new thread on the io.Intelligence server.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.agentId | string | Yes | Agent identifier |
| params.resourceId | string | Yes | Resource identifier |
| params.title | string | No | Thread title |
| params.metadata | Record<string, any> | No | Arbitrary metadata |
| params.threadId | string | No | Specify a custom thread ID instead of letting the server generate one |
Returns: Promise<Thread>
Example:
const thread = await intelWeb.threads.create({
agentId: agent.id,
resourceId: "user-123",
title: "Support Chat",
metadata: { topic: "billing" },
});
console.log(`Thread created: ${thread.id}`);threads.deleteThreadState(params)
Deletes the local io.Connect Preferences state associated with a thread. This removes any MCP App state (e.g., saved tool call records) stored in io.Connect Preferences for this thread. It does not delete the thread itself from the server.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.threadId | string | Yes | Thread identifier |
Returns: Promise<void>
Thread object
Properties:
| Property | Type | Description |
|----------|------|-------------|
| id | string | Thread identifier |
| resourceId | string | Resource identifier |
| createdAt | Date | Creation timestamp |
| updatedAt | Date | Last update timestamp |
| title | string \| undefined | Thread title |
Methods:
thread.update(params)
Updates thread properties on the server.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.title | string | No | New title |
| params.metadata | Record<string, any> | No | New metadata |
Returns: Promise<void>
await thread.update({ title: "Resolved — Billing Query" });thread.delete()
Permanently deletes the thread and its message history from the server.
Returns: Promise<void>
await thread.delete();thread.getMessages(params)
Retrieves the message history for the thread.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.limit | number | No | Maximum number of messages to return |
Returns: Promise<GetMessagesResponse>
interface GetMessagesResponse {
messages: any[]; // raw message objects from the server
}Example:
const { messages } = await thread.getMessages({ limit: 50 });
console.log(`Thread has ${messages.length} messages`);Tools API
Accessed via intelWeb.tools.
tools.list()
Returns all tools discovered from connected MCP servers.
Returns: Promise<Tool[]>
Each tool object has:
| Property | Type | Description |
|----------|------|-------------|
| name | string | Tool name (unique across all servers) |
| title | string \| undefined | Display title |
| description | string \| undefined | Human-readable description |
| inputSchema | Record<string, any> | JSON Schema for the tool's input |
| outputSchema | Record<string, any> \| undefined | JSON Schema for the tool's output |
| annotations | Record<string, any> \| undefined | Additional metadata annotations |
| source.mcpName | string | Name of the MCP server that provides this tool |
| enabled | boolean | Whether the tool is enabled for agent use |
Example:
const tools = await intelWeb.tools.list();
tools.forEach((tool) => {
console.log(`[${tool.enabled ? "ON" : "OFF"}] ${tool.name}: ${tool.description}`);
});tools.toggleTool(name, enabled)
Enables or disables a tool. Disabled tools are not passed to agents during stream() or generate() calls.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| name | string | Yes | Tool name |
| enabled | boolean | Yes | true to enable, false to disable |
Returns: Tool — the updated tool object.
Throws: Throws if no tool with the given name exists.
Example:
const updatedTool = intelWeb.tools.toggleTool("web-search", false);
console.log(`${updatedTool.name} is now disabled`);tools.callTool(name, input, fromApp?)
Invokes a tool directly, bypassing agent orchestration. The tool must be enabled.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| name | string | Yes | Tool name |
| input | Record<string, any> | Yes | Tool input matching the tool's inputSchema |
| fromApp | boolean | No | Set to true when calling from an MCP App context. Enforces tool visibility restrictions. |
Returns: Promise<any> — the tool result as returned by the MCP server.
Throws: Throws if the tool does not exist, is disabled, or if the tool call fails on the MCP server.
Example:
const result = await intelWeb.tools.callTool("get-stock-price", {
ticker: "AAPL",
});
console.log(result);Resources API
Accessed via intelWeb.resources.
resources.list()
Returns all MCP resources discovered from connected MCP servers.
Returns: Promise<ResourceDefinition[]>
Each resource definition has:
| Property | Type | Description |
|----------|------|-------------|
| uri | string | Unique resource URI |
| name | string | Resource name |
| title | string \| undefined | Display title |
| description | string \| undefined | Human-readable description |
| mimeType | string \| undefined | MIME type of the resource content |
| annotations | Record<string, any> \| undefined | Additional metadata |
| source.mcpName | string | Name of the MCP server that provides this resource |
Example:
const resources = await intelWeb.resources.list();
resources.forEach((r) => {
console.log(`${r.uri} (${r.mimeType ?? "unknown type"})`);
});resources.readResource(serverName, uri)
Reads the content of an MCP resource.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| serverName | string | Yes | The MCP server name (matches source.mcpName) |
| uri | string | Yes | The resource URI |
Returns: Promise<ReadResourceResult>
interface ReadResourceResult {
contents: ResourceContent[]; // array of text or blob content objects
_meta?: Record<string, unknown>;
}
// Text resource:
interface ResourceTextContent {
uri: string;
text: string;
mimeType?: string;
}
// Binary resource:
interface ResourceBlobContent {
uri: string;
blob: string; // base64-encoded
mimeType?: string;
}Throws: Throws if the specified MCP server is not connected, or if the resource read fails.
Example:
const result = await intelWeb.resources.readResource("my-mcp-server", "file:///docs/intro.md");
const content = result.contents[0];
if ("text" in content) {
console.log(content.text);
}MCP Apps API
Accessed via intelWeb.mcpApps. This API is only available when mcp.mcpApps is configured.
MCP Apps lets your application host interactive web UI returned by MCP tool calls. When an agent calls an MCP tool that is backed by a UI resource, the library can render that UI in an iframe (inline mode) or in an io.Connect Workspace window (workspace mode).
mcpApps.create(params)
Creates one or more MCP App instances for tool calls within a given thread. Use this to render apps when loading a thread's existing message history.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.threadId | string | Yes | Thread identifier |
| params.apps | AppCreateEntry[] | Yes | Array of app entries to create |
Each AppCreateEntry has:
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| toolCallId | string | Yes | Unique tool call identifier |
| toolName | string | Yes | Name of the MCP tool |
| toolInput | Record<string, unknown> | No | Tool input arguments |
| toolResult | unknown | No | Tool result to pre-populate |
| displayMode | "inline" \| "workspace" | No | Override the default display mode |
Returns: Promise<AppInstance[]>
Throws: Throws if MCP Apps is not configured.
mcpApps.recreate(params)
Atomically closes all existing MCP App instances and creates new ones for the given thread. Intended for thread-switching: call recreate() whenever the active thread changes so that the displayed apps always match the current conversation.
Concurrent calls are safe: if a newer recreate() call is made while a previous one is still in progress, the previous call is cancelled and only the latest call's apps are created.
Parameters: Same as create().
Returns: Promise<AppInstance[]>
mcpApps.close(params)
Closes a single MCP App instance.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.id | string | Yes | App instance ID (same as toolCallId) |
Returns: Promise<void>
mcpApps.closeAll()
Closes all active MCP App instances.
Returns: Promise<void>
mcpApps.find(params)
Finds an active MCP App instance by ID.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.id | string | Yes | App instance ID |
Returns: AppInstance | undefined
mcpApps.list(params?)
Lists active MCP App instances, optionally filtered by thread.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.threadId | string | No | Filter by thread |
Returns: AppInstance[]
mcpApps.notifyPendingResponse(isPending)
Notifies all active MCP App instances whether the agent is currently generating a response. Apps use this signal to show or hide a loading indicator.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| isPending | boolean | Yes | true if a response is being generated |
Returns: void
mcpApps.onAppCreated(handler)
Subscribes to app creation events. Fired each time a new MCP App instance becomes available (either from a stream intercept or a create() call).
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| handler | (app: AppInstance) => void | Yes | Handler function |
Returns: Unsubscribe — call to remove the handler.
mcpApps.onRecreateRequested(handler)
Subscribes to duplicate-detection events. When a tool call produces an app for a tool that already has an active instance (in workspace mode), the event is fired instead of automatically creating a second window. Your handler must call event.select() to resolve the duplicate.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| handler | (event: RecreateRequestEvent) => void | Yes | Handler function |
Returns: Unsubscribe
The RecreateRequestEvent has:
| Property | Type | Description |
|----------|------|-------------|
| toolName | string | The tool involved |
| existingAppId | string | ID of the existing instance |
| newToolCallId | string | ID of the incoming tool call |
| select(option) | (option: 'recreate' \| 'newInstance') => Promise<AppInstance> | Resolves the duplicate: 'recreate' replaces the existing instance; 'newInstance' opens a second window alongside it |
mcpApps.onAppRecreated(handler)
Subscribes to app recreation events. Fired after a 'recreate' selection completes.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| handler | (event: AppRecreatedEvent) => void | Yes | Handler function |
Returns: Unsubscribe
The AppRecreatedEvent has:
| Property | Type | Description |
|----------|------|-------------|
| oldId | string | ID of the closed instance |
| newApp | AppInstance | The newly created instance |
AppInstance object
| Property / Method | Type | Description |
|-------------------|------|-------------|
| id | string | Instance ID (same as toolCallId) |
| threadId | string | Thread this instance belongs to |
| displayMode | "inline" \| "workspace" | Rendering mode |
| status | AppStatus | Current status: "initializing", "active", "closed" |
| element | HTMLElement \| null | The wrapper <div> containing the iframe. Only set in inline mode; null in workspace mode. Mount this element in your DOM. |
| sendMessage(payload) | (payload: unknown) => Promise<void> | Sends a message to the app guest |
| onMessage(handler) | (handler: (text, meta?) => void) => Unsubscribe | Subscribes to messages from the app guest |
| onStatusChange(handler) | (handler: (status: AppStatus) => void) => Unsubscribe | Subscribes to status changes |
| close() | () => Promise<void> | Closes and cleans up this instance |
System API
Accessed via intelWeb.system.
system.disconnectMCPTransports()
Terminates the Streamable HTTP transport sessions for all connected MCP servers. Call this when your application is unmounting to clean up server-side resources. Web-based MCP clients (using ioIntel.web) do not have transport sessions and are silently skipped.
Returns: void
Example:
// On application teardown
window.addEventListener("beforeunload", () => {
intelWeb.system.disconnectMCPTransports();
});Context API
Accessed via intelWeb.context. Only available when context is provided in the configuration.
context.get()
Returns the current snapshot of working context data.
Returns: Record<string, IoIntelWorkingContext.Property>
Throws: Throws if working context was not configured.
Example:
if (intelWeb.context) {
const ctx = intelWeb.context.get();
console.log("Working context:", ctx);
}context.onChanged(callback)
Subscribes to working context updates.
Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| callback | (data: Record<string, Property>) => void | Yes | Called whenever working context changes |
Returns: UnsubscribeFunction — call to remove the subscription.
Example:
if (intelWeb.context) {
const unsubscribe = intelWeb.context.onChanged((data) => {
console.log("Context updated:", data);
});
// Later, to stop listening:
unsubscribe();
}Configuration
WebConfig
The top-level configuration object passed to IoAiWebFactory.
interface WebConfig {
agentServer: AgentServerConfig; // required
mcp?: MCPConfig; // optional
context?: WorkingContextConfig; // optional
}| Property | Type | Required | Description |
|----------|------|----------|-------------|
| agentServer | AgentServerConfig | Yes | io.Intelligence server connection settings |
| mcp | MCPConfig | No | MCP client configuration. Omit entirely to disable all MCP connections. |
| context | WorkingContextConfig | No | Working context integration settings |
AgentServerConfig
Configures the connection to the io.Intelligence server (the backend that hosts agents, threads, and streaming).
interface AgentServerConfig {
baseUrl: string;
retries?: number;
backoffMs?: number;
maxBackoffMs?: number;
headers?: Record<string, string>;
abortSignal?: AbortSignal;
credentials?: "omit" | "same-origin" | "include";
}| Property | Type | Required | Default | Description |
|----------|------|----------|---------|-------------|
| baseUrl | string | Yes | — | Base URL of the io.Intelligence server. Must be a valid URL. |
| retries | number | No | — | Number of retry attempts on failure |
| backoffMs | number | No | — | Initial backoff delay in milliseconds |
| maxBackoffMs | number | No | — | Maximum backoff delay in milliseconds |
| headers | Record<string, string> | No | — | Static headers included with every request (e.g., Authorization) |
| abortSignal | AbortSignal | No | — | Abort signal for all requests |
| credentials | "omit" \| "same-origin" \| "include" | No | — | Credentials mode for fetch requests |
Example:
const agentServer: IoAiWeb.AgentServerConfig = {
baseUrl: "https://intel-server.example.com",
retries: 3,
backoffMs: 500,
maxBackoffMs: 5000,
headers: {
Authorization: "Bearer my-token",
},
credentials: "include",
};MCPConfig
Configures MCP client connections.
interface MCPConfig {
clientsConfig: MCPClientConfig;
ioIntel?: {
remote?: MCPRemoteServerConfig;
web?: {
enabled?: boolean; // default: true
hasPriority?: boolean; // default: true
};
};
remoteServers?: MCPRemoteServerConfig[];
mcpApps?: McpApps.Config;
}| Property | Type | Required | Description |
|----------|------|----------|-------------|
| clientsConfig | MCPClientConfig | Yes | Shared client configuration applied to all MCP connections |
| ioIntel | object | No | Configuration for the io.Intelligence MCP server |
| ioIntel.remote | MCPRemoteServerConfig | No | Connect to the io.Intelligence MCP server via Streamable HTTP |
| ioIntel.web | object | No | Connect to the io.Intelligence MCP server via the in-process web client |
| ioIntel.web.enabled | boolean | No | Enable the web client connection. Default: true |
| ioIntel.web.hasPriority | boolean | No | Try the web client before falling back to remote. Default: true |
| remoteServers | MCPRemoteServerConfig[] | No | Additional third-party MCP servers to connect to. Names must be unique. |
| mcpApps | McpApps.Config | No | Configuration for MCP Apps rendering |
MCP Apps sub-config:
interface McpApps.Config {
sandboxProxyUrl: string; // required: URL of your sandbox proxy HTML page
displayMode?: "inline" | "workspace"; // optional: default resolved at runtime
}MCPClientConfig
Shared configuration applied to every MCP client connection established by the library.
interface MCPClientConfig {
enforceStrictCapabilities?: boolean; // default: true
debouncedNotificationMethods?: string[]; // default: []
capabilities: {
sampling?: {
handler?: (serverName: string, params: SamplingRequestParams) => Promise<SamplingSuccessResponse | SamplingErrorResponse>;
};
elicitation?: {
handler?: (serverName: string, params: ElicitationRequestParams) => Promise<ElicitationResponse>;
};
extensions?: {
"io.modelcontextprotocol/ui"?: {
mimeTypes: string[];
};
[key: string]: any;
};
experimental?: Record<string, any>;
};
}| Property | Type | Default | Description |
|----------|------|---------|-------------|
| enforceStrictCapabilities | boolean | true | Enforce strict MCP capability negotiation |
| debouncedNotificationMethods | string[] | [] | Notification method names to debounce |
| capabilities.sampling | object | — | Declares sampling capability. Provide handler to respond to sampling requests from MCP servers. |
| capabilities.elicitation | object | — | Declares elicitation capability. Provide handler to respond to elicitation requests. |
| capabilities.extensions | object | — | Declares extension capabilities (e.g., io.modelcontextprotocol/ui for MCP Apps). |
| capabilities.experimental | object | — | Declares experimental capabilities as key-value pairs. |
Sampling handler signature:
async function samplingHandler(
serverName: string,
params: SamplingRequestParams
): Promise<SamplingSuccessResponse | SamplingErrorResponse>Elicitation handler signature:
async function elicitationHandler(
serverName: string,
params: ElicitationRequestParams
): Promise<ElicitationResponse>
// ElicitationResponse = { action: "accept"; content: Record<string, any> }
// | { action: "decline" }
// | { action: "cancel" }MCPRemoteServerConfig
Configures a single Streamable HTTP MCP server connection.
interface MCPRemoteServerConfig {
streamableHttp: {
url: string; // must be a valid URL
name: string; // must be unique across all configured MCP servers
options?: StreamableHTTPClientTransportOptions;
};
}| Property | Type | Required | Description |
|----------|------|----------|-------------|
| streamableHttp.url | string | Yes | The server's Streamable HTTP endpoint URL |
| streamableHttp.name | string | Yes | A unique name for this server connection |
| streamableHttp.options | object | No | Additional options passed to StreamableHTTPClientTransport |
WorkingContextConfig
Configures the optional integration with the @interopio/working-context library.
interface WorkingContextConfig {
factory: IoIntelWorkingContextFactoryFunction;
config?: IoIntelWorkingContext.Config;
}| Property | Type | Required | Description |
|----------|------|----------|-------------|
| factory | function | Yes | The factory function from @interopio/working-context |
| config | object | No | Configuration passed to the working context factory |
Integration Examples
With io.Connect Browser
import { IoAiWebFactory } from "@interopio/ai-web";
import IOBrowser from "@interopio/browser";
const io = await IOBrowser({ /* your io.Connect Browser config */ });
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://intel-server.example.com",
},
mcp: {
clientsConfig: {
capabilities: {},
},
ioIntel: {
// Connect via the in-process web client (requires io.Intelligence app running in io.Connect)
web: {
enabled: true,
hasPriority: true,
},
// Fallback to a remote HTTP endpoint
remote: {
streamableHttp: {
url: "https://mcp.intel-server.example.com/mcp",
name: "io-intel-mcp",
},
},
},
},
});With io.Connect Desktop
import { IoAiWebFactory } from "@interopio/ai-web";
import IODesktop from "@interopio/desktop";
const io = await IODesktop();
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://intel-server.example.com",
},
mcp: {
clientsConfig: {
capabilities: {},
},
ioIntel: {
remote: {
streamableHttp: {
url: "https://mcp.intel-server.example.com/mcp",
name: "io-intel-mcp",
},
},
},
},
});With Working Context
import { IoAiWebFactory } from "@interopio/ai-web";
import { IoIntelWorkingContextFactory } from "@interopio/working-context";
import IOBrowser from "@interopio/browser";
const io = await IOBrowser();
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://intel-server.example.com",
},
mcp: {
clientsConfig: {
capabilities: {},
},
},
context: {
factory: IoIntelWorkingContextFactory,
config: {
// Working context configuration — see @interopio/working-context docs
},
},
});
// Working context is automatically injected into agent system prompts.
// You can also read it directly:
if (intelWeb.context) {
const ctx = intelWeb.context.get();
console.log("Current context:", ctx);
intelWeb.context.onChanged((updated) => {
console.log("Context changed:", updated);
});
}With MCP Apps
This example shows how to configure MCP Apps and handle the full app lifecycle during a conversation.
import { IoAiWebFactory } from "@interopio/ai-web";
import IOBrowser from "@interopio/browser";
const io = await IOBrowser();
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://intel-server.example.com",
},
mcp: {
clientsConfig: {
capabilities: {
extensions: {
// Declare MCP Apps capability with supported MIME types
"io.modelcontextprotocol/ui": {
mimeTypes: ["text/html"],
},
},
},
},
mcpApps: {
// URL of the sandbox proxy page served alongside your app
sandboxProxyUrl: "https://your-app.example.com/sandbox-proxy.html",
},
},
});
const mcpApps = intelWeb.mcpApps!;
// Subscribe to new app instances created during streaming
const unsubCreated = mcpApps.onAppCreated((app) => {
console.log(`App created: ${app.id} (mode: ${app.displayMode})`);
if (app.displayMode === "inline" && app.element) {
// Mount the iframe wrapper element in your UI
document.getElementById("app-container")?.appendChild(app.element);
}
app.onStatusChange((status) => {
console.log(`App ${app.id} status: ${status}`);
});
});
// Handle duplicate detection in workspace mode
const unsubRecreate = mcpApps.onRecreateRequested(async (event) => {
const userChoice = await promptUser(
`Tool "${event.toolName}" is already open. Replace it or open a new window?`
);
const app = await event.select(userChoice === "replace" ? "recreate" : "newInstance");
console.log(`Resolved duplicate — new app: ${app.id}`);
});
// When switching threads, reload the apps for the new thread
async function switchThread(threadId: string, messages: any[]) {
// Notify apps that a new response may be pending
mcpApps.notifyPendingResponse(true);
// Atomically close old apps and create new ones from history
const toolCallEntries = messages
.filter((m) => m.toolCallId)
.map((m) => ({
toolCallId: m.toolCallId,
toolName: m.toolName,
toolInput: m.toolInput,
toolResult: m.toolResult,
}));
await mcpApps.recreate({ threadId, apps: toolCallEntries });
mcpApps.notifyPendingResponse(false);
}
// Cleanup on teardown
function teardown() {
unsubCreated();
unsubRecreate();
mcpApps.closeAll();
intelWeb.system.disconnectMCPTransports();
}Sampling and Elicitation Handlers
Sampling allows MCP servers to request LLM completions from your application. Elicitation allows MCP servers to collect structured user input.
import { IoAiWebFactory, IoAiWeb } from "@interopio/ai-web";
import IOBrowser from "@interopio/browser";
const io = await IOBrowser();
const intelWeb = await IoAiWebFactory(io, {
agentServer: {
baseUrl: "https://intel-server.example.com",
},
mcp: {
clientsConfig: {
capabilities: {
sampling: {
handler: async (
serverName: string,
params: IoAiWeb.SamplingRequestParams
): Promise<IoAiWeb.SamplingSuccessResponse | IoAiWeb.SamplingErrorResponse> => {
console.log(`Sampling request from server: ${serverName}`);
try {
// Forward to your own LLM service
const response = await myLlmService.complete({
messages: params.messages,
maxTokens: params.maxTokens,
temperature: params.temperature,
systemPrompt: params.systemPrompt,
});
return {
model: response.model,
role: "assistant",
content: {
type: "text",
text: response.text,
},
stopReason: "endTurn",
};
} catch (error: any) {
return {
code: 500,
message: error.message,
};
}
},
},
elicitation: {
handler: async (
serverName: string,
params: IoAiWeb.ElicitationRequestParams
): Promise<IoAiWeb.ElicitationResponse> => {
console.log(`Elicitation request from server: ${serverName}`);
console.log(`Message: ${params.message}`);
// Show a form to the user and collect input
const userResponse = await showForm(params.message, params.requestedSchema);
if (userResponse.cancelled) {
return { action: "cancel" };
}
if (userResponse.declined) {
return { action: "decline" };
}
return {
action: "accept",
content: userResponse.data,
};
},
},
},
},
ioIntel: {
remote: {
streamableHttp: {
url: "https://mcp.intel-server.example.com/mcp",
name: "io-intel-mcp",
},
},
},
},
});