@agent-native/core 0.128.3 → 0.129.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.
Files changed (350) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +91 -0
  3. package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  4. package/corpus/core/package.json +6 -3
  5. package/corpus/core/src/a2a/client.ts +18 -4
  6. package/corpus/core/src/a2a/server.ts +19 -3
  7. package/corpus/core/src/a2a/types.ts +7 -0
  8. package/corpus/core/src/action.ts +43 -16
  9. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -10
  10. package/corpus/core/src/agent/production-agent.ts +41 -3
  11. package/corpus/core/src/agent/thread-data-builder.ts +5 -0
  12. package/corpus/core/src/chat-threads/migrations.ts +53 -0
  13. package/corpus/core/src/cli/create.ts +4 -0
  14. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  15. package/corpus/core/src/cli/skills.ts +16 -0
  16. package/corpus/core/src/client/AgentPanel.tsx +9 -5
  17. package/corpus/core/src/client/AssistantChat.tsx +138 -46
  18. package/corpus/core/src/client/MultiTabAssistantChat.tsx +16 -10
  19. package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
  20. package/corpus/core/src/client/analytics.ts +159 -102
  21. package/corpus/core/src/client/chat/legacy-chart-shorthand.tsx +479 -0
  22. package/corpus/core/src/client/chat/markdown-renderer.tsx +23 -3
  23. package/corpus/core/src/client/chat/message-components.tsx +12 -5
  24. package/corpus/core/src/client/chat/runtime.ts +2 -1
  25. package/corpus/core/src/client/chat/tool-call-display.tsx +25 -2
  26. package/corpus/core/src/client/chat-model-groups.ts +3 -0
  27. package/corpus/core/src/client/client-status-requests.ts +161 -0
  28. package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  29. package/corpus/core/src/client/sse-event-processor.ts +15 -3
  30. package/corpus/core/src/client/use-agent-engine-configured.ts +54 -50
  31. package/corpus/core/src/client/use-chat-models.ts +16 -11
  32. package/corpus/core/src/client/use-chat-threads.ts +32 -9
  33. package/corpus/core/src/client/use-session.ts +4 -5
  34. package/corpus/core/src/db/client.ts +83 -8
  35. package/corpus/core/src/db/ensure-additive-columns.ts +31 -20
  36. package/corpus/core/src/db/migrations.ts +22 -2
  37. package/corpus/core/src/extensions/actions.ts +1 -1
  38. package/corpus/core/src/mcp/approval-store.ts +99 -0
  39. package/corpus/core/src/mcp/build-server.ts +579 -303
  40. package/corpus/core/src/mcp/embed-app.ts +1 -1
  41. package/corpus/core/src/mcp/oauth-client-metadata.ts +380 -0
  42. package/corpus/core/src/mcp/oauth-route.ts +57 -52
  43. package/corpus/core/src/mcp/screen-memory-stdio.ts +400 -378
  44. package/corpus/core/src/mcp/server.ts +29 -145
  45. package/corpus/core/src/mcp/stdio.ts +72 -80
  46. package/corpus/core/src/mcp-client/manager.ts +6 -10
  47. package/corpus/core/src/mcp-client/oauth-client.ts +127 -36
  48. package/corpus/core/src/mcp-client/oauth-routes.ts +16 -6
  49. package/corpus/core/src/mcp-client/routes.ts +6 -5
  50. package/corpus/core/src/server/agent-capabilities.ts +20 -0
  51. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +27 -0
  52. package/corpus/core/src/server/agent-chat-plugin.ts +18 -0
  53. package/corpus/core/src/server/transcribe-voice.ts +14 -5
  54. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  55. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  56. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  57. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  58. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +12 -0
  59. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +13 -2
  60. package/corpus/templates/analytics/actions/generate-chart.ts +2 -2
  61. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -10
  62. package/corpus/templates/analytics/app/global.css +0 -31
  63. package/corpus/templates/analytics/app/lib/sql-query.ts +46 -20
  64. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboard-loading-states-now-use-a-simpler-standar.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboards-now-load-first-party-charts-reliably-in.md +6 -0
  66. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +42 -16
  67. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
  68. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  69. package/corpus/templates/assets/.agents/skills/security/SKILL.md +13 -2
  70. package/corpus/templates/brain/.agents/skills/security/SKILL.md +13 -2
  71. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +13 -2
  72. package/corpus/templates/chat/.agents/skills/security/SKILL.md +13 -2
  73. package/corpus/templates/clips/.agents/skills/security/SKILL.md +13 -2
  74. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -6
  75. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -2
  76. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -1
  77. package/corpus/templates/clips/changelog/2026-07-28-copied-share-links-no-longer-leave-an-unnecessary-confirmati.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-07-28-playback-skip-controls-no-longer-show-overlapping-numbers.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-28-public-clips-share-buttons-now-use-a-clean-text-only-label.md +6 -0
  80. package/corpus/templates/clips/desktop/src/app.tsx +14 -17
  81. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +3 -1
  82. package/corpus/templates/clips/desktop/src/styles.css +13 -10
  83. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +2 -2
  84. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +36 -0
  85. package/corpus/templates/content/.agents/skills/security/SKILL.md +13 -2
  86. package/corpus/templates/content/actions/_builder-cms-read-client.ts +155 -34
  87. package/corpus/templates/content/actions/_builder-docs-client.ts +151 -30
  88. package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +5 -2
  89. package/corpus/templates/content/app/root.tsx +35 -14
  90. package/corpus/templates/content/changelog/2026-07-28-builder-backed-files-media-fields-now-accept-direct-file-url.md +6 -0
  91. package/corpus/templates/content/changelog/2026-07-28-builder-content-reads-now-negotiate-the-newest-mcp-protocol-.md +6 -0
  92. package/corpus/templates/content/changelog/2026-07-28-public-content-pages-load-faster-before-the-optional-ask-sid.md +6 -0
  93. package/corpus/templates/design/.agents/skills/security/SKILL.md +13 -2
  94. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +36 -16
  95. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +42 -3
  96. package/corpus/templates/design/actions/open-visual-edit.ts +2 -1
  97. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  98. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -15
  99. package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +314 -0
  100. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +88 -3
  101. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
  102. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +7 -6
  103. package/corpus/templates/design/app/i18n-data.ts +56 -11
  104. package/corpus/templates/design/app/i18n-responsive-interact.ts +183 -0
  105. package/corpus/templates/design/app/lib/agent-chat.ts +60 -0
  106. package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +89 -0
  107. package/corpus/templates/design/changelog/2026-07-28-design-edits-can-be-applied-through-the-host-coding-agent-fr.md +6 -0
  108. package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +6 -0
  109. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +6 -0
  110. package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +2 -2
  111. package/corpus/templates/design/changelog/2026-07-28-visual-edit-now-always-shows-the-live-running-app-in-the-can.md +6 -0
  112. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +13 -2
  113. package/corpus/templates/forms/.agents/skills/security/SKILL.md +13 -2
  114. package/corpus/templates/macros/.agents/skills/security/SKILL.md +13 -2
  115. package/corpus/templates/mail/.agents/skills/security/SKILL.md +13 -2
  116. package/corpus/templates/plan/.agents/skills/security/SKILL.md +13 -2
  117. package/corpus/templates/slides/.agents/skills/security/SKILL.md +13 -2
  118. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +13 -2
  119. package/corpus/toolkit/CHANGELOG.md +13 -0
  120. package/corpus/toolkit/package.json +1 -1
  121. package/corpus/toolkit/src/chat-history.css +1 -2
  122. package/corpus/toolkit/src/composer/TiptapComposer.tsx +45 -0
  123. package/dist/a2a/client.d.ts +7 -0
  124. package/dist/a2a/client.d.ts.map +1 -1
  125. package/dist/a2a/client.js +8 -3
  126. package/dist/a2a/client.js.map +1 -1
  127. package/dist/a2a/server.d.ts.map +1 -1
  128. package/dist/a2a/server.js +18 -3
  129. package/dist/a2a/server.js.map +1 -1
  130. package/dist/a2a/types.d.ts +7 -0
  131. package/dist/a2a/types.d.ts.map +1 -1
  132. package/dist/a2a/types.js.map +1 -1
  133. package/dist/action.d.ts +15 -1
  134. package/dist/action.d.ts.map +1 -1
  135. package/dist/action.js +39 -16
  136. package/dist/action.js.map +1 -1
  137. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  138. package/dist/agent/engine/ai-sdk-engine.js +14 -10
  139. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  140. package/dist/agent/production-agent.d.ts.map +1 -1
  141. package/dist/agent/production-agent.js +35 -7
  142. package/dist/agent/production-agent.js.map +1 -1
  143. package/dist/agent/thread-data-builder.d.ts +2 -0
  144. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  145. package/dist/agent/thread-data-builder.js +3 -0
  146. package/dist/agent/thread-data-builder.js.map +1 -1
  147. package/dist/chat-threads/migrations.d.ts +21 -0
  148. package/dist/chat-threads/migrations.d.ts.map +1 -0
  149. package/dist/chat-threads/migrations.js +45 -0
  150. package/dist/chat-threads/migrations.js.map +1 -0
  151. package/dist/cli/create.d.ts.map +1 -1
  152. package/dist/cli/create.js +4 -0
  153. package/dist/cli/create.js.map +1 -1
  154. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  155. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  156. package/dist/cli/skills-content/design-visual-edit-skill.js +36 -16
  157. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  158. package/dist/cli/skills.d.ts.map +1 -1
  159. package/dist/cli/skills.js +15 -0
  160. package/dist/cli/skills.js.map +1 -1
  161. package/dist/client/AgentPanel.d.ts.map +1 -1
  162. package/dist/client/AgentPanel.js +6 -5
  163. package/dist/client/AgentPanel.js.map +1 -1
  164. package/dist/client/AssistantChat.d.ts +37 -0
  165. package/dist/client/AssistantChat.d.ts.map +1 -1
  166. package/dist/client/AssistantChat.js +41 -8
  167. package/dist/client/AssistantChat.js.map +1 -1
  168. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  169. package/dist/client/MultiTabAssistantChat.js +11 -10
  170. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  171. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  172. package/dist/client/agent-chat-adapter.js +19 -3
  173. package/dist/client/agent-chat-adapter.js.map +1 -1
  174. package/dist/client/analytics.d.ts.map +1 -1
  175. package/dist/client/analytics.js +150 -101
  176. package/dist/client/analytics.js.map +1 -1
  177. package/dist/client/chat/legacy-chart-shorthand.d.ts +29 -0
  178. package/dist/client/chat/legacy-chart-shorthand.d.ts.map +1 -0
  179. package/dist/client/chat/legacy-chart-shorthand.js +330 -0
  180. package/dist/client/chat/legacy-chart-shorthand.js.map +1 -0
  181. package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
  182. package/dist/client/chat/markdown-renderer.js +8 -2
  183. package/dist/client/chat/markdown-renderer.js.map +1 -1
  184. package/dist/client/chat/message-components.d.ts +2 -1
  185. package/dist/client/chat/message-components.d.ts.map +1 -1
  186. package/dist/client/chat/message-components.js +11 -4
  187. package/dist/client/chat/message-components.js.map +1 -1
  188. package/dist/client/chat/runtime.d.ts.map +1 -1
  189. package/dist/client/chat/runtime.js +2 -1
  190. package/dist/client/chat/runtime.js.map +1 -1
  191. package/dist/client/chat/tool-call-display.d.ts +4 -1
  192. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  193. package/dist/client/chat/tool-call-display.js +14 -8
  194. package/dist/client/chat/tool-call-display.js.map +1 -1
  195. package/dist/client/chat-model-groups.d.ts.map +1 -1
  196. package/dist/client/chat-model-groups.js +3 -0
  197. package/dist/client/chat-model-groups.js.map +1 -1
  198. package/dist/client/client-status-requests.d.ts +14 -0
  199. package/dist/client/client-status-requests.d.ts.map +1 -0
  200. package/dist/client/client-status-requests.js +126 -0
  201. package/dist/client/client-status-requests.js.map +1 -0
  202. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  203. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  204. package/dist/client/sse-event-processor.d.ts +7 -0
  205. package/dist/client/sse-event-processor.d.ts.map +1 -1
  206. package/dist/client/sse-event-processor.js +8 -3
  207. package/dist/client/sse-event-processor.js.map +1 -1
  208. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  209. package/dist/client/use-agent-engine-configured.js +29 -43
  210. package/dist/client/use-agent-engine-configured.js.map +1 -1
  211. package/dist/client/use-chat-models.d.ts.map +1 -1
  212. package/dist/client/use-chat-models.js +11 -11
  213. package/dist/client/use-chat-models.js.map +1 -1
  214. package/dist/client/use-chat-threads.d.ts.map +1 -1
  215. package/dist/client/use-chat-threads.js +23 -7
  216. package/dist/client/use-chat-threads.js.map +1 -1
  217. package/dist/client/use-session.d.ts.map +1 -1
  218. package/dist/client/use-session.js +4 -4
  219. package/dist/client/use-session.js.map +1 -1
  220. package/dist/collab/awareness.d.ts +2 -2
  221. package/dist/collab/awareness.d.ts.map +1 -1
  222. package/dist/db/client.d.ts.map +1 -1
  223. package/dist/db/client.js +48 -4
  224. package/dist/db/client.js.map +1 -1
  225. package/dist/db/ensure-additive-columns.d.ts.map +1 -1
  226. package/dist/db/ensure-additive-columns.js +32 -21
  227. package/dist/db/ensure-additive-columns.js.map +1 -1
  228. package/dist/db/migrations.d.ts +9 -0
  229. package/dist/db/migrations.d.ts.map +1 -1
  230. package/dist/db/migrations.js +14 -3
  231. package/dist/db/migrations.js.map +1 -1
  232. package/dist/extensions/actions.js +1 -1
  233. package/dist/extensions/actions.js.map +1 -1
  234. package/dist/mcp/approval-store.d.ts +16 -0
  235. package/dist/mcp/approval-store.d.ts.map +1 -0
  236. package/dist/mcp/approval-store.js +85 -0
  237. package/dist/mcp/approval-store.js.map +1 -0
  238. package/dist/mcp/build-server.d.ts +1 -34
  239. package/dist/mcp/build-server.d.ts.map +1 -1
  240. package/dist/mcp/build-server.js +220 -23
  241. package/dist/mcp/build-server.js.map +1 -1
  242. package/dist/mcp/embed-app.js +1 -1
  243. package/dist/mcp/embed-app.js.map +1 -1
  244. package/dist/mcp/oauth-client-metadata.d.ts +15 -0
  245. package/dist/mcp/oauth-client-metadata.d.ts.map +1 -0
  246. package/dist/mcp/oauth-client-metadata.js +297 -0
  247. package/dist/mcp/oauth-client-metadata.js.map +1 -0
  248. package/dist/mcp/oauth-route.d.ts.map +1 -1
  249. package/dist/mcp/oauth-route.js +39 -53
  250. package/dist/mcp/oauth-route.js.map +1 -1
  251. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  252. package/dist/mcp/screen-memory-stdio.js +304 -296
  253. package/dist/mcp/screen-memory-stdio.js.map +1 -1
  254. package/dist/mcp/server.d.ts +12 -25
  255. package/dist/mcp/server.d.ts.map +1 -1
  256. package/dist/mcp/server.js +24 -133
  257. package/dist/mcp/server.js.map +1 -1
  258. package/dist/mcp/stdio.d.ts.map +1 -1
  259. package/dist/mcp/stdio.js +36 -35
  260. package/dist/mcp/stdio.js.map +1 -1
  261. package/dist/mcp-client/manager.d.ts.map +1 -1
  262. package/dist/mcp-client/manager.js +6 -6
  263. package/dist/mcp-client/manager.js.map +1 -1
  264. package/dist/mcp-client/oauth-client.d.ts +13 -17
  265. package/dist/mcp-client/oauth-client.d.ts.map +1 -1
  266. package/dist/mcp-client/oauth-client.js +74 -16
  267. package/dist/mcp-client/oauth-client.js.map +1 -1
  268. package/dist/mcp-client/oauth-routes.d.ts +2 -2
  269. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  270. package/dist/mcp-client/oauth-routes.js +16 -6
  271. package/dist/mcp-client/oauth-routes.js.map +1 -1
  272. package/dist/mcp-client/routes.js +5 -5
  273. package/dist/mcp-client/routes.js.map +1 -1
  274. package/dist/notifications/routes.d.ts +1 -1
  275. package/dist/progress/routes.d.ts +1 -1
  276. package/dist/resources/handlers.d.ts +1 -1
  277. package/dist/secrets/routes.d.ts +9 -9
  278. package/dist/server/agent-capabilities.d.ts.map +1 -1
  279. package/dist/server/agent-capabilities.js +21 -0
  280. package/dist/server/agent-capabilities.js.map +1 -1
  281. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -0
  282. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  283. package/dist/server/agent-chat/action-filters-a2a.js +16 -0
  284. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  285. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  286. package/dist/server/agent-chat-plugin.js +11 -1
  287. package/dist/server/agent-chat-plugin.js.map +1 -1
  288. package/dist/server/transcribe-voice.js +12 -6
  289. package/dist/server/transcribe-voice.js.map +1 -1
  290. package/dist/templates/chat/.agents/skills/security/SKILL.md +13 -2
  291. package/dist/templates/default/.agents/skills/security/SKILL.md +13 -2
  292. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  293. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  294. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  295. package/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  296. package/package.json +7 -4
  297. package/src/a2a/client.ts +18 -4
  298. package/src/a2a/server.ts +19 -3
  299. package/src/a2a/types.ts +7 -0
  300. package/src/action.ts +43 -16
  301. package/src/agent/engine/ai-sdk-engine.ts +14 -10
  302. package/src/agent/production-agent.ts +41 -3
  303. package/src/agent/thread-data-builder.ts +5 -0
  304. package/src/chat-threads/migrations.ts +53 -0
  305. package/src/cli/create.ts +4 -0
  306. package/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  307. package/src/cli/skills.ts +16 -0
  308. package/src/client/AgentPanel.tsx +9 -5
  309. package/src/client/AssistantChat.tsx +138 -46
  310. package/src/client/MultiTabAssistantChat.tsx +16 -10
  311. package/src/client/agent-chat-adapter.ts +21 -3
  312. package/src/client/analytics.ts +159 -102
  313. package/src/client/chat/legacy-chart-shorthand.tsx +479 -0
  314. package/src/client/chat/markdown-renderer.tsx +23 -3
  315. package/src/client/chat/message-components.tsx +12 -5
  316. package/src/client/chat/runtime.ts +2 -1
  317. package/src/client/chat/tool-call-display.tsx +25 -2
  318. package/src/client/chat-model-groups.ts +3 -0
  319. package/src/client/client-status-requests.ts +161 -0
  320. package/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  321. package/src/client/sse-event-processor.ts +15 -3
  322. package/src/client/use-agent-engine-configured.ts +54 -50
  323. package/src/client/use-chat-models.ts +16 -11
  324. package/src/client/use-chat-threads.ts +32 -9
  325. package/src/client/use-session.ts +4 -5
  326. package/src/db/client.ts +83 -8
  327. package/src/db/ensure-additive-columns.ts +31 -20
  328. package/src/db/migrations.ts +22 -2
  329. package/src/extensions/actions.ts +1 -1
  330. package/src/mcp/approval-store.ts +99 -0
  331. package/src/mcp/build-server.ts +579 -303
  332. package/src/mcp/embed-app.ts +1 -1
  333. package/src/mcp/oauth-client-metadata.ts +380 -0
  334. package/src/mcp/oauth-route.ts +57 -52
  335. package/src/mcp/screen-memory-stdio.ts +400 -378
  336. package/src/mcp/server.ts +29 -145
  337. package/src/mcp/stdio.ts +72 -80
  338. package/src/mcp-client/manager.ts +6 -10
  339. package/src/mcp-client/oauth-client.ts +127 -36
  340. package/src/mcp-client/oauth-routes.ts +16 -6
  341. package/src/mcp-client/routes.ts +6 -5
  342. package/src/server/agent-capabilities.ts +20 -0
  343. package/src/server/agent-chat/action-filters-a2a.ts +27 -0
  344. package/src/server/agent-chat-plugin.ts +18 -0
  345. package/src/server/transcribe-voice.ts +14 -5
  346. package/src/templates/chat/.agents/skills/security/SKILL.md +13 -2
  347. package/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  348. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  349. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  350. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
@@ -0,0 +1,16 @@
1
+ export interface McpApprovalGrant {
2
+ nonce: string;
3
+ callerKey: string;
4
+ actionName: string;
5
+ argumentsHash: string;
6
+ expiresAt: number;
7
+ }
8
+ export declare function createMcpApprovalGrant(grant: McpApprovalGrant): Promise<void>;
9
+ /**
10
+ * Atomically consume an exact grant. The UPDATE predicate is the security
11
+ * boundary: only one hosted instance can move a matching, unexpired row from
12
+ * pending to consumed, so an accepted response is at-most-once even when the
13
+ * same signed requestState is replayed concurrently.
14
+ */
15
+ export declare function consumeMcpApprovalGrant(grant: McpApprovalGrant): Promise<boolean>;
16
+ //# sourceMappingURL=approval-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-store.d.ts","sourceRoot":"","sources":["../../src/mcp/approval-store.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,OAAO,CAAC,CAsBlB"}
@@ -0,0 +1,85 @@
1
+ import { getDbExec, intType, isPostgres } from "../db/client.js";
2
+ import { ensureTableExists } from "../db/ddl-guard.js";
3
+ let initPromise;
4
+ async function ensureApprovalTable() {
5
+ if (!initPromise) {
6
+ initPromise = (async () => {
7
+ const createSql = `
8
+ CREATE TABLE IF NOT EXISTS mcp_action_approvals (
9
+ nonce TEXT PRIMARY KEY,
10
+ caller_key TEXT NOT NULL,
11
+ action_name TEXT NOT NULL,
12
+ arguments_hash TEXT NOT NULL,
13
+ expires_at ${intType()} NOT NULL,
14
+ consumed_at ${intType()}
15
+ )
16
+ `;
17
+ if (isPostgres()) {
18
+ await ensureTableExists("mcp_action_approvals", createSql);
19
+ }
20
+ else {
21
+ await getDbExec().execute(createSql);
22
+ }
23
+ })().catch((error) => {
24
+ initPromise = undefined;
25
+ throw error;
26
+ });
27
+ }
28
+ return initPromise;
29
+ }
30
+ export async function createMcpApprovalGrant(grant) {
31
+ await ensureApprovalTable();
32
+ const client = getDbExec();
33
+ await client.execute({
34
+ sql: `INSERT INTO mcp_action_approvals (nonce, caller_key, action_name, arguments_hash, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, NULL)`,
35
+ args: [
36
+ grant.nonce,
37
+ grant.callerKey,
38
+ grant.actionName,
39
+ grant.argumentsHash,
40
+ grant.expiresAt,
41
+ ],
42
+ });
43
+ // This is storage hygiene only. A failed cleanup must never turn a failed
44
+ // grant insert/consume into success, and expired rows remain unusable because
45
+ // consumeMcpApprovalGrant checks expires_at in the atomic update below.
46
+ try {
47
+ await client.execute({
48
+ sql: `DELETE FROM mcp_action_approvals WHERE expires_at < ?`,
49
+ args: [Date.now() - 24 * 60 * 60_000],
50
+ });
51
+ }
52
+ catch (error) {
53
+ console.warn("[mcp] Could not clean expired action approval grants", error);
54
+ }
55
+ }
56
+ /**
57
+ * Atomically consume an exact grant. The UPDATE predicate is the security
58
+ * boundary: only one hosted instance can move a matching, unexpired row from
59
+ * pending to consumed, so an accepted response is at-most-once even when the
60
+ * same signed requestState is replayed concurrently.
61
+ */
62
+ export async function consumeMcpApprovalGrant(grant) {
63
+ await ensureApprovalTable();
64
+ const now = Date.now();
65
+ const result = await getDbExec().execute({
66
+ sql: `UPDATE mcp_action_approvals
67
+ SET consumed_at = ?
68
+ WHERE nonce = ?
69
+ AND caller_key = ?
70
+ AND action_name = ?
71
+ AND arguments_hash = ?
72
+ AND consumed_at IS NULL
73
+ AND expires_at >= ?`,
74
+ args: [
75
+ now,
76
+ grant.nonce,
77
+ grant.callerKey,
78
+ grant.actionName,
79
+ grant.argumentsHash,
80
+ now,
81
+ ],
82
+ });
83
+ return result.rowsAffected === 1;
84
+ }
85
+ //# sourceMappingURL=approval-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-store.js","sourceRoot":"","sources":["../../src/mcp/approval-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,IAAI,WAAsC,CAAC;AAE3C,KAAK,UAAU,mBAAmB;IAChC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;YACxB,MAAM,SAAS,GAAG;;;;;;uBAMD,OAAO,EAAE;wBACR,OAAO,EAAE;;OAE1B,CAAC;YACF,IAAI,UAAU,EAAE,EAAE,CAAC;gBACjB,MAAM,iBAAiB,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,WAAW,GAAG,SAAS,CAAC;YACxB,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAuB;IAEvB,MAAM,mBAAmB,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,yIAAyI;QAC9I,IAAI,EAAE;YACJ,KAAK,CAAC,KAAK;YACX,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,aAAa;YACnB,KAAK,CAAC,SAAS;SAChB;KACF,CAAC,CAAC;IAEH,0EAA0E;IAC1E,8EAA8E;IAC9E,wEAAwE;IACxE,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC;YACnB,GAAG,EAAE,uDAAuD;YAC5D,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;SACtC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,KAAK,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAuB;IAEvB,MAAM,mBAAmB,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;;;;;;;4BAOmB;QACxB,IAAI,EAAE;YACJ,GAAG;YACH,KAAK,CAAC,KAAK;YACX,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,aAAa;YACnB,GAAG;SACJ;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC;AACnC,CAAC","sourcesContent":["import { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists } from \"../db/ddl-guard.js\";\n\nlet initPromise: Promise<void> | undefined;\n\nasync function ensureApprovalTable(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const createSql = `\n CREATE TABLE IF NOT EXISTS mcp_action_approvals (\n nonce TEXT PRIMARY KEY,\n caller_key TEXT NOT NULL,\n action_name TEXT NOT NULL,\n arguments_hash TEXT NOT NULL,\n expires_at ${intType()} NOT NULL,\n consumed_at ${intType()}\n )\n `;\n if (isPostgres()) {\n await ensureTableExists(\"mcp_action_approvals\", createSql);\n } else {\n await getDbExec().execute(createSql);\n }\n })().catch((error) => {\n initPromise = undefined;\n throw error;\n });\n }\n return initPromise;\n}\n\nexport interface McpApprovalGrant {\n nonce: string;\n callerKey: string;\n actionName: string;\n argumentsHash: string;\n expiresAt: number;\n}\n\nexport async function createMcpApprovalGrant(\n grant: McpApprovalGrant,\n): Promise<void> {\n await ensureApprovalTable();\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO mcp_action_approvals (nonce, caller_key, action_name, arguments_hash, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, NULL)`,\n args: [\n grant.nonce,\n grant.callerKey,\n grant.actionName,\n grant.argumentsHash,\n grant.expiresAt,\n ],\n });\n\n // This is storage hygiene only. A failed cleanup must never turn a failed\n // grant insert/consume into success, and expired rows remain unusable because\n // consumeMcpApprovalGrant checks expires_at in the atomic update below.\n try {\n await client.execute({\n sql: `DELETE FROM mcp_action_approvals WHERE expires_at < ?`,\n args: [Date.now() - 24 * 60 * 60_000],\n });\n } catch (error) {\n console.warn(\"[mcp] Could not clean expired action approval grants\", error);\n }\n}\n\n/**\n * Atomically consume an exact grant. The UPDATE predicate is the security\n * boundary: only one hosted instance can move a matching, unexpired row from\n * pending to consumed, so an accepted response is at-most-once even when the\n * same signed requestState is replayed concurrently.\n */\nexport async function consumeMcpApprovalGrant(\n grant: McpApprovalGrant,\n): Promise<boolean> {\n await ensureApprovalTable();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE mcp_action_approvals\n SET consumed_at = ?\n WHERE nonce = ?\n AND caller_key = ?\n AND action_name = ?\n AND arguments_hash = ?\n AND consumed_at IS NULL\n AND expires_at >= ?`,\n args: [\n now,\n grant.nonce,\n grant.callerKey,\n grant.actionName,\n grant.argumentsHash,\n now,\n ],\n });\n return result.rowsAffected === 1;\n}\n"]}
@@ -196,40 +196,7 @@ export declare function buildLinkArtifacts(entry: ActionEntry, args: Record<stri
196
196
  * `requestMeta`; the stdio standalone path passes the resolved local app
197
197
  * origin so deep links still become absolute URLs.
198
198
  */
199
- export declare function createMCPServerForRequest(config: MCPConfig, identity: MCPCallerIdentity | undefined, requestMeta?: MCPRequestMeta): Promise<import("@modelcontextprotocol/sdk/server").Server<{
200
- method: string;
201
- params?: {
202
- [x: string]: unknown;
203
- _meta?: {
204
- [x: string]: unknown;
205
- progressToken?: string | number;
206
- "io.modelcontextprotocol/related-task"?: {
207
- taskId: string;
208
- };
209
- };
210
- };
211
- }, {
212
- method: string;
213
- params?: {
214
- [x: string]: unknown;
215
- _meta?: {
216
- [x: string]: unknown;
217
- progressToken?: string | number;
218
- "io.modelcontextprotocol/related-task"?: {
219
- taskId: string;
220
- };
221
- };
222
- };
223
- }, {
224
- [x: string]: unknown;
225
- _meta?: {
226
- [x: string]: unknown;
227
- progressToken?: string | number;
228
- "io.modelcontextprotocol/related-task"?: {
229
- taskId: string;
230
- };
231
- };
232
- }>>;
199
+ export declare function createMCPServerForRequest(config: MCPConfig, identity: MCPCallerIdentity | undefined, requestMeta?: MCPRequestMeta): Promise<import("@modelcontextprotocol/server").Server>;
233
200
  export declare function getAccessTokens(): string[];
234
201
  export declare function getBearerToken(authHeader: string | undefined): string | undefined;
235
202
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"build-server.d.ts","sourceRoot":"","sources":["../../src/mcp/build-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build-server.d.ts","sourceRoot":"","sources":["../../src/mcp/build-server.ts"],"names":[],"mappings":"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"}
@@ -24,10 +24,65 @@ import { buildDeepLink, toAbsoluteOpenUrl, toDesktopOpenUrl, toVsCodeOpenUrl, }
24
24
  import { getRequestContext, getRequestOrgId, getRequestUserEmail, runWithRequestContext, } from "../server/request-context.js";
25
25
  import { isAgentNativeOpenDeepLink, withCollapsedAgentSidebarParam, } from "../shared/agent-sidebar-url.js";
26
26
  import { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from "../shared/embed-auth.js";
27
+ import { consumeMcpApprovalGrant, createMcpApprovalGrant, } from "./approval-store.js";
27
28
  import { getBuiltinCrossAppTools } from "./builtin-tools.js";
28
29
  import { MCP_CONNECT_OAUTH_CLIENT_ID, MCP_CONNECT_SCOPE, } from "./connect-store.js";
29
30
  import { MCP_APP_REQUEST_ORIGIN_CSP_SOURCE } from "./embed-app.js";
30
31
  import { hasMcpOAuthScope, verifyMcpOAuthAccessToken, } from "./oauth-token.js";
32
+ const MCP_ACTION_APPROVAL_TTL_SECONDS = 10 * 60;
33
+ const MCP_ACTION_APPROVAL_INPUT_KEY = "actionApproval";
34
+ function canonicalJson(value) {
35
+ if (value === null ||
36
+ typeof value === "boolean" ||
37
+ typeof value === "string") {
38
+ return JSON.stringify(value);
39
+ }
40
+ if (typeof value === "number") {
41
+ if (!Number.isFinite(value)) {
42
+ throw new TypeError("MCP action arguments must contain finite numbers");
43
+ }
44
+ return JSON.stringify(value);
45
+ }
46
+ if (Array.isArray(value)) {
47
+ return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
48
+ }
49
+ if (value && typeof value === "object") {
50
+ const record = value;
51
+ return `{${Object.keys(record)
52
+ .sort()
53
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
54
+ .join(",")}}`;
55
+ }
56
+ throw new TypeError("MCP action arguments must be JSON values");
57
+ }
58
+ async function sha256Base64Url(value) {
59
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
60
+ const bytes = new Uint8Array(digest);
61
+ let binary = "";
62
+ for (const byte of bytes)
63
+ binary += String.fromCharCode(byte);
64
+ return btoa(binary)
65
+ .replace(/\+/g, "-")
66
+ .replace(/\//g, "_")
67
+ .replace(/=+$/, "");
68
+ }
69
+ function mcpApprovalPrincipal(identity, orgId, requestMeta) {
70
+ return canonicalJson({
71
+ caller: "mcp",
72
+ userEmail: identity?.userEmail?.trim().toLowerCase() ?? "",
73
+ orgId: orgId ?? identity?.orgId ?? "",
74
+ orgDomain: identity?.orgDomain?.trim().toLowerCase() ?? "",
75
+ oauthClientId: identity?.oauthClientId ?? "",
76
+ firstPartyMcp: identity?.firstPartyMcp === true,
77
+ origin: requestMeta?.origin ?? "",
78
+ });
79
+ }
80
+ function actionApprovalError(message) {
81
+ return {
82
+ content: [{ type: "text", text: message }],
83
+ isError: true,
84
+ };
85
+ }
31
86
  const ASK_AGENT_DEFAULT_INLINE_WAIT_MS = 20_000;
32
87
  const ASK_AGENT_MAX_INLINE_WAIT_MS = 25_000;
33
88
  function boundedAskAgentWaitMs(raw) {
@@ -639,6 +694,9 @@ function mcpServerInfo(config, requestMeta) {
639
694
  ...(icons?.length ? { icons } : {}),
640
695
  };
641
696
  }
697
+ function compareMcpCatalogValues(a, b) {
698
+ return a < b ? -1 : a > b ? 1 : 0;
699
+ }
642
700
  function safeUiSegment(value, fallback) {
643
701
  const normalized = (value || fallback)
644
702
  .trim()
@@ -1016,8 +1074,7 @@ function conciseToolResultText(name, result, options) {
1016
1074
  * origin so deep links still become absolute URLs.
1017
1075
  */
1018
1076
  export async function createMCPServerForRequest(config, identity, requestMeta) {
1019
- const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
1020
- const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, } = await import("@modelcontextprotocol/sdk/types.js");
1077
+ const { ResourceNotFoundError, Server, acceptedContent, createRequestStateCodec, inputRequired, } = await import("@modelcontextprotocol/server");
1021
1078
  // Resolve the effective caller identity. JWT / header-derived identity
1022
1079
  // (passed by `mountMCP` via `verifyAuth`) wins. When the caller passed no
1023
1080
  // identity — the stdio **standalone** path — fall back to the
@@ -1101,6 +1158,37 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1101
1158
  if (fullCatalogRequested) {
1102
1159
  warnFullCatalogServed(Object.keys(advertisedActions).length);
1103
1160
  }
1161
+ // Resolve orgId once per request (DB lookup) so approval state and every
1162
+ // downstream action see the same authenticated principal.
1163
+ const orgIdPromise = resolveMcpIdentityOrgId(effectiveIdentity);
1164
+ const hasApprovalActions = Object.values(actions).some((entry) => entry.needsApproval !== undefined);
1165
+ const approvalOrgId = hasApprovalActions ? await orgIdPromise : undefined;
1166
+ const approvalPrincipal = hasApprovalActions
1167
+ ? mcpApprovalPrincipal(effectiveIdentity, approvalOrgId, requestMeta)
1168
+ : undefined;
1169
+ const approvalCallerKey = approvalPrincipal
1170
+ ? await sha256Base64Url(approvalPrincipal)
1171
+ : undefined;
1172
+ let approvalCodec;
1173
+ let approvalConfigurationError = false;
1174
+ if (hasApprovalActions && approvalPrincipal) {
1175
+ try {
1176
+ const { getAuthSecret } = await import("../server/better-auth-instance.js");
1177
+ approvalCodec = createRequestStateCodec({
1178
+ key: getAuthSecret(),
1179
+ ttlSeconds: MCP_ACTION_APPROVAL_TTL_SECONDS,
1180
+ bind: (ctx) => `${ctx.mcpReq.method}\0${approvalPrincipal}`,
1181
+ });
1182
+ }
1183
+ catch {
1184
+ // Production secret resolution deliberately throws when neither the
1185
+ // explicit Better Auth secret nor the workspace-derived A2A secret is
1186
+ // stable. Keep the MCP server available for reads, but refuse every
1187
+ // approval-gated action rather than minting replayable/ephemeral hosted
1188
+ // authorization state.
1189
+ approvalConfigurationError = true;
1190
+ }
1191
+ }
1104
1192
  const supportsMcpApps = compactMcpAppCatalog ||
1105
1193
  Object.values(advertisedActions).some((entry) => Boolean(entry.mcpApp?.resource));
1106
1194
  const server = new Server(mcpServerInfo(config, requestMeta), {
@@ -1117,13 +1205,27 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1117
1205
  }
1118
1206
  : {}),
1119
1207
  },
1208
+ // Every catalog and resource is identity-scoped and can change at runtime
1209
+ // through app configuration/HMR. Explicitly mark modern cacheable results
1210
+ // private and immediately stale; the 2026 codec adds the required
1211
+ // ttlMs/cacheScope fields while 2025 responses remain byte-compatible.
1212
+ cacheHints: {
1213
+ "server/discover": { ttlMs: 0, cacheScope: "private" },
1214
+ "tools/list": { ttlMs: 0, cacheScope: "private" },
1215
+ "resources/list": { ttlMs: 0, cacheScope: "private" },
1216
+ "resources/templates/list": { ttlMs: 0, cacheScope: "private" },
1217
+ "resources/read": { ttlMs: 0, cacheScope: "private" },
1218
+ },
1219
+ ...(approvalCodec
1220
+ ? {
1221
+ inputRequired: {
1222
+ maxRounds: 2,
1223
+ roundTimeoutMs: 10 * 60_000,
1224
+ },
1225
+ requestState: { verify: approvalCodec.verify },
1226
+ }
1227
+ : {}),
1120
1228
  });
1121
- // Resolve orgId once per request (DB lookup) so subsequent wraps are
1122
- // synchronous. The caller identity may be undefined for true dev-open —
1123
- // in that case we run with no userEmail/orgId, which makes downstream
1124
- // tools that require per-user scope return empty results rather than
1125
- // cross-tenant data (the safe default).
1126
- const orgIdPromise = resolveMcpIdentityOrgId(effectiveIdentity);
1127
1229
  /**
1128
1230
  * Wrap a callback in
1129
1231
  * `runWithRequestContext({ userEmail, orgId, requestOrigin }, fn)`.
@@ -1143,12 +1245,100 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1143
1245
  ...(requestMeta?.origin ? { requestOrigin: requestMeta.origin } : {}),
1144
1246
  }, fn);
1145
1247
  }
1248
+ async function requireMcpActionApproval(entry, name, args, ctx) {
1249
+ const verifiedState = ctx.mcpReq.requestState() ?? undefined;
1250
+ const argumentsHash = await sha256Base64Url(canonicalJson(args));
1251
+ if (verifiedState !== undefined) {
1252
+ if (verifiedState.version !== 1 ||
1253
+ typeof verifiedState.nonce !== "string" ||
1254
+ verifiedState.actionName !== name ||
1255
+ verifiedState.argumentsHash !== argumentsHash ||
1256
+ !Number.isFinite(verifiedState.expiresAt) ||
1257
+ verifiedState.expiresAt < Date.now() ||
1258
+ !approvalCallerKey) {
1259
+ return actionApprovalError(`Approval for ${name} is invalid or does not match this exact call.`);
1260
+ }
1261
+ const consumed = await consumeMcpApprovalGrant({
1262
+ nonce: verifiedState.nonce,
1263
+ callerKey: approvalCallerKey,
1264
+ actionName: name,
1265
+ argumentsHash,
1266
+ expiresAt: verifiedState.expiresAt,
1267
+ });
1268
+ if (!consumed) {
1269
+ return actionApprovalError(`Approval for ${name} is invalid, expired, or already used.`);
1270
+ }
1271
+ const approval = acceptedContent(ctx.mcpReq.inputResponses, MCP_ACTION_APPROVAL_INPUT_KEY);
1272
+ if (approval?.decision !== "approve") {
1273
+ return actionApprovalError(`${name} was not approved.`);
1274
+ }
1275
+ return undefined;
1276
+ }
1277
+ if (entry.needsApproval === undefined)
1278
+ return undefined;
1279
+ let mustApprove = false;
1280
+ try {
1281
+ mustApprove =
1282
+ typeof entry.needsApproval === "function"
1283
+ ? Boolean(await entry.needsApproval(args, {
1284
+ userEmail: getRequestUserEmail(),
1285
+ orgId: getRequestOrgId() ?? null,
1286
+ caller: "mcp",
1287
+ actionName: name,
1288
+ }))
1289
+ : entry.needsApproval === true;
1290
+ }
1291
+ catch {
1292
+ mustApprove = true;
1293
+ }
1294
+ if (!mustApprove)
1295
+ return undefined;
1296
+ if (approvalConfigurationError || !approvalCodec || !approvalCallerKey) {
1297
+ return actionApprovalError(`${name} requires approval, but secure MCP approval is not configured on this server.`);
1298
+ }
1299
+ const now = Date.now();
1300
+ const approvalState = {
1301
+ version: 1,
1302
+ nonce: globalThis.crypto.randomUUID(),
1303
+ actionName: name,
1304
+ argumentsHash,
1305
+ expiresAt: now + MCP_ACTION_APPROVAL_TTL_SECONDS * 1000,
1306
+ };
1307
+ await createMcpApprovalGrant({
1308
+ ...approvalState,
1309
+ callerKey: approvalCallerKey,
1310
+ });
1311
+ const requestState = await approvalCodec.mint(approvalState, ctx);
1312
+ return inputRequired({
1313
+ inputRequests: {
1314
+ [MCP_ACTION_APPROVAL_INPUT_KEY]: inputRequired.elicit({
1315
+ message: `Action "${name}" requires your approval before it can run. ` +
1316
+ "Review the exact tool arguments in your MCP client, then explicitly choose Approve or Deny.",
1317
+ requestedSchema: {
1318
+ type: "object",
1319
+ properties: {
1320
+ decision: {
1321
+ type: "string",
1322
+ title: `Run ${name}?`,
1323
+ description: "Approve runs this exact call once. Deny leaves it unexecuted.",
1324
+ enum: ["approve", "deny"],
1325
+ },
1326
+ },
1327
+ required: ["decision"],
1328
+ },
1329
+ }),
1330
+ },
1331
+ requestState,
1332
+ });
1333
+ }
1146
1334
  // tools/list — return all actions + ask-agent meta-tool. Wrapped in the
1147
1335
  // request context so per-user MCP visibility (mcp-client/visibility.ts)
1148
1336
  // applies to the listing too.
1149
- server.setRequestHandler(ListToolsRequestSchema, async () => {
1337
+ server.setRequestHandler("tools/list", async () => {
1150
1338
  return withCallerContext(async () => {
1151
- const tools = await Promise.all(Object.entries(advertisedActions).map(async ([name, entry]) => {
1339
+ const tools = await Promise.all(Object.entries(advertisedActions)
1340
+ .sort(([a], [b]) => compareMcpCatalogValues(a, b))
1341
+ .map(async ([name, entry]) => {
1152
1342
  const hasLink = typeof entry.link === "function";
1153
1343
  const mcpAppResource = await resolveMcpAppResourceSafely(config, name, entry, requestMeta);
1154
1344
  const rawToolMeta = entry.tool._meta &&
@@ -1159,8 +1349,8 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1159
1349
  const toolMeta = {
1160
1350
  ...rawToolMeta,
1161
1351
  // Advertisement path: only tag the tool with its inline-embed
1162
- // descriptor when the kill switch is on, so disabled embeds never
1163
- // prompt a host to render/read the `ui://` resource.
1352
+ // descriptor when the kill switch is on, so disabled embeds
1353
+ // never prompt a host to render/read the `ui://` resource.
1164
1354
  ...(mcpAppResource && requestMeta?.inlineMcpApps
1165
1355
  ? {
1166
1356
  ...openAiToolDescriptorMeta(mcpAppResource),
@@ -1173,7 +1363,8 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1173
1363
  const baseDescription = entry.tool.description ?? name;
1174
1364
  const annotations = {
1175
1365
  readOnlyHint: entry.readOnly === true,
1176
- destructiveHint: entry.publicAgent?.isConsequential === true,
1366
+ destructiveHint: entry.publicAgent?.isConsequential === true ||
1367
+ entry.needsApproval !== undefined,
1177
1368
  openWorldHint: false,
1178
1369
  };
1179
1370
  if (hasLink)
@@ -1227,13 +1418,14 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1227
1418
  },
1228
1419
  });
1229
1420
  }
1421
+ tools.sort((a, b) => compareMcpCatalogValues(a.name, b.name));
1230
1422
  return { tools };
1231
1423
  });
1232
1424
  });
1233
1425
  // tools/call — dispatch to action registry or ask-agent. Wrapped in the
1234
1426
  // request context so the action's `run(args)` and `askAgent()` execute
1235
1427
  // with the verified caller's identity, not the platform default.
1236
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
1428
+ server.setRequestHandler("tools/call", async (request, ctx) => {
1237
1429
  return withCallerContext(async () => {
1238
1430
  const { name, arguments: args } = request.params;
1239
1431
  if (name === "ask-agent" && config.askAgent) {
@@ -1309,6 +1501,9 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1309
1501
  };
1310
1502
  }
1311
1503
  try {
1504
+ const approvalResult = await requireMcpActionApproval(entry, name, args ?? {}, ctx);
1505
+ if (approvalResult !== undefined)
1506
+ return approvalResult;
1312
1507
  // We're inside `withCallerContext`, so the request-context getters
1313
1508
  // resolve the verified MCP caller's identity (do NOT inject a dev
1314
1509
  // fallback). Tag the call as an external-agent MCP dispatch.
@@ -1404,11 +1599,13 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1404
1599
  });
1405
1600
  });
1406
1601
  if (supportsMcpApps) {
1407
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
1602
+ server.setRequestHandler("resources/list", async () => {
1408
1603
  return withCallerContext(async () => {
1409
1604
  const mcpAppResources = await getMcpAppResources(config, advertisedActions, requestMeta);
1410
1605
  return {
1411
- resources: mcpAppResources.map((resource) => ({
1606
+ resources: mcpAppResources
1607
+ .sort((a, b) => compareMcpCatalogValues(a.uri, b.uri))
1608
+ .map((resource) => ({
1412
1609
  uri: resource.uri,
1413
1610
  name: resource.name,
1414
1611
  ...(resource.title ? { title: resource.title } : {}),
@@ -1421,11 +1618,13 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1421
1618
  };
1422
1619
  });
1423
1620
  });
1424
- server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
1621
+ server.setRequestHandler("resources/templates/list", async () => {
1425
1622
  return withCallerContext(async () => {
1426
1623
  const mcpAppResources = await getMcpAppResources(config, advertisedActions, requestMeta);
1427
1624
  return {
1428
- resourceTemplates: mcpAppResources.map((resource) => ({
1625
+ resourceTemplates: mcpAppResources
1626
+ .sort((a, b) => compareMcpCatalogValues(a.uri, b.uri))
1627
+ .map((resource) => ({
1429
1628
  uriTemplate: resource.uri,
1430
1629
  name: resource.name,
1431
1630
  ...(resource.title ? { title: resource.title } : {}),
@@ -1438,7 +1637,7 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1438
1637
  };
1439
1638
  });
1440
1639
  });
1441
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1640
+ server.setRequestHandler("resources/read", async (request) => {
1442
1641
  return withCallerContext(async () => {
1443
1642
  const uri = request.params?.uri;
1444
1643
  let found = null;
@@ -1457,7 +1656,7 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1457
1656
  // aborting and reporting the resource as missing.
1458
1657
  }
1459
1658
  if (!found) {
1460
- throw new Error(`MCP App resource not found: ${uri}`);
1659
+ throw new ResourceNotFoundError(String(uri ?? ""), `MCP App resource not found: ${uri}`);
1461
1660
  }
1462
1661
  return {
1463
1662
  contents: [
@@ -1465,9 +1664,7 @@ export async function createMCPServerForRequest(config, identity, requestMeta) {
1465
1664
  uri,
1466
1665
  mimeType: found.resource.mimeType,
1467
1666
  text: renderMcpAppHtml(found.resource, found.actionName, config, requestMeta),
1468
- ...(found.resource._meta
1469
- ? { _meta: found.resource._meta }
1470
- : {}),
1667
+ ...(found.resource._meta ? { _meta: found.resource._meta } : {}),
1471
1668
  },
1472
1669
  ],
1473
1670
  };