mcp-ui-ext-apps-openai 1.0.2 → 1.0.4
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/dist/{chunk-MX7VCLPI.mjs → chunk-IHPQNUCK.mjs} +11 -1
- package/dist/chunk-IHPQNUCK.mjs.map +1 -0
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +9 -5
- package/dist/react.d.ts +9 -5
- package/dist/react.js +27 -8
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +18 -9
- package/dist/react.mjs.map +1 -1
- package/package.json +13 -2
- package/dist/chunk-MX7VCLPI.mjs.map +0 -1
|
@@ -85,6 +85,13 @@ function createOpenAIAdapter(options) {
|
|
|
85
85
|
return { isError: true };
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
|
+
updateModelContext: async ({ content, structuredContent }) => {
|
|
89
|
+
if (openai.updateModelContext) {
|
|
90
|
+
await openai.updateModelContext({ content, structuredContent });
|
|
91
|
+
} else {
|
|
92
|
+
console.log(`[${options.appInfo.name}] updateModelContext:`, { content, structuredContent });
|
|
93
|
+
}
|
|
94
|
+
},
|
|
88
95
|
getHostContext: () => ({
|
|
89
96
|
theme: openai.theme,
|
|
90
97
|
displayMode: openai.displayMode,
|
|
@@ -175,6 +182,9 @@ function createMCPAdapter(app, _options) {
|
|
|
175
182
|
const result = await app.openLink({ url });
|
|
176
183
|
return { isError: result.isError ?? false };
|
|
177
184
|
},
|
|
185
|
+
updateModelContext: async ({ content, structuredContent }) => {
|
|
186
|
+
console.log(`[MCP App] updateModelContext:`, { content, structuredContent });
|
|
187
|
+
},
|
|
178
188
|
getHostContext: () => {
|
|
179
189
|
const ctx = app.getHostContext();
|
|
180
190
|
return {
|
|
@@ -258,4 +268,4 @@ export {
|
|
|
258
268
|
createMCPAdapter,
|
|
259
269
|
createUnifiedApp
|
|
260
270
|
};
|
|
261
|
-
//# sourceMappingURL=chunk-
|
|
271
|
+
//# sourceMappingURL=chunk-IHPQNUCK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/unified-app.ts"],"sourcesContent":["/**\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 UpdateModelContextParams {\n /** Text content array for context update */\n content?: Array<{ type: string; text?: string; [key: string]: unknown }>;\n /** Structured content for easier parsing */\n structuredContent?: Record<string, unknown>;\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 /**\n * Update the AI's context silently (not visible in chat).\n * Use this to inform the AI about user actions like viewing products,\n * adding items to cart, or changing settings without triggering a response.\n */\n updateModelContext: (params: UpdateModelContextParams) => Promise<void>;\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 updateModelContext?: (params: { content?: unknown[]; structuredContent?: Record<string, unknown> }) => Promise<void>;\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 updateModelContext: async ({ content, structuredContent }) => {\n // Check if host provides updateModelContext (e.g., our custom mock)\n if (openai.updateModelContext) {\n await openai.updateModelContext({ content, structuredContent });\n } else {\n // Fallback: log for debugging (native OpenAI doesn't have this yet)\n console.log(`[${options.appInfo.name}] updateModelContext:`, { content, structuredContent });\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 updateModelContext: async ({ content, structuredContent }) => {\n // MCP Apps SDK doesn't have updateModelContext yet\n // This will be implemented by custom hosts or future MCP SDK versions\n // For now, log for debugging - custom hosts can intercept this via postMessage\n console.log(`[MCP App] updateModelContext:`, { content, structuredContent });\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"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -38,6 +38,16 @@ interface UnifiedHostContext {
|
|
|
38
38
|
maxWidth?: number;
|
|
39
39
|
maxHeight?: number;
|
|
40
40
|
}
|
|
41
|
+
interface UpdateModelContextParams {
|
|
42
|
+
/** Text content array for context update */
|
|
43
|
+
content?: Array<{
|
|
44
|
+
type: string;
|
|
45
|
+
text?: string;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}>;
|
|
48
|
+
/** Structured content for easier parsing */
|
|
49
|
+
structuredContent?: Record<string, unknown>;
|
|
50
|
+
}
|
|
41
51
|
interface UnifiedAppOptions {
|
|
42
52
|
appInfo: {
|
|
43
53
|
name: string;
|
|
@@ -75,6 +85,12 @@ interface UnifiedApp {
|
|
|
75
85
|
}) => Promise<{
|
|
76
86
|
isError: boolean;
|
|
77
87
|
}>;
|
|
88
|
+
/**
|
|
89
|
+
* Update the AI's context silently (not visible in chat).
|
|
90
|
+
* Use this to inform the AI about user actions like viewing products,
|
|
91
|
+
* adding items to cart, or changing settings without triggering a response.
|
|
92
|
+
*/
|
|
93
|
+
updateModelContext: (params: UpdateModelContextParams) => Promise<void>;
|
|
78
94
|
/** Get the current host context */
|
|
79
95
|
getHostContext: () => UnifiedHostContext;
|
|
80
96
|
/** Get tool input (initial arguments passed to the tool) */
|
|
@@ -147,6 +163,10 @@ interface OpenAIGlobal {
|
|
|
147
163
|
share: (params: unknown) => Promise<void>;
|
|
148
164
|
streamCompletion: (params: unknown) => AsyncIterable<unknown>;
|
|
149
165
|
callCompletion: (params: unknown) => Promise<unknown>;
|
|
166
|
+
updateModelContext?: (params: {
|
|
167
|
+
content?: unknown[];
|
|
168
|
+
structuredContent?: Record<string, unknown>;
|
|
169
|
+
}) => Promise<void>;
|
|
150
170
|
toolInput: Record<string, unknown>;
|
|
151
171
|
toolOutput: unknown;
|
|
152
172
|
toolResponseMetadata: Record<string, unknown>;
|
|
@@ -222,4 +242,4 @@ interface CreateUnifiedAppResult {
|
|
|
222
242
|
*/
|
|
223
243
|
declare function createUnifiedApp(options: UnifiedAppOptions): CreateUnifiedAppResult;
|
|
224
244
|
|
|
225
|
-
export { type CreateUnifiedAppResult, type OpenAIGlobal, type OpenAIWidgetProps, type Platform, type UnifiedApp, type UnifiedAppOptions, type UnifiedHostContext, type UnifiedMessage, type UnifiedToolResult, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI };
|
|
245
|
+
export { type CreateUnifiedAppResult, type OpenAIGlobal, type OpenAIWidgetProps, type Platform, type UnifiedApp, type UnifiedAppOptions, type UnifiedHostContext, type UnifiedMessage, type UnifiedToolResult, type UpdateModelContextParams, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI };
|
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,16 @@ interface UnifiedHostContext {
|
|
|
38
38
|
maxWidth?: number;
|
|
39
39
|
maxHeight?: number;
|
|
40
40
|
}
|
|
41
|
+
interface UpdateModelContextParams {
|
|
42
|
+
/** Text content array for context update */
|
|
43
|
+
content?: Array<{
|
|
44
|
+
type: string;
|
|
45
|
+
text?: string;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}>;
|
|
48
|
+
/** Structured content for easier parsing */
|
|
49
|
+
structuredContent?: Record<string, unknown>;
|
|
50
|
+
}
|
|
41
51
|
interface UnifiedAppOptions {
|
|
42
52
|
appInfo: {
|
|
43
53
|
name: string;
|
|
@@ -75,6 +85,12 @@ interface UnifiedApp {
|
|
|
75
85
|
}) => Promise<{
|
|
76
86
|
isError: boolean;
|
|
77
87
|
}>;
|
|
88
|
+
/**
|
|
89
|
+
* Update the AI's context silently (not visible in chat).
|
|
90
|
+
* Use this to inform the AI about user actions like viewing products,
|
|
91
|
+
* adding items to cart, or changing settings without triggering a response.
|
|
92
|
+
*/
|
|
93
|
+
updateModelContext: (params: UpdateModelContextParams) => Promise<void>;
|
|
78
94
|
/** Get the current host context */
|
|
79
95
|
getHostContext: () => UnifiedHostContext;
|
|
80
96
|
/** Get tool input (initial arguments passed to the tool) */
|
|
@@ -147,6 +163,10 @@ interface OpenAIGlobal {
|
|
|
147
163
|
share: (params: unknown) => Promise<void>;
|
|
148
164
|
streamCompletion: (params: unknown) => AsyncIterable<unknown>;
|
|
149
165
|
callCompletion: (params: unknown) => Promise<unknown>;
|
|
166
|
+
updateModelContext?: (params: {
|
|
167
|
+
content?: unknown[];
|
|
168
|
+
structuredContent?: Record<string, unknown>;
|
|
169
|
+
}) => Promise<void>;
|
|
150
170
|
toolInput: Record<string, unknown>;
|
|
151
171
|
toolOutput: unknown;
|
|
152
172
|
toolResponseMetadata: Record<string, unknown>;
|
|
@@ -222,4 +242,4 @@ interface CreateUnifiedAppResult {
|
|
|
222
242
|
*/
|
|
223
243
|
declare function createUnifiedApp(options: UnifiedAppOptions): CreateUnifiedAppResult;
|
|
224
244
|
|
|
225
|
-
export { type CreateUnifiedAppResult, type OpenAIGlobal, type OpenAIWidgetProps, type Platform, type UnifiedApp, type UnifiedAppOptions, type UnifiedHostContext, type UnifiedMessage, type UnifiedToolResult, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI };
|
|
245
|
+
export { type CreateUnifiedAppResult, type OpenAIGlobal, type OpenAIWidgetProps, type Platform, type UnifiedApp, type UnifiedAppOptions, type UnifiedHostContext, type UnifiedMessage, type UnifiedToolResult, type UpdateModelContextParams, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI };
|
package/dist/index.js
CHANGED
|
@@ -116,6 +116,13 @@ function createOpenAIAdapter(options) {
|
|
|
116
116
|
return { isError: true };
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
|
+
updateModelContext: async ({ content, structuredContent }) => {
|
|
120
|
+
if (openai.updateModelContext) {
|
|
121
|
+
await openai.updateModelContext({ content, structuredContent });
|
|
122
|
+
} else {
|
|
123
|
+
console.log(`[${options.appInfo.name}] updateModelContext:`, { content, structuredContent });
|
|
124
|
+
}
|
|
125
|
+
},
|
|
119
126
|
getHostContext: () => ({
|
|
120
127
|
theme: openai.theme,
|
|
121
128
|
displayMode: openai.displayMode,
|
|
@@ -206,6 +213,9 @@ function createMCPAdapter(app, _options) {
|
|
|
206
213
|
const result = await app.openLink({ url });
|
|
207
214
|
return { isError: result.isError ?? false };
|
|
208
215
|
},
|
|
216
|
+
updateModelContext: async ({ content, structuredContent }) => {
|
|
217
|
+
console.log(`[MCP App] updateModelContext:`, { content, structuredContent });
|
|
218
|
+
},
|
|
209
219
|
getHostContext: () => {
|
|
210
220
|
const ctx = app.getHostContext();
|
|
211
221
|
return {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/unified-app.ts"],"sourcesContent":["/**\n * @file mcp-ui-ext-apps-openai - Unified utility for OpenAI ChatGPT and MCP Apps\n *\n * This package provides a unified interface for building apps that work on both\n * OpenAI's ChatGPT platform and MCP Apps hosts.\n *\n * @example\n * ```ts\n * // Core utilities (no React dependency)\n * import { detectPlatform, isOpenAI, isMCP, createUnifiedApp } from \"mcp-ui-ext-apps-openai\";\n *\n * // Check platform\n * if (isOpenAI()) {\n * console.log(\"Running on OpenAI ChatGPT\");\n * }\n *\n * // Create unified app (for non-React usage)\n * const { app, isConnected, platform } = createUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * });\n * ```\n */\n\n// Core types and utilities\nexport {\n type Platform,\n type UnifiedApp,\n type UnifiedAppOptions,\n type UnifiedHostContext,\n type UnifiedMessage,\n type UnifiedToolResult,\n type OpenAIGlobal,\n type OpenAIWidgetProps,\n type CreateUnifiedAppResult,\n detectPlatform,\n isOpenAI,\n isMCP,\n createOpenAIAdapter,\n createMCPAdapter,\n createUnifiedApp,\n} from \"./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"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/unified-app.ts"],"sourcesContent":["/**\n * @file mcp-ui-ext-apps-openai - Unified utility for OpenAI ChatGPT and MCP Apps\n *\n * This package provides a unified interface for building apps that work on both\n * OpenAI's ChatGPT platform and MCP Apps hosts.\n *\n * @example\n * ```ts\n * // Core utilities (no React dependency)\n * import { detectPlatform, isOpenAI, isMCP, createUnifiedApp } from \"mcp-ui-ext-apps-openai\";\n *\n * // Check platform\n * if (isOpenAI()) {\n * console.log(\"Running on OpenAI ChatGPT\");\n * }\n *\n * // Create unified app (for non-React usage)\n * const { app, isConnected, platform } = createUnifiedApp({\n * appInfo: { name: \"My App\", version: \"1.0.0\" },\n * });\n * ```\n */\n\n// Core types and utilities\nexport {\n type Platform,\n type UnifiedApp,\n type UnifiedAppOptions,\n type UnifiedHostContext,\n type UnifiedMessage,\n type UnifiedToolResult,\n type UpdateModelContextParams,\n type OpenAIGlobal,\n type OpenAIWidgetProps,\n type CreateUnifiedAppResult,\n detectPlatform,\n isOpenAI,\n isMCP,\n createOpenAIAdapter,\n createMCPAdapter,\n createUnifiedApp,\n} from \"./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 UpdateModelContextParams {\n /** Text content array for context update */\n content?: Array<{ type: string; text?: string; [key: string]: unknown }>;\n /** Structured content for easier parsing */\n structuredContent?: Record<string, unknown>;\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 /**\n * Update the AI's context silently (not visible in chat).\n * Use this to inform the AI about user actions like viewing products,\n * adding items to cart, or changing settings without triggering a response.\n */\n updateModelContext: (params: UpdateModelContextParams) => Promise<void>;\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 updateModelContext?: (params: { content?: unknown[]; structuredContent?: Record<string, unknown> }) => Promise<void>;\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 updateModelContext: async ({ content, structuredContent }) => {\n // Check if host provides updateModelContext (e.g., our custom mock)\n if (openai.updateModelContext) {\n await openai.updateModelContext({ content, structuredContent });\n } else {\n // Fallback: log for debugging (native OpenAI doesn't have this yet)\n console.log(`[${options.appInfo.name}] updateModelContext:`, { content, structuredContent });\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 updateModelContext: async ({ content, structuredContent }) => {\n // MCP Apps SDK doesn't have updateModelContext yet\n // This will be implemented by custom hosts or future MCP SDK versions\n // For now, log for debugging - custom hosts can intercept this via postMessage\n console.log(`[MCP App] updateModelContext:`, { content, structuredContent });\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"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
package/dist/react.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UnifiedToolResult, UnifiedHostContext, UnifiedApp, Platform } from './index.mjs';
|
|
1
|
+
import { UnifiedToolResult, UnifiedHostContext, UnifiedApp, Platform, UpdateModelContextParams } from './index.mjs';
|
|
2
2
|
export { CreateUnifiedAppResult, OpenAIGlobal, OpenAIWidgetProps, UnifiedAppOptions, UnifiedMessage, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI } from './index.mjs';
|
|
3
3
|
import { McpUiAppCapabilities } from '@modelcontextprotocol/ext-apps';
|
|
4
4
|
|
|
@@ -32,16 +32,20 @@ interface UseUnifiedAppResult {
|
|
|
32
32
|
platform: Platform;
|
|
33
33
|
/** Current host context (theme, displayMode, locale, etc.) */
|
|
34
34
|
hostContext: UnifiedHostContext | undefined;
|
|
35
|
-
/** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */
|
|
36
|
-
initialProps: unknown;
|
|
37
35
|
/** Widget props (OpenAI only, reactive) */
|
|
38
36
|
widgetProps: Record<string, unknown>;
|
|
39
|
-
/** Widget state - works on both platforms via React state */
|
|
37
|
+
/** Widget state - initialized from toolOutput on mount, works on both platforms via React state */
|
|
40
38
|
widgetState: unknown;
|
|
41
39
|
/** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */
|
|
42
40
|
setWidgetState: <T = unknown>(state: T) => void;
|
|
43
41
|
/** Update widget state partially - works on both platforms */
|
|
44
42
|
updateWidgetState: <T = unknown>(state: Partial<T>) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Update the AI's context silently (not visible in chat).
|
|
45
|
+
* Use this to inform the AI about user actions like viewing products,
|
|
46
|
+
* adding items to cart, or changing settings without triggering a response.
|
|
47
|
+
*/
|
|
48
|
+
updateModelContext: (params: UpdateModelContextParams) => Promise<void>;
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* React hook that provides a unified app interface for both OpenAI and MCP platforms
|
|
@@ -78,4 +82,4 @@ interface UseUnifiedAppResult {
|
|
|
78
82
|
*/
|
|
79
83
|
declare function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult;
|
|
80
84
|
|
|
81
|
-
export { Platform, UnifiedApp, UnifiedHostContext, UnifiedToolResult, type UseUnifiedAppOptions, type UseUnifiedAppResult, useUnifiedApp };
|
|
85
|
+
export { Platform, UnifiedApp, UnifiedHostContext, UnifiedToolResult, UpdateModelContextParams, type UseUnifiedAppOptions, type UseUnifiedAppResult, useUnifiedApp };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UnifiedToolResult, UnifiedHostContext, UnifiedApp, Platform } from './index.js';
|
|
1
|
+
import { UnifiedToolResult, UnifiedHostContext, UnifiedApp, Platform, UpdateModelContextParams } from './index.js';
|
|
2
2
|
export { CreateUnifiedAppResult, OpenAIGlobal, OpenAIWidgetProps, UnifiedAppOptions, UnifiedMessage, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI } from './index.js';
|
|
3
3
|
import { McpUiAppCapabilities } from '@modelcontextprotocol/ext-apps';
|
|
4
4
|
|
|
@@ -32,16 +32,20 @@ interface UseUnifiedAppResult {
|
|
|
32
32
|
platform: Platform;
|
|
33
33
|
/** Current host context (theme, displayMode, locale, etc.) */
|
|
34
34
|
hostContext: UnifiedHostContext | undefined;
|
|
35
|
-
/** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */
|
|
36
|
-
initialProps: unknown;
|
|
37
35
|
/** Widget props (OpenAI only, reactive) */
|
|
38
36
|
widgetProps: Record<string, unknown>;
|
|
39
|
-
/** Widget state - works on both platforms via React state */
|
|
37
|
+
/** Widget state - initialized from toolOutput on mount, works on both platforms via React state */
|
|
40
38
|
widgetState: unknown;
|
|
41
39
|
/** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */
|
|
42
40
|
setWidgetState: <T = unknown>(state: T) => void;
|
|
43
41
|
/** Update widget state partially - works on both platforms */
|
|
44
42
|
updateWidgetState: <T = unknown>(state: Partial<T>) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Update the AI's context silently (not visible in chat).
|
|
45
|
+
* Use this to inform the AI about user actions like viewing products,
|
|
46
|
+
* adding items to cart, or changing settings without triggering a response.
|
|
47
|
+
*/
|
|
48
|
+
updateModelContext: (params: UpdateModelContextParams) => Promise<void>;
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* React hook that provides a unified app interface for both OpenAI and MCP platforms
|
|
@@ -78,4 +82,4 @@ interface UseUnifiedAppResult {
|
|
|
78
82
|
*/
|
|
79
83
|
declare function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult;
|
|
80
84
|
|
|
81
|
-
export { Platform, UnifiedApp, UnifiedHostContext, UnifiedToolResult, type UseUnifiedAppOptions, type UseUnifiedAppResult, useUnifiedApp };
|
|
85
|
+
export { Platform, UnifiedApp, UnifiedHostContext, UnifiedToolResult, UpdateModelContextParams, type UseUnifiedAppOptions, type UseUnifiedAppResult, useUnifiedApp };
|
package/dist/react.js
CHANGED
|
@@ -127,6 +127,13 @@ function createOpenAIAdapter(options) {
|
|
|
127
127
|
return { isError: true };
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
|
+
updateModelContext: async ({ content, structuredContent }) => {
|
|
131
|
+
if (openai.updateModelContext) {
|
|
132
|
+
await openai.updateModelContext({ content, structuredContent });
|
|
133
|
+
} else {
|
|
134
|
+
console.log(`[${options.appInfo.name}] updateModelContext:`, { content, structuredContent });
|
|
135
|
+
}
|
|
136
|
+
},
|
|
130
137
|
getHostContext: () => ({
|
|
131
138
|
theme: openai.theme,
|
|
132
139
|
displayMode: openai.displayMode,
|
|
@@ -217,6 +224,9 @@ function createMCPAdapter(app, _options) {
|
|
|
217
224
|
const result = await app.openLink({ url });
|
|
218
225
|
return { isError: result.isError ?? false };
|
|
219
226
|
},
|
|
227
|
+
updateModelContext: async ({ content, structuredContent }) => {
|
|
228
|
+
console.log(`[MCP App] updateModelContext:`, { content, structuredContent });
|
|
229
|
+
},
|
|
220
230
|
getHostContext: () => {
|
|
221
231
|
const ctx = app.getHostContext();
|
|
222
232
|
return {
|
|
@@ -339,6 +349,13 @@ function useUnifiedApp(options) {
|
|
|
339
349
|
const openaiLocale = useOpenAIGlobal("locale", "en-US");
|
|
340
350
|
const optionsRef = (0, import_react.useRef)(options);
|
|
341
351
|
optionsRef.current = options;
|
|
352
|
+
const hasInitialized = (0, import_react.useRef)(false);
|
|
353
|
+
(0, import_react.useEffect)(() => {
|
|
354
|
+
if (platform === "openai" && !hasInitialized.current && openaiToolOutput !== void 0) {
|
|
355
|
+
setLocalWidgetState(openaiToolOutput);
|
|
356
|
+
hasInitialized.current = true;
|
|
357
|
+
}
|
|
358
|
+
}, [platform, openaiToolOutput]);
|
|
342
359
|
(0, import_react.useEffect)(() => {
|
|
343
360
|
if (platform === "openai" && openaiWidgetState !== null) {
|
|
344
361
|
if (!isOurUpdateRef.current) {
|
|
@@ -361,6 +378,11 @@ function useUnifiedApp(options) {
|
|
|
361
378
|
unifiedAppRef.current.updateWidgetState(state);
|
|
362
379
|
}
|
|
363
380
|
}, [platform]);
|
|
381
|
+
const updateModelContext = (0, import_react.useCallback)(async (params) => {
|
|
382
|
+
if (unifiedAppRef.current) {
|
|
383
|
+
await unifiedAppRef.current.updateModelContext(params);
|
|
384
|
+
}
|
|
385
|
+
}, []);
|
|
364
386
|
(0, import_react.useEffect)(() => {
|
|
365
387
|
if (platform === "openai" && isConnected) {
|
|
366
388
|
setHostContext({
|
|
@@ -423,10 +445,6 @@ function useUnifiedApp(options) {
|
|
|
423
445
|
mcpApp.ontoolresult = async (result) => {
|
|
424
446
|
await opts.onToolResult?.(result);
|
|
425
447
|
};
|
|
426
|
-
mcpApp.onerror = (err) => {
|
|
427
|
-
console.error(`[${opts.appInfo.name}]`, err);
|
|
428
|
-
opts.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
429
|
-
};
|
|
430
448
|
mcpApp.onhostcontextchanged = (params) => {
|
|
431
449
|
if (mounted) {
|
|
432
450
|
setHostContext((prev) => {
|
|
@@ -453,10 +471,12 @@ function useUnifiedApp(options) {
|
|
|
453
471
|
}
|
|
454
472
|
} catch (err) {
|
|
455
473
|
console.error(`[${opts.appInfo.name}]`, err);
|
|
474
|
+
const error2 = err instanceof Error ? err : new Error("Failed to connect");
|
|
456
475
|
if (mounted) {
|
|
457
|
-
setError(
|
|
476
|
+
setError(error2);
|
|
458
477
|
setIsConnected(false);
|
|
459
478
|
}
|
|
479
|
+
opts.onError?.(error2);
|
|
460
480
|
}
|
|
461
481
|
}
|
|
462
482
|
}
|
|
@@ -471,18 +491,17 @@ function useUnifiedApp(options) {
|
|
|
471
491
|
return "unknown";
|
|
472
492
|
}, [platform, isConnected]);
|
|
473
493
|
const widgetProps = platform === "openai" ? openaiWidgetProps?.props || {} : {};
|
|
474
|
-
const initialProps = platform === "openai" ? openaiToolOutput : void 0;
|
|
475
494
|
return {
|
|
476
495
|
app: unifiedApp,
|
|
477
496
|
isConnected,
|
|
478
497
|
error,
|
|
479
498
|
platform: finalPlatform,
|
|
480
499
|
hostContext,
|
|
481
|
-
initialProps,
|
|
482
500
|
widgetProps,
|
|
483
501
|
widgetState: localWidgetState,
|
|
484
502
|
setWidgetState,
|
|
485
|
-
updateWidgetState
|
|
503
|
+
updateWidgetState,
|
|
504
|
+
updateModelContext
|
|
486
505
|
};
|
|
487
506
|
}
|
|
488
507
|
// Annotate the CommonJS export names for ESM import in node:
|
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 // 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":[]}
|
|
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 UpdateModelContextParams {\n /** Text content array for context update */\n content?: Array<{ type: string; text?: string; [key: string]: unknown }>;\n /** Structured content for easier parsing */\n structuredContent?: Record<string, unknown>;\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 /**\n * Update the AI's context silently (not visible in chat).\n * Use this to inform the AI about user actions like viewing products,\n * adding items to cart, or changing settings without triggering a response.\n */\n updateModelContext: (params: UpdateModelContextParams) => Promise<void>;\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 updateModelContext?: (params: { content?: unknown[]; structuredContent?: Record<string, unknown> }) => Promise<void>;\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 updateModelContext: async ({ content, structuredContent }) => {\n // Check if host provides updateModelContext (e.g., our custom mock)\n if (openai.updateModelContext) {\n await openai.updateModelContext({ content, structuredContent });\n } else {\n // Fallback: log for debugging (native OpenAI doesn't have this yet)\n console.log(`[${options.appInfo.name}] updateModelContext:`, { content, structuredContent });\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 updateModelContext: async ({ content, structuredContent }) => {\n // MCP Apps SDK doesn't have updateModelContext yet\n // This will be implemented by custom hosts or future MCP SDK versions\n // For now, log for debugging - custom hosts can intercept this via postMessage\n console.log(`[MCP App] updateModelContext:`, { content, structuredContent });\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 type UpdateModelContextParams,\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 /** Widget props (OpenAI only, reactive) */\n widgetProps: Record<string, unknown>;\n /** Widget state - initialized from toolOutput on mount, 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 * Update the AI's context silently (not visible in chat).\n * Use this to inform the AI about user actions like viewing products,\n * adding items to cart, or changing settings without triggering a response.\n */\n updateModelContext: (params: UpdateModelContextParams) => Promise<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 // Initialize localWidgetState from initialProps (toolOutput) on first mount for OpenAI\n const hasInitialized = useRef(false);\n useEffect(() => {\n if (platform === \"openai\" && !hasInitialized.current && openaiToolOutput !== undefined) {\n setLocalWidgetState(openaiToolOutput);\n hasInitialized.current = true;\n }\n }, [platform, openaiToolOutput]);\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 model context - stable callback using ref\n const updateModelContext = useCallback(async (params: UpdateModelContextParams) => {\n if (unifiedAppRef.current) {\n await unifiedAppRef.current.updateModelContext(params);\n }\n }, []);\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.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 const error = err instanceof Error ? err : new Error(\"Failed to connect\");\n if (mounted) {\n setError(error);\n setIsConnected(false);\n }\n opts.onError?.(error);\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\n return {\n app: unifiedApp,\n isConnected,\n error,\n platform: finalPlatform,\n hostContext,\n widgetProps,\n widgetState: localWidgetState,\n setWidgetState,\n updateWidgetState,\n updateModelContext,\n };\n}\n\nexport default useUnifiedApp;\n"],"mappings":"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","names":["error"]}
|
package/dist/react.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
detectPlatform,
|
|
6
6
|
isMCP,
|
|
7
7
|
isOpenAI
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-IHPQNUCK.mjs";
|
|
9
9
|
|
|
10
10
|
// src/use-unified-app.ts
|
|
11
11
|
import { PostMessageTransport } from "@modelcontextprotocol/ext-apps";
|
|
@@ -54,6 +54,13 @@ function useUnifiedApp(options) {
|
|
|
54
54
|
const openaiLocale = useOpenAIGlobal("locale", "en-US");
|
|
55
55
|
const optionsRef = useRef(options);
|
|
56
56
|
optionsRef.current = options;
|
|
57
|
+
const hasInitialized = useRef(false);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (platform === "openai" && !hasInitialized.current && openaiToolOutput !== void 0) {
|
|
60
|
+
setLocalWidgetState(openaiToolOutput);
|
|
61
|
+
hasInitialized.current = true;
|
|
62
|
+
}
|
|
63
|
+
}, [platform, openaiToolOutput]);
|
|
57
64
|
useEffect(() => {
|
|
58
65
|
if (platform === "openai" && openaiWidgetState !== null) {
|
|
59
66
|
if (!isOurUpdateRef.current) {
|
|
@@ -76,6 +83,11 @@ function useUnifiedApp(options) {
|
|
|
76
83
|
unifiedAppRef.current.updateWidgetState(state);
|
|
77
84
|
}
|
|
78
85
|
}, [platform]);
|
|
86
|
+
const updateModelContext = useCallback(async (params) => {
|
|
87
|
+
if (unifiedAppRef.current) {
|
|
88
|
+
await unifiedAppRef.current.updateModelContext(params);
|
|
89
|
+
}
|
|
90
|
+
}, []);
|
|
79
91
|
useEffect(() => {
|
|
80
92
|
if (platform === "openai" && isConnected) {
|
|
81
93
|
setHostContext({
|
|
@@ -138,10 +150,6 @@ function useUnifiedApp(options) {
|
|
|
138
150
|
mcpApp.ontoolresult = async (result) => {
|
|
139
151
|
await opts.onToolResult?.(result);
|
|
140
152
|
};
|
|
141
|
-
mcpApp.onerror = (err) => {
|
|
142
|
-
console.error(`[${opts.appInfo.name}]`, err);
|
|
143
|
-
opts.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
144
|
-
};
|
|
145
153
|
mcpApp.onhostcontextchanged = (params) => {
|
|
146
154
|
if (mounted) {
|
|
147
155
|
setHostContext((prev) => {
|
|
@@ -168,10 +176,12 @@ function useUnifiedApp(options) {
|
|
|
168
176
|
}
|
|
169
177
|
} catch (err) {
|
|
170
178
|
console.error(`[${opts.appInfo.name}]`, err);
|
|
179
|
+
const error2 = err instanceof Error ? err : new Error("Failed to connect");
|
|
171
180
|
if (mounted) {
|
|
172
|
-
setError(
|
|
181
|
+
setError(error2);
|
|
173
182
|
setIsConnected(false);
|
|
174
183
|
}
|
|
184
|
+
opts.onError?.(error2);
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
187
|
}
|
|
@@ -186,18 +196,17 @@ function useUnifiedApp(options) {
|
|
|
186
196
|
return "unknown";
|
|
187
197
|
}, [platform, isConnected]);
|
|
188
198
|
const widgetProps = platform === "openai" ? openaiWidgetProps?.props || {} : {};
|
|
189
|
-
const initialProps = platform === "openai" ? openaiToolOutput : void 0;
|
|
190
199
|
return {
|
|
191
200
|
app: unifiedApp,
|
|
192
201
|
isConnected,
|
|
193
202
|
error,
|
|
194
203
|
platform: finalPlatform,
|
|
195
204
|
hostContext,
|
|
196
|
-
initialProps,
|
|
197
205
|
widgetProps,
|
|
198
206
|
widgetState: localWidgetState,
|
|
199
207
|
setWidgetState,
|
|
200
|
-
updateWidgetState
|
|
208
|
+
updateWidgetState,
|
|
209
|
+
updateModelContext
|
|
201
210
|
};
|
|
202
211
|
}
|
|
203
212
|
export {
|
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 // 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":[]}
|
|
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 type UpdateModelContextParams,\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 /** Widget props (OpenAI only, reactive) */\n widgetProps: Record<string, unknown>;\n /** Widget state - initialized from toolOutput on mount, 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 * Update the AI's context silently (not visible in chat).\n * Use this to inform the AI about user actions like viewing products,\n * adding items to cart, or changing settings without triggering a response.\n */\n updateModelContext: (params: UpdateModelContextParams) => Promise<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 // Initialize localWidgetState from initialProps (toolOutput) on first mount for OpenAI\n const hasInitialized = useRef(false);\n useEffect(() => {\n if (platform === \"openai\" && !hasInitialized.current && openaiToolOutput !== undefined) {\n setLocalWidgetState(openaiToolOutput);\n hasInitialized.current = true;\n }\n }, [platform, openaiToolOutput]);\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 model context - stable callback using ref\n const updateModelContext = useCallback(async (params: UpdateModelContextParams) => {\n if (unifiedAppRef.current) {\n await unifiedAppRef.current.updateModelContext(params);\n }\n }, []);\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.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 const error = err instanceof Error ? err : new Error(\"Failed to connect\");\n if (mounted) {\n setError(error);\n setIsConnected(false);\n }\n opts.onError?.(error);\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\n return {\n app: unifiedApp,\n isConnected,\n error,\n platform: finalPlatform,\n hostContext,\n widgetProps,\n widgetState: localWidgetState,\n setWidgetState,\n updateWidgetState,\n updateModelContext,\n };\n}\n\nexport default useUnifiedApp;\n"],"mappings":"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","names":["error"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-ui-ext-apps-openai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Unified utility for building apps that work on both OpenAI ChatGPT and MCP Apps platforms",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"build": "tsup",
|
|
26
26
|
"dev": "tsup --watch",
|
|
27
27
|
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "vitest",
|
|
29
|
+
"test:run": "vitest run",
|
|
28
30
|
"prepublishOnly": "npm run build"
|
|
29
31
|
},
|
|
30
32
|
"keywords": [
|
|
@@ -52,9 +54,18 @@
|
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
56
|
"@modelcontextprotocol/ext-apps": "^0.3.0",
|
|
57
|
+
"@testing-library/dom": "^10.4.1",
|
|
58
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
59
|
+
"@testing-library/react": "^16.3.1",
|
|
60
|
+
"@types/node": "^25.0.9",
|
|
55
61
|
"@types/react": "^18.2.0",
|
|
62
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
63
|
+
"jsdom": "^27.0.1",
|
|
56
64
|
"react": "^18.2.0",
|
|
65
|
+
"react-dom": "^18.2.0",
|
|
57
66
|
"tsup": "^8.0.0",
|
|
58
|
-
"typescript": "^5.0.0"
|
|
67
|
+
"typescript": "^5.0.0",
|
|
68
|
+
"vite": "^7.3.1",
|
|
69
|
+
"vitest": "^3.2.4"
|
|
59
70
|
}
|
|
60
71
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/unified-app.ts"],"sourcesContent":["/**\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"],"mappings":"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","names":[]}
|