mcp-ui-ext-apps-openai 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/react.js +22 -8
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +22 -8
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,3 +224,72 @@ if (data.status) {
|
|
|
224
224
|
## License
|
|
225
225
|
|
|
226
226
|
MIT
|
|
227
|
+
|
|
228
|
+
## Examples
|
|
229
|
+
|
|
230
|
+
The `examples/` directory contains a complete counter app example:
|
|
231
|
+
|
|
232
|
+
### Structure
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
examples/
|
|
236
|
+
├── server/ # MCP server with counter tools
|
|
237
|
+
│ ├── server.ts # Express + MCP server
|
|
238
|
+
│ └── package.json
|
|
239
|
+
└── ui/ # React counter app
|
|
240
|
+
├── src/main.tsx # Counter UI using useUnifiedApp
|
|
241
|
+
└── package.json
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Server Example (`examples/server/`)
|
|
245
|
+
|
|
246
|
+
A fully-featured MCP server that:
|
|
247
|
+
- Auto-detects OpenAI vs MCP Apps clients via user-agent
|
|
248
|
+
- Uses proper MIME types (`text/html+skybridge` for OpenAI, standard for MCP)
|
|
249
|
+
- Registers resources with `registerAppResource`
|
|
250
|
+
- Registers tools with `registerAppTool`
|
|
251
|
+
- Manages sessions (stateful, 5 min timeout)
|
|
252
|
+
|
|
253
|
+
**Tools:**
|
|
254
|
+
- `counter` - Widget tool with resource binding
|
|
255
|
+
- `get-counter` - Returns `{ status: true, value: number }`
|
|
256
|
+
- `set-counter` - Accepts `{ value: number }`, returns `{ status: true, value: number }`
|
|
257
|
+
|
|
258
|
+
**Run:**
|
|
259
|
+
```bash
|
|
260
|
+
cd examples/server
|
|
261
|
+
npm install
|
|
262
|
+
npm run dev # Starts on http://localhost:3001
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### UI Example (`examples/ui/`)
|
|
266
|
+
|
|
267
|
+
A React counter app that:
|
|
268
|
+
- Uses `useUnifiedApp` hook for cross-platform support
|
|
269
|
+
- Initializes from `initialProps` (OpenAI) or fetches via `get-counter` (MCP)
|
|
270
|
+
- Shows counter with +/- buttons
|
|
271
|
+
- Optimistically updates UI, reverts on error
|
|
272
|
+
- Persists changes via `set-counter` tool
|
|
273
|
+
|
|
274
|
+
**Run:**
|
|
275
|
+
```bash
|
|
276
|
+
cd examples/ui
|
|
277
|
+
npm install
|
|
278
|
+
npm run build # Builds to dist/index.html
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The built `dist/index.html` is served by the server as the widget HTML resource.
|
|
282
|
+
|
|
283
|
+
### Running the Full Example
|
|
284
|
+
|
|
285
|
+
1. Build the UI:
|
|
286
|
+
```bash
|
|
287
|
+
cd examples/ui && npm install && npm run build
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
2. Start the server:
|
|
291
|
+
```bash
|
|
292
|
+
cd examples/server && npm install && npm run dev
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
3. Connect from OpenAI ChatGPT or MCP Apps client to `http://localhost:3001/mcp`
|
package/dist/react.js
CHANGED
|
@@ -321,7 +321,16 @@ function useUnifiedApp(options) {
|
|
|
321
321
|
const [isConnected, setIsConnected] = (0, import_react.useState)(false);
|
|
322
322
|
const [error, setError] = (0, import_react.useState)(null);
|
|
323
323
|
const [hostContext, setHostContext] = (0, import_react.useState)();
|
|
324
|
-
const [localWidgetState, setLocalWidgetState] = (0, import_react.useState)(
|
|
324
|
+
const [localWidgetState, setLocalWidgetState] = (0, import_react.useState)(() => {
|
|
325
|
+
if (platform === "openai" && typeof window !== "undefined" && window.openai) {
|
|
326
|
+
const openai = window.openai;
|
|
327
|
+
return openai.toolOutput ?? openai.widgetState ?? null;
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
});
|
|
331
|
+
const isOurUpdateRef = (0, import_react.useRef)(false);
|
|
332
|
+
const unifiedAppRef = (0, import_react.useRef)(null);
|
|
333
|
+
unifiedAppRef.current = unifiedApp;
|
|
325
334
|
const openaiWidgetState = useOpenAIGlobal("widgetState", null);
|
|
326
335
|
const openaiWidgetProps = useOpenAIGlobal("widget", {});
|
|
327
336
|
const openaiToolOutput = useOpenAIGlobal("toolOutput", void 0);
|
|
@@ -332,21 +341,26 @@ function useUnifiedApp(options) {
|
|
|
332
341
|
optionsRef.current = options;
|
|
333
342
|
(0, import_react.useEffect)(() => {
|
|
334
343
|
if (platform === "openai" && openaiWidgetState !== null) {
|
|
335
|
-
|
|
344
|
+
if (!isOurUpdateRef.current) {
|
|
345
|
+
setLocalWidgetState(openaiWidgetState);
|
|
346
|
+
}
|
|
347
|
+
isOurUpdateRef.current = false;
|
|
336
348
|
}
|
|
337
349
|
}, [platform, openaiWidgetState]);
|
|
338
350
|
const setWidgetState = (0, import_react.useCallback)((state) => {
|
|
339
351
|
setLocalWidgetState(state);
|
|
340
|
-
if (
|
|
341
|
-
|
|
352
|
+
if (platform === "openai" && unifiedAppRef.current) {
|
|
353
|
+
isOurUpdateRef.current = true;
|
|
354
|
+
unifiedAppRef.current.setWidgetState(state);
|
|
342
355
|
}
|
|
343
|
-
}, [
|
|
356
|
+
}, [platform]);
|
|
344
357
|
const updateWidgetState = (0, import_react.useCallback)((state) => {
|
|
345
358
|
setLocalWidgetState((prev) => ({ ...prev, ...state }));
|
|
346
|
-
if (
|
|
347
|
-
|
|
359
|
+
if (platform === "openai" && unifiedAppRef.current) {
|
|
360
|
+
isOurUpdateRef.current = true;
|
|
361
|
+
unifiedAppRef.current.updateWidgetState(state);
|
|
348
362
|
}
|
|
349
|
-
}, [
|
|
363
|
+
}, [platform]);
|
|
350
364
|
(0, import_react.useEffect)(() => {
|
|
351
365
|
if (platform === "openai" && isConnected) {
|
|
352
366
|
setHostContext({
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.ts","../src/unified-app.ts","../src/use-unified-app.ts"],"sourcesContent":["/**\n * @file React bindings for mcp-ui-ext-apps-openai\n *\n * @example\n * ```tsx\n * import { useUnifiedApp } from \"mcp-ui-ext-apps-openai/react\";\n *\n * function MyApp() {\n * const {\n * app,\n * isConnected,\n * platform,\n * hostContext,\n * widgetState,\n * setWidgetState,\n * } = useUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * onError: console.error,\n * });\n *\n * const counter = (widgetState as { value?: number } | null)?.value ?? 0;\n *\n * return (\n * <div>\n * <p>Platform: {platform}</p>\n * <p>Counter: {counter}</p>\n * <button onClick={() => setWidgetState({ value: counter + 1 })}>+</button>\n * </div>\n * );\n * }\n * ```\n */\n\n// Re-export everything from core\nexport * from \"./index\";\n\n// React hook\nexport {\n useUnifiedApp,\n type UseUnifiedAppOptions,\n type UseUnifiedAppResult,\n} from \"./use-unified-app\";\n","/**\n * @file Unified App Utility - Works with both OpenAI ChatGPT Apps and MCP Apps\n *\n * This utility provides a unified interface that abstracts away the differences\n * between OpenAI's window.openai API and MCP Apps SDK.\n */\n\nimport type { App, McpUiAppCapabilities } from \"@modelcontextprotocol/ext-apps\";\n\n// ============================================================================\n// Types & Interfaces\n// ============================================================================\n\nexport type Platform = \"openai\" | \"mcp\" | \"unknown\";\n\nexport interface UnifiedToolResult {\n content: Array<{ type: string; text?: string; [key: string]: unknown }>;\n structuredContent?: Record<string, unknown>;\n isError?: boolean;\n}\n\nexport interface UnifiedMessage {\n role: \"user\" | \"assistant\";\n content: Array<{ type: string; text?: string; [key: string]: unknown }>;\n}\n\nexport interface UnifiedHostContext {\n theme?: \"light\" | \"dark\";\n displayMode?: \"inline\" | \"pip\" | \"fullscreen\";\n locale?: string;\n safeAreaInsets?: {\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n };\n maxWidth?: number;\n maxHeight?: number;\n}\n\nexport interface UnifiedAppOptions {\n appInfo: { name: string; version: string };\n capabilities?: McpUiAppCapabilities;\n onToolInput?: (input: unknown) => void | Promise<void>;\n onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;\n onHostContextChanged?: (context: UnifiedHostContext) => void;\n onTeardown?: () => void | Promise<void>;\n onError?: (error: Error) => void;\n}\n\nexport interface UnifiedApp {\n /** The underlying platform */\n platform: Platform;\n\n /** Call a server tool by name with arguments */\n callServerTool: (params: { name: string; arguments: Record<string, unknown> }) => Promise<UnifiedToolResult>;\n\n /** Send a message to the host/conversation */\n sendMessage: (message: UnifiedMessage, options?: { signal?: AbortSignal }) => Promise<{ isError: boolean }>;\n\n /** Send a log message to the host */\n sendLog: (params: { level: \"info\" | \"warning\" | \"error\" | \"debug\"; data: string }) => Promise<void>;\n\n /** Open an external link */\n openLink: (params: { url: string }) => Promise<{ isError: boolean }>;\n\n /** Get the current host context */\n getHostContext: () => UnifiedHostContext;\n\n /** Get tool input (initial arguments passed to the tool) */\n getToolInput: () => Record<string, unknown>;\n\n /** Get tool output (result from server) */\n getToolOutput: () => unknown;\n\n /** Get tool response metadata (OpenAI only, returns {} on MCP) */\n getToolResponseMetadata: () => Record<string, unknown>;\n\n /** Get widget state (OpenAI only, returns null on MCP) */\n getWidgetState: <T = unknown>() => T | null;\n\n /** Get widget props (OpenAI only, returns {} on MCP) */\n getWidgetProps: <T = Record<string, unknown>>() => T;\n\n /** Set widget state - persists across renders (OpenAI only, no-op on MCP) */\n setWidgetState: <T = unknown>(state: T) => void;\n\n /** Update widget state - partial update (OpenAI only, no-op on MCP) */\n updateWidgetState: <T = unknown>(state: Partial<T>) => void;\n\n /** Request a specific display mode (OpenAI only, no-op on MCP) */\n requestDisplayMode: (mode: \"inline\" | \"pip\" | \"fullscreen\") => Promise<void>;\n\n /** Request to close the widget (OpenAI only, no-op on MCP) */\n requestClose: () => void;\n\n /** Notify intrinsic height for dynamic sizing (OpenAI only, no-op on MCP) */\n notifyIntrinsicHeight: (height: number) => void;\n\n /** Upload a file (OpenAI only, throws on MCP) */\n uploadFile: (file: File) => Promise<{ fileId: string }>;\n\n /** Get file download URL (OpenAI only, throws on MCP) */\n getFileDownloadUrl: (params: { fileId: string }) => Promise<string>;\n\n /** Set URL for \"Open in App\" button (OpenAI only, no-op on MCP) */\n setOpenInAppUrl: (params: { href: string }) => void;\n\n /** Share content (OpenAI only, throws on MCP) */\n share: (params: unknown) => Promise<void>;\n\n /** Call AI completion (OpenAI only, throws on MCP) */\n callCompletion: (params: unknown) => Promise<unknown>;\n\n /** Stream AI completion (OpenAI only, throws on MCP) */\n streamCompletion: (params: unknown) => AsyncIterable<unknown>;\n\n /** The raw underlying app instance */\n _raw: App | OpenAIGlobal | null;\n}\n\n// OpenAI window.openai type definition based on documentation\nexport interface OpenAIGlobal {\n callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;\n sendFollowUpMessage: (params: { prompt: string }) => Promise<void>;\n openExternal: (params: { href: string }) => Promise<void>;\n setOpenInAppUrl: (params: { href: string }) => void;\n requestDisplayMode: (mode: string) => Promise<void>;\n requestModal: (params: unknown) => Promise<void>;\n requestClose: () => void;\n notifyIntrinsicHeight: (height: number) => void;\n uploadFile: (file: File) => Promise<{ fileId: string }>;\n getFileDownloadUrl: (params: { fileId: string }) => Promise<string>;\n getFileMetadata: (params: { fileId: string }) => Promise<unknown>;\n setWidgetState: (state: unknown) => void;\n updateWidgetState: (state: unknown) => void;\n share: (params: unknown) => Promise<void>;\n streamCompletion: (params: unknown) => AsyncIterable<unknown>;\n callCompletion: (params: unknown) => Promise<unknown>;\n\n // Properties\n toolInput: Record<string, unknown>;\n toolOutput: unknown;\n toolResponseMetadata: Record<string, unknown>;\n widgetState: unknown;\n theme: \"light\" | \"dark\";\n displayMode: \"inline\" | \"pip\" | \"fullscreen\";\n locale: string;\n maxWidth?: number;\n maxHeight?: number;\n safeArea: { insets: { top: number; right: number; bottom: number; left: number } };\n userAgent: { device: unknown; capabilities: unknown };\n view: { params: unknown; mode: string };\n widget: { state: unknown; props: unknown; setState: (state: unknown) => void };\n subjectId: string;\n}\n\n/** Widget props from OpenAI (passed to widget via widget.props) */\nexport interface OpenAIWidgetProps {\n [key: string]: unknown;\n}\n\ndeclare global {\n interface Window {\n openai?: OpenAIGlobal;\n }\n}\n\n// ============================================================================\n// Platform Detection\n// ============================================================================\n\n/**\n * Detect which platform the app is running on\n */\nexport function detectPlatform(): Platform {\n if (typeof window !== \"undefined\" && window.openai) {\n return \"openai\";\n }\n // MCP apps are detected by the useApp hook connecting successfully\n // For now, return \"unknown\" and let the hook determine\n return \"unknown\";\n}\n\n/**\n * Check if running in OpenAI ChatGPT environment\n */\nexport function isOpenAI(): boolean {\n return detectPlatform() === \"openai\";\n}\n\n/**\n * Check if running in MCP Apps environment\n */\nexport function isMCP(): boolean {\n return detectPlatform() === \"mcp\" || detectPlatform() === \"unknown\";\n}\n\n// ============================================================================\n// OpenAI Adapter\n// ============================================================================\n\n/**\n * Create a UnifiedApp from OpenAI's window.openai\n */\nexport function createOpenAIAdapter(options: UnifiedAppOptions): UnifiedApp {\n const openai = window.openai!;\n\n // Set up event listeners for OpenAI\n const handleGlobalsChange = (_event: Event) => {\n if (options.onHostContextChanged) {\n options.onHostContextChanged({\n theme: openai.theme,\n displayMode: openai.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: openai.locale,\n safeAreaInsets: openai.safeArea?.insets,\n maxWidth: openai.maxWidth,\n maxHeight: openai.maxHeight,\n });\n }\n };\n\n window.addEventListener(\"openai:set_globals\", handleGlobalsChange);\n\n // Initial context callback\n if (options.onHostContextChanged) {\n options.onHostContextChanged({\n theme: openai.theme,\n displayMode: openai.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: openai.locale,\n safeAreaInsets: openai.safeArea?.insets,\n maxWidth: openai.maxWidth,\n maxHeight: openai.maxHeight,\n });\n }\n\n // If there's initial tool output, notify\n if (options.onToolResult && openai.toolOutput) {\n const result = convertOpenAIToolOutput(openai.toolOutput);\n options.onToolResult(result);\n }\n\n if (options.onToolInput && openai.toolInput) {\n options.onToolInput(openai.toolInput);\n }\n\n return {\n platform: \"openai\",\n\n callServerTool: async ({ name, arguments: args }) => {\n try {\n const result = await openai.callTool(name, args);\n return convertOpenAIToolOutput(result);\n } catch (error) {\n if (options.onError) {\n options.onError(error as Error);\n }\n throw error;\n }\n },\n\n sendMessage: async (message, _opts) => {\n try {\n // OpenAI uses sendFollowUpMessage for user messages\n const textContent = message.content.find(c => c.type === \"text\");\n if (textContent?.text) {\n await openai.sendFollowUpMessage({ prompt: textContent.text });\n }\n return { isError: false };\n } catch (error) {\n if (options.onError) {\n options.onError(error as Error);\n }\n return { isError: true };\n }\n },\n\n sendLog: async ({ level, data }) => {\n // OpenAI doesn't have a direct log API, use console as fallback\n // Map 'warning' to 'warn' for console compatibility\n const consoleLevel = level === \"warning\" ? \"warn\" : level;\n console[consoleLevel](`[${options.appInfo.name}]`, data);\n },\n\n openLink: async ({ url }) => {\n try {\n await openai.openExternal({ href: url });\n return { isError: false };\n } catch (error) {\n if (options.onError) {\n options.onError(error as Error);\n }\n return { isError: true };\n }\n },\n\n getHostContext: () => ({\n theme: openai.theme,\n displayMode: openai.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: openai.locale,\n safeAreaInsets: openai.safeArea?.insets,\n maxWidth: openai.maxWidth,\n maxHeight: openai.maxHeight,\n }),\n\n getToolInput: () => openai.toolInput || {},\n\n getToolOutput: () => openai.toolOutput,\n\n getToolResponseMetadata: () => openai.toolResponseMetadata || {},\n\n getWidgetState: <T = unknown>() => openai.widgetState as T | null,\n\n getWidgetProps: <T = Record<string, unknown>>() => (openai.widget?.props || {}) as T,\n\n setWidgetState: <T = unknown>(state: T) => {\n openai.setWidgetState(state);\n },\n\n updateWidgetState: <T = unknown>(state: Partial<T>) => {\n openai.updateWidgetState(state);\n },\n\n requestDisplayMode: async (mode) => {\n await openai.requestDisplayMode(mode);\n },\n\n requestClose: () => {\n openai.requestClose();\n },\n\n notifyIntrinsicHeight: (height) => {\n openai.notifyIntrinsicHeight(height);\n },\n\n uploadFile: async (file) => {\n return await openai.uploadFile(file);\n },\n\n getFileDownloadUrl: async ({ fileId }) => {\n return await openai.getFileDownloadUrl({ fileId });\n },\n\n setOpenInAppUrl: ({ href }) => {\n openai.setOpenInAppUrl({ href });\n },\n\n share: async (params) => {\n await openai.share(params);\n },\n\n callCompletion: async (params) => {\n return await openai.callCompletion(params);\n },\n\n streamCompletion: (params) => {\n return openai.streamCompletion(params);\n },\n\n _raw: openai,\n };\n}\n\n/**\n * Convert OpenAI tool output to unified format\n */\nfunction convertOpenAIToolOutput(output: unknown): UnifiedToolResult {\n // OpenAI tool output can be various formats\n if (typeof output === \"string\") {\n return {\n content: [{ type: \"text\", text: output }],\n };\n }\n\n if (output && typeof output === \"object\") {\n // Check if it's already in MCP-like format\n if (\"content\" in output && Array.isArray((output as { content: unknown }).content)) {\n return output as UnifiedToolResult;\n }\n\n // Check for text property\n if (\"text\" in output) {\n return {\n content: [{ type: \"text\", text: String((output as { text: unknown }).text) }],\n };\n }\n\n // Serialize object as JSON text\n return {\n content: [{ type: \"text\", text: JSON.stringify(output) }],\n };\n }\n\n return {\n content: [{ type: \"text\", text: String(output) }],\n };\n}\n\n// ============================================================================\n// MCP Adapter\n// ============================================================================\n\n/**\n * Create a UnifiedApp from MCP App instance\n */\nexport function createMCPAdapter(app: App, _options: UnifiedAppOptions): UnifiedApp {\n return {\n platform: \"mcp\",\n\n callServerTool: async ({ name, arguments: args }) => {\n const result = await app.callServerTool({ name, arguments: args });\n return result as UnifiedToolResult;\n },\n\n sendMessage: async (message, opts) => {\n const result = await app.sendMessage(message as Parameters<typeof app.sendMessage>[0], opts);\n return { isError: result.isError ?? false };\n },\n\n sendLog: async ({ level, data }) => {\n await app.sendLog({ level, data });\n },\n\n openLink: async ({ url }) => {\n const result = await app.openLink({ url });\n return { isError: result.isError ?? false };\n },\n\n getHostContext: () => {\n const ctx = app.getHostContext();\n return {\n theme: ctx?.theme as UnifiedHostContext[\"theme\"],\n displayMode: ctx?.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: ctx?.locale,\n safeAreaInsets: ctx?.safeAreaInsets,\n };\n },\n\n getToolInput: () => ({}), // MCP handles this via ontoolinput callback\n\n getToolOutput: () => null, // MCP handles this via ontoolresult callback\n\n // OpenAI-specific methods - no-op or stub implementations for MCP\n getToolResponseMetadata: () => ({}),\n\n getWidgetState: <T = unknown>() => null as T | null,\n\n getWidgetProps: <T = Record<string, unknown>>() => ({} as T),\n\n setWidgetState: <T = unknown>(_state: T) => {\n // No-op on MCP\n },\n\n updateWidgetState: <T = unknown>(_state: Partial<T>) => {\n // No-op on MCP\n },\n\n requestDisplayMode: async (_mode) => {\n // No-op on MCP\n },\n\n requestClose: () => {\n // No-op on MCP\n },\n\n notifyIntrinsicHeight: (_height) => {\n // No-op on MCP\n },\n\n uploadFile: async (_file) => {\n throw new Error(\"uploadFile is not supported on MCP platform\");\n },\n\n getFileDownloadUrl: async (_params) => {\n throw new Error(\"getFileDownloadUrl is not supported on MCP platform\");\n },\n\n setOpenInAppUrl: (_params) => {\n // No-op on MCP\n },\n\n share: async (_params) => {\n throw new Error(\"share is not supported on MCP platform\");\n },\n\n callCompletion: async (_params) => {\n throw new Error(\"callCompletion is not supported on MCP platform\");\n },\n\n streamCompletion: (_params) => {\n throw new Error(\"streamCompletion is not supported on MCP platform\");\n },\n\n _raw: app,\n };\n}\n\n// ============================================================================\n// Unified Factory\n// ============================================================================\n\nexport interface CreateUnifiedAppResult {\n app: UnifiedApp | null;\n isConnected: boolean;\n error: Error | null;\n platform: Platform;\n}\n\n/**\n * Create a unified app - automatically detects platform and creates appropriate adapter\n *\n * For OpenAI: Returns immediately with the adapter\n * For MCP: Returns null app (use the React hook instead for MCP)\n */\nexport function createUnifiedApp(options: UnifiedAppOptions): CreateUnifiedAppResult {\n const platform = detectPlatform();\n\n if (platform === \"openai\") {\n try {\n const app = createOpenAIAdapter(options);\n return {\n app,\n isConnected: true,\n error: null,\n platform: \"openai\",\n };\n } catch (error) {\n return {\n app: null,\n isConnected: false,\n error: error as Error,\n platform: \"openai\",\n };\n }\n }\n\n // For MCP, return null - the React hook will handle connection\n return {\n app: null,\n isConnected: false,\n error: null,\n platform: \"unknown\",\n };\n}\n","/**\n * @file React hook for unified OpenAI/MCP app usage\n *\n * This hook provides a unified interface for apps that need to run on both\n * OpenAI ChatGPT and MCP Apps platforms.\n */\n\nimport type { App, McpUiAppCapabilities } from \"@modelcontextprotocol/ext-apps\";\nimport { PostMessageTransport } from \"@modelcontextprotocol/ext-apps\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n createMCPAdapter,\n createOpenAIAdapter,\n detectPlatform,\n type Platform,\n type UnifiedApp,\n type UnifiedHostContext,\n type UnifiedToolResult,\n} from \"./unified-app\";\n\nexport interface UseUnifiedAppOptions {\n appInfo: { name: string; version: string };\n capabilities?: McpUiAppCapabilities;\n onToolInput?: (input: unknown) => void | Promise<void>;\n onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;\n onHostContextChanged?: (context: UnifiedHostContext) => void;\n onTeardown?: () => void | Promise<void>;\n onError?: (error: Error) => void;\n}\n\nexport interface UseUnifiedAppResult {\n /** The unified app instance */\n app: UnifiedApp | null;\n /** Whether the app is connected to the host */\n isConnected: boolean;\n /** Any connection error */\n error: Error | null;\n /** The detected platform */\n platform: Platform;\n /** Current host context (theme, displayMode, locale, etc.) */\n hostContext: UnifiedHostContext | undefined;\n /** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */\n initialProps: unknown;\n /** Widget props (OpenAI only, reactive) */\n widgetProps: Record<string, unknown>;\n /** Widget state - works on both platforms via React state */\n widgetState: unknown;\n /** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */\n setWidgetState: <T = unknown>(state: T) => void;\n /** Update widget state partially - works on both platforms */\n updateWidgetState: <T = unknown>(state: Partial<T>) => void;\n}\n\n/**\n * Hook to subscribe to OpenAI global property changes\n * Listens for 'openai:set_globals' events\n */\nfunction useOpenAIGlobal<T>(key: string, initialValue: T): T {\n const [value, setValue] = useState<T>(initialValue);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !window.openai) return;\n\n // Get initial value - cast to unknown first then to Record\n const openai = window.openai as unknown as Record<string, unknown>;\n if (key in openai) {\n setValue(openai[key] as T);\n }\n\n // Listen for changes\n const handleChange = () => {\n const currentOpenai = window.openai as unknown as Record<string, unknown>;\n if (currentOpenai && key in currentOpenai) {\n setValue(currentOpenai[key] as T);\n }\n };\n\n window.addEventListener(\"openai:set_globals\", handleChange);\n return () => window.removeEventListener(\"openai:set_globals\", handleChange);\n }, [key]);\n\n return value;\n}\n\n/**\n * React hook that provides a unified app interface for both OpenAI and MCP platforms\n *\n * @example\n * ```tsx\n * function MyApp() {\n * const {\n * app,\n * isConnected,\n * platform,\n * hostContext,\n * widgetState,\n * setWidgetState,\n * } = useUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * capabilities: {},\n * onError: console.error,\n * });\n *\n * const counter = (widgetState as { value?: number } | null)?.value ?? 0;\n *\n * return (\n * <div>\n * <p>Running on: {platform}</p>\n * <p>Counter: {counter}</p>\n * <button onClick={() => setWidgetState({ value: counter + 1 })}>\n * Increment\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult {\n // Detect platform once on mount\n const platformRef = useRef<Platform>(detectPlatform());\n const platform = platformRef.current;\n\n const [unifiedApp, setUnifiedApp] = useState<UnifiedApp | null>(null);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [hostContext, setHostContext] = useState<UnifiedHostContext | undefined>();\n\n // React state for widgetState - works on both platforms\n const [localWidgetState, setLocalWidgetState] = useState<unknown>(null);\n\n // OpenAI reactive globals\n const openaiWidgetState = useOpenAIGlobal<unknown>(\"widgetState\", null);\n const openaiWidgetProps = useOpenAIGlobal<Record<string, unknown>>(\"widget\", {});\n const openaiToolOutput = useOpenAIGlobal<unknown>(\"toolOutput\", undefined);\n const openaiTheme = useOpenAIGlobal<\"light\" | \"dark\">(\"theme\", \"light\");\n const openaiDisplayMode = useOpenAIGlobal<string>(\"displayMode\", \"inline\");\n const openaiLocale = useOpenAIGlobal<string>(\"locale\", \"en-US\");\n\n // Store options in ref to avoid stale closures\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Sync OpenAI widgetState to local state when it changes\n useEffect(() => {\n if (platform === \"openai\" && openaiWidgetState !== null) {\n setLocalWidgetState(openaiWidgetState);\n }\n }, [platform, openaiWidgetState]);\n\n // Widget state setters - work on both platforms via React state\n // Also syncs to OpenAI if on that platform\n const setWidgetState = useCallback(<T = unknown>(state: T) => {\n setLocalWidgetState(state);\n if (unifiedApp && platform === \"openai\") {\n unifiedApp.setWidgetState(state);\n }\n }, [unifiedApp, platform]);\n\n const updateWidgetState = useCallback(<T = unknown>(state: Partial<T>) => {\n setLocalWidgetState((prev: unknown) => ({ ...(prev as object), ...state }));\n if (unifiedApp && platform === \"openai\") {\n unifiedApp.updateWidgetState(state);\n }\n }, [unifiedApp, platform]);\n\n // Update host context from OpenAI globals\n useEffect(() => {\n if (platform === \"openai\" && isConnected) {\n setHostContext({\n theme: openaiTheme,\n displayMode: openaiDisplayMode as UnifiedHostContext[\"displayMode\"],\n locale: openaiLocale,\n safeAreaInsets: window.openai?.safeArea?.insets,\n maxWidth: window.openai?.maxWidth,\n maxHeight: window.openai?.maxHeight,\n });\n }\n }, [platform, isConnected, openaiTheme, openaiDisplayMode, openaiLocale]);\n\n useEffect(() => {\n let mounted = true;\n let mcpApp: App | null = null;\n\n async function initialize() {\n const opts = optionsRef.current;\n\n if (platform === \"openai\") {\n // OpenAI platform - use the adapter directly\n try {\n const adapter = createOpenAIAdapter({\n ...opts,\n onToolResult: (result) => {\n opts.onToolResult?.(result);\n },\n onHostContextChanged: (ctx) => {\n if (mounted) {\n setHostContext(ctx);\n opts.onHostContextChanged?.(ctx);\n }\n },\n onError: (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err);\n },\n });\n\n if (mounted) {\n setUnifiedApp(adapter);\n setIsConnected(true);\n setHostContext(adapter.getHostContext());\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err as Error);\n setIsConnected(false);\n }\n }\n } else {\n // MCP platform - need to dynamically import and create connection\n try {\n // Dynamic import to avoid issues when running on OpenAI\n const { App } = await import(\"@modelcontextprotocol/ext-apps\");\n\n const transport = new PostMessageTransport(window.parent, window.parent);\n mcpApp = new App(opts.appInfo, opts.capabilities || {});\n\n // Set up handlers before connecting\n mcpApp.onteardown = async () => {\n await opts.onTeardown?.();\n return {};\n };\n\n mcpApp.ontoolinput = async (input) => {\n await opts.onToolInput?.(input);\n };\n\n mcpApp.ontoolresult = async (result) => {\n await opts.onToolResult?.(result as UnifiedToolResult);\n };\n\n mcpApp.onerror = (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err instanceof Error ? err : new Error(String(err)));\n };\n\n mcpApp.onhostcontextchanged = (params) => {\n if (mounted) {\n setHostContext((prev) => {\n const newCtx = { ...prev, ...params } as UnifiedHostContext;\n opts.onHostContextChanged?.(newCtx);\n return newCtx;\n });\n }\n };\n\n await mcpApp.connect(transport);\n\n if (mounted) {\n const adapter = createMCPAdapter(mcpApp, opts);\n setUnifiedApp(adapter);\n setIsConnected(true);\n\n // Get initial host context\n const ctx = mcpApp.getHostContext();\n if (ctx) {\n setHostContext({\n theme: ctx.theme as UnifiedHostContext[\"theme\"],\n displayMode: ctx.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: ctx.locale,\n safeAreaInsets: ctx.safeAreaInsets,\n });\n }\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err instanceof Error ? err : new Error(\"Failed to connect\"));\n setIsConnected(false);\n }\n }\n }\n }\n\n initialize();\n\n return () => {\n mounted = false;\n // Note: MCP App cleanup would go here if needed\n };\n }, [platform]);\n\n // Determine final platform based on connection state\n const finalPlatform: Platform = useMemo(() => {\n if (platform === \"openai\") return \"openai\";\n if (isConnected) return \"mcp\";\n return \"unknown\";\n }, [platform, isConnected]);\n\n // Return platform-appropriate values\n const widgetProps = platform === \"openai\" ? ((openaiWidgetProps as { props?: Record<string, unknown> })?.props || {}) : {};\n const initialProps = platform === \"openai\" ? openaiToolOutput : undefined;\n\n return {\n app: unifiedApp,\n isConnected,\n error,\n platform: finalPlatform,\n hostContext,\n initialProps,\n widgetProps,\n widgetState: localWidgetState,\n setWidgetState,\n updateWidgetState,\n };\n}\n\nexport default useUnifiedApp;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.ts","../src/unified-app.ts","../src/use-unified-app.ts"],"sourcesContent":["/**\n * @file React bindings for mcp-ui-ext-apps-openai\n *\n * @example\n * ```tsx\n * import { useUnifiedApp } from \"mcp-ui-ext-apps-openai/react\";\n *\n * function MyApp() {\n * const {\n * app,\n * isConnected,\n * platform,\n * hostContext,\n * widgetState,\n * setWidgetState,\n * } = useUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * onError: console.error,\n * });\n *\n * const counter = (widgetState as { value?: number } | null)?.value ?? 0;\n *\n * return (\n * <div>\n * <p>Platform: {platform}</p>\n * <p>Counter: {counter}</p>\n * <button onClick={() => setWidgetState({ value: counter + 1 })}>+</button>\n * </div>\n * );\n * }\n * ```\n */\n\n// Re-export everything from core\nexport * from \"./index\";\n\n// React hook\nexport {\n useUnifiedApp,\n type UseUnifiedAppOptions,\n type UseUnifiedAppResult,\n} from \"./use-unified-app\";\n","/**\n * @file Unified App Utility - Works with both OpenAI ChatGPT Apps and MCP Apps\n *\n * This utility provides a unified interface that abstracts away the differences\n * between OpenAI's window.openai API and MCP Apps SDK.\n */\n\nimport type { App, McpUiAppCapabilities } from \"@modelcontextprotocol/ext-apps\";\n\n// ============================================================================\n// Types & Interfaces\n// ============================================================================\n\nexport type Platform = \"openai\" | \"mcp\" | \"unknown\";\n\nexport interface UnifiedToolResult {\n content: Array<{ type: string; text?: string; [key: string]: unknown }>;\n structuredContent?: Record<string, unknown>;\n isError?: boolean;\n}\n\nexport interface UnifiedMessage {\n role: \"user\" | \"assistant\";\n content: Array<{ type: string; text?: string; [key: string]: unknown }>;\n}\n\nexport interface UnifiedHostContext {\n theme?: \"light\" | \"dark\";\n displayMode?: \"inline\" | \"pip\" | \"fullscreen\";\n locale?: string;\n safeAreaInsets?: {\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n };\n maxWidth?: number;\n maxHeight?: number;\n}\n\nexport interface UnifiedAppOptions {\n appInfo: { name: string; version: string };\n capabilities?: McpUiAppCapabilities;\n onToolInput?: (input: unknown) => void | Promise<void>;\n onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;\n onHostContextChanged?: (context: UnifiedHostContext) => void;\n onTeardown?: () => void | Promise<void>;\n onError?: (error: Error) => void;\n}\n\nexport interface UnifiedApp {\n /** The underlying platform */\n platform: Platform;\n\n /** Call a server tool by name with arguments */\n callServerTool: (params: { name: string; arguments: Record<string, unknown> }) => Promise<UnifiedToolResult>;\n\n /** Send a message to the host/conversation */\n sendMessage: (message: UnifiedMessage, options?: { signal?: AbortSignal }) => Promise<{ isError: boolean }>;\n\n /** Send a log message to the host */\n sendLog: (params: { level: \"info\" | \"warning\" | \"error\" | \"debug\"; data: string }) => Promise<void>;\n\n /** Open an external link */\n openLink: (params: { url: string }) => Promise<{ isError: boolean }>;\n\n /** Get the current host context */\n getHostContext: () => UnifiedHostContext;\n\n /** Get tool input (initial arguments passed to the tool) */\n getToolInput: () => Record<string, unknown>;\n\n /** Get tool output (result from server) */\n getToolOutput: () => unknown;\n\n /** Get tool response metadata (OpenAI only, returns {} on MCP) */\n getToolResponseMetadata: () => Record<string, unknown>;\n\n /** Get widget state (OpenAI only, returns null on MCP) */\n getWidgetState: <T = unknown>() => T | null;\n\n /** Get widget props (OpenAI only, returns {} on MCP) */\n getWidgetProps: <T = Record<string, unknown>>() => T;\n\n /** Set widget state - persists across renders (OpenAI only, no-op on MCP) */\n setWidgetState: <T = unknown>(state: T) => void;\n\n /** Update widget state - partial update (OpenAI only, no-op on MCP) */\n updateWidgetState: <T = unknown>(state: Partial<T>) => void;\n\n /** Request a specific display mode (OpenAI only, no-op on MCP) */\n requestDisplayMode: (mode: \"inline\" | \"pip\" | \"fullscreen\") => Promise<void>;\n\n /** Request to close the widget (OpenAI only, no-op on MCP) */\n requestClose: () => void;\n\n /** Notify intrinsic height for dynamic sizing (OpenAI only, no-op on MCP) */\n notifyIntrinsicHeight: (height: number) => void;\n\n /** Upload a file (OpenAI only, throws on MCP) */\n uploadFile: (file: File) => Promise<{ fileId: string }>;\n\n /** Get file download URL (OpenAI only, throws on MCP) */\n getFileDownloadUrl: (params: { fileId: string }) => Promise<string>;\n\n /** Set URL for \"Open in App\" button (OpenAI only, no-op on MCP) */\n setOpenInAppUrl: (params: { href: string }) => void;\n\n /** Share content (OpenAI only, throws on MCP) */\n share: (params: unknown) => Promise<void>;\n\n /** Call AI completion (OpenAI only, throws on MCP) */\n callCompletion: (params: unknown) => Promise<unknown>;\n\n /** Stream AI completion (OpenAI only, throws on MCP) */\n streamCompletion: (params: unknown) => AsyncIterable<unknown>;\n\n /** The raw underlying app instance */\n _raw: App | OpenAIGlobal | null;\n}\n\n// OpenAI window.openai type definition based on documentation\nexport interface OpenAIGlobal {\n callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;\n sendFollowUpMessage: (params: { prompt: string }) => Promise<void>;\n openExternal: (params: { href: string }) => Promise<void>;\n setOpenInAppUrl: (params: { href: string }) => void;\n requestDisplayMode: (mode: string) => Promise<void>;\n requestModal: (params: unknown) => Promise<void>;\n requestClose: () => void;\n notifyIntrinsicHeight: (height: number) => void;\n uploadFile: (file: File) => Promise<{ fileId: string }>;\n getFileDownloadUrl: (params: { fileId: string }) => Promise<string>;\n getFileMetadata: (params: { fileId: string }) => Promise<unknown>;\n setWidgetState: (state: unknown) => void;\n updateWidgetState: (state: unknown) => void;\n share: (params: unknown) => Promise<void>;\n streamCompletion: (params: unknown) => AsyncIterable<unknown>;\n callCompletion: (params: unknown) => Promise<unknown>;\n\n // Properties\n toolInput: Record<string, unknown>;\n toolOutput: unknown;\n toolResponseMetadata: Record<string, unknown>;\n widgetState: unknown;\n theme: \"light\" | \"dark\";\n displayMode: \"inline\" | \"pip\" | \"fullscreen\";\n locale: string;\n maxWidth?: number;\n maxHeight?: number;\n safeArea: { insets: { top: number; right: number; bottom: number; left: number } };\n userAgent: { device: unknown; capabilities: unknown };\n view: { params: unknown; mode: string };\n widget: { state: unknown; props: unknown; setState: (state: unknown) => void };\n subjectId: string;\n}\n\n/** Widget props from OpenAI (passed to widget via widget.props) */\nexport interface OpenAIWidgetProps {\n [key: string]: unknown;\n}\n\ndeclare global {\n interface Window {\n openai?: OpenAIGlobal;\n }\n}\n\n// ============================================================================\n// Platform Detection\n// ============================================================================\n\n/**\n * Detect which platform the app is running on\n */\nexport function detectPlatform(): Platform {\n if (typeof window !== \"undefined\" && window.openai) {\n return \"openai\";\n }\n // MCP apps are detected by the useApp hook connecting successfully\n // For now, return \"unknown\" and let the hook determine\n return \"unknown\";\n}\n\n/**\n * Check if running in OpenAI ChatGPT environment\n */\nexport function isOpenAI(): boolean {\n return detectPlatform() === \"openai\";\n}\n\n/**\n * Check if running in MCP Apps environment\n */\nexport function isMCP(): boolean {\n return detectPlatform() === \"mcp\" || detectPlatform() === \"unknown\";\n}\n\n// ============================================================================\n// OpenAI Adapter\n// ============================================================================\n\n/**\n * Create a UnifiedApp from OpenAI's window.openai\n */\nexport function createOpenAIAdapter(options: UnifiedAppOptions): UnifiedApp {\n const openai = window.openai!;\n\n // Set up event listeners for OpenAI\n const handleGlobalsChange = (_event: Event) => {\n if (options.onHostContextChanged) {\n options.onHostContextChanged({\n theme: openai.theme,\n displayMode: openai.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: openai.locale,\n safeAreaInsets: openai.safeArea?.insets,\n maxWidth: openai.maxWidth,\n maxHeight: openai.maxHeight,\n });\n }\n };\n\n window.addEventListener(\"openai:set_globals\", handleGlobalsChange);\n\n // Initial context callback\n if (options.onHostContextChanged) {\n options.onHostContextChanged({\n theme: openai.theme,\n displayMode: openai.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: openai.locale,\n safeAreaInsets: openai.safeArea?.insets,\n maxWidth: openai.maxWidth,\n maxHeight: openai.maxHeight,\n });\n }\n\n // If there's initial tool output, notify\n if (options.onToolResult && openai.toolOutput) {\n const result = convertOpenAIToolOutput(openai.toolOutput);\n options.onToolResult(result);\n }\n\n if (options.onToolInput && openai.toolInput) {\n options.onToolInput(openai.toolInput);\n }\n\n return {\n platform: \"openai\",\n\n callServerTool: async ({ name, arguments: args }) => {\n try {\n const result = await openai.callTool(name, args);\n return convertOpenAIToolOutput(result);\n } catch (error) {\n if (options.onError) {\n options.onError(error as Error);\n }\n throw error;\n }\n },\n\n sendMessage: async (message, _opts) => {\n try {\n // OpenAI uses sendFollowUpMessage for user messages\n const textContent = message.content.find(c => c.type === \"text\");\n if (textContent?.text) {\n await openai.sendFollowUpMessage({ prompt: textContent.text });\n }\n return { isError: false };\n } catch (error) {\n if (options.onError) {\n options.onError(error as Error);\n }\n return { isError: true };\n }\n },\n\n sendLog: async ({ level, data }) => {\n // OpenAI doesn't have a direct log API, use console as fallback\n // Map 'warning' to 'warn' for console compatibility\n const consoleLevel = level === \"warning\" ? \"warn\" : level;\n console[consoleLevel](`[${options.appInfo.name}]`, data);\n },\n\n openLink: async ({ url }) => {\n try {\n await openai.openExternal({ href: url });\n return { isError: false };\n } catch (error) {\n if (options.onError) {\n options.onError(error as Error);\n }\n return { isError: true };\n }\n },\n\n getHostContext: () => ({\n theme: openai.theme,\n displayMode: openai.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: openai.locale,\n safeAreaInsets: openai.safeArea?.insets,\n maxWidth: openai.maxWidth,\n maxHeight: openai.maxHeight,\n }),\n\n getToolInput: () => openai.toolInput || {},\n\n getToolOutput: () => openai.toolOutput,\n\n getToolResponseMetadata: () => openai.toolResponseMetadata || {},\n\n getWidgetState: <T = unknown>() => openai.widgetState as T | null,\n\n getWidgetProps: <T = Record<string, unknown>>() => (openai.widget?.props || {}) as T,\n\n setWidgetState: <T = unknown>(state: T) => {\n openai.setWidgetState(state);\n },\n\n updateWidgetState: <T = unknown>(state: Partial<T>) => {\n openai.updateWidgetState(state);\n },\n\n requestDisplayMode: async (mode) => {\n await openai.requestDisplayMode(mode);\n },\n\n requestClose: () => {\n openai.requestClose();\n },\n\n notifyIntrinsicHeight: (height) => {\n openai.notifyIntrinsicHeight(height);\n },\n\n uploadFile: async (file) => {\n return await openai.uploadFile(file);\n },\n\n getFileDownloadUrl: async ({ fileId }) => {\n return await openai.getFileDownloadUrl({ fileId });\n },\n\n setOpenInAppUrl: ({ href }) => {\n openai.setOpenInAppUrl({ href });\n },\n\n share: async (params) => {\n await openai.share(params);\n },\n\n callCompletion: async (params) => {\n return await openai.callCompletion(params);\n },\n\n streamCompletion: (params) => {\n return openai.streamCompletion(params);\n },\n\n _raw: openai,\n };\n}\n\n/**\n * Convert OpenAI tool output to unified format\n */\nfunction convertOpenAIToolOutput(output: unknown): UnifiedToolResult {\n // OpenAI tool output can be various formats\n if (typeof output === \"string\") {\n return {\n content: [{ type: \"text\", text: output }],\n };\n }\n\n if (output && typeof output === \"object\") {\n // Check if it's already in MCP-like format\n if (\"content\" in output && Array.isArray((output as { content: unknown }).content)) {\n return output as UnifiedToolResult;\n }\n\n // Check for text property\n if (\"text\" in output) {\n return {\n content: [{ type: \"text\", text: String((output as { text: unknown }).text) }],\n };\n }\n\n // Serialize object as JSON text\n return {\n content: [{ type: \"text\", text: JSON.stringify(output) }],\n };\n }\n\n return {\n content: [{ type: \"text\", text: String(output) }],\n };\n}\n\n// ============================================================================\n// MCP Adapter\n// ============================================================================\n\n/**\n * Create a UnifiedApp from MCP App instance\n */\nexport function createMCPAdapter(app: App, _options: UnifiedAppOptions): UnifiedApp {\n return {\n platform: \"mcp\",\n\n callServerTool: async ({ name, arguments: args }) => {\n const result = await app.callServerTool({ name, arguments: args });\n return result as UnifiedToolResult;\n },\n\n sendMessage: async (message, opts) => {\n const result = await app.sendMessage(message as Parameters<typeof app.sendMessage>[0], opts);\n return { isError: result.isError ?? false };\n },\n\n sendLog: async ({ level, data }) => {\n await app.sendLog({ level, data });\n },\n\n openLink: async ({ url }) => {\n const result = await app.openLink({ url });\n return { isError: result.isError ?? false };\n },\n\n getHostContext: () => {\n const ctx = app.getHostContext();\n return {\n theme: ctx?.theme as UnifiedHostContext[\"theme\"],\n displayMode: ctx?.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: ctx?.locale,\n safeAreaInsets: ctx?.safeAreaInsets,\n };\n },\n\n getToolInput: () => ({}), // MCP handles this via ontoolinput callback\n\n getToolOutput: () => null, // MCP handles this via ontoolresult callback\n\n // OpenAI-specific methods - no-op or stub implementations for MCP\n getToolResponseMetadata: () => ({}),\n\n getWidgetState: <T = unknown>() => null as T | null,\n\n getWidgetProps: <T = Record<string, unknown>>() => ({} as T),\n\n setWidgetState: <T = unknown>(_state: T) => {\n // No-op on MCP\n },\n\n updateWidgetState: <T = unknown>(_state: Partial<T>) => {\n // No-op on MCP\n },\n\n requestDisplayMode: async (_mode) => {\n // No-op on MCP\n },\n\n requestClose: () => {\n // No-op on MCP\n },\n\n notifyIntrinsicHeight: (_height) => {\n // No-op on MCP\n },\n\n uploadFile: async (_file) => {\n throw new Error(\"uploadFile is not supported on MCP platform\");\n },\n\n getFileDownloadUrl: async (_params) => {\n throw new Error(\"getFileDownloadUrl is not supported on MCP platform\");\n },\n\n setOpenInAppUrl: (_params) => {\n // No-op on MCP\n },\n\n share: async (_params) => {\n throw new Error(\"share is not supported on MCP platform\");\n },\n\n callCompletion: async (_params) => {\n throw new Error(\"callCompletion is not supported on MCP platform\");\n },\n\n streamCompletion: (_params) => {\n throw new Error(\"streamCompletion is not supported on MCP platform\");\n },\n\n _raw: app,\n };\n}\n\n// ============================================================================\n// Unified Factory\n// ============================================================================\n\nexport interface CreateUnifiedAppResult {\n app: UnifiedApp | null;\n isConnected: boolean;\n error: Error | null;\n platform: Platform;\n}\n\n/**\n * Create a unified app - automatically detects platform and creates appropriate adapter\n *\n * For OpenAI: Returns immediately with the adapter\n * For MCP: Returns null app (use the React hook instead for MCP)\n */\nexport function createUnifiedApp(options: UnifiedAppOptions): CreateUnifiedAppResult {\n const platform = detectPlatform();\n\n if (platform === \"openai\") {\n try {\n const app = createOpenAIAdapter(options);\n return {\n app,\n isConnected: true,\n error: null,\n platform: \"openai\",\n };\n } catch (error) {\n return {\n app: null,\n isConnected: false,\n error: error as Error,\n platform: \"openai\",\n };\n }\n }\n\n // For MCP, return null - the React hook will handle connection\n return {\n app: null,\n isConnected: false,\n error: null,\n platform: \"unknown\",\n };\n}\n","/**\n * @file React hook for unified OpenAI/MCP app usage\n *\n * This hook provides a unified interface for apps that need to run on both\n * OpenAI ChatGPT and MCP Apps platforms.\n */\n\nimport type { App, McpUiAppCapabilities } from \"@modelcontextprotocol/ext-apps\";\nimport { PostMessageTransport } from \"@modelcontextprotocol/ext-apps\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n createMCPAdapter,\n createOpenAIAdapter,\n detectPlatform,\n type Platform,\n type UnifiedApp,\n type UnifiedHostContext,\n type UnifiedToolResult,\n} from \"./unified-app\";\n\nexport interface UseUnifiedAppOptions {\n appInfo: { name: string; version: string };\n capabilities?: McpUiAppCapabilities;\n onToolInput?: (input: unknown) => void | Promise<void>;\n onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;\n onHostContextChanged?: (context: UnifiedHostContext) => void;\n onTeardown?: () => void | Promise<void>;\n onError?: (error: Error) => void;\n}\n\nexport interface UseUnifiedAppResult {\n /** The unified app instance */\n app: UnifiedApp | null;\n /** Whether the app is connected to the host */\n isConnected: boolean;\n /** Any connection error */\n error: Error | null;\n /** The detected platform */\n platform: Platform;\n /** Current host context (theme, displayMode, locale, etc.) */\n hostContext: UnifiedHostContext | undefined;\n /** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */\n initialProps: unknown;\n /** Widget props (OpenAI only, reactive) */\n widgetProps: Record<string, unknown>;\n /** Widget state - works on both platforms via React state */\n widgetState: unknown;\n /** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */\n setWidgetState: <T = unknown>(state: T) => void;\n /** Update widget state partially - works on both platforms */\n updateWidgetState: <T = unknown>(state: Partial<T>) => void;\n}\n\n/**\n * Hook to subscribe to OpenAI global property changes\n * Listens for 'openai:set_globals' events\n */\nfunction useOpenAIGlobal<T>(key: string, initialValue: T): T {\n const [value, setValue] = useState<T>(initialValue);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !window.openai) return;\n\n // Get initial value - cast to unknown first then to Record\n const openai = window.openai as unknown as Record<string, unknown>;\n if (key in openai) {\n setValue(openai[key] as T);\n }\n\n // Listen for changes\n const handleChange = () => {\n const currentOpenai = window.openai as unknown as Record<string, unknown>;\n if (currentOpenai && key in currentOpenai) {\n setValue(currentOpenai[key] as T);\n }\n };\n\n window.addEventListener(\"openai:set_globals\", handleChange);\n return () => window.removeEventListener(\"openai:set_globals\", handleChange);\n }, [key]);\n\n return value;\n}\n\n/**\n * React hook that provides a unified app interface for both OpenAI and MCP platforms\n *\n * @example\n * ```tsx\n * function MyApp() {\n * const {\n * app,\n * isConnected,\n * platform,\n * hostContext,\n * widgetState,\n * setWidgetState,\n * } = useUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * capabilities: {},\n * onError: console.error,\n * });\n *\n * const counter = (widgetState as { value?: number } | null)?.value ?? 0;\n *\n * return (\n * <div>\n * <p>Running on: {platform}</p>\n * <p>Counter: {counter}</p>\n * <button onClick={() => setWidgetState({ value: counter + 1 })}>\n * Increment\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult {\n // Detect platform once on mount\n const platformRef = useRef<Platform>(detectPlatform());\n const platform = platformRef.current;\n\n const [unifiedApp, setUnifiedApp] = useState<UnifiedApp | null>(null);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [hostContext, setHostContext] = useState<UnifiedHostContext | undefined>();\n\n // React state for widgetState - works on both platforms\n // Initialize from OpenAI's toolOutput (initialProps) if available\n const [localWidgetState, setLocalWidgetState] = useState<unknown>(() => {\n if (platform === \"openai\" && typeof window !== \"undefined\" && window.openai) {\n const openai = window.openai as unknown as Record<string, unknown>;\n return openai.toolOutput ?? openai.widgetState ?? null;\n }\n return null;\n });\n\n // Track if we set widget state ourselves (to prevent sync loop)\n const isOurUpdateRef = useRef(false);\n\n // Store unifiedApp in ref for stable callbacks\n const unifiedAppRef = useRef<UnifiedApp | null>(null);\n unifiedAppRef.current = unifiedApp;\n\n // OpenAI reactive globals\n const openaiWidgetState = useOpenAIGlobal<unknown>(\"widgetState\", null);\n const openaiWidgetProps = useOpenAIGlobal<Record<string, unknown>>(\"widget\", {});\n const openaiToolOutput = useOpenAIGlobal<unknown>(\"toolOutput\", undefined);\n const openaiTheme = useOpenAIGlobal<\"light\" | \"dark\">(\"theme\", \"light\");\n const openaiDisplayMode = useOpenAIGlobal<string>(\"displayMode\", \"inline\");\n const openaiLocale = useOpenAIGlobal<string>(\"locale\", \"en-US\");\n\n // Store options in ref to avoid stale closures\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Sync OpenAI widgetState to local state when it changes (only if not caused by us)\n useEffect(() => {\n if (platform === \"openai\" && openaiWidgetState !== null) {\n if (!isOurUpdateRef.current) {\n setLocalWidgetState(openaiWidgetState);\n }\n isOurUpdateRef.current = false;\n }\n }, [platform, openaiWidgetState]);\n\n // Widget state setters - stable callbacks using refs\n const setWidgetState = useCallback(<T = unknown>(state: T) => {\n setLocalWidgetState(state);\n if (platform === \"openai\" && unifiedAppRef.current) {\n isOurUpdateRef.current = true;\n unifiedAppRef.current.setWidgetState(state);\n }\n }, [platform]);\n\n const updateWidgetState = useCallback(<T = unknown>(state: Partial<T>) => {\n setLocalWidgetState((prev: unknown) => ({ ...(prev as object), ...state }));\n if (platform === \"openai\" && unifiedAppRef.current) {\n isOurUpdateRef.current = true;\n unifiedAppRef.current.updateWidgetState(state);\n }\n }, [platform]);\n\n // Update host context from OpenAI globals\n useEffect(() => {\n if (platform === \"openai\" && isConnected) {\n setHostContext({\n theme: openaiTheme,\n displayMode: openaiDisplayMode as UnifiedHostContext[\"displayMode\"],\n locale: openaiLocale,\n safeAreaInsets: window.openai?.safeArea?.insets,\n maxWidth: window.openai?.maxWidth,\n maxHeight: window.openai?.maxHeight,\n });\n }\n }, [platform, isConnected, openaiTheme, openaiDisplayMode, openaiLocale]);\n\n useEffect(() => {\n let mounted = true;\n let mcpApp: App | null = null;\n\n async function initialize() {\n const opts = optionsRef.current;\n\n if (platform === \"openai\") {\n // OpenAI platform - use the adapter directly\n try {\n const adapter = createOpenAIAdapter({\n ...opts,\n onToolResult: (result) => {\n opts.onToolResult?.(result);\n },\n onHostContextChanged: (ctx) => {\n if (mounted) {\n setHostContext(ctx);\n opts.onHostContextChanged?.(ctx);\n }\n },\n onError: (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err);\n },\n });\n\n if (mounted) {\n setUnifiedApp(adapter);\n setIsConnected(true);\n setHostContext(adapter.getHostContext());\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err as Error);\n setIsConnected(false);\n }\n }\n } else {\n // MCP platform - need to dynamically import and create connection\n try {\n // Dynamic import to avoid issues when running on OpenAI\n const { App } = await import(\"@modelcontextprotocol/ext-apps\");\n\n const transport = new PostMessageTransport(window.parent, window.parent);\n mcpApp = new App(opts.appInfo, opts.capabilities || {});\n\n // Set up handlers before connecting\n mcpApp.onteardown = async () => {\n await opts.onTeardown?.();\n return {};\n };\n\n mcpApp.ontoolinput = async (input) => {\n await opts.onToolInput?.(input);\n };\n\n mcpApp.ontoolresult = async (result) => {\n await opts.onToolResult?.(result as UnifiedToolResult);\n };\n\n mcpApp.onerror = (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err instanceof Error ? err : new Error(String(err)));\n };\n\n mcpApp.onhostcontextchanged = (params) => {\n if (mounted) {\n setHostContext((prev) => {\n const newCtx = { ...prev, ...params } as UnifiedHostContext;\n opts.onHostContextChanged?.(newCtx);\n return newCtx;\n });\n }\n };\n\n await mcpApp.connect(transport);\n\n if (mounted) {\n const adapter = createMCPAdapter(mcpApp, opts);\n setUnifiedApp(adapter);\n setIsConnected(true);\n\n // Get initial host context\n const ctx = mcpApp.getHostContext();\n if (ctx) {\n setHostContext({\n theme: ctx.theme as UnifiedHostContext[\"theme\"],\n displayMode: ctx.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: ctx.locale,\n safeAreaInsets: ctx.safeAreaInsets,\n });\n }\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err instanceof Error ? err : new Error(\"Failed to connect\"));\n setIsConnected(false);\n }\n }\n }\n }\n\n initialize();\n\n return () => {\n mounted = false;\n // Note: MCP App cleanup would go here if needed\n };\n }, [platform]);\n\n // Determine final platform based on connection state\n const finalPlatform: Platform = useMemo(() => {\n if (platform === \"openai\") return \"openai\";\n if (isConnected) return \"mcp\";\n return \"unknown\";\n }, [platform, isConnected]);\n\n // Return platform-appropriate values\n const widgetProps = platform === \"openai\" ? ((openaiWidgetProps as { props?: Record<string, unknown> })?.props || {}) : {};\n const initialProps = platform === \"openai\" ? openaiToolOutput : undefined;\n\n return {\n app: unifiedApp,\n isConnected,\n error,\n platform: finalPlatform,\n hostContext,\n initialProps,\n widgetProps,\n widgetState: localWidgetState,\n setWidgetState,\n updateWidgetState,\n };\n}\n\nexport default useUnifiedApp;\n"],"mappings":"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","names":[]}
|
package/dist/react.mjs
CHANGED
|
@@ -36,7 +36,16 @@ function useUnifiedApp(options) {
|
|
|
36
36
|
const [isConnected, setIsConnected] = useState(false);
|
|
37
37
|
const [error, setError] = useState(null);
|
|
38
38
|
const [hostContext, setHostContext] = useState();
|
|
39
|
-
const [localWidgetState, setLocalWidgetState] = useState(
|
|
39
|
+
const [localWidgetState, setLocalWidgetState] = useState(() => {
|
|
40
|
+
if (platform === "openai" && typeof window !== "undefined" && window.openai) {
|
|
41
|
+
const openai = window.openai;
|
|
42
|
+
return openai.toolOutput ?? openai.widgetState ?? null;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
});
|
|
46
|
+
const isOurUpdateRef = useRef(false);
|
|
47
|
+
const unifiedAppRef = useRef(null);
|
|
48
|
+
unifiedAppRef.current = unifiedApp;
|
|
40
49
|
const openaiWidgetState = useOpenAIGlobal("widgetState", null);
|
|
41
50
|
const openaiWidgetProps = useOpenAIGlobal("widget", {});
|
|
42
51
|
const openaiToolOutput = useOpenAIGlobal("toolOutput", void 0);
|
|
@@ -47,21 +56,26 @@ function useUnifiedApp(options) {
|
|
|
47
56
|
optionsRef.current = options;
|
|
48
57
|
useEffect(() => {
|
|
49
58
|
if (platform === "openai" && openaiWidgetState !== null) {
|
|
50
|
-
|
|
59
|
+
if (!isOurUpdateRef.current) {
|
|
60
|
+
setLocalWidgetState(openaiWidgetState);
|
|
61
|
+
}
|
|
62
|
+
isOurUpdateRef.current = false;
|
|
51
63
|
}
|
|
52
64
|
}, [platform, openaiWidgetState]);
|
|
53
65
|
const setWidgetState = useCallback((state) => {
|
|
54
66
|
setLocalWidgetState(state);
|
|
55
|
-
if (
|
|
56
|
-
|
|
67
|
+
if (platform === "openai" && unifiedAppRef.current) {
|
|
68
|
+
isOurUpdateRef.current = true;
|
|
69
|
+
unifiedAppRef.current.setWidgetState(state);
|
|
57
70
|
}
|
|
58
|
-
}, [
|
|
71
|
+
}, [platform]);
|
|
59
72
|
const updateWidgetState = useCallback((state) => {
|
|
60
73
|
setLocalWidgetState((prev) => ({ ...prev, ...state }));
|
|
61
|
-
if (
|
|
62
|
-
|
|
74
|
+
if (platform === "openai" && unifiedAppRef.current) {
|
|
75
|
+
isOurUpdateRef.current = true;
|
|
76
|
+
unifiedAppRef.current.updateWidgetState(state);
|
|
63
77
|
}
|
|
64
|
-
}, [
|
|
78
|
+
}, [platform]);
|
|
65
79
|
useEffect(() => {
|
|
66
80
|
if (platform === "openai" && isConnected) {
|
|
67
81
|
setHostContext({
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/use-unified-app.ts"],"sourcesContent":["/**\n * @file React hook for unified OpenAI/MCP app usage\n *\n * This hook provides a unified interface for apps that need to run on both\n * OpenAI ChatGPT and MCP Apps platforms.\n */\n\nimport type { App, McpUiAppCapabilities } from \"@modelcontextprotocol/ext-apps\";\nimport { PostMessageTransport } from \"@modelcontextprotocol/ext-apps\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n createMCPAdapter,\n createOpenAIAdapter,\n detectPlatform,\n type Platform,\n type UnifiedApp,\n type UnifiedHostContext,\n type UnifiedToolResult,\n} from \"./unified-app\";\n\nexport interface UseUnifiedAppOptions {\n appInfo: { name: string; version: string };\n capabilities?: McpUiAppCapabilities;\n onToolInput?: (input: unknown) => void | Promise<void>;\n onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;\n onHostContextChanged?: (context: UnifiedHostContext) => void;\n onTeardown?: () => void | Promise<void>;\n onError?: (error: Error) => void;\n}\n\nexport interface UseUnifiedAppResult {\n /** The unified app instance */\n app: UnifiedApp | null;\n /** Whether the app is connected to the host */\n isConnected: boolean;\n /** Any connection error */\n error: Error | null;\n /** The detected platform */\n platform: Platform;\n /** Current host context (theme, displayMode, locale, etc.) */\n hostContext: UnifiedHostContext | undefined;\n /** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */\n initialProps: unknown;\n /** Widget props (OpenAI only, reactive) */\n widgetProps: Record<string, unknown>;\n /** Widget state - works on both platforms via React state */\n widgetState: unknown;\n /** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */\n setWidgetState: <T = unknown>(state: T) => void;\n /** Update widget state partially - works on both platforms */\n updateWidgetState: <T = unknown>(state: Partial<T>) => void;\n}\n\n/**\n * Hook to subscribe to OpenAI global property changes\n * Listens for 'openai:set_globals' events\n */\nfunction useOpenAIGlobal<T>(key: string, initialValue: T): T {\n const [value, setValue] = useState<T>(initialValue);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !window.openai) return;\n\n // Get initial value - cast to unknown first then to Record\n const openai = window.openai as unknown as Record<string, unknown>;\n if (key in openai) {\n setValue(openai[key] as T);\n }\n\n // Listen for changes\n const handleChange = () => {\n const currentOpenai = window.openai as unknown as Record<string, unknown>;\n if (currentOpenai && key in currentOpenai) {\n setValue(currentOpenai[key] as T);\n }\n };\n\n window.addEventListener(\"openai:set_globals\", handleChange);\n return () => window.removeEventListener(\"openai:set_globals\", handleChange);\n }, [key]);\n\n return value;\n}\n\n/**\n * React hook that provides a unified app interface for both OpenAI and MCP platforms\n *\n * @example\n * ```tsx\n * function MyApp() {\n * const {\n * app,\n * isConnected,\n * platform,\n * hostContext,\n * widgetState,\n * setWidgetState,\n * } = useUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * capabilities: {},\n * onError: console.error,\n * });\n *\n * const counter = (widgetState as { value?: number } | null)?.value ?? 0;\n *\n * return (\n * <div>\n * <p>Running on: {platform}</p>\n * <p>Counter: {counter}</p>\n * <button onClick={() => setWidgetState({ value: counter + 1 })}>\n * Increment\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult {\n // Detect platform once on mount\n const platformRef = useRef<Platform>(detectPlatform());\n const platform = platformRef.current;\n\n const [unifiedApp, setUnifiedApp] = useState<UnifiedApp | null>(null);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [hostContext, setHostContext] = useState<UnifiedHostContext | undefined>();\n\n // React state for widgetState - works on both platforms\n const [localWidgetState, setLocalWidgetState] = useState<unknown>(null);\n\n // OpenAI reactive globals\n const openaiWidgetState = useOpenAIGlobal<unknown>(\"widgetState\", null);\n const openaiWidgetProps = useOpenAIGlobal<Record<string, unknown>>(\"widget\", {});\n const openaiToolOutput = useOpenAIGlobal<unknown>(\"toolOutput\", undefined);\n const openaiTheme = useOpenAIGlobal<\"light\" | \"dark\">(\"theme\", \"light\");\n const openaiDisplayMode = useOpenAIGlobal<string>(\"displayMode\", \"inline\");\n const openaiLocale = useOpenAIGlobal<string>(\"locale\", \"en-US\");\n\n // Store options in ref to avoid stale closures\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Sync OpenAI widgetState to local state when it changes\n useEffect(() => {\n if (platform === \"openai\" && openaiWidgetState !== null) {\n setLocalWidgetState(openaiWidgetState);\n }\n }, [platform, openaiWidgetState]);\n\n // Widget state setters - work on both platforms via React state\n // Also syncs to OpenAI if on that platform\n const setWidgetState = useCallback(<T = unknown>(state: T) => {\n setLocalWidgetState(state);\n if (unifiedApp && platform === \"openai\") {\n unifiedApp.setWidgetState(state);\n }\n }, [unifiedApp, platform]);\n\n const updateWidgetState = useCallback(<T = unknown>(state: Partial<T>) => {\n setLocalWidgetState((prev: unknown) => ({ ...(prev as object), ...state }));\n if (unifiedApp && platform === \"openai\") {\n unifiedApp.updateWidgetState(state);\n }\n }, [unifiedApp, platform]);\n\n // Update host context from OpenAI globals\n useEffect(() => {\n if (platform === \"openai\" && isConnected) {\n setHostContext({\n theme: openaiTheme,\n displayMode: openaiDisplayMode as UnifiedHostContext[\"displayMode\"],\n locale: openaiLocale,\n safeAreaInsets: window.openai?.safeArea?.insets,\n maxWidth: window.openai?.maxWidth,\n maxHeight: window.openai?.maxHeight,\n });\n }\n }, [platform, isConnected, openaiTheme, openaiDisplayMode, openaiLocale]);\n\n useEffect(() => {\n let mounted = true;\n let mcpApp: App | null = null;\n\n async function initialize() {\n const opts = optionsRef.current;\n\n if (platform === \"openai\") {\n // OpenAI platform - use the adapter directly\n try {\n const adapter = createOpenAIAdapter({\n ...opts,\n onToolResult: (result) => {\n opts.onToolResult?.(result);\n },\n onHostContextChanged: (ctx) => {\n if (mounted) {\n setHostContext(ctx);\n opts.onHostContextChanged?.(ctx);\n }\n },\n onError: (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err);\n },\n });\n\n if (mounted) {\n setUnifiedApp(adapter);\n setIsConnected(true);\n setHostContext(adapter.getHostContext());\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err as Error);\n setIsConnected(false);\n }\n }\n } else {\n // MCP platform - need to dynamically import and create connection\n try {\n // Dynamic import to avoid issues when running on OpenAI\n const { App } = await import(\"@modelcontextprotocol/ext-apps\");\n\n const transport = new PostMessageTransport(window.parent, window.parent);\n mcpApp = new App(opts.appInfo, opts.capabilities || {});\n\n // Set up handlers before connecting\n mcpApp.onteardown = async () => {\n await opts.onTeardown?.();\n return {};\n };\n\n mcpApp.ontoolinput = async (input) => {\n await opts.onToolInput?.(input);\n };\n\n mcpApp.ontoolresult = async (result) => {\n await opts.onToolResult?.(result as UnifiedToolResult);\n };\n\n mcpApp.onerror = (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err instanceof Error ? err : new Error(String(err)));\n };\n\n mcpApp.onhostcontextchanged = (params) => {\n if (mounted) {\n setHostContext((prev) => {\n const newCtx = { ...prev, ...params } as UnifiedHostContext;\n opts.onHostContextChanged?.(newCtx);\n return newCtx;\n });\n }\n };\n\n await mcpApp.connect(transport);\n\n if (mounted) {\n const adapter = createMCPAdapter(mcpApp, opts);\n setUnifiedApp(adapter);\n setIsConnected(true);\n\n // Get initial host context\n const ctx = mcpApp.getHostContext();\n if (ctx) {\n setHostContext({\n theme: ctx.theme as UnifiedHostContext[\"theme\"],\n displayMode: ctx.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: ctx.locale,\n safeAreaInsets: ctx.safeAreaInsets,\n });\n }\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err instanceof Error ? err : new Error(\"Failed to connect\"));\n setIsConnected(false);\n }\n }\n }\n }\n\n initialize();\n\n return () => {\n mounted = false;\n // Note: MCP App cleanup would go here if needed\n };\n }, [platform]);\n\n // Determine final platform based on connection state\n const finalPlatform: Platform = useMemo(() => {\n if (platform === \"openai\") return \"openai\";\n if (isConnected) return \"mcp\";\n return \"unknown\";\n }, [platform, isConnected]);\n\n // Return platform-appropriate values\n const widgetProps = platform === \"openai\" ? ((openaiWidgetProps as { props?: Record<string, unknown> })?.props || {}) : {};\n const initialProps = platform === \"openai\" ? openaiToolOutput : undefined;\n\n return {\n app: unifiedApp,\n isConnected,\n error,\n platform: finalPlatform,\n hostContext,\n initialProps,\n widgetProps,\n widgetState: localWidgetState,\n setWidgetState,\n updateWidgetState,\n };\n}\n\nexport default useUnifiedApp;\n"],"mappings":";;;;;;;;;;AAQA,SAAS,4BAA4B;AACrC,SAAS,aAAa,WAAW,SAAS,QAAQ,gBAAgB;AAgDlE,SAAS,gBAAmB,KAAa,cAAoB;AAC3D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAY,YAAY;AAElD,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,OAAQ;AAGrD,UAAM,SAAS,OAAO;AACtB,QAAI,OAAO,QAAQ;AACjB,eAAS,OAAO,GAAG,CAAM;AAAA,IAC3B;AAGA,UAAM,eAAe,MAAM;AACzB,YAAM,gBAAgB,OAAO;AAC7B,UAAI,iBAAiB,OAAO,eAAe;AACzC,iBAAS,cAAc,GAAG,CAAM;AAAA,MAClC;AAAA,IACF;AAEA,WAAO,iBAAiB,sBAAsB,YAAY;AAC1D,WAAO,MAAM,OAAO,oBAAoB,sBAAsB,YAAY;AAAA,EAC5E,GAAG,CAAC,GAAG,CAAC;AAER,SAAO;AACT;AAmCO,SAAS,cAAc,SAAoD;AAEhF,QAAM,cAAc,OAAiB,eAAe,CAAC;AACrD,QAAM,WAAW,YAAY;AAE7B,QAAM,CAAC,YAAY,aAAa,IAAI,SAA4B,IAAI;AACpE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AACrD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAyC;AAG/E,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAkB,IAAI;AAGtE,QAAM,oBAAoB,gBAAyB,eAAe,IAAI;AACtE,QAAM,oBAAoB,gBAAyC,UAAU,CAAC,CAAC;AAC/E,QAAM,mBAAmB,gBAAyB,cAAc,MAAS;AACzE,QAAM,cAAc,gBAAkC,SAAS,OAAO;AACtE,QAAM,oBAAoB,gBAAwB,eAAe,QAAQ;AACzE,QAAM,eAAe,gBAAwB,UAAU,OAAO;AAG9D,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAGrB,YAAU,MAAM;AACd,QAAI,aAAa,YAAY,sBAAsB,MAAM;AACvD,0BAAoB,iBAAiB;AAAA,IACvC;AAAA,EACF,GAAG,CAAC,UAAU,iBAAiB,CAAC;AAIhC,QAAM,iBAAiB,YAAY,CAAc,UAAa;AAC5D,wBAAoB,KAAK;AACzB,QAAI,cAAc,aAAa,UAAU;AACvC,iBAAW,eAAe,KAAK;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,YAAY,QAAQ,CAAC;AAEzB,QAAM,oBAAoB,YAAY,CAAc,UAAsB;AACxE,wBAAoB,CAAC,UAAmB,EAAE,GAAI,MAAiB,GAAG,MAAM,EAAE;AAC1E,QAAI,cAAc,aAAa,UAAU;AACvC,iBAAW,kBAAkB,KAAK;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,YAAY,QAAQ,CAAC;AAGzB,YAAU,MAAM;AACd,QAAI,aAAa,YAAY,aAAa;AACxC,qBAAe;AAAA,QACb,OAAO;AAAA,QACP,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,gBAAgB,OAAO,QAAQ,UAAU;AAAA,QACzC,UAAU,OAAO,QAAQ;AAAA,QACzB,WAAW,OAAO,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,UAAU,aAAa,aAAa,mBAAmB,YAAY,CAAC;AAExE,YAAU,MAAM;AACd,QAAI,UAAU;AACd,QAAI,SAAqB;AAEzB,mBAAe,aAAa;AAC1B,YAAM,OAAO,WAAW;AAExB,UAAI,aAAa,UAAU;AAEzB,YAAI;AACF,gBAAM,UAAU,oBAAoB;AAAA,YAClC,GAAG;AAAA,YACH,cAAc,CAAC,WAAW;AACxB,mBAAK,eAAe,MAAM;AAAA,YAC5B;AAAA,YACA,sBAAsB,CAAC,QAAQ;AAC7B,kBAAI,SAAS;AACX,+BAAe,GAAG;AAClB,qBAAK,uBAAuB,GAAG;AAAA,cACjC;AAAA,YACF;AAAA,YACA,SAAS,CAAC,QAAQ;AAChB,sBAAQ,MAAM,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC3C,mBAAK,UAAU,GAAG;AAAA,YACpB;AAAA,UACF,CAAC;AAED,cAAI,SAAS;AACX,0BAAc,OAAO;AACrB,2BAAe,IAAI;AACnB,2BAAe,QAAQ,eAAe,CAAC;AAAA,UACzC;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ,MAAM,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC3C,cAAI,SAAS;AACX,qBAAS,GAAY;AACrB,2BAAe,KAAK;AAAA,UACtB;AAAA,QACF;AAAA,MACF,OAAO;AAEL,YAAI;AAEF,gBAAM,EAAE,IAAI,IAAI,MAAM,OAAO,gCAAgC;AAE7D,gBAAM,YAAY,IAAI,qBAAqB,OAAO,QAAQ,OAAO,MAAM;AACvE,mBAAS,IAAI,IAAI,KAAK,SAAS,KAAK,gBAAgB,CAAC,CAAC;AAGtD,iBAAO,aAAa,YAAY;AAC9B,kBAAM,KAAK,aAAa;AACxB,mBAAO,CAAC;AAAA,UACV;AAEA,iBAAO,cAAc,OAAO,UAAU;AACpC,kBAAM,KAAK,cAAc,KAAK;AAAA,UAChC;AAEA,iBAAO,eAAe,OAAO,WAAW;AACtC,kBAAM,KAAK,eAAe,MAA2B;AAAA,UACvD;AAEA,iBAAO,UAAU,CAAC,QAAQ;AACxB,oBAAQ,MAAM,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC3C,iBAAK,UAAU,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,UACpE;AAEA,iBAAO,uBAAuB,CAAC,WAAW;AACxC,gBAAI,SAAS;AACX,6BAAe,CAAC,SAAS;AACvB,sBAAM,SAAS,EAAE,GAAG,MAAM,GAAG,OAAO;AACpC,qBAAK,uBAAuB,MAAM;AAClC,uBAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,UACF;AAEA,gBAAM,OAAO,QAAQ,SAAS;AAE9B,cAAI,SAAS;AACX,kBAAM,UAAU,iBAAiB,QAAQ,IAAI;AAC7C,0BAAc,OAAO;AACrB,2BAAe,IAAI;AAGnB,kBAAM,MAAM,OAAO,eAAe;AAClC,gBAAI,KAAK;AACP,6BAAe;AAAA,gBACb,OAAO,IAAI;AAAA,gBACX,aAAa,IAAI;AAAA,gBACjB,QAAQ,IAAI;AAAA,gBACZ,gBAAgB,IAAI;AAAA,cACtB,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ,MAAM,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC3C,cAAI,SAAS;AACX,qBAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,mBAAmB,CAAC;AACpE,2BAAe,KAAK;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,eAAW;AAEX,WAAO,MAAM;AACX,gBAAU;AAAA,IAEZ;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAGb,QAAM,gBAA0B,QAAQ,MAAM;AAC5C,QAAI,aAAa,SAAU,QAAO;AAClC,QAAI,YAAa,QAAO;AACxB,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,WAAW,CAAC;AAG1B,QAAM,cAAc,aAAa,WAAa,mBAA2D,SAAS,CAAC,IAAK,CAAC;AACzH,QAAM,eAAe,aAAa,WAAW,mBAAmB;AAEhE,SAAO;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/use-unified-app.ts"],"sourcesContent":["/**\n * @file React hook for unified OpenAI/MCP app usage\n *\n * This hook provides a unified interface for apps that need to run on both\n * OpenAI ChatGPT and MCP Apps platforms.\n */\n\nimport type { App, McpUiAppCapabilities } from \"@modelcontextprotocol/ext-apps\";\nimport { PostMessageTransport } from \"@modelcontextprotocol/ext-apps\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n createMCPAdapter,\n createOpenAIAdapter,\n detectPlatform,\n type Platform,\n type UnifiedApp,\n type UnifiedHostContext,\n type UnifiedToolResult,\n} from \"./unified-app\";\n\nexport interface UseUnifiedAppOptions {\n appInfo: { name: string; version: string };\n capabilities?: McpUiAppCapabilities;\n onToolInput?: (input: unknown) => void | Promise<void>;\n onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;\n onHostContextChanged?: (context: UnifiedHostContext) => void;\n onTeardown?: () => void | Promise<void>;\n onError?: (error: Error) => void;\n}\n\nexport interface UseUnifiedAppResult {\n /** The unified app instance */\n app: UnifiedApp | null;\n /** Whether the app is connected to the host */\n isConnected: boolean;\n /** Any connection error */\n error: Error | null;\n /** The detected platform */\n platform: Platform;\n /** Current host context (theme, displayMode, locale, etc.) */\n hostContext: UnifiedHostContext | undefined;\n /** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */\n initialProps: unknown;\n /** Widget props (OpenAI only, reactive) */\n widgetProps: Record<string, unknown>;\n /** Widget state - works on both platforms via React state */\n widgetState: unknown;\n /** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */\n setWidgetState: <T = unknown>(state: T) => void;\n /** Update widget state partially - works on both platforms */\n updateWidgetState: <T = unknown>(state: Partial<T>) => void;\n}\n\n/**\n * Hook to subscribe to OpenAI global property changes\n * Listens for 'openai:set_globals' events\n */\nfunction useOpenAIGlobal<T>(key: string, initialValue: T): T {\n const [value, setValue] = useState<T>(initialValue);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !window.openai) return;\n\n // Get initial value - cast to unknown first then to Record\n const openai = window.openai as unknown as Record<string, unknown>;\n if (key in openai) {\n setValue(openai[key] as T);\n }\n\n // Listen for changes\n const handleChange = () => {\n const currentOpenai = window.openai as unknown as Record<string, unknown>;\n if (currentOpenai && key in currentOpenai) {\n setValue(currentOpenai[key] as T);\n }\n };\n\n window.addEventListener(\"openai:set_globals\", handleChange);\n return () => window.removeEventListener(\"openai:set_globals\", handleChange);\n }, [key]);\n\n return value;\n}\n\n/**\n * React hook that provides a unified app interface for both OpenAI and MCP platforms\n *\n * @example\n * ```tsx\n * function MyApp() {\n * const {\n * app,\n * isConnected,\n * platform,\n * hostContext,\n * widgetState,\n * setWidgetState,\n * } = useUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * capabilities: {},\n * onError: console.error,\n * });\n *\n * const counter = (widgetState as { value?: number } | null)?.value ?? 0;\n *\n * return (\n * <div>\n * <p>Running on: {platform}</p>\n * <p>Counter: {counter}</p>\n * <button onClick={() => setWidgetState({ value: counter + 1 })}>\n * Increment\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult {\n // Detect platform once on mount\n const platformRef = useRef<Platform>(detectPlatform());\n const platform = platformRef.current;\n\n const [unifiedApp, setUnifiedApp] = useState<UnifiedApp | null>(null);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [hostContext, setHostContext] = useState<UnifiedHostContext | undefined>();\n\n // React state for widgetState - works on both platforms\n // Initialize from OpenAI's toolOutput (initialProps) if available\n const [localWidgetState, setLocalWidgetState] = useState<unknown>(() => {\n if (platform === \"openai\" && typeof window !== \"undefined\" && window.openai) {\n const openai = window.openai as unknown as Record<string, unknown>;\n return openai.toolOutput ?? openai.widgetState ?? null;\n }\n return null;\n });\n\n // Track if we set widget state ourselves (to prevent sync loop)\n const isOurUpdateRef = useRef(false);\n\n // Store unifiedApp in ref for stable callbacks\n const unifiedAppRef = useRef<UnifiedApp | null>(null);\n unifiedAppRef.current = unifiedApp;\n\n // OpenAI reactive globals\n const openaiWidgetState = useOpenAIGlobal<unknown>(\"widgetState\", null);\n const openaiWidgetProps = useOpenAIGlobal<Record<string, unknown>>(\"widget\", {});\n const openaiToolOutput = useOpenAIGlobal<unknown>(\"toolOutput\", undefined);\n const openaiTheme = useOpenAIGlobal<\"light\" | \"dark\">(\"theme\", \"light\");\n const openaiDisplayMode = useOpenAIGlobal<string>(\"displayMode\", \"inline\");\n const openaiLocale = useOpenAIGlobal<string>(\"locale\", \"en-US\");\n\n // Store options in ref to avoid stale closures\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Sync OpenAI widgetState to local state when it changes (only if not caused by us)\n useEffect(() => {\n if (platform === \"openai\" && openaiWidgetState !== null) {\n if (!isOurUpdateRef.current) {\n setLocalWidgetState(openaiWidgetState);\n }\n isOurUpdateRef.current = false;\n }\n }, [platform, openaiWidgetState]);\n\n // Widget state setters - stable callbacks using refs\n const setWidgetState = useCallback(<T = unknown>(state: T) => {\n setLocalWidgetState(state);\n if (platform === \"openai\" && unifiedAppRef.current) {\n isOurUpdateRef.current = true;\n unifiedAppRef.current.setWidgetState(state);\n }\n }, [platform]);\n\n const updateWidgetState = useCallback(<T = unknown>(state: Partial<T>) => {\n setLocalWidgetState((prev: unknown) => ({ ...(prev as object), ...state }));\n if (platform === \"openai\" && unifiedAppRef.current) {\n isOurUpdateRef.current = true;\n unifiedAppRef.current.updateWidgetState(state);\n }\n }, [platform]);\n\n // Update host context from OpenAI globals\n useEffect(() => {\n if (platform === \"openai\" && isConnected) {\n setHostContext({\n theme: openaiTheme,\n displayMode: openaiDisplayMode as UnifiedHostContext[\"displayMode\"],\n locale: openaiLocale,\n safeAreaInsets: window.openai?.safeArea?.insets,\n maxWidth: window.openai?.maxWidth,\n maxHeight: window.openai?.maxHeight,\n });\n }\n }, [platform, isConnected, openaiTheme, openaiDisplayMode, openaiLocale]);\n\n useEffect(() => {\n let mounted = true;\n let mcpApp: App | null = null;\n\n async function initialize() {\n const opts = optionsRef.current;\n\n if (platform === \"openai\") {\n // OpenAI platform - use the adapter directly\n try {\n const adapter = createOpenAIAdapter({\n ...opts,\n onToolResult: (result) => {\n opts.onToolResult?.(result);\n },\n onHostContextChanged: (ctx) => {\n if (mounted) {\n setHostContext(ctx);\n opts.onHostContextChanged?.(ctx);\n }\n },\n onError: (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err);\n },\n });\n\n if (mounted) {\n setUnifiedApp(adapter);\n setIsConnected(true);\n setHostContext(adapter.getHostContext());\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err as Error);\n setIsConnected(false);\n }\n }\n } else {\n // MCP platform - need to dynamically import and create connection\n try {\n // Dynamic import to avoid issues when running on OpenAI\n const { App } = await import(\"@modelcontextprotocol/ext-apps\");\n\n const transport = new PostMessageTransport(window.parent, window.parent);\n mcpApp = new App(opts.appInfo, opts.capabilities || {});\n\n // Set up handlers before connecting\n mcpApp.onteardown = async () => {\n await opts.onTeardown?.();\n return {};\n };\n\n mcpApp.ontoolinput = async (input) => {\n await opts.onToolInput?.(input);\n };\n\n mcpApp.ontoolresult = async (result) => {\n await opts.onToolResult?.(result as UnifiedToolResult);\n };\n\n mcpApp.onerror = (err) => {\n console.error(`[${opts.appInfo.name}]`, err);\n opts.onError?.(err instanceof Error ? err : new Error(String(err)));\n };\n\n mcpApp.onhostcontextchanged = (params) => {\n if (mounted) {\n setHostContext((prev) => {\n const newCtx = { ...prev, ...params } as UnifiedHostContext;\n opts.onHostContextChanged?.(newCtx);\n return newCtx;\n });\n }\n };\n\n await mcpApp.connect(transport);\n\n if (mounted) {\n const adapter = createMCPAdapter(mcpApp, opts);\n setUnifiedApp(adapter);\n setIsConnected(true);\n\n // Get initial host context\n const ctx = mcpApp.getHostContext();\n if (ctx) {\n setHostContext({\n theme: ctx.theme as UnifiedHostContext[\"theme\"],\n displayMode: ctx.displayMode as UnifiedHostContext[\"displayMode\"],\n locale: ctx.locale,\n safeAreaInsets: ctx.safeAreaInsets,\n });\n }\n }\n } catch (err) {\n console.error(`[${opts.appInfo.name}]`, err);\n if (mounted) {\n setError(err instanceof Error ? err : new Error(\"Failed to connect\"));\n setIsConnected(false);\n }\n }\n }\n }\n\n initialize();\n\n return () => {\n mounted = false;\n // Note: MCP App cleanup would go here if needed\n };\n }, [platform]);\n\n // Determine final platform based on connection state\n const finalPlatform: Platform = useMemo(() => {\n if (platform === \"openai\") return \"openai\";\n if (isConnected) return \"mcp\";\n return \"unknown\";\n }, [platform, isConnected]);\n\n // Return platform-appropriate values\n const widgetProps = platform === \"openai\" ? ((openaiWidgetProps as { props?: Record<string, unknown> })?.props || {}) : {};\n const initialProps = platform === \"openai\" ? openaiToolOutput : undefined;\n\n return {\n app: unifiedApp,\n isConnected,\n error,\n platform: finalPlatform,\n hostContext,\n initialProps,\n widgetProps,\n widgetState: localWidgetState,\n setWidgetState,\n updateWidgetState,\n };\n}\n\nexport default useUnifiedApp;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED