@agent-native/core 0.22.19 → 0.22.20

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.
Files changed (47) hide show
  1. package/dist/client/embed-auth.d.ts.map +1 -1
  2. package/dist/client/embed-auth.js +85 -3
  3. package/dist/client/embed-auth.js.map +1 -1
  4. package/dist/client/mcp-apps/McpAppRenderer.d.ts +3 -0
  5. package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  6. package/dist/client/mcp-apps/McpAppRenderer.js +86 -9
  7. package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
  8. package/dist/deploy/build.d.ts.map +1 -1
  9. package/dist/deploy/build.js +73 -5
  10. package/dist/deploy/build.js.map +1 -1
  11. package/dist/mcp/build-server.d.ts.map +1 -1
  12. package/dist/mcp/build-server.js +40 -3
  13. package/dist/mcp/build-server.js.map +1 -1
  14. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  15. package/dist/mcp/builtin-tools.js +6 -3
  16. package/dist/mcp/builtin-tools.js.map +1 -1
  17. package/dist/mcp/embed-app.d.ts +2 -2
  18. package/dist/mcp/embed-app.d.ts.map +1 -1
  19. package/dist/mcp/embed-app.js +390 -17
  20. package/dist/mcp/embed-app.js.map +1 -1
  21. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  22. package/dist/server/core-routes-plugin.js +37 -10
  23. package/dist/server/core-routes-plugin.js.map +1 -1
  24. package/dist/server/create-server.d.ts.map +1 -1
  25. package/dist/server/create-server.js +21 -7
  26. package/dist/server/create-server.js.map +1 -1
  27. package/dist/server/embed-route.d.ts.map +1 -1
  28. package/dist/server/embed-route.js +62 -21
  29. package/dist/server/embed-route.js.map +1 -1
  30. package/dist/server/security-headers.d.ts.map +1 -1
  31. package/dist/server/security-headers.js +9 -1
  32. package/dist/server/security-headers.js.map +1 -1
  33. package/dist/server/ssr-handler.d.ts +2 -0
  34. package/dist/server/ssr-handler.d.ts.map +1 -1
  35. package/dist/server/ssr-handler.js +66 -11
  36. package/dist/server/ssr-handler.js.map +1 -1
  37. package/dist/shared/mcp-embed-headers.d.ts +12 -0
  38. package/dist/shared/mcp-embed-headers.d.ts.map +1 -0
  39. package/dist/shared/mcp-embed-headers.js +51 -0
  40. package/dist/shared/mcp-embed-headers.js.map +1 -0
  41. package/dist/vite/client.d.ts.map +1 -1
  42. package/dist/vite/client.js +23 -0
  43. package/dist/vite/client.js.map +1 -1
  44. package/docs/content/actions.md +15 -5
  45. package/docs/content/external-agents.md +53 -27
  46. package/docs/content/mcp-protocol.md +29 -4
  47. package/package.json +1 -1
@@ -199,13 +199,23 @@ export default defineAction({
199
199
  This advertises the MCP Apps extension (`io.modelcontextprotocol/ui`), exposes the HTML via MCP resources/templates, and includes standard MCP Apps plus ChatGPT Apps SDK widget metadata for compatible hosts. Keep `link` as the fallback for CLI and non-UI MCP clients; see [External Agents](/docs/external-agents#mcp-apps).
200
200
 
201
201
  The helper launches the action's `link` target through `/_agent-native/embed/start` with a short-lived browser session, so routes such as full dashboards, filtered inboxes, drafts, and extension pages can reuse the app's React components directly.
202
+ ChatGPT uses a controlled route iframe to avoid web-sandbox auto-height
203
+ feedback loops. Claude web uses a single-frame mount path automatically: the
204
+ launcher fetches the signed route HTML inside Claude's MCP resource frame,
205
+ mounts the real app document there, and rewrites app-origin network calls back
206
+ to the original app with the embed token.
202
207
 
203
208
  Embedded routes can use the exported client helpers for the MCP App host
204
- bridge. Under the hood, routes receive `agentNative.mcpHostContext` and may
205
- post `agentNative.mcpHost.updateModelContext`,
206
- `agentNative.mcpHost.openLink`, or
207
- `agentNative.mcpHost.requestDisplayMode`; the wrapper replies with
208
- `agentNative.mcpHost.response`.
209
+ bridge. Direct and Claude-mounted route embeds post standard `ui/*` JSON-RPC
210
+ messages to the host, while the ChatGPT controlled-frame path and explicit
211
+ nested-iframe diagnostic path proxy `agentNative.mcpHost.*` messages through
212
+ the launch wrapper.
213
+ When a submitted app prompt should continue the host chat, call
214
+ `sendToAgentChat()` from the embedded route; it sends hidden model context and
215
+ then posts a visible user message through the host bridge where supported.
216
+ Design those routes with their own scrolling, because the MCP resource reports
217
+ a bounded inline height rather than asking the host to size itself to the full
218
+ app document.
209
219
 
210
220
  ## Standard actions {#standard-actions}
211
221
 
@@ -219,38 +219,55 @@ Claude Code and other CLI-first clients still receive the same resources and met
219
219
 
220
220
  MCP App embeds are route embeds, not separate mini-products. `embedApp()`
221
221
  starts from the action's `link` target, creates a short-lived embed session,
222
- and by default navigates the MCP App frame itself to that app route. That keeps
223
- Claude and ChatGPT on a single iframe instead of relying on a nested app iframe.
224
- Design embedded routes so a reload with the same URL reconstructs the same view.
222
+ and launches that signed app route. Standard MCP Apps hosts can navigate the
223
+ MCP App frame itself. ChatGPT gets a controlled route iframe because its web
224
+ sandbox can otherwise auto-size a full app route into a feedback loop. Claude
225
+ web currently proxies MCP App content through `claudemcpcontent.com`; direct
226
+ route navigation can fetch the app HTML there without reliably running the
227
+ framework bootstrap, and a second nested iframe is easy for the host to block.
228
+ For Claude, the launcher fetches the signed app HTML and mounts that document
229
+ into the existing MCP resource frame, then rewrites app-origin
230
+ fetch/XHR/EventSource calls back to the app with the embed token. The app still
231
+ renders the normal route and React components. Design embedded routes so a
232
+ reload with the same signed URL reconstructs the same view.
225
233
 
226
234
  ChatGPT gets a dedicated compatibility path through `window.openai`: the launch
227
235
  document reads `toolInput`, `toolOutput`, and `toolResponseMetadata` directly,
228
236
  then calls `create_embed_session` via `window.openai.callTool(...)`. Standard
229
- MCP Apps hosts use the `ui/*` JSON-RPC bridge. After the direct navigation, the
230
- loaded app route can still call `ui/update-model-context`, `ui/message`,
231
- `ui/open-link`, and `ui/request-display-mode` through the host bridge helpers.
232
- Keep the result shape identical for both paths: return a focused `link` and
233
- concise structured content.
234
-
235
- An explicit nested iframe diagnostic path remains available with
236
- `embedMode: "iframe"`, `renderMode: "iframe"`, `nested: true`, or
237
- `frame: "iframe"`. Use it only when debugging host behavior. If that diagnostic
238
- iframe is blocked, `embedApp()` replaces it with an open-app fallback: the user
239
- can retry inline, open a freshly minted embed session through the host, or use
240
- the visible route URL. Keep the action's `link` target useful on its own because
241
- it is still the universal escape hatch.
237
+ MCP Apps hosts use the `ui/*` JSON-RPC bridge. Direct and Claude-mounted routes
238
+ can call `ui/update-model-context`, `ui/message`, `ui/open-link`, and
239
+ `ui/request-display-mode` through the host bridge helpers. When the ChatGPT or
240
+ explicit diagnostic iframe path is used, the wrapper relays the same host
241
+ actions over `agentNative.mcpHost.*` postMessage requests. Keep the result
242
+ shape identical for both paths: return a focused `link` and concise structured
243
+ content.
244
+
245
+ The resource shell owns the outer host size. Keep embedded app routes
246
+ internally scrollable and let the launcher report a bounded intrinsic height
247
+ rather than the full document height; otherwise host auto-resize can turn a
248
+ normal app page into a very tall chat artifact. A changed shell only affects
249
+ new MCP App resources and new tool calls. Old ChatGPT/Claude conversation
250
+ frames can keep the previous resource behavior, so verify sizing with a fresh
251
+ inline render before judging a fix.
252
+
253
+ You can force the nested diagnostic iframe with `embedMode: "iframe"`,
254
+ `renderMode: "iframe"`, `nested: true`, or `frame: "iframe"` when debugging
255
+ host behavior. If the iframe is blocked, `embedApp()` replaces it with an
256
+ open-app fallback: the user can retry inline, open a freshly minted embed
257
+ session through the host, or use the visible route URL. Keep the action's
258
+ `link` target useful on its own because it is still the universal escape hatch.
242
259
 
243
260
  The host bridge is deliberately small:
244
261
 
245
- | Mode | Message type | Use it for |
246
- | ----------------- | ------------------------------------- | ---------------------------------------- |
247
- | direct | `ui/update-model-context` | Hidden context for the host model |
248
- | direct | `ui/message` | Post a visible user turn into the host |
249
- | direct | `ui/open-link` | Open an external or app URL via the host |
250
- | direct | `ui/request-display-mode` | Request `inline`, `fullscreen`, or `pip` |
251
- | nested diagnostic | `agentNative.mcpHostContext` | Theme, locale, host platform, dimensions |
252
- | nested diagnostic | `agentNative.embeddedAppReady` | Confirm the route iframe loaded |
253
- | nested diagnostic | `agentNative.mcpHost.*` / `.response` | Wrapper relay for host requests |
262
+ | Mode | Message type | Use it for |
263
+ | ------------------------ | ------------------------------------- | ---------------------------------------- |
264
+ | direct / Claude | `ui/update-model-context` | Hidden context for the host model |
265
+ | direct / Claude | `ui/message` | Post a visible user turn into the host |
266
+ | direct / Claude | `ui/open-link` | Open an external or app URL via the host |
267
+ | direct / Claude | `ui/request-display-mode` | Request `inline`, `fullscreen`, or `pip` |
268
+ | ChatGPT / iframe wrapper | `agentNative.mcpHostContext` | Theme, locale, host platform, dimensions |
269
+ | ChatGPT / iframe wrapper | `agentNative.embeddedAppReady` | Confirm the route iframe loaded |
270
+ | ChatGPT / iframe wrapper | `agentNative.mcpHost.*` / `.response` | Wrapper relay for host requests |
254
271
 
255
272
  Embedded routes can use `updateMcpAppModelContext()`,
256
273
  `openMcpAppHostLink()`, `requestMcpAppDisplayMode()`,
@@ -352,7 +369,6 @@ export default defineAction({
352
369
  description: "Open the generated draft in the real Mail compose UI.",
353
370
  iframeTitle: "Agent-Native Mail",
354
371
  openLabel: "Open in Mail",
355
- frameDomains: ["https:", "http://localhost:*", "http://127.0.0.1:*"],
356
372
  }),
357
373
  },
358
374
  });
@@ -362,9 +378,16 @@ The MCP server advertises extension `io.modelcontextprotocol/ui`, adds `_meta.ui
362
378
 
363
379
  Keep the existing `link` builder even when adding `mcpApp`. CLI-only clients, older hosts, and any host that does not render MCP Apps will ignore the UI metadata and still need the `"Open in … →"` link. `embedApp()` uses that link as its launch target, calls the app-only `create_embed_session` helper, exchanges a one-time SQL ticket at `/_agent-native/embed/start`, and navigates the MCP App frame to the target route with a short-lived browser session plus a bearer fallback for same-origin fetches. `open_app({ app, path, embed: true })` is the generic escape hatch for routes such as full dashboards, filtered inboxes, calendar draft views, analyses, and extension pages, and should be used liberally when the full app is the clearest review/edit surface.
364
380
 
381
+ `embedApp()` includes the MCP request origin in the resource CSP so the launcher
382
+ can fetch and, when explicitly requested, frame the signed first-party app
383
+ route. Only pass additional `frameDomains` for a custom MCP App that truly
384
+ embeds a third-party player.
385
+
365
386
  Inside those `embedApp()` routes, `sendToAgentChat()` is embed-aware.
366
387
  Auto-submitted prompts relay to the MCP host as `ui/update-model-context` plus
367
- `ui/message`; `submit: false` remains local prefill/review behavior.
388
+ `ui/message`, so a button in the embedded app can intentionally continue the
389
+ Claude/ChatGPT conversation from the selected app state. `submit: false`
390
+ remains local prefill/review behavior.
368
391
 
369
392
  ### The `link` contract {#link-contract}
370
393
 
@@ -509,6 +532,9 @@ The fallback hosted `connect` flow never copies the deployment's shared secret.
509
532
  - Test MCP Apps with the lightweight fixtures around `embedApp()` and
510
533
  `McpAppRenderer`; they cover CSP, host context, app launch, and bridge
511
534
  message behavior without needing a real external host.
535
+ - When validating ChatGPT or Claude web, trigger a fresh tool call after shell
536
+ changes and measure the visible iframe. Previously rendered frames in the
537
+ same conversation may still show cached height or launch behavior.
512
538
 
513
539
  **Don't**
514
540
 
@@ -78,11 +78,26 @@ If an action declares `mcpApp`, the server also advertises the official MCP Apps
78
78
 
79
79
  `embedApp()` is the low-level URL-first MCP App helper. It reads the action
80
80
  result's open link, asks the app-only `create_embed_session` tool to mint a
81
- route-scoped session, then navigates the MCP App frame itself to the resulting
82
- app route. For normal action authoring, use `embedRoute()` when the action's
81
+ route-scoped session, then launches the resulting app route. ChatGPT and hosts
82
+ that allow direct route hydration launch the same signed app URL, but ChatGPT
83
+ keeps it in a controlled route iframe to avoid a web-sandbox auto-height
84
+ feedback loop. Claude web currently proxies MCP App content under
85
+ `claudemcpcontent.com`; direct route navigation can fetch app HTML there
86
+ without reliably running the framework bootstrap, and a second nested iframe is
87
+ easy for the host to block. For Claude, `embedApp()` fetches the signed app
88
+ HTML and mounts the real route document into the existing MCP resource frame,
89
+ with app-origin requests routed back to the original app using the embed token.
90
+ For normal action authoring, use `embedRoute()` when the action's
83
91
  `link` and `mcpApp` should come from the same pure route builder. The route
84
92
  itself should derive state from the URL and normal app data fetching.
85
93
 
94
+ The outer MCP resource reports a bounded inline height to the host and the app
95
+ route scrolls internally. Do not rely on host auto-resize measuring the full
96
+ document; in ChatGPT and Claude this can make a normal full-app route appear as
97
+ a huge chat artifact. Host conversations also keep already-rendered iframes, so
98
+ after changing the resource shell or `ui://` version, test a fresh tool call
99
+ rather than re-measuring an old frame.
100
+
86
101
  Default direct embeds talk to the MCP Apps host through standard `ui/*`
87
102
  JSON-RPC messages:
88
103
 
@@ -93,8 +108,9 @@ JSON-RPC messages:
93
108
  | `ui/open-link` | `{ url }` |
94
109
  | `ui/request-display-mode` | `{ mode }` |
95
110
 
96
- An explicit `embedMode: "iframe"` / `renderMode: "iframe"` diagnostic path
97
- keeps the old wrapper-to-route postMessage relay:
111
+ The ChatGPT controlled-frame path and any explicit `embedMode: "iframe"` /
112
+ `renderMode: "iframe"` diagnostic path use the wrapper-to-route postMessage
113
+ relay:
98
114
 
99
115
  | Direction | Type | Payload shape |
100
116
  | --------------- | ---------------------------------------- | --------------------------------------------- |
@@ -104,8 +120,17 @@ keeps the old wrapper-to-route postMessage relay:
104
120
  | route → wrapper | `agentNative.mcpHost.requestDisplayMode` | `{ requestId, mode }` |
105
121
  | wrapper → route | `agentNative.mcpHost.response` | `{ requestId, ok, result?, error? }` |
106
122
 
123
+ `embedApp()` includes the MCP request origin in the resource CSP so the
124
+ launcher can fetch and, when explicitly requested, frame the signed first-party
125
+ route. Pass additional `frameDomains` only for custom third-party frames.
126
+
107
127
  Host-mediated open links keep the iframe from choosing its own browser target.
108
128
  Model context updates are opt-in and hidden from the user-facing transcript.
129
+ `ui/message` is the portable way for an embedded app button to ask the host to
130
+ post a visible user message and continue the chat. In agent-native routes,
131
+ `sendToAgentChat()` uses `ui/update-model-context` plus `ui/message` when
132
+ called from a submitted MCP App embed, while `submit: false` remains an
133
+ in-route draft/prefill path.
109
134
  Display mode requests are best-effort: a host can honor, ignore, or reject the
110
135
  request. Embedded routes must remain functional in the default inline mode.
111
136
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.22.19",
3
+ "version": "0.22.20",
4
4
  "type": "module",
5
5
  "description": "Framework for agent-native application development — where AI agents and UI share state via files",
6
6
  "license": "MIT",