@agent-native/core 0.145.3 → 0.145.5

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 (51) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/server/plugins/agent-chat.ts +10 -8
  3. package/corpus/templates/assets/server/plugins/agent-chat.ts +1 -1
  4. package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
  5. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +8 -3
  6. package/corpus/templates/clips/app/components/editor/waveform.tsx +2 -1
  7. package/corpus/templates/clips/app/components/library/search-bar.tsx +1 -1
  8. package/corpus/templates/clips/app/global.css +9 -0
  9. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +22 -1
  10. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +10 -11
  11. package/corpus/templates/design/actions/view-screen.ts +1 -0
  12. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +14 -1
  13. package/corpus/templates/design/changelog/2026-08-07-cmd-c-now-copies-selected-text-in-the-agent-panel-instead-of.md +6 -0
  14. package/corpus/templates/design/changelog/2026-08-07-real-time-voice-can-now-read-the-current-selection-and-edit-.md +6 -0
  15. package/corpus/templates/design/server/lib/reprompt-action-guard.ts +7 -3
  16. package/corpus/templates/mail/server/plugins/agent-chat.ts +1 -1
  17. package/corpus/templates/plan/server/lib/plan-framework-tools.ts +11 -0
  18. package/corpus/templates/plan/server/plugins/00-mcp.ts +25 -68
  19. package/corpus/templates/plan/server/plugins/agent-chat.ts +8 -2
  20. package/dist/collab/awareness.d.ts +2 -2
  21. package/dist/collab/routes.d.ts +1 -1
  22. package/dist/framework-tools.d.ts +33 -1
  23. package/dist/framework-tools.js +88 -4
  24. package/dist/mcp/build-server.d.ts +15 -0
  25. package/dist/mcp/build-server.js +94 -30
  26. package/dist/mcp/oauth-client-metadata.js +6 -2
  27. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  28. package/dist/notifications/routes.d.ts +2 -2
  29. package/dist/observability/routes.d.ts +3 -3
  30. package/dist/progress/routes.d.ts +1 -1
  31. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  32. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  33. package/dist/secrets/routes.d.ts +9 -9
  34. package/dist/server/action-discovery.d.ts +3 -18
  35. package/dist/server/action-discovery.js +9 -65
  36. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -2
  37. package/dist/server/agent-chat/action-filters-a2a.js +1 -1
  38. package/dist/server/agent-chat/mcp-options.d.ts +106 -0
  39. package/dist/server/agent-chat/mcp-options.js +50 -0
  40. package/dist/server/agent-chat/plugin-options.d.ts +17 -26
  41. package/dist/server/agent-chat-plugin.js +21 -11
  42. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  43. package/dist/server/index.d.ts +3 -0
  44. package/dist/server/index.js +7 -0
  45. package/dist/server/realtime-token.d.ts +1 -1
  46. package/dist/server/realtime-voice.d.ts +2 -1
  47. package/dist/server/realtime-voice.js +38 -56
  48. package/dist/server/short-lived-token.d.ts +43 -0
  49. package/dist/server/short-lived-token.js +74 -0
  50. package/dist/server/transcribe-voice.d.ts +1 -1
  51. package/package.json +2 -2
@@ -26,7 +26,7 @@
26
26
  * ```
27
27
  */
28
28
  import type { ActionEntry } from "../agent/production-agent.js";
29
- import type { FrameworkToolGroup } from "../framework-tools.js";
29
+ import { CORE_ACTION_GROUPS } from "../framework-tools.js";
30
30
  /**
31
31
  * Register a map of actions contributed by a published package.
32
32
  *
@@ -81,22 +81,7 @@ export declare function loadActionsFromStaticRegistry(modules: Record<string, un
81
81
  * passing to `createAgentChatPlugin({ actions })`.
82
82
  */
83
83
  export declare function autoDiscoverActions(from: string): Promise<Record<string, ActionEntry>>;
84
- /**
85
- * Which `frameworkTools` switch owns each framework action kit.
86
- *
87
- * The group is stamped onto the entry as `frameworkGroup`, which is what lets
88
- * one filter subtract a whole kit from every agent tool surface at once — the
89
- * plugin spreads these registries at thirteen composition sites, and a
90
- * per-site condition would be the same omission waiting to happen thirteen
91
- * times. The tag also keeps these ~45 schemas out of the DEFAULT first-request
92
- * tool list; they stay reachable through `tool-search`.
93
- *
94
- * Anything absent from this map is always-on and must be listed in
95
- * `ALWAYS_ON_CORE_ACTIONS` instead. `action-discovery.spec.ts` fails when a new
96
- * core action appears in neither, so "always-on" stays a decision someone made
97
- * rather than the default that happens when a map goes un-updated.
98
- */
99
- export declare const CORE_ACTION_GROUPS: Record<string, FrameworkToolGroup>;
84
+ export { CORE_ACTION_GROUPS };
100
85
  /**
101
86
  * Core actions with no `frameworkTools` switch, and why:
102
87
  *
@@ -104,7 +89,7 @@ export declare const CORE_ACTION_GROUPS: Record<string, FrameworkToolGroup>;
104
89
  * - The email catalog is mounted everywhere on purpose so Dispatch can ask any
105
90
  * app what it sends without that app opting in (see its comment below). It is
106
91
  * a read surface, not the `email` group's send capability.
107
- * - MCP tools and org service tokens are already governed by `disableMcp`.
92
+ * - MCP tools and org service tokens are already governed by `mcp.enabled`.
108
93
  */
109
94
  export declare const ALWAYS_ON_CORE_ACTIONS: ReadonlySet<string>;
110
95
  export declare function mergeCoreSharingActions(registry: Record<string, ActionEntry>): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import nodePath from "node:path";
2
+ import { CORE_ACTION_GROUPS } from "../framework-tools.js";
2
3
  import { captureCliOutput } from "./cli-capture.js";
3
4
  // Lazy fs — loaded via dynamic import() on first use.
4
5
  // Avoids require() which bundlers convert to createRequire() that crashes on CF Workers.
@@ -497,70 +498,10 @@ export async function autoDiscoverActions(from) {
497
498
  }
498
499
  return registry;
499
500
  }
500
- /**
501
- * Which `frameworkTools` switch owns each framework action kit.
502
- *
503
- * The group is stamped onto the entry as `frameworkGroup`, which is what lets
504
- * one filter subtract a whole kit from every agent tool surface at once — the
505
- * plugin spreads these registries at thirteen composition sites, and a
506
- * per-site condition would be the same omission waiting to happen thirteen
507
- * times. The tag also keeps these ~45 schemas out of the DEFAULT first-request
508
- * tool list; they stay reachable through `tool-search`.
509
- *
510
- * Anything absent from this map is always-on and must be listed in
511
- * `ALWAYS_ON_CORE_ACTIONS` instead. `action-discovery.spec.ts` fails when a new
512
- * core action appears in neither, so "always-on" stays a decision someone made
513
- * rather than the default that happens when a map goes un-updated.
514
- */
515
- export const CORE_ACTION_GROUPS = {
516
- "share-resource": "sharing",
517
- "unshare-resource": "sharing",
518
- "list-resource-shares": "sharing",
519
- "set-resource-visibility": "sharing",
520
- "create-agent-resource-link": "sharing",
521
- "get-feature-flags": "featureFlags",
522
- "list-feature-flags": "featureFlags",
523
- "set-feature-flag": "featureFlags",
524
- "list-recurring-jobs": "automation",
525
- "manage-recurring-job": "automation",
526
- "run-automation-now": "automation",
527
- "list-automation-runs": "automation",
528
- "list-automations": "automation",
529
- "manage-automation": "automation",
530
- "context-manifest-get": "contextXray",
531
- "context-preview-get": "contextXray",
532
- "context-pin": "contextXray",
533
- "context-evict": "contextXray",
534
- "context-restore": "contextXray",
535
- "context-report": "contextXray",
536
- "get-localization-preference": "localization",
537
- "set-localization-preference": "localization",
538
- // Profile, credentials, and appearance share one switch: an app that hides
539
- // its profile surface from the agent hides password management with it.
540
- "get-user-profile": "userProfile",
541
- "update-user-profile": "userProfile",
542
- "get-auth-methods": "userProfile",
543
- "set-password": "userProfile",
544
- "change-password": "userProfile",
545
- "change-appearance": "userProfile",
546
- "list-audit-events": "audit",
547
- "get-audit-event": "audit",
548
- "export-audit-events": "audit",
549
- "create-resource-version": "history",
550
- "list-resource-versions": "history",
551
- "get-resource-version": "history",
552
- "restore-resource-version": "history",
553
- "list-resource-history": "history",
554
- "list-review-comments": "review",
555
- "create-review-comment": "review",
556
- "reply-review-comment": "review",
557
- "resolve-review-thread": "review",
558
- "delete-review-comment": "review",
559
- "consume-review-feedback": "review",
560
- "get-review-feedback": "review",
561
- "set-review-status": "review",
562
- "send-review-thread-to-agent": "review",
563
- };
501
+ // `CORE_ACTION_GROUPS` lives in `framework-tools.ts` so the group filter can
502
+ // resolve a kit by name without importing this module. Re-exported here
503
+ // because this is where callers have always found it.
504
+ export { CORE_ACTION_GROUPS };
564
505
  /**
565
506
  * Core actions with no `frameworkTools` switch, and why:
566
507
  *
@@ -568,7 +509,7 @@ export const CORE_ACTION_GROUPS = {
568
509
  * - The email catalog is mounted everywhere on purpose so Dispatch can ask any
569
510
  * app what it sends without that app opting in (see its comment below). It is
570
511
  * a read surface, not the `email` group's send capability.
571
- * - MCP tools and org service tokens are already governed by `disableMcp`.
512
+ * - MCP tools and org service tokens are already governed by `mcp.enabled`.
572
513
  */
573
514
  export const ALWAYS_ON_CORE_ACTIONS = new Set([
574
515
  "upload-image",
@@ -818,6 +759,9 @@ export async function mergeCoreSharingActions(registry) {
818
759
  // actions' `toolCallable: false` (audit-H5) is dropped and the
819
760
  // tools-iframe bridge 403 in action-routes.ts never fires.
820
761
  ...preserveActionFlags(def),
762
+ // Pre-resolved copy of what `resolveFrameworkGroup` would compute
763
+ // from the name anyway. Kept so an entry read in isolation still
764
+ // reports its kit; the filters no longer depend on it.
821
765
  ...(CORE_ACTION_GROUPS[name]
822
766
  ? { frameworkGroup: CORE_ACTION_GROUPS[name] }
823
767
  : {}),
@@ -3,6 +3,7 @@ import type { EngineTool } from "../../agent/engine/types.js";
3
3
  import { type ActionEntry, type AgentLoopOutcome } from "../../agent/production-agent.js";
4
4
  import { runAgentLoopDirectWithSoftTimeout } from "../../agent/run-loop-with-resume.js";
5
5
  import type { AgentChatEvent } from "../../agent/types.js";
6
+ import type { ExternalAgentPolicy } from "../../mcp/external-agent-policy.js";
6
7
  import type { AgentChatPluginOptions } from "./plugin-options.js";
7
8
  export declare function filterReadOnlyActions(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
8
9
  /** Drop actions that opted out of agent exposure via `agentTool: false`. They
@@ -25,13 +26,24 @@ export declare function filterPublicAgentActions(actions: Record<string, ActionE
25
26
  * action out of this rule.
26
27
  */
27
28
  export declare function hasRawQueryInput(entry: ActionEntry): boolean;
29
+ /**
30
+ * The two policy fields A2A shares with the MCP mount. Structural rather than
31
+ * `Pick<AgentChatPluginOptions, …>` so callers pass the *resolved* MCP options:
32
+ * reading the raw plugin options here would leave A2A on the deprecated
33
+ * top-level keys, so `mcp: { connectorCatalog }` would narrow the MCP surface
34
+ * while A2A kept serving the old one.
35
+ */
36
+ export interface A2AExternalAgentSurface {
37
+ connectorCatalog?: string[];
38
+ externalAgents?: ExternalAgentPolicy;
39
+ }
28
40
  /**
29
41
  * Direct A2A action calls share the authenticated external-agent policy with
30
42
  * MCP, but remain stricter: only explicitly exposed, authenticated read-only
31
43
  * actions can skip the receiver's model loop, and never one that takes a raw
32
44
  * query the caller wrote.
33
45
  */
34
- export declare function filterDirectA2AActions(actions: Record<string, ActionEntry>, options: Pick<AgentChatPluginOptions, "connectorCatalog" | "externalAgents">): Record<string, ActionEntry>;
46
+ export declare function filterDirectA2AActions(actions: Record<string, ActionEntry>, options: A2AExternalAgentSurface): Record<string, ActionEntry>;
35
47
  export declare function buildPublicAgentA2ASkills(actions: Record<string, ActionEntry>): Array<{
36
48
  id: string;
37
49
  name: string;
@@ -47,7 +59,7 @@ export declare function buildPublicAgentA2ASkills(actions: Record<string, Action
47
59
  * `requiresAuth === true` — advertising the public set alone tells siblings
48
60
  * an app has nothing callable when it does.
49
61
  */
50
- export declare function buildAuthenticatedAgentA2ASkills(actions: Record<string, ActionEntry>, options: Pick<AgentChatPluginOptions, "connectorCatalog" | "externalAgents">): Array<{
62
+ export declare function buildAuthenticatedAgentA2ASkills(actions: Record<string, ActionEntry>, options: A2AExternalAgentSurface): Array<{
51
63
  id: string;
52
64
  name: string;
53
65
  description: string;
@@ -405,6 +405,6 @@ export function resolveInitialToolNames(templateActions, configured) {
405
405
  if (configured)
406
406
  return configured;
407
407
  return Object.entries(templateActions)
408
- .filter(([, entry]) => !isFrameworkGroupedAction(entry))
408
+ .filter(([name, entry]) => !isFrameworkGroupedAction(name, entry))
409
409
  .map(([name]) => name);
410
410
  }
@@ -0,0 +1,106 @@
1
+ import type { ExternalAgentPolicy } from "../../mcp/external-agent-policy.js";
2
+ /** App icon descriptor surfaced during the MCP `initialize` handshake. */
3
+ export interface AgentChatMcpIcon {
4
+ src: string;
5
+ mimeType?: string;
6
+ sizes?: string[];
7
+ theme?: "light" | "dark";
8
+ }
9
+ /**
10
+ * Everything the agent-chat plugin can configure about its MCP mount, in one
11
+ * object.
12
+ *
13
+ * Before this existed, the MCP settings were five unrelated top-level keys
14
+ * (`disableMcp`, `mcpServerInfo`, `connectorCatalog`, `externalAgents`) and
15
+ * several `MCPConfig` fields with no route through the plugin at all —
16
+ * `builtinCrossAppTools` was reachable only by calling `mountMCP` directly,
17
+ * which is why `frameworkTools` could never remove the cross-app builtins from
18
+ * an app that used the normal plugin entry point.
19
+ */
20
+ export interface AgentChatMcpOptions {
21
+ /**
22
+ * Mount the remote MCP protocol route. Defaults to `true`.
23
+ *
24
+ * Set `false` for hosted apps with a dedicated early MCP plugin, so their
25
+ * external connector does not depend on the heavier chat plugin
26
+ * initialization path.
27
+ */
28
+ enabled?: boolean;
29
+ /**
30
+ * `"app"` serves external callers exactly this app's own tool registry,
31
+ * flat — the same actions the in-app agent holds, with no cross-app
32
+ * builtins, no `ask-agent`, no `tool-search`, and no compact/connector
33
+ * trimming. `connectorCatalog` and the `--full-catalog` opt-ins are inert
34
+ * once this is set; `externalAgents.denyActions` and OAuth scopes still
35
+ * apply.
36
+ *
37
+ * Weigh the token cost before setting it: an app registering ~100 actions
38
+ * puts every one of their schemas in the caller's context on `tools/list`,
39
+ * which is the exact footgun the compact default exists to avoid.
40
+ */
41
+ catalog?: "app";
42
+ /**
43
+ * Curated allow-list of action names served to external connector clients.
44
+ * Trims both the advertised and the callable surface — a name outside the
45
+ * list is rejected, not merely hidden.
46
+ */
47
+ connectorCatalog?: string[];
48
+ /**
49
+ * Default authenticated external-agent policy. In `auto` read mode, every
50
+ * action explicitly marked as GET + readOnly + publicAgent.requiresAuth is
51
+ * added to the connector surface automatically. Writes remain ask_app-only
52
+ * unless `writes: "allowlisted"` is explicitly selected.
53
+ */
54
+ externalAgents?: ExternalAgentPolicy;
55
+ /**
56
+ * Merge the generic cross-app builtins (`list_apps`, `open_app`, `ask_app`,
57
+ * `ask_app_status`, `create_embed_session`, `create_workspace_app`,
58
+ * `list_templates`). Defaults to `true`.
59
+ *
60
+ * These are added by the MCP layer itself, downstream of `frameworkTools`,
61
+ * so they are NOT removed by `frameworkTools: "minimal"` or
62
+ * `workspaceApps: false` — this is the switch that turns them off.
63
+ */
64
+ builtinCrossAppTools?: boolean;
65
+ /** Human-facing title. Defaults to the capitalized app id/name. */
66
+ title?: string;
67
+ /** Host-facing description. Defaults to "Agent-native <app> agent". */
68
+ description?: string;
69
+ /** Canonical app URL. Relative URLs are resolved against the request origin. */
70
+ websiteUrl?: string;
71
+ /** App icons. Relative `src` values are resolved against the request origin. */
72
+ icons?: AgentChatMcpIcon[];
73
+ }
74
+ /** The legacy top-level keys `mcp` replaces. */
75
+ export interface AgentChatMcpLegacyInput {
76
+ /** @deprecated Use `mcp.enabled: false`. */
77
+ disableMcp?: boolean;
78
+ /** @deprecated Use `mcp.title` / `mcp.description` / `mcp.websiteUrl` / `mcp.icons`. */
79
+ mcpServerInfo?: {
80
+ title?: string;
81
+ description?: string;
82
+ websiteUrl?: string;
83
+ icons?: AgentChatMcpIcon[];
84
+ };
85
+ /** @deprecated Use `mcp.connectorCatalog`. */
86
+ connectorCatalog?: string[];
87
+ /** @deprecated Use `mcp.externalAgents`. */
88
+ externalAgents?: ExternalAgentPolicy;
89
+ mcp?: AgentChatMcpOptions;
90
+ }
91
+ export interface ResolvedAgentChatMcp {
92
+ enabled: boolean;
93
+ catalog: "app" | undefined;
94
+ connectorCatalog: string[] | undefined;
95
+ externalAgents: ExternalAgentPolicy | undefined;
96
+ builtinCrossAppTools: boolean | undefined;
97
+ title: string | undefined;
98
+ description: string | undefined;
99
+ websiteUrl: string | undefined;
100
+ icons: AgentChatMcpIcon[] | undefined;
101
+ }
102
+ /**
103
+ * Collapse the nested `mcp` option and the legacy top-level keys into the one
104
+ * shape the plugin threads into `mountMCP`.
105
+ */
106
+ export declare function resolveAgentChatMcpOptions(input: AgentChatMcpLegacyInput | undefined): ResolvedAgentChatMcp;
@@ -0,0 +1,50 @@
1
+ function conflict(key, legacyKey, legacyValue, nestedValue) {
2
+ throw new Error(`[agent-native] Conflicting agent-chat options: \`${legacyKey}: ${JSON.stringify(legacyValue)}\` ` +
3
+ `and \`mcp.${key}: ${JSON.stringify(nestedValue)}\` disagree. ` +
4
+ `Remove the deprecated \`${legacyKey}\` and keep \`mcp.${key}\`.`);
5
+ }
6
+ const warnedLegacyKeys = new Set();
7
+ /**
8
+ * Nested value wins, but only when the two forms agree.
9
+ *
10
+ * Disagreement throws at plugin init rather than picking a side: an app that
11
+ * boots with an MCP surface nobody chose is how a "why can't the connector see
12
+ * this tool" report ends up unexplainable. Same contract as
13
+ * `resolveFrameworkTools`.
14
+ */
15
+ function pick(key, legacyKey, legacyValue, nestedValue) {
16
+ if (legacyValue === undefined)
17
+ return nestedValue;
18
+ if (nestedValue !== undefined &&
19
+ JSON.stringify(legacyValue) !== JSON.stringify(nestedValue)) {
20
+ conflict(key, legacyKey, legacyValue, nestedValue);
21
+ }
22
+ if (!warnedLegacyKeys.has(legacyKey)) {
23
+ warnedLegacyKeys.add(legacyKey);
24
+ console.warn(`[agent-native] \`${legacyKey}\` is deprecated — use \`mcp: { ${key}: … }\`.`);
25
+ }
26
+ return nestedValue ?? legacyValue;
27
+ }
28
+ /**
29
+ * Collapse the nested `mcp` option and the legacy top-level keys into the one
30
+ * shape the plugin threads into `mountMCP`.
31
+ */
32
+ export function resolveAgentChatMcpOptions(input) {
33
+ const mcp = input?.mcp ?? {};
34
+ const legacyInfo = input?.mcpServerInfo;
35
+ // `disableMcp` is the inverse of `enabled`, so normalize before comparing —
36
+ // otherwise `disableMcp: true` + `enabled: false` would read as a conflict.
37
+ const legacyEnabled = input?.disableMcp === undefined ? undefined : !input.disableMcp;
38
+ const enabled = pick("enabled", "disableMcp", legacyEnabled, mcp.enabled);
39
+ return {
40
+ enabled: enabled ?? true,
41
+ catalog: mcp.catalog,
42
+ connectorCatalog: pick("connectorCatalog", "connectorCatalog", input?.connectorCatalog, mcp.connectorCatalog),
43
+ externalAgents: pick("externalAgents", "externalAgents", input?.externalAgents, mcp.externalAgents),
44
+ builtinCrossAppTools: mcp.builtinCrossAppTools,
45
+ title: pick("title", "mcpServerInfo.title", legacyInfo?.title, mcp.title),
46
+ description: pick("description", "mcpServerInfo.description", legacyInfo?.description, mcp.description),
47
+ websiteUrl: pick("websiteUrl", "mcpServerInfo.websiteUrl", legacyInfo?.websiteUrl, mcp.websiteUrl),
48
+ icons: pick("icons", "mcpServerInfo.icons", legacyInfo?.icons, mcp.icons),
49
+ };
50
+ }
@@ -5,6 +5,7 @@ import type { FrameworkToolsConfig } from "../../framework-tools.js";
5
5
  import type { ExternalAgentPolicy } from "../../mcp/external-agent-policy.js";
6
6
  import type { DatabaseToolsOption } from "../../scripts/db/tool-mode.js";
7
7
  import type { PromptExamples } from "../prompts/index.js";
8
+ import type { AgentChatMcpIcon, AgentChatMcpOptions } from "./mcp-options.js";
8
9
  /** Shape of a Nitro plugin function: receives the Nitro app instance at
9
10
  * startup and may register routes/hooks synchronously or asynchronously. */
10
11
  export type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
@@ -51,21 +52,23 @@ export interface AgentChatPluginOptions {
51
52
  mentionProviders?: Record<string, MentionProvider> | (() => Record<string, MentionProvider> | Promise<Record<string, MentionProvider>>);
52
53
  /** App ID used to exclude self from agent discovery (e.g., "mail", "calendar") */
53
54
  appId?: string;
54
- /** Optional MCP server branding surfaced during the initialize handshake. */
55
+ /**
56
+ * Everything about this app's MCP mount — whether it is mounted, which tools
57
+ * external callers see, and the branding sent during the `initialize`
58
+ * handshake. See `AgentChatMcpOptions`.
59
+ *
60
+ * Replaces the top-level `disableMcp`, `mcpServerInfo`, `connectorCatalog`,
61
+ * and `externalAgents`, which stay accepted for one minor. Setting both
62
+ * forms to disagreeing values throws at plugin init rather than silently
63
+ * picking one.
64
+ */
65
+ mcp?: AgentChatMcpOptions;
66
+ /** @deprecated Use `mcp.title` / `mcp.description` / `mcp.websiteUrl` / `mcp.icons`. */
55
67
  mcpServerInfo?: {
56
- /** Human-facing title. Defaults to the capitalized app id/name. */
57
68
  title?: string;
58
- /** Host-facing description. Defaults to "Agent-native <app> agent". */
59
69
  description?: string;
60
- /** Canonical app URL. Relative URLs are resolved against the request origin. */
61
70
  websiteUrl?: string;
62
- /** App icons. Relative `src` values are resolved against the request origin. */
63
- icons?: Array<{
64
- src: string;
65
- mimeType?: string;
66
- sizes?: string[];
67
- theme?: "light" | "dark";
68
- }>;
71
+ icons?: AgentChatMcpIcon[];
69
72
  };
70
73
  /**
71
74
  * Optional callback to resolve the org ID for the current request.
@@ -330,15 +333,10 @@ export interface AgentChatPluginOptions {
330
333
  * claim in their connect-minted JWT) or the deployment-wide
331
334
  * `AGENT_NATIVE_MCP_FULL_CATALOG=1` env override.
332
335
  *
333
- * Declare here rather than in MCPConfig directly; the plugin copies it through.
336
+ * @deprecated Use `mcp.connectorCatalog`.
334
337
  */
335
338
  connectorCatalog?: string[];
336
- /**
337
- * Default authenticated external-agent policy. In `auto` read mode, every
338
- * action explicitly marked as GET + readOnly + publicAgent.requiresAuth is
339
- * added to the connector surface automatically. Writes remain ask_app-only
340
- * unless `writes: "allowlisted"` is explicitly selected.
341
- */
339
+ /** @deprecated Use `mcp.externalAgents`. */
342
340
  externalAgents?: ExternalAgentPolicy;
343
341
  /**
344
342
  * Allow this app's A2A agent to delegate to a different app through the
@@ -359,14 +357,7 @@ export interface AgentChatPluginOptions {
359
357
  maxRunInputTokens?: number;
360
358
  maxToolResultChars?: number;
361
359
  };
362
- /**
363
- * Skip mounting the remote MCP protocol route.
364
- *
365
- * Most apps should leave this off so agent chat, A2A, and MCP share one
366
- * runtime. Hosted apps with a dedicated early MCP plugin can set this to
367
- * true so their external connector does not depend on the heavier chat
368
- * plugin initialization path.
369
- */
360
+ /** @deprecated Use `mcp: { enabled: false }`. */
370
361
  disableMcp?: boolean;
371
362
  /**
372
363
  * Code-execution capability for the production agent.
@@ -110,6 +110,7 @@ import { createA2AEngineToolSurface, DEFAULT_DELEGATED_MAX_ITERATIONS, DEFAULT_D
110
110
  import { createBuilderBrowserTool, createTeamTools, } from "./agent-chat/browser-team-tools.js";
111
111
  import { createDataWidgetActionEntries, createFrameworkContextEntry, createRefreshScreenEntry, createUrlTools, } from "./agent-chat/context-tools.js";
112
112
  import { _agentChatPromptSectionsForTests, buildFrameworkPrompts, buildSchemaBlock, collectFiles, corpusToolNamesTaughtByPrompt, generateActionsPrompt, generateCorpusToolsPrompt, } from "./agent-chat/framework-prompts.js";
113
+ import { resolveAgentChatMcpOptions } from "./agent-chat/mcp-options.js";
113
114
  import { resolveA2AAgentDelegationEnabled, } from "./agent-chat/plugin-options.js";
114
115
  import { finalizeClaimedAgentChatProcessRunFailure } from "./agent-chat/process-run-failure.js";
115
116
  import { loadResourcesForPrompt, promptResourceManifestSections, resourceScopeForOwner, } from "./agent-chat/prompt-resources.js";
@@ -286,6 +287,11 @@ export function createAgentChatPlugin(options) {
286
287
  // here rather than booting with a surface nobody chose.
287
288
  const frameworkTools = resolveFrameworkTools(options);
288
289
  const disabledFrameworkGroups = frameworkTools.disabledGroups;
290
+ // Same treatment for the MCP mount: one resolved shape, folding the
291
+ // deprecated `disableMcp` / `mcpServerInfo` / `connectorCatalog` /
292
+ // `externalAgents` into `mcp`. A2A reads the same object, so the
293
+ // connector policy cannot diverge between the two external surfaces.
294
+ const mcpOptions = resolveAgentChatMcpOptions(options);
289
295
  // Build the four assembled system prompt strings. These are static for the
290
296
  // lifetime of this plugin instance — examples come from options once at
291
297
  // startup, not per-request.
@@ -1055,12 +1061,12 @@ export function createAgentChatPlugin(options) {
1055
1061
  : "Agent",
1056
1062
  description: `Agent-native ${options?.appId ?? "app"} agent`,
1057
1063
  skills: buildPublicAgentA2ASkills(allScripts),
1058
- authenticatedSkills: buildAuthenticatedAgentA2ASkills(mcpFullActions ?? allScripts, options ?? {}),
1064
+ authenticatedSkills: buildAuthenticatedAgentA2ASkills(mcpFullActions ?? allScripts, mcpOptions),
1059
1065
  publicSkillsOnly: true,
1060
1066
  streaming: true,
1061
1067
  durableBackgroundRuns: options?.durableBackgroundRuns,
1062
1068
  executeReadOnlyAction: async ({ action, input, invocationId }) => {
1063
- const actions = filterDirectA2AActions(mcpFullActions ?? allScripts, options ?? {});
1069
+ const actions = filterDirectA2AActions(mcpFullActions ?? allScripts, mcpOptions);
1064
1070
  const entry = actions[action];
1065
1071
  if (!entry) {
1066
1072
  return {
@@ -1667,26 +1673,30 @@ export function createAgentChatPlugin(options) {
1667
1673
  : DEV_FRAMEWORK_PROMPT) + devActionsPrompt;
1668
1674
  // Keep legacy names for the composition below
1669
1675
  const basePrompt = prodPrompt;
1670
- if (options?.disableMcp !== true) {
1676
+ if (mcpOptions.enabled) {
1671
1677
  // Mount MCP remote server — same action registry as A2A + agent chat
1672
1678
  const { mountMCP } = await import("../mcp/server.js");
1673
1679
  mountMCP(nitroApp, {
1674
1680
  name: options?.appId
1675
1681
  ? options.appId.charAt(0).toUpperCase() + options.appId.slice(1)
1676
1682
  : "Agent",
1677
- title: options?.mcpServerInfo?.title,
1683
+ title: mcpOptions.title,
1678
1684
  appId: options?.appId,
1679
- description: options?.mcpServerInfo?.description ??
1685
+ description: mcpOptions.description ??
1680
1686
  `Agent-native ${options?.appId ?? "app"} agent`,
1681
- websiteUrl: options?.mcpServerInfo?.websiteUrl,
1682
- icons: options?.mcpServerInfo?.icons,
1687
+ websiteUrl: mcpOptions.websiteUrl,
1688
+ icons: mcpOptions.icons,
1683
1689
  actions: allScripts,
1684
1690
  productionActions: mcpFullActions,
1685
- ...(options?.connectorCatalog
1686
- ? { connectorCatalog: options.connectorCatalog }
1691
+ ...(mcpOptions.catalog ? { catalogMode: mcpOptions.catalog } : {}),
1692
+ ...(mcpOptions.builtinCrossAppTools !== undefined
1693
+ ? { builtinCrossAppTools: mcpOptions.builtinCrossAppTools }
1687
1694
  : {}),
1688
- ...(options?.externalAgents
1689
- ? { externalAgents: options.externalAgents }
1695
+ ...(mcpOptions.connectorCatalog
1696
+ ? { connectorCatalog: mcpOptions.connectorCatalog }
1697
+ : {}),
1698
+ ...(mcpOptions.externalAgents
1699
+ ? { externalAgents: mcpOptions.externalAgents }
1690
1700
  : {}),
1691
1701
  askAgent: async (message) => {
1692
1702
  const ownerEmail = getRequestUserEmail();
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
27
27
  export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
28
28
  error: any;
29
29
  } | {
30
- error?: undefined;
31
30
  ok: boolean;
32
31
  key: string;
33
32
  baseUrlKey?: string;
34
33
  scope: AgentEngineApiKeyScope;
34
+ error?: undefined;
35
35
  }>>;
36
36
  export {};
@@ -32,6 +32,7 @@ export { createObservationalMemoryPlugin, defaultObservationalMemoryPlugin, } fr
32
32
  export { createGoogleAuthPlugin, type GoogleAuthPluginOptions, } from "./google-auth-plugin.js";
33
33
  export type { GoogleAuthMode } from "./google-auth-mode.js";
34
34
  export { createAgentChatPlugin, defaultAgentChatPlugin, refreshGlobalMcpManager, type AgentChatPluginOptions, } from "./agent-chat-plugin.js";
35
+ export type { AgentChatMcpIcon, AgentChatMcpOptions, } from "./agent-chat/mcp-options.js";
35
36
  export { configureAgentNativeEmbeddedEnvironment, createAgentNativeEmbeddedAuthOptions, createAgentNativeEmbeddedPlugin, mountAgentNativeEmbedded, normalizeAgentNativeEmbeddedSession, type AgentNativeEmbeddedAuthOptions, type AgentNativeEmbeddedGetSession, type AgentNativeEmbeddedHostSession, type AgentNativeEmbeddedPluginOptions, } from "./embedded.js";
36
37
  export { createThread, getThread, listThreads, updateThreadData, deleteThread, setThreadArchived, setThreadPinned, setThreadScope, type ChatThread, type ChatThreadScope, type ChatThreadSummary, type ListThreadsOptions, } from "../chat-threads/store.js";
37
38
  export { createResourcesPlugin, defaultResourcesPlugin, } from "./resources-plugin.js";
@@ -52,6 +53,8 @@ export { getH3App, awaitBootstrap, markDefaultPluginProvided, type H3AppShim, }
52
53
  export { fireInternalDispatch, resolveSelfDispatchBaseUrl, type FireInternalDispatchOptions, } from "./self-dispatch.js";
53
54
  export { extractBearerToken as extractInternalBearerToken, verifyInternalToken, } from "../integrations/internal-token.js";
54
55
  export { autoDiscoverActions, autoDiscoverScripts, loadActionsFromStaticRegistry, mergeCoreSharingActions, registerPackageActions, } from "./action-discovery.js";
56
+ export { attachToolSearch, createToolSearchEntry, searchToolRegistry, TOOL_SEARCH_ACTION_NAME, } from "../agent/tool-search.js";
57
+ export { filterFrameworkToolGroups, frameworkGroupEnabled, resolveFrameworkTools, FRAMEWORK_TOOL_GROUPS, type FrameworkToolGroup, type FrameworkToolsConfig, type FrameworkToolsOption, type ResolvedFrameworkTools, } from "../framework-tools.js";
55
58
  export { registerPromptContextProvider, type PromptContextProvider, type PromptContextProviderContext, type PromptContextProviderContribution, } from "./agent-chat/prompt-resources.js";
56
59
  export { mountActionRoutes, type MountActionRoutesOptions, type ActionRouteAuthAdapter, type ActionRouteResolvedCaller, } from "./action-routes.js";
57
60
  export { AGENT_RUN_OWNER_CONTEXT_KEY, seedAgentRunOwnerContext, type AgentRunOwnerContext, } from "./agent-run-context.js";
@@ -53,6 +53,13 @@ export { getH3App, awaitBootstrap, markDefaultPluginProvided, } from "./framewor
53
53
  export { fireInternalDispatch, resolveSelfDispatchBaseUrl, } from "./self-dispatch.js";
54
54
  export { extractBearerToken as extractInternalBearerToken, verifyInternalToken, } from "../integrations/internal-token.js";
55
55
  export { autoDiscoverActions, autoDiscoverScripts, loadActionsFromStaticRegistry, mergeCoreSharingActions, registerPackageActions, } from "./action-discovery.js";
56
+ // A standalone `mountMCP` plugin has to compose the same action surface the
57
+ // agent-chat plugin does. Without these, the only way to build one was to
58
+ // hand-roll a copy — which is how a template ends up with a `tool-search` that
59
+ // drifts from the framework's, and an MCP mount that silently ignores
60
+ // `frameworkTools`.
61
+ export { attachToolSearch, createToolSearchEntry, searchToolRegistry, TOOL_SEARCH_ACTION_NAME, } from "../agent/tool-search.js";
62
+ export { filterFrameworkToolGroups, frameworkGroupEnabled, resolveFrameworkTools, FRAMEWORK_TOOL_GROUPS, } from "../framework-tools.js";
56
63
  export { registerPromptContextProvider, } from "./agent-chat/prompt-resources.js";
57
64
  export { mountActionRoutes, } from "./action-routes.js";
58
65
  export { AGENT_RUN_OWNER_CONTEXT_KEY, seedAgentRunOwnerContext, } from "./agent-run-context.js";
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
26
26
  expiresAt?: undefined;
27
27
  ttlSeconds?: undefined;
28
28
  } | {
29
+ error?: undefined;
29
30
  token: string;
30
31
  expiresAt: string;
31
32
  ttlSeconds: number;
32
- error?: undefined;
33
33
  }>>;
@@ -8,8 +8,9 @@ export declare const REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS = 16000;
8
8
  export declare const REALTIME_VOICE_MAX_TOOLS = 32;
9
9
  export declare const REALTIME_VOICE_MAX_TOOL_SCHEMA_BYTES = 32000;
10
10
  export declare const REALTIME_VOICE_MAX_SESSION_BYTES = 64000;
11
+ /** Absolute, not sliding — a signed grant cannot be extended server-side, so
12
+ * this must outlast the provider's 60-minute maximum realtime session. */
11
13
  export declare const REALTIME_VOICE_TOOL_GRANT_TTL_MS: number;
12
- export declare const REALTIME_VOICE_MAX_TOOL_GRANT_SESSIONS = 256;
13
14
  export declare const REALTIME_VOICE_CAPABILITY_HEADER = "X-Agent-Native-Realtime-Capability";
14
15
  export interface RealtimeVoiceRequestContext {
15
16
  event: H3Event;