17
}
18
19
#[derive(Debug, Deserialize, JsonSchema)]
20
-pub struct DeviceScreenshotArgs {
21
- /// Physical device identifier known to CoreDevice: UDID, ECID, serial
22
- /// number, user-provided name, or DNS name.
23
- pub device: String,
20
+pub struct ScreenshotArgs {
21
+ /// Simulator name of the target to capture. Omit to use the target selected
22
+ /// with the use-target tool.
23
+ #[serde(default)]
24
+ pub simulator_name: Option<String>,
25
+ /// Simulator UDID of the target to capture.
26
+ #[serde(default)]
27
+ pub simulator_udid: Option<String>,
28
+ /// Physical device identifier known to CoreDevice (UDID, ECID, serial
29
+ /// number, user-provided name, or DNS name). Provide this to capture a
30
+ /// paired physical device instead of a simulator.
31
+ #[serde(default)]
32
+ pub device: Option<String>,
33
+}
34
+
35
+/// A target chosen once with the use-target tool and reused by later actions so
36
+/// they no longer need per-call target arguments.
37
+#[derive(Debug, Clone, Default, Deserialize, JsonSchema, serde::Serialize)]
38
+pub struct StoredTarget {
39
+ #[serde(default)]
40
+ pub simulator_name: Option<String>,
41
+ #[serde(default)]
42
+ pub simulator_udid: Option<String>,
43
+ #[serde(default)]
44
+ pub host: Option<String>,
45
+ #[serde(default)]
46
+ pub controlkit_port: Option<u16>,
47
+}
48
+
49
+#[derive(Debug, Deserialize, JsonSchema)]
50
+pub struct UseTargetArgs {
51
+ /// Exact simulator name to remember as the active target.
52
+ #[serde(default)]
53
+ pub simulator_name: Option<String>,
54
+ /// Simulator UDID to remember as the active target.
55
+ #[serde(default)]
56
+ pub simulator_udid: Option<String>,
57
+ /// ControlKit host of a physical device or remote runner to remember.
58
+ #[serde(default)]
59
+ pub host: Option<String>,
60
+ /// Local ControlKit JSON-RPC port to remember. Defaults to 12004.
61
+ #[serde(default)]
62
+ pub controlkit_port: Option<u16>,
63
}
64
65
#[derive(Debug, Deserialize, JsonSchema)]
300
$simulator_tap_name:literal,
301
$simulator_text_name:literal,
302
$simulator_swipe_name:literal,
264
- $simulator_home_name:literal,
303
$simulator_button_name:literal,
304
$simulator_orientation_get_name:literal,
305
$simulator_orientation_set_name:literal,
306
$controlkit_capabilities_name:literal,
307
$macos_click_name:literal,
308
$visionos_spatial_tap_name:literal,
271
- $watchos_tap_name:literal,
272
- $device_screenshot_name:literal
309
+ $use_target_name:literal
310
) => {
311
#[::rmcp::tool_router(router = xcrs_tool_router, vis = "pub(crate)")]
312
impl $server {
313
+ fn selected_target_slot(
314
+ ) -> &'static ::std::sync::Mutex<Option<$crate::mcp::StoredTarget>> {
315
+ static SLOT: ::std::sync::Mutex<Option<$crate::mcp::StoredTarget>> =
316
+ ::std::sync::Mutex::new(None);
317
+ &SLOT
318
+ }
319
+
320
+ fn stored_target() -> Option<$crate::mcp::StoredTarget> {
321
+ Self::selected_target_slot()
322
+ .lock()
323
+ .ok()
324
+ .and_then(|slot| slot.clone())
325
+ }
326
+
327
+ /// Fill missing target arguments from the target selected with the
328
+ /// use-target tool so per-call target fields become optional.
329
+ fn resolve_target_fields(
330
+ simulator_name: &Option<String>,
331
+ simulator_udid: &Option<String>,
332
+ host: &Option<String>,
333
+ controlkit_port: Option<u16>,
334
+ ) -> (Option<String>, Option<String>, Option<String>, Option<u16>) {
335
+ if simulator_name.is_none() && simulator_udid.is_none() && host.is_none() {
336
+ if let Some(stored) = Self::stored_target() {
337
+ return (
338
+ stored.simulator_name,
339
+ stored.simulator_udid,
340
+ stored.host,
341
+ controlkit_port.or(stored.controlkit_port),
342
+ );
343
+ }
344
+ }
345
+ (
346
+ simulator_name.clone(),
347
+ simulator_udid.clone(),
348
+ host.clone(),
349
+ controlkit_port,
350
+ )
351
+ }
352
+
353
fn simulator_from_target(
354
target: &$crate::mcp::SimulatorTargetArgs,
355
) -> ::std::result::Result<$crate::Simulator, ::rmcp::model::ErrorData> {
356
let tools = $crate::XcodeCommandLineTools::new();
280
- match (&target.simulator_udid, &target.simulator_name) {
357
+ let (simulator_name, simulator_udid, _host, _port) = Self::resolve_target_fields(
358
+ &target.simulator_name,
359
+ &target.simulator_udid,
360
+ &None,
361
+ target.controlkit_port,
362
+ );
363
+ match (&simulator_udid, &simulator_name) {
364
(Some(udid), _) => tools
365
.simctl()
366
.find_simulator_by_udid(udid)
374
::rmcp::model::ErrorData::invalid_request(error.to_string(), None)
375
}),
376
(None, None) => Err(::rmcp::model::ErrorData::invalid_request(
294
- "Provide either simulator_name or simulator_udid.",
377
+ "No target. Pass simulator_name or simulator_udid, or select one first with the use-target tool.",
378
None,
379
)),
380
}
389
(Option<$crate::Simulator>, $crate::ControlKit),
390
::rmcp::model::ErrorData,
391
> {
392
+ let (simulator_name, simulator_udid, host, controlkit_port) =
393
+ Self::resolve_target_fields(
394
+ simulator_name,
395
+ simulator_udid,
396
+ host,
397
+ controlkit_port,
398
+ );
399
if let Some(host) = host {
400
return Ok((
401
None,
312
- $crate::ControlKit::with_host(host, controlkit_port.unwrap_or(12004)),
402
+ $crate::ControlKit::with_host(&host, controlkit_port.unwrap_or(12004)),
403
));
404
}
405
let target = $crate::mcp::SimulatorTargetArgs {
316
- simulator_name: simulator_name.clone(),
317
- simulator_udid: simulator_udid.clone(),
406
+ simulator_name,
407
+ simulator_udid,
408
controlkit_port,
409
};
410
let simulator = Self::simulator_from_target(&target)?;
427
($crate::ControlKit, Option<$crate::Simulator>),
428
::rmcp::model::ErrorData,
429
> {
340
- let simulator = match (&endpoint.simulator_udid, &endpoint.simulator_name) {
430
+ let (simulator_name, simulator_udid, host, controlkit_port) =
431
+ Self::resolve_target_fields(
432
+ &endpoint.simulator_name,
433
+ &endpoint.simulator_udid,
434
+ &endpoint.host,
435
+ endpoint.controlkit_port,
436
+ );
437
+ let simulator = match (&simulator_udid, &simulator_name) {
438
(Some(udid), _) => Some(
439
$crate::XcodeCommandLineTools::new()
440
.simctl()
459
),
460
(None, None) => None,
461
};
365
- let host = endpoint.host.as_deref().unwrap_or("127.0.0.1");
462
+ let host = host.unwrap_or_else(|| "127.0.0.1".to_string());
463
Ok((
464
$crate::ControlKit::with_host(
368
- host,
369
- endpoint.controlkit_port.unwrap_or(12004),
465
+ &host,
466
+ controlkit_port.unwrap_or(12004),
467
),
468
simulator,
469
))
480
}
481
}
482
483
+ #[::rmcp::tool(
484
+ name = $use_target_name,
485
+ description = "Select the Apple target (simulator or physical/remote device) that later UI actions use by default, so you don't repeat target arguments on every call. Pass a simulator_name/simulator_udid for a simulator, or a host (plus optional controlkit_port) for a physical device or remote runner. Call this once after picking a target with the list-simulators tool; individual actions can still override it with their own target arguments."
486
+ )]
487
+ async fn use_target(
488
+ &self,
489
+ ::rmcp::handler::server::wrapper::Parameters(
490
+ args,
491
+ ): ::rmcp::handler::server::wrapper::Parameters<
492
+ $crate::mcp::UseTargetArgs,
493
+ >,
494
+ ) -> ::std::result::Result<
495
+ ::rmcp::model::CallToolResult,
496
+ ::rmcp::model::ErrorData,
497
+ > {
498
+ if args.simulator_name.is_none()
499
+ && args.simulator_udid.is_none()
500
+ && args.host.is_none()
501
+ {
502
+ return Err(::rmcp::model::ErrorData::invalid_request(
503
+ "Provide a simulator_name/simulator_udid or a host to select as the active target.",
504
+ None,
505
+ ));
506
+ }
507
+ let stored = $crate::mcp::StoredTarget {
508
+ simulator_name: args.simulator_name,
509
+ simulator_udid: args.simulator_udid,
510
+ host: args.host,
511
+ controlkit_port: args.controlkit_port,
512
+ };
513
+ match Self::selected_target_slot().lock() {
514
+ Ok(mut slot) => *slot = Some(stored.clone()),
515
+ Err(_) => {
516
+ return Err(::rmcp::model::ErrorData::internal_error(
517
+ "Failed to store the selected target.",
518
+ None,
519
+ ))
520
+ }
521
+ }
522
+ Ok(::rmcp::model::CallToolResult::success(vec![
523
+ ::rmcp::model::ContentBlock::json(&::serde_json::json!({
524
+ "selected": stored,
525
+ }))?,
526
+ ]))
527
+ }
528
+
529
#[::rmcp::tool(
530
name = $controlkit_capabilities_name,
388
- description = "Read the platform and supported capabilities from a ControlKit runner. Use a simulator name/UDID for iOS, tvOS, watchOS, or visionOS; omit them for a local macOS runner."
531
+ description = "Report the platform and supported input capabilities of the active Apple target. Use this to discover whether the target supports taps, spatial taps, buttons, or orientation before driving it. Works on a simulator, a physical/remote device, or the local Mac."
532
)]
533
async fn controlkit_capabilities(
534
&self,
558
559
#[::rmcp::tool(
560
name = $macos_click_name,
418
- description = "Click a macOS ControlKit runner at screen coordinates. Omit simulator_name and simulator_udid for the local Mac."
561
+ description = "Click the running macOS app at screen coordinates. Use this for macOS targets; use the tap tool for iOS/tvOS/watchOS and the gesture tool for visionOS."
562
)]
563
async fn macos_click(
564
&self,
592
593
#[::rmcp::tool(
594
name = $visionos_spatial_tap_name,
452
- description = "Perform a spatial tap on a visionOS ControlKit runner."
595
+ description = "Perform a spatial tap on the running visionOS app at screen coordinates. Use this for visionOS targets; use the tap tool for iOS/tvOS/watchOS and the click tool for macOS."
596
)]
597
async fn visionos_spatial_tap(
598
&self,
624
]))
625
}
626
484
- #[::rmcp::tool(
485
- name = $watchos_tap_name,
486
- description = "Tap a watchOS ControlKit runner at screen coordinates."
487
- )]
488
- async fn watchos_tap(
489
- &self,
490
- ::rmcp::handler::server::wrapper::Parameters(
491
- args,
492
- ): ::rmcp::handler::server::wrapper::Parameters<
493
- $crate::mcp::ControlKitCoordinateArgs,
494
- >,
495
- ) -> ::std::result::Result<
496
- ::rmcp::model::CallToolResult,
497
- ::rmcp::model::ErrorData,
498
- > {
499
- let endpoint = Self::endpoint_from_coordinate(&args);
500
- let (controlkit, _) = Self::controlkit_from_endpoint(&endpoint)?;
501
- controlkit
502
- .call(
503
- "device.io.tap",
504
- ::serde_json::json!({ "x": args.x, "y": args.y }),
505
- )
506
- .await
507
- .map_err(|error| {
508
- ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
509
- })?;
510
- Ok(::rmcp::model::CallToolResult::success(vec![
511
- ::rmcp::model::ContentBlock::text(format!(
512
- "Tapped ({}, {}).",
513
- args.x, args.y
514
- )),
515
- ]))
516
- }
517
-
627
#[::rmcp::tool(
628
name = $simulator_list_name,
520
- description = "List all Apple simulator devices known to Xcode, including their platform, runtime, UDID, availability, and current state."
629
+ description = "List every Apple simulator known to Xcode with its platform, runtime, UDID, availability, and current state. Start here to pick a simulator, then remember it with the use-target tool."
630
)]
631
async fn simulator_list(
632
&self,
647
648
#[::rmcp::tool(
649
name = $simulator_find_name,
541
- description = "Find an Apple simulator by its exact name and return its details as JSON."
650
+ description = "Find an Apple simulator by its exact name and return its details as JSON. Use this to resolve a UDID before selecting it with the use-target tool."
651
)]
652
async fn simulator_find(
653
&self,
673
674
#[::rmcp::tool(
675
name = $ios_app_test_name,
567
- description = "Boot an iOS simulator, install an .app bundle, optionally terminate the bundle, launch it, and return its app container path."
676
+ description = "Boot an iOS simulator, install an .app bundle, optionally terminate it first, launch it, and return its app container path. Simulator-only end-to-end setup step; use launch-app/terminate-app for an already-installed app."
677
)]
678
async fn ios_app_test(
679
&self,
711
712
#[::rmcp::tool(
713
name = $simulator_screenshot_name,
605
- description = "Capture a PNG screenshot from an Apple simulator."
714
+ description = "Capture a PNG screenshot of the running app. Targets a simulator (via simulator_name/simulator_udid or the selected target) or a paired physical device (via the device identifier). Use describe-ui instead when you need element coordinates rather than pixels."
715
)]
716
async fn simulator_screenshot(
717
&self,
718
::rmcp::handler::server::wrapper::Parameters(
719
args,
720
): ::rmcp::handler::server::wrapper::Parameters<
612
- $crate::mcp::SimulatorTargetArgs,
721
+ $crate::mcp::ScreenshotArgs,
722
>,
723
) -> ::std::result::Result<
724
::rmcp::model::CallToolResult,
725
::rmcp::model::ErrorData,
726
> {
618
- let simulator = Self::simulator_from_target(&args)?;
619
- let screenshot = $crate::XcodeCommandLineTools::new()
620
- .simctl()
621
- .screenshot(&simulator.udid)
622
- .map_err(|error| {
623
- ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
624
- })?;
625
- let encoded = $crate::encode_base64(screenshot);
626
- Ok(::rmcp::model::CallToolResult::success(vec![
627
- ::rmcp::model::ContentBlock::image(encoded, "image/png"),
628
- ]))
629
- }
630
-
631
- #[::rmcp::tool(
632
- name = $device_screenshot_name,
633
- description = "Capture a PNG screenshot from a physical iOS, tvOS, watchOS, or visionOS device paired with this Mac. Accepts a UDID, ECID, serial number, user-provided name, or DNS name."
634
- )]
635
- async fn device_screenshot(
636
- &self,
637
- ::rmcp::handler::server::wrapper::Parameters(
638
- args,
639
- ): ::rmcp::handler::server::wrapper::Parameters<
640
- $crate::mcp::DeviceScreenshotArgs,
641
- >,
642
- ) -> ::std::result::Result<
643
- ::rmcp::model::CallToolResult,
644
- ::rmcp::model::ErrorData,
645
- > {
646
- let screenshot = $crate::XcodeCommandLineTools::new()
647
- .devicectl()
648
- .screenshot(&args.device)
649
- .map_err(|error| {
650
- ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
651
- })?;
727
+ let screenshot = if let Some(device) = &args.device {
728
+ $crate::XcodeCommandLineTools::new()
729
+ .devicectl()
730
+ .screenshot(device)
731
+ .map_err(|error| {
732
+ ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
733
+ })?
734
+ } else {
735
+ let target = $crate::mcp::SimulatorTargetArgs {
736
+ simulator_name: args.simulator_name.clone(),
737
+ simulator_udid: args.simulator_udid.clone(),
738
+ controlkit_port: None,
739
+ };
740
+ let simulator = Self::simulator_from_target(&target)?;
741
+ $crate::XcodeCommandLineTools::new()
742
+ .simctl()
743
+ .screenshot(&simulator.udid)
744
+ .map_err(|error| {
745
+ ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
746
+ })?
747
+ };
748
let encoded = $crate::encode_base64(screenshot);
749
Ok(::rmcp::model::CallToolResult::success(vec![
750
::rmcp::model::ContentBlock::image(encoded, "image/png"),
753
754
#[::rmcp::tool(
755
name = $simulator_launch_app_name,
660
- description = "Launch an installed app on an Apple simulator by bundle identifier."
756
+ description = "Launch an installed app by bundle identifier on the active target. Works on a simulator or a physical/remote device."
757
)]
758
async fn simulator_launch_app(
759
&self,
766
::rmcp::model::CallToolResult,
767
::rmcp::model::ErrorData,
768
> {
673
- if let Some(host) = &args.host {
769
+ let (simulator_name, simulator_udid, host, controlkit_port) =
770
+ Self::resolve_target_fields(
771
+ &args.simulator_name,
772
+ &args.simulator_udid,
773
+ &args.host,
774
+ args.controlkit_port,
775
+ );
776
+ if let Some(host) = &host {
777
let controlkit =
675
- $crate::ControlKit::with_host(host, args.controlkit_port.unwrap_or(12004));
778
+ $crate::ControlKit::with_host(host, controlkit_port.unwrap_or(12004));
779
controlkit
780
.call(
781
"device.apps.launch",
793
]));
794
}
795
let target = $crate::mcp::SimulatorTargetArgs {
693
- simulator_name: args.simulator_name,
694
- simulator_udid: args.simulator_udid,
695
- controlkit_port: args.controlkit_port,
796
+ simulator_name,
797
+ simulator_udid,
798
+ controlkit_port,
799
};
800
let simulator = Self::simulator_from_target(&target)?;
801
$crate::XcodeCommandLineTools::new()
814
815
#[::rmcp::tool(
816
name = $simulator_terminate_app_name,
714
- description = "Terminate an installed app on an Apple simulator by bundle identifier."
817
+ description = "Terminate an installed app by bundle identifier on the active target. Works on a simulator or a physical/remote device."
818
)]
819
async fn simulator_terminate_app(
820
&self,
827
::rmcp::model::CallToolResult,
828
::rmcp::model::ErrorData,
829
> {
727
- if let Some(host) = &args.host {
830
+ let (simulator_name, simulator_udid, host, controlkit_port) =
831
+ Self::resolve_target_fields(
832
+ &args.simulator_name,
833
+ &args.simulator_udid,
834
+ &args.host,
835
+ args.controlkit_port,
836
+ );
837
+ if let Some(host) = &host {
838
let controlkit =
729
- $crate::ControlKit::with_host(host, args.controlkit_port.unwrap_or(12004));
839
+ $crate::ControlKit::with_host(host, controlkit_port.unwrap_or(12004));
840
controlkit
841
.call(
842
"device.apps.terminate",
854
]));
855
}
856
let target = $crate::mcp::SimulatorTargetArgs {
747
- simulator_name: args.simulator_name,
748
- simulator_udid: args.simulator_udid,
749
- controlkit_port: args.controlkit_port,
857
+ simulator_name,
858
+ simulator_udid,
859
+ controlkit_port,
860
};
861
let simulator = Self::simulator_from_target(&target)?;
862
$crate::XcodeCommandLineTools::new()
875
876
#[::rmcp::tool(
877
name = $simulator_open_url_name,
768
- description = "Open a URL or custom URL scheme on an Apple simulator."
878
+ description = "Open a URL or custom URL scheme on the active simulator target (simulator only)."
879
)]
880
async fn simulator_open_url(
881
&self,
910
911
#[::rmcp::tool(
912
name = $simulator_ui_dump_name,
803
- description = "Return the accessibility UI hierarchy from the foreground Apple app through ControlKit."
913
+ description = "Return the full accessibility hierarchy of the foreground app as JSON. Use this first to see what is on screen before tapping or typing. Works on a simulator or a physical/remote device. Prefer list-elements when you only need actionable elements and their tap coordinates."
914
)]
915
async fn simulator_ui_dump(
916
&self,
945
946
#[::rmcp::tool(
947
name = $simulator_list_elements_name,
838
- description = "List actionable accessibility elements and coordinates from the foreground Apple app through ControlKit."
948
+ description = "List just the actionable accessibility elements of the foreground app with their labels and tap coordinates. Use this to decide where to tap. Works on a simulator or a physical/remote device. Use describe-ui when you need the full hierarchy."
949
)]
950
async fn simulator_list_elements(
951
&self,
981
982
#[::rmcp::tool(
983
name = $simulator_tap_name,
874
- description = "Tap an Apple simulator screen at the given coordinates through ControlKit."
984
+ description = "Tap the running app at screen coordinates. Use this for iOS, tvOS, and watchOS targets, on a simulator or a physical/remote device. Use the click tool for macOS and the gesture tool for visionOS. Read coordinates from list-elements or describe-ui first."
985
)]
986
async fn simulator_tap(
987
&self,
1019
1020
#[::rmcp::tool(
1021
name = $simulator_text_name,
912
- description = "Type text into the focused Apple simulator field through ControlKit."
1022
+ description = "Type text into the focused field of the running app. Works on a simulator or a physical/remote device. Tap the field first to focus it."
1023
)]
1024
async fn simulator_text(
1025
&self,
1054
1055
#[::rmcp::tool(
1056
name = $simulator_swipe_name,
947
- description = "Swipe between two screen coordinates on an Apple simulator through ControlKit."
1057
+ description = "Swipe between two screen coordinates in the running app, e.g. to scroll. Works on a simulator or a physical/remote device."
1058
)]
1059
async fn simulator_swipe(
1060
&self,
1095
]))
1096
}
1097
988
- #[::rmcp::tool(
989
- name = $simulator_home_name,
990
- description = "Press the iOS Home button through ControlKit."
991
- )]
992
- async fn simulator_home(
993
- &self,
994
- ::rmcp::handler::server::wrapper::Parameters(
995
- args,
996
- ): ::rmcp::handler::server::wrapper::Parameters<
997
- $crate::mcp::SimulatorControlKitArgs,
998
- >,
999
- ) -> ::std::result::Result<
1000
- ::rmcp::model::CallToolResult,
1001
- ::rmcp::model::ErrorData,
1002
- > {
1003
- let (simulator, controlkit) = Self::controlkit_from_target(
1004
- &args.simulator_name,
1005
- &args.simulator_udid,
1006
- &args.host,
1007
- args.controlkit_port,
1008
- )?;
1009
- controlkit
1010
- .call("device.io.button", ::serde_json::json!({ "button": "home" }))
1011
- .await
1012
- .map_err(|error| {
1013
- ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
1014
- })?;
1015
- Ok(::rmcp::model::CallToolResult::success(vec![
1016
- ::rmcp::model::ContentBlock::text(format!(
1017
- "Pressed Home on {}.",
1018
- Self::target_label(&simulator, &args.host)
1019
- )),
1020
- ]))
1021
- }
1022
-
1098
#[::rmcp::tool(
1099
name = $simulator_button_name,
1025
- description = "Press a ControlKit remote button on an iOS or tvOS simulator. tvOS supports up, down, left, right, select, menu, home, and playPause."
1100
+ description = "Press a hardware or remote button on the running target: home (iOS), or up, down, left, right, select, menu, playPause (tvOS remote). Works on a simulator or a physical/remote device. Use this to press Home instead of a dedicated tool."
1101
)]
1102
async fn simulator_button(
1103
&self,
1151
1152
#[::rmcp::tool(
1153
name = $simulator_orientation_get_name,
1079
- description = "Read the current iOS simulator orientation through ControlKit."
1154
+ description = "Read the current screen orientation of the running target. Works on a simulator or a physical/remote device."
1155
)]
1156
async fn simulator_orientation_get(
1157
&self,
1186
1187
#[::rmcp::tool(
1188
name = $simulator_orientation_set_name,
1114
- description = "Set the iOS simulator orientation to PORTRAIT or LANDSCAPE through ControlKit."
1189
+ description = "Set the screen orientation of the running target to PORTRAIT or LANDSCAPE. Works on a simulator or a physical/remote device."
1190
)]
1191
async fn simulator_orientation_set(
1192
&self,
1234
1235
xcrs_mcp_tools!(
1236
XcrsMcpServer,
1162
- "xcrs_simulator_list",
1163
- "xcrs_simulator_find",
1164
- "xcrs_ios_app_test",
1165
- "xcrs_simulator_screenshot",
1166
- "xcrs_simulator_launch_app",
1167
- "xcrs_simulator_terminate_app",
1168
- "xcrs_simulator_open_url",
1169
- "xcrs_simulator_ui_dump",
1170
- "xcrs_simulator_list_elements",
1171
- "xcrs_simulator_tap",
1172
- "xcrs_simulator_type_text",
1173
- "xcrs_simulator_swipe",
1174
- "xcrs_simulator_press_home",
1175
- "xcrs_simulator_press_button",
1176
- "xcrs_simulator_orientation_get",
1177
- "xcrs_simulator_orientation_set",
1178
- "xcrs_controlkit_capabilities",
1179
- "xcrs_macos_click",
1180
- "xcrs_visionos_spatial_tap",
1181
- "xcrs_watchos_tap",
1182
- "xcrs_device_screenshot"
1237
+ "xcrs_list_simulators",
1238
+ "xcrs_find_simulator",
1239
+ "xcrs_boot_and_install",
1240
+ "xcrs_screenshot",
1241
+ "xcrs_launch_app",
1242
+ "xcrs_terminate_app",
1243
+ "xcrs_open_url",
1244
+ "xcrs_describe_ui",
1245
+ "xcrs_list_elements",
1246
+ "xcrs_tap",
1247
+ "xcrs_type_text",
1248
+ "xcrs_swipe",
1249
+ "xcrs_button",
1250
+ "xcrs_orientation_get",
1251
+ "xcrs_orientation_set",
1252
+ "xcrs_capabilities",
1253
+ "xcrs_click",
1254
+ "xcrs_gesture",
1255
+ "xcrs_use_target"
1256
);
1257
1258
#[rmcp::tool_handler(router = Self::xcrs_tool_router())]