React Native host helpers (MCP Apps)
read as.md@ggui-ai/mcp-apps-react-native is a host-helper library, not an SDK. It gives your app the host half of the MCP Apps contract — mount a UI resource, answer ui/initialize, relay tools/call, open links, tear down. Rendering semantics (freeze latch, history epochs, error surfaces) live inside the mounted view, not in your tree.
Installation
Section titled “Installation”npm install @ggui-ai/mcp-apps-react-native@0.9.0Peer dependencies, installed by your app: react (18 or 19), react-native (>= 0.70), react-native-webview (>= 13), and @modelcontextprotocol/sdk.
| Import path | Contents |
|---|---|
@ggui-ai/mcp-apps-react-native |
<McpAppIframe>, <GguiProvider>, useInvoke, <UiFeedback>, theme |
@ggui-ai/mcp-apps-react-native/chat-helpers |
Message-grouping + render-extraction helpers |
Mounting a render — <McpAppIframe>
Section titled “Mounting a render — <McpAppIframe>”Give the component the UI resource from a tool result plus the render metadata carried on _meta["ai.ggui/render"]. It mounts the resource in a WebView and runs the host side of the handshake:
import { useRef } from "react";import { McpAppIframe } from "@ggui-ai/mcp-apps-react-native";import type { McpAppIframeProps, McpAppIframeRef } from "@ggui-ai/mcp-apps-react-native";
export function AgentCard({ resource, meta, callTool,}: { resource: McpAppIframeProps["resource"]; meta: McpAppIframeProps["meta"]; callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;}) { const ref = useRef<McpAppIframeRef>(null);
return ( <McpAppIframe ref={ref} resource={resource} meta={meta} containerDimensions={{ width: 360, maxHeight: 640 }} onToolCall={callTool} onError={(err) => console.warn("render error", err.kind)} onObserve={(event) => console.log("observe", event.kind)} onLifecycle={(event) => console.log("lifecycle", event.state)} /> );}| Prop | Type | Notes |
|---|---|---|
resource |
ResourceContents |
Required. text mounts as inline HTML, blob as a data URL, otherwise the uri is loaded (http(s) only). |
meta |
McpAppAiGguiRenderMeta |
First-party ggui renders only. Delivered as a ui/notifications/tool-result right after the initialize response. |
onToolCall |
(toolName, args) => Promise<unknown> |
Relay for tools/call. Without it every call is rejected no-tool-handler. |
onUpdateModelContext |
(params) => Promise<void> | void |
Receives ui/update-model-context. Without it the host answers method_not_supported. |
containerDimensions |
{ width?, height?, maxWidth?, maxHeight? } |
Styles the outer <View> and is echoed on ui/initialize.result.hostContext. |
permissions |
{ camera?, microphone?, geolocation?, clipboardWrite? } |
Platform WebView gating. Never reaches ui/initialize. |
locale |
string |
Forwarded as hostContext.locale. Defaults to en-US — RN has no navigator. |
onError |
(err: ProtocolError) => void |
Classified failures, including the ggui:bootstrap-failed envelope. Handlers must not throw. |
onObserve |
(event: ObservabilityEvent) => void |
The ggui:observe channel. Tolerate unknown event.kind. |
onLifecycle |
(event: McpAppLifecycleEvent) => void |
Also mirrored on the outer <View> as accessibilityValue={{ text: state }} whether or not you bind the callback. |
The imperative ref exposes one method, dispatchAction(name, data), which posts a fire-and-forget JSON-RPC notification into the WebView.
ui/open-link with an http(s) URL is delegated to Linking.openURL; other schemes are rejected unsupported-scheme. Set meta only for WebViews you spawned from ggui’s own render resource URI — passing it to third-party MCP App content leaks outer-app state across the adapter boundary.
Relationship to the web package
Section titled “Relationship to the web package”The two packages are twins, and a parity test enforces it. chat-helpers/message-groups.ts, chat-helpers/render.ts, and chat-helpers/useRafThrottled.ts are byte-identical across both packages, and components/GguiProvider.tsx and components/UiFeedback.tsx are documented platform-delta twins whose exported surfaces must match. A one-sided edit fails both suites.
Two deliberate differences:
useMcpAppsChatis web-only. RN’schat-helpersships the platform-neutral subset —invokeMessageToContentGroups,contentGroupsToConversationMessages,conversationMessagesToInvokeHistory,extractRenderFromToolResult,extractSessionIdFromToolResult,useRafThrottled. Drive the stream withuseInvoke(mounted inside<GguiProvider>) and own your message history — the pattern is the chat-own-storage cookbook.<McpAppIframe>is RN-only, for the sandbox-proxy reason above.
Everything else lines up: the same ProtocolError union, the same ObservabilityEvent catalog, the same protocol types, and a React Native theme system (ThemeProvider, useTheme) mirroring the web design tokens.
What was removed
Section titled “What was removed”The legacy in-tree render family — GguiRender, DynamicComponent, WebViewRenderer, NativeRegistry, and their WebSocket stack — was deleted from this package. Generated component code runs inside the WebView, not in your React Native tree; there is no in-process renderer to import.