@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
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: idw-app-development
|
|
3
|
+
description: Use when building IDW-compatible MCP Apps, generic IDW Web apps, or adapting apps to @onereach/idw-apps, including widget state, lazy auth, IDW actions, agent context, RWC boundaries, and OpenAI/standard MCP compatibility.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# IDW App Development
|
|
7
|
+
|
|
8
|
+
Use this skill when creating an app that will run inside IDW through `@onereach/idw-apps`. This covers MCP Apps and generic IDW Web apps. Keep the app protocol close to MCP Apps; do not invent a custom bridge unless the package already exposes it.
|
|
9
|
+
|
|
10
|
+
For full API details, read the package `README.md`. This skill captures the implementation decisions and app-building defaults.
|
|
11
|
+
|
|
12
|
+
## One Skill Or Many
|
|
13
|
+
|
|
14
|
+
Use this single skill for now. MCP Apps and IDW Web apps share the same app primitives:
|
|
15
|
+
|
|
16
|
+
- durable widget state
|
|
17
|
+
- optional lazy `orToken`
|
|
18
|
+
- optional host actions
|
|
19
|
+
- optional agent/model context updates
|
|
20
|
+
- app/backend-owned authoritative data
|
|
21
|
+
|
|
22
|
+
“IDW Web app” means the app is not backed by an MCP server or tool resource. It
|
|
23
|
+
still uses `createIdwApp()` and the MCP Apps handshake as the universal IDW app
|
|
24
|
+
protocol; only its business-data backend differs.
|
|
25
|
+
|
|
26
|
+
Split into separate skills only if one app type grows a truly different build workflow.
|
|
27
|
+
|
|
28
|
+
## Choose The App Type
|
|
29
|
+
|
|
30
|
+
| App type | Use when | Main differences |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| MCP App | An MCP server exposes an interactive UI resource or URL. | Prefer standard MCP Apps APIs for tools/resources/prompts. Use IDW additions only when the app needs IDW widget state, IDW actions, lazy auth, or agent context. |
|
|
33
|
+
| Standard external MCP App | The user brings an external MCP server/app built directly with `@modelcontextprotocol/ext-apps`. | It must work in IDW without importing `@onereach/idw-apps/app`. Do not require IDW-only messages for basic MCP behavior. |
|
|
34
|
+
| IDW Web app | A hosted IDW user app embedded or opened by IDW, often with its own backend. | Same protocol regardless of where IDW renders it. Request `orToken` lazily when the app needs IDW/API auth. Store only restore hints in widget state. Use `surface` only as a layout/context hint. |
|
|
35
|
+
| RWC app | Existing Rich Web Chat app/skill. | Treat as separate unless the user explicitly asks to migrate it. RWC continues using `pageData`/`orToken` because its backend expects data through its UI. |
|
|
36
|
+
|
|
37
|
+
## Preferred App Import
|
|
38
|
+
|
|
39
|
+
For new IDW-aware apps, import from `@onereach/idw-apps/app`:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createIdwApp } from '@onereach/idw-apps/app';
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
IDW app authors should not need to install `@modelcontextprotocol/ext-apps` separately. The IDW `App` extends the MCP Apps `App`, so standard MCP methods remain available.
|
|
46
|
+
|
|
47
|
+
External MCP apps built directly with `@modelcontextprotocol/ext-apps` should still work with the IDW host, but they will not have the `window.idw.app` helpers unless they include this package or an equivalent bootstrap.
|
|
48
|
+
|
|
49
|
+
Treat current OpenAI Apps SDK apps as standard MCP Apps when they use the MCP
|
|
50
|
+
Apps bridge. Do not assume IDW implements the complete ChatGPT-only
|
|
51
|
+
`window.openai` runtime. IDW currently aliases widget state only; feature-detect
|
|
52
|
+
any optional `window.openai` methods and keep core behavior on MCP Apps.
|
|
53
|
+
|
|
54
|
+
New IDW apps should not call `installIdwAppApi()` directly. It is reserved for
|
|
55
|
+
custom bootstraps and legacy integrations.
|
|
56
|
+
|
|
57
|
+
## Choose The API By Direction
|
|
58
|
+
|
|
59
|
+
| Intent | API |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| App calls its MCP server | `app.callServerTool()` |
|
|
62
|
+
| IDW host/agent calls a live app tool | app `onlisttools` / `oncalltool`; host `listAppTools()` / `callAppTool()` |
|
|
63
|
+
| App requests an IDW host/platform action | `app.callIdwAction()` |
|
|
64
|
+
| App gives concise context to the agent | `app.updateAgentContext()` |
|
|
65
|
+
| App saves a lightweight restore hint | `app.state.setWidgetState()` |
|
|
66
|
+
|
|
67
|
+
Do not invent another action channel for app-provided tools.
|
|
68
|
+
|
|
69
|
+
## Basic IDW App Skeleton
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { createIdwApp } from '@onereach/idw-apps/app';
|
|
73
|
+
|
|
74
|
+
const app = createIdwApp({
|
|
75
|
+
appInfo: {
|
|
76
|
+
name: 'My IDW App',
|
|
77
|
+
version: '1.0.0',
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
app.ontoolinput = ({ arguments: args }) => {
|
|
82
|
+
// Standard MCP Apps tool input notification from the host/model turn.
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
app.ontoolresult = (result) => {
|
|
86
|
+
// The latest tool result (especially structuredContent) is authoritative
|
|
87
|
+
// business data. Store it and render from it.
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
app.state.subscribeWidgetState((state) => {
|
|
91
|
+
// Apply only route, selected ID, active tab, or another restore/view hint.
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await app.connect();
|
|
95
|
+
|
|
96
|
+
const widgetState = app.state.getWidgetState();
|
|
97
|
+
const hostContext = app.getHostContext();
|
|
98
|
+
const hostCapabilities = app.getHostCapabilities();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`capabilities` is optional. Add `capabilities.tools` only when the app itself
|
|
102
|
+
exposes MCP-style tools to the host.
|
|
103
|
+
|
|
104
|
+
Use `app.dispose()` when the app is being torn down by your framework.
|
|
105
|
+
|
|
106
|
+
## Widget State
|
|
107
|
+
|
|
108
|
+
Widget state is durable UI restoration state owned by the host. It is not the app's authoritative data store.
|
|
109
|
+
|
|
110
|
+
The host stores widget state for the durable app instance, not for a single tool call. On the next app render or reload, it can arrive before or after the MCP tool result, and there may be no saved widget state at all. Connect immediately and render authoritative tool results without waiting for state.
|
|
111
|
+
|
|
112
|
+
Use these names in new code:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
app.state.getWidgetState();
|
|
116
|
+
app.state.setWidgetState({ route: '/lessons/lesson-3', selectedLessonId: 'lesson-3' });
|
|
117
|
+
app.state.subscribeWidgetState((state) => {
|
|
118
|
+
// sync local UI
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Use `whenWidgetStateReady()` only after `app.connect()` when a restore hint is specifically needed. It resolves on a host reply or the fallback timeout; it is not a startup prerequisite.
|
|
123
|
+
|
|
124
|
+
Deprecated aliases may exist for compatibility: `getState`, `setState`, `whenStateReady`, `subscribeState`. Do not use them in new apps.
|
|
125
|
+
|
|
126
|
+
`setWidgetState(state)` replaces the whole widget-state object. Keep it JSON-serializable, small, and non-sensitive.
|
|
127
|
+
|
|
128
|
+
Good widget state:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
{
|
|
132
|
+
route: '/lessons/lesson-3/remediation',
|
|
133
|
+
selectedLessonId: 'lesson-3',
|
|
134
|
+
activeTab: 'quiz',
|
|
135
|
+
draftId: 'draft-abc',
|
|
136
|
+
currentStep: 2
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Bad widget state:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
{
|
|
144
|
+
orToken: '...',
|
|
145
|
+
fullApiResponse: {},
|
|
146
|
+
entireUserProfile: {},
|
|
147
|
+
largeDataset: [],
|
|
148
|
+
lastToolResult: {}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For apps with their own backend, widget state should contain restore hints such as IDs/routes/tabs. On reload, use those hints to fetch authoritative data from the app backend.
|
|
153
|
+
|
|
154
|
+
For small apps without a backend, such as a simple game, it is acceptable to store full lightweight local state if it is small, non-sensitive, and JSON-serializable.
|
|
155
|
+
|
|
156
|
+
Do not block UI on persistence. The host updates local state immediately and persists asynchronously.
|
|
157
|
+
|
|
158
|
+
Avoid writing every tiny transient interaction. Persist restore-critical changes such as route, selected IDs, active tab, draft ID, current workflow step, or compact local state for small backend-less apps.
|
|
159
|
+
|
|
160
|
+
## Rendering And State Ownership
|
|
161
|
+
|
|
162
|
+
Keep these layers separate:
|
|
163
|
+
|
|
164
|
+
- **Tool result** — authoritative business state. A tool that renders or updates the app should return the complete current business snapshot in `structuredContent`; every new result replaces the previous snapshot.
|
|
165
|
+
- **Widget state** — lightweight UI/restore hint. Reapply it on top of the latest business snapshot only when it is still valid.
|
|
166
|
+
- **Local component state** — ephemeral rendering details.
|
|
167
|
+
|
|
168
|
+
Tool-result and widget-state messages have no guaranteed relative order. A new app must work when the first render has only tool input/result and no widget state. On a reload, it may render a persisted tool result first and receive `{ sessionId, route }` later.
|
|
169
|
+
|
|
170
|
+
If a restore hint causes a backend fetch, keep that request from overwriting a newer tool-result snapshot. Use your framework's normal cancellation or request-generation pattern when both can race.
|
|
171
|
+
|
|
172
|
+
## Backend Data
|
|
173
|
+
|
|
174
|
+
Use this split:
|
|
175
|
+
|
|
176
|
+
- IDW widget state: lightweight restore checkpoint.
|
|
177
|
+
- App backend/session: authoritative business or game data.
|
|
178
|
+
- Agent context: selected model-visible activity/context only.
|
|
179
|
+
- IDW auth token: requested lazily as a user identity token and never persisted in widget state.
|
|
180
|
+
|
|
181
|
+
When restoring:
|
|
182
|
+
|
|
183
|
+
1. Register tool-result and widget-state handlers.
|
|
184
|
+
2. Call `app.connect()` immediately.
|
|
185
|
+
3. Render every authoritative tool result as it arrives.
|
|
186
|
+
4. Apply widget state as a route/selection/session hint whenever it arrives.
|
|
187
|
+
5. If the hint needs backend hydration, request auth only if needed and fetch without replacing a newer tool result.
|
|
188
|
+
|
|
189
|
+
## Lazy Auth
|
|
190
|
+
|
|
191
|
+
The only current token type is `orToken`. It is a user identity token issued for
|
|
192
|
+
one web app session. It lets the embedded app call IDW/platform APIs as the
|
|
193
|
+
current user, subject to the platform's normal authorization. Request it only
|
|
194
|
+
when needed:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
const auth = await app.getAuthToken();
|
|
198
|
+
|
|
199
|
+
// Use auth.token for app/API calls that require IDW identity.
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The host must opt in with `auth.allowTokenRequest: true`. If it uses
|
|
203
|
+
`allowedActions`, it must also include `getAuthToken` (or its configured token
|
|
204
|
+
action name). Otherwise `getAuthToken` returns a normal MCP-style error result.
|
|
205
|
+
|
|
206
|
+
The host may provide a static `auth.orToken` or an async
|
|
207
|
+
`auth.getOrToken(runtimeContext)` provider. The provider is called lazily for
|
|
208
|
+
each actual host token-action request and receives the current runtime context;
|
|
209
|
+
it is never exposed through MCP host context. App-side `getAuthToken()` still
|
|
210
|
+
caches normal requests, while `forceRefresh: true` sends another host request.
|
|
211
|
+
|
|
212
|
+
`app.getAuthToken()` caches the in-memory token for the current app instance, so
|
|
213
|
+
calling `app.validateAccess()` and then making app API requests, or doing those
|
|
214
|
+
in the opposite order, does not require duplicate host token requests. Use
|
|
215
|
+
`forceRefresh: true` only when the product explicitly needs a new session token.
|
|
216
|
+
|
|
217
|
+
The package does not verify `orToken` and should treat it as opaque. Verification belongs to IDW/platform APIs or the app backend using the platform's normal server-side authorization flow.
|
|
218
|
+
|
|
219
|
+
Do not put tokens into widget state, model/agent context, logs, URLs, or localStorage unless the product explicitly requires and reviews it.
|
|
220
|
+
|
|
221
|
+
## Web App Access Validation
|
|
222
|
+
|
|
223
|
+
For Web Apps/skills replacing `idw-skill`, validate access from the app after
|
|
224
|
+
the MCP Apps handshake:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
await app.connect();
|
|
228
|
+
|
|
229
|
+
const { skill, user } = await app.validateAccess({
|
|
230
|
+
url: window.location.href,
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`validateAccess()` keeps the old app-side request model: it reads
|
|
235
|
+
`hostContext.idw.idwId` and `hostContext.idw.auth.apiBaseUrl`, requests a lazy
|
|
236
|
+
`orToken` through the cached `app.getAuthToken()` helper, then calls:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
POST {apiBaseUrl}/idw/{idwId}/skills/access
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The host must provide `idwId`, `auth.apiBaseUrl`, `auth.allowTokenRequest: true`,
|
|
243
|
+
and, when `allowedActions` is set, the configured token action name. Do not use
|
|
244
|
+
host-mediated `callIdwAction()` access checks when the product requires the app
|
|
245
|
+
to perform this request itself.
|
|
246
|
+
|
|
247
|
+
## MCP Server Tools And Resources
|
|
248
|
+
|
|
249
|
+
Use standard MCP Apps methods for MCP server capabilities:
|
|
250
|
+
|
|
251
|
+
- `callServerTool()` for MCP server tools.
|
|
252
|
+
- resource/prompt list and read APIs for MCP resources/prompts.
|
|
253
|
+
- `updateModelContext()` for standard MCP model context updates.
|
|
254
|
+
|
|
255
|
+
Use IDW methods only for IDW-specific behavior:
|
|
256
|
+
|
|
257
|
+
- `callIdwAction()` for host/platform actions.
|
|
258
|
+
- `updateAgentContext()` as a semantic IDW alias for `updateModelContext()`.
|
|
259
|
+
- widget-state helpers for durable UI restoration.
|
|
260
|
+
|
|
261
|
+
If an MCP app needs external scripts, styles, or assets, define UI CSP metadata on the app resource, not on the tool. Include the needed asset origins in the resource UI CSP. Do not assume arbitrary external asset origins will be allowed.
|
|
262
|
+
|
|
263
|
+
For local development, localhost app/resource URLs can be valid if the host environment is configured to allow them.
|
|
264
|
+
|
|
265
|
+
## App-Provided Agent Tools
|
|
266
|
+
|
|
267
|
+
An app can expose standard MCP tools for IDW to make available to the agent.
|
|
268
|
+
Use this for either UI actions (`openRequest`, `selectTab`) or app/API actions
|
|
269
|
+
(`listTasks`, `createTask`); the app owns the tool implementation.
|
|
270
|
+
|
|
271
|
+
This is the reverse of `callServerTool()`:
|
|
272
|
+
|
|
273
|
+
- `callServerTool()` is app → MCP server.
|
|
274
|
+
- `onlisttools` / `oncalltool` is IDW host/agent → app.
|
|
275
|
+
|
|
276
|
+
Declare tool capability and register handlers before connecting:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
const app = createIdwApp({
|
|
280
|
+
appInfo: {
|
|
281
|
+
name: 'Tasks',
|
|
282
|
+
version: '1.0.0',
|
|
283
|
+
},
|
|
284
|
+
capabilities: {
|
|
285
|
+
tools: {},
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
app.onlisttools = async () => ({
|
|
290
|
+
tools: [{
|
|
291
|
+
name: 'listTasks',
|
|
292
|
+
description: 'List tasks visible to the current user.',
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: {},
|
|
296
|
+
},
|
|
297
|
+
annotations: {
|
|
298
|
+
readOnlyHint: true,
|
|
299
|
+
},
|
|
300
|
+
}],
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
app.oncalltool = async ({ name, arguments: args }) => {
|
|
304
|
+
if (name !== 'listTasks') {
|
|
305
|
+
return { content: [], isError: true };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const tasks = await appBackend.listTasks(args);
|
|
309
|
+
return {
|
|
310
|
+
content: [{ type: 'text', text: `Found ${tasks.length} tasks.` }],
|
|
311
|
+
structuredContent: { tasks },
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
await app.connect();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Tool descriptions, input schemas, output, and annotations are part of the
|
|
319
|
+
agent-facing contract. Keep them concise and accurate. Mark read-only tools
|
|
320
|
+
with `readOnlyHint`; provide honest destructive/idempotency annotations for
|
|
321
|
+
side effects. The IDW host decides whether a declared tool is agent-visible or
|
|
322
|
+
requires confirmation.
|
|
323
|
+
|
|
324
|
+
App-provided tools run in the live iframe. For a backend capability that must
|
|
325
|
+
work when the app is not mounted, expose it as a durable MCP or backend tool
|
|
326
|
+
instead. Do not put raw backend URLs, credentials, or authorization policy in
|
|
327
|
+
the tool manifest; use the app's normal backend/auth flow inside the handler.
|
|
328
|
+
|
|
329
|
+
## IDW Host Actions
|
|
330
|
+
|
|
331
|
+
Use `callIdwAction()` for host-defined platform actions:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
const result = await app.callIdwAction({
|
|
335
|
+
name: 'openSkill',
|
|
336
|
+
arguments: {
|
|
337
|
+
slug: 'generate-image',
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if (result.isError) {
|
|
342
|
+
// Show a local app error state if useful.
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Action names are host-defined strings. Do not assume built-in actions other than `getAuthToken`.
|
|
347
|
+
|
|
348
|
+
The host may pass `allowedActions`. If an action is not allowed, including
|
|
349
|
+
`getAuthToken`, the app receives `CallToolResult.isError = true`.
|
|
350
|
+
|
|
351
|
+
For MCP server tools, do not use `callIdwAction`; use `callServerTool`.
|
|
352
|
+
|
|
353
|
+
## Agent Context
|
|
354
|
+
|
|
355
|
+
Use `updateAgentContext()` when the app knows something useful for the IDW agent's future reasoning:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
await app.updateAgentContext({
|
|
359
|
+
content: [{
|
|
360
|
+
type: 'text',
|
|
361
|
+
text: 'User selected lesson 3 and opened quiz remediation.',
|
|
362
|
+
}],
|
|
363
|
+
structuredContent: {
|
|
364
|
+
selectedLessonId: 'lesson-3',
|
|
365
|
+
currentView: 'quiz-remediation',
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Rules:
|
|
371
|
+
|
|
372
|
+
- It accepts the same payload shape as MCP Apps `updateModelContext()`.
|
|
373
|
+
- Keep it concise and relevant to the agent.
|
|
374
|
+
- Do not use it for monitoring telemetry or debug logs.
|
|
375
|
+
- Do not include secrets, auth tokens, private backend payloads, or large data.
|
|
376
|
+
- Use `sendLog()` / logging APIs for debug or diagnostic events.
|
|
377
|
+
|
|
378
|
+
## Host Context
|
|
379
|
+
|
|
380
|
+
The host may include a compact IDW block in MCP host context:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
const hostContext = app.getHostContext();
|
|
384
|
+
const idw = hostContext.idw as {
|
|
385
|
+
idwId?: string;
|
|
386
|
+
surface?: string;
|
|
387
|
+
appInstanceId?: string;
|
|
388
|
+
auth?: {
|
|
389
|
+
apiBaseUrl?: string;
|
|
390
|
+
orToken?: string;
|
|
391
|
+
};
|
|
392
|
+
allowedActions?: string[];
|
|
393
|
+
} | undefined;
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
By default, `auth.orToken` is omitted. Prefer lazy `getAuthToken`.
|
|
397
|
+
|
|
398
|
+
Use `surface` to adjust layout, not to fork the app protocol.
|
|
399
|
+
|
|
400
|
+
## OpenAI Compatibility
|
|
401
|
+
|
|
402
|
+
`@onereach/idw-apps/app` provides a minimal OpenAI widget-state compatibility shim:
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
window.openai?.widgetState;
|
|
406
|
+
window.openai?.setWidgetState?.({ route: '/details' });
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
This mirrors/delegates to IDW widget state. It is not a full OpenAI Apps SDK implementation.
|
|
410
|
+
|
|
411
|
+
Apps built for OpenAI Apps SDK may need adaptation unless they only depend on compatible widget-state behavior and standard MCP Apps behavior. Prefer the explicit IDW APIs for new IDW apps.
|
|
412
|
+
|
|
413
|
+
## IDW Web Apps (Not MCP-Server-Backed)
|
|
414
|
+
|
|
415
|
+
For IDW Web apps:
|
|
416
|
+
|
|
417
|
+
- Use the same `createIdwApp()` skeleton above; Web Apps do not use a separate app class.
|
|
418
|
+
- They still use the standard MCP Apps transport/handshake as the universal IDW
|
|
419
|
+
app protocol; “non-MCP” refers only to the absence of an MCP server backend.
|
|
420
|
+
- Omit `capabilities` unless the Web App exposes app-side MCP tools.
|
|
421
|
+
- Use the same widget-state and lazy-auth rules.
|
|
422
|
+
- Use `app.validateAccess()` after `app.connect()` when replacing old
|
|
423
|
+
`idw-skill` Web Skill access checks.
|
|
424
|
+
- Treat `surface` as a layout hint.
|
|
425
|
+
- Resolve business/session data from the app backend.
|
|
426
|
+
- Request `app.getAuthToken()` only when the app needs IDW/API identity.
|
|
427
|
+
- Use `callIdwAction` for host actions, not for app-backend business operations.
|
|
428
|
+
- Use `updateAgentContext` only for concise model-visible app activity.
|
|
429
|
+
|
|
430
|
+
If the app is cross-origin and does not bundle `@onereach/idw-apps/app` or receive an equivalent bootstrap, the host cannot reliably add `window.idw.app` after the page has loaded.
|
|
431
|
+
|
|
432
|
+
## Security And Privacy
|
|
433
|
+
|
|
434
|
+
- Feature-detect optional APIs before using them when building portable apps.
|
|
435
|
+
- Never persist or expose tokens in widget state or agent context.
|
|
436
|
+
- Do not leak `chatId`, backend URLs, token values, or private API payloads into model-visible context unless explicitly required.
|
|
437
|
+
- Avoid storing PII in widget state; store IDs and reload data from the backend.
|
|
438
|
+
- Validate and sanitize user-controlled values before rendering.
|
|
439
|
+
- Treat host action failures as normal app errors, not crashes.
|
|
440
|
+
- Keep app state JSON-serializable so it can cross `postMessage` and persist cleanly.
|
|
441
|
+
|
|
442
|
+
## Build Checklist
|
|
443
|
+
|
|
444
|
+
Before finishing an app:
|
|
445
|
+
|
|
446
|
+
- The app uses `createIdwApp()` and connects immediately after registering handlers.
|
|
447
|
+
- The app renders from tool results even when no widget state exists.
|
|
448
|
+
- Widget state is applied only as a view/restore overlay and never replaces business data.
|
|
449
|
+
- Stateful reload works from `getWidgetState()` or `subscribeWidgetState()` without blocking the initial MCP render.
|
|
450
|
+
- `setWidgetState()` is called only for lightweight restore-critical state.
|
|
451
|
+
- Backend-backed data reloads from the app/backend using IDs from widget state.
|
|
452
|
+
- Auth is requested lazily with `getAuthToken` only when needed.
|
|
453
|
+
- MCP server operations use `callServerTool`, not `callIdwAction`.
|
|
454
|
+
- Agent-callable app tools use `capabilities.tools`, `onlisttools`, and `oncalltool`.
|
|
455
|
+
- IDW host/platform operations use `callIdwAction`.
|
|
456
|
+
- Agent context is concise, non-secret, and useful to the model.
|
|
457
|
+
- The app behaves reasonably on its target surface.
|
|
458
|
+
- Standard external MCP compatibility is not broken by IDW-specific assumptions.
|