1pub mod requests;
33pub mod server;
34
35pub use requests::*;
36pub use server::{serve, AscapiMcpServer};
37
38#[macro_export]
39macro_rules! ascapi_mcp_tools {
40 (
41 $server:ty,
42 $app_list_name:literal,
43 $app_get_name:literal,
44 $app_update_name:literal,
45 $app_info_list_name:literal,
46 $build_list_name:literal,
47 $bundle_id_list_name:literal,
48 $bundle_id_create_name:literal,
49 $app_store_version_list_name:literal,
50 $app_store_version_get_name:literal,
51 $app_store_version_create_name:literal,
52 $app_store_version_delete_name:literal,
53 $app_store_version_set_build_name:literal,
54 $app_store_version_build_get_name:literal,
55 $app_info_localization_list_name:literal,
56 $app_info_localization_create_name:literal,
57 $app_info_localization_update_name:literal,
58 $app_info_localization_delete_name:literal,
59 $app_store_version_localization_list_name:literal,
60 $app_store_version_localization_create_name:literal,
61 $app_store_version_localization_update_name:literal,
62 $app_store_version_localization_delete_name:literal,
63 $app_screenshot_set_list_name:literal,
64 $app_screenshot_set_create_name:literal,
65 $app_screenshot_set_delete_name:literal,
66 $app_screenshot_list_name:literal,
67 $app_screenshot_upload_name:literal,
68 $app_screenshot_delete_name:literal,
69 $certificate_list_name:literal,
70 $certificate_create_name:literal,
71 $beta_group_list_name:literal,
72 $beta_app_review_detail_get_name:literal
73 ) => {
74 #[::rmcp::tool_router(router = ascapi_tool_router, vis = "pub(crate)")]
75 impl $server {
76 fn client() -> ::std::result::Result<::smbcloud_ascapi_core::Client, ::rmcp::model::ErrorData> {
77 let api_key = ::smbcloud_ascapi_frontend::api_key_from_env()
78 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?;
79 Ok(::smbcloud_ascapi_core::Client::new(api_key))
80 }
81
82 #[::rmcp::tool(
83 name = $app_list_name,
84 title = "List apps",
85 annotations(title = "List apps", read_only_hint = true, idempotent_hint = true),
86 description = "Purpose: find the App Store Connect apps on this account, and translate a bundle identifier into the numeric app id every other tool wants. When to use vs siblings: call this first when you only know a bundle identifier; use app_get once you have an id and want one app's full attributes. Behavior: returns each app's id, name, bundle identifier, SKU, and primary locale, optionally filtered to one bundle identifier. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: returns apps only, not their versions, builds, or localizations, each of which has its own tool."
87 )]
88 async fn app_list(
89 &self,
90 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppListRequest>,
91 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
92 #[allow(unused_imports)]
93 use ::smbcloud_ascapi_aso::prelude::*;
94 #[allow(unused_imports)]
95 use ::smbcloud_ascapi_signing::prelude::*;
96 let client = Self::client()?;
97 let request = parameters.0;
98 let value = client
99 .list_apps(request.bundle_id.as_deref())
100 .await
101 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
102 Ok(::rmcp::model::CallToolResult::success(vec![
103 ::rmcp::model::ContentBlock::json(&value)?,
104 ]))
105 }
106
107 #[::rmcp::tool(
108 name = $app_get_name,
109 title = "Get one app",
110 annotations(title = "Get one app", read_only_hint = true, idempotent_hint = true),
111 description = "Purpose: fetch one app's full attributes by its App Store Connect id. When to use vs siblings: use app_list when you have a bundle identifier rather than an id, and app_info_list when you want the AppInfo records that hold localized names. Behavior: returns the single app resource with its attributes. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: attributes only; related resources such as versions and builds are fetched with their own tools."
112 )]
113 async fn app_get(
114 &self,
115 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppIdRequest>,
116 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
117 #[allow(unused_imports)]
118 use ::smbcloud_ascapi_aso::prelude::*;
119 #[allow(unused_imports)]
120 use ::smbcloud_ascapi_signing::prelude::*;
121 let client = Self::client()?;
122 let request = parameters.0;
123 let value = client
124 .get_app(&request.app_id)
125 .await
126 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
127 Ok(::rmcp::model::CallToolResult::success(vec![
128 ::rmcp::model::ContentBlock::json(&value)?,
129 ]))
130 }
131
132 #[::rmcp::tool(
133 name = $app_update_name,
134 title = "Update app attributes",
135 annotations(title = "Update app attributes", read_only_hint = false, destructive_hint = false, idempotent_hint = true),
136 description = "Purpose: change an app's primary locale or content rights declaration. When to use vs siblings: this edits the app record itself; localized names and subtitles live on AppInfo localizations and are edited with app_info_localization_update. Behavior: sends only the fields provided, leaving the rest untouched, and returns the updated app. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: only these two attributes are editable through this endpoint; most app metadata belongs to a version or a localization instead."
137 )]
138 async fn app_update(
139 &self,
140 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppUpdateRequest>,
141 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
142 #[allow(unused_imports)]
143 use ::smbcloud_ascapi_aso::prelude::*;
144 #[allow(unused_imports)]
145 use ::smbcloud_ascapi_signing::prelude::*;
146 let client = Self::client()?;
147 let request = parameters.0;
148 let attributes = ::smbcloud_ascapi_aso::app::AppUpdateAttributes {
149 primary_locale: request.primary_locale,
150 content_rights_declaration: request.content_rights_declaration,
151 };
152 let value = client
153 .update_app(&request.app_id, attributes)
154 .await
155 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
156 Ok(::rmcp::model::CallToolResult::success(vec![
157 ::rmcp::model::ContentBlock::json(&value)?,
158 ]))
159 }
160
161 #[::rmcp::tool(
162 name = $app_info_list_name,
163 title = "List app infos",
164 annotations(title = "List app infos", read_only_hint = true, idempotent_hint = true),
165 description = "Purpose: list an app's AppInfo records, which are the containers for localized names, subtitles, and privacy policy URLs. When to use vs siblings: call this to get the app_info_id that app_info_localization_list and app_info_localization_create need. Behavior: returns each AppInfo with its state and id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: an app typically has one editable AppInfo plus historical ones for shipped versions, and this returns all of them without saying which is editable."
166 )]
167 async fn app_info_list(
168 &self,
169 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppIdRequest>,
170 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
171 #[allow(unused_imports)]
172 use ::smbcloud_ascapi_aso::prelude::*;
173 #[allow(unused_imports)]
174 use ::smbcloud_ascapi_signing::prelude::*;
175 let client = Self::client()?;
176 let request = parameters.0;
177 let value = client
178 .list_app_infos(&request.app_id)
179 .await
180 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
181 Ok(::rmcp::model::CallToolResult::success(vec![
182 ::rmcp::model::ContentBlock::json(&value)?,
183 ]))
184 }
185
186 #[::rmcp::tool(
187 name = $build_list_name,
188 title = "List builds",
189 annotations(title = "List builds", read_only_hint = true, idempotent_hint = true),
190 description = "Purpose: list the builds uploaded for an app, so one can be attached to a version. When to use vs siblings: pair with app_store_version_set_build, which needs a build id from here. Behavior: returns each build's id, version string, upload date, and processing state, newest upload first, optionally narrowed by build number, marketing version, or processing state. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: a build that is still processing cannot be attached yet, and this reports that state rather than waiting for it."
191 )]
192 async fn build_list(
193 &self,
194 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::BuildListRequest>,
195 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
196 #[allow(unused_imports)]
197 use ::smbcloud_ascapi_aso::prelude::*;
198 #[allow(unused_imports)]
199 use ::smbcloud_ascapi_signing::prelude::*;
200 let client = Self::client()?;
201 let request = parameters.0;
202 let filter = ::smbcloud_ascapi_aso::build::BuildFilter {
203 version: request.version,
204 pre_release_version: request.pre_release_version,
205 processing_state: request.processing_state,
206 };
207 let value = client
208 .list_builds(&request.app_id, &filter)
209 .await
210 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
211 Ok(::rmcp::model::CallToolResult::success(vec![
212 ::rmcp::model::ContentBlock::json(&value)?,
213 ]))
214 }
215
216 #[::rmcp::tool(
217 name = $bundle_id_list_name,
218 title = "List bundle IDs",
219 annotations(title = "List bundle IDs", read_only_hint = true, idempotent_hint = true),
220 description = "Purpose: list the bundle identifiers registered to the team, or check whether one exists before trying to register it. When to use vs siblings: call this before bundle_id_create, which fails on an identifier that already exists. Behavior: returns each bundle ID's id, identifier, name, and platform, optionally filtered to an exact identifier. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: registration state only; whether an app exists for the identifier is a separate question answered by app_list."
221 )]
222 async fn bundle_id_list(
223 &self,
224 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::BundleIdListRequest>,
225 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
226 #[allow(unused_imports)]
227 use ::smbcloud_ascapi_aso::prelude::*;
228 #[allow(unused_imports)]
229 use ::smbcloud_ascapi_signing::prelude::*;
230 let client = Self::client()?;
231 let request = parameters.0;
232 let value = client
233 .list_bundle_ids(request.identifier.as_deref())
234 .await
235 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
236 Ok(::rmcp::model::CallToolResult::success(vec![
237 ::rmcp::model::ContentBlock::json(&value)?,
238 ]))
239 }
240
241 #[::rmcp::tool(
242 name = $bundle_id_create_name,
243 title = "Register a bundle ID",
244 annotations(title = "Register a bundle ID", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
245 description = "Purpose: register a new reverse-DNS bundle identifier with the developer account. When to use vs siblings: call bundle_id_list first, since registering an identifier that already exists fails. Behavior: registers the identifier under the given platform namespace and returns the created resource. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: identifiers cannot be renamed or deleted through this API once registered, so a typo here is permanent; visionOS apps register as ios because visionOS shares the iOS namespace."
246 )]
247 async fn bundle_id_create(
248 &self,
249 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::BundleIdCreateRequest>,
250 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
251 #[allow(unused_imports)]
252 use ::smbcloud_ascapi_aso::prelude::*;
253 #[allow(unused_imports)]
254 use ::smbcloud_ascapi_signing::prelude::*;
255 let client = Self::client()?;
256 let request = parameters.0;
257 let platform = ::smbcloud_ascapi_frontend::bundle_id_platform_from_str(&request.platform)
258 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?;
259 let attributes = ::smbcloud_ascapi_aso::bundle_id::BundleIdCreateAttributes {
260 identifier: request.identifier,
261 name: request.name,
262 platform,
263 };
264 let value = client
265 .create_bundle_id(attributes)
266 .await
267 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
268 Ok(::rmcp::model::CallToolResult::success(vec![
269 ::rmcp::model::ContentBlock::json(&value)?,
270 ]))
271 }
272
273 #[::rmcp::tool(
274 name = $app_store_version_list_name,
275 title = "List app store versions",
276 annotations(title = "List app store versions", read_only_hint = true, idempotent_hint = true),
277 description = "Purpose: list an app's per-platform version records, which are what the store actually reviews and releases. When to use vs siblings: use this to find the version id that the localization and build tools need. Behavior: returns each version's id, platform, version string, and app store state. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: versions are per platform, so one app can have several concurrent editable versions and this returns all of them."
278 )]
279 async fn app_store_version_list(
280 &self,
281 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppStoreVersionListRequest>,
282 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
283 #[allow(unused_imports)]
284 use ::smbcloud_ascapi_aso::prelude::*;
285 #[allow(unused_imports)]
286 use ::smbcloud_ascapi_signing::prelude::*;
287 let client = Self::client()?;
288 let request = parameters.0;
289 let platform = match request.platform.as_deref() {
290 Some(value) => Some(
291 ::smbcloud_ascapi_frontend::platform_from_str(value)
292 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?,
293 ),
294 None => None,
295 };
296 let value = client
297 .list_app_store_versions(&request.app_id, platform)
298 .await
299 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
300 Ok(::rmcp::model::CallToolResult::success(vec![
301 ::rmcp::model::ContentBlock::json(&value)?,
302 ]))
303 }
304
305 #[::rmcp::tool(
306 name = $app_store_version_get_name,
307 title = "Get one app store version",
308 annotations(title = "Get one app store version", read_only_hint = true, idempotent_hint = true),
309 description = "Purpose: fetch one App Store Version's attributes by id. When to use vs siblings: use app_store_version_list when you have an app id rather than a version id. Behavior: returns the single version resource with its platform, version string, and release state. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: attributes only; its localizations are fetched with their own tools, and its attached build with app_store_version_build_get."
310 )]
311 async fn app_store_version_get(
312 &self,
313 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
314 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
315 #[allow(unused_imports)]
316 use ::smbcloud_ascapi_aso::prelude::*;
317 #[allow(unused_imports)]
318 use ::smbcloud_ascapi_signing::prelude::*;
319 let client = Self::client()?;
320 let request = parameters.0;
321 let value = client
322 .get_app_store_version(&request.id)
323 .await
324 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
325 Ok(::rmcp::model::CallToolResult::success(vec![
326 ::rmcp::model::ContentBlock::json(&value)?,
327 ]))
328 }
329
330 #[::rmcp::tool(
331 name = $app_store_version_create_name,
332 title = "Create an app store version",
333 annotations(title = "Create an app store version", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
334 description = "Purpose: create a new version record for an app on a given platform, which is how a new platform such as visionOS is added to an existing app. When to use vs siblings: call app_store_version_list first, because an app can hold only one editable version per platform at a time. Behavior: creates the version and returns it. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: creating a version does not submit anything; localizations, screenshots, and a build still have to be attached before review."
335 )]
336 async fn app_store_version_create(
337 &self,
338 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppStoreVersionCreateRequest>,
339 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
340 #[allow(unused_imports)]
341 use ::smbcloud_ascapi_aso::prelude::*;
342 #[allow(unused_imports)]
343 use ::smbcloud_ascapi_signing::prelude::*;
344 let client = Self::client()?;
345 let request = parameters.0;
346 let platform = ::smbcloud_ascapi_frontend::platform_from_str(&request.platform)
347 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?;
348 let attributes = ::smbcloud_ascapi_aso::app_store_version::AppStoreVersionCreateAttributes {
349 platform,
350 version_string: request.version_string,
351 copyright: request.copyright,
352 };
353 let value = client
354 .create_app_store_version(&request.app_id, attributes)
355 .await
356 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
357 Ok(::rmcp::model::CallToolResult::success(vec![
358 ::rmcp::model::ContentBlock::json(&value)?,
359 ]))
360 }
361
362 #[::rmcp::tool(
363 name = $app_store_version_delete_name,
364 title = "Delete an app store version",
365 annotations(title = "Delete an app store version", read_only_hint = false, destructive_hint = true, idempotent_hint = true),
366 description = "Purpose: delete an editable App Store Version record. When to use vs siblings: use this to discard a version created by mistake, typically after app_store_version_list shows a duplicate. Behavior: deletes the version and everything attached to it, including its localizations and screenshots, then returns the deleted id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: destructive and not undoable, though the version can be recreated; a version already submitted or released cannot be deleted and Apple rejects the attempt."
367 )]
368 async fn app_store_version_delete(
369 &self,
370 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
371 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
372 #[allow(unused_imports)]
373 use ::smbcloud_ascapi_aso::prelude::*;
374 #[allow(unused_imports)]
375 use ::smbcloud_ascapi_signing::prelude::*;
376 let client = Self::client()?;
377 let request = parameters.0;
378 client
379 .delete_app_store_version(&request.id)
380 .await
381 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
382 Ok(::rmcp::model::CallToolResult::success(vec![
383 ::rmcp::model::ContentBlock::json(&::serde_json::json!({ "deleted": request.id }))?,
384 ]))
385 }
386
387 #[::rmcp::tool(
388 name = $app_store_version_set_build_name,
389 title = "Attach a build to a version",
390 annotations(title = "Attach a build to a version", read_only_hint = false, destructive_hint = false, idempotent_hint = true),
391 description = "Purpose: attach an uploaded build to an App Store Version, which a version needs before it can be submitted. When to use vs siblings: get the build id from build_list and the version id from app_store_version_list. Behavior: points the version's build relationship at the given build, replacing whatever was attached, then reads the build back and fails if the version does not report it; returns the version id and the attached build with its version, processing state, and export-compliance answer. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: the build must have finished processing, and its platform must match the version's; Apple rejects the pairing otherwise."
392 )]
393 async fn app_store_version_set_build(
394 &self,
395 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::SetBuildRequest>,
396 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
397 #[allow(unused_imports)]
398 use ::smbcloud_ascapi_aso::prelude::*;
399 #[allow(unused_imports)]
400 use ::smbcloud_ascapi_signing::prelude::*;
401 let client = Self::client()?;
402 let request = parameters.0;
403 let attached = ::smbcloud_ascapi_frontend::attach_build(
404 &client,
405 &request.version_id,
406 &request.build_id,
407 true,
408 )
409 .await
410 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error, None))?;
411 Ok(::rmcp::model::CallToolResult::success(vec![
412 ::rmcp::model::ContentBlock::json(&attached)?,
413 ]))
414 }
415
416 #[::rmcp::tool(
417 name = $app_store_version_build_get_name,
418 title = "Get the build attached to a version",
419 annotations(title = "Get the build attached to a version", read_only_hint = true, idempotent_hint = true),
420 description = "Purpose: read which build is attached to an App Store Version, so it can be checked before the version is submitted for review. When to use vs siblings: app_store_version_get returns the version without its build; use this after app_store_version_set_build or before review_submission_submit. Behavior: returns the attached build with its id, version string, processing state, and export-compliance answer, or null when no build is attached. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: reports only the current attachment, not earlier builds; use build_list for the builds uploaded for an app."
421 )]
422 async fn app_store_version_build_get(
423 &self,
424 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
425 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
426 #[allow(unused_imports)]
427 use ::smbcloud_ascapi_aso::prelude::*;
428 #[allow(unused_imports)]
429 use ::smbcloud_ascapi_signing::prelude::*;
430 let client = Self::client()?;
431 let request = parameters.0;
432 let value = client
433 .get_app_store_version_build(&request.id)
434 .await
435 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
436 Ok(::rmcp::model::CallToolResult::success(vec![
437 ::rmcp::model::ContentBlock::json(&value)?,
438 ]))
439 }
440
441 #[::rmcp::tool(
442 name = $app_info_localization_list_name,
443 title = "List app info localizations",
444 annotations(title = "List app info localizations", read_only_hint = true, idempotent_hint = true),
445 description = "Purpose: list the localized names and subtitles attached to an AppInfo. When to use vs siblings: this covers name, subtitle, and privacy policy URL; description, keywords, and release notes live on version localizations instead. Behavior: returns each localization's id, locale, name, and subtitle. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: only the editable AppInfo accepts changes, and this does not distinguish editable from historical records."
446 )]
447 async fn app_info_localization_list(
448 &self,
449 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppInfoLocalizationListRequest>,
450 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
451 #[allow(unused_imports)]
452 use ::smbcloud_ascapi_aso::prelude::*;
453 #[allow(unused_imports)]
454 use ::smbcloud_ascapi_signing::prelude::*;
455 let client = Self::client()?;
456 let request = parameters.0;
457 let value = client
458 .list_app_info_localizations(&request.app_info_id)
459 .await
460 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
461 Ok(::rmcp::model::CallToolResult::success(vec![
462 ::rmcp::model::ContentBlock::json(&value)?,
463 ]))
464 }
465
466 #[::rmcp::tool(
467 name = $app_info_localization_create_name,
468 title = "Add an app info localization",
469 annotations(title = "Add an app info localization", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
470 description = "Purpose: add a localized app name and subtitle for a new locale. When to use vs siblings: use app_info_localization_update when the locale already exists, since creating a duplicate fails. Behavior: creates the localization under the AppInfo and returns it. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: the subtitle is capped at 30 characters and the name at 30 by App Store Connect, which rejects longer values rather than truncating them."
471 )]
472 async fn app_info_localization_create(
473 &self,
474 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppInfoLocalizationCreateRequest>,
475 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
476 #[allow(unused_imports)]
477 use ::smbcloud_ascapi_aso::prelude::*;
478 #[allow(unused_imports)]
479 use ::smbcloud_ascapi_signing::prelude::*;
480 let client = Self::client()?;
481 let request = parameters.0;
482 let attributes = ::smbcloud_ascapi_aso::app_info_localization::AppInfoLocalizationCreateAttributes {
483 locale: request.locale,
484 name: request.name,
485 subtitle: request.subtitle,
486 privacy_policy_url: request.privacy_policy_url,
487 };
488 let value = client
489 .create_app_info_localization(&request.app_info_id, attributes)
490 .await
491 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
492 Ok(::rmcp::model::CallToolResult::success(vec![
493 ::rmcp::model::ContentBlock::json(&value)?,
494 ]))
495 }
496
497 #[::rmcp::tool(
498 name = $app_info_localization_update_name,
499 title = "Edit an app info localization",
500 annotations(title = "Edit an app info localization", read_only_hint = false, destructive_hint = false, idempotent_hint = true),
501 description = "Purpose: change the localized name, subtitle, or privacy policy URL for a locale that already exists. When to use vs siblings: use app_info_localization_create for a locale that has no record yet. Behavior: sends only the fields provided and leaves the others untouched, then returns the updated localization. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: the locale itself cannot be changed; delete and recreate to move copy to a different locale."
502 )]
503 async fn app_info_localization_update(
504 &self,
505 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::AppInfoLocalizationUpdateRequest>,
506 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
507 #[allow(unused_imports)]
508 use ::smbcloud_ascapi_aso::prelude::*;
509 #[allow(unused_imports)]
510 use ::smbcloud_ascapi_signing::prelude::*;
511 let client = Self::client()?;
512 let request = parameters.0;
513 let attributes = ::smbcloud_ascapi_aso::app_info_localization::AppInfoLocalizationUpdateAttributes {
514 name: request.name,
515 subtitle: request.subtitle,
516 privacy_policy_url: request.privacy_policy_url,
517 };
518 let value = client
519 .update_app_info_localization(&request.id, attributes)
520 .await
521 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
522 Ok(::rmcp::model::CallToolResult::success(vec![
523 ::rmcp::model::ContentBlock::json(&value)?,
524 ]))
525 }
526
527 #[::rmcp::tool(
528 name = $app_info_localization_delete_name,
529 title = "Delete an app info localization",
530 annotations(title = "Delete an app info localization", read_only_hint = false, destructive_hint = true, idempotent_hint = true),
531 description = "Purpose: remove a locale's app name and subtitle. When to use vs siblings: use this to drop a language from the product page; use app_info_localization_update to change copy rather than remove it. Behavior: deletes the localization and returns the deleted id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: destructive but recreatable with app_info_localization_create; the app's primary locale cannot be deleted and Apple rejects the attempt."
532 )]
533 async fn app_info_localization_delete(
534 &self,
535 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
536 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
537 #[allow(unused_imports)]
538 use ::smbcloud_ascapi_aso::prelude::*;
539 #[allow(unused_imports)]
540 use ::smbcloud_ascapi_signing::prelude::*;
541 let client = Self::client()?;
542 let request = parameters.0;
543 client
544 .delete_app_info_localization(&request.id)
545 .await
546 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
547 Ok(::rmcp::model::CallToolResult::success(vec![
548 ::rmcp::model::ContentBlock::json(&::serde_json::json!({ "deleted": request.id }))?,
549 ]))
550 }
551
552 #[::rmcp::tool(
553 name = $app_store_version_localization_list_name,
554 title = "List version localizations",
555 annotations(title = "List version localizations", read_only_hint = true, idempotent_hint = true),
556 description = "Purpose: list the per-locale description, keywords, promotional text, and release notes for a version. When to use vs siblings: this is the version-scoped copy; the app name and subtitle live on AppInfo localizations instead. Behavior: returns each localization's id, locale, and copy fields. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: screenshots hang off these localizations but are listed separately with app_screenshot_set_list."
557 )]
558 async fn app_store_version_localization_list(
559 &self,
560 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::VersionLocalizationListRequest>,
561 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
562 #[allow(unused_imports)]
563 use ::smbcloud_ascapi_aso::prelude::*;
564 #[allow(unused_imports)]
565 use ::smbcloud_ascapi_signing::prelude::*;
566 let client = Self::client()?;
567 let request = parameters.0;
568 let value = client
569 .list_app_store_version_localizations(&request.version_id)
570 .await
571 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
572 Ok(::rmcp::model::CallToolResult::success(vec![
573 ::rmcp::model::ContentBlock::json(&value)?,
574 ]))
575 }
576
577 #[::rmcp::tool(
578 name = $app_store_version_localization_create_name,
579 title = "Add a version localization",
580 annotations(title = "Add a version localization", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
581 description = "Purpose: add the description, keywords, and release notes for a locale on a specific version. When to use vs siblings: use the update tool when the locale already exists on this version. Behavior: creates the localization with whichever fields are provided and returns it. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: field limits are checked before sending (4000 characters for description and release notes, 100 for keywords, 170 for promotional text), and a value over its limit returns an invalid-params error naming the field."
582 )]
583 async fn app_store_version_localization_create(
584 &self,
585 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::VersionLocalizationCreateRequest>,
586 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
587 #[allow(unused_imports)]
588 use ::smbcloud_ascapi_aso::prelude::*;
589 #[allow(unused_imports)]
590 use ::smbcloud_ascapi_signing::prelude::*;
591 let client = Self::client()?;
592 let request = parameters.0;
593 let fields = ::smbcloud_ascapi_aso::app_store_version_localization::AppStoreVersionLocalizationFields {
594 description: request.description,
595 keywords: request.keywords,
596 marketing_url: request.marketing_url,
597 promotional_text: request.promotional_text,
598 support_url: request.support_url,
599 whats_new: request.whats_new,
600 };
601 fields
602 .validate()
603 .map_err(|message| ::rmcp::model::ErrorData::invalid_params(message, None))?;
604 let attributes = ::smbcloud_ascapi_aso::app_store_version_localization::AppStoreVersionLocalizationCreateAttributes {
605 locale: request.locale,
606 fields,
607 };
608 let value = client
609 .create_app_store_version_localization(&request.version_id, attributes)
610 .await
611 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
612 Ok(::rmcp::model::CallToolResult::success(vec![
613 ::rmcp::model::ContentBlock::json(&value)?,
614 ]))
615 }
616
617 #[::rmcp::tool(
618 name = $app_store_version_localization_update_name,
619 title = "Edit a version localization",
620 annotations(title = "Edit a version localization", read_only_hint = false, destructive_hint = false, idempotent_hint = true),
621 description = "Purpose: change the description, keywords, promotional text, or release notes for an existing locale on a version. When to use vs siblings: promotional text is the one field editable without shipping a new build, which makes this the tool for timely copy changes. Behavior: sends only the fields provided and leaves the rest untouched. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: field limits are checked before sending (4000 characters for description and release notes, 100 for keywords, 170 for promotional text) and a value over its limit returns an invalid-params error naming the field; which fields are editable depends on the version's review state, and Apple rejects edits to a version under review."
622 )]
623 async fn app_store_version_localization_update(
624 &self,
625 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::VersionLocalizationUpdateRequest>,
626 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
627 #[allow(unused_imports)]
628 use ::smbcloud_ascapi_aso::prelude::*;
629 #[allow(unused_imports)]
630 use ::smbcloud_ascapi_signing::prelude::*;
631 let client = Self::client()?;
632 let request = parameters.0;
633 let fields = ::smbcloud_ascapi_aso::app_store_version_localization::AppStoreVersionLocalizationFields {
634 description: request.description,
635 keywords: request.keywords,
636 marketing_url: request.marketing_url,
637 promotional_text: request.promotional_text,
638 support_url: request.support_url,
639 whats_new: request.whats_new,
640 };
641 fields
642 .validate()
643 .map_err(|message| ::rmcp::model::ErrorData::invalid_params(message, None))?;
644 let value = client
645 .update_app_store_version_localization(&request.id, fields)
646 .await
647 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
648 Ok(::rmcp::model::CallToolResult::success(vec![
649 ::rmcp::model::ContentBlock::json(&value)?,
650 ]))
651 }
652
653 #[::rmcp::tool(
654 name = $app_store_version_localization_delete_name,
655 title = "Delete a version localization",
656 annotations(title = "Delete a version localization", read_only_hint = false, destructive_hint = true, idempotent_hint = true),
657 description = "Purpose: remove a locale's copy from a version. When to use vs siblings: use the update tool to change copy; use this only to drop a language from this version entirely. Behavior: deletes the localization along with its screenshot sets and returns the deleted id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: destructive and takes the locale's screenshots with it, though everything can be recreated; the primary locale cannot be removed."
658 )]
659 async fn app_store_version_localization_delete(
660 &self,
661 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
662 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
663 #[allow(unused_imports)]
664 use ::smbcloud_ascapi_aso::prelude::*;
665 #[allow(unused_imports)]
666 use ::smbcloud_ascapi_signing::prelude::*;
667 let client = Self::client()?;
668 let request = parameters.0;
669 client
670 .delete_app_store_version_localization(&request.id)
671 .await
672 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
673 Ok(::rmcp::model::CallToolResult::success(vec![
674 ::rmcp::model::ContentBlock::json(&::serde_json::json!({ "deleted": request.id }))?,
675 ]))
676 }
677
678 #[::rmcp::tool(
679 name = $app_screenshot_set_list_name,
680 title = "List screenshot sets",
681 annotations(title = "List screenshot sets", read_only_hint = true, idempotent_hint = true),
682 description = "Purpose: list the per-device-class screenshot buckets on a version localization. When to use vs siblings: each set holds the images for one display type, so call this to find the set id that app_screenshot_list and app_screenshot_upload need. Behavior: returns each set's id and display type. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: reports the sets, not how many images each holds or whether the set satisfies Apple's per-device requirements."
683 )]
684 async fn app_screenshot_set_list(
685 &self,
686 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::ScreenshotSetListRequest>,
687 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
688 #[allow(unused_imports)]
689 use ::smbcloud_ascapi_aso::prelude::*;
690 #[allow(unused_imports)]
691 use ::smbcloud_ascapi_signing::prelude::*;
692 let client = Self::client()?;
693 let request = parameters.0;
694 let value = client
695 .list_app_screenshot_sets(&request.localization_id)
696 .await
697 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
698 Ok(::rmcp::model::CallToolResult::success(vec![
699 ::rmcp::model::ContentBlock::json(&value)?,
700 ]))
701 }
702
703 #[::rmcp::tool(
704 name = $app_screenshot_set_create_name,
705 title = "Create a screenshot set",
706 annotations(title = "Create a screenshot set", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
707 description = "Purpose: create the bucket that holds screenshots for one device class on a version localization. When to use vs siblings: call app_screenshot_set_list first, since a localization holds at most one set per display type. Behavior: creates the set for the given display type and returns it, ready for app_screenshot_upload. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: the display type must be one App Store Connect recognizes, and image dimensions are validated later at upload rather than here."
708 )]
709 async fn app_screenshot_set_create(
710 &self,
711 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::ScreenshotSetCreateRequest>,
712 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
713 #[allow(unused_imports)]
714 use ::smbcloud_ascapi_aso::prelude::*;
715 #[allow(unused_imports)]
716 use ::smbcloud_ascapi_signing::prelude::*;
717 let client = Self::client()?;
718 let request = parameters.0;
719 let display_type = ::smbcloud_ascapi_frontend::display_type_from_str(&request.display_type)
720 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?;
721 let attributes = ::smbcloud_ascapi_aso::app_screenshot_set::AppScreenshotSetCreateAttributes {
722 screenshot_display_type: display_type,
723 };
724 let value = client
725 .create_app_screenshot_set(&request.localization_id, attributes)
726 .await
727 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
728 Ok(::rmcp::model::CallToolResult::success(vec![
729 ::rmcp::model::ContentBlock::json(&value)?,
730 ]))
731 }
732
733 #[::rmcp::tool(
734 name = $app_screenshot_set_delete_name,
735 title = "Delete a screenshot set",
736 annotations(title = "Delete a screenshot set", read_only_hint = false, destructive_hint = true, idempotent_hint = true),
737 description = "Purpose: remove a device class's screenshot bucket and every image in it. When to use vs siblings: use app_screenshot_delete to remove a single image instead of the whole set. Behavior: deletes the set and its screenshots, then returns the deleted id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: destructive and takes every image with it, though the set and its images can be recreated by uploading again."
738 )]
739 async fn app_screenshot_set_delete(
740 &self,
741 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
742 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
743 #[allow(unused_imports)]
744 use ::smbcloud_ascapi_aso::prelude::*;
745 #[allow(unused_imports)]
746 use ::smbcloud_ascapi_signing::prelude::*;
747 let client = Self::client()?;
748 let request = parameters.0;
749 client
750 .delete_app_screenshot_set(&request.id)
751 .await
752 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
753 Ok(::rmcp::model::CallToolResult::success(vec![
754 ::rmcp::model::ContentBlock::json(&::serde_json::json!({ "deleted": request.id }))?,
755 ]))
756 }
757
758 #[::rmcp::tool(
759 name = $app_screenshot_list_name,
760 title = "List screenshots",
761 annotations(title = "List screenshots", read_only_hint = true, idempotent_hint = true),
762 description = "Purpose: list the images in a screenshot set, with their upload and processing state. When to use vs siblings: call this after app_screenshot_upload to confirm an image finished processing, since upload returns before Apple has validated the file. Behavior: returns each screenshot's id, file name, size, and asset delivery state. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: an image that failed Apple's dimension checks appears here with an error state rather than being absent, so the state is worth reading."
763 )]
764 async fn app_screenshot_list(
765 &self,
766 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::ScreenshotListRequest>,
767 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
768 #[allow(unused_imports)]
769 use ::smbcloud_ascapi_aso::prelude::*;
770 #[allow(unused_imports)]
771 use ::smbcloud_ascapi_signing::prelude::*;
772 let client = Self::client()?;
773 let request = parameters.0;
774 let value = client
775 .list_app_screenshots(&request.screenshot_set_id)
776 .await
777 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
778 Ok(::rmcp::model::CallToolResult::success(vec![
779 ::rmcp::model::ContentBlock::json(&value)?,
780 ]))
781 }
782
783 #[::rmcp::tool(
784 name = $app_screenshot_upload_name,
785 title = "Upload a screenshot",
786 annotations(title = "Upload a screenshot", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
787 description = "Purpose: upload one image into a screenshot set, running the whole three-step reservation, upload, and commit dance in a single call. When to use vs siblings: create the set with app_screenshot_set_create first, then verify with app_screenshot_list, since Apple validates asynchronously. Behavior: reads the file from this machine, reserves the asset, PUTs the bytes to the pre-signed URL, and commits it with its checksum. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: an unreadable path fails before any network call; a wrong image size is accepted here and rejected later, surfacing in app_screenshot_list. Limitations: the path is read by this process, so it must be reachable from wherever the server runs, not from the client's machine."
788 )]
789 async fn app_screenshot_upload(
790 &self,
791 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::ScreenshotUploadRequest>,
792 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
793 #[allow(unused_imports)]
794 use ::smbcloud_ascapi_aso::prelude::*;
795 #[allow(unused_imports)]
796 use ::smbcloud_ascapi_signing::prelude::*;
797 let client = Self::client()?;
798 let request = parameters.0;
799 let path = ::std::path::PathBuf::from(&request.file_path);
800 let bytes = ::std::fs::read(&path).map_err(|error| {
801 ::rmcp::model::ErrorData::invalid_request(
802 format!("reading {}: {error}", path.display()),
803 None,
804 )
805 })?;
806 let file_name = path
807 .file_name()
808 .map(|name| name.to_string_lossy().into_owned())
809 .ok_or_else(|| {
810 ::rmcp::model::ErrorData::invalid_request(
811 format!("{} has no file name", path.display()),
812 None,
813 )
814 })?;
815 let value = client
816 .upload_app_screenshot(&request.screenshot_set_id, file_name, bytes)
817 .await
818 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
819 Ok(::rmcp::model::CallToolResult::success(vec![
820 ::rmcp::model::ContentBlock::json(&value)?,
821 ]))
822 }
823
824 #[::rmcp::tool(
825 name = $app_screenshot_delete_name,
826 title = "Delete a screenshot",
827 annotations(title = "Delete a screenshot", read_only_hint = false, destructive_hint = true, idempotent_hint = true),
828 description = "Purpose: remove one uploaded screenshot. When to use vs siblings: use app_screenshot_set_delete to clear an entire device class at once. Behavior: deletes the image and returns the deleted id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: destructive but trivially recoverable by uploading the file again, which makes it the safest of the delete tools."
829 )]
830 async fn app_screenshot_delete(
831 &self,
832 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::IdRequest>,
833 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
834 #[allow(unused_imports)]
835 use ::smbcloud_ascapi_aso::prelude::*;
836 #[allow(unused_imports)]
837 use ::smbcloud_ascapi_signing::prelude::*;
838 let client = Self::client()?;
839 let request = parameters.0;
840 client
841 .delete_app_screenshot(&request.id)
842 .await
843 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
844 Ok(::rmcp::model::CallToolResult::success(vec![
845 ::rmcp::model::ContentBlock::json(&::serde_json::json!({ "deleted": request.id }))?,
846 ]))
847 }
848
849 #[::rmcp::tool(
850 name = $certificate_list_name,
851 title = "List signing certificates",
852 annotations(title = "List signing certificates", read_only_hint = true, idempotent_hint = true),
853 description = "Purpose: report every signing certificate the team holds, with the expiry of each, so an agent can tell what is usable before a build is attempted. When to use vs siblings: call this first to see whether a valid certificate already exists; use certificate_create only when none of the required type exists, or the existing one has expired. Behavior: returns id, name, type, serial number, expiry, and a computed expired flag, soonest expiry first. The flag is computed here because App Store Connect offers no filter for it and returns expired certificates alongside valid ones. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: certificate bodies are omitted deliberately, and this reports certificates only, not provisioning profiles, so a valid certificate here does not by itself mean a build will sign."
854 )]
855 async fn certificate_list(
856 &self,
857 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::CertificateListRequest>,
858 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
859 #[allow(unused_imports)]
860 use ::smbcloud_ascapi_aso::prelude::*;
861 #[allow(unused_imports)]
862 use ::smbcloud_ascapi_signing::prelude::*;
863 let client = Self::client()?;
864 let request = parameters.0;
865 let filter = match request.certificate_type.as_deref() {
866 Some(value) => Some(
867 ::smbcloud_ascapi_frontend::certificate_type_from_str(value)
868 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?,
869 ),
870 None => None,
871 };
872 let mut certificates = client.list_certificates(filter).await.map_err(|error| {
873 ::rmcp::model::ErrorData::internal_error(error.to_string(), None)
874 })?;
875 certificates.sort_by(|a, b| {
876 a.attributes.expiration_date.cmp(&b.attributes.expiration_date)
877 });
878 let now = ::smbcloud_ascapi_frontend::now_iso8601();
879 let summaries: Vec<_> = certificates
880 .iter()
881 .map(|certificate| ::smbcloud_ascapi_frontend::CertificateSummary::from_resource(certificate, &now))
882 .collect();
883 Ok(::rmcp::model::CallToolResult::success(vec![
884 ::rmcp::model::ContentBlock::json(&summaries)?,
885 ]))
886 }
887
888 #[::rmcp::tool(
889 name = $certificate_create_name,
890 title = "Issue a signing certificate",
891 annotations(title = "Issue a signing certificate", read_only_hint = false, destructive_hint = false, idempotent_hint = false),
892 description = "Purpose: issue a new Apple signing certificate end to end, generating the key pair locally and writing both halves to disk, so a machine can sign without anyone visiting the developer portal. When to use vs siblings: call certificate_list first and use this only when no valid certificate of the needed type exists; an expired certificate cannot be renewed, so replacing one also means calling this. Behavior: generates an RSA 2048 key pair, the only kind Apple accepts, writes the private key with owner-only permissions, sends a signing request, then writes the issued certificate beside the key and returns both paths. The key is written before the request is sent, so a failure mid-flight leaves an unused key rather than a certificate whose key was never saved. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: Apple caps how many certificates of each type a team may hold and returns an error at the cap, which is resolved by revoking one from the command line, never from here. Limitations: the response carries paths, never key material; Apple keeps no copy of the private key, so losing the file makes the certificate permanently useless."
893 )]
894 async fn certificate_create(
895 &self,
896 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::CertificateCreateRequest>,
897 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
898 #[allow(unused_imports)]
899 use ::smbcloud_ascapi_aso::prelude::*;
900 #[allow(unused_imports)]
901 use ::smbcloud_ascapi_signing::prelude::*;
902 let client = Self::client()?;
903 let request = parameters.0;
904 let certificate_type = ::smbcloud_ascapi_frontend::certificate_type_from_str(&request.certificate_type)
905 .map_err(|error| ::rmcp::model::ErrorData::invalid_request(error, None))?;
906 let common_name = request
907 .common_name
908 .unwrap_or_else(|| "smbcloud-ascapi".to_string());
909 let out_dir = ::std::path::PathBuf::from(&request.out_dir);
910 let outcome = ::smbcloud_ascapi_frontend::issue_certificate(
911 &client,
912 certificate_type,
913 &common_name,
914 &out_dir,
915 )
916 .await
917 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error, None))?;
918 Ok(::rmcp::model::CallToolResult::success(vec![
919 ::rmcp::model::ContentBlock::json(&outcome)?,
920 ]))
921 }
922
923 #[::rmcp::tool(
924 name = $beta_group_list_name,
925 title = "List beta groups",
926 annotations(title = "List beta groups", read_only_hint = true, idempotent_hint = true),
927 description = "Purpose: list every TestFlight beta group (internal and external) for an app, so an agent knows which group ids are available before adding builds or testers. When to use vs siblings: call this first to discover group ids; use beta_app_review_detail_get to retrieve the contact info Apple's beta review team will see. Behavior: returns each group's id, name, whether it is an internal group, whether it requires a public link, the public link URL (when enabled), and tester counts where available. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: tester emails are not included; use the App Store Connect portal or the CLI to enumerate individual testers."
928 )]
929 async fn beta_group_list(
930 &self,
931 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::BetaGroupListRequest>,
932 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
933 #[allow(unused_imports)]
934 use ::smbcloud_ascapi_aso::prelude::*;
935 let client = Self::client()?;
936 let request = parameters.0;
937 let groups = client
938 .list_beta_groups(&request.app_id)
939 .await
940 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
941 Ok(::rmcp::model::CallToolResult::success(vec![
942 ::rmcp::model::ContentBlock::json(&groups)?,
943 ]))
944 }
945
946 #[::rmcp::tool(
947 name = $beta_app_review_detail_get_name,
948 title = "Get beta app review contact detail",
949 annotations(title = "Get beta app review contact detail", read_only_hint = true, idempotent_hint = true),
950 description = "Purpose: retrieve the contact information Apple's TestFlight review team will use when evaluating a beta submission, such as the first name, last name, phone number, email, and any demo account credentials. When to use vs siblings: call this to verify or surface the review contact before submitting a build for beta review; use beta_group_list to discover available groups. Behavior: returns the beta app review detail resource for the given app id. Prerequisites: ASC_API_KEY, ASC_ISSUER_ID, and a readable .p8 key at ASC_PRIVATE_KEY_PATH or ~/.appstoreconnect/private_keys/AuthKey_<key-id>.p8. Failure modes: missing or unreadable credentials return an invalid-request error naming what is absent; App Store Connect errors are returned verbatim with their status. Limitations: updating the contact detail is a CLI-only operation."
951 )]
952 async fn beta_app_review_detail_get(
953 &self,
954 parameters: ::rmcp::handler::server::wrapper::Parameters<$crate::BetaAppReviewDetailGetRequest>,
955 ) -> ::std::result::Result<::rmcp::model::CallToolResult, ::rmcp::model::ErrorData> {
956 #[allow(unused_imports)]
957 use ::smbcloud_ascapi_aso::prelude::*;
958 let client = Self::client()?;
959 let request = parameters.0;
960 let detail = client
961 .get_beta_app_review_detail(&request.app_id)
962 .await
963 .map_err(|error| ::rmcp::model::ErrorData::internal_error(error.to_string(), None))?;
964 Ok(::rmcp::model::CallToolResult::success(vec![
965 ::rmcp::model::ContentBlock::json(&detail)?,
966 ]))
967 }
968 }
969 };
970}