@onereach/idw-apps 0.1.2-beta.6.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/README.md +411 -0
- package/dist/app/createIdwApp.d.ts +104 -0
- package/dist/app/createIdwApp.d.ts.map +1 -0
- package/dist/app/createIdwApp.js +151 -0
- package/dist/app/createIdwApp.js.map +1 -0
- package/dist/app/index.d.ts +15 -0
- package/dist/app/index.d.ts.map +1 -0
- package/dist/app/index.js +3 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/installIdwAppApi.d.ts +98 -0
- package/dist/app/installIdwAppApi.d.ts.map +1 -0
- package/dist/app/installIdwAppApi.js +314 -0
- package/dist/app/installIdwAppApi.js.map +1 -0
- package/dist/createIdwAppBridge.d.ts +13 -0
- package/dist/createIdwAppBridge.d.ts.map +1 -0
- package/dist/createIdwAppBridge.js +555 -0
- package/dist/createIdwAppBridge.js.map +1 -0
- package/dist/hostStateChannel.d.ts +51 -0
- package/dist/hostStateChannel.d.ts.map +1 -0
- package/dist/hostStateChannel.js +104 -0
- package/dist/hostStateChannel.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +81 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +67 -0
- package/dist/protocol.js.map +1 -0
- package/dist/stateStore.d.ts +23 -0
- package/dist/stateStore.d.ts.map +1 -0
- package/dist/stateStore.js +70 -0
- package/dist/stateStore.js.map +1 -0
- package/dist/types.d.ts +268 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
- package/skills/idw-app-development/SKILL.md +458 -0
package/README.md
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# @onereach/idw-apps
|
|
2
|
+
|
|
3
|
+
IDW adapter around [`@modelcontextprotocol/ext-apps`](https://www.npmjs.com/package/@modelcontextprotocol/ext-apps) for hosting **MCP Apps** and embedded IDW web apps (Web Skills, feed widgets, sidebar apps), plus a small iframe-facing IDW/OpenAI-compatible **widget-state** API.
|
|
4
|
+
|
|
5
|
+
This is a thin wrapper, not a replacement or a new framework:
|
|
6
|
+
|
|
7
|
+
- All standard MCP Apps behavior is preserved (tool input/result notifications, app tool calls, server tool calls, server resource/prompt discovery, resource reads, messages, downloads, open-link, logging, size changes, display-mode requests, teardown requests, and model-context updates).
|
|
8
|
+
- On top of that it adds an **IDW app channel** (`window.idw.app`) for durable state and host actions, plus an optional **OpenAI Apps SDK compatibility shim** (`window.openai`).
|
|
9
|
+
- IDW app methods intentionally mirror MCP Apps semantics: `callIdwAction()` feels like `callServerTool()`, and `updateAgentContext()` feels like `updateModelContext()`.
|
|
10
|
+
- It is **framework-agnostic** — no Vue/React. State persistence happens through caller callbacks, never direct store/backend imports.
|
|
11
|
+
|
|
12
|
+
External apps built directly with `@modelcontextprotocol/ext-apps` work with the
|
|
13
|
+
host bridge. They use the standard MCP Apps JSON-RPC transport and do not need
|
|
14
|
+
`@onereach/idw-apps/app`. The IDW channel is additive and ignored by standard
|
|
15
|
+
MCP apps.
|
|
16
|
+
|
|
17
|
+
Current OpenAI Apps SDK apps that use the
|
|
18
|
+
[standard MCP Apps bridge](https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt#host-bridge)
|
|
19
|
+
have the same compatibility. ChatGPT-only `window.openai` extensions are a separate layer:
|
|
20
|
+
IDW currently provides only the documented widget-state aliases, not the full
|
|
21
|
+
ChatGPT component runtime (`callTool`, file APIs, modals, checkout, and other
|
|
22
|
+
host-specific extensions). Build portable app behavior on MCP Apps and
|
|
23
|
+
feature-detect optional `window.openai` methods.
|
|
24
|
+
|
|
25
|
+
> **Module format:** ESM-only with `bundler` module resolution (required to consume `@modelcontextprotocol/ext-apps`' `exports` map). Both consumers — the IDW host (Vite) and in-iframe apps — are bundled, so this is a deliberate deviation from the CJS+ESM packages in this repo.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add @onereach/idw-apps
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`@onereach/idw-apps` owns the underlying MCP Apps SDK dependency. IDW app authors do not
|
|
34
|
+
install or import `@modelcontextprotocol/ext-apps` directly.
|
|
35
|
+
|
|
36
|
+
## Two entry points
|
|
37
|
+
|
|
38
|
+
| Import | Runs in | Purpose |
|
|
39
|
+
|--------|---------|---------|
|
|
40
|
+
| `@onereach/idw-apps` | Host (idw-ui) | `createIdwAppBridge` — wrap `AppBridge` for one app iframe |
|
|
41
|
+
| `@onereach/idw-apps/app` | Inside the app iframe | `App` / `createIdwApp` — standard MCP App plus IDW state/actions |
|
|
42
|
+
|
|
43
|
+
## Host side
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { createIdwAppBridge } from '@onereach/idw-apps';
|
|
47
|
+
|
|
48
|
+
const bridge = createIdwAppBridge({
|
|
49
|
+
iframe,
|
|
50
|
+
idwId,
|
|
51
|
+
chatId,
|
|
52
|
+
hostKey, // durable app-instance identity
|
|
53
|
+
surface: 'app-sidebar', // web-app | feed-widget | app-sidebar | chat-inline | fullscreen
|
|
54
|
+
appInstanceId: hostKey,
|
|
55
|
+
auth: {
|
|
56
|
+
getOrToken: (ctx) => issueOrTokenForApp(ctx),
|
|
57
|
+
apiBaseUrl: IDW_API_URL,
|
|
58
|
+
allowTokenRequest: true, // enables lazy callIdwAction({ name: 'getAuthToken' })
|
|
59
|
+
},
|
|
60
|
+
// Include getAuthToken here when an allowlist is present and lazy auth is enabled.
|
|
61
|
+
allowedActions: ['openSkill', 'startConversation', 'getAuthToken'],
|
|
62
|
+
artifactId, // optional: current MCP-backed runtime tool call
|
|
63
|
+
toolCallId, // optional: current MCP-backed tool call id
|
|
64
|
+
initialState, // durable widget/view state previously loaded from your DB
|
|
65
|
+
onStateChange: (state) => persistWidgetState(chatId, hostKey, state), // may be async
|
|
66
|
+
onIdwAction: (params, ctx) => handleIdwAction(params, ctx),
|
|
67
|
+
onCallTool: (params, ctx) => chatApi.callMcpAppTool(ctx.chatId!, ctx.artifactId!, params),
|
|
68
|
+
onListResources: (params, ctx) => chatApi.listMcpAppResources(ctx.chatId!, ctx.artifactId!, params),
|
|
69
|
+
onListResourceTemplates: (params, ctx) => chatApi.listMcpAppResourceTemplates(ctx.chatId!, ctx.artifactId!, params),
|
|
70
|
+
onListPrompts: (params, ctx) => chatApi.listMcpAppPrompts(ctx.chatId!, ctx.artifactId!, params),
|
|
71
|
+
onReadResource: (params, ctx) => chatApi.readMcpAppResource(ctx.chatId!, ctx.artifactId!, params),
|
|
72
|
+
onMessage: (params, ctx) => enqueueAppMessage(ctx.chatId!, ctx.hostKey!, params),
|
|
73
|
+
onDownloadFile: (params, ctx) => downloadFromApp(ctx.hostKey!, params),
|
|
74
|
+
// Keep the original MCP payload: it may be structured-only, in which case
|
|
75
|
+
// `text` is an empty string.
|
|
76
|
+
onUpdateModelContext: (text, params) => queueAppContext(hostKey, { text, params }),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await bridge.connect();
|
|
80
|
+
|
|
81
|
+
// New agent turn on the same app — no reconnect, no iframe reload:
|
|
82
|
+
await bridge.updateRuntimeContext({
|
|
83
|
+
artifactId: next.tool_call_id,
|
|
84
|
+
toolCallId: next.tool_call_id,
|
|
85
|
+
toolInput: next.tool_args,
|
|
86
|
+
toolResult: next.tool_result,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// on unmount
|
|
90
|
+
await bridge.disconnect();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Returned API
|
|
94
|
+
|
|
95
|
+
- `connect()` / `disconnect()`
|
|
96
|
+
- `updateRuntimeContext({ artifactId, toolCallId, toolInput, toolResult })` — update runtime identity without recreating the bridge
|
|
97
|
+
- `sendToolInput(args, toolCallId?)` / `sendToolResult(result, toolCallId?)` — stale `toolCallId` values are ignored
|
|
98
|
+
- `listAppTools()` / `callAppTool(params, options?)` — discover and invoke standard MCP tools supplied by the connected iframe app
|
|
99
|
+
- `getAppCapabilities()` — inspect capabilities declared by the initialized app before discovering optional features such as app tools
|
|
100
|
+
- `sendToolCancelled(reason?)`
|
|
101
|
+
- `sendToolListChanged()` / `sendResourceListChanged()` / `sendPromptListChanged()`
|
|
102
|
+
- `setHostContext(context)` — update standard MCP Apps host context while preserving `hostContext.idw`
|
|
103
|
+
- `getWidgetState()` / `setWidgetState(state)`
|
|
104
|
+
- IDW host context is sent through MCP host context under `hostContext.idw`:
|
|
105
|
+
`{ idwId, surface, appInstanceId, auth: { apiBaseUrl }, allowedActions }`.
|
|
106
|
+
`auth.orToken` is omitted by default.
|
|
107
|
+
|
|
108
|
+
For advanced host integrations, `@onereach/idw-apps` also exports:
|
|
109
|
+
|
|
110
|
+
- `McpAppBridge` — the low-level upstream MCP Apps host bridge
|
|
111
|
+
- `McpAppBridgePostMessageTransport`
|
|
112
|
+
|
|
113
|
+
Use `createIdwAppBridge` for IDW chat apps unless you explicitly need to build
|
|
114
|
+
your own host lifecycle/state adapter.
|
|
115
|
+
|
|
116
|
+
`onOpenIdwApp` and `openIdwAppToolName` remain available only as deprecated
|
|
117
|
+
migration hooks for the legacy `open_idw_app` convention. New integrations
|
|
118
|
+
should use standard MCP tools.
|
|
119
|
+
|
|
120
|
+
### Host persistence contract
|
|
121
|
+
|
|
122
|
+
`onStateChange` is the only persistence hook. When an app calls
|
|
123
|
+
`window.idw.app.setWidgetState(state)` or `window.openai.setWidgetState(state)`, the
|
|
124
|
+
host wrapper updates its in-memory state immediately and then invokes
|
|
125
|
+
`onStateChange(state)`. Store that value in your DB using the durable `hostKey`
|
|
126
|
+
(usually scoped by `chatId` and user/session). On the next reload, load that DB
|
|
127
|
+
value first and pass it back as `initialState`.
|
|
128
|
+
|
|
129
|
+
The package does not import your DB/client/store directly.
|
|
130
|
+
|
|
131
|
+
### Artifact / runtime split
|
|
132
|
+
|
|
133
|
+
- `hostKey` identifies the durable app instance — one bridge, one iframe.
|
|
134
|
+
- `artifactId` / `toolCallId` identify the current runtime tool call and may change over the bridge's lifetime.
|
|
135
|
+
- `updateRuntimeContext` updates them **without** recreating `AppBridge` or reloading the iframe, so widget UI state survives new turns.
|
|
136
|
+
- Generic Web Apps can omit runtime tool-call identity and MCP proxy callbacks entirely.
|
|
137
|
+
|
|
138
|
+
### App-provided tools
|
|
139
|
+
|
|
140
|
+
An iframe app can expose standard MCP tools for the host or agent to call. After
|
|
141
|
+
the app initializes, discover and invoke those tools through the bridge:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
if (bridge.getAppCapabilities()?.tools) {
|
|
145
|
+
const appTools = await bridge.listAppTools();
|
|
146
|
+
|
|
147
|
+
const result = await bridge.callAppTool({
|
|
148
|
+
name: 'listTasks',
|
|
149
|
+
arguments: {},
|
|
150
|
+
}, {
|
|
151
|
+
timeout: 10_000,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`callAppTool()` is host → app. It is distinct from `onCallTool` and
|
|
157
|
+
`app.callServerTool()`, which are app → MCP-server operations. It delegates to
|
|
158
|
+
the standard MCP Apps `tools/list` and `tools/call` protocol; the package adds
|
|
159
|
+
no IDW-specific action messages.
|
|
160
|
+
|
|
161
|
+
App-provided tools exist only while their iframe is connected. IDW UI/API is
|
|
162
|
+
responsible for deciding which tools become agent-visible, namespacing them by
|
|
163
|
+
app instance, applying product policy/confirmation, and routing an agent call
|
|
164
|
+
to the live bridge. A backend capability that must work without the iframe
|
|
165
|
+
mounted should be a server/backend tool instead.
|
|
166
|
+
|
|
167
|
+
Use the following direction map when choosing an API:
|
|
168
|
+
|
|
169
|
+
| Intent | API |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| App calls its MCP server | `app.callServerTool()` |
|
|
172
|
+
| IDW host/agent calls a live app tool | `bridge.listAppTools()` / `bridge.callAppTool()` |
|
|
173
|
+
| App requests an IDW host/platform action | `app.callIdwAction()` |
|
|
174
|
+
| App gives concise context to the agent | `app.updateAgentContext()` |
|
|
175
|
+
| App saves a lightweight restore hint | `app.state.setWidgetState()` |
|
|
176
|
+
|
|
177
|
+
## App side (inside the iframe)
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { createIdwApp } from '@onereach/idw-apps/app';
|
|
181
|
+
|
|
182
|
+
const app = createIdwApp({
|
|
183
|
+
appInfo: {
|
|
184
|
+
name: 'Hangman',
|
|
185
|
+
version: '1.0.0',
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
app.ontoolinput = ({ arguments: args }) => {
|
|
190
|
+
// Standard MCP Apps tool input notification.
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
app.ontoolresult = (result) => {
|
|
194
|
+
// Store/render the latest authoritative business snapshot from
|
|
195
|
+
// result.structuredContent.
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
app.state.subscribeWidgetState((state) => {
|
|
199
|
+
// Reapply only a lightweight view/restore hint such as a route or selected ID.
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await app.connect(); // Connect immediately so tool input/results can arrive.
|
|
203
|
+
|
|
204
|
+
const hostCapabilities = app.getHostCapabilities();
|
|
205
|
+
const hostContext = app.getHostContext();
|
|
206
|
+
|
|
207
|
+
const access = await app.validateAccess({
|
|
208
|
+
url: window.location.href,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
app.state.getWidgetState(); // -> latest widget state known to the app
|
|
212
|
+
app.state.setWidgetState({ tab: 'chart' }); // replaces the whole widget-state object
|
|
213
|
+
app.state.subscribeWidgetState((state) => {
|
|
214
|
+
// React to local or host-pushed state changes.
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
await app.callIdwAction({
|
|
218
|
+
name: 'openSkill',
|
|
219
|
+
arguments: {
|
|
220
|
+
skillId: 'training',
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const auth = await app.getAuthToken();
|
|
225
|
+
// auth.token is an OR user identity token for this web app session.
|
|
226
|
+
|
|
227
|
+
await app.updateAgentContext({
|
|
228
|
+
content: [{
|
|
229
|
+
type: 'text',
|
|
230
|
+
text: 'User selected lesson 3 and opened quiz remediation.',
|
|
231
|
+
}],
|
|
232
|
+
structuredContent: {
|
|
233
|
+
selectedLessonId: 'lesson-3',
|
|
234
|
+
currentView: 'quiz-remediation',
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// OpenAI compatibility shim (aliases only):
|
|
239
|
+
window.openai.widgetState;
|
|
240
|
+
window.openai.setWidgetState({ tab: 'chart' });
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
For advanced cases, `@onereach/idw-apps/app` also exports:
|
|
244
|
+
|
|
245
|
+
- `App` / `IdwApp` — IDW's subclass of the standard MCP Apps `App`
|
|
246
|
+
- `PostMessageTransport`
|
|
247
|
+
- `installIdwAppApi` — state-only installer, only for custom bootstraps or legacy integrations
|
|
248
|
+
|
|
249
|
+
New apps should use `createIdwApp()` rather than calling `installIdwAppApi()` directly.
|
|
250
|
+
|
|
251
|
+
The iframe API is available only to apps that install this entry point, or to
|
|
252
|
+
HTML resources where the IDW API injects an equivalent bootstrap while proxying
|
|
253
|
+
the resource. A cross-origin app loaded by URL cannot be modified by the host
|
|
254
|
+
after it has loaded.
|
|
255
|
+
|
|
256
|
+
### App-provided tools
|
|
257
|
+
|
|
258
|
+
Apps can expose standard MCP tools that the host may make available to the
|
|
259
|
+
agent. This works for UI actions (`openRequest`) and app/backend actions
|
|
260
|
+
(`listTasks`) alike; the app owns the implementation.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const app = createIdwApp({
|
|
264
|
+
appInfo: {
|
|
265
|
+
name: 'Tasks',
|
|
266
|
+
version: '1.0.0',
|
|
267
|
+
},
|
|
268
|
+
capabilities: {
|
|
269
|
+
tools: {},
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
app.onlisttools = async () => ({
|
|
274
|
+
tools: [{
|
|
275
|
+
name: 'listTasks',
|
|
276
|
+
description: 'List tasks visible to the current user.',
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: 'object',
|
|
279
|
+
properties: {},
|
|
280
|
+
},
|
|
281
|
+
annotations: {
|
|
282
|
+
readOnlyHint: true,
|
|
283
|
+
},
|
|
284
|
+
}],
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
app.oncalltool = async ({ name, arguments: args }) => {
|
|
288
|
+
if (name !== 'listTasks') {
|
|
289
|
+
return { content: [], isError: true };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const tasks = await appBackend.listTasks(args);
|
|
293
|
+
return {
|
|
294
|
+
content: [{ type: 'text', text: `Found ${tasks.length} tasks.` }],
|
|
295
|
+
structuredContent: { tasks },
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Register `onlisttools` and `oncalltool` before `app.connect()`. Use accurate
|
|
301
|
+
descriptions and JSON Schema, return MCP `CallToolResult` values, and set tool
|
|
302
|
+
annotations such as `readOnlyHint` or `destructiveHint` honestly. The host—not
|
|
303
|
+
the app—decides whether a declared tool is agent-visible or needs confirmation.
|
|
304
|
+
|
|
305
|
+
### MCP-backed and Web Apps
|
|
306
|
+
|
|
307
|
+
An IDW Web App is an app that is not backed by an MCP server/resource. It still
|
|
308
|
+
uses the same MCP Apps transport and `createIdwApp()` initialization handshake
|
|
309
|
+
as an MCP-backed app; only its business-data source differs.
|
|
310
|
+
|
|
311
|
+
- A generic Web App normally omits `capabilities`. Add `capabilities.tools` only
|
|
312
|
+
when the app itself exposes MCP-style tools to the host/agent.
|
|
313
|
+
- Use `callServerTool()` and server resource APIs only when the host is proxying
|
|
314
|
+
an MCP server for this app. Otherwise use the app's own backend for business
|
|
315
|
+
APIs and `callIdwAction()` only for IDW host/platform actions.
|
|
316
|
+
- Use `app.validateAccess()` after `app.connect()` when the app needs to verify
|
|
317
|
+
the current user can access the Web App/skill. The host must provide `idwId`,
|
|
318
|
+
`auth.apiBaseUrl`, and lazy token access.
|
|
319
|
+
- The host bridge may omit `artifactId`, `toolCallId`, and MCP proxy callbacks
|
|
320
|
+
for a Web App.
|
|
321
|
+
|
|
322
|
+
### Business data and widget state
|
|
323
|
+
|
|
324
|
+
- Tool input/result uses standard MCP Apps notifications. A tool that renders or
|
|
325
|
+
updates an app should return the complete current business-data snapshot in
|
|
326
|
+
`structuredContent`. Treat that latest tool result as authoritative and
|
|
327
|
+
re-render from it.
|
|
328
|
+
- Widget state is only a lightweight UI/restore overlay: route, selected ID,
|
|
329
|
+
active tab, draft ID, current step, or an opaque backend `sessionId`.
|
|
330
|
+
- Tool results and widget-state messages are independent. Either can arrive
|
|
331
|
+
first, and widget state can be absent on an app's first render. Register both
|
|
332
|
+
handlers and call `app.connect()` immediately; do not wait for widget state
|
|
333
|
+
before connecting or rendering the initial tool result.
|
|
334
|
+
- When a new tool result arrives, replace business data with that snapshot and
|
|
335
|
+
reapply only still-valid UI state. Never merge widget state into business data.
|
|
336
|
+
- If a restore hint starts a backend fetch, do not let that older fetch overwrite
|
|
337
|
+
a newer tool-result snapshot.
|
|
338
|
+
|
|
339
|
+
### Widget-state semantics
|
|
340
|
+
|
|
341
|
+
- `stateOptions.initialState` is an iframe-local fallback before the host replies.
|
|
342
|
+
It is useful for custom integrations but is not the durable host snapshot; new
|
|
343
|
+
apps normally leave it unset.
|
|
344
|
+
- `setWidgetState(state)` **replaces** the whole widget-state object and updates local state immediately; the host persists asynchronously via `onStateChange` (the iframe never blocks on persistence).
|
|
345
|
+
- `whenWidgetStateReady()` is an optional signal for code that needs a restore hint
|
|
346
|
+
after `app.connect()`. It resolves on a host reply or the fallback timeout; it
|
|
347
|
+
is never a prerequisite for MCP initialization or first render.
|
|
348
|
+
- `getWidgetState()` returns the latest widget state known to the wrapper.
|
|
349
|
+
- `subscribeWidgetState(listener)` observes both local `setWidgetState` calls and host-pushed widget-state changes.
|
|
350
|
+
- `getState()`, `setState()`, `whenStateReady()`, and `subscribeState()` remain as deprecated aliases for existing apps.
|
|
351
|
+
- Widget state must be **JSON-serializable** and is **lightweight widget/view state only** — never store tool results or authoritative business data as widget state.
|
|
352
|
+
|
|
353
|
+
### IDW actions and agent context
|
|
354
|
+
|
|
355
|
+
- `callIdwAction({ name, arguments })` requests a host/platform action. It returns a standard MCP `CallToolResult`; errors use `isError: true`.
|
|
356
|
+
- Hosts may pass `allowedActions`. When set, every IDW action—including the
|
|
357
|
+
configured auth-token action—must be included or it is rejected.
|
|
358
|
+
- Action names are host-defined strings. This package does not hardcode `openSkill`, `startConversation`, `close`, or other product actions.
|
|
359
|
+
- `getAuthToken` is the built-in lazy auth action. It only returns a token when
|
|
360
|
+
the host passes `auth.allowTokenRequest: true` and, when an allowlist is set,
|
|
361
|
+
includes that action; otherwise it returns `isError: true`.
|
|
362
|
+
- Hosts can pass `auth.getOrToken(runtimeContext)` to retrieve or generate a
|
|
363
|
+
fresh token for every actual token-action request. The provider receives the
|
|
364
|
+
current live runtime context, takes precedence over `auth.orToken`, and is not
|
|
365
|
+
called during bridge creation or connection. The host bridge does not cache
|
|
366
|
+
provider results.
|
|
367
|
+
- `app.getAuthToken()` is the preferred app-side helper. It requests the OR user
|
|
368
|
+
identity token lazily and reuses the in-memory token for the current web app
|
|
369
|
+
session, so `validateAccess()` and later app API calls do not need duplicate
|
|
370
|
+
token requests. Use `forceRefresh: true` only when the host/app explicitly
|
|
371
|
+
needs to replace the session token.
|
|
372
|
+
- `validateAccess({ url })` is an app-side helper for IDW Web apps/skills. It
|
|
373
|
+
reads `hostContext.idw.idwId` and `hostContext.idw.auth.apiBaseUrl`, requests
|
|
374
|
+
the same cached `orToken` used by `app.getAuthToken()`, then calls
|
|
375
|
+
`POST /idw/:idwId/skills/access` from the app. Call it after `app.connect()`.
|
|
376
|
+
- `updateAgentContext(params)` is a semantic alias for MCP Apps `updateModelContext(params)`. It does not add a custom payload shape.
|
|
377
|
+
- The host callback receives both flattened text and the original MCP params.
|
|
378
|
+
Preserve the original params: a useful update can be structured-only and have
|
|
379
|
+
no text to flatten.
|
|
380
|
+
- Use `updateAgentContext()` only for app-reported context the IDW agent should understand in future turns. Use `sendLog()` for debug/telemetry.
|
|
381
|
+
- Auth currently supports only `orToken`; it is a user identity token issued for
|
|
382
|
+
one web app session. The app wrapper caches it in memory for the app instance.
|
|
383
|
+
The host may provide a static `auth.orToken` or generate one lazily with
|
|
384
|
+
`auth.getOrToken`; the package itself does not mint, refresh, or persist tokens.
|
|
385
|
+
|
|
386
|
+
### OpenAI compatibility
|
|
387
|
+
|
|
388
|
+
- `window.openai.widgetState` mirrors the latest IDW widget state.
|
|
389
|
+
- `window.openai.setWidgetState(state)` delegates to `window.idw.app.setWidgetState(state)`.
|
|
390
|
+
- Existing `window.openai` fields are preserved; the shim only adds/replaces `widgetState` and `setWidgetState`, then restores previous values on `dispose()`.
|
|
391
|
+
- These are compatibility shims only, not a full OpenAI Apps SDK, and should only be exposed inside MCP app iframes.
|
|
392
|
+
- Apps that depend only on ChatGPT-specific `window.openai` methods are not
|
|
393
|
+
automatically portable to IDW. Their core transport should use MCP Apps;
|
|
394
|
+
optional ChatGPT extensions must be feature-detected.
|
|
395
|
+
|
|
396
|
+
## Security
|
|
397
|
+
|
|
398
|
+
- Every inbound state message is validated against the target iframe's `contentWindow` (host) / the host window (app) before it is trusted.
|
|
399
|
+
- Every inbound action message uses the same iframe source validation and optional action allowlist before caller code runs.
|
|
400
|
+
- Standard MCP Apps traffic stays on the upstream `PostMessageTransport`; the
|
|
401
|
+
IDW channel ignores MCP JSON-RPC frames and sends host-initiated IDW state
|
|
402
|
+
messages only after the iframe has opted into the IDW channel.
|
|
403
|
+
- IDW/OpenAI APIs are only present in iframes that call `installIdwAppApi`.
|
|
404
|
+
- Tool calls and resource reads are routed through your callbacks, so they keep going through the backend's existing authorization checks.
|
|
405
|
+
- The IDW side channel validates the iframe window and binds a non-opaque app origin when the bridge is created. Sandboxed opaque-origin/srcdoc apps require wildcard targeting, so only enable token access for app resources and configuration the host already trusts.
|
|
406
|
+
- `orToken` is not exposed through host context by default. Prefer lazy token access via `callIdwAction({ name: 'getAuthToken' })` with `auth.allowTokenRequest: true`; also allowlist that action when `allowedActions` is set. `auth.getOrToken` and its resolved values are never serialized into host context. Use `auth.exposeInHostContext: true` only for trusted legacy apps that need eager injection of a static `auth.orToken`; it never invokes the provider.
|
|
407
|
+
- Do not leak `chatId`, auth tokens, or backend URLs into model context unless explicitly required.
|
|
408
|
+
|
|
409
|
+
## Non-goals
|
|
410
|
+
|
|
411
|
+
Does not replace `@modelcontextprotocol/ext-apps`, does not add custom IDW aliases for MCP tool calls/resource reads (those stay on the standard MCP bridge), does not mint auth tokens, does not migrate RWC/pageData integrations, and does not make the OpenAI Apps SDK the primary abstraction.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { App as McpApp, PostMessageTransport } from '@modelcontextprotocol/ext-apps';
|
|
2
|
+
import type { IdwActionParams, IdwActionResult } from '../types';
|
|
3
|
+
import { type IdwActionCallOptions, type InstalledIdwAppApi, type InstallIdwAppApiOptions } from './installIdwAppApi';
|
|
4
|
+
export type IdwAppInfo = ConstructorParameters<typeof McpApp>[0];
|
|
5
|
+
export type IdwAppCapabilities = ConstructorParameters<typeof McpApp>[1];
|
|
6
|
+
export type IdwAppOptions = ConstructorParameters<typeof McpApp>[2];
|
|
7
|
+
export type IdwAgentContextParams = Parameters<McpApp['updateModelContext']>[0];
|
|
8
|
+
export type IdwStateOptions = InstallIdwAppApiOptions;
|
|
9
|
+
export interface IdwAuthToken {
|
|
10
|
+
token: string;
|
|
11
|
+
tokenType: 'or-token';
|
|
12
|
+
apiBaseUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface GetAuthTokenOptions extends IdwActionCallOptions {
|
|
15
|
+
/** Lazy auth-token action name. Defaults to `getAuthToken`. */
|
|
16
|
+
tokenActionName?: string;
|
|
17
|
+
/** Request a new token instead of reusing the in-memory app-session token. */
|
|
18
|
+
forceRefresh?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ValidateAccessParams {
|
|
21
|
+
/** App URL checked by the IDW API. Defaults to `window.location.href`. */
|
|
22
|
+
url?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ValidateAccessOptions extends GetAuthTokenOptions {
|
|
25
|
+
/** Fetch implementation override for tests or custom runtimes. */
|
|
26
|
+
fetch?: typeof fetch;
|
|
27
|
+
}
|
|
28
|
+
export interface SkillData {
|
|
29
|
+
slug: string;
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
created_at: string;
|
|
34
|
+
meta: {
|
|
35
|
+
skill: {
|
|
36
|
+
advanced: boolean;
|
|
37
|
+
disabled: boolean;
|
|
38
|
+
editable: boolean;
|
|
39
|
+
docUrl?: string;
|
|
40
|
+
};
|
|
41
|
+
chat?: {
|
|
42
|
+
id: string;
|
|
43
|
+
url: string;
|
|
44
|
+
web?: boolean;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export interface ValidateAccessResult {
|
|
49
|
+
skill: SkillData;
|
|
50
|
+
user?: {
|
|
51
|
+
role: string;
|
|
52
|
+
userId?: string;
|
|
53
|
+
email?: string;
|
|
54
|
+
accountId?: string;
|
|
55
|
+
idwId?: string;
|
|
56
|
+
contactId?: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export interface CreateIdwAppOptions {
|
|
60
|
+
/** App identity sent through the standard MCP Apps initialize handshake. */
|
|
61
|
+
appInfo: IdwAppInfo;
|
|
62
|
+
/** Standard MCP Apps capabilities declared by the iframe app. */
|
|
63
|
+
capabilities?: IdwAppCapabilities;
|
|
64
|
+
/** Standard MCP Apps options, forwarded to `@modelcontextprotocol/ext-apps` App. */
|
|
65
|
+
appOptions?: IdwAppOptions;
|
|
66
|
+
/** IDW/OpenAI-compatible widget-state options. */
|
|
67
|
+
stateOptions?: IdwStateOptions;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* IDW's app-side MCP App class.
|
|
71
|
+
*
|
|
72
|
+
* This extends the standard `@modelcontextprotocol/ext-apps` `App` class, so it
|
|
73
|
+
* still performs the normal MCP Apps initialize handshake and exposes
|
|
74
|
+
* `getHostCapabilities()`, `getHostContext()`, `callServerTool()`, etc. IDW adds
|
|
75
|
+
* widget state, host/platform actions, and a semantic agent-context alias. The
|
|
76
|
+
* state API installs `window.idw.app` and the optional `window.openai`
|
|
77
|
+
* compatibility shim.
|
|
78
|
+
*/
|
|
79
|
+
export declare class IdwApp extends McpApp {
|
|
80
|
+
readonly state: InstalledIdwAppApi;
|
|
81
|
+
private authToken?;
|
|
82
|
+
private authTokenRequest?;
|
|
83
|
+
constructor(appInfo: IdwAppInfo, capabilities?: IdwAppCapabilities, appOptions?: IdwAppOptions, stateOptions?: IdwStateOptions);
|
|
84
|
+
/** Request an IDW host/platform action. Mirrors MCP `callServerTool()`. */
|
|
85
|
+
callIdwAction(params: IdwActionParams, options?: IdwActionCallOptions): Promise<IdwActionResult>;
|
|
86
|
+
/** Semantic IDW alias for MCP Apps `updateModelContext()`. */
|
|
87
|
+
updateAgentContext(params: IdwAgentContextParams, options?: Parameters<McpApp['updateModelContext']>[1]): ReturnType<McpApp['updateModelContext']>;
|
|
88
|
+
/** Return the OR user identity token for this app session, requesting it lazily once. */
|
|
89
|
+
getAuthToken(options?: GetAuthTokenOptions): Promise<IdwAuthToken>;
|
|
90
|
+
/**
|
|
91
|
+
* Validate that the current user can access this IDW Web app/skill.
|
|
92
|
+
*
|
|
93
|
+
* This intentionally keeps the old app-side `idw-skill` behavior: the app
|
|
94
|
+
* requests a lazy OR token, then calls the IDW API itself. Call after
|
|
95
|
+
* `connect()`, because it depends on MCP host context.
|
|
96
|
+
*/
|
|
97
|
+
validateAccess(params?: ValidateAccessParams, options?: ValidateAccessOptions): Promise<ValidateAccessResult>;
|
|
98
|
+
/** Remove IDW globals and close the underlying MCP App transport. */
|
|
99
|
+
dispose(): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
/** Create an IDW app-side MCP App with widget-state support installed. */
|
|
102
|
+
export declare function createIdwApp(options: CreateIdwAppOptions): IdwApp;
|
|
103
|
+
export { PostMessageTransport };
|
|
104
|
+
//# sourceMappingURL=createIdwApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createIdwApp.d.ts","sourceRoot":"","sources":["../../src/app/createIdwApp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { App as McpApp, PostMessageTransport, } from '@modelcontextprotocol/ext-apps';
|
|
2
|
+
import { installIdwAppApi, } from './installIdwAppApi';
|
|
3
|
+
const DEFAULT_AUTH_TOKEN_ACTION = 'getAuthToken';
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null;
|
|
6
|
+
}
|
|
7
|
+
function currentUrl(url) {
|
|
8
|
+
const resolved = url ?? globalThis.window?.location?.href;
|
|
9
|
+
if (!resolved) {
|
|
10
|
+
throw new Error('IdwApp.validateAccess: url is required outside a browser window');
|
|
11
|
+
}
|
|
12
|
+
return resolved;
|
|
13
|
+
}
|
|
14
|
+
function validateAccessUrl(apiBaseUrl, idwId) {
|
|
15
|
+
const base = apiBaseUrl.endsWith('/') ? apiBaseUrl : `${apiBaseUrl}/`;
|
|
16
|
+
return new URL(`idw/${encodeURIComponent(idwId)}/skills/access`, base).toString();
|
|
17
|
+
}
|
|
18
|
+
function extractAuthToken(result, apiBaseUrl) {
|
|
19
|
+
const structuredContent = result.structuredContent;
|
|
20
|
+
if (!isRecord(structuredContent) || typeof structuredContent.token !== 'string') {
|
|
21
|
+
throw new Error('IdwApp.getAuthToken: getAuthToken did not return a token');
|
|
22
|
+
}
|
|
23
|
+
if (structuredContent.tokenType !== undefined
|
|
24
|
+
&& structuredContent.tokenType !== 'or-token') {
|
|
25
|
+
throw new Error('IdwApp.getAuthToken: getAuthToken returned an unsupported token type');
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
token: structuredContent.token,
|
|
29
|
+
tokenType: 'or-token',
|
|
30
|
+
apiBaseUrl: typeof structuredContent.apiBaseUrl === 'string'
|
|
31
|
+
? structuredContent.apiBaseUrl
|
|
32
|
+
: apiBaseUrl,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function extractToolError(result) {
|
|
36
|
+
const content = result.content;
|
|
37
|
+
if (Array.isArray(content)) {
|
|
38
|
+
const text = content
|
|
39
|
+
.map((block) => (isRecord(block) && typeof block.text === 'string' ? block.text : ''))
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.join('\n');
|
|
42
|
+
if (text) {
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return 'Auth token access failed';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* IDW's app-side MCP App class.
|
|
50
|
+
*
|
|
51
|
+
* This extends the standard `@modelcontextprotocol/ext-apps` `App` class, so it
|
|
52
|
+
* still performs the normal MCP Apps initialize handshake and exposes
|
|
53
|
+
* `getHostCapabilities()`, `getHostContext()`, `callServerTool()`, etc. IDW adds
|
|
54
|
+
* widget state, host/platform actions, and a semantic agent-context alias. The
|
|
55
|
+
* state API installs `window.idw.app` and the optional `window.openai`
|
|
56
|
+
* compatibility shim.
|
|
57
|
+
*/
|
|
58
|
+
export class IdwApp extends McpApp {
|
|
59
|
+
constructor(appInfo, capabilities, appOptions, stateOptions) {
|
|
60
|
+
super(appInfo, capabilities, appOptions);
|
|
61
|
+
this.state = installIdwAppApi(stateOptions);
|
|
62
|
+
this.onteardown = async () => ({});
|
|
63
|
+
}
|
|
64
|
+
/** Request an IDW host/platform action. Mirrors MCP `callServerTool()`. */
|
|
65
|
+
callIdwAction(params, options) {
|
|
66
|
+
return this.state.callIdwAction(params, options);
|
|
67
|
+
}
|
|
68
|
+
/** Semantic IDW alias for MCP Apps `updateModelContext()`. */
|
|
69
|
+
updateAgentContext(params, options) {
|
|
70
|
+
return this.updateModelContext(params, options);
|
|
71
|
+
}
|
|
72
|
+
/** Return the OR user identity token for this app session, requesting it lazily once. */
|
|
73
|
+
getAuthToken(options = {}) {
|
|
74
|
+
if (!options.forceRefresh && this.authToken) {
|
|
75
|
+
return Promise.resolve(this.authToken);
|
|
76
|
+
}
|
|
77
|
+
if (!options.forceRefresh && this.authTokenRequest) {
|
|
78
|
+
return this.authTokenRequest;
|
|
79
|
+
}
|
|
80
|
+
const hostContext = this.getHostContext();
|
|
81
|
+
const apiBaseUrl = hostContext?.idw?.auth?.apiBaseUrl;
|
|
82
|
+
const request = this.callIdwAction({
|
|
83
|
+
name: options.tokenActionName ?? DEFAULT_AUTH_TOKEN_ACTION,
|
|
84
|
+
}, {
|
|
85
|
+
timeout: options.timeout,
|
|
86
|
+
}).then((tokenResult) => {
|
|
87
|
+
if (tokenResult.isError) {
|
|
88
|
+
throw new Error(`IdwApp.getAuthToken: ${extractToolError(tokenResult)}`);
|
|
89
|
+
}
|
|
90
|
+
const token = extractAuthToken(tokenResult, apiBaseUrl);
|
|
91
|
+
this.authToken = token;
|
|
92
|
+
return token;
|
|
93
|
+
}).finally(() => {
|
|
94
|
+
if (this.authTokenRequest === request) {
|
|
95
|
+
this.authTokenRequest = undefined;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
this.authTokenRequest = request;
|
|
99
|
+
return request;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate that the current user can access this IDW Web app/skill.
|
|
103
|
+
*
|
|
104
|
+
* This intentionally keeps the old app-side `idw-skill` behavior: the app
|
|
105
|
+
* requests a lazy OR token, then calls the IDW API itself. Call after
|
|
106
|
+
* `connect()`, because it depends on MCP host context.
|
|
107
|
+
*/
|
|
108
|
+
async validateAccess(params = {}, options = {}) {
|
|
109
|
+
const hostContext = this.getHostContext();
|
|
110
|
+
const idw = hostContext?.idw;
|
|
111
|
+
const apiBaseUrl = idw?.auth?.apiBaseUrl;
|
|
112
|
+
const idwId = idw?.idwId;
|
|
113
|
+
if (!apiBaseUrl) {
|
|
114
|
+
throw new Error('IdwApp.validateAccess: host context is missing idw.auth.apiBaseUrl');
|
|
115
|
+
}
|
|
116
|
+
if (!idwId) {
|
|
117
|
+
throw new Error('IdwApp.validateAccess: host context is missing idw.idwId');
|
|
118
|
+
}
|
|
119
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
120
|
+
if (typeof fetchFn !== 'function') {
|
|
121
|
+
throw new Error('IdwApp.validateAccess: fetch is unavailable');
|
|
122
|
+
}
|
|
123
|
+
const auth = await this.getAuthToken(options);
|
|
124
|
+
const endpoint = validateAccessUrl(apiBaseUrl, idwId);
|
|
125
|
+
const response = await fetchFn(endpoint, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: {
|
|
128
|
+
'Content-Type': 'application/json;charset=UTF-8',
|
|
129
|
+
ortoken: auth.token,
|
|
130
|
+
},
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
url: currentUrl(params.url),
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new Error(`IdwApp.validateAccess: request failed [POST ${endpoint}] - ${response.status} ${response.statusText}`);
|
|
137
|
+
}
|
|
138
|
+
return response.json();
|
|
139
|
+
}
|
|
140
|
+
/** Remove IDW globals and close the underlying MCP App transport. */
|
|
141
|
+
async dispose() {
|
|
142
|
+
this.state.dispose();
|
|
143
|
+
await this.close();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Create an IDW app-side MCP App with widget-state support installed. */
|
|
147
|
+
export function createIdwApp(options) {
|
|
148
|
+
return new IdwApp(options.appInfo, options.capabilities, options.appOptions, options.stateOptions);
|
|
149
|
+
}
|
|
150
|
+
export { PostMessageTransport };
|
|
151
|
+
//# sourceMappingURL=createIdwApp.js.map
|