Skip to main content
Universal provider component for mcp-use widgets. It works identically whether your widget runs in MCP Apps clients (Claude, Goose) or in ChatGPT via the Apps SDK, so no protocol-specific conditional logic is needed in your widget code. McpUseProvider composes the common React setup for a widget into a single wrapper. From outermost to innermost it renders StrictMode, then ThemeProvider, then (conditionally) WidgetControls, then ErrorBoundary around your children. When autoSize is enabled it also wraps the tree in a container div that a ResizeObserver measures to notify the host of height changes.

McpUseProvider

Unified provider component that combines all common React setup for mcp-use widgets.
import { McpUseProvider } from "mcp-use/react";
Wraps widget children with the standard mcp-use provider stack. Always includes StrictMode (outermost), ThemeProvider, and ErrorBoundary (innermost). Conditionally adds WidgetControls when debugger is true or viewControls is set, and an auto-sizing container div when autoSize is true. The auto-sizing container uses a ResizeObserver on the wrapping div to detect content height changes and notifies the host through the MCP Apps bridge (ui/notifications/size-changed). Notifications are debounced by 150ms to let animations settle, and a positive height change is only sent when it differs from the last reported height by at least 5px. A zero height is always reported, which lets the host collapse and hide an empty widget. When the widget renders inside the top-level window (not an iframe, where window === window.parent) the height notification is skipped. If ResizeObserver is unavailable or window is undefined (server-side rendering), auto-sizing is a no-op. Props
children
React.ReactNode
required
The widget content to wrap.
debugger
boolean
default:"false"
Enable the debug button in the WidgetControls component. When true, WidgetControls is rendered around the content.
viewControls
boolean | "pip" | "fullscreen"
default:"false"
Enable view controls in the WidgetControls component. true shows both the picture-in-picture and fullscreen buttons, "pip" shows only the picture-in-picture button, and "fullscreen" shows only the fullscreen button. Any truthy value also causes WidgetControls to be rendered.
autoSize
boolean
default:"true"
Automatically notify the host about container height changes for auto-sizing using a ResizeObserver on the children container, sent via the MCP Apps ui/notifications/size-changed notification.
colorScheme
boolean
default:"true"
Set color-scheme on the document root to match the active theme, enabling native dark scrollbars and the CSS light-dark() function. Disable only when you need the browser canvas to stay transparent in hosts that render widgets over a differently themed background. Forwarded to ThemeProvider.
Signature
function McpUseProvider({ children, debugger: enableDebugger = false, viewControls = false, autoSize = true, colorScheme = true }: McpUseProviderProps): React.ReactElement
The default for autoSize is true in the current source. Widgets are auto-sized out of the box. Pass autoSize={false} to opt out.

Types

The props object accepted by McpUseProvider.

McpUseProviderProps

Shape of the props object passed to McpUseProvider. Fields
children
React.ReactNode
required
The widget content to wrap.
debugger
boolean
Optional. Enable the debug button in WidgetControls. Defaults to false.
viewControls
boolean | "pip" | "fullscreen"
Optional. Enable view controls in WidgetControls. Defaults to false.
autoSize
boolean
Optional. Enable host height notifications via ResizeObserver. Defaults to true.
colorScheme
boolean
Optional. Set color-scheme on the document root to match the active theme. Defaults to true.
Signature
interface McpUseProviderProps {
  children: React.ReactNode;
  debugger?: boolean;
  viewControls?: boolean | "pip" | "fullscreen";
  autoSize?: boolean;
  colorScheme?: boolean;
}

What is included

McpUseProvider builds the component tree with conditional wrappers, from outermost to innermost:
  1. StrictMode (always, outermost): React development-mode checks.
  2. ThemeProvider (always): protocol-aware theme management, syncs with both MCP Apps and ChatGPT. Receives the colorScheme prop.
  3. WidgetControls (conditional): rendered when debugger is true or viewControls is set. Receives debugger and viewControls.
  4. Auto-sizing container div (conditional): rendered when autoSize is true. Styled with width: "100%" and minHeight: 0, and observed by a ResizeObserver.
  5. ErrorBoundary (always, innermost): catches render errors for graceful failures.

Usage

Minimal usage wraps a single widget component. The widget itself is registered server-side and rendered inside the iframe; the example server below shows the matching server tool that drives a weather-display widget.
import { McpUseProvider } from "mcp-use/react";

export default function App() {
  return (
    <McpUseProvider>
      <div>My widget content</div>
    </McpUseProvider>
  );
}
Enable the debug button, view controls, and auto-sizing together:
import { McpUseProvider } from "mcp-use/react";

export default function App() {
  return (
    <McpUseProvider debugger viewControls autoSize>
      <div>My widget content</div>
    </McpUseProvider>
  );
}
The matching server registers a tool that returns the widget. Use new MCPServer({ ... }) to construct the server:
import { MCPServer, widget } from "mcp-use/server";
import { z } from "zod";

const server = new MCPServer({
  name: "mcp-apps-example",
  version: "1.0.0",
  description:
    "Example MCP server demonstrating dual-protocol widget support (works with both ChatGPT and MCP Apps clients)",
});

server.tool(
  {
    name: "get-weather",
    description:
      "Get current weather for a city (works with ChatGPT and MCP Apps clients)",
    schema: z.object({
      city: z.string().describe("City name"),
    }),
    widget: {
      name: "weather-display",
      invoking: "Fetching weather data...",
      invoked: "Weather data loaded",
    },
  },
  async ({ city }) =>
    widget({
      props: { city },
      message: `Current weather in ${city}`,
    })
);

await server.listen();
When autoSize is enabled (the default) and your widget returns null, for example when there are no results to display, the ResizeObserver reports a zero height. A zero height is always sent to the host (it bypasses the 5px change threshold), so the host can collapse the iframe and hide the empty widget.
McpUseProvider does not include BrowserRouter. If your widget uses react-router for navigation, add it explicitly inside the provider:
import { BrowserRouter } from "react-router";
import { McpUseProvider } from "mcp-use/react";

<McpUseProvider>
  <BrowserRouter>
    <MyWidget />
  </BrowserRouter>
</McpUseProvider>;