Skip to content

Commit 248f7e3

Browse files
zelentsov-devclaude
andcommitted
feat: Phase 3 — upload infrastructure + image workers + app availabilities v2 (284 tools)
New infrastructure: - UploadService: universal file upload helper (read file, MD5 checksum, parallel chunk upload via TaskGroup, presigned URLs without JWT) New workers (1): - ReviewAttachmentsWorker (review_attachments_*): 4 tools — upload (full reserve→upload→commit cycle), get, delete, list Extended workers with upload support (3): - InAppPurchasesWorker: +3 (iap_upload_image, iap_get_image, iap_delete_image) - SubscriptionsWorker: +6 (subscription images upload/get/delete, review screenshots upload/get/delete) - PromotedPurchasesWorker: +3 (promoted_upload_image, promoted_get_image, promoted_delete_image) Extended workers (1): - PricingWorker: +3 (app availabilities v2 — create, get, list territory availabilities) Total: 33 workers, 284 tools, 435 tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d196be8 commit 248f7e3

29 files changed

Lines changed: 2227 additions & 66 deletions

CLAUDE.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ MCP (Model Context Protocol) server for App Store Connect API integration, desig
1616
# Build the project
1717
swift build
1818

19-
# Run all unit tests (423 tests)
19+
# Run all unit tests (435 tests)
2020
swift test
2121

2222
# Run the MCP server (requires environment variables or companies.json)
@@ -49,7 +49,7 @@ Each company needs: `keyID`, `issuerID`, `privateKeyPath` (path to `.p8` file).
4949

5050
**WorkerManager** (`Workers/MainWorker/WorkerManager.swift`) — central registry, routes tool calls by prefix.
5151

52-
**Workers** (31 workers, 265 tools):
52+
**Workers** (33 workers, 284 tools):
5353

5454
| Worker | Prefix | Tools | Domain |
5555
|--------|--------|-------|--------|
@@ -62,15 +62,15 @@ Each company needs: `keyID`, `issuerID`, `privateKeyPath` (path to `.p8` file).
6262
| AppLifecycleWorker | `app_versions_` | 14 | Versions, submit, release, phased rollout, delete |
6363
| ReviewsWorker | `reviews_` | 8 | Customer reviews, responses, AI summarizations |
6464
| BetaGroupsWorker | `beta_groups_` | 9 | TestFlight groups CRUD, testers, builds |
65-
| InAppPurchasesWorker | `iap_` | 19 | IAP, subscriptions, localizations, prices, screenshots, availability |
65+
| InAppPurchasesWorker | `iap_` | 22 | IAP, subscriptions, localizations, prices, screenshots, availability, images |
6666
| ProvisioningWorker | `provisioning_` | 17 | Bundle IDs, devices, certificates, profiles, capabilities |
6767
| BetaTestersWorker | `beta_testers_` | 12 | Tester management, search, invite, relationships, invitations |
6868
| AppInfoWorker | `app_info_` | 10 | App info, categories, localizations, EULA |
69-
| PricingWorker | `pricing_` | 6 | Territories, availability, price points/schedule |
69+
| PricingWorker | `pricing_` | 9 | Territories, availability, price points/schedule, app availabilities v2 |
7070
| UsersWorker | `users_` | 10 | Team members, roles, invitations, visible apps |
7171
| AppEventsWorker | `app_events_` | 9 | In-app events CRUD, localizations |
7272
| AnalyticsWorker | `analytics_` | 11 | Sales/financial reports, app summary, analytics reports/instances/segments, snapshot status |
73-
| SubscriptionsWorker | `subscriptions_` | 21 | Subscription CRUD, groups, localizations, prices, submit, group localizations, price delete |
73+
| SubscriptionsWorker | `subscriptions_` | 27 | Subscription CRUD, groups, localizations, prices, submit, group localizations, images, review screenshots |
7474
| OfferCodesWorker | `offer_codes_` | 10 | Subscription offer codes, one-time codes, custom codes |
7575
| WinBackOffersWorker | `winback_` | 5 | Win-back offers for subscriptions |
7676
| IntroductoryOffersWorker | `intro_offers_` | 4 | Introductory offers (free trial, pay-as-you-go, pay-up-front) |
@@ -82,7 +82,8 @@ Each company needs: `keyID`, `issuerID`, `privateKeyPath` (path to `.p8` file).
8282
| ScreenshotsWorker | `screenshots_` | 12 | Screenshots, previews, sets, reorder |
8383
| CustomProductPagesWorker | `custom_pages_` | 10 | Custom product pages, versions, localizations |
8484
| ProductPageOptimizationWorker | `ppo_` | 9 | A/B test experiments, treatments |
85-
| PromotedPurchasesWorker | `promoted_` | 5 | Promoted in-app purchases, images |
85+
| PromotedPurchasesWorker | `promoted_` | 8 | Promoted in-app purchases, images upload |
86+
| ReviewAttachmentsWorker | `review_attachments_` | 4 | App Store review attachments (upload, get, delete, list) |
8687
| MetricsWorker | `metrics_` | 4 | Performance/power metrics, diagnostics |
8788

8889
**Services**: HTTPClient (actor, GET/POST/PATCH/PUT/DELETE + retry with 429), JWTService (ES256), CompaniesManager
@@ -109,7 +110,7 @@ Each company needs: `keyID`, `issuerID`, `privateKeyPath` (path to `.p8` file).
109110
### Unit Tests (Swift Testing)
110111

111112
```bash
112-
swift test # Run all 423 tests across 31 suites
113+
swift test # Run all 435 tests across 31 suites
113114
```
114115

115116
Test categories:

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
## Overview
3131

32-
**asc-mcp** is a Swift-based MCP server that bridges [Claude](https://claude.ai) (or any MCP-compatible host) with the [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi). It exposes **265 tools** across 31 workers, enabling you to automate your entire iOS/macOS release workflow through natural language.
32+
**asc-mcp** is a Swift-based MCP server that bridges [Claude](https://claude.ai) (or any MCP-compatible host) with the [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi). It exposes **284 tools** across 33 workers, enabling you to automate your entire iOS/macOS release workflow through natural language.
3333

3434
### Key capabilities
3535

@@ -368,7 +368,7 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
368368
369369
### Worker Filtering
370370
371-
The server exposes **265 tools** across 31 workers. Some MCP clients impose a tool limit (e.g., Windsurf caps at 100). Use `--workers` to enable only the workers you need:
371+
The server exposes **284 tools** across 33 workers. Some MCP clients impose a tool limit (e.g., Windsurf caps at 100). Use `--workers` to enable only the workers you need:
372372
373373
```bash
374374
# Only load apps, builds, and version lifecycle tools
@@ -432,7 +432,7 @@ For Claude (200K context) ~22K tokens is ~5–7% — negligible. For clients wit
432432
433433
## Available Tools
434434
435-
**265 tools** organized across 31 workers (use `--workers` to filter — see [Worker Filtering](#worker-filtering)):
435+
**284 tools** organized across 33 workers (use `--workers` to filter — see [Worker Filtering](#worker-filtering)):
436436
437437
<details>
438438
<summary><strong>Company Management</strong> — 3 tools</summary>
@@ -887,7 +887,7 @@ Sources/asc-mcp/
887887
│ ├── HTTPClient.swift # Actor-based HTTP with retry logic
888888
│ ├── JWTService.swift # ES256 JWT token generation
889889
│ └── CompaniesManager.swift # Multi-account management
890-
└── Workers/ # MCP tool implementations (31 workers)
890+
└── Workers/ # MCP tool implementations (33 workers)
891891
├── MainWorker/WorkerManager # Central tool registry & routing
892892
├── CompaniesWorker/ # company_* tools
893893
├── AuthWorker/ # auth_* tools

Sources/asc-mcp/Core/Application.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public func runApplication(enabledWorkers: Set<String>? = nil) async throws {
6868
- ppo_* -- product page optimization (A/B testing)
6969
- promoted_* -- promoted in-app purchases
7070
- metrics_* -- performance metrics and diagnostics
71+
- review_attachments_* -- app store review attachments (upload, get, delete, list)
7172
""",
7273
capabilities: Server.Capabilities(
7374
tools: Server.Capabilities.Tools(listChanged: true)

Sources/asc-mcp/EntryPoint.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ struct ASCMCPApp {
5151
"subscriptions", "offer_codes", "winback", "intro_offers", "promo_offers",
5252
"sandbox", "beta_app", "pre_release", "beta_license",
5353
"screenshots", "custom_pages",
54-
"ppo", "promoted", "metrics"
54+
"ppo", "promoted", "metrics", "review_attachments"
5555
]
5656

5757
let requested = CommandLine.arguments[index + 1]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import Foundation
2+
3+
// MARK: - Review Attachment Models
4+
5+
/// Review attachments list response
6+
public struct ASCReviewAttachmentsResponse: Codable, Sendable {
7+
public let data: [ASCReviewAttachment]
8+
public let links: ASCPagedDocumentLinks?
9+
}
10+
11+
/// Single review attachment response
12+
public struct ASCReviewAttachmentResponse: Codable, Sendable {
13+
public let data: ASCReviewAttachment
14+
}
15+
16+
/// Review attachment resource
17+
public struct ASCReviewAttachment: Codable, Sendable {
18+
public let type: String
19+
public let id: String
20+
public let attributes: ReviewAttachmentAttributes?
21+
}
22+
23+
/// Review attachment attributes
24+
public struct ReviewAttachmentAttributes: Codable, Sendable {
25+
public let fileSize: Int?
26+
public let fileName: String?
27+
public let sourceFileChecksum: String?
28+
public let uploadOperations: [ASCUploadOperation]?
29+
public let assetDeliveryState: ASCAssetDeliveryState?
30+
}
31+
32+
// MARK: - Review Attachment Request Models
33+
34+
/// Create review attachment reservation request
35+
public struct CreateReviewAttachmentRequest: Codable, Sendable {
36+
public let data: CreateData
37+
38+
public struct CreateData: Codable, Sendable {
39+
public let type: String = "appStoreReviewAttachments"
40+
public let attributes: Attributes
41+
public let relationships: Relationships
42+
}
43+
44+
public struct Attributes: Codable, Sendable {
45+
public let fileSize: Int
46+
public let fileName: String
47+
}
48+
49+
public struct Relationships: Codable, Sendable {
50+
public let appStoreReviewDetail: ReviewDetailRelationship
51+
}
52+
53+
public struct ReviewDetailRelationship: Codable, Sendable {
54+
public let data: ASCResourceIdentifier
55+
}
56+
}
57+
58+
/// Commit review attachment request
59+
public struct CommitReviewAttachmentRequest: Codable, Sendable {
60+
public let data: CommitData
61+
62+
public struct CommitData: Codable, Sendable {
63+
public let type: String = "appStoreReviewAttachments"
64+
public let id: String
65+
public let attributes: Attributes
66+
}
67+
68+
public struct Attributes: Codable, Sendable {
69+
public let sourceFileChecksum: String?
70+
public let uploaded: Bool?
71+
}
72+
}

Sources/asc-mcp/Models/InAppPurchases/InAppPurchaseModels.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,70 @@ public struct CreateIAPAvailabilityRequest: Codable, Sendable {
350350
}
351351
}
352352

353+
// MARK: - IAP Image Models
354+
355+
/// IAP image single response
356+
public struct ASCIAPImageResponse: Codable, Sendable {
357+
public let data: ASCIAPImage
358+
}
359+
360+
/// IAP image resource
361+
public struct ASCIAPImage: Codable, Sendable {
362+
public let type: String
363+
public let id: String
364+
public let attributes: IAPImageAttributes?
365+
}
366+
367+
/// IAP image attributes
368+
public struct IAPImageAttributes: Codable, Sendable {
369+
public let fileSize: Int?
370+
public let fileName: String?
371+
public let sourceFileChecksum: String?
372+
public let imageAsset: ASCImageAsset?
373+
public let uploadOperations: [ASCUploadOperation]?
374+
public let state: String?
375+
}
376+
377+
/// Create IAP image reservation request
378+
public struct CreateIAPImageRequest: Codable, Sendable {
379+
public let data: CreateData
380+
381+
public struct CreateData: Codable, Sendable {
382+
public let type: String = "inAppPurchaseImages"
383+
public let attributes: Attributes
384+
public let relationships: Relationships
385+
}
386+
387+
public struct Attributes: Codable, Sendable {
388+
public let fileSize: Int
389+
public let fileName: String
390+
}
391+
392+
public struct Relationships: Codable, Sendable {
393+
public let inAppPurchase: IAPImageRelationship
394+
}
395+
396+
public struct IAPImageRelationship: Codable, Sendable {
397+
public let data: ASCResourceIdentifier
398+
}
399+
}
400+
401+
/// Commit IAP image request
402+
public struct CommitIAPImageRequest: Codable, Sendable {
403+
public let data: CommitData
404+
405+
public struct CommitData: Codable, Sendable {
406+
public let type: String = "inAppPurchaseImages"
407+
public let id: String
408+
public let attributes: Attributes
409+
}
410+
411+
public struct Attributes: Codable, Sendable {
412+
public let sourceFileChecksum: String?
413+
public let uploaded: Bool?
414+
}
415+
}
416+
353417
// MARK: - Create IAP Review Screenshot Request
354418

355419
/// Create IAP review screenshot request
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import Foundation
2+
3+
// MARK: - Promoted Purchase Image Models
4+
5+
/// Single promoted purchase image response
6+
public struct ASCPromotedPurchaseImageResponse: Codable, Sendable {
7+
public let data: ASCPromotedPurchaseImage
8+
}
9+
10+
/// Promoted purchase image resource
11+
public struct ASCPromotedPurchaseImage: Codable, Sendable {
12+
public let type: String
13+
public let id: String
14+
public let attributes: PromotedPurchaseImageAttributes?
15+
}
16+
17+
/// Promoted purchase image attributes
18+
public struct PromotedPurchaseImageAttributes: Codable, Sendable {
19+
public let fileSize: Int?
20+
public let fileName: String?
21+
public let sourceFileChecksum: String?
22+
public let imageAsset: ASCImageAsset?
23+
public let uploadOperations: [ASCUploadOperation]?
24+
public let state: String?
25+
}
26+
27+
// MARK: - Request Models
28+
29+
/// Create promoted purchase image request (reserve step)
30+
public struct CreatePromotedPurchaseImageRequest: Codable, Sendable {
31+
public let data: CreateData
32+
33+
public struct CreateData: Codable, Sendable {
34+
public let type: String = "promotedPurchaseImages"
35+
public let attributes: Attributes
36+
public let relationships: Relationships
37+
}
38+
39+
public struct Attributes: Codable, Sendable {
40+
public let fileSize: Int
41+
public let fileName: String
42+
}
43+
44+
public struct Relationships: Codable, Sendable {
45+
public let promotedPurchase: PromotedPurchaseRelationship
46+
}
47+
48+
public struct PromotedPurchaseRelationship: Codable, Sendable {
49+
public let data: ASCResourceIdentifier
50+
}
51+
}
52+
53+
/// Commit promoted purchase image request
54+
public struct CommitPromotedPurchaseImageRequest: Codable, Sendable {
55+
public let data: CommitData
56+
57+
public struct CommitData: Codable, Sendable {
58+
public let type: String = "promotedPurchaseImages"
59+
public let id: String
60+
public let attributes: Attributes
61+
}
62+
63+
public struct Attributes: Codable, Sendable {
64+
public let sourceFileChecksum: String?
65+
public let uploaded: Bool?
66+
}
67+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
3+
// MARK: - Create App Availability v2 Request
4+
5+
/// Request body for creating app availability via v2 endpoint
6+
public struct CreateAppAvailabilityV2Request: Codable, Sendable {
7+
public let data: CreateData
8+
9+
public struct CreateData: Codable, Sendable {
10+
public let type: String = "appAvailabilities"
11+
public let attributes: Attributes
12+
public let relationships: Relationships
13+
}
14+
15+
public struct Attributes: Codable, Sendable {
16+
public let availableInNewTerritories: Bool
17+
}
18+
19+
public struct Relationships: Codable, Sendable {
20+
public let app: AppRelationship
21+
public let territoryAvailabilities: TerritoryAvailabilitiesRelationship
22+
}
23+
24+
public struct AppRelationship: Codable, Sendable {
25+
public let data: ASCResourceIdentifier
26+
}
27+
28+
public struct TerritoryAvailabilitiesRelationship: Codable, Sendable {
29+
public let data: [ASCResourceIdentifier]
30+
}
31+
}

0 commit comments

Comments
 (0)