@agent-native/core 0.168.0 → 0.168.2

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 (63) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/netlify.toml +4 -4
  3. package/corpus/templates/assets/netlify.toml +4 -1
  4. package/corpus/templates/brain/netlify.toml +4 -1
  5. package/corpus/templates/calendar/netlify.toml +4 -1
  6. package/corpus/templates/chat/netlify.toml +4 -1
  7. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +32 -0
  8. package/corpus/templates/clips/netlify.toml +4 -1
  9. package/corpus/templates/content/netlify.toml +4 -1
  10. package/corpus/templates/crm/netlify.toml +6 -1
  11. package/corpus/templates/design/netlify.toml +4 -1
  12. package/corpus/templates/dispatch/netlify.toml +4 -1
  13. package/corpus/templates/factory/netlify.toml +4 -1
  14. package/corpus/templates/forms/netlify.toml +4 -1
  15. package/corpus/templates/macros/netlify.toml +3 -3
  16. package/corpus/templates/mail/actions/view-screen.ts +80 -8
  17. package/corpus/templates/mail/app/components/email/EmailList.tsx +3 -1
  18. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +1 -35
  19. package/corpus/templates/mail/app/components/email/EmailThread.tsx +16 -8
  20. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +146 -87
  21. package/corpus/templates/mail/app/global.css +21 -0
  22. package/corpus/templates/mail/app/hooks/use-emails.ts +6 -0
  23. package/corpus/templates/mail/app/hooks/use-navigation-state.ts +2 -0
  24. package/corpus/templates/mail/app/lib/inbox-tabs.ts +5 -0
  25. package/corpus/templates/mail/app/lib/label-colors.ts +47 -0
  26. package/corpus/templates/mail/app/pages/InboxPage.tsx +25 -8
  27. package/corpus/templates/mail/netlify.toml +4 -1
  28. package/corpus/templates/mail/server/handlers/emails.ts +27 -2
  29. package/corpus/templates/plan/netlify.toml +4 -1
  30. package/corpus/templates/slides/netlify.toml +4 -1
  31. package/corpus/templates/tasks/netlify.toml +6 -1
  32. package/dist/app-config/migration.d.ts +5 -0
  33. package/dist/app-config/migration.js +11 -0
  34. package/dist/app-config/schema.d.ts +4 -0
  35. package/dist/app-config/schema.js +2 -0
  36. package/dist/client/AgentPanel.d.ts +3 -1
  37. package/dist/client/AgentPanel.js +2 -2
  38. package/dist/client/agent-page/AgentTabsPage.d.ts +4 -1
  39. package/dist/client/agent-page/AgentTabsPage.js +2 -2
  40. package/dist/client/integrations/IntegrationConnectionChoice.d.ts +4 -1
  41. package/dist/client/integrations/IntegrationConnectionChoice.js +6 -2
  42. package/dist/client/onboarding/FirstRunOnboarding.js +9 -5
  43. package/dist/client/resources/McpIntegrationDialog.d.ts +3 -1
  44. package/dist/client/resources/McpIntegrationDialog.js +181 -112
  45. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  46. package/dist/client/resources/mcp-integration-catalog.js +5 -2
  47. package/dist/collab/routes.d.ts +1 -1
  48. package/dist/db/migrations.js +10 -2
  49. package/dist/deploy/build.js +6 -0
  50. package/dist/deploy/netlify-static-headers.d.ts +3 -0
  51. package/dist/deploy/netlify-static-headers.js +60 -0
  52. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  53. package/dist/localization/default-messages.d.ts +9 -0
  54. package/dist/localization/default-messages.js +9 -0
  55. package/dist/observability/routes.d.ts +6 -6
  56. package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
  57. package/dist/provider-api/actions/provider-api.d.ts +17 -17
  58. package/dist/resources/handlers.d.ts +2 -2
  59. package/dist/server/transcribe-voice.d.ts +1 -1
  60. package/dist/templates/chat/netlify.toml +4 -1
  61. package/dist/vite/client.js +2 -0
  62. package/package.json +2 -1
  63. package/src/templates/chat/netlify.toml +4 -1
@@ -8,9 +8,9 @@ import { useT } from "../i18n.js";
8
8
  import { IntegrationConnectionChoice } from "../integrations/IntegrationConnectionChoice.js";
9
9
  import { IntegrationGrid } from "../integrations/IntegrationGrid.js";
10
10
  import { cn } from "../utils.js";
11
- import { buildMcpOAuthStartUrl, createMcpIntegrationFormDefaults, filterMcpIntegrations, getMcpIntegrationApiFallback, getDefaultMcpIntegrations, isCustomMcpIntegrationEnabled, navigateToMcpOAuthStart, resolveMcpIntegrationScope, shouldOfferMcpIntegrationOrganizationScope, shouldOfferMcpOrganizationScope, } from "./mcp-integration-catalog.js";
11
+ import { buildMcpOAuthStartUrl, createMcpIntegrationFormDefaults, filterMcpIntegrations, getMcpIntegrationApiFallback, getDefaultMcpIntegrations, isCustomMcpIntegrationEnabled, navigateToMcpOAuthStart, resolveMcpIntegrationScope, shouldOfferMcpIntegrationOrganizationScope, shouldOfferMcpOrganizationScope, supportsMcpIntegrationOrganizationScope, } from "./mcp-integration-catalog.js";
12
12
  import { McpIntegrationLogo } from "./McpIntegrationLogo.js";
13
- import { formatMcpServerError, getMcpUrlValidationError, useMcpServersApi, useMcpServers, } from "./use-mcp-servers.js";
13
+ import { formatMcpServerError, formatMcpServersLoadError, getMcpUrlValidationError, useMcpServersApi, useMcpServers, } from "./use-mcp-servers.js";
14
14
  function parseHeaderLines(text) {
15
15
  const out = {};
16
16
  for (const line of text.split(/\r?\n/)) {
@@ -49,7 +49,7 @@ function resolveIntegrationScope(integration, defaultScope, hasOrg, canCreateOrg
49
49
  (integration.supportsOrganizationScope === true &&
50
50
  integration.managedOAuth !== true));
51
51
  }
52
- export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId = null, connectIntegrationId = null, quickConnectIntegrationId = null, defaultScope, canCreateOrgMcp, hasOrg, onCreateMcpServer, onCreated, integrations, }) {
52
+ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId = null, connectIntegrationId = null, quickConnectIntegrationId = null, defaultScope, canCreateOrgMcp, hasOrg, onCreateMcpServer, onOAuthStart, oauthReturnPath, onCreated, integrations, }) {
53
53
  const t = useT();
54
54
  const [mode, setMode] = useState("catalog");
55
55
  const [query, setQuery] = useState("");
@@ -89,11 +89,21 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
89
89
  useEffect(() => {
90
90
  if (!open)
91
91
  return;
92
+ if (initialIntegrationId && !mcpServersQuery.isSuccess)
93
+ return;
92
94
  const initialIntegration = initialIntegrationId
93
95
  ? defaultIntegrations.find((integration) => integration.id === initialIntegrationId)
94
96
  : null;
95
97
  const initialDefaults = createMcpIntegrationFormDefaults(initialIntegration);
96
- setMode(initialIntegration || !showCatalog ? "form" : "catalog");
98
+ const initialNeedsScopeChoice = Boolean(initialIntegration &&
99
+ hasOrg &&
100
+ requiresMcpIntegrationSetup(initialIntegration) &&
101
+ supportsMcpIntegrationOrganizationScope(initialIntegration));
102
+ setMode(initialNeedsScopeChoice
103
+ ? "choice"
104
+ : initialIntegration || !showCatalog
105
+ ? "form"
106
+ : "catalog");
97
107
  setQuery("");
98
108
  setSelected(initialIntegration ?? null);
99
109
  setScope(resolveIntegrationScope(initialIntegration, defaultScope, hasOrg, canCreateOrgMcp));
@@ -114,6 +124,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
114
124
  open,
115
125
  safeDefaultScope,
116
126
  showCatalog,
127
+ mcpServersQuery.isSuccess,
117
128
  ]);
118
129
  useEffect(() => {
119
130
  if (open && mode === "form") {
@@ -147,18 +158,31 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
147
158
  return;
148
159
  }
149
160
  setBusy(true);
150
- const returnUrl = typeof window === "undefined"
151
- ? "/"
152
- : window.location.pathname +
153
- window.location.search +
154
- window.location.hash;
155
- navigateToMcpOAuthStart(agentNativePath(buildMcpOAuthStartUrl({
161
+ const returnUrl = oauthReturnPath?.startsWith("/")
162
+ ? oauthReturnPath
163
+ : typeof window === "undefined"
164
+ ? "/"
165
+ : window.location.pathname +
166
+ window.location.search +
167
+ window.location.hash;
168
+ const oauthUrl = agentNativePath(buildMcpOAuthStartUrl({
156
169
  name: args.name,
157
170
  url: args.url,
158
171
  description: args.description,
159
172
  scope: options?.scope ?? scope,
160
173
  returnUrl,
161
- })));
174
+ }));
175
+ if (!onOAuthStart) {
176
+ navigateToMcpOAuthStart(oauthUrl);
177
+ return;
178
+ }
179
+ void Promise.resolve()
180
+ .then(() => onOAuthStart(oauthUrl))
181
+ .then(() => onOpenChange(false))
182
+ .catch((cause) => {
183
+ setBusy(false);
184
+ setError(formatMcpServerError(cause));
185
+ });
162
186
  };
163
187
  const connectWithOAuth = (integration, options) => beginOAuth({
164
188
  name: integration.name,
@@ -218,6 +242,11 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
218
242
  });
219
243
  };
220
244
  const quickConnect = (integration) => {
245
+ if (hasOrg) {
246
+ setSelected(integration);
247
+ setMode("choice");
248
+ return;
249
+ }
221
250
  if (requiresMcpIntegrationSetup(integration)) {
222
251
  openForm(integration);
223
252
  return;
@@ -240,6 +269,13 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
240
269
  });
241
270
  };
242
271
  const selectCatalogConnection = (integration) => {
272
+ if (!mcpServersQuery.isSuccess)
273
+ return;
274
+ if (hasOrg) {
275
+ setSelected(integration);
276
+ setMode("choice");
277
+ return;
278
+ }
243
279
  if (requiresMcpIntegrationSetup(integration)) {
244
280
  const apiFallback = getMcpIntegrationApiFallback(integration);
245
281
  if (apiFallback) {
@@ -250,11 +286,6 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
250
286
  }
251
287
  return;
252
288
  }
253
- if (shouldOfferMcpIntegrationOrganizationScope(integration, hasOrg, canCreateOrgMcp)) {
254
- setSelected(integration);
255
- setMode("choice");
256
- return;
257
- }
258
289
  quickConnect(integration);
259
290
  };
260
291
  quickConnectRef.current = quickConnect;
@@ -267,15 +298,30 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
267
298
  quickConnectAttemptedRef.current === quickConnectIntegrationId) {
268
299
  return;
269
300
  }
301
+ if (mcpServersQuery.isError)
302
+ return;
303
+ if (!mcpServersQuery.isSuccess)
304
+ return;
270
305
  const integration = defaultIntegrations.find((candidate) => candidate.id === quickConnectIntegrationId);
271
306
  if (!integration)
272
307
  return;
273
308
  quickConnectAttemptedRef.current = quickConnectIntegrationId;
274
309
  quickConnectRef.current?.(integration);
275
- }, [defaultIntegrations, open, quickConnectIntegrationId]);
310
+ }, [
311
+ defaultIntegrations,
312
+ hasOrg,
313
+ mcpServersQuery.isError,
314
+ mcpServersQuery.isSuccess,
315
+ open,
316
+ quickConnectIntegrationId,
317
+ ]);
276
318
  useEffect(() => {
277
319
  if (!open || !connectIntegrationId)
278
320
  return;
321
+ if (mcpServersQuery.isError)
322
+ return;
323
+ if (!mcpServersQuery.isSuccess)
324
+ return;
279
325
  const integration = defaultIntegrations.find((candidate) => candidate.id === connectIntegrationId);
280
326
  if (!integration)
281
327
  return;
@@ -283,25 +329,36 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
283
329
  if (quickConnectAttemptedRef.current === attemptKey)
284
330
  return;
285
331
  quickConnectAttemptedRef.current = attemptKey;
332
+ if (hasOrg) {
333
+ setSelected(integration);
334
+ setMode("choice");
335
+ return;
336
+ }
286
337
  if (requiresMcpIntegrationSetup(integration)) {
287
338
  openForm(integration);
288
339
  return;
289
340
  }
290
- const showWorkspaceChoice = shouldOfferMcpIntegrationOrganizationScope(integration, hasOrg, canCreateOrgMcp);
291
- if (!showWorkspaceChoice) {
292
- quickConnectRef.current?.(integration);
293
- }
294
- else {
295
- setMode("choice");
296
- }
341
+ quickConnectRef.current?.(integration);
297
342
  }, [
298
343
  canCreateOrgMcp,
299
344
  connectIntegrationId,
300
345
  defaultIntegrations,
301
346
  hasOrg,
347
+ mcpServersQuery.isError,
348
+ mcpServersQuery.isSuccess,
302
349
  open,
303
350
  ]);
304
351
  const connectPersonal = (integration) => {
352
+ if (requiresMcpIntegrationSetup(integration)) {
353
+ const apiFallback = getMcpIntegrationApiFallback(integration);
354
+ if (apiFallback) {
355
+ openAgentSettings(`secrets:${apiFallback.secretKey}`);
356
+ }
357
+ else {
358
+ openForm(integration, { scope: "user" });
359
+ }
360
+ return;
361
+ }
305
362
  if (integration.authMode === "oauth") {
306
363
  connectWithOAuth(integration, { scope: "user" });
307
364
  return;
@@ -319,6 +376,12 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
319
376
  openForm(integration, { scope: "user" });
320
377
  };
321
378
  const connectWorkspace = (integration) => {
379
+ if (!canCreateOrgMcp)
380
+ return;
381
+ if (requiresMcpIntegrationSetup(integration)) {
382
+ openForm(integration, { scope: "org" });
383
+ return;
384
+ }
322
385
  if (integration.authMode === "oauth") {
323
386
  connectWithOAuth(integration, { scope: "org" });
324
387
  return;
@@ -384,93 +447,99 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
384
447
  };
385
448
  if (!showCatalog && !customIntegrationEnabled)
386
449
  return null;
387
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsx(DialogContent, { "aria-describedby": undefined, className: "inset-0 flex h-[100dvh] max-h-none w-full max-w-none translate-x-0 translate-y-0 flex-col gap-0 overflow-hidden rounded-none p-0", children: mode === "choice" && selected ? (_jsxs(_Fragment, { children: [_jsx(DialogHeader, { className: "sr-only", children: _jsxs(DialogTitle, { children: ["Connect ", selected.name] }) }), _jsx(IntegrationConnectionChoice, { name: selected.name, logo: _jsx(McpIntegrationLogo, { name: selected.name, logoUrl: selected.logoUrl, integrationId: selected.id, className: "size-7 rounded-md", imageClassName: "size-full p-1" }), showWorkspaceOption: shouldOfferMcpIntegrationOrganizationScope(selected, hasOrg, canCreateOrgMcp), busy: busy, onPersonal: () => connectPersonal(selected), onWorkspace: () => connectWorkspace(selected) })] })) : mode === "catalog" ? (_jsxs(_Fragment, { children: [_jsxs(DialogHeader, { className: "shrink-0 border-b border-border px-7 pb-5 pe-14 pt-7 sm:px-10", children: [_jsx(DialogTitle, { children: t("mcpIntegrations.title") }), _jsx(DialogDescription, { children: t("mcpIntegrations.description", {
388
- count: defaultIntegrations.length,
389
- }) })] }), _jsx("div", { className: "shrink-0 px-7 pb-5 sm:px-10", children: _jsxs("div", { className: "mx-auto flex w-full max-w-5xl flex-col gap-3 sm:flex-row", children: [_jsxs("label", { className: "relative min-w-0 flex-1", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { value: query, onChange: (event) => setQuery(event.target.value), className: "h-9 w-full rounded-md border border-border bg-background pe-3 ps-8 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.searchPlaceholder") })] }), _jsx("button", { type: "button", onClick: () => openForm(null), className: cn("inline-flex h-9 items-center justify-center gap-1.5 rounded-md border border-border bg-background px-3 text-[12px] font-medium text-foreground hover:bg-accent", !customIntegrationEnabled && "hidden"), children: t("mcpIntegrations.addYourOwn") })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-7 pb-10 pt-7 sm:px-10", children: _jsxs("div", { className: "mx-auto w-full max-w-5xl", children: [error && (_jsx("div", { className: "mb-3 rounded-md border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-relaxed text-destructive", children: error })), _jsx(IntegrationGrid, { items: filteredIntegrations.map((integration) => {
390
- const connected = connectedUrls.has(compareUrl(integration.url));
391
- const setupOnly = requiresMcpIntegrationSetup(integration);
392
- const apiFallback = getMcpIntegrationApiFallback(integration);
393
- return {
394
- id: integration.id,
395
- name: integration.name,
396
- description: t(integration.descriptionKey),
397
- logo: (_jsx(McpIntegrationLogo, { name: integration.name, logoUrl: integration.logoUrl, integrationId: integration.id, className: "size-7 rounded-md", imageClassName: "size-full p-1" })),
398
- status: connected
399
- ? t("mcpIntegrations.connected")
400
- : setupOnly
401
- ? t("mcpIntegrations.status.setupRequired")
402
- : undefined,
403
- statusClassName: connected
404
- ? "text-emerald-600 dark:text-emerald-400"
405
- : "text-muted-foreground",
406
- actionLabel: connected
407
- ? "Manage"
408
- : setupOnly && apiFallback
409
- ? t("mcpIntegrations.useApiToken")
450
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { "aria-describedby": undefined, className: "inset-0 flex h-[100dvh] max-h-none w-full max-w-none translate-x-0 translate-y-0 flex-col gap-0 overflow-hidden rounded-none p-0", children: [mcpServersQuery.isError ? (_jsxs("div", { role: "alert", className: "mx-7 mt-4 shrink-0 rounded-md border border-destructive/20 bg-destructive/5 px-3 py-2 text-xs text-destructive sm:mx-10", children: [_jsx("p", { children: formatMcpServersLoadError(mcpServersQuery.error) }), _jsx("button", { type: "button", onClick: () => void mcpServersQuery.refetch(), disabled: mcpServersQuery.isFetching, className: "mt-2 font-medium underline underline-offset-2 hover:text-foreground disabled:cursor-wait disabled:opacity-60", children: mcpServersQuery.isFetching
451
+ ? t("mcpIntegrations.retrying")
452
+ : t("mcpIntegrations.retry") })] })) : null, mode === "choice" && selected ? (_jsxs(_Fragment, { children: [_jsx(DialogHeader, { className: "sr-only", children: _jsxs(DialogTitle, { children: [t("mcpIntegrations.connect"), " ", selected.name] }) }), _jsx(IntegrationConnectionChoice, { name: selected.name, logo: _jsx(McpIntegrationLogo, { name: selected.name, logoUrl: selected.logoUrl, integrationId: selected.id, className: "size-7 rounded-md", imageClassName: "size-full p-1" }), showWorkspaceOption: supportsMcpIntegrationOrganizationScope(selected), workspaceOptionDisabled: !canCreateOrgMcp, workspaceOptionDisabledReason: !canCreateOrgMcp
453
+ ? t("mcpIntegrations.workspaceAdminRequired")
454
+ : undefined, personalOnlyReason: !supportsMcpIntegrationOrganizationScope(selected)
455
+ ? t("mcpIntegrations.personalOnlyDescription")
456
+ : undefined, busy: busy, onPersonal: () => connectPersonal(selected), onWorkspace: () => connectWorkspace(selected) })] })) : mode === "catalog" ? (_jsxs(_Fragment, { children: [_jsxs(DialogHeader, { className: "shrink-0 border-b border-border px-7 pb-5 pe-14 pt-7 sm:px-10", children: [_jsx(DialogTitle, { children: t("mcpIntegrations.title") }), _jsx(DialogDescription, { children: t("mcpIntegrations.description", {
457
+ count: defaultIntegrations.length,
458
+ }) })] }), _jsx("div", { className: "shrink-0 px-7 pb-5 sm:px-10", children: _jsxs("div", { className: "mx-auto flex w-full max-w-5xl flex-col gap-3 sm:flex-row", children: [_jsxs("label", { className: "relative min-w-0 flex-1", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { value: query, onChange: (event) => setQuery(event.target.value), className: "h-9 w-full rounded-md border border-border bg-background pe-3 ps-8 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.searchPlaceholder") })] }), _jsx("button", { type: "button", onClick: () => openForm(null), className: cn("inline-flex h-9 items-center justify-center gap-1.5 rounded-md border border-border bg-background px-3 text-[12px] font-medium text-foreground hover:bg-accent", !customIntegrationEnabled && "hidden"), children: t("mcpIntegrations.addYourOwn") })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-7 pb-10 pt-7 sm:px-10", children: _jsxs("div", { className: "mx-auto w-full max-w-5xl", children: [error && (_jsx("div", { className: "mb-3 rounded-md border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-relaxed text-destructive", children: error })), !mcpServersQuery.isSuccess && !mcpServersQuery.isError ? (_jsx("div", { role: "status", className: "mb-3 rounded-md border border-border bg-muted/30 px-3 py-2 text-[12px] leading-relaxed text-muted-foreground", children: t("mcpIntegrations.loadingScopeMetadata") })) : null, _jsx(IntegrationGrid, { items: filteredIntegrations.map((integration) => {
459
+ const connected = connectedUrls.has(compareUrl(integration.url));
460
+ const setupOnly = requiresMcpIntegrationSetup(integration);
461
+ const apiFallback = getMcpIntegrationApiFallback(integration);
462
+ return {
463
+ id: integration.id,
464
+ name: integration.name,
465
+ description: t(integration.descriptionKey),
466
+ logo: (_jsx(McpIntegrationLogo, { name: integration.name, logoUrl: integration.logoUrl, integrationId: integration.id, className: "size-7 rounded-md", imageClassName: "size-full p-1" })),
467
+ status: connected
468
+ ? t("mcpIntegrations.connected")
410
469
  : setupOnly
411
- ? t("mcpIntegrations.viewSetup")
412
- : t("mcpIntegrations.connect"),
413
- disabled: connected || busy,
414
- onAction: () => {
415
- if (connected) {
416
- openForm(integration);
417
- return;
418
- }
419
- selectCatalogConnection(integration);
420
- },
421
- };
422
- }), emptyLabel: t("mcpIntegrations.noMatches") })] }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(DialogHeader, { className: "shrink-0 border-b border-border px-7 pb-5 pe-14 pt-7 sm:px-10", children: [_jsxs("button", { type: "button", onClick: () => {
423
- clearFeedback();
424
- setMode("catalog");
425
- }, className: cn("mb-1 inline-flex w-fit items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground", !showCatalog && "hidden"), children: [_jsx(IconArrowLeft, { className: "h-3 w-3 rtl:-scale-x-100" }), t("mcpIntegrations.backToIntegrations")] }), _jsx(DialogTitle, { children: selected
426
- ? selectedRequiresSetup
427
- ? t("mcpIntegrations.setupTitle", {
428
- name: selected.name,
429
- })
430
- : t("mcpIntegrations.configureTitle", {
431
- name: selected.name,
432
- })
433
- : t("mcpIntegrations.customTitle") }), _jsx(DialogDescription, { children: selected
434
- ? selectedRequiresSetup
435
- ? t("mcpIntegrations.providerSetupFormDescription")
436
- : selected.authMode === "none"
437
- ? t("mcpIntegrations.presetNoAuthDescription")
438
- : t("mcpIntegrations.presetAuthDescription")
439
- : t("mcpIntegrations.customDescription") })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-7 py-7 sm:px-10", children: _jsxs("div", { className: "mx-auto max-w-2xl space-y-3", children: [renderScopeSelector(), selected?.setupNoteKey && !selectedRequiresSetup ? (_jsx("div", { className: "rounded-md bg-muted/40 px-3 py-2 text-[11px] leading-relaxed text-muted-foreground", children: t(selected.setupNoteKey) })) : null, selectedRequiresSetup && selected && (_jsxs("div", { className: "mx-auto grid w-full max-w-xl gap-4 py-8", children: [_jsxs("div", { children: [_jsx("p", { className: "text-base font-semibold tracking-[-0.02em] text-foreground", children: t("mcpIntegrations.providerSetupRequired") }), _jsx("p", { className: "mt-1 text-sm leading-6 text-muted-foreground", children: t("mcpIntegrations.providerSetupDescription", {
440
- name: selected.name,
441
- }) })] }), selected.setupNoteKey ? (_jsx("p", { className: "text-sm leading-6 text-muted-foreground", children: t(selected.setupNoteKey) })) : null, selected.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex w-fit items-center gap-1 text-sm font-medium text-foreground underline underline-offset-4 hover:text-muted-foreground", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "size-3.5" })] })) : null] })), !selected && (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md bg-muted/30 px-3 py-2 text-[11px]", children: [_jsx("span", { className: "text-muted-foreground", children: customAuthMode === "oauth"
442
- ? t(
443
- /* i18n-key-ignore */
444
- "mcpIntegrations.customOAuthDefault", { defaultValue: "Sign in with OAuth" })
445
- : t(
446
- /* i18n-key-ignore */
447
- "mcpIntegrations.customHeadersMode", { defaultValue: "Use an API key" }) }), _jsx("button", { type: "button", onClick: () => {
448
- setCustomAuthMode((current) => current === "oauth" ? "headers" : "oauth");
449
- clearFeedback();
450
- }, className: "font-medium text-foreground underline underline-offset-2 hover:text-muted-foreground", children: customAuthMode === "oauth"
451
- ? t(
452
- /* i18n-key-ignore */
453
- "mcpIntegrations.useApiKeyInstead", { defaultValue: "Use an API key instead" })
454
- : t(
455
- /* i18n-key-ignore */
456
- "mcpIntegrations.useOAuthInstead", { defaultValue: "Use OAuth instead" }) })] })), !selectedRequiresSetup && (_jsxs(_Fragment, { children: [selected?.authMode === "oauth" && (_jsx("div", { className: "rounded-md border border-primary/20 bg-primary/5 px-3 py-2 text-[11px] leading-relaxed text-primary", children: t("mcpIntegrations.oauthNotice") })), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.serverName") }), _jsx("input", { ref: inputRef, value: name, onChange: (event) => {
457
- setName(event.target.value);
458
- clearFeedback();
459
- }, className: "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.serverNamePlaceholder") })] }), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.url") }), _jsx("input", { value: url, onChange: (event) => {
460
- setUrl(event.target.value);
461
- clearFeedback();
462
- }, className: "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.urlPlaceholder") })] }), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.fieldDescription") }), _jsx("input", { value: description, onChange: (event) => {
463
- setDescription(event.target.value);
464
- clearFeedback();
465
- }, className: "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.descriptionPlaceholder") })] }), (selected
466
- ? selected.authMode !== "oauth"
467
- : customAuthMode === "headers") && (_jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.headers") }), _jsx("textarea", { value: headersText, onChange: (event) => {
468
- setHeadersText(event.target.value);
469
- clearFeedback();
470
- }, rows: 3, className: "w-full resize-y rounded-md border border-border bg-background px-2.5 py-1.5 text-[12px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", style: {
471
- fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
472
- }, placeholder: selected?.headerPlaceholder ??
473
- t("mcpIntegrations.headersPlaceholder") })] })), selected?.docsUrl && (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-[11px] text-muted-foreground underline hover:text-foreground", children: [t("mcpIntegrations.openSetupDocs"), _jsx(IconExternalLink, { className: "h-3 w-3" })] }))] })), testResult && (_jsxs("div", { className: cn("flex items-start gap-1 rounded-md px-3 py-2 text-[11px] leading-snug", testResult.ok
474
- ? "bg-green-500/5 text-green-600 dark:text-green-400"
475
- : "bg-red-500/5 text-red-600 dark:text-red-400"), children: [testResult.ok && (_jsx(IconCheck, { className: "mt-0.5 h-3 w-3 shrink-0" })), _jsx("span", { className: "min-w-0 break-words", children: testResult.message })] })), error && (_jsx("div", { className: "break-words rounded-md bg-red-500/5 px-3 py-2 text-[11px] leading-snug text-red-600 dark:text-red-400", children: error }))] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-t border-border px-7 py-4", children: [!selectedRequiresSetup && (_jsx("button", { type: "button", onClick: runTest, disabled: !url.trim() || busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: t("mcpIntegrations.test") })), !selected && customAuthMode === "oauth" ? (_jsxs("button", { type: "button", onClick: connectCustomWithOAuth, disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "me-1.5 inline h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.connectWithOAuth")] })) : null, selectedRequiresSetup ? (_jsxs("div", { className: "ms-auto flex items-center gap-2", children: [selected?.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, selected?.authMode === "oauth" && (_jsxs("button", { type: "button", onClick: () => connectWithOAuth(selected), disabled: busy, "aria-busy": busy, className: "inline-flex min-w-[132px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.continueToConnect")] }))] })) : selected?.authMode === "oauth" ? (_jsxs("button", { type: "button", onClick: () => connectWithOAuth(selected), disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && _jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), t("mcpIntegrations.connectWithOAuth")] })) : !selected && customAuthMode === "oauth" ? null : (_jsxs("button", { type: "button", onClick: submitForm, disabled: !name.trim() || !url.trim() || busy, className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && _jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), t("mcpIntegrations.connect")] }))] })] })) }) }));
470
+ ? t("mcpIntegrations.status.setupRequired")
471
+ : undefined,
472
+ statusClassName: connected
473
+ ? "text-emerald-600 dark:text-emerald-400"
474
+ : "text-muted-foreground",
475
+ actionLabel: connected
476
+ ? "Manage"
477
+ : setupOnly && apiFallback
478
+ ? t("mcpIntegrations.useApiToken")
479
+ : setupOnly
480
+ ? t("mcpIntegrations.viewSetup")
481
+ : t("mcpIntegrations.connect"),
482
+ disabled: connected || busy || !mcpServersQuery.isSuccess,
483
+ onAction: () => {
484
+ if (connected) {
485
+ openForm(integration);
486
+ return;
487
+ }
488
+ selectCatalogConnection(integration);
489
+ },
490
+ };
491
+ }), emptyLabel: t("mcpIntegrations.noMatches") })] }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(DialogHeader, { className: "shrink-0 border-b border-border px-7 pb-5 pe-14 pt-7 sm:px-10", children: [_jsxs("button", { type: "button", onClick: () => {
492
+ clearFeedback();
493
+ setMode("catalog");
494
+ }, className: cn("mb-1 inline-flex w-fit items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground", !showCatalog && "hidden"), children: [_jsx(IconArrowLeft, { className: "h-3 w-3 rtl:-scale-x-100" }), t("mcpIntegrations.backToIntegrations")] }), _jsx(DialogTitle, { children: selected
495
+ ? selectedRequiresSetup
496
+ ? t("mcpIntegrations.setupTitle", {
497
+ name: selected.name,
498
+ })
499
+ : t("mcpIntegrations.configureTitle", {
500
+ name: selected.name,
501
+ })
502
+ : t("mcpIntegrations.customTitle") }), _jsx(DialogDescription, { children: selected
503
+ ? selectedRequiresSetup
504
+ ? t("mcpIntegrations.providerSetupFormDescription")
505
+ : selected.authMode === "none"
506
+ ? t("mcpIntegrations.presetNoAuthDescription")
507
+ : t("mcpIntegrations.presetAuthDescription")
508
+ : t("mcpIntegrations.customDescription") })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-7 py-7 sm:px-10", children: _jsxs("div", { className: "mx-auto max-w-2xl space-y-3", children: [renderScopeSelector(), selected?.setupNoteKey && !selectedRequiresSetup ? (_jsx("div", { className: "rounded-md bg-muted/40 px-3 py-2 text-[11px] leading-relaxed text-muted-foreground", children: t(selected.setupNoteKey) })) : null, selectedRequiresSetup && selected && (_jsxs("div", { className: "mx-auto grid w-full max-w-xl gap-4 py-8", children: [_jsxs("div", { children: [_jsx("p", { className: "text-base font-semibold tracking-[-0.02em] text-foreground", children: t("mcpIntegrations.providerSetupRequired") }), _jsx("p", { className: "mt-1 text-sm leading-6 text-muted-foreground", children: t("mcpIntegrations.providerSetupDescription", {
509
+ name: selected.name,
510
+ }) })] }), selected.setupNoteKey ? (_jsx("p", { className: "text-sm leading-6 text-muted-foreground", children: t(selected.setupNoteKey) })) : null, selected.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex w-fit items-center gap-1 text-sm font-medium text-foreground underline underline-offset-4 hover:text-muted-foreground", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "size-3.5" })] })) : null] })), !selected && (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md bg-muted/30 px-3 py-2 text-[11px]", children: [_jsx("span", { className: "text-muted-foreground", children: customAuthMode === "oauth"
511
+ ? t(
512
+ /* i18n-key-ignore */
513
+ "mcpIntegrations.customOAuthDefault", { defaultValue: "Sign in with OAuth" })
514
+ : t(
515
+ /* i18n-key-ignore */
516
+ "mcpIntegrations.customHeadersMode", { defaultValue: "Use an API key" }) }), _jsx("button", { type: "button", onClick: () => {
517
+ setCustomAuthMode((current) => current === "oauth" ? "headers" : "oauth");
518
+ clearFeedback();
519
+ }, className: "font-medium text-foreground underline underline-offset-2 hover:text-muted-foreground", children: customAuthMode === "oauth"
520
+ ? t(
521
+ /* i18n-key-ignore */
522
+ "mcpIntegrations.useApiKeyInstead", { defaultValue: "Use an API key instead" })
523
+ : t(
524
+ /* i18n-key-ignore */
525
+ "mcpIntegrations.useOAuthInstead", { defaultValue: "Use OAuth instead" }) })] })), !selectedRequiresSetup && (_jsxs(_Fragment, { children: [selected?.authMode === "oauth" && (_jsx("div", { className: "rounded-md border border-primary/20 bg-primary/5 px-3 py-2 text-[11px] leading-relaxed text-primary", children: t("mcpIntegrations.oauthNotice") })), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.serverName") }), _jsx("input", { ref: inputRef, value: name, onChange: (event) => {
526
+ setName(event.target.value);
527
+ clearFeedback();
528
+ }, className: "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.serverNamePlaceholder") })] }), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.url") }), _jsx("input", { value: url, onChange: (event) => {
529
+ setUrl(event.target.value);
530
+ clearFeedback();
531
+ }, className: "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.urlPlaceholder") })] }), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.fieldDescription") }), _jsx("input", { value: description, onChange: (event) => {
532
+ setDescription(event.target.value);
533
+ clearFeedback();
534
+ }, className: "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", placeholder: t("mcpIntegrations.descriptionPlaceholder") })] }), (selected
535
+ ? selected.authMode !== "oauth"
536
+ : customAuthMode === "headers") && (_jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1 block text-[10px] font-medium text-muted-foreground", children: t("mcpIntegrations.headers") }), _jsx("textarea", { value: headersText, onChange: (event) => {
537
+ setHeadersText(event.target.value);
538
+ clearFeedback();
539
+ }, rows: 3, className: "w-full resize-y rounded-md border border-border bg-background px-2.5 py-1.5 text-[12px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-ring", style: {
540
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
541
+ }, placeholder: selected?.headerPlaceholder ??
542
+ t("mcpIntegrations.headersPlaceholder") })] })), selected?.docsUrl && (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-[11px] text-muted-foreground underline hover:text-foreground", children: [t("mcpIntegrations.openSetupDocs"), _jsx(IconExternalLink, { className: "h-3 w-3" })] }))] })), testResult && (_jsxs("div", { className: cn("flex items-start gap-1 rounded-md px-3 py-2 text-[11px] leading-snug", testResult.ok
543
+ ? "bg-green-500/5 text-green-600 dark:text-green-400"
544
+ : "bg-red-500/5 text-red-600 dark:text-red-400"), children: [testResult.ok && (_jsx(IconCheck, { className: "mt-0.5 h-3 w-3 shrink-0" })), _jsx("span", { className: "min-w-0 break-words", children: testResult.message })] })), error && (_jsx("div", { className: "break-words rounded-md bg-red-500/5 px-3 py-2 text-[11px] leading-snug text-red-600 dark:text-red-400", children: error }))] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-t border-border px-7 py-4", children: [!selectedRequiresSetup && (_jsx("button", { type: "button", onClick: runTest, disabled: !url.trim() || busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: t("mcpIntegrations.test") })), !selected && customAuthMode === "oauth" ? (_jsxs("button", { type: "button", onClick: connectCustomWithOAuth, disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "me-1.5 inline h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.connectWithOAuth")] })) : null, selectedRequiresSetup ? (_jsxs("div", { className: "ms-auto flex items-center gap-2", children: [selected?.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, selected?.authMode === "oauth" && (_jsxs("button", { type: "button", onClick: () => connectWithOAuth(selected), disabled: busy, "aria-busy": busy, className: "inline-flex min-w-[132px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.continueToConnect")] }))] })) : selected?.authMode === "oauth" ? (_jsxs("button", { type: "button", onClick: () => connectWithOAuth(selected), disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && _jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), t("mcpIntegrations.connectWithOAuth")] })) : !selected && customAuthMode === "oauth" ? null : (_jsxs("button", { type: "button", onClick: submitForm, disabled: !name.trim() || !url.trim() || busy, className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && _jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), t("mcpIntegrations.connect")] }))] })] }))] }) }));
476
545
  }
@@ -71,6 +71,7 @@ export declare function buildMcpOAuthStartUrl({ name, url, description, scope, r
71
71
  export declare function navigateToMcpOAuthStart(url: string): void;
72
72
  export declare function resolveMcpIntegrationScope(defaultScope: "user" | "org", hasOrg: boolean, canCreateOrgMcp: boolean, supportsOrganizationScope?: boolean): "user" | "org";
73
73
  export declare function shouldOfferMcpOrganizationScope(hasOrg: boolean, canCreateOrgMcp: boolean): boolean;
74
+ export declare function supportsMcpIntegrationOrganizationScope(integration: DefaultMcpIntegration): boolean;
74
75
  export declare function shouldOfferMcpIntegrationOrganizationScope(integration: DefaultMcpIntegration, hasOrg: boolean, canCreateOrgMcp: boolean): boolean;
75
76
  export declare function filterMcpIntegrations(query: string, integrations?: DefaultMcpIntegration[]): DefaultMcpIntegration[];
76
77
  export declare function findMcpIntegrationForText(text: string, integrations?: DefaultMcpIntegration[]): DefaultMcpIntegration | null;
@@ -863,10 +863,13 @@ export function resolveMcpIntegrationScope(defaultScope, hasOrg, canCreateOrgMcp
863
863
  export function shouldOfferMcpOrganizationScope(hasOrg, canCreateOrgMcp) {
864
864
  return hasOrg && canCreateOrgMcp;
865
865
  }
866
+ export function supportsMcpIntegrationOrganizationScope(integration) {
867
+ return (integration.supportsOrganizationScope === true &&
868
+ integration.managedOAuth !== true);
869
+ }
866
870
  export function shouldOfferMcpIntegrationOrganizationScope(integration, hasOrg, canCreateOrgMcp) {
867
871
  return (shouldOfferMcpOrganizationScope(hasOrg, canCreateOrgMcp) &&
868
- integration.supportsOrganizationScope === true &&
869
- integration.managedOAuth !== true);
872
+ supportsMcpIntegrationOrganizationScope(integration));
870
873
  }
871
874
  export function filterMcpIntegrations(query, integrations = getDefaultMcpIntegrations()) {
872
875
  const needle = query.trim().toLowerCase();
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -1,3 +1,4 @@
1
+ import { getAppConfig } from "../app-config/index.js";
1
2
  import { getDbExec, createDbExec, isPostgres, getDialect, getMigrationDatabaseUrl, getCloudflareD1Binding, retrySqliteBusy, } from "./client.js";
2
3
  import { isMigrationAuthorizedRuntime } from "./migration-runtime.js";
3
4
  // ---------------------------------------------------------------------------
@@ -250,8 +251,15 @@ function isServerlessRequestRuntime() {
250
251
  * migration skipping is safe only when that runner owns schema setup.
251
252
  */
252
253
  function appMigratesAtRelease() {
253
- const raw = process.env.AGENT_NATIVE_RELEASE_MIGRATIONS?.trim();
254
- return !!raw && ["1", "true", "yes", "on"].includes(raw.toLowerCase());
254
+ const { migration } = getAppConfig();
255
+ if (migration.releaseMigrations)
256
+ return true;
257
+ // Clips beta uses the production-owned schema but its masked prebuilt build
258
+ // must not run migrate:production against the fake build database. The
259
+ // owner marker is embedded into the deployed server bundle and therefore
260
+ // carries the same request-path skip decision without pretending the masked
261
+ // build performed the release migration.
262
+ return migration.betaSchemaOwner?.toLowerCase() === "production";
255
263
  }
256
264
  /**
257
265
  * Run an explicit release-time migration job with migration duty enabled.
@@ -34,6 +34,7 @@ import { generateActionRegistryForProject } from "../vite/action-types-plugin.js
34
34
  import { createAgentNativeConfigContext, loadResolvedAgentNativeConfig, } from "../vite/agent-native-config-loader.js";
35
35
  import { cloneServerBundleForFunction, copyDir } from "./function-bundle.js";
36
36
  import { collectImmutableAssetPaths, IMMUTABLE_ASSET_CACHE_CONTROL, IMMUTABLE_ASSET_CACHE_HEADERS, prefixAssetPath, } from "./immutable-assets.js";
37
+ import { writeNetlifyStaticHeaders } from "./netlify-static-headers.js";
37
38
  import { discoverApiRoutes, discoverPlugins, discoverActionFiles, getMissingDefaultPlugins, DEFAULT_PLUGIN_REGISTRY, } from "./route-discovery.js";
38
39
  import { getWorkspaceCoreExports, } from "./workspace-core.js";
39
40
  const cwd = process.cwd();
@@ -3892,6 +3893,7 @@ export function resolveNitroBuildReplacements(env = process.env, deploymentEnvir
3892
3893
  // a separate server build, so embed release migration ownership here as
3893
3894
  // well as in the Vite server bundle.
3894
3895
  "process.env.AGENT_NATIVE_RELEASE_MIGRATIONS": JSON.stringify(env.AGENT_NATIVE_RELEASE_MIGRATIONS?.trim() || ""),
3896
+ "process.env.AGENT_NATIVE_BETA_SCHEMA_OWNER": JSON.stringify(env.AGENT_NATIVE_BETA_SCHEMA_OWNER?.trim() || ""),
3895
3897
  "process.env.AGENT_NATIVE_BUILD_DEPLOY_CONTEXT": JSON.stringify(env.CONTEXT?.trim() || env.NETLIFY_CONTEXT?.trim() || ""),
3896
3898
  // Whether the recurring-jobs scheduled function exists is decided HERE, by
3897
3899
  // the build env. `scheduledTriggerAvailability` cannot re-derive it later —
@@ -4087,6 +4089,10 @@ export default bundle;
4087
4089
  }
4088
4090
  }
4089
4091
  writeSingleTemplateNetlifyRedirects(cwd);
4092
+ // React Router prerendered pages bypass the SSR function and are served
4093
+ // directly from Netlify's static backing store. Keep that artifact on the
4094
+ // same public SWR policy as runtime SSR and .data responses.
4095
+ writeNetlifyStaticHeaders(path.join(cwd, "dist"));
4090
4096
  assertSingleTemplateNetlifyBuildOutput(cwd);
4091
4097
  }
4092
4098
  // Resolve remaining bare npm imports by bundling them into _libs/.
@@ -0,0 +1,3 @@
1
+ export declare const GENERATED_NETLIFY_HEADERS_MARKER = "# Generated by @agent-native/core deploy/netlify-static-headers.ts";
2
+ export declare function renderNetlifyStaticHeaders(env?: Record<string, string | undefined>): string;
3
+ export declare function writeNetlifyStaticHeaders(publishDir: string, env?: Record<string, string | undefined>): void;
@@ -0,0 +1,60 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { resolveSsrCacheHeaders } from "../shared/cache-control.js";
4
+ import { IMMUTABLE_ASSET_CACHE_CONTROL } from "./immutable-assets.js";
5
+ export const GENERATED_NETLIFY_HEADERS_MARKER = "# Generated by @agent-native/core deploy/netlify-static-headers.ts";
6
+ const LEGACY_GENERATED_NETLIFY_HEADERS_MARKER = "# Generated by @agent-native/docs scripts/generate-netlify-headers.ts";
7
+ function renderHeaderBlock(pathname, headers) {
8
+ return [
9
+ pathname,
10
+ ...headers.map(([name, value]) => ` ${name}: ${value}`),
11
+ ].join("\n");
12
+ }
13
+ export function renderNetlifyStaticHeaders(env = process.env) {
14
+ const ssrHeaders = resolveSsrCacheHeaders(env);
15
+ const ssrHeaderEntries = [
16
+ ["Cache-Control", ssrHeaders["cache-control"]],
17
+ ["CDN-Cache-Control", ssrHeaders["cdn-cache-control"]],
18
+ ["Netlify-CDN-Cache-Control", ssrHeaders["netlify-cdn-cache-control"]],
19
+ ];
20
+ const immutableAssetHeaders = [
21
+ ["Cache-Control", IMMUTABLE_ASSET_CACHE_CONTROL],
22
+ ["CDN-Cache-Control", IMMUTABLE_ASSET_CACHE_CONTROL],
23
+ ["Netlify-CDN-Cache-Control", IMMUTABLE_ASSET_CACHE_CONTROL],
24
+ ];
25
+ const internalHeaders = [
26
+ ["Cache-Control", "no-cache, no-store"],
27
+ ["CDN-Cache-Control", "no-cache, no-store"],
28
+ ["Netlify-CDN-Cache-Control", "no-cache, no-store"],
29
+ ];
30
+ return ([
31
+ GENERATED_NETLIFY_HEADERS_MARKER,
32
+ renderHeaderBlock("/*", ssrHeaderEntries),
33
+ renderHeaderBlock("/assets/*", immutableAssetHeaders),
34
+ renderHeaderBlock("/_agent-native/*", internalHeaders),
35
+ ].join("\n\n") + "\n");
36
+ }
37
+ export function writeNetlifyStaticHeaders(publishDir, env = process.env) {
38
+ if (!fs.existsSync(publishDir)) {
39
+ throw new Error(`Netlify publish directory does not exist: ${publishDir}`);
40
+ }
41
+ const headersPath = path.join(publishDir, "_headers");
42
+ const existing = fs.existsSync(headersPath)
43
+ ? fs.readFileSync(headersPath, "utf8").trim()
44
+ : "";
45
+ const generated = renderNetlifyStaticHeaders(env).trim();
46
+ const generatedStart = [
47
+ GENERATED_NETLIFY_HEADERS_MARKER,
48
+ LEGACY_GENERATED_NETLIFY_HEADERS_MARKER,
49
+ ]
50
+ .map((marker) => existing.indexOf(marker))
51
+ .filter((index) => index >= 0)
52
+ .sort((a, b) => a - b)[0];
53
+ const existingWithoutGenerated = generatedStart === undefined
54
+ ? existing
55
+ : existing.slice(0, generatedStart).trimEnd();
56
+ const contents = existingWithoutGenerated
57
+ ? `${existingWithoutGenerated}\n\n${generated}`
58
+ : generated;
59
+ fs.writeFileSync(headersPath, `${contents}\n`);
60
+ }