Elements

Elements · AUI connected · AUI

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

No connectors configured

Custom servers

fig. 01

Installation

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 init

Then 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.

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, since connecting, authorizing, and listing a server's tools are all inherently live states that only exist once something is actually trying to connect.

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.

app/assistant.tsx
import { useAui } from "@assistant-ui/react";
import { McpManagerResource } from "@assistant-ui/react-mcp";

useAui({
  mcp: McpManagerResource({
    connectors: [
      { id: "linear", name: "Linear", url: "https://mcp.linear.app/mcp", auth: { type: "oauth" } },
    ],
  }),
});

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.

components/assistant-ui/elements/thread.aui.tsx
import { McpConfigDialog } from "@/components/assistant-ui/elements/mcp-config.aui";

<McpConfigDialog />

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.

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 bearer-token input or an OAuth scopes input depending on the selected AuthSelect value by default; pass children to render your own inputs for the current auth type instead.

<McpAddFormPrimitive.AuthFields>
  {({ authType }) => (authType === "bearer" ? <MyTokenField /> : null)}
</McpAddFormPrimitive.AuthFields>

API reference

McpConfigDialog props

PropTypeDefaultDescription
childrenReactNodeoutlined button with a plug iconCustom trigger element.

McpManagerPrimitive

PartRendersNotes
RootdivProvides McpManagerState; sets data-mcp-hydrated once storage has loaded.
ConnectorslistRender-prop, called once per app-defined connector with { server }.
CustomServerslistRender-prop, called once per user-added server with { server }.
AddCustomTriggerbuttonOpens the add-server form.

McpServerPrimitive

PartRendersNotes
RootdivSets data-server-id, data-kind, data-connection-state, data-has-error.
NametextThe server's name.
ConnectButtonbuttonRenders null unless connectionState is "disconnected", "error", or "authRequired". Calls aui.mcpServer.connect().
OAuthLinkaRenders null unless the server has an authorizationUrl; opens it in a new tab.
DisconnectButtonbuttonRenders null unless connectionState is "connected", "connecting", or "authPending". Calls aui.mcpServer.disconnect().
RemoveButtonbuttonCalls 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

PartRendersNotes
RootformOwns the form's state. Takes onSubmitted(id) and onCancel.
NameField, UrlFieldinputBound text inputs.
AuthSelectselect"oauth" | "bearer" | "none", defaults to "oauth".
AuthFieldsinputsBearer token or OAuth scopes input, matching the current AuthSelect value.
SubmitbuttonDisabled while submitting.
CancelbuttonResets the form and calls onCancel.
ErrortextThe current validation or submit error, if any.

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.

Manager and server state

Selector / callTypeDescription
s.mcp.isHydratedbooleanWhether persisted custom servers have finished loading.
s.mcp.connectors / s.mcp.customServersMCPServerState[]The two lists the dialog renders as its two sections.
s.mcpServer.connectionStateMCPConnectionState"disconnected" | "authRequired" | "authPending" | "connecting" | "connected" | "error".
s.mcpServer.lastError{ message: string } | nullThe server's most recent connection error.
s.mcpServer.authorizationUrlstring | nullPresent 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.