@wdio/mcp 3.3.0 → 3.4.1
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.
- package/README.md +1 -0
- package/lib/server.js +103 -73
- package/lib/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -426,6 +426,7 @@ All session types support `reporting` labels that appear in the BrowserStack Aut
|
|
|
426
426
|
| `scroll` | Scroll in a direction (up/down) by specified pixels. Browser-only. |
|
|
427
427
|
| `execute_script` | Execute arbitrary JavaScript in the browser, or Appium mobile commands on devices |
|
|
428
428
|
| `switch_tab` | Switch to a different browser tab by handle or 0-based index. Browser-only. |
|
|
429
|
+
| `switch_frame` | Switch into an iframe by CSS/XPath selector, or back to the top-level frame if no selector is given. Browser-only. |
|
|
429
430
|
|
|
430
431
|
### Element Interaction (Web & Mobile)
|
|
431
432
|
|
package/lib/server.js
CHANGED
|
@@ -46,7 +46,7 @@ var package_default = {
|
|
|
46
46
|
type: "git",
|
|
47
47
|
url: "git://github.com/webdriverio/mcp.git"
|
|
48
48
|
},
|
|
49
|
-
version: "3.
|
|
49
|
+
version: "3.4.0",
|
|
50
50
|
description: "MCP server with WebdriverIO for browser and mobile app automation (iOS/Android via Appium)",
|
|
51
51
|
main: "./lib/server.js",
|
|
52
52
|
module: "./lib/server.js",
|
|
@@ -173,7 +173,8 @@ init_state();
|
|
|
173
173
|
import { z } from "zod";
|
|
174
174
|
var navigateToolDefinition = {
|
|
175
175
|
name: "navigate",
|
|
176
|
-
description: "Loads a URL in the current tab and waits for the page load event. Resets page state
|
|
176
|
+
description: "Loads a URL in the current tab and waits for the page load event. Resets page state \u2014 DOM, JS runtime, timers, and frame context are destroyed. Use instead of clicking links when the target URL is known.",
|
|
177
|
+
annotations: { title: "Navigate to URL", destructiveHint: false, idempotentHint: true },
|
|
177
178
|
inputSchema: {
|
|
178
179
|
url: z.string().min(1).describe("The URL to navigate to")
|
|
179
180
|
}
|
|
@@ -214,7 +215,8 @@ var coerceBoolean = z2.preprocess((val) => {
|
|
|
214
215
|
var defaultTimeout = 3e3;
|
|
215
216
|
var clickToolDefinition = {
|
|
216
217
|
name: "click_element",
|
|
217
|
-
description: "Waits for an element
|
|
218
|
+
description: "Waits for an element, scrolls it into view, and fires element.click(). May trigger navigation, form submission, or modals. Browser sessions only \u2014 on iOS element.click() is silently ignored; use tap_element instead. Default timeout: 3000ms.",
|
|
219
|
+
annotations: { title: "Click Element", destructiveHint: false },
|
|
218
220
|
inputSchema: {
|
|
219
221
|
selector: z3.string().describe(`Value for the selector, in the form of css selector or xpath ("button.my-class" or "//button[@class='my-class']" or "button=Exact text with spaces" or "a*=Link containing text")`),
|
|
220
222
|
scrollToView: coerceBoolean.optional().describe("Whether to scroll the element into view before clicking").default(true),
|
|
@@ -247,7 +249,8 @@ import { z as z4 } from "zod";
|
|
|
247
249
|
var defaultTimeout2 = 3e3;
|
|
248
250
|
var setValueToolDefinition = {
|
|
249
251
|
name: "set_value",
|
|
250
|
-
description: "Clears an input or textarea
|
|
252
|
+
description: "Clears an input or textarea then types the given text character by character. Always replaces existing content \u2014 clearValue() runs first. Triggers input, change, and key events which may fire validation or autocomplete. Scrolls into view by default.",
|
|
253
|
+
annotations: { title: "Set Input Value", destructiveHint: false, idempotentHint: true },
|
|
251
254
|
inputSchema: {
|
|
252
255
|
selector: z4.string().describe(`Value for the selector, in the form of css selector or xpath ("button.my-class" or "//button[@class='my-class']")`),
|
|
253
256
|
value: z4.string().describe("Text to enter into the element"),
|
|
@@ -281,7 +284,8 @@ init_state();
|
|
|
281
284
|
import { z as z5 } from "zod";
|
|
282
285
|
var scrollToolDefinition = {
|
|
283
286
|
name: "scroll",
|
|
284
|
-
description: "
|
|
287
|
+
description: "Scrolls the page vertically by a pixel amount. Browser-only \u2014 for mobile scrolling use swipe. Only supports up/down; no horizontal scrolling.",
|
|
288
|
+
annotations: { title: "Scroll Page", destructiveHint: false },
|
|
285
289
|
inputSchema: {
|
|
286
290
|
direction: z5.enum(["up", "down"]).describe("Scroll direction"),
|
|
287
291
|
pixels: z5.number().optional().default(500).describe("Number of pixels to scroll")
|
|
@@ -316,7 +320,8 @@ var scrollTool = async ({ direction, pixels = 500 }) => scrollAction(direction,
|
|
|
316
320
|
import { z as z6 } from "zod";
|
|
317
321
|
var setCookieToolDefinition = {
|
|
318
322
|
name: "set_cookie",
|
|
319
|
-
description: "Sets a browser cookie
|
|
323
|
+
description: "Sets a browser cookie on the active session. The browser must already be on the target domain \u2014 cookies cannot be set cross-domain. Use to inject session tokens or feature flags without login flows.",
|
|
324
|
+
annotations: { title: "Set Cookie", destructiveHint: false, idempotentHint: true },
|
|
320
325
|
inputSchema: {
|
|
321
326
|
name: z6.string().describe("Cookie name"),
|
|
322
327
|
value: z6.string().describe("Cookie value"),
|
|
@@ -355,7 +360,8 @@ var setCookieTool = async ({
|
|
|
355
360
|
};
|
|
356
361
|
var deleteCookiesToolDefinition = {
|
|
357
362
|
name: "delete_cookies",
|
|
358
|
-
description: "
|
|
363
|
+
description: "Deletes all cookies or a single cookie by name from the current browser session. Irreversible \u2014 deleted cookies cannot be recovered.",
|
|
364
|
+
annotations: { title: "Delete Cookies", destructiveHint: true, idempotentHint: true },
|
|
359
365
|
inputSchema: {
|
|
360
366
|
name: z6.string().optional().describe("Optional cookie name to delete a specific cookie. If not provided, deletes all cookies")
|
|
361
367
|
}
|
|
@@ -387,7 +393,8 @@ init_state();
|
|
|
387
393
|
import { z as z7 } from "zod";
|
|
388
394
|
var tapElementToolDefinition = {
|
|
389
395
|
name: "tap_element",
|
|
390
|
-
description: "
|
|
396
|
+
description: "Taps a matched element via element.tap() or at absolute screen coordinates (x, y). No scroll-into-view or wait \u2014 element must already be visible on screen. Use instead of click_element on iOS where element.click() is ignored. Provide selector OR both x and y. Mobile-only.",
|
|
397
|
+
annotations: { title: "Tap Element", destructiveHint: false },
|
|
391
398
|
inputSchema: {
|
|
392
399
|
selector: z7.string().optional().describe("Element selector (CSS, XPath, accessibility ID, or UiAutomator)"),
|
|
393
400
|
x: z7.number().optional().describe("X coordinate for screen tap (if no selector provided)"),
|
|
@@ -424,7 +431,8 @@ var tapAction = async (args) => {
|
|
|
424
431
|
var tapElementTool = async (args) => tapAction(args);
|
|
425
432
|
var swipeToolDefinition = {
|
|
426
433
|
name: "swipe",
|
|
427
|
-
description: 'Performs a full-screen swipe gesture
|
|
434
|
+
description: 'Performs a full-screen swipe gesture. Direction is content movement \u2014 "up" scrolls content upward (finger moves down). For browser scrolling use scroll; for dragging a specific element use drag_and_drop. No error if content cannot scroll further. Mobile-only.',
|
|
435
|
+
annotations: { title: "Swipe Screen", destructiveHint: false },
|
|
428
436
|
inputSchema: {
|
|
429
437
|
direction: z7.enum(["up", "down", "left", "right"]).describe("Swipe direction"),
|
|
430
438
|
duration: z7.number().min(100).max(5e3).optional().describe("Swipe duration in milliseconds (default: 500)"),
|
|
@@ -460,7 +468,8 @@ var swipeAction = async (args) => {
|
|
|
460
468
|
var swipeTool = async (args) => swipeAction(args);
|
|
461
469
|
var dragAndDropToolDefinition = {
|
|
462
470
|
name: "drag_and_drop",
|
|
463
|
-
description: "
|
|
471
|
+
description: "Drags an element to another element or to relative x/y offsets. x and y are offsets from the source element, not absolute screen coordinates (unlike tap_element). Provide targetSelector OR both x and y. Mobile-only.",
|
|
472
|
+
annotations: { title: "Drag and Drop", destructiveHint: false },
|
|
464
473
|
inputSchema: {
|
|
465
474
|
sourceSelector: z7.string().describe("Source element selector to drag"),
|
|
466
475
|
targetSelector: z7.string().optional().describe("Target element selector to drop onto"),
|
|
@@ -504,7 +513,8 @@ init_state();
|
|
|
504
513
|
import { z as z8 } from "zod";
|
|
505
514
|
var switchContextToolDefinition = {
|
|
506
515
|
name: "switch_context",
|
|
507
|
-
description: "Switches between native and webview automation contexts in a hybrid mobile app.
|
|
516
|
+
description: "Switches between native and webview automation contexts in a hybrid mobile app. In NATIVE_APP context, use accessibility IDs; in WEBVIEW_* context, use CSS/XPath. Changes persist for all subsequent commands. Accepts context name or 1-based index. Use get_contexts to discover available targets. Mobile-only.",
|
|
517
|
+
annotations: { title: "Switch Context", destructiveHint: false, idempotentHint: true },
|
|
508
518
|
inputSchema: {
|
|
509
519
|
context: z8.string().describe(
|
|
510
520
|
'Context name to switch to (e.g., "NATIVE_APP", "WEBVIEW_com.example.app", or use index from wdio://session/current/contexts resource)'
|
|
@@ -542,19 +552,22 @@ init_state();
|
|
|
542
552
|
import { z as z9 } from "zod";
|
|
543
553
|
var hideKeyboardToolDefinition = {
|
|
544
554
|
name: "hide_keyboard",
|
|
545
|
-
description: "Dismisses the
|
|
555
|
+
description: "Dismisses the on-screen keyboard on mobile. Call after text entry when the keyboard obscures elements. No-op if already hidden. Mobile-only.",
|
|
556
|
+
annotations: { title: "Hide Keyboard", destructiveHint: false, idempotentHint: true },
|
|
546
557
|
inputSchema: {}
|
|
547
558
|
};
|
|
548
559
|
var rotateDeviceToolDefinition = {
|
|
549
560
|
name: "rotate_device",
|
|
550
|
-
description: "Rotates a mobile device to portrait or landscape
|
|
561
|
+
description: "Rotates a mobile device to portrait or landscape orientation. Waits for the OS rotation animation to complete. Use to test orientation-dependent layouts. Mobile-only; no effect in browser sessions.",
|
|
562
|
+
annotations: { title: "Rotate Device", destructiveHint: false, idempotentHint: true },
|
|
551
563
|
inputSchema: {
|
|
552
564
|
orientation: z9.enum(["PORTRAIT", "LANDSCAPE"]).describe("Device orientation")
|
|
553
565
|
}
|
|
554
566
|
};
|
|
555
567
|
var setGeolocationToolDefinition = {
|
|
556
568
|
name: "set_geolocation",
|
|
557
|
-
description: "Overrides
|
|
569
|
+
description: "Overrides GPS coordinates for the session. Affects navigator.geolocation in browsers and location services on mobile. Location permissions must already be granted to the app.",
|
|
570
|
+
annotations: { title: "Set Geolocation", destructiveHint: false, idempotentHint: true },
|
|
558
571
|
inputSchema: {
|
|
559
572
|
latitude: z9.number().min(-90).max(90).describe("Latitude coordinate"),
|
|
560
573
|
longitude: z9.number().min(-180).max(180).describe("Longitude coordinate"),
|
|
@@ -619,21 +632,8 @@ init_state();
|
|
|
619
632
|
import { z as z10 } from "zod";
|
|
620
633
|
var executeScriptToolDefinition = {
|
|
621
634
|
name: "execute_script",
|
|
622
|
-
description: `Executes JavaScript in browser or mobile commands
|
|
623
|
-
|
|
624
|
-
**Option B for browser interaction** \u2014 prefer get_visible_elements or click_element/set_value with a selector instead. Use execute_script only when no dedicated tool covers the action (e.g. reading computed values, triggering custom events, scrolling to a position).
|
|
625
|
-
|
|
626
|
-
**Browser:** Runs JavaScript in page context. Use 'return' to get values back.
|
|
627
|
-
- Example: execute_script({ script: "return document.title" })
|
|
628
|
-
- Example: execute_script({ script: "return window.scrollY" })
|
|
629
|
-
- Example: execute_script({ script: "arguments[0].click()", args: ["#myButton"] })
|
|
630
|
-
|
|
631
|
-
**Mobile (Appium):** Executes mobile-specific commands using 'mobile: <command>' syntax.
|
|
632
|
-
- Press key (Android): execute_script({ script: "mobile: pressKey", args: [{ keycode: 4 }] }) // BACK=4, HOME=3
|
|
633
|
-
- Activate app: execute_script({ script: "mobile: activateApp", args: [{ appId: "com.example" }] })
|
|
634
|
-
- Terminate app: execute_script({ script: "mobile: terminateApp", args: [{ appId: "com.example" }] })
|
|
635
|
-
- Deep link: execute_script({ script: "mobile: deepLink", args: [{ url: "myapp://screen", package: "com.example" }] })
|
|
636
|
-
- Shell command (Android): execute_script({ script: "mobile: shell", args: [{ command: "dumpsys", args: ["battery"] }] })`,
|
|
635
|
+
description: `Executes arbitrary JavaScript in browser page context or Appium mobile: commands. Can read/modify DOM, trigger events, terminate apps, or run Android shell commands \u2014 use only when no dedicated tool covers the action. Browser: pass JS in script, use 'return' for values, string args matching selectors auto-resolve to elements. Mobile: use 'mobile: <command>' syntax in script with args array (e.g. "mobile: pressKey", "mobile: activateApp"). Prefer click_element/set_value/get_elements for standard interactions.`,
|
|
636
|
+
annotations: { title: "Execute Script", destructiveHint: false },
|
|
637
637
|
inputSchema: {
|
|
638
638
|
script: z10.string().describe('JavaScript code (browser) or mobile command string like "mobile: pressKey" (Appium)'),
|
|
639
639
|
args: z10.array(z10.any()).optional().describe("Arguments to pass to the script. For browser: element selectors or values. For mobile commands: command-specific parameters as objects.")
|
|
@@ -1839,7 +1839,8 @@ async function getElements(browser, params) {
|
|
|
1839
1839
|
import { encode } from "@toon-format/toon";
|
|
1840
1840
|
var getElementsToolDefinition = {
|
|
1841
1841
|
name: "get_elements",
|
|
1842
|
-
description: "
|
|
1842
|
+
description: "Returns interactable elements on the current page with selectors, text, and bounding boxes. Supports filtering by element type, viewport visibility, and pagination. Use when the wdio://session/current/elements resource does not return desired elements.",
|
|
1843
|
+
annotations: { title: "Get Visible Elements", readOnlyHint: true, idempotentHint: true },
|
|
1843
1844
|
inputSchema: {
|
|
1844
1845
|
inViewportOnly: coerceBoolean.optional().default(false).describe("Only return elements visible in the current viewport (default: false)."),
|
|
1845
1846
|
includeContainers: coerceBoolean.optional().default(false).describe("Include container elements like divs and sections (default: false)"),
|
|
@@ -1874,19 +1875,8 @@ import { z as z12 } from "zod";
|
|
|
1874
1875
|
var USER_DATA_DIR = join(tmpdir(), "chrome-debug");
|
|
1875
1876
|
var launchChromeToolDefinition = {
|
|
1876
1877
|
name: "launch_chrome",
|
|
1877
|
-
description:
|
|
1878
|
-
|
|
1879
|
-
Two modes:
|
|
1880
|
-
|
|
1881
|
-
newInstance (default): Opens a Chrome window alongside your existing one using a separate
|
|
1882
|
-
profile dir. Your current Chrome session is untouched.
|
|
1883
|
-
|
|
1884
|
-
freshSession: Launches Chrome with an empty profile (no cookies, no logins).
|
|
1885
|
-
|
|
1886
|
-
Use copyProfileFiles: true to carry over your cookies and logins into the debug session.
|
|
1887
|
-
Note: changes made during the session won't sync back to your main profile.
|
|
1888
|
-
|
|
1889
|
-
After this tool succeeds, call attach_browser() to connect.`,
|
|
1878
|
+
description: 'Launches Chrome with remote debugging enabled. Wipes and recreates a temporary profile directory on each call. Mode "newInstance" (default) runs alongside existing Chrome; "freshSession" starts with an empty profile. Set copyProfileFiles to copy cookies/logins from your Default profile \u2014 changes do not sync back. After launch, call start_session with attach: true to connect. Spawns a detached Chrome process that persists if the server exits.',
|
|
1879
|
+
annotations: { title: "Launch Chrome", destructiveHint: false },
|
|
1890
1880
|
inputSchema: {
|
|
1891
1881
|
port: z12.number().default(9222).describe("Remote debugging port (default: 9222)"),
|
|
1892
1882
|
mode: z12.enum(["newInstance", "freshSession"]).default("newInstance").describe(
|
|
@@ -1990,14 +1980,8 @@ import { z as z13 } from "zod";
|
|
|
1990
1980
|
var restoreFunctions = /* @__PURE__ */ new Map();
|
|
1991
1981
|
var emulateDeviceToolDefinition = {
|
|
1992
1982
|
name: "emulate_device",
|
|
1993
|
-
description:
|
|
1994
|
-
|
|
1995
|
-
Requires a BiDi-enabled session: start_browser({ capabilities: { webSocketUrl: true } })
|
|
1996
|
-
|
|
1997
|
-
Usage:
|
|
1998
|
-
emulate_device() \u2014 list available device presets
|
|
1999
|
-
emulate_device({ device: "iPhone 15" }) \u2014 activate emulation
|
|
2000
|
-
emulate_device({ device: "reset" }) \u2014 restore desktop defaults`,
|
|
1983
|
+
description: 'Emulates a mobile or tablet device in the current browser session by setting viewport, DPR, user-agent, and touch events. Requires a BiDi-enabled session (start_session with capabilities: { webSocketUrl: true }). Omit device to list available presets. Pass "reset" to restore desktop defaults. Changes persist for all subsequent tool calls until reset or session close. Browser-only.',
|
|
1984
|
+
annotations: { title: "Emulate Device", destructiveHint: false, idempotentHint: true },
|
|
2001
1985
|
inputSchema: {
|
|
2002
1986
|
device: z13.string().optional().describe(
|
|
2003
1987
|
'Device preset name (e.g. "iPhone 15", "Pixel 7"). Omit to list available presets. Pass "reset" to restore desktop defaults.'
|
|
@@ -3152,7 +3136,8 @@ function getAppiumServerConfig(overrides) {
|
|
|
3152
3136
|
return {
|
|
3153
3137
|
hostname: overrides?.hostname || process.env.APPIUM_URL || "127.0.0.1",
|
|
3154
3138
|
port: overrides?.port || Number(process.env.APPIUM_URL_PORT) || 4723,
|
|
3155
|
-
path: overrides?.path || process.env.APPIUM_PATH || "/"
|
|
3139
|
+
path: overrides?.path || process.env.APPIUM_PATH || "/",
|
|
3140
|
+
protocol: overrides?.protocol || process.env.APPIUM_PROTOCOL || "http"
|
|
3156
3141
|
};
|
|
3157
3142
|
}
|
|
3158
3143
|
function buildIOSCapabilities(appPath, options) {
|
|
@@ -3235,12 +3220,12 @@ var LocalAppiumProvider = class {
|
|
|
3235
3220
|
name = "local-appium";
|
|
3236
3221
|
getConnectionConfig(options) {
|
|
3237
3222
|
const appiumConfig = options.appiumConfig;
|
|
3238
|
-
|
|
3223
|
+
return getAppiumServerConfig({
|
|
3239
3224
|
hostname: appiumConfig?.host,
|
|
3240
3225
|
port: appiumConfig?.port,
|
|
3241
|
-
path: appiumConfig?.path
|
|
3226
|
+
path: appiumConfig?.path,
|
|
3227
|
+
protocol: appiumConfig?.protocol
|
|
3242
3228
|
});
|
|
3243
|
-
return { protocol: "http", ...config };
|
|
3244
3229
|
}
|
|
3245
3230
|
buildCapabilities(options) {
|
|
3246
3231
|
const platform2 = options.platform;
|
|
@@ -3501,7 +3486,8 @@ var browserEnum = z14.enum(["chrome", "firefox", "edge", "safari"]);
|
|
|
3501
3486
|
var automationEnum = z14.enum(["XCUITest", "UiAutomator2"]);
|
|
3502
3487
|
var startSessionToolDefinition = {
|
|
3503
3488
|
name: "start_session",
|
|
3504
|
-
description: 'Starts a
|
|
3489
|
+
description: 'Starts a browser or mobile automation session. Only one active session at a time \u2014 starting a new one closes the existing session first. Use platform "browser" with a browser name, or "ios"/"android" with deviceName. Set attach: true to connect to a running Chrome via CDP instead of launching a new browser.',
|
|
3490
|
+
annotations: { title: "Start Session", destructiveHint: false },
|
|
3505
3491
|
inputSchema: {
|
|
3506
3492
|
provider: z14.enum(["local", "browserstack"]).optional().default("local").describe("Session provider (default: local)"),
|
|
3507
3493
|
platform: platformEnum.describe("Session platform type"),
|
|
@@ -3538,7 +3524,8 @@ var startSessionToolDefinition = {
|
|
|
3538
3524
|
appiumConfig: z14.object({
|
|
3539
3525
|
host: z14.string().optional(),
|
|
3540
3526
|
port: z14.number().optional(),
|
|
3541
|
-
path: z14.string().optional()
|
|
3527
|
+
path: z14.string().optional(),
|
|
3528
|
+
protocol: z14.string().optional()
|
|
3542
3529
|
}).optional().describe("Appium server connection (local provider only)"),
|
|
3543
3530
|
browserstackLocal: z14.union([coerceBoolean, z14.literal("external")]).optional().default(false).describe('Enable BrowserStack Local tunnel routing (BrowserStack only, default: false). true = auto-start tunnel before session and stop on close. "external" = tunnel already running externally, set local: true in capabilities only.'),
|
|
3544
3531
|
navigationUrl: z14.string().optional().describe("URL to navigate to after starting"),
|
|
@@ -3547,7 +3534,8 @@ var startSessionToolDefinition = {
|
|
|
3547
3534
|
};
|
|
3548
3535
|
var closeSessionToolDefinition = {
|
|
3549
3536
|
name: "close_session",
|
|
3550
|
-
description: "Closes
|
|
3537
|
+
description: "Closes the current session or detaches without terminating. Detach preserves app state on the Appium server \u2014 sessions with noReset: true auto-detach by default. Closing a browser attach session terminates chromedriver but the Chrome process spawned by launch_chrome remains running.",
|
|
3538
|
+
annotations: { title: "Close Session", destructiveHint: true },
|
|
3551
3539
|
inputSchema: {
|
|
3552
3540
|
detach: coerceBoolean.optional().describe("If true, disconnect without terminating (preserves app state). Default: false")
|
|
3553
3541
|
}
|
|
@@ -3797,7 +3785,8 @@ init_state();
|
|
|
3797
3785
|
import { z as z15 } from "zod";
|
|
3798
3786
|
var switchTabToolDefinition = {
|
|
3799
3787
|
name: "switch_tab",
|
|
3800
|
-
description: "Focuses a browser tab by window handle or 0-based index. All subsequent tool calls operate on the
|
|
3788
|
+
description: "Focuses a browser tab by window handle or 0-based index. All subsequent tool calls operate on the active tab. Provide handle OR index \u2014 use get_tabs to find them. Browser-only; use switch_context for mobile webviews.",
|
|
3789
|
+
annotations: { title: "Switch Browser Tab", destructiveHint: false, idempotentHint: true },
|
|
3801
3790
|
inputSchema: {
|
|
3802
3791
|
handle: z15.string().optional().describe("Window handle to switch to"),
|
|
3803
3792
|
index: z15.number().int().min(0).optional().describe("0-based tab index to switch to")
|
|
@@ -3823,9 +3812,40 @@ var switchTabTool = async ({ handle, index }) => {
|
|
|
3823
3812
|
}
|
|
3824
3813
|
};
|
|
3825
3814
|
|
|
3815
|
+
// src/tools/switch-frame.tool.ts
|
|
3816
|
+
init_state();
|
|
3817
|
+
import { z as z16 } from "zod";
|
|
3818
|
+
var switchFrameToolDefinition = {
|
|
3819
|
+
name: "switch_frame",
|
|
3820
|
+
description: "Switches WebDriver frame context into an iframe by CSS/XPath selector, or back to top-level if selector is omitted. Changes persist \u2014 all subsequent click_element, set_value, get_elements calls operate within the switched frame until you switch back. Waits up to 5s for the iframe. Browser-only.",
|
|
3821
|
+
annotations: { title: "Switch Frame", destructiveHint: false, idempotentHint: true },
|
|
3822
|
+
inputSchema: {
|
|
3823
|
+
selector: z16.string().optional().describe(
|
|
3824
|
+
"CSS/XPath selector for the iframe element. Omit to switch back to the top-level frame."
|
|
3825
|
+
)
|
|
3826
|
+
}
|
|
3827
|
+
};
|
|
3828
|
+
var switchFrameTool = async ({
|
|
3829
|
+
selector
|
|
3830
|
+
}) => {
|
|
3831
|
+
try {
|
|
3832
|
+
const browser = getBrowser();
|
|
3833
|
+
if (!selector) {
|
|
3834
|
+
await browser.switchFrame(null);
|
|
3835
|
+
return { content: [{ type: "text", text: "Switched back to top-level frame" }] };
|
|
3836
|
+
}
|
|
3837
|
+
const iframe = await browser.$(selector);
|
|
3838
|
+
await iframe.waitForExist({ timeout: 5e3 });
|
|
3839
|
+
await browser.switchFrame(iframe);
|
|
3840
|
+
return { content: [{ type: "text", text: `Switched to iframe: ${selector}` }] };
|
|
3841
|
+
} catch (e) {
|
|
3842
|
+
return { isError: true, content: [{ type: "text", text: `Error switching frame: ${e}` }] };
|
|
3843
|
+
}
|
|
3844
|
+
};
|
|
3845
|
+
|
|
3826
3846
|
// src/tools/browserstack.tool.ts
|
|
3827
3847
|
import { existsSync as existsSync2, createReadStream } from "fs";
|
|
3828
|
-
import { z as
|
|
3848
|
+
import { z as z17 } from "zod";
|
|
3829
3849
|
var BS_API = "https://api-cloud.browserstack.com";
|
|
3830
3850
|
function getAuth() {
|
|
3831
3851
|
const user = process.env.BROWSERSTACK_USERNAME;
|
|
@@ -3843,10 +3863,11 @@ function formatAppList(apps) {
|
|
|
3843
3863
|
var listAppsToolDefinition = {
|
|
3844
3864
|
name: "list_apps",
|
|
3845
3865
|
description: "List apps uploaded to BrowserStack App Automate. Reads BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY from environment.",
|
|
3866
|
+
annotations: { title: "List BrowserStack Apps", readOnlyHint: true, idempotentHint: true },
|
|
3846
3867
|
inputSchema: {
|
|
3847
|
-
sortBy:
|
|
3868
|
+
sortBy: z17.enum(["app_name", "uploaded_at"]).optional().default("uploaded_at").describe("Sort order for results"),
|
|
3848
3869
|
organizationWide: coerceBoolean.optional().default(false).describe("List apps uploaded by all users in the organization (uses recent_group_apps endpoint). Defaults to false (own uploads only)."),
|
|
3849
|
-
limit:
|
|
3870
|
+
limit: z17.number().int().min(1).optional().default(20).describe("Maximum number of apps to return (only applies when organizationWide is true, default 20)")
|
|
3850
3871
|
}
|
|
3851
3872
|
};
|
|
3852
3873
|
var listAppsTool = async ({ sortBy = "uploaded_at", organizationWide = false, limit = 20 }) => {
|
|
@@ -3875,9 +3896,10 @@ var listAppsTool = async ({ sortBy = "uploaded_at", organizationWide = false, li
|
|
|
3875
3896
|
var uploadAppToolDefinition = {
|
|
3876
3897
|
name: "upload_app",
|
|
3877
3898
|
description: "Upload a local .apk or .ipa to BrowserStack App Automate. Returns a bs:// URL for use in start_session.",
|
|
3899
|
+
annotations: { title: "Upload App to BrowserStack", destructiveHint: false },
|
|
3878
3900
|
inputSchema: {
|
|
3879
|
-
path:
|
|
3880
|
-
customId:
|
|
3901
|
+
path: z17.string().describe("Absolute path to the .apk or .ipa file"),
|
|
3902
|
+
customId: z17.string().optional().describe("Optional custom ID for the app (used to reference it later)")
|
|
3881
3903
|
}
|
|
3882
3904
|
};
|
|
3883
3905
|
var uploadAppTool = async ({ path, customId }) => {
|
|
@@ -3919,6 +3941,7 @@ Use this URL as the "app" parameter in start_session with provider: "browserstac
|
|
|
3919
3941
|
var screenshotToolDefinition = {
|
|
3920
3942
|
name: "get_screenshot",
|
|
3921
3943
|
description: "Takes a screenshot of the current page or screen and returns a base64-encoded image, resized and compressed for model context limits.",
|
|
3944
|
+
annotations: { title: "Get Screenshot", readOnlyHint: true, idempotentHint: true },
|
|
3922
3945
|
inputSchema: {}
|
|
3923
3946
|
};
|
|
3924
3947
|
var screenshotTool = async () => {
|
|
@@ -3930,14 +3953,15 @@ var screenshotTool = async () => {
|
|
|
3930
3953
|
};
|
|
3931
3954
|
|
|
3932
3955
|
// src/tools/accessibility.tool.ts
|
|
3933
|
-
import { z as
|
|
3956
|
+
import { z as z18 } from "zod";
|
|
3934
3957
|
var accessibilityToolDefinition = {
|
|
3935
3958
|
name: "get_accessibility_tree",
|
|
3936
3959
|
description: "Returns the page accessibility tree with roles, names, and selectors. Browser-only. Supports filtering by ARIA roles and pagination via limit/offset.",
|
|
3960
|
+
annotations: { title: "Get Accessibility Tree", readOnlyHint: true, idempotentHint: true },
|
|
3937
3961
|
inputSchema: {
|
|
3938
|
-
limit:
|
|
3939
|
-
offset:
|
|
3940
|
-
roles:
|
|
3962
|
+
limit: z18.number().optional().default(0).describe("Maximum number of nodes to return (0 = no limit)"),
|
|
3963
|
+
offset: z18.number().optional().default(0).describe("Number of nodes to skip for pagination"),
|
|
3964
|
+
roles: z18.array(z18.string()).optional().describe('Filter by ARIA roles, e.g. ["button", "link", "heading"]')
|
|
3941
3965
|
}
|
|
3942
3966
|
};
|
|
3943
3967
|
var accessibilityTool = async ({ limit = 0, offset = 0, roles }) => {
|
|
@@ -3952,6 +3976,7 @@ var accessibilityTool = async ({ limit = 0, offset = 0, roles }) => {
|
|
|
3952
3976
|
var getTabsToolDefinition = {
|
|
3953
3977
|
name: "get_tabs",
|
|
3954
3978
|
description: "Lists all browser tabs with handle, title, URL, and which is active. Use before switch_tab to find the target handle or index. Browser-only.",
|
|
3979
|
+
annotations: { title: "Get Browser Tabs", readOnlyHint: true, idempotentHint: true },
|
|
3955
3980
|
inputSchema: {}
|
|
3956
3981
|
};
|
|
3957
3982
|
var getTabsTool = async () => {
|
|
@@ -3966,6 +3991,7 @@ var getTabsTool = async () => {
|
|
|
3966
3991
|
var getContextsToolDefinition = {
|
|
3967
3992
|
name: "get_contexts",
|
|
3968
3993
|
description: "Returns available automation contexts and the currently active one. Use before switch_context to discover NATIVE_APP and WEBVIEW_* targets. Mobile-only.",
|
|
3994
|
+
annotations: { title: "Get Automation Contexts", readOnlyHint: true, idempotentHint: true },
|
|
3969
3995
|
inputSchema: {}
|
|
3970
3996
|
};
|
|
3971
3997
|
var getContextsTool = async () => {
|
|
@@ -3984,12 +4010,13 @@ var getContextsTool = async () => {
|
|
|
3984
4010
|
};
|
|
3985
4011
|
|
|
3986
4012
|
// src/tools/app-state.tool.ts
|
|
3987
|
-
import { z as
|
|
4013
|
+
import { z as z19 } from "zod";
|
|
3988
4014
|
var appStateToolDefinition = {
|
|
3989
4015
|
name: "get_app_state",
|
|
3990
4016
|
description: "Returns the current state of a mobile app: not installed, not running, background, or foreground. Mobile-only.",
|
|
4017
|
+
annotations: { title: "Get App State", readOnlyHint: true, idempotentHint: true },
|
|
3991
4018
|
inputSchema: {
|
|
3992
|
-
bundleId:
|
|
4019
|
+
bundleId: z19.string().describe('App bundle ID (iOS) or package name (Android), e.g. "com.example.app"')
|
|
3993
4020
|
}
|
|
3994
4021
|
};
|
|
3995
4022
|
var appStateTool = async ({ bundleId }) => {
|
|
@@ -4001,12 +4028,13 @@ var appStateTool = async ({ bundleId }) => {
|
|
|
4001
4028
|
};
|
|
4002
4029
|
|
|
4003
4030
|
// src/tools/get-cookies.tool.ts
|
|
4004
|
-
import { z as
|
|
4031
|
+
import { z as z20 } from "zod";
|
|
4005
4032
|
var getCookiesToolDefinition = {
|
|
4006
4033
|
name: "get_cookies",
|
|
4007
4034
|
description: "Returns all cookies for the current session, or a single cookie by name. Use to verify auth state, session tokens, or feature flags after login flows.",
|
|
4035
|
+
annotations: { title: "Get Cookies", readOnlyHint: true, idempotentHint: true },
|
|
4008
4036
|
inputSchema: {
|
|
4009
|
-
name:
|
|
4037
|
+
name: z20.string().optional().describe("Cookie name to retrieve a specific cookie. If omitted, returns all cookies.")
|
|
4010
4038
|
}
|
|
4011
4039
|
};
|
|
4012
4040
|
var getCookiesTool = async ({ name }) => {
|
|
@@ -4038,7 +4066,8 @@ function createServer() {
|
|
|
4038
4066
|
});
|
|
4039
4067
|
const registerTool = (definition, callback) => server.registerTool(definition.name, {
|
|
4040
4068
|
description: definition.description,
|
|
4041
|
-
inputSchema: definition.inputSchema
|
|
4069
|
+
inputSchema: definition.inputSchema,
|
|
4070
|
+
...definition.annotations && { annotations: definition.annotations }
|
|
4042
4071
|
}, callback);
|
|
4043
4072
|
const registerResource = (definition) => {
|
|
4044
4073
|
if ("uri" in definition) {
|
|
@@ -4063,6 +4092,7 @@ function createServer() {
|
|
|
4063
4092
|
registerTool(emulateDeviceToolDefinition, emulateDeviceTool);
|
|
4064
4093
|
registerTool(navigateToolDefinition, withRecording("navigate", navigateTool));
|
|
4065
4094
|
registerTool(switchTabToolDefinition, switchTabTool);
|
|
4095
|
+
registerTool(switchFrameToolDefinition, switchFrameTool);
|
|
4066
4096
|
registerTool(scrollToolDefinition, withRecording("scroll", scrollTool));
|
|
4067
4097
|
registerTool(clickToolDefinition, withRecording("click_element", clickTool));
|
|
4068
4098
|
registerTool(setValueToolDefinition, withRecording("set_value", setValueTool));
|