View the source code for this module on GitHub: https://github.com/mcp-use/mcp-use/blob/main/libraries/typescript/packages/mcp-use/src/react/useWidget.ts
useWidget abstracts over two data providers, selected automatically:
- MCP Apps bridge (SEP-1865
postMessage), the primary runtime for hosted widget iframes including ChatGPT. The hook connects viaui/initializeand listens forui/notifications/tool-input,ui/notifications/tool-input-partial,ui/notifications/tool-result, andui/notifications/host-context-changed. - URL params fallback (
mcpUseParams), used during local development (themcp-use devinspector) wheretoolInputandtoolOutputare injected via the query string. There is no live streaming in this mode.
content text array) separately from what the widget sees (structuredContent mapped to props). This lets a tool return rich structured data for rendering without polluting the model’s context.
All exports come from mcp-use/react:
Hooks
useWidget
The primary hook for building MCP Apps widgets. Returns props, persisted state, host context (theme, display mode, locale, layout), host actions (call a tool, send a message, open a link, request a display mode), and streaming and availability flags.TProps, TState, TOutput, TMetadata, TToolInput. The single runtime argument, defaultProps, seeds props before any tool data arrives.
props is merged from toolInput (base) and structuredContent (overlay). When the widget is exposed as a tool, props equals toolInput while pending and structuredContent once done. When the widget is returned by another tool, props equals structuredContent and toolInput holds the parent tool’s arguments.
Type Parameters
Parametersobjectdefault:"UnknownObject"Shape ofprops, mapped fromstructuredContentin the tool result (or fromtoolInputwhile pending).objectdefault:"UnknownObject"Shape of the persisted widgetstateread and written viasetState.objectdefault:"UnknownObject"Shape ofoutput, the rawstructuredContentfield from the tool result.objectdefault:"UnknownObject"Shape ofmetadata, the_metaobject from the tool result.objectdefault:"UnknownObject"Shape oftoolInput, the arguments the model passed to the tool.
ReturnsTPropsdefault:"undefined"Optional initial props used as the merge base beforetoolInputorstructuredContentarrives. When omitted, the base is an empty object.
UseWidgetResult<TProps, TState, TOutput, TMetadata, TToolInput>, a discriminated union on isPending. While isPending is true, props is Partial<TProps>; once isPending is false, props is the full TProps.
SignaturePartial<TProps> | TPropsMerged widget props.Partial<TProps>whileisPendingistrue, then the fullTPropsonce the tool result arrives. Defaults todefaultProps(or{}).TToolInputThe arguments the model passed when calling the tool, delivered viaui/notifications/tool-input. Defaults to{}when unavailable.TOutput | nullThestructuredContentfield from the tool result. Widget-only data the LLM never sees.nullwhile the tool is still executing.TMetadata | nullThe_metaobject from the tool result (timestamps, cache headers, API version, etc.). Not in the model’s context.nullwhile pending, and alwaysnullon the URL params fallback.TState | nullPersisted widget state the model can read on future turns.nulluntilsetStateis first called. The internal model-context key is stripped from the returned value.(state: TState | ((prevState: TState | null) => TState)) => Promise<void>Update the persisted widget state. Accepts a new value or a functional updater(prev) => next. Sendsui/update-model-contextso the LLM sees the new state next turn. Rapid updates before the next user message are deduplicated by the host.ThemeThe host’s color-scheme preference,"light"or"dark". Defaults to"light". Changes arrive viaui/notifications/host-context-changed.DisplayModeCurrent rendering context:"inline","fullscreen", or"pip". Defaults to"inline". On mobile,"pip"is coerced to"fullscreen"by the host.SafeAreaSafe-area inset boundaries (in pixels) for OS chrome (notch, home indicator, system bars). Defaults to{ insets: { top: 0, bottom: 0, left: 0, right: 0 } }. FromHostContext.safeAreaInsets.numberMaximum height the widget container can grow to, in pixels. Defaults to600. FromHostContext.containerDimensions.maxHeight.number | undefinedMaximum width the widget container can grow to, in pixels. FromHostContext.containerDimensions.maxWidth.undefinedwhen unbounded or unavailable (no default is applied).UserAgentDevice type and input-capability flags. Defaults to{ device: { type: "desktop" }, capabilities: { hover: true, touch: false } }. Derived fromHostContext.platformandHostContext.deviceCapabilities.stringThe user’s BCP 47 language and region tag (for example"en-US"). Defaults toWIDGET_DEFAULTS.LOCALE. FromHostContext.locale.stringThe user’s IANA timezone (for example"America/New_York"). FromHostContext.timeZone. Falls back to the browser timezone (Intl.DateTimeFormat().resolvedOptions().timeZone), or"UTC"when nowindowis present.stringBase URL of the MCP server running inside the sandbox, derived fromwindow.__mcpPublicUrlwith the/mcp-use/publicsuffix removed.""when unavailable.(name: string, args: Record<string, unknown>) => Promise<CallToolResponse>Call any tool registered on the MCP server (including app-only tools). Sendstools/callvia the host and returns a normalizedCallToolResponse.(content: string | MessageContentBlock[]) => Promise<void>Add a user-role message to the conversation and trigger a new model turn. A plain string is wrapped as a singletextblock; an array ofMessageContentBlockis sent as-is. Sendsui/message.(href: string) => voidAsk the host to open a URL in the user’s browser or a new tab viaui/open-link. The host may deny the request; errors are logged, not thrown.(mode: DisplayMode) => Promise<{ mode: DisplayMode }>Request a display-mode change viaui/request-display-mode. The host returns the actual granted mode, which may differ from the requested one.booleantruewhen the MCP AppspostMessagebridge has connected.falsewhile connecting or on the URL params fallback (where it is alwaysfalse).booleantruewhile the tool is still executing; becomesfalseonceui/notifications/tool-resultis received. Whiletrue,propsisPartial<TProps>. On the URL params fallback it istrueuntiltoolOutputis present.Partial<TToolInput> | nullPartial tool arguments streamed in real time while the LLM is still generating them, viaui/notifications/tool-input-partial. A best-effort parse of incomplete JSON (auto-closed brackets), so fields may be missing or change.nullon the URL params fallback.booleantruewhilepartialToolInputis non-null and the completetoolInputhas not yet arrived. Alwaysfalseon the URL params fallback.{ name: string; version: string } | undefinedName and version of the MCP Apps host from theui/initializehandshake.undefinedoutside a supported MCP Apps host.Record<string, unknown> | undefinedCapabilities advertised by the host duringui/initialize(SEP-1865HostCapabilities).undefinedoutside a supported host or when none were sent.HostContext | undefinedRaw host context from the MCP Apps bridge. Includes standardized host style variables athostContext.styles.variableswhen provided.undefinedoutside a supported host.
new MCPServer({ ... }):
useWidgetProps
Convenience wrapper arounduseWidget that returns only the widget props. Equivalent to calling useWidget(defaultProps) and reading the props field.
Type Parameters
Parametersobjectdefault:"UnknownObject"Shape of the returned props.
ReturnsTPropsdefault:"undefined"Optional initial props used as the merge base, forwarded touseWidget.
SignaturePartial<TProps>The merged widget props. Fields may beundefinedwhile the tool is still executing.
useViewState
useViewState replaces the legacy useWidgetState helper. It requires a JSON-serializable object default, shares one state object across the mounted view, and returns a synchronous useState-style setter.
useViewState API reference for host restoration and ModelContext integration.
useWidgetTheme
Convenience wrapper arounduseWidget that returns only the current theme value.
Returns
SignatureThemeThe host’s color-scheme preference,"light"or"dark". Defaults to"light".
Types
UseWidgetResult
The return type ofuseWidget. A discriminated union on isPending over a shared base of fields.
When isPending is true, props is typed as Partial<TProps> (fields may be undefined). When isPending is false, props is typed as the full TProps (all required fields present). Guarding on isPending narrows the type, so after an if (isPending) return ... guard, props field access is safe without optional chaining.
Every field is described in the useWidget Returns section above.
Type Parameters
Signatureobjectdefault:"UnknownObject"Shape ofprops.objectdefault:"UnknownObject"Shape ofstate.objectdefault:"UnknownObject"Shape ofoutput.objectdefault:"UnknownObject"Shape ofmetadata.objectdefault:"UnknownObject"Shape oftoolInput.
API
Type definition for thewindow.openai extension API exposed by the ChatGPT Apps SDK host. useWidget wraps these methods (for example callTool, sendFollowUpMessage, openExternal, requestDisplayMode, setState) so widget code does not call window.openai directly. The interface is parameterized by WidgetState.
Type Parameters
MembersUnknownObjectdefault:"UnknownObject"Shape of the persisted widget state passed tosetWidgetState.
Signature(name: string, args: Record<string, unknown>) => Promise<CallToolResponse>Call a tool on your MCP server and return the full response.(args: { prompt: string }) => Promise<void>Trigger a follow-up turn in the conversation.(payload: { href: string }) => voidOpen an external link, redirect the web page, or open a mobile app.(args: { mode: DisplayMode }) => Promise<{ mode: DisplayMode }>Transition the app between inline, fullscreen, and pip. The host may reject the request, and on mobile pip is always coerced to fullscreen.(state: WidgetState) => Promise<void>Persist widget state that will be shown to the model.(height: number) => Promise<void>Notify OpenAI about intrinsic height changes for auto-sizing.((file: File) => Promise<FileMetadata>) | undefinedUpload a file to the host. Optional: only present when the OpenAI extension exposes file APIs. Detect availability withuseFiles().isSupported.((file: FileMetadata) => Promise<{ downloadUrl: string }>) | undefinedGet a temporary download URL for a previously uploaded file. Optional: only present when the OpenAI extension exposes file APIs.
DisplayMode
The rendering context of a widget."inline" embeds the widget in the host’s content flow, "fullscreen" occupies the full screen or window, and "pip" is a floating picture-in-picture overlay. On mobile, "pip" requests are coerced to "fullscreen" by the host.
Signature
SafeArea
Host-provided safe-area boundaries for a widget. Wraps aSafeAreaInsets value under an insets key. Apply the insets as padding or margin so interactive elements are not hidden behind OS chrome.
Members
SignatureSafeAreaInsetsThe four-sided inset measurements, in pixels.
SafeAreaInsets
Pixel inset measurements for each side of the viewport, describing the space consumed by OS chrome (notch, home indicator, rounded corners, system bars). MembersSignaturenumberInset from the top edge, in pixels.numberInset from the bottom edge, in pixels.numberInset from the left edge, in pixels.numberInset from the right edge, in pixels.
Theme
The host’s color-scheme preference.useWidget defaults this to "light" when no host context is available.
Signature
DeviceType
The device class reported by the host, used asUserAgent.device.type. The MCP Apps bridge maps host platforms to "mobile" or "desktop"; "tablet" and "unknown" are valid members of the type.
Signature
UserAgent
Device type and input-capability flags reported by the host. Usedevice.type to adapt layout density and capabilities.hover / capabilities.touch to decide whether to show hover-only UI or touch-friendly hit targets.
Members
Signature{ type: DeviceType }The reported device class.{ hover: boolean; touch: boolean }Input capabilities: whether the device supports hover and whether it supports touch.
OpenAiGlobals
Type definition for the global state values exposed onwindow.openai by the ChatGPT Apps SDK host (theme, layout, locale, and tool state). useWidget reads equivalent values from the MCP Apps host context, so widget code does not access these globals directly. The interface is parameterized by the tool input, output, response-metadata, and widget-state shapes.
Type Parameters
MembersUnknownObjectdefault:"UnknownObject"Shape of the tool input arguments.UnknownObjectdefault:"UnknownObject"Shape of the tool output.UnknownObjectdefault:"UnknownObject"Shape of the tool response metadata.UnknownObjectdefault:"UnknownObject"Shape of the persisted widget state.
SignatureThemeCurrent color-scheme preference.UserAgentDevice type and input-capability flags.stringThe user’s BCP 47 locale tag.numberMaximum container height, in pixels.DisplayModeCurrent display mode.SafeAreaSafe-area insets for the current layout.ToolInputThe arguments the model passed to the tool.ToolOutput | nullThe tool output, ornullwhile pending.ToolResponseMetadata | nullThe tool response metadata, ornullwhile pending.WidgetState | nullThe persisted widget state, ornullwhen unset.
Widget lifecycle
Widgets render before the tool finishes executing:- First render (
isPending === true): the widget mounts immediately,propsisPartial<TProps>, andoutputandmetadataarenull. Render a loading state here. - After the tool completes (
isPending === false):propsis the fullTProps, andoutputandmetadataare populated. The widget re-renders with full data.
mcp-use dev inspector or MCP Apps clients that send ui/notifications/tool-input-partial), an optional phase occurs between (1) and (2): the widget receives partialToolInput in real time while isStreaming is true, then transitions to full props. On the URL params fallback, partialToolInput stays null and isStreaming stays false.
Default values
When host values are unavailable,useWidget returns these safe defaults:
theme:"light"displayMode:"inline"safeArea:{ insets: { top: 0, bottom: 0, left: 0, right: 0 } }maxHeight:600userAgent:{ device: { type: "desktop" }, capabilities: { hover: true, touch: false } }locale:WIDGET_DEFAULTS.LOCALEtimeZone: browser timezone, or"UTC"when nowindowprops:defaultPropswhen provided, otherwise{}output:nullmetadata:nullstate:nullmaxWidth: no default (staysundefinedwhen unavailable)
Related
McpUseProviderfor unified widget setup including auto-sizing.WidgetControlsfor debug and view controls.ThemeProviderfor theme management.ErrorBoundaryfor widget error handling.