@agent-native/core 0.175.0-nightly-20260826053817 → 0.175.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.
@@ -63,6 +63,8 @@ function compactSession(session) {
63
63
  requiresApproval: action.requiresApproval,
64
64
  schema: action.schema ?? action.parameters,
65
65
  })),
66
+ webmcpToolCount: session.webmcpTools?.length ?? 0,
67
+ webmcpTools: session.webmcpTools ?? [],
66
68
  };
67
69
  }
68
70
  export function createBrowserSessionActionEntries(options) {
@@ -142,6 +144,83 @@ export function createBrowserSessionActionEntries(options) {
142
144
  return { ok: true, sessionId, actions };
143
145
  },
144
146
  },
147
+ "list-browser-session-webmcp-tools": {
148
+ readOnly: true,
149
+ tool: {
150
+ description: "List WebMCP tools exposed by a connected browser page. Use this before running one because names, schemas, origins, and read-only hints are page-local and can change after navigation.",
151
+ parameters: {
152
+ type: "object",
153
+ properties: {
154
+ sessionId: {
155
+ type: "string",
156
+ description: "Browser session id from list-browser-sessions. Optional when only one tab is active.",
157
+ },
158
+ timeoutMs: {
159
+ type: "number",
160
+ description: "How long to wait for the live tab to respond.",
161
+ },
162
+ },
163
+ },
164
+ },
165
+ run: async (args) => {
166
+ const ownerEmail = requireOwner(options);
167
+ const sessionId = await resolveSessionId(ownerEmail, readString(args, "sessionId"));
168
+ const tools = await callBrowserSession(ownerEmail, sessionId, {
169
+ type: "list-webmcp-tools",
170
+ timeoutMs: readTimeoutMs(args, options),
171
+ }, { timeoutMs: readTimeoutMs(args, options) });
172
+ return { ok: true, sessionId, tools };
173
+ },
174
+ },
175
+ "run-browser-session-webmcp-tool": {
176
+ needsApproval: () => true,
177
+ tool: {
178
+ description: "Run a WebMCP tool exposed by a connected browser page. Use list-browser-session-webmcp-tools first, preserve its origin for duplicate names, and expect human approval for every execution.",
179
+ parameters: {
180
+ type: "object",
181
+ properties: {
182
+ sessionId: {
183
+ type: "string",
184
+ description: "Browser session id from list-browser-sessions. Optional when only one tab is active.",
185
+ },
186
+ name: {
187
+ type: "string",
188
+ description: "The WebMCP tool name to run.",
189
+ },
190
+ origin: {
191
+ type: "string",
192
+ description: "The registered tool origin when the same name is exposed by multiple origins.",
193
+ },
194
+ args: {
195
+ type: "object",
196
+ description: "JSON-serializable arguments matching the discovered WebMCP input schema.",
197
+ },
198
+ timeoutMs: {
199
+ type: "number",
200
+ description: "How long to wait for the live tab to respond.",
201
+ },
202
+ },
203
+ required: ["name"],
204
+ },
205
+ },
206
+ run: async (args) => {
207
+ const ownerEmail = requireOwner(options);
208
+ const name = readString(args, "name");
209
+ if (!name)
210
+ throw new Error("name is required");
211
+ const timeoutMs = readTimeoutMs(args, options);
212
+ const sessionId = await resolveSessionId(ownerEmail, readString(args, "sessionId"));
213
+ const origin = readString(args, "origin");
214
+ const result = await callBrowserSession(ownerEmail, sessionId, {
215
+ type: "run-webmcp-tool",
216
+ name,
217
+ origin,
218
+ args: args.args,
219
+ timeoutMs,
220
+ }, { timeoutMs });
221
+ return { ok: true, sessionId, name, result };
222
+ },
223
+ },
145
224
  "run-browser-session-action": {
146
225
  tool: {
147
226
  description: "Run a live client action in a connected browser tab. Use list-browser-session-actions first to discover the current action names and schemas. Omit sessionId to use the most recently active tab.",
@@ -6,6 +6,8 @@ export const DEFAULT_BROWSER_SESSION_REQUEST_POLL_MS = 250;
6
6
  const SESSION_TABLE = "agent_native_browser_sessions";
7
7
  const REQUEST_TABLE = "agent_native_browser_session_requests";
8
8
  const SAFE_ID_RE = /^[A-Za-z0-9._:-]{1,160}$/;
9
+ const MAX_WEBMCP_TOOL_COUNT = 100;
10
+ const MAX_WEBMCP_MANIFEST_CHARS = 500_000;
9
11
  let initPromise;
10
12
  function nowMs() {
11
13
  return Date.now();
@@ -117,6 +119,60 @@ function parseActions(value) {
117
119
  typeof action.name === "string")
118
120
  : [];
119
121
  }
122
+ function isWebMcpAction(action) {
123
+ return action.source === "webmcp";
124
+ }
125
+ function normalizeWebMcpTools(value) {
126
+ if (value === undefined)
127
+ return [];
128
+ if (!Array.isArray(value)) {
129
+ throw new Error("webmcpTools must be an array");
130
+ }
131
+ if (value.length > MAX_WEBMCP_TOOL_COUNT) {
132
+ throw new Error(`WebMCP returned more than the ${MAX_WEBMCP_TOOL_COUNT}-tool limit`);
133
+ }
134
+ let serialized;
135
+ try {
136
+ serialized = JSON.stringify(value);
137
+ }
138
+ catch {
139
+ throw new Error("WebMCP tool manifest must be JSON-serializable");
140
+ }
141
+ if (serialized === undefined) {
142
+ throw new Error("WebMCP tool manifest must be JSON-serializable");
143
+ }
144
+ if (serialized.length > MAX_WEBMCP_MANIFEST_CHARS) {
145
+ throw new Error(`WebMCP tool manifest exceeds the ${MAX_WEBMCP_MANIFEST_CHARS}-character limit`);
146
+ }
147
+ return value;
148
+ }
149
+ function toWebMcpAction(tool) {
150
+ return {
151
+ name: tool.name,
152
+ description: tool.description,
153
+ ...(tool.title ? { title: tool.title } : {}),
154
+ ...(tool.inputSchema ? { schema: tool.inputSchema } : {}),
155
+ ...(tool.origin ? { origin: tool.origin } : {}),
156
+ ...(tool.annotations ? { annotations: tool.annotations } : {}),
157
+ source: "webmcp",
158
+ availability: "browser-session",
159
+ };
160
+ }
161
+ function fromWebMcpAction(action) {
162
+ const schema = action.schema ?? action.parameters;
163
+ return {
164
+ name: action.name,
165
+ description: action.description,
166
+ ...(typeof action.title === "string" ? { title: action.title } : {}),
167
+ ...(schema ? { inputSchema: schema } : {}),
168
+ ...(typeof action.origin === "string" ? { origin: action.origin } : {}),
169
+ ...(action.annotations && typeof action.annotations === "object"
170
+ ? {
171
+ annotations: action.annotations,
172
+ }
173
+ : {}),
174
+ };
175
+ }
120
176
  function parseSession(value, sessionId) {
121
177
  const parsed = safeJsonParse(value, { id: sessionId });
122
178
  return parsed && typeof parsed === "object" && !Array.isArray(parsed)
@@ -162,6 +218,7 @@ function normalizeSessionInput(input) {
162
218
  connectedAt: new Date(connectedAt).toISOString(),
163
219
  ...(url ? { url } : {}),
164
220
  };
221
+ const webmcpTools = normalizeWebMcpTools(input.webmcpTools);
165
222
  return {
166
223
  sessionId,
167
224
  session,
@@ -172,7 +229,10 @@ function normalizeSessionInput(input) {
172
229
  !Array.isArray(input.context)
173
230
  ? input.context
174
231
  : undefined,
175
- actions: Array.isArray(input.actions) ? input.actions : [],
232
+ actions: [
233
+ ...(Array.isArray(input.actions) ? input.actions : []),
234
+ ...webmcpTools.map(toWebMcpAction),
235
+ ],
176
236
  connectedAt,
177
237
  ttlMs: typeof input.ttlMs === "number" && input.ttlMs > 0
178
238
  ? Math.min(input.ttlMs, 5 * 60 * 1000)
@@ -182,13 +242,15 @@ function normalizeSessionInput(input) {
182
242
  function rowToSession(row, now = nowMs()) {
183
243
  const sessionId = String(row.session_id ?? "");
184
244
  const expiresAt = Number(row.expires_at ?? 0);
245
+ const parsedActions = parseActions(row.actions_json);
185
246
  return {
186
247
  sessionId,
187
248
  session: parseSession(row.session_json, sessionId),
188
249
  label: typeof row.label === "string" ? row.label : undefined,
189
250
  url: typeof row.url === "string" ? row.url : undefined,
190
251
  context: parseOptionalObject(row.context_json),
191
- actions: parseActions(row.actions_json),
252
+ actions: parsedActions.filter((action) => !isWebMcpAction(action)),
253
+ webmcpTools: parsedActions.filter(isWebMcpAction).map(fromWebMcpAction),
192
254
  connectedAt: Number(row.connected_at ?? 0),
193
255
  lastSeenAt: Number(row.last_seen_at ?? 0),
194
256
  expiresAt,
@@ -219,6 +281,20 @@ function rowToRequest(row) {
219
281
  };
220
282
  if (type === "run-action")
221
283
  request.args = payload;
284
+ else if (type === "run-webmcp-tool") {
285
+ const envelope = payload && typeof payload === "object" && !Array.isArray(payload)
286
+ ? payload
287
+ : undefined;
288
+ request.args =
289
+ envelope && "args" in envelope
290
+ ? envelope.args
291
+ : typeof envelope?.origin === "string" && envelope.origin
292
+ ? {}
293
+ : payload;
294
+ if (typeof envelope?.origin === "string" && envelope.origin) {
295
+ request.origin = envelope.origin;
296
+ }
297
+ }
222
298
  else
223
299
  request.payload = payload;
224
300
  return request;
@@ -339,14 +415,17 @@ function normalizeRequestInput(input) {
339
415
  if (input.type !== "get-context" &&
340
416
  input.type !== "list-actions" &&
341
417
  input.type !== "run-action" &&
418
+ input.type !== "list-webmcp-tools" &&
419
+ input.type !== "run-webmcp-tool" &&
342
420
  input.type !== "command") {
343
- throw new Error("request type must be get-context, list-actions, run-action, or command");
421
+ throw new Error("request type must be get-context, list-actions, run-action, list-webmcp-tools, run-webmcp-tool, or command");
344
422
  }
345
423
  const name = typeof input.name === "string" && input.name.trim()
346
424
  ? input.name.trim()
347
425
  : undefined;
348
- if (input.type === "run-action" && !name) {
349
- throw new Error("name is required for run-action requests");
426
+ if ((input.type === "run-action" || input.type === "run-webmcp-tool") &&
427
+ !name) {
428
+ throw new Error("name is required for tool execution requests");
350
429
  }
351
430
  const command = typeof input.command === "string" && input.command.trim()
352
431
  ? input.command.trim()
@@ -356,8 +435,13 @@ function normalizeRequestInput(input) {
356
435
  return {
357
436
  type: input.type,
358
437
  name,
438
+ ...(input.origin ? { origin: input.origin } : {}),
359
439
  command,
360
- payload: input.type === "run-action" ? input.args : input.payload,
440
+ payload: input.type === "run-action"
441
+ ? input.args
442
+ : input.type === "run-webmcp-tool"
443
+ ? { args: input.args, origin: input.origin }
444
+ : input.payload,
361
445
  timeoutMs: typeof input.timeoutMs === "number" && input.timeoutMs > 0
362
446
  ? Math.min(input.timeoutMs, 2 * 60 * 1000)
363
447
  : DEFAULT_BROWSER_SESSION_REQUEST_TIMEOUT_MS,
@@ -1,4 +1,5 @@
1
- export type AgentNativeBrowserSessionRequestType = "get-context" | "list-actions" | "run-action" | "command";
1
+ import type { AgentNativeWebMcpTool } from "../client/webmcp.js";
2
+ export type AgentNativeBrowserSessionRequestType = "get-context" | "list-actions" | "run-action" | "list-webmcp-tools" | "run-webmcp-tool" | "command";
2
3
  export type AgentNativeBrowserSessionRequestStatus = "pending" | "claimed" | "completed" | "failed" | "expired";
3
4
  export type AgentNativeBrowserSessionJsonObject = Record<string, unknown>;
4
5
  export interface AgentNativeBrowserSessionAction {
@@ -27,6 +28,7 @@ export interface AgentNativeBrowserSessionRecord {
27
28
  url?: string;
28
29
  context?: AgentNativeBrowserSessionJsonObject;
29
30
  actions: AgentNativeBrowserSessionAction[];
31
+ webmcpTools?: AgentNativeWebMcpTool[];
30
32
  connectedAt: number;
31
33
  lastSeenAt: number;
32
34
  expiresAt: number;
@@ -37,6 +39,7 @@ export interface AgentNativeBrowserSessionRequest {
37
39
  sessionId: string;
38
40
  type: AgentNativeBrowserSessionRequestType;
39
41
  name?: string;
42
+ origin?: string;
40
43
  command?: string;
41
44
  args?: unknown;
42
45
  payload?: unknown;
@@ -55,11 +58,13 @@ export interface RegisterAgentNativeBrowserSessionInput {
55
58
  url?: string;
56
59
  context?: AgentNativeBrowserSessionJsonObject;
57
60
  actions?: AgentNativeBrowserSessionAction[];
61
+ webmcpTools?: AgentNativeWebMcpTool[];
58
62
  ttlMs?: number;
59
63
  }
60
64
  export interface CreateAgentNativeBrowserSessionRequestInput {
61
65
  type: AgentNativeBrowserSessionRequestType;
62
66
  name?: string;
67
+ origin?: string;
63
68
  command?: string;
64
69
  args?: unknown;
65
70
  payload?: unknown;
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { type AgentChatSurfaceProps, type AgentSidebarProps } from "./AgentPanel.js";
3
3
  import { type AgentNativeBrowserSessionBridge } from "./browser-session-bridge.js";
4
4
  import { type AgentNativeClientActions, type AgentNativeHostCommandHandlers, type AgentNativeHostContextGetter, type AgentNativeHostSession, type AgentNativeScreenSnapshotOptions } from "./host-bridge.js";
5
+ import type { AgentNativeWebMcpClient } from "./webmcp.js";
5
6
  export interface AgentNativeEmbeddedCommandCallbackInfo {
6
7
  command: string;
7
8
  requestId?: string;
@@ -24,6 +25,8 @@ export interface UseAgentNativeEmbeddedBrowserSessionOptions {
24
25
  getContext?: AgentNativeHostContextGetter;
25
26
  screen?: boolean | AgentNativeScreenSnapshotOptions;
26
27
  commands?: AgentNativeHostCommandHandlers;
28
+ /** WebMCP tools this chat may consume from the current page. */
29
+ webmcp?: AgentNativeWebMcpClient;
27
30
  session?: string | Partial<AgentNativeHostSession>;
28
31
  browserSession?: AgentNativeEmbeddedBrowserSessionOptions;
29
32
  onRefresh?: AgentNativeEmbeddedCommandCallback;
@@ -42,5 +45,5 @@ export interface AgentNativeEmbeddedProps extends Omit<AgentSidebarProps, "child
42
45
  /** Props forwarded to AgentChatSurface in panel mode. */
43
46
  panel?: AgentChatSurfaceProps;
44
47
  }
45
- export declare function useAgentNativeEmbeddedBrowserSession({ enabled, actions, getContext, screen, commands, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, }: UseAgentNativeEmbeddedBrowserSessionOptions): void;
46
- export declare function AgentNativeEmbedded({ children, surface, actions, getContext, enabled, screen, commands, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, panel, ...sidebarProps }: AgentNativeEmbeddedProps): React.JSX.Element;
48
+ export declare function useAgentNativeEmbeddedBrowserSession({ enabled, actions, getContext, screen, commands, webmcp, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, }: UseAgentNativeEmbeddedBrowserSessionOptions): void;
49
+ export declare function AgentNativeEmbedded({ children, surface, actions, getContext, enabled, screen, commands, webmcp, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, panel, ...sidebarProps }: AgentNativeEmbeddedProps): React.JSX.Element;
@@ -71,7 +71,7 @@ function useMergedEmbeddedCommands({ commands, onNavigate, onOpenResource, onRef
71
71
  onRequestApproval,
72
72
  ]);
73
73
  }
74
- export function useAgentNativeEmbeddedBrowserSession({ enabled = true, actions, getContext, screen = true, commands, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, }) {
74
+ export function useAgentNativeEmbeddedBrowserSession({ enabled = true, actions, getContext, screen = true, commands, webmcp, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, }) {
75
75
  const mergedCommands = useMergedEmbeddedCommands({
76
76
  commands,
77
77
  onNavigate,
@@ -101,6 +101,7 @@ export function useAgentNativeEmbeddedBrowserSession({ enabled = true, actions,
101
101
  session,
102
102
  getContext: getMergedContext,
103
103
  actions,
104
+ webmcp,
104
105
  commands: mergedCommands,
105
106
  }).start();
106
107
  browserSession?.onReady?.(bridge);
@@ -119,15 +120,17 @@ export function useAgentNativeEmbeddedBrowserSession({ enabled = true, actions,
119
120
  getMergedContext,
120
121
  mergedCommands,
121
122
  session,
123
+ webmcp,
122
124
  ]);
123
125
  }
124
- export function AgentNativeEmbedded({ children, surface, actions, getContext, enabled, screen, commands, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, panel, ...sidebarProps }) {
126
+ export function AgentNativeEmbedded({ children, surface, actions, getContext, enabled, screen, commands, webmcp, session, browserSession, onNavigate, onOpenResource, onRefresh, onRemount, onRequestApproval, panel, ...sidebarProps }) {
125
127
  useAgentNativeEmbeddedBrowserSession({
126
128
  enabled,
127
129
  actions,
128
130
  getContext,
129
131
  screen,
130
132
  commands,
133
+ webmcp,
131
134
  session,
132
135
  browserSession,
133
136
  onNavigate,
@@ -1,5 +1,6 @@
1
1
  import React, { type IframeHTMLAttributes } from "react";
2
2
  import { type AgentNativeClientActions, type AgentNativeHostAuth, type AgentNativeHostBridge, type AgentNativeHostBridgeEvent, type AgentNativeHostCommandHandlers, type AgentNativeHostContextGetter, type AgentNativeHostSession } from "./host-bridge.js";
3
+ import type { AgentNativeWebMcpClient } from "./webmcp.js";
3
4
  export interface AgentNativeFrameProps extends Omit<IframeHTMLAttributes<HTMLIFrameElement>, "src"> {
4
5
  /** URL of the Agent-Native sidecar/frame app. */
5
6
  agentUrl: string;
@@ -16,6 +17,8 @@ export interface AgentNativeFrameProps extends Omit<IframeHTMLAttributes<HTMLIFr
16
17
  commands?: AgentNativeHostCommandHandlers;
17
18
  /** Live browser-session actions the iframe sidecar can discover and call. */
18
19
  actions?: AgentNativeClientActions;
20
+ /** WebMCP tools the iframe sidecar may discover and call in the host page. */
21
+ webmcp?: AgentNativeWebMcpClient;
19
22
  /** Optional auth payload sent to the trusted iframe sidecar. */
20
23
  auth?: AgentNativeHostAuth;
21
24
  onBridgeEvent?: (event: AgentNativeHostBridgeEvent) => void;
@@ -23,7 +23,7 @@ const defaultStyle = {
23
23
  width: "100%",
24
24
  height: "100%",
25
25
  };
26
- export const AgentNativeFrame = forwardRef(function AgentNativeFrame({ agentUrl, agentOrigin, session, getContext, commands, actions, auth, onBridgeEvent, onBridgeReady, title = "Agent Native assistant", sandbox = "allow-scripts allow-same-origin allow-forms allow-popups allow-downloads", allow = "clipboard-read; clipboard-write; microphone; fullscreen", referrerPolicy = "strict-origin-when-cross-origin", style, onLoad, ...iframeProps }, forwardedRef) {
26
+ export const AgentNativeFrame = forwardRef(function AgentNativeFrame({ agentUrl, agentOrigin, session, getContext, commands, actions, webmcp, auth, onBridgeEvent, onBridgeReady, title = "Agent Native assistant", sandbox = "allow-scripts allow-same-origin allow-forms allow-popups allow-downloads", allow = "clipboard-read; clipboard-write; microphone; fullscreen", referrerPolicy = "strict-origin-when-cross-origin", style, onLoad, ...iframeProps }, forwardedRef) {
27
27
  const iframeRef = useRef(null);
28
28
  const bridgeRef = useRef(null);
29
29
  const resolvedOrigin = useMemo(() => agentOrigin ?? originFromUrl(agentUrl), [agentOrigin, agentUrl]);
@@ -34,6 +34,7 @@ export const AgentNativeFrame = forwardRef(function AgentNativeFrame({ agentUrl,
34
34
  getContext,
35
35
  commands,
36
36
  actions,
37
+ webmcp,
37
38
  auth,
38
39
  onEvent: onBridgeEvent,
39
40
  targetWindow: iframeRef.current?.contentWindow ?? null,
@@ -54,6 +55,7 @@ export const AgentNativeFrame = forwardRef(function AgentNativeFrame({ agentUrl,
54
55
  onBridgeReady,
55
56
  resolvedOrigin,
56
57
  session,
58
+ webmcp,
57
59
  ]);
58
60
  return (_jsx("iframe", { ...iframeProps, ref: (node) => {
59
61
  iframeRef.current = node;
@@ -1,5 +1,6 @@
1
1
  import type { AgentNativeBrowserSessionRecord, AgentNativeBrowserSessionRequest } from "../browser-sessions/types.js";
2
2
  import { type AgentNativeClientActions, type AgentNativeHostRequestOptions, type AgentNativeHostCommandHandlers, type AgentNativeHostContextGetter, type AgentNativeHostSession } from "./host-bridge.js";
3
+ import type { AgentNativeWebMcpClient } from "./webmcp.js";
3
4
  export interface AgentNativeBrowserSessionBridgeOptions extends AgentNativeHostRequestOptions {
4
5
  /** Framework browser-session endpoint. Defaults to /_agent-native/browser-sessions. */
5
6
  endpoint?: string;
@@ -17,6 +18,8 @@ export interface AgentNativeBrowserSessionBridgeOptions extends AgentNativeHostR
17
18
  getContext?: AgentNativeHostContextGetter;
18
19
  /** Direct in-app client actions exposed to backend browser-session tools. */
19
20
  actions?: AgentNativeClientActions;
21
+ /** WebMCP tools available in this document, or "host" through an iframe bridge. */
22
+ webmcp?: AgentNativeWebMcpClient | "host";
20
23
  /** Direct in-app host commands exposed to backend browser-session tools. */
21
24
  commands?: AgentNativeHostCommandHandlers;
22
25
  /** Origin label passed to direct action/command callbacks. */
@@ -1,6 +1,6 @@
1
1
  import { createPollEngine } from "../shared/poll-engine.js";
2
2
  import { agentNativePath } from "./api-path.js";
3
- import { announceAgentNativeFrameReady, defaultAgentNativeHostCommands, requestAgentNativeHostActions, requestAgentNativeHostContext, runAgentNativeHostAction, sendAgentNativeHostCommand, } from "./host-bridge.js";
3
+ import { announceAgentNativeFrameReady, defaultAgentNativeHostCommands, requestAgentNativeHostActions, requestAgentNativeHostContext, requestAgentNativeHostWebMcpTools, runAgentNativeHostAction, runAgentNativeHostWebMcpTool, sendAgentNativeHostCommand, } from "./host-bridge.js";
4
4
  const DEFAULT_ENDPOINT = "/_agent-native/browser-sessions";
5
5
  const DEFAULT_HEARTBEAT_MS = 5_000;
6
6
  const DEFAULT_POLL_MS = 500;
@@ -95,14 +95,15 @@ async function deleteJson(options, path) {
95
95
  }
96
96
  }
97
97
  function hostRequestOptions(options) {
98
- const { endpoint: _endpoint, sessionId: _sessionId, session: _session, getContext: _getContext, actions: _actions, commands: _commands, origin: _origin, label: _label, heartbeatMs: _heartbeatMs, pollMs: _pollMs, ttlMs: _ttlMs, fetch: _fetch, ...hostOptions } = options;
98
+ const { endpoint: _endpoint, sessionId: _sessionId, session: _session, getContext: _getContext, actions: _actions, webmcp: _webmcp, commands: _commands, origin: _origin, label: _label, heartbeatMs: _heartbeatMs, pollMs: _pollMs, ttlMs: _ttlMs, fetch: _fetch, ...hostOptions } = options;
99
99
  return hostOptions;
100
100
  }
101
101
  function hasDirectHost(options) {
102
102
  return Boolean(options.getContext ||
103
103
  options.actions ||
104
104
  options.commands ||
105
- options.session);
105
+ options.session ||
106
+ (options.webmcp && options.webmcp !== "host"));
106
107
  }
107
108
  function directOrigin(options) {
108
109
  return options.origin || "agent-native-embedded";
@@ -169,6 +170,35 @@ async function resolveDirectActionManifest(options) {
169
170
  .map(toActionManifest)
170
171
  .filter(Boolean);
171
172
  }
173
+ async function resolveWebMcpTools(options) {
174
+ if (!options.webmcp)
175
+ return undefined;
176
+ try {
177
+ if (options.webmcp === "host") {
178
+ return await requestAgentNativeHostWebMcpTools(hostRequestOptions(options));
179
+ }
180
+ if (!options.webmcp.supported)
181
+ return [];
182
+ return await options.webmcp.listTools();
183
+ }
184
+ catch (error) {
185
+ void error;
186
+ return undefined;
187
+ }
188
+ }
189
+ function requireDirectWebMcpClient(options) {
190
+ if (!options.webmcp || options.webmcp === "host") {
191
+ throw new Error("WebMCP is not enabled for this browser session");
192
+ }
193
+ return options.webmcp;
194
+ }
195
+ function findWebMcpTool(tools, name, origin) {
196
+ const matches = tools.filter((tool) => tool.name === name && (!origin || tool.origin === origin));
197
+ if (matches.length > 1 && !origin) {
198
+ throw new Error(`WebMCP tool "${name}" is exposed by multiple origins; origin is required`);
199
+ }
200
+ return matches[0];
201
+ }
172
202
  async function findDirectAction(options, name) {
173
203
  const actions = await resolveClientActions(options.actions);
174
204
  return actions.find((action) => action.name === name);
@@ -196,6 +226,10 @@ async function executeDirectBrowserSessionRequest(request, options) {
196
226
  if (request.type === "list-actions") {
197
227
  return resolveDirectActionManifest(options);
198
228
  }
229
+ if (request.type === "list-webmcp-tools") {
230
+ const tools = await requireDirectWebMcpClient(options).listTools();
231
+ return tools;
232
+ }
199
233
  if (request.type === "run-action") {
200
234
  if (!request.name) {
201
235
  throw new Error("Browser-session action request is missing name");
@@ -217,6 +251,18 @@ async function executeDirectBrowserSessionRequest(request, options) {
217
251
  command: (command, payload) => runDirectCommand(command, payload, request.id, options),
218
252
  });
219
253
  }
254
+ if (request.type === "run-webmcp-tool") {
255
+ if (!request.name) {
256
+ throw new Error("Browser-session WebMCP request is missing name");
257
+ }
258
+ const client = requireDirectWebMcpClient(options);
259
+ const tools = await client.listTools();
260
+ const tool = findWebMcpTool(tools, request.name, request.origin);
261
+ if (!tool) {
262
+ throw new Error(`WebMCP tool "${request.name}" is no longer available`);
263
+ }
264
+ return client.executeListedTool(tool, request.args);
265
+ }
220
266
  if (request.type === "command") {
221
267
  return runDirectCommand(request.command || "refreshData", request.payload, request.id, options);
222
268
  }
@@ -238,21 +284,39 @@ function normalizeSession(sessionId, label, hostSession, contextUrl) {
238
284
  };
239
285
  }
240
286
  async function executeBrowserSessionRequest(request, options) {
287
+ const hostOptions = hostRequestOptions(options);
288
+ if (options.webmcp === "host" && request.type === "list-webmcp-tools") {
289
+ return requestAgentNativeHostWebMcpTools(hostOptions);
290
+ }
291
+ if (options.webmcp === "host" && request.type === "run-webmcp-tool") {
292
+ if (!request.name) {
293
+ throw new Error("Browser-session WebMCP request is missing name");
294
+ }
295
+ return runAgentNativeHostWebMcpTool({ name: request.name, origin: request.origin }, request.args, hostOptions);
296
+ }
241
297
  if (hasDirectHost(options)) {
242
298
  return executeDirectBrowserSessionRequest(request, options);
243
299
  }
244
- const hostOptions = hostRequestOptions(options);
245
300
  if (request.type === "get-context") {
246
301
  return requestAgentNativeHostContext(hostOptions);
247
302
  }
248
303
  if (request.type === "list-actions") {
249
304
  return requestAgentNativeHostActions(hostOptions);
250
305
  }
306
+ if (request.type === "list-webmcp-tools") {
307
+ return requestAgentNativeHostWebMcpTools(hostOptions);
308
+ }
251
309
  if (request.type === "run-action") {
252
310
  if (!request.name)
253
311
  throw new Error("Browser-session action request is missing name");
254
312
  return runAgentNativeHostAction(request.name, request.args, hostOptions);
255
313
  }
314
+ if (request.type === "run-webmcp-tool") {
315
+ if (!request.name) {
316
+ throw new Error("Browser-session WebMCP request is missing name");
317
+ }
318
+ return runAgentNativeHostWebMcpTool({ name: request.name, origin: request.origin }, request.args, hostOptions);
319
+ }
256
320
  if (request.type === "command") {
257
321
  return sendAgentNativeHostCommand(request.command || "refreshData", request.payload, hostOptions);
258
322
  }
@@ -263,18 +327,23 @@ export function createAgentNativeBrowserSessionBridge(options = {}) {
263
327
  let fallbackSessionId = null;
264
328
  let started = false;
265
329
  let onVisibility;
330
+ let lastWebMcpTools;
266
331
  async function refreshRegistration() {
267
332
  const direct = hasDirectHost(options);
268
333
  const hostOptions = hostRequestOptions(options);
269
- const [context, actions] = direct
334
+ const [context, actions, webmcpTools] = direct
270
335
  ? await Promise.all([
271
336
  resolveDirectContext(options),
272
337
  resolveDirectActionManifest(options).catch(() => []),
338
+ resolveWebMcpTools(options),
273
339
  ])
274
340
  : await Promise.all([
275
341
  requestAgentNativeHostContext(hostOptions),
276
342
  requestAgentNativeHostActions(hostOptions).catch(() => []),
343
+ resolveWebMcpTools(options),
277
344
  ]);
345
+ if (webmcpTools !== undefined)
346
+ lastWebMcpTools = webmcpTools;
278
347
  const hostSession = context.session;
279
348
  if (!currentSessionId) {
280
349
  currentSessionId =
@@ -287,6 +356,9 @@ export function createAgentNativeBrowserSessionBridge(options = {}) {
287
356
  sessionId: currentSessionId,
288
357
  context,
289
358
  actions,
359
+ ...(lastWebMcpTools !== undefined
360
+ ? { webmcpTools: lastWebMcpTools }
361
+ : {}),
290
362
  ttlMs: options.ttlMs,
291
363
  });
292
364
  return body.session;
@@ -12,8 +12,9 @@ export { AgentNativeExtensionFrame, AgentNativeExtensionSlot, type AgentNativeEx
12
12
  export { AGENT_NATIVE_EXTENSION_MESSAGE_TYPES, buildAgentNativeExtensionHtml, createHttpAgentNativeExtensionStorage, createLocalStorageAgentNativeExtensionStorage, getAgentNativeExtensionManifest, isAgentNativeExtensionAllowedInSlot, normalizeAgentNativeExtensionSandbox, type AgentNativeExtensionDefinition, type AgentNativeExtensionManifest, type AgentNativeExtensionMessageType, type AgentNativeExtensionStorage, type AgentNativeExtensionStorageContext, type AgentNativeExtensionStorageOptions, type AgentNativeExtensionStorageRow, type AgentNativeExtensionStorageScope, type BuildAgentNativeExtensionHtmlOptions, type CreateHttpAgentNativeExtensionStorageOptions, } from "../extensions/portable-extension.js";
13
13
  export { buildSessionReplayIframeBootstrap, injectSessionReplayIframeBootstrap, } from "../../extensions/session-replay-iframe.js";
14
14
  export { SESSION_REPLAY_IFRAME_ATTRIBUTE, SESSION_REPLAY_IFRAME_PROBE, SESSION_REPLAY_IFRAME_START, SESSION_REPLAY_IFRAME_STOP, } from "../../session-replay-iframe-protocol.js";
15
- export { AGENT_NATIVE_HOST_BRIDGE_VERSION, AGENT_NATIVE_HOST_MESSAGE_TYPES, announceAgentNativeFrameReady, createAgentNativeHostBridge, defaultAgentNativeHostCommands, onAgentNativeHostInit, readAgentNativeScreenContext, requestAgentNativeHostActions, requestAgentNativeHostContext, runAgentNativeHostAction, sendAgentNativeHostCommand, type AgentNativeActionAvailability, type AgentNativeActionManifestEntry, type AgentNativeClientAction, type AgentNativeClientActionApprovalConfig, type AgentNativeClientActionGetter, type AgentNativeClientActionRuntime, type AgentNativeClientActions, type AgentNativeHostAuth, type AgentNativeHostAuthPayload, type AgentNativeHostBridge, type AgentNativeHostBridgeEvent, type AgentNativeHostBridgeOptions, type AgentNativeHostCapabilities, type AgentNativeHostCommandHandler, type AgentNativeHostCommandHandlers, type AgentNativeHostCommandRequest, type AgentNativeHostContext, type AgentNativeHostContextGetter, type AgentNativeHostInit, type AgentNativeHostMessageType, type AgentNativeHostRequestOptions, type AgentNativeHostResourceContext, type AgentNativeHostRouteContext, type AgentNativeHostSelectionContext, type AgentNativeHostSession, type AgentNativeJsonSchema, type AgentNativeScreenSnapshot, type AgentNativeScreenSnapshotOptions, type BuiltInAgentNativeHostCommand, } from "../host-bridge.js";
16
- export { AGENT_NATIVE_HOST_TOOL_NAMES, createAgentNativeHostTools, type AgentNativeHostToolDefinition, type AgentNativeHostToolName, type AgentNativeHostToolParameters, type AgentNativeHostToolSet, type CreateAgentNativeHostToolsOptions, type RunAgentNativeHostActionToolInput, type SendAgentNativeHostCommandToolInput, } from "../host-tools.js";
15
+ export { AGENT_NATIVE_HOST_BRIDGE_VERSION, AGENT_NATIVE_HOST_MESSAGE_TYPES, announceAgentNativeFrameReady, createAgentNativeHostBridge, defaultAgentNativeHostCommands, onAgentNativeHostInit, readAgentNativeScreenContext, requestAgentNativeHostActions, requestAgentNativeHostContext, requestAgentNativeHostWebMcpTools, runAgentNativeHostAction, runAgentNativeHostWebMcpTool, sendAgentNativeHostCommand, type AgentNativeActionAvailability, type AgentNativeActionManifestEntry, type AgentNativeClientAction, type AgentNativeClientActionApprovalConfig, type AgentNativeClientActionGetter, type AgentNativeClientActionRuntime, type AgentNativeClientActions, type AgentNativeHostAuth, type AgentNativeHostAuthPayload, type AgentNativeHostBridge, type AgentNativeHostBridgeEvent, type AgentNativeHostBridgeOptions, type AgentNativeHostCapabilities, type AgentNativeHostCommandHandler, type AgentNativeHostCommandHandlers, type AgentNativeHostCommandRequest, type AgentNativeHostContext, type AgentNativeHostContextGetter, type AgentNativeHostInit, type AgentNativeHostMessageType, type AgentNativeHostRequestOptions, type AgentNativeHostResourceContext, type AgentNativeHostRouteContext, type AgentNativeHostSelectionContext, type AgentNativeHostSession, type AgentNativeJsonSchema, type AgentNativeScreenSnapshot, type AgentNativeScreenSnapshotOptions, type BuiltInAgentNativeHostCommand, } from "../host-bridge.js";
16
+ export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, isAgentNativeWebMcpSupported, type AgentNativeWebMcpApprovalRequest, type AgentNativeWebMcpClient, type AgentNativeWebMcpClientOptions, type AgentNativeWebMcpRegistration, type AgentNativeWebMcpRegistrationOptions, type AgentNativeWebMcpTool, type AgentNativeWebMcpToolAnnotations, type AgentNativeWebMcpToolExecutionOptions, type AgentNativeWebMcpToolResult, } from "../webmcp.js";
17
+ export { AGENT_NATIVE_HOST_TOOL_NAMES, createAgentNativeHostTools, type AgentNativeHostToolDefinition, type AgentNativeHostToolName, type AgentNativeHostToolParameters, type AgentNativeHostToolSet, type CreateAgentNativeHostToolsOptions, type RunAgentNativeHostActionToolInput, type RunAgentNativeHostWebMcpToolInput, type SendAgentNativeHostCommandToolInput, } from "../host-tools.js";
17
18
  export { createAgentNativeBrowserSessionBridge, startAgentNativeBrowserSessionBridge, type AgentNativeBrowserSessionBridge, type AgentNativeBrowserSessionBridgeOptions, } from "../browser-session-bridge.js";
18
19
  export type { AgentNativeBrowserSession, AgentNativeBrowserSessionAction, AgentNativeBrowserSessionRecord, AgentNativeBrowserSessionRequest, AgentNativeBrowserSessionRequestStatus, AgentNativeBrowserSessionRequestType, CreateAgentNativeBrowserSessionRequestInput, RegisterAgentNativeBrowserSessionInput, } from "../../browser-sessions/types.js";
19
20
  export type { AppToFrameMessage, FrameToAppMessage, FrameMessage, CodeCompleteMessage, ChatRunningMessage, } from "../frame-protocol.js";
@@ -12,7 +12,8 @@ export { AgentNativeExtensionFrame, AgentNativeExtensionSlot, } from "../extensi
12
12
  export { AGENT_NATIVE_EXTENSION_MESSAGE_TYPES, buildAgentNativeExtensionHtml, createHttpAgentNativeExtensionStorage, createLocalStorageAgentNativeExtensionStorage, getAgentNativeExtensionManifest, isAgentNativeExtensionAllowedInSlot, normalizeAgentNativeExtensionSandbox, } from "../extensions/portable-extension.js";
13
13
  export { buildSessionReplayIframeBootstrap, injectSessionReplayIframeBootstrap, } from "../../extensions/session-replay-iframe.js";
14
14
  export { SESSION_REPLAY_IFRAME_ATTRIBUTE, SESSION_REPLAY_IFRAME_PROBE, SESSION_REPLAY_IFRAME_START, SESSION_REPLAY_IFRAME_STOP, } from "../../session-replay-iframe-protocol.js";
15
- export { AGENT_NATIVE_HOST_BRIDGE_VERSION, AGENT_NATIVE_HOST_MESSAGE_TYPES, announceAgentNativeFrameReady, createAgentNativeHostBridge, defaultAgentNativeHostCommands, onAgentNativeHostInit, readAgentNativeScreenContext, requestAgentNativeHostActions, requestAgentNativeHostContext, runAgentNativeHostAction, sendAgentNativeHostCommand, } from "../host-bridge.js";
15
+ export { AGENT_NATIVE_HOST_BRIDGE_VERSION, AGENT_NATIVE_HOST_MESSAGE_TYPES, announceAgentNativeFrameReady, createAgentNativeHostBridge, defaultAgentNativeHostCommands, onAgentNativeHostInit, readAgentNativeScreenContext, requestAgentNativeHostActions, requestAgentNativeHostContext, requestAgentNativeHostWebMcpTools, runAgentNativeHostAction, runAgentNativeHostWebMcpTool, sendAgentNativeHostCommand, } from "../host-bridge.js";
16
+ export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, isAgentNativeWebMcpSupported, } from "../webmcp.js";
16
17
  export { AGENT_NATIVE_HOST_TOOL_NAMES, createAgentNativeHostTools, } from "../host-tools.js";
17
18
  export { createAgentNativeBrowserSessionBridge, startAgentNativeBrowserSessionBridge, } from "../browser-session-bridge.js";
18
19
  export { IframeEmbed, parseEmbedBody } from "../IframeEmbed.js";