2 files changed
+80
-80
crates/xcrs/src/mcp.rs
+68
-68
@@ -41,9 +41,9 @@ pub struct SimulatorTargetArgs {
41
/// Simulator UDID.
42
#[serde(default)]
43
pub simulator_udid: Option<String>,
44
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
44
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
45
#[serde(default)]
46
- pub devicekit_port: Option<u16>,
46
+ pub controlkit_port: Option<u16>,
47
}
48
49
#[derive(Debug, Deserialize, JsonSchema)]
@@ -54,9 +54,9 @@ pub struct SimulatorAppArgs {
54
/// Simulator UDID.
55
#[serde(default)]
56
pub simulator_udid: Option<String>,
57
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
57
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
58
#[serde(default)]
59
- pub devicekit_port: Option<u16>,
59
+ pub controlkit_port: Option<u16>,
60
/// App bundle identifier.
61
pub bundle_id: String,
62
}
@@ -69,9 +69,9 @@ pub struct SimulatorOpenUrlArgs {
69
/// Simulator UDID.
70
#[serde(default)]
71
pub simulator_udid: Option<String>,
72
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
72
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
73
#[serde(default)]
74
- pub devicekit_port: Option<u16>,
74
+ pub controlkit_port: Option<u16>,
75
/// HTTP(S) URL or custom URL scheme.
76
pub url: String,
77
}
@@ -84,9 +84,9 @@ pub struct SimulatorTapArgs {
84
/// Simulator UDID.
85
#[serde(default)]
86
pub simulator_udid: Option<String>,
87
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
87
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
88
#[serde(default)]
89
- pub devicekit_port: Option<u16>,
89
+ pub controlkit_port: Option<u16>,
90
/// Horizontal screen coordinate.
91
pub x: f32,
92
/// Vertical screen coordinate.
@@ -101,9 +101,9 @@ pub struct SimulatorTextArgs {
101
/// Simulator UDID.
102
#[serde(default)]
103
pub simulator_udid: Option<String>,
104
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
104
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
105
#[serde(default)]
106
- pub devicekit_port: Option<u16>,
106
+ pub controlkit_port: Option<u16>,
107
/// Text to type into the focused field.
108
pub text: String,
109
}
@@ -116,9 +116,9 @@ pub struct SimulatorSwipeArgs {
116
/// Simulator UDID.
117
#[serde(default)]
118
pub simulator_udid: Option<String>,
119
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
119
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
120
#[serde(default)]
121
- pub devicekit_port: Option<u16>,
121
+ pub controlkit_port: Option<u16>,
122
/// Swipe start horizontal coordinate.
123
pub x1: i32,
124
/// Swipe start vertical coordinate.
@@ -137,24 +137,24 @@ pub struct SimulatorOrientationArgs {
137
/// Simulator UDID.
138
#[serde(default)]
139
pub simulator_udid: Option<String>,
140
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
140
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
141
#[serde(default)]
142
- pub devicekit_port: Option<u16>,
142
+ pub controlkit_port: Option<u16>,
143
/// Desired orientation: PORTRAIT or LANDSCAPE.
144
pub orientation: String,
145
}
146
147
#[derive(Debug, Deserialize, JsonSchema)]
148
-pub struct SimulatorDeviceKitArgs {
148
+pub struct SimulatorControlKitArgs {
149
/// Exact simulator name.
150
#[serde(default)]
151
pub simulator_name: Option<String>,
152
/// Simulator UDID.
153
#[serde(default)]
154
pub simulator_udid: Option<String>,
155
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
155
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
156
#[serde(default)]
157
- pub devicekit_port: Option<u16>,
157
+ pub controlkit_port: Option<u16>,
158
}
159
160
#[derive(Debug, Deserialize, JsonSchema)]
@@ -165,9 +165,9 @@ pub struct SimulatorButtonArgs {
165
/// Simulator UDID.
166
#[serde(default)]
167
pub simulator_udid: Option<String>,
168
- /// Local DeviceKit JSON-RPC port. Defaults to 12004.
168
+ /// Local ControlKit JSON-RPC port. Defaults to 12004.
169
#[serde(default)]
170
- pub devicekit_port: Option<u16>,
170
+ pub controlkit_port: Option<u16>,
171
/// tvOS button: up, down, left, right, select, menu, home, or playPause.
172
pub button: String,
173
}
@@ -228,23 +228,23 @@ macro_rules! xcrs_mcp_tools {
228
}
229
}
230
231
- fn devicekit_from_target(
231
+ fn controlkit_from_target(
232
simulator_name: &Option<String>,
233
simulator_udid: &Option<String>,
234
- devicekit_port: Option<u16>,
234
+ controlkit_port: Option<u16>,
235
) -> ::std::result::Result<
236
- ($crate::Simulator, $crate::DeviceKit),
236
+ ($crate::Simulator, $crate::ControlKit),
237
::rmcp::model::ErrorData,
238
> {
239
let target = $crate::mcp::SimulatorTargetArgs {
240
simulator_name: simulator_name.clone(),
241
simulator_udid: simulator_udid.clone(),
242
- devicekit_port,
242
+ controlkit_port,
243
};
244
let simulator = Self::simulator_from_target(&target)?;
245
Ok((
246
simulator,
247
- $crate::DeviceKit::new(devicekit_port.unwrap_or(12004)),
247
+ $crate::ControlKit::new(controlkit_port.unwrap_or(12004)),
248
))
249
}
250
@@ -379,7 +379,7 @@ macro_rules! xcrs_mcp_tools {
379
let target = $crate::mcp::SimulatorTargetArgs {
380
simulator_name: args.simulator_name,
381
simulator_udid: args.simulator_udid,
382
- devicekit_port: args.devicekit_port,
382
+ controlkit_port: args.controlkit_port,
383
};
384
let simulator = Self::simulator_from_target(&target)?;
385
$crate::XcodeCommandLineTools::new()
@@ -414,7 +414,7 @@ macro_rules! xcrs_mcp_tools {
414
let target = $crate::mcp::SimulatorTargetArgs {
415
simulator_name: args.simulator_name,
416
simulator_udid: args.simulator_udid,
417
- devicekit_port: args.devicekit_port,
417
+ controlkit_port: args.controlkit_port,
418
};
419
let simulator = Self::simulator_from_target(&target)?;
420
$crate::XcodeCommandLineTools::new()
@@ -449,7 +449,7 @@ macro_rules! xcrs_mcp_tools {
449
let target = $crate::mcp::SimulatorTargetArgs {
450
simulator_name: args.simulator_name,
451
simulator_udid: args.simulator_udid,
452
- devicekit_port: args.devicekit_port,
452
+ controlkit_port: args.controlkit_port,
453
};
454
let simulator = Self::simulator_from_target(&target)?;
455
$crate::XcodeCommandLineTools::new()
@@ -468,25 +468,25 @@ macro_rules! xcrs_mcp_tools {
468
469
#[::rmcp::tool(
470
name = $simulator_ui_dump_name,
471
- description = "Return the accessibility UI hierarchy from the foreground iOS app through DeviceKit."
471
+ description = "Return the accessibility UI hierarchy from the foreground iOS app through ControlKit."
472
)]
473
async fn simulator_ui_dump(
474
&self,
475
::rmcp::handler::server::wrapper::Parameters(
476
args,
477
): ::rmcp::handler::server::wrapper::Parameters<
478
- $crate::mcp::SimulatorDeviceKitArgs,
478
+ $crate::mcp::SimulatorControlKitArgs,
479
>,
480
) -> ::std::result::Result<
481
::rmcp::model::CallToolResult,
482
::rmcp::model::ErrorData,
483
> {
484
- let (simulator, devicekit) = Self::devicekit_from_target(
484
+ let (simulator, controlkit) = Self::controlkit_from_target(
485
&args.simulator_name,
486
&args.simulator_udid,
487
- args.devicekit_port,
487
+ args.controlkit_port,
488
)?;
489
- let result = devicekit
489
+ let result = controlkit
490
.call("device.dump.ui", ::serde_json::json!({ "format": "json" }))
491
.await
492
.map_err(|error| {
@@ -502,31 +502,31 @@ macro_rules! xcrs_mcp_tools {
502
503
#[::rmcp::tool(
504
name = $simulator_list_elements_name,
505
- description = "List actionable accessibility elements and coordinates from the foreground iOS app through DeviceKit."
505
+ description = "List actionable accessibility elements and coordinates from the foreground iOS app through ControlKit."
506
)]
507
async fn simulator_list_elements(
508
&self,
509
::rmcp::handler::server::wrapper::Parameters(
510
args,
511
): ::rmcp::handler::server::wrapper::Parameters<
512
- $crate::mcp::SimulatorDeviceKitArgs,
512
+ $crate::mcp::SimulatorControlKitArgs,
513
>,
514
) -> ::std::result::Result<
515
::rmcp::model::CallToolResult,
516
::rmcp::model::ErrorData,
517
> {
518
- let (simulator, devicekit) = Self::devicekit_from_target(
518
+ let (simulator, controlkit) = Self::controlkit_from_target(
519
&args.simulator_name,
520
&args.simulator_udid,
521
- args.devicekit_port,
521
+ args.controlkit_port,
522
)?;
523
- let ui = devicekit
523
+ let ui = controlkit
524
.call("device.dump.ui", ::serde_json::json!({ "format": "json" }))
525
.await
526
.map_err(|error| {
527
::rmcp::model::ErrorData::internal_error(error.to_string(), None)
528
})?;
529
- let elements = $crate::extract_devicekit_elements(&ui);
529
+ let elements = $crate::extract_controlkit_elements(&ui);
530
Ok(::rmcp::model::CallToolResult::success(vec![
531
::rmcp::model::ContentBlock::json(&::serde_json::json!({
532
"simulator": simulator,
@@ -537,7 +537,7 @@ macro_rules! xcrs_mcp_tools {
537
538
#[::rmcp::tool(
539
name = $simulator_tap_name,
540
- description = "Tap the iOS simulator screen at the given coordinates through DeviceKit."
540
+ description = "Tap the iOS simulator screen at the given coordinates through ControlKit."
541
)]
542
async fn simulator_tap(
543
&self,
@@ -550,12 +550,12 @@ macro_rules! xcrs_mcp_tools {
550
::rmcp::model::CallToolResult,
551
::rmcp::model::ErrorData,
552
> {
553
- let (simulator, devicekit) = Self::devicekit_from_target(
553
+ let (simulator, controlkit) = Self::controlkit_from_target(
554
&args.simulator_name,
555
&args.simulator_udid,
556
- args.devicekit_port,
556
+ args.controlkit_port,
557
)?;
558
- devicekit
558
+ controlkit
559
.call(
560
"device.io.tap",
561
::serde_json::json!({ "x": args.x, "y": args.y }),
@@ -574,7 +574,7 @@ macro_rules! xcrs_mcp_tools {
574
575
#[::rmcp::tool(
576
name = $simulator_text_name,
577
- description = "Type text into the focused iOS simulator field through DeviceKit."
577
+ description = "Type text into the focused iOS simulator field through ControlKit."
578
)]
579
async fn simulator_text(
580
&self,
@@ -587,12 +587,12 @@ macro_rules! xcrs_mcp_tools {
587
::rmcp::model::CallToolResult,
588
::rmcp::model::ErrorData,
589
> {
590
- let (simulator, devicekit) = Self::devicekit_from_target(
590
+ let (simulator, controlkit) = Self::controlkit_from_target(
591
&args.simulator_name,
592
&args.simulator_udid,
593
- args.devicekit_port,
593
+ args.controlkit_port,
594
)?;
595
- devicekit
595
+ controlkit
596
.call("device.io.text", ::serde_json::json!({ "text": args.text }))
597
.await
598
.map_err(|error| {
@@ -608,7 +608,7 @@ macro_rules! xcrs_mcp_tools {
608
609
#[::rmcp::tool(
610
name = $simulator_swipe_name,
611
- description = "Swipe between two screen coordinates on an iOS simulator through DeviceKit."
611
+ description = "Swipe between two screen coordinates on an iOS simulator through ControlKit."
612
)]
613
async fn simulator_swipe(
614
&self,
@@ -621,12 +621,12 @@ macro_rules! xcrs_mcp_tools {
621
::rmcp::model::CallToolResult,
622
::rmcp::model::ErrorData,
623
> {
624
- let (simulator, devicekit) = Self::devicekit_from_target(
624
+ let (simulator, controlkit) = Self::controlkit_from_target(
625
&args.simulator_name,
626
&args.simulator_udid,
627
- args.devicekit_port,
627
+ args.controlkit_port,
628
)?;
629
- devicekit
629
+ controlkit
630
.call(
631
"device.io.swipe",
632
::serde_json::json!({
@@ -650,25 +650,25 @@ macro_rules! xcrs_mcp_tools {
650
651
#[::rmcp::tool(
652
name = $simulator_home_name,
653
- description = "Press the iOS simulator Home button through DeviceKit."
653
+ description = "Press the iOS simulator Home button through ControlKit."
654
)]
655
async fn simulator_home(
656
&self,
657
::rmcp::handler::server::wrapper::Parameters(
658
args,
659
): ::rmcp::handler::server::wrapper::Parameters<
660
- $crate::mcp::SimulatorDeviceKitArgs,
660
+ $crate::mcp::SimulatorControlKitArgs,
661
>,
662
) -> ::std::result::Result<
663
::rmcp::model::CallToolResult,
664
::rmcp::model::ErrorData,
665
> {
666
- let (simulator, devicekit) = Self::devicekit_from_target(
666
+ let (simulator, controlkit) = Self::controlkit_from_target(
667
&args.simulator_name,
668
&args.simulator_udid,
669
- args.devicekit_port,
669
+ args.controlkit_port,
670
)?;
671
- devicekit
671
+ controlkit
672
.call("device.io.button", ::serde_json::json!({ "button": "home" }))
673
.await
674
.map_err(|error| {
@@ -684,7 +684,7 @@ macro_rules! xcrs_mcp_tools {
684
685
#[::rmcp::tool(
686
name = $simulator_button_name,
687
- description = "Press a DeviceKit remote button on an iOS or tvOS simulator. tvOS supports up, down, left, right, select, menu, home, and playPause."
687
+ description = "Press a ControlKit remote button on an iOS or tvOS simulator. tvOS supports up, down, left, right, select, menu, home, and playPause."
688
)]
689
async fn simulator_button(
690
&self,
@@ -697,10 +697,10 @@ macro_rules! xcrs_mcp_tools {
697
::rmcp::model::CallToolResult,
698
::rmcp::model::ErrorData,
699
> {
700
- let (simulator, devicekit) = Self::devicekit_from_target(
700
+ let (simulator, controlkit) = Self::controlkit_from_target(
701
&args.simulator_name,
702
&args.simulator_udid,
703
- args.devicekit_port,
703
+ args.controlkit_port,
704
)?;
705
let supported_buttons = [
706
"up",
@@ -718,7 +718,7 @@ macro_rules! xcrs_mcp_tools {
718
None,
719
));
720
}
721
- devicekit
721
+ controlkit
722
.call(
723
"device.io.button",
724
::serde_json::json!({ "button": args.button }),
@@ -737,25 +737,25 @@ macro_rules! xcrs_mcp_tools {
737
738
#[::rmcp::tool(
739
name = $simulator_orientation_get_name,
740
- description = "Read the current iOS simulator orientation through DeviceKit."
740
+ description = "Read the current iOS simulator orientation through ControlKit."
741
)]
742
async fn simulator_orientation_get(
743
&self,
744
::rmcp::handler::server::wrapper::Parameters(
745
args,
746
): ::rmcp::handler::server::wrapper::Parameters<
747
- $crate::mcp::SimulatorDeviceKitArgs,
747
+ $crate::mcp::SimulatorControlKitArgs,
748
>,
749
) -> ::std::result::Result<
750
::rmcp::model::CallToolResult,
751
::rmcp::model::ErrorData,
752
> {
753
- let (simulator, devicekit) = Self::devicekit_from_target(
753
+ let (simulator, controlkit) = Self::controlkit_from_target(
754
&args.simulator_name,
755
&args.simulator_udid,
756
- args.devicekit_port,
756
+ args.controlkit_port,
757
)?;
758
- let result = devicekit
758
+ let result = controlkit
759
.call("device.io.orientation.get", ::serde_json::json!({}))
760
.await
761
.map_err(|error| {
@@ -771,7 +771,7 @@ macro_rules! xcrs_mcp_tools {
771
772
#[::rmcp::tool(
773
name = $simulator_orientation_set_name,
774
- description = "Set the iOS simulator orientation to PORTRAIT or LANDSCAPE through DeviceKit."
774
+ description = "Set the iOS simulator orientation to PORTRAIT or LANDSCAPE through ControlKit."
775
)]
776
async fn simulator_orientation_set(
777
&self,
@@ -784,10 +784,10 @@ macro_rules! xcrs_mcp_tools {
784
::rmcp::model::CallToolResult,
785
::rmcp::model::ErrorData,
786
> {
787
- let (simulator, devicekit) = Self::devicekit_from_target(
787
+ let (simulator, controlkit) = Self::controlkit_from_target(
788
&args.simulator_name,
789
&args.simulator_udid,
790
- args.devicekit_port,
790
+ args.controlkit_port,
791
)?;
792
let orientation = args.orientation.to_uppercase();
793
if orientation != "PORTRAIT" && orientation != "LANDSCAPE" {
@@ -796,7 +796,7 @@ macro_rules! xcrs_mcp_tools {
796
None,
797
));
798
}
799
- devicekit
799
+ controlkit
800
.call(
801
"device.io.orientation.set",
802
::serde_json::json!({ "orientation": orientation }),
crates/xcrs/src/xcrs.rs
+12
-12
@@ -16,13 +16,13 @@ pub fn encode_base64(data: impl AsRef<[u8]>) -> String {
16
STANDARD.encode(data)
17
}
18
19
-pub fn extract_devicekit_elements(root: &serde_json::Value) -> Vec<serde_json::Value> {
19
+pub fn extract_controlkit_elements(root: &serde_json::Value) -> Vec<serde_json::Value> {
20
let mut elements = Vec::new();
21
- collect_devicekit_elements(root, &mut elements);
21
+ collect_controlkit_elements(root, &mut elements);
22
elements
23
}
24
25
-fn collect_devicekit_elements(element: &serde_json::Value, elements: &mut Vec<serde_json::Value>) {
25
+fn collect_controlkit_elements(element: &serde_json::Value, elements: &mut Vec<serde_json::Value>) {
26
let object = match element.as_object() {
27
Some(object) => object,
28
None => return,
@@ -71,24 +71,24 @@ fn collect_devicekit_elements(element: &serde_json::Value, elements: &mut Vec<se
71
72
if let Some(children) = object.get("children").and_then(serde_json::Value::as_array) {
73
for child in children {
74
- collect_devicekit_elements(child, elements);
74
+ collect_controlkit_elements(child, elements);
75
}
76
}
77
}
78
79
#[derive(Debug, Clone)]
80
-pub struct DeviceKit {
80
+pub struct ControlKit {
81
client: reqwest::Client,
82
base_url: String,
83
}
84
85
-impl Default for DeviceKit {
85
+impl Default for ControlKit {
86
fn default() -> Self {
87
Self::new(12004)
88
}
89
}
90
91
-impl DeviceKit {
91
+impl ControlKit {
92
pub fn new(port: u16) -> Self {
93
Self {
94
client: reqwest::Client::new(),
@@ -109,25 +109,25 @@ impl DeviceKit {
109
}))
110
.send()
111
.await
112
- .with_context(|| format!("failed to connect to DeviceKit at {}", self.base_url))?;
112
+ .with_context(|| format!("failed to connect to ControlKit at {}", self.base_url))?;
113
114
let status = response.status();
115
let body: serde_json::Value = response
116
.json()
117
.await
118
- .context("failed to decode DeviceKit JSON-RPC response")?;
118
+ .context("failed to decode ControlKit JSON-RPC response")?;
119
120
if !status.is_success() {
121
- return Err(anyhow!("DeviceKit returned HTTP {}: {}", status, body));
121
+ return Err(anyhow!("ControlKit returned HTTP {}: {}", status, body));
122
}
123
124
if let Some(error) = body.get("error") {
125
- return Err(anyhow!("DeviceKit method '{method}' failed: {error}"));
125
+ return Err(anyhow!("ControlKit method '{method}' failed: {error}"));
126
}
127
128
body.get("result")
129
.cloned()
130
- .ok_or_else(|| anyhow!("DeviceKit response for '{method}' did not contain a result"))
130
+ .ok_or_else(|| anyhow!("ControlKit response for '{method}' did not contain a result"))
131
}
132
}
133