@wdio/mcp 3.4.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/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.3.0",
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 (DOM, JS runtime). Use instead of clicking links to go directly to a known URL.",
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 to exist, scrolls it into view, and calls element.click(). For browser sessions. On iOS, element.click() is sometimes ignored \u2014 use tap_element (which calls element.tap()) instead.",
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 and types the given text. Always replaces existing content. Fails if the element is not found or not interactable within the timeout.",
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: "scrolls the page by specified pixels (browser only). For mobile, use the swipe tool.",
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 for 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 going through login flows.",
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: "deletes all cookies or a specific cookie by name",
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: "Calls element.tap() on a matched element or taps at absolute screen coordinates. Use on iOS when element.click() (click_element) is ignored \u2014 tap is the native gesture iOS responds to. Mobile-only.",
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 on mobile. Direction is content movement (e.g. "up" scrolls a list upward, not the finger direction). Use to scroll past visible bounds; for moving a specific element use drag_and_drop. Mobile-only \u2014 use scroll for browsers.',
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: "drags an element to another element or coordinates (mobile)",
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. Required before using CSS/XPath selectors inside an embedded webview \u2014 switch to WEBVIEW_* first, then switch back to NATIVE_APP for native elements. List available contexts using get_contexts tool or wdio://session/current/contexts resource.",
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 software keyboard on mobile. Call after text entry when the keyboard obscures elements you need to interact with next. No-op if already hidden. Mobile-only.",
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 and waits for the OS rotation to complete. Use to test orientation-dependent layouts. Mobile-only; no effect in browser sessions.",
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 the device GPS coordinates for the session. Affects navigator.geolocation on web and location services on mobile. Location permissions must be granted to the app before calling this.",
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 via Appium.
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: "Get interactable elements on the current page. Use when wdio://session/current/elements does not return the desired elements.",
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: `Prepares and launches Chrome with remote debugging enabled so attach_browser() can connect.
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: `Emulate a mobile or tablet device in the current browser session (sets viewport, DPR, user-agent, touch events).
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.'
@@ -3502,7 +3486,8 @@ var browserEnum = z14.enum(["chrome", "firefox", "edge", "safari"]);
3502
3486
  var automationEnum = z14.enum(["XCUITest", "UiAutomator2"]);
3503
3487
  var startSessionToolDefinition = {
3504
3488
  name: "start_session",
3505
- description: 'Starts a new browser or mobile automation session. Only one active session at a time \u2014 starting a new one closes the existing one. Use platform "browser" with a browser name, or "ios"/"android" with a deviceName. Use attach mode to connect to an already-running Chrome instance via CDP.',
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 },
3506
3491
  inputSchema: {
3507
3492
  provider: z14.enum(["local", "browserstack"]).optional().default("local").describe("Session provider (default: local)"),
3508
3493
  platform: platformEnum.describe("Session platform type"),
@@ -3549,7 +3534,8 @@ var startSessionToolDefinition = {
3549
3534
  };
3550
3535
  var closeSessionToolDefinition = {
3551
3536
  name: "close_session",
3552
- description: "Closes or detaches from the current session. Detach disconnects without terminating the process, preserving app state on the Appium server. Sessions started with noReset: true auto-detach by default.",
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 },
3553
3539
  inputSchema: {
3554
3540
  detach: coerceBoolean.optional().describe("If true, disconnect without terminating (preserves app state). Default: false")
3555
3541
  }
@@ -3799,7 +3785,8 @@ init_state();
3799
3785
  import { z as z15 } from "zod";
3800
3786
  var switchTabToolDefinition = {
3801
3787
  name: "switch_tab",
3802
- description: "Focuses a browser tab by window handle or 0-based index. All subsequent tool calls operate on the newly active tab. Get handles from get_tabs tool or wdio://session/current/tabs resource. Browser-only \u2014 use switch_context for mobile webviews.",
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 },
3803
3790
  inputSchema: {
3804
3791
  handle: z15.string().optional().describe("Window handle to switch to"),
3805
3792
  index: z15.number().int().min(0).optional().describe("0-based tab index to switch to")
@@ -3830,7 +3817,8 @@ init_state();
3830
3817
  import { z as z16 } from "zod";
3831
3818
  var switchFrameToolDefinition = {
3832
3819
  name: "switch_frame",
3833
- description: "Switches into an iframe by CSS or XPath selector, or back to the top-level frame if no selector is given. Required before interacting with elements inside iframes \u2014 click, set_value, and get_elements only see the current frame context. Browser-only.",
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 },
3834
3822
  inputSchema: {
3835
3823
  selector: z16.string().optional().describe(
3836
3824
  "CSS/XPath selector for the iframe element. Omit to switch back to the top-level frame."
@@ -3875,6 +3863,7 @@ function formatAppList(apps) {
3875
3863
  var listAppsToolDefinition = {
3876
3864
  name: "list_apps",
3877
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 },
3878
3867
  inputSchema: {
3879
3868
  sortBy: z17.enum(["app_name", "uploaded_at"]).optional().default("uploaded_at").describe("Sort order for results"),
3880
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)."),
@@ -3907,6 +3896,7 @@ var listAppsTool = async ({ sortBy = "uploaded_at", organizationWide = false, li
3907
3896
  var uploadAppToolDefinition = {
3908
3897
  name: "upload_app",
3909
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 },
3910
3900
  inputSchema: {
3911
3901
  path: z17.string().describe("Absolute path to the .apk or .ipa file"),
3912
3902
  customId: z17.string().optional().describe("Optional custom ID for the app (used to reference it later)")
@@ -3951,6 +3941,7 @@ Use this URL as the "app" parameter in start_session with provider: "browserstac
3951
3941
  var screenshotToolDefinition = {
3952
3942
  name: "get_screenshot",
3953
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 },
3954
3945
  inputSchema: {}
3955
3946
  };
3956
3947
  var screenshotTool = async () => {
@@ -3966,6 +3957,7 @@ import { z as z18 } from "zod";
3966
3957
  var accessibilityToolDefinition = {
3967
3958
  name: "get_accessibility_tree",
3968
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 },
3969
3961
  inputSchema: {
3970
3962
  limit: z18.number().optional().default(0).describe("Maximum number of nodes to return (0 = no limit)"),
3971
3963
  offset: z18.number().optional().default(0).describe("Number of nodes to skip for pagination"),
@@ -3984,6 +3976,7 @@ var accessibilityTool = async ({ limit = 0, offset = 0, roles }) => {
3984
3976
  var getTabsToolDefinition = {
3985
3977
  name: "get_tabs",
3986
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 },
3987
3980
  inputSchema: {}
3988
3981
  };
3989
3982
  var getTabsTool = async () => {
@@ -3998,6 +3991,7 @@ var getTabsTool = async () => {
3998
3991
  var getContextsToolDefinition = {
3999
3992
  name: "get_contexts",
4000
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 },
4001
3995
  inputSchema: {}
4002
3996
  };
4003
3997
  var getContextsTool = async () => {
@@ -4020,6 +4014,7 @@ import { z as z19 } from "zod";
4020
4014
  var appStateToolDefinition = {
4021
4015
  name: "get_app_state",
4022
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 },
4023
4018
  inputSchema: {
4024
4019
  bundleId: z19.string().describe('App bundle ID (iOS) or package name (Android), e.g. "com.example.app"')
4025
4020
  }
@@ -4037,6 +4032,7 @@ import { z as z20 } from "zod";
4037
4032
  var getCookiesToolDefinition = {
4038
4033
  name: "get_cookies",
4039
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 },
4040
4036
  inputSchema: {
4041
4037
  name: z20.string().optional().describe("Cookie name to retrieve a specific cookie. If omitted, returns all cookies.")
4042
4038
  }
@@ -4070,7 +4066,8 @@ function createServer() {
4070
4066
  });
4071
4067
  const registerTool = (definition, callback) => server.registerTool(definition.name, {
4072
4068
  description: definition.description,
4073
- inputSchema: definition.inputSchema
4069
+ inputSchema: definition.inputSchema,
4070
+ ...definition.annotations && { annotations: definition.annotations }
4074
4071
  }, callback);
4075
4072
  const registerResource = (definition) => {
4076
4073
  if ("uri" in definition) {