MCP config dialog
A dialog for connectors and custom MCP servers, including authentication and connection state.
MCP servers
Connect servers to expose their tools to this assistant.
Connectors
Custom servers
Installation
Install manually
npx shadcn@latest add "@assistant-ui/mcp-config"First time? Set up a runtime
Runtime components read their state from an assistant-ui runtime. Add one to an existing project:
npx assistant-ui@latest initThen wrap your app in a runtime provider:
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";
export default function App() {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({ api: "/api/chat" }),
});
return (
<AssistantRuntimeProvider runtime={runtime}>
{/* your components */}
</AssistantRuntimeProvider>
);
}The installation guide covers new projects, templates, and API routes.
This component is composed from runtime primitives and has no standalone build.
MCP config dialog lists every app-defined connector and user-added custom server in one place, with inline connect, authorize, and remove controls, plus a form for adding a new server. With a runtime it reads and drives live connection state through the MCP manager. There is no standalone form, because connection, authorization, and tool lists are states of a running manager.
Getting started
Mount the manager
McpManagerResource is the entry point: mount it once at the root of your app, with the connectors you want available to every user.
import { AuiConfig, AuiProvider, useAui } from "@assistant-ui/react";
import { McpManagerResource } from "@assistant-ui/react-mcp";
export function McpProviders({ children }: { children: React.ReactNode }) {
const aui = useAui();
const config = AuiConfig({
mcp: McpManagerResource({
connectors: [
{ id: "linear", name: "Linear", url: "https://mcp.linear.app/mcp", auth: { type: "oauth" } },
],
}),
});
return (
<AuiProvider extends={aui} config={config}>
{children}
</AuiProvider>
);
}Render the dialog
McpConfigDialog renders anywhere inside the provider. With no children, it renders its own outlined trigger button; pass an element as children to use your own trigger instead.
import { McpConfigDialog } from "@/components/assistant-ui/elements/mcp-config.aui";
<McpConfigDialog />Standalone, there's no connection to configure. McpConfigDialog drives a live McpManagerResource, so it takes no static props for servers or their state. For a single server card without the dialog chrome, compose McpServerPrimitive.Root against a mounted manager. For a server list driven by props, see Server panel.
Anatomy
<Dialog>
<DialogTrigger>{/* plug icon + "MCP servers", or your own children */}</DialogTrigger>
<DialogContent>
<section> {/* Connectors */}
{/* one server card per app-defined connector */}
</section>
<section> {/* Custom servers */}
{/* one server card per user-added server */}
{/* "Add server" trigger, or the add form once opened */}
</section>
</DialogContent>
</Dialog>Each server card shows an avatar (the server's icon, or a fallback icon), its name, a status badge, action buttons, and, once it has one, an error banner. Which action buttons render depends on connectionState: Connect shows for "disconnected", "error", or "authRequired"; the OAuth link shows whenever the server exposes an authorizationUrl; Disconnect shows for "connected", "connecting", or "authPending". A card in "error" state additionally gets a destructive-tinted border. Each card also carries a visually hidden role="status" region that announces later connection state changes and new connection errors with their message, while the state a card opens with stays silent.
Examples
Custom trigger
Pass any element as children and it becomes the dialog's trigger in place of the default button.
<McpConfigDialog>
<button type="button">Connectors</button>
</McpConfigDialog>A server card outside the dialog
Compose McpServerPrimitive.Root directly for a sidebar or settings page that shows one server without the rest of the dialog's chrome.
import { McpServerByIdProvider, McpServerPrimitive } from "@assistant-ui/react-mcp";
<McpServerByIdProvider id="linear">
<McpServerPrimitive.Root>
<McpServerPrimitive.Name />
<McpServerPrimitive.ConnectButton>Connect</McpServerPrimitive.ConnectButton>
</McpServerPrimitive.Root>
</McpServerByIdProvider>Custom auth fields
AuthFields renders a labeled bearer token input or OAuth scopes input depending on the selected AuthSelect value by default; pass children to render your own inputs for the current auth type instead. Render them through BearerTokenField and ScopesField so their values reach the submitted auth config. With asChild, your component receives the bound value and onChange and must pass them on to its input.
<McpAddFormPrimitive.AuthFields>
{({ authType }) =>
authType === "bearer" ? (
<McpAddFormPrimitive.BearerTokenField asChild>
<MyTokenField />
</McpAddFormPrimitive.BearerTokenField>
) : null
}
</McpAddFormPrimitive.AuthFields>API reference
McpConfigDialog props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | outlined button with a plug icon | Custom trigger element. |
McpManagerPrimitive
| Part | Renders | Notes |
|---|---|---|
Root | div | Sets data-mcp-hydrated once storage has loaded. |
Connectors | list | Render-prop, called once per app-defined connector with { server }. |
CustomServers | list | Render-prop, called once per user-added server with { server }. |
AddCustomTrigger | button | A plain button with no built-in action; show your add-server form from its onClick. |
McpServerPrimitive
| Part | Renders | Notes |
|---|---|---|
Root | div | Sets data-server-id, data-kind, data-connection-state, data-has-error. |
Name | text | The server's name. |
ConnectButton | button | Renders null unless connectionState is "disconnected", "error", or "authRequired". Calls aui.mcpServer.connect(). |
OAuthLink | a | Renders null unless the server has an authorizationUrl; opens it in a new tab. |
DisconnectButton | button | Renders null unless connectionState is "connected", "connecting", or "authPending". Calls aui.mcpServer.disconnect(). |
RemoveButton | button | Calls aui.mcpServer.remove(). |
Status, Error, Icon, Tools, and ToolName are also available on McpServerPrimitive for building a more detailed server view than the dialog's own card.
McpAddFormPrimitive
| Part | Renders | Notes |
|---|---|---|
Root | form | Owns the form's state. Takes onSubmitted(id) and onCancel. |
NameField, UrlField | input | Bound text inputs. The field that fails validation gets aria-invalid and the error's id in aria-describedby; with asChild, pass aria-describedby to the part, because the slotted child's own value replaces the composed one. |
AuthSelect | select | "oauth" | "bearer" | "none", defaults to "oauth". |
AuthFields | labeled inputs | Bearer token or OAuth scopes input with its own label, matching the current AuthSelect value. |
BearerTokenField, ScopesField | input | The bound inputs AuthFields renders, for use in custom AuthFields children. BearerTokenField marks a missing token the way NameField marks a missing name, with the same asChild rule. |
Submit | button | Disabled while submitting. |
Cancel | button | Resets the form and calls onCancel. |
Error | div | Renders null without an error; otherwise a role="alert" element that the invalid field's aria-describedby references. |
Submitting validates that name is non-empty, that the URL parses as http:/https:, and that a bearer token is present when authType is "bearer"; a failing check sets the form's error instead of calling aui.mcp.addCustomServer. Editing the field an error names clears it.
Manager and server state
| Selector / call | Type | Description |
|---|---|---|
s.mcp.isHydrated | boolean | Whether persisted custom servers have finished loading. |
s.mcp.connectors / s.mcp.customServers | MCPServerState[] | The two lists the dialog renders as its two sections. |
s.mcpServer.connectionState | MCPConnectionState | "disconnected" | "authRequired" | "authPending" | "connecting" | "connected" | "error". |
s.mcpServer.lastError | { message: string } | null | The server's most recent connection error. |
s.mcpServer.authorizationUrl | string | null | Present once an OAuth flow is ready to start. |
aui.mcp.addCustomServer(input) | Promise<string> | Adds a custom server; resolves to its id. |
aui.mcp.removeServer(id) | Promise<void> | Removes a custom server by id. |
aui.mcpServer.connect() / .disconnect() / .remove() | Promise<void> | Drive the current server's connection lifecycle. |