react-mcp
assistant-ui/skills
Add user-managed browser MCP servers to assistant-ui with OAuth, persistence, and tool registration.
What is react-mcp?
@assistant-ui/react-mcp lets users connect preset or custom Streamable HTTP MCP servers in the browser, persisting server and auth state while registering each tool into modelContext. Use this when building a chat UI that needs end-user-chosen MCP integrations with OAuth support and connection management.
- Mount McpManagerResource with preset connectors and custom server support
- Persist server configuration and OAuth state in browser storage
- Register connected MCP tools as frontend tools named serverId__toolName
- Compose headless UI primitives (McpManagerPrimitive, McpServerPrimitive, McpAddFormPrimitive, McpElicitationPrimitive)
- Handle OAuth callbacks with PKCE and DCR flows
- Render structured input elicitation requests from MCP servers during tool calls
How to install react-mcp
npx skills add https://github.com/assistant-ui/skills --skill react-mcp- @assistant-ui/react installed and configured
- AuiProvider wrapping your application
- Chat runtime (via useChatRuntime or minimal manager-provided context)
How to use react-mcp
- 1.Define preset connectors using defineConnector with id, name, url, and auth config
- 2.Mount McpManagerResource in AuiConfig with your connectors and connectionTimeout
- 3.Wrap your app with AuiProvider extending the config
- 4.Optionally install the styled dialog with npx assistant-ui@latest add mcp-config
- 5.Render McpElicitationPrimitive.Items if servers request structured input during tool calls
- 6.Call aui.mcp.addCustomServer(...) or use McpAddFormPrimitive for user-supplied servers
- 7.Ensure your chat runtime sends completed tool results back to continue the model
Use cases
- Build a chat interface where users add their own Linear, GitHub, or other MCP-compatible services
- Create a settings page to manage connected servers and OAuth credentials
- Implement structured input forms when an MCP server requests data during tool execution
- Display connection status and handle reconnection for user-added integrations
- Support both preset connectors (Linear, Weather) and arbitrary custom HTTP MCP servers
- React developers building assistant-ui chat applications
- Teams needing user-managed integrations without backend MCP infrastructure
- Developers implementing OAuth-based tool connections in the browser
react-mcp FAQ
Each connected tool is registered into modelContext as a frontend tool named serverId__toolName (e.g., linear__createIssue). The chat runtime must send completed tool results back to the route for the model to continue.
OAuth with PKCE and DCR, bearer token auth, and no auth. OAuth state is persisted in browser storage.
Yes. Use McpAddFormPrimitive or call aui.mcp.addCustomServer(...) to let users add custom Streamable HTTP MCP servers; they share state and storage with preset connectors.
The server sends an elicitation request. Render McpElicitationPrimitive.Items to display structured input forms (text, select, checkbox) and collect user responses.
Check that the server is connected (not just added), verify tool names are prefixed with serverId__, ensure your chat runtime calls sendAutomaticallyWhen, and check browser storage for persisted state.
Full instructions (SKILL.md)
Source of truth, from assistant-ui/skills.
name: react-mcp
description: "Adds user managed browser MCP servers to assistant-ui with @assistant-ui/react-mcp. Use when mounting McpManagerResource, declaring defineConnector presets, persisting custom servers and OAuth state, composing McpManagerPrimitive, McpServerPrimitive, McpAddFormPrimitive, or McpElicitationPrimitive, handling an OAuth callback, browsing MCP resources, or diagnosing missing server tools, connection failures, and OAuth redirects. This covers end user chosen servers, not application owned backend MCP tools. For those use tools; for the chat runtime use runtime; for the copied dialog use elements."
license: MIT
assistant-ui React MCP
Always consult assistant-ui.com/llms.txt for the latest API.
@assistant-ui/react-mcp lets users connect preset or custom Streamable HTTP MCP servers in the browser. The manager persists server and auth state, exposes connection state to primitives, and registers each connected tool into modelContext as a frontend tool named serverId__toolName.
References
- ./references/setup.md -- manager configuration, storage, state, imperative methods, and resource reads
- ./references/ui.md -- the copied configuration dialog and every manager, server, and add form primitive
- ./references/oauth.md -- auth shapes, OAuth callback handling, CIMD, and connection states
Mount the manager
Declare fixed connectors with defineConnector, then extend the nearest aui scope with a config. connectionTimeout is a manager default in milliseconds. A connector or custom server can override it for its own connect and listTools() readiness flow.
"use client";
import type { ReactNode } from "react";
import { AuiConfig, AuiProvider, useAui } from "@assistant-ui/react";
import { McpManagerResource, defineConnector } from "@assistant-ui/react-mcp";
const connectors = [
defineConnector({
id: "linear",
name: "Linear",
url: "https://mcp.linear.app",
auth: { type: "oauth", scopes: ["read"] },
icon: "/icons/linear.svg",
}),
defineConnector({
id: "weather",
name: "Weather",
url: "https://mcp.example.com/weather",
auth: { type: "none" },
connectionTimeout: 10_000,
}),
];
export function Providers({ children }: { children: ReactNode }) {
const aui = useAui();
const config = AuiConfig({
mcp: McpManagerResource({
connectors,
connectionTimeout: 15_000,
}),
});
return (
<AuiProvider extends={aui} config={config}>
{children}
</AuiProvider>
);
}
Connectors are application presets and cannot be removed. A custom server is user supplied through McpAddFormPrimitive or aui.mcp.addCustomServer(...). Both kinds share state, storage, elicitation, and the tool registration path. See setup for manager options and storage lifetime rules.
Continue after frontend tool calls
The manager registers connected tools automatically. A chat runtime still needs to send completed frontend tool results to the route so the model can continue.
"use client";
import type { ReactNode } from "react";
import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";
export function Chat({ children }: { children: ReactNode }) {
const runtime = useChatRuntime({
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
});
return <AssistantRuntimeProvider runtime={runtime}>{children}</AssistantRuntimeProvider>;
}
If no chat runtime provides modelContext, the manager mounts a minimal one. Use aui.mcp.server({ id }).callTool(...) for a direct call in an event handler.
Use the configuration UI
Install the styled dialog with npx assistant-ui@latest add mcp-config. It is a copied runtime connected element, so import it from @/components/assistant-ui/elements/mcp-config.aui and render it under the manager provider.
import { McpConfigDialog } from "@/components/assistant-ui/elements/mcp-config.aui";
export function ServerSettings() {
return <McpConfigDialog />;
}
Pass an element as children to replace its default trigger. For a settings page, sidebar, or custom add flow, compose the headless primitives in ui.
Render elicitation requests
An MCP server can request structured input while a tool call is in flight. Render Items in the same mcpServer scope as the connected server. Set elicitation: false on a connector or custom server to opt it out of the protocol capability.
import { McpElicitationPrimitive } from "@assistant-ui/react-mcp";
type FieldSchema = {
type?: string;
enum?: readonly string[];
};
export function ElicitationRequests() {
return (
<McpElicitationPrimitive.Items>
{() => (
<McpElicitationPrimitive.Root>
<McpElicitationPrimitive.Message />
<McpElicitationPrimitive.Error />
<McpElicitationPrimitive.Fields>
{({ name, schema, value, setValue }) => {
const field = schema as FieldSchema;
if (field.type === "boolean") {
return (
<label>
{name}
<input
type="checkbox"
checked={value === true}
onChange={(event) => setValue(event.target.checked)}
/>
</label>
);
}
if (field.enum) {
return (
<label>
{name}
<select
value={typeof value === "string" ? value : ""}
onChange={(event) => setValue(event.target.value)}
>
{!field.enum.includes("") && <option value="">Select {name}</option>}
{field.enum.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</label>
);
}
return (
<label>
{name}
<input
value={typeof value === "string" || typeof value === "number" ? String(value) : ""}
onChange={(event) => setValue(event.target.value)}
/>
</label>
);
}}
</McpElicitationPrimitive.Fields>
<McpElicitationPrimitive.Accept>Submit</McpElicitationPrimitive.Accept>
<McpElicitationPrimitive.Decline>Decline</McpElicitationPrimitive.Decline>
<McpElicitationPrimitive.Cancel>Cancel</McpElicitationPrimitive.Cancel>
</McpElicitationPrimitive.Root>
)}
</McpElicitationPrimitive.Items>
);
}
The render function receives a separate active request each time. Numeric strings are coerced when valid, while boolean fields must set a boolean. An empty string is unanswered unless the property allows it through an enum member or default, so render enum properties as a select. Accept remains disabled for missing required fields and invalid values.
v1 scope
This package ships tool listing and invocation, resource listing and reads, form elicitation, OAuth with PKCE and DCR, bearer and no auth, Streamable HTTP transport, and manual connect or disconnect. Prompts, sampling, automatic reconnect with backoff, persisted per tool enablement, per tool consent, and built in token encryption are deferred.
Common Gotchas
Tools never reach the model
- The server must be
connected. Its tools are namedserverId__toolName, not just the MCP tool name. - Configure
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCallsonuseChatRuntimeso completed frontend tool calls resume the model.
OAuth callback fails or never completes
- The callback route must exactly match
oauthRedirectUriand renderMcpOAuthCallbackbeneath the manager provider. - A CIMD only authorization server without a registration endpoint needs a static OAuth
clientId.
Server state leaks across users or a storage swap loses records
McpLocalStorage()is browser only and stores tokens in plain text. Use an HTTP only cookie backedMcpCustomStoragefor production auth state.- Do not swap
storagefor another user. Remount the manager because custom server records hydrate only once. See storage identity.
An elicitation form stays blank
Itemsrenders nothing without pending requests and requires the matching server scope. Confirm that the server is connected and has not setelicitation: false.
A resource browser stops after the first page
- Pass each returned
nextCursortolistResources({ cursor })until it is absent, then callreadResource(uri)for a selected item.
Related Skills
- tools -- developer owned frontend and backend tools that complement user managed MCP servers
- setup -- CLI setup and the
mcpproject template - runtime --
useChatRuntimeand runtime provider configuration - elements -- installing and modifying the copied
mcp-configelement
Related skills
More from assistant-ui/skills and the wider catalog.

runtime
State and action layer for chat UIs: manage threads, messages, and runtime lifecycle.

setup
CLI for scaffolding, configuring, and maintaining assistant-ui projects with runtime adapter selection.

streaming
Streaming wire protocols and backend helpers for building custom assistant-ui endpoints outside the Vercel AI SDK.

thread-list
Build and customize conversation lists in assistant-ui with thread management, search, grouping, and persistence.

tools
Define model-callable tools and render their calls in assistant-ui applications.

update
Upgrade existing assistant-ui applications to AI SDK v7 and assistant-ui 0.15.x with guided migrations and codemods.