mcp-ui-ext-apps-openai 1.0.0

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/index.js ADDED
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ createMCPAdapter: () => createMCPAdapter,
24
+ createOpenAIAdapter: () => createOpenAIAdapter,
25
+ createUnifiedApp: () => createUnifiedApp,
26
+ detectPlatform: () => detectPlatform,
27
+ isMCP: () => isMCP,
28
+ isOpenAI: () => isOpenAI
29
+ });
30
+ module.exports = __toCommonJS(src_exports);
31
+
32
+ // src/unified-app.ts
33
+ function detectPlatform() {
34
+ if (typeof window !== "undefined" && window.openai) {
35
+ return "openai";
36
+ }
37
+ return "unknown";
38
+ }
39
+ function isOpenAI() {
40
+ return detectPlatform() === "openai";
41
+ }
42
+ function isMCP() {
43
+ return detectPlatform() === "mcp" || detectPlatform() === "unknown";
44
+ }
45
+ function createOpenAIAdapter(options) {
46
+ const openai = window.openai;
47
+ const handleGlobalsChange = (_event) => {
48
+ if (options.onHostContextChanged) {
49
+ options.onHostContextChanged({
50
+ theme: openai.theme,
51
+ displayMode: openai.displayMode,
52
+ locale: openai.locale,
53
+ safeAreaInsets: openai.safeArea?.insets,
54
+ maxWidth: openai.maxWidth,
55
+ maxHeight: openai.maxHeight
56
+ });
57
+ }
58
+ };
59
+ window.addEventListener("openai:set_globals", handleGlobalsChange);
60
+ if (options.onHostContextChanged) {
61
+ options.onHostContextChanged({
62
+ theme: openai.theme,
63
+ displayMode: openai.displayMode,
64
+ locale: openai.locale,
65
+ safeAreaInsets: openai.safeArea?.insets,
66
+ maxWidth: openai.maxWidth,
67
+ maxHeight: openai.maxHeight
68
+ });
69
+ }
70
+ if (options.onToolResult && openai.toolOutput) {
71
+ const result = convertOpenAIToolOutput(openai.toolOutput);
72
+ options.onToolResult(result);
73
+ }
74
+ if (options.onToolInput && openai.toolInput) {
75
+ options.onToolInput(openai.toolInput);
76
+ }
77
+ return {
78
+ platform: "openai",
79
+ callServerTool: async ({ name, arguments: args }) => {
80
+ try {
81
+ const result = await openai.callTool(name, args);
82
+ return convertOpenAIToolOutput(result);
83
+ } catch (error) {
84
+ if (options.onError) {
85
+ options.onError(error);
86
+ }
87
+ throw error;
88
+ }
89
+ },
90
+ sendMessage: async (message, _opts) => {
91
+ try {
92
+ const textContent = message.content.find((c) => c.type === "text");
93
+ if (textContent?.text) {
94
+ await openai.sendFollowUpMessage({ prompt: textContent.text });
95
+ }
96
+ return { isError: false };
97
+ } catch (error) {
98
+ if (options.onError) {
99
+ options.onError(error);
100
+ }
101
+ return { isError: true };
102
+ }
103
+ },
104
+ sendLog: async ({ level, data }) => {
105
+ const consoleLevel = level === "warning" ? "warn" : level;
106
+ console[consoleLevel](`[${options.appInfo.name}]`, data);
107
+ },
108
+ openLink: async ({ url }) => {
109
+ try {
110
+ await openai.openExternal({ href: url });
111
+ return { isError: false };
112
+ } catch (error) {
113
+ if (options.onError) {
114
+ options.onError(error);
115
+ }
116
+ return { isError: true };
117
+ }
118
+ },
119
+ getHostContext: () => ({
120
+ theme: openai.theme,
121
+ displayMode: openai.displayMode,
122
+ locale: openai.locale,
123
+ safeAreaInsets: openai.safeArea?.insets,
124
+ maxWidth: openai.maxWidth,
125
+ maxHeight: openai.maxHeight
126
+ }),
127
+ getToolInput: () => openai.toolInput || {},
128
+ getToolOutput: () => openai.toolOutput,
129
+ getToolResponseMetadata: () => openai.toolResponseMetadata || {},
130
+ getWidgetState: () => openai.widgetState,
131
+ getWidgetProps: () => openai.widget?.props || {},
132
+ setWidgetState: (state) => {
133
+ openai.setWidgetState(state);
134
+ },
135
+ updateWidgetState: (state) => {
136
+ openai.updateWidgetState(state);
137
+ },
138
+ requestDisplayMode: async (mode) => {
139
+ await openai.requestDisplayMode(mode);
140
+ },
141
+ requestClose: () => {
142
+ openai.requestClose();
143
+ },
144
+ notifyIntrinsicHeight: (height) => {
145
+ openai.notifyIntrinsicHeight(height);
146
+ },
147
+ uploadFile: async (file) => {
148
+ return await openai.uploadFile(file);
149
+ },
150
+ getFileDownloadUrl: async ({ fileId }) => {
151
+ return await openai.getFileDownloadUrl({ fileId });
152
+ },
153
+ setOpenInAppUrl: ({ href }) => {
154
+ openai.setOpenInAppUrl({ href });
155
+ },
156
+ share: async (params) => {
157
+ await openai.share(params);
158
+ },
159
+ callCompletion: async (params) => {
160
+ return await openai.callCompletion(params);
161
+ },
162
+ streamCompletion: (params) => {
163
+ return openai.streamCompletion(params);
164
+ },
165
+ _raw: openai
166
+ };
167
+ }
168
+ function convertOpenAIToolOutput(output) {
169
+ if (typeof output === "string") {
170
+ return {
171
+ content: [{ type: "text", text: output }]
172
+ };
173
+ }
174
+ if (output && typeof output === "object") {
175
+ if ("content" in output && Array.isArray(output.content)) {
176
+ return output;
177
+ }
178
+ if ("text" in output) {
179
+ return {
180
+ content: [{ type: "text", text: String(output.text) }]
181
+ };
182
+ }
183
+ return {
184
+ content: [{ type: "text", text: JSON.stringify(output) }]
185
+ };
186
+ }
187
+ return {
188
+ content: [{ type: "text", text: String(output) }]
189
+ };
190
+ }
191
+ function createMCPAdapter(app, _options) {
192
+ return {
193
+ platform: "mcp",
194
+ callServerTool: async ({ name, arguments: args }) => {
195
+ const result = await app.callServerTool({ name, arguments: args });
196
+ return result;
197
+ },
198
+ sendMessage: async (message, opts) => {
199
+ const result = await app.sendMessage(message, opts);
200
+ return { isError: result.isError ?? false };
201
+ },
202
+ sendLog: async ({ level, data }) => {
203
+ await app.sendLog({ level, data });
204
+ },
205
+ openLink: async ({ url }) => {
206
+ const result = await app.openLink({ url });
207
+ return { isError: result.isError ?? false };
208
+ },
209
+ getHostContext: () => {
210
+ const ctx = app.getHostContext();
211
+ return {
212
+ theme: ctx?.theme,
213
+ displayMode: ctx?.displayMode,
214
+ locale: ctx?.locale,
215
+ safeAreaInsets: ctx?.safeAreaInsets
216
+ };
217
+ },
218
+ getToolInput: () => ({}),
219
+ // MCP handles this via ontoolinput callback
220
+ getToolOutput: () => null,
221
+ // MCP handles this via ontoolresult callback
222
+ // OpenAI-specific methods - no-op or stub implementations for MCP
223
+ getToolResponseMetadata: () => ({}),
224
+ getWidgetState: () => null,
225
+ getWidgetProps: () => ({}),
226
+ setWidgetState: (_state) => {
227
+ },
228
+ updateWidgetState: (_state) => {
229
+ },
230
+ requestDisplayMode: async (_mode) => {
231
+ },
232
+ requestClose: () => {
233
+ },
234
+ notifyIntrinsicHeight: (_height) => {
235
+ },
236
+ uploadFile: async (_file) => {
237
+ throw new Error("uploadFile is not supported on MCP platform");
238
+ },
239
+ getFileDownloadUrl: async (_params) => {
240
+ throw new Error("getFileDownloadUrl is not supported on MCP platform");
241
+ },
242
+ setOpenInAppUrl: (_params) => {
243
+ },
244
+ share: async (_params) => {
245
+ throw new Error("share is not supported on MCP platform");
246
+ },
247
+ callCompletion: async (_params) => {
248
+ throw new Error("callCompletion is not supported on MCP platform");
249
+ },
250
+ streamCompletion: (_params) => {
251
+ throw new Error("streamCompletion is not supported on MCP platform");
252
+ },
253
+ _raw: app
254
+ };
255
+ }
256
+ function createUnifiedApp(options) {
257
+ const platform = detectPlatform();
258
+ if (platform === "openai") {
259
+ try {
260
+ const app = createOpenAIAdapter(options);
261
+ return {
262
+ app,
263
+ isConnected: true,
264
+ error: null,
265
+ platform: "openai"
266
+ };
267
+ } catch (error) {
268
+ return {
269
+ app: null,
270
+ isConnected: false,
271
+ error,
272
+ platform: "openai"
273
+ };
274
+ }
275
+ }
276
+ return {
277
+ app: null,
278
+ isConnected: false,
279
+ error: null,
280
+ platform: "unknown"
281
+ };
282
+ }
283
+ // Annotate the CommonJS export names for ESM import in node:
284
+ 0 && (module.exports = {
285
+ createMCPAdapter,
286
+ createOpenAIAdapter,
287
+ createUnifiedApp,
288
+ detectPlatform,
289
+ isMCP,
290
+ isOpenAI
291
+ });
292
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,17 @@
1
+ import {
2
+ createMCPAdapter,
3
+ createOpenAIAdapter,
4
+ createUnifiedApp,
5
+ detectPlatform,
6
+ isMCP,
7
+ isOpenAI
8
+ } from "./chunk-MX7VCLPI.mjs";
9
+ export {
10
+ createMCPAdapter,
11
+ createOpenAIAdapter,
12
+ createUnifiedApp,
13
+ detectPlatform,
14
+ isMCP,
15
+ isOpenAI
16
+ };
17
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,81 @@
1
+ import { UnifiedToolResult, UnifiedHostContext, UnifiedApp, Platform } from './index.mjs';
2
+ export { CreateUnifiedAppResult, OpenAIGlobal, OpenAIWidgetProps, UnifiedAppOptions, UnifiedMessage, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI } from './index.mjs';
3
+ import { McpUiAppCapabilities } from '@modelcontextprotocol/ext-apps';
4
+
5
+ /**
6
+ * @file React hook for unified OpenAI/MCP app usage
7
+ *
8
+ * This hook provides a unified interface for apps that need to run on both
9
+ * OpenAI ChatGPT and MCP Apps platforms.
10
+ */
11
+
12
+ interface UseUnifiedAppOptions {
13
+ appInfo: {
14
+ name: string;
15
+ version: string;
16
+ };
17
+ capabilities?: McpUiAppCapabilities;
18
+ onToolInput?: (input: unknown) => void | Promise<void>;
19
+ onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;
20
+ onHostContextChanged?: (context: UnifiedHostContext) => void;
21
+ onTeardown?: () => void | Promise<void>;
22
+ onError?: (error: Error) => void;
23
+ }
24
+ interface UseUnifiedAppResult {
25
+ /** The unified app instance */
26
+ app: UnifiedApp | null;
27
+ /** Whether the app is connected to the host */
28
+ isConnected: boolean;
29
+ /** Any connection error */
30
+ error: Error | null;
31
+ /** The detected platform */
32
+ platform: Platform;
33
+ /** Current host context (theme, displayMode, locale, etc.) */
34
+ hostContext: UnifiedHostContext | undefined;
35
+ /** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */
36
+ initialProps: unknown;
37
+ /** Widget props (OpenAI only, reactive) */
38
+ widgetProps: Record<string, unknown>;
39
+ /** Widget state - works on both platforms via React state */
40
+ widgetState: unknown;
41
+ /** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */
42
+ setWidgetState: <T = unknown>(state: T) => void;
43
+ /** Update widget state partially - works on both platforms */
44
+ updateWidgetState: <T = unknown>(state: Partial<T>) => void;
45
+ }
46
+ /**
47
+ * React hook that provides a unified app interface for both OpenAI and MCP platforms
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * function MyApp() {
52
+ * const {
53
+ * app,
54
+ * isConnected,
55
+ * platform,
56
+ * hostContext,
57
+ * widgetState,
58
+ * setWidgetState,
59
+ * } = useUnifiedApp({
60
+ * appInfo: { name: "My App", version: "1.0.0" },
61
+ * capabilities: {},
62
+ * onError: console.error,
63
+ * });
64
+ *
65
+ * const counter = (widgetState as { value?: number } | null)?.value ?? 0;
66
+ *
67
+ * return (
68
+ * <div>
69
+ * <p>Running on: {platform}</p>
70
+ * <p>Counter: {counter}</p>
71
+ * <button onClick={() => setWidgetState({ value: counter + 1 })}>
72
+ * Increment
73
+ * </button>
74
+ * </div>
75
+ * );
76
+ * }
77
+ * ```
78
+ */
79
+ declare function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult;
80
+
81
+ export { Platform, UnifiedApp, UnifiedHostContext, UnifiedToolResult, type UseUnifiedAppOptions, type UseUnifiedAppResult, useUnifiedApp };
@@ -0,0 +1,81 @@
1
+ import { UnifiedToolResult, UnifiedHostContext, UnifiedApp, Platform } from './index.js';
2
+ export { CreateUnifiedAppResult, OpenAIGlobal, OpenAIWidgetProps, UnifiedAppOptions, UnifiedMessage, createMCPAdapter, createOpenAIAdapter, createUnifiedApp, detectPlatform, isMCP, isOpenAI } from './index.js';
3
+ import { McpUiAppCapabilities } from '@modelcontextprotocol/ext-apps';
4
+
5
+ /**
6
+ * @file React hook for unified OpenAI/MCP app usage
7
+ *
8
+ * This hook provides a unified interface for apps that need to run on both
9
+ * OpenAI ChatGPT and MCP Apps platforms.
10
+ */
11
+
12
+ interface UseUnifiedAppOptions {
13
+ appInfo: {
14
+ name: string;
15
+ version: string;
16
+ };
17
+ capabilities?: McpUiAppCapabilities;
18
+ onToolInput?: (input: unknown) => void | Promise<void>;
19
+ onToolResult?: (result: UnifiedToolResult) => void | Promise<void>;
20
+ onHostContextChanged?: (context: UnifiedHostContext) => void;
21
+ onTeardown?: () => void | Promise<void>;
22
+ onError?: (error: Error) => void;
23
+ }
24
+ interface UseUnifiedAppResult {
25
+ /** The unified app instance */
26
+ app: UnifiedApp | null;
27
+ /** Whether the app is connected to the host */
28
+ isConnected: boolean;
29
+ /** Any connection error */
30
+ error: Error | null;
31
+ /** The detected platform */
32
+ platform: Platform;
33
+ /** Current host context (theme, displayMode, locale, etc.) */
34
+ hostContext: UnifiedHostContext | undefined;
35
+ /** Initial props from toolOutput (OpenAI only, undefined on MCP) - use as initial data for component */
36
+ initialProps: unknown;
37
+ /** Widget props (OpenAI only, reactive) */
38
+ widgetProps: Record<string, unknown>;
39
+ /** Widget state - works on both platforms via React state */
40
+ widgetState: unknown;
41
+ /** Set widget state - works on both platforms (also syncs to OpenAI if on that platform) */
42
+ setWidgetState: <T = unknown>(state: T) => void;
43
+ /** Update widget state partially - works on both platforms */
44
+ updateWidgetState: <T = unknown>(state: Partial<T>) => void;
45
+ }
46
+ /**
47
+ * React hook that provides a unified app interface for both OpenAI and MCP platforms
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * function MyApp() {
52
+ * const {
53
+ * app,
54
+ * isConnected,
55
+ * platform,
56
+ * hostContext,
57
+ * widgetState,
58
+ * setWidgetState,
59
+ * } = useUnifiedApp({
60
+ * appInfo: { name: "My App", version: "1.0.0" },
61
+ * capabilities: {},
62
+ * onError: console.error,
63
+ * });
64
+ *
65
+ * const counter = (widgetState as { value?: number } | null)?.value ?? 0;
66
+ *
67
+ * return (
68
+ * <div>
69
+ * <p>Running on: {platform}</p>
70
+ * <p>Counter: {counter}</p>
71
+ * <button onClick={() => setWidgetState({ value: counter + 1 })}>
72
+ * Increment
73
+ * </button>
74
+ * </div>
75
+ * );
76
+ * }
77
+ * ```
78
+ */
79
+ declare function useUnifiedApp(options: UseUnifiedAppOptions): UseUnifiedAppResult;
80
+
81
+ export { Platform, UnifiedApp, UnifiedHostContext, UnifiedToolResult, type UseUnifiedAppOptions, type UseUnifiedAppResult, useUnifiedApp };