@agent-native/core 0.22.19 → 0.22.21

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 +420 -29
  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 +16 -5
  45. package/docs/content/external-agents.md +61 -26
  46. package/docs/content/mcp-protocol.md +32 -4
  47. package/package.json +1 -1
@@ -199,13 +199,24 @@ 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
+ Standard hosts navigate the MCP App frame directly to that signed route.
203
+ Claude web uses a single-frame transplant path that hydrates the signed app
204
+ HTML inside Claude's MCP App iframe because Claude does not reliably allow
205
+ app-owned child iframes or external frame navigation. ChatGPT web uses a
206
+ controlled route iframe for stable `window.openai` host APIs and bounded height
207
+ control.
202
208
 
203
209
  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`.
210
+ bridge. Direct route embeds and Claude's transplanted route post standard
211
+ `ui/*` JSON-RPC messages to the host, while the ChatGPT controlled-frame path
212
+ and explicit diagnostic iframe path proxy `agentNative.mcpHost.*` messages
213
+ through the launch wrapper.
214
+ When a submitted app prompt should continue the host chat, call
215
+ `sendToAgentChat()` from the embedded route; it sends hidden model context and
216
+ then posts a visible user message through the host bridge where supported.
217
+ Design those routes with their own scrolling, because the MCP resource reports
218
+ a bounded inline height rather than asking the host to size itself to the full
219
+ app document.
209
220
 
210
221
  ## Standard actions {#standard-actions}
211
222
 
@@ -219,38 +219,64 @@ 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 when the host can hydrate the route directly. Claude web
224
+ uses a single-frame transplant path: the resource document fetches the signed
225
+ app HTML and hydrates it inside Claude's MCP App iframe because Claude does not
226
+ reliably allow app-owned child iframes or external frame navigation. ChatGPT
227
+ web gets a controlled route iframe because its Apps bridge gives us stable
228
+ `window.openai` host APIs and bounded height control. All paths point at the
229
+ same signed app route and render the normal route and React components. Design
230
+ embedded routes so a reload with the same signed URL reconstructs the same
231
+ view.
225
232
 
226
233
  ChatGPT gets a dedicated compatibility path through `window.openai`: the launch
227
234
  document reads `toolInput`, `toolOutput`, and `toolResponseMetadata` directly,
228
235
  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
+ MCP Apps hosts use the `ui/*` JSON-RPC bridge. Directly hydrated routes can
237
+ call `ui/update-model-context`, `ui/message`, `ui/open-link`, and
238
+ `ui/request-display-mode` through the host bridge helpers. Claude's
239
+ transplanted route uses the same direct `ui/*` host bridge after hydration.
240
+ When the ChatGPT or explicit diagnostic iframe path is used, the wrapper
241
+ relays the same host actions over `agentNative.mcpHost.*` postMessage
242
+ requests. Keep the result shape identical for both paths: return a focused
243
+ `link` and concise structured 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
+ Claude uses the single-frame transplant path by default. You can also force it
254
+ in other hosts with `embedMode: "transplant"` or `frame: "transplant"` when
255
+ debugging host module-loading behavior. You can force the nested diagnostic iframe with
236
256
  `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.
257
+ `frame: "iframe"`. If the iframe is blocked, `embedApp()` replaces it with an
258
+ open-app fallback: the user can retry inline, open a freshly minted embed
259
+ session through the host, or use the visible route URL. Keep the action's
260
+ `link` target useful on its own because it is still the universal escape hatch.
261
+
262
+ When testing Claude through ngrok, use a production build (`agent-native build`
263
+ then `agent-native start`) or a deployed preview/production URL. Claude's
264
+ single-frame transplant path works with production asset chunks; raw Vite dev
265
+ modules such as `/app/root.tsx` can be protected by app auth and fail dynamic
266
+ imports from the Claude resource origin.
242
267
 
243
268
  The host bridge is deliberately small:
244
269
 
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 |
270
+ | Mode | Message type | Use it for |
271
+ | ---------------------- | ------------------------------------- | ---------------------------------------- |
272
+ | direct host route | `ui/update-model-context` | Hidden context for the host model |
273
+ | direct host route | `ui/message` | Post a visible user turn into the host |
274
+ | direct host route | `ui/open-link` | Open an external or app URL via the host |
275
+ | direct host route | `ui/request-display-mode` | Request `inline`, `fullscreen`, or `pip` |
276
+ | Claude transplant | `ui/*` | Same direct host bridge after hydration |
277
+ | ChatGPT / iframe route | `agentNative.mcpHostContext` | Theme, locale, host platform, dimensions |
278
+ | ChatGPT / iframe route | `agentNative.embeddedAppReady` | Confirm the route iframe loaded |
279
+ | ChatGPT / iframe route | `agentNative.mcpHost.*` / `.response` | Wrapper relay for host requests |
254
280
 
255
281
  Embedded routes can use `updateMcpAppModelContext()`,
256
282
  `openMcpAppHostLink()`, `requestMcpAppDisplayMode()`,
@@ -352,7 +378,6 @@ export default defineAction({
352
378
  description: "Open the generated draft in the real Mail compose UI.",
353
379
  iframeTitle: "Agent-Native Mail",
354
380
  openLabel: "Open in Mail",
355
- frameDomains: ["https:", "http://localhost:*", "http://127.0.0.1:*"],
356
381
  }),
357
382
  },
358
383
  });
@@ -362,9 +387,16 @@ The MCP server advertises extension `io.modelcontextprotocol/ui`, adds `_meta.ui
362
387
 
363
388
  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
389
 
390
+ `embedApp()` includes the MCP request origin in the resource CSP so the launcher
391
+ can fetch and, when explicitly requested, frame the signed first-party app
392
+ route. Only pass additional `frameDomains` for a custom MCP App that truly
393
+ embeds a third-party player.
394
+
365
395
  Inside those `embedApp()` routes, `sendToAgentChat()` is embed-aware.
366
396
  Auto-submitted prompts relay to the MCP host as `ui/update-model-context` plus
367
- `ui/message`; `submit: false` remains local prefill/review behavior.
397
+ `ui/message`, so a button in the embedded app can intentionally continue the
398
+ Claude/ChatGPT conversation from the selected app state. `submit: false`
399
+ remains local prefill/review behavior.
368
400
 
369
401
  ### The `link` contract {#link-contract}
370
402
 
@@ -509,6 +541,9 @@ The fallback hosted `connect` flow never copies the deployment's shared secret.
509
541
  - Test MCP Apps with the lightweight fixtures around `embedApp()` and
510
542
  `McpAppRenderer`; they cover CSP, host context, app launch, and bridge
511
543
  message behavior without needing a real external host.
544
+ - When validating ChatGPT or Claude web, trigger a fresh tool call after shell
545
+ changes and measure the visible iframe. Previously rendered frames in the
546
+ same conversation may still show cached height or launch behavior.
512
547
 
513
548
  **Don't**
514
549
 
@@ -78,11 +78,24 @@ 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. Standard hosts
82
+ hydrate the signed route by navigating the MCP App frame itself. Claude web
83
+ uses a single-frame transplant path that fetches the signed app HTML and
84
+ hydrates it inside Claude's MCP App iframe because Claude does not reliably
85
+ allow app-owned child iframes or external frame navigation. ChatGPT web keeps
86
+ the signed app URL in a controlled route iframe for stable `window.openai`
87
+ host APIs and bounded height control.
88
+ For normal action authoring, use `embedRoute()` when the action's
83
89
  `link` and `mcpApp` should come from the same pure route builder. The route
84
90
  itself should derive state from the URL and normal app data fetching.
85
91
 
92
+ The outer MCP resource reports a bounded inline height to the host and the app
93
+ route scrolls internally. Do not rely on host auto-resize measuring the full
94
+ document; in ChatGPT and Claude this can make a normal full-app route appear as
95
+ a huge chat artifact. Host conversations also keep already-rendered iframes, so
96
+ after changing the resource shell or `ui://` version, test a fresh tool call
97
+ rather than re-measuring an old frame.
98
+
86
99
  Default direct embeds talk to the MCP Apps host through standard `ui/*`
87
100
  JSON-RPC messages:
88
101
 
@@ -93,8 +106,14 @@ JSON-RPC messages:
93
106
  | `ui/open-link` | `{ url }` |
94
107
  | `ui/request-display-mode` | `{ mode }` |
95
108
 
96
- An explicit `embedMode: "iframe"` / `renderMode: "iframe"` diagnostic path
97
- keeps the old wrapper-to-route postMessage relay:
109
+ Claude's transplanted route uses the same `ui/*` bridge after hydration. Test
110
+ Claude against deployed/preview URLs or a local production build served with
111
+ `agent-native start`; raw Vite dev modules can be app-auth protected and fail
112
+ dynamic imports from Claude's resource origin.
113
+
114
+ The ChatGPT controlled-frame path and any explicit `embedMode: "iframe"` /
115
+ `renderMode: "iframe"` diagnostic path use the wrapper-to-route postMessage
116
+ relay:
98
117
 
99
118
  | Direction | Type | Payload shape |
100
119
  | --------------- | ---------------------------------------- | --------------------------------------------- |
@@ -104,8 +123,17 @@ keeps the old wrapper-to-route postMessage relay:
104
123
  | route → wrapper | `agentNative.mcpHost.requestDisplayMode` | `{ requestId, mode }` |
105
124
  | wrapper → route | `agentNative.mcpHost.response` | `{ requestId, ok, result?, error? }` |
106
125
 
126
+ `embedApp()` includes the MCP request origin in the resource CSP so the
127
+ launcher can fetch and, when explicitly requested, frame the signed first-party
128
+ route. Pass additional `frameDomains` only for custom third-party frames.
129
+
107
130
  Host-mediated open links keep the iframe from choosing its own browser target.
108
131
  Model context updates are opt-in and hidden from the user-facing transcript.
132
+ `ui/message` is the portable way for an embedded app button to ask the host to
133
+ post a visible user message and continue the chat. In agent-native routes,
134
+ `sendToAgentChat()` uses `ui/update-model-context` plus `ui/message` when
135
+ called from a submitted MCP App embed, while `submit: false` remains an
136
+ in-route draft/prefill path.
109
137
  Display mode requests are best-effort: a host can honor, ignore, or reject the
110
138
  request. Embedded routes must remain functional in the default inline mode.
111
139
 
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.21",
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",