Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let package = Package(
.executable(name: "asc-mcp", targets: ["asc-mcp"])
],
dependencies: [
.package(url: "https://github.com/modelcontextprotocol/swift-sdk.git", from: "0.3.0")
.package(url: "https://github.com/modelcontextprotocol/swift-sdk.git", from: "0.12.0")
],
targets: [
.executableTarget(
Expand Down
25 changes: 25 additions & 0 deletions Sources/asc-mcp/Core/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ public func runApplication(enabledWorkers: Set<String>? = nil) async throws {
- promoted_* -- promoted in-app purchases
- metrics_* -- performance metrics and diagnostics
- review_attachments_* -- app store review attachments (upload, get, delete, list)

## Subscription Setup Workflow (FULL)
When asked to set up subscriptions for an app, always follow this exact order:

1. Find app ID: apps_list or apps_search
2. Check existing groups: iap_list_subscriptions (app_id)
3. Create group if needed: subscriptions_create_group (app_id, reference_name)
4. Create group localization: subscriptions_create_group_localization (group_id, locale, name)
5. For each subscription:
a. subscriptions_create (group_id, name, product_id, period, group_level, review_note)
b. subscriptions_create_localization (sub_id, locale, display_name, description ≤55 chars)
c. Set territory availability via bash curl POST /v1/subscriptionAvailabilities
with all 175 territories (GET /v1/territories first to get all IDs)
d. subscriptions_list_price_points (sub_id, territory=USA) — find price point for desired price
e. subscriptions_set_price (sub_id, price_point_id)
f. If free trial needed: intro_offers_create (sub_id, duration, number_of_periods, offer_type=FREE_TRIAL)
6. Review screenshot: subscriptions_upload_review_screenshot (sub_id, image_path)
— ONLY if screenshot file path is provided; otherwise notify user it's still needed

Notes:
- Availability step MUST happen before set_price, otherwise price API returns 409
- Description max length is 55 characters
- group_level: 1 = highest tier, 2 = mid, 3 = lowest (affects upgrade/downgrade logic)
- After all steps, subscriptions move from MISSING_METADATA → READY_TO_SUBMIT once screenshot is uploaded
- Do NOT submit for App Store review — user does that manually
""",
capabilities: Server.Capabilities(
tools: Server.Capabilities.Tools(listChanged: true)
Expand Down
13 changes: 13 additions & 0 deletions Sources/asc-mcp/Models/Marketing/ScreenshotModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,19 @@ public struct CommitPreviewRequest: Codable, Sendable {
}
}

/// Update preview timecode request
public struct UpdatePreviewRequest: Codable, Sendable {
public let data: UpdateData
public struct UpdateData: Codable, Sendable {
public let type: String = "appPreviews"
public let id: String
public let attributes: Attributes
}
public struct Attributes: Codable, Sendable {
public let previewFrameTimeCode: String
}
}

/// Create preview reservation request
public struct CreatePreviewRequest: Codable, Sendable {
public let data: CreateData
Expand Down
49 changes: 49 additions & 0 deletions Sources/asc-mcp/Models/Subscriptions/IntroductoryOfferModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,55 @@ public struct CreateIntroductoryOfferRequest: Codable, Sendable {
}
}

// MARK: - Set All Territories Introductory Offer (PATCH /v1/subscriptions/{id})

/// Request to set FREE_TRIAL intro offer for all territories in one PATCH.
/// Uses relationships.introductoryOffers + included array with ${N} local IDs.
public struct SetAllIntroductoryOffersRequest: Encodable, Sendable {
public let data: UpdateData
public let included: [InlineOffer]

public struct UpdateData: Encodable, Sendable {
public let type: String = "subscriptions"
public let id: String
public let relationships: Relationships
}

public struct Relationships: Encodable, Sendable {
public let introductoryOffers: OffersData
}

public struct OffersData: Encodable, Sendable {
public let data: [OfferRef]
}

public struct OfferRef: Encodable, Sendable {
public let id: String
public let type: String = "subscriptionIntroductoryOffers"
}

public struct InlineOffer: Encodable, Sendable {
public let id: String
public let type: String = "subscriptionIntroductoryOffers"
public let attributes: OfferAttrs
public let relationships: InlineOfferRels
}

public struct OfferAttrs: Encodable, Sendable {
public let duration: String
public let offerMode: String
public let numberOfPeriods: Int
}

public struct InlineOfferRels: Encodable, Sendable {
public let territory: TerritoryRef
}

public struct TerritoryRef: Encodable, Sendable {
public let data: ASCResourceIdentifier
}
}

/// Update introductory offer request
public struct UpdateIntroductoryOfferRequest: Codable, Sendable {
public let data: UpdateData
Expand Down
127 changes: 127 additions & 0 deletions Sources/asc-mcp/Models/Subscriptions/SubscriptionModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,54 @@ public struct SubscriptionLocalizationAttributes: Codable, Sendable {
public let description: String?
}

// MARK: - Set All Subscription Prices (PATCH /v1/subscriptions/{id})

/// Request to set prices for all territories via PATCH subscription.
/// Uses GET /v1/subscriptionPricePoints/{id}/equalizations to get all territory price points,
/// then PATCHes the subscription with all of them in a single request.
public struct SetAllSubscriptionPricesRequest: Encodable, Sendable {
public let data: UpdateData
public let included: [InlinePrice]

public struct UpdateData: Encodable, Sendable {
public let type: String = "subscriptions"
public let id: String
public let relationships: Relationships
}

public struct Relationships: Encodable, Sendable {
public let prices: PricesData
}

public struct PricesData: Encodable, Sendable {
public let data: [PriceRef]
}

public struct PriceRef: Encodable, Sendable {
public let id: String
public let type: String = "subscriptionPrices"
}

public struct InlinePrice: Encodable, Sendable {
public let id: String
public let type: String = "subscriptionPrices"
public let attributes: PriceAttrs
public let relationships: InlinePriceRels
}

public struct PriceAttrs: Encodable, Sendable {
public let preserveCurrentPrice: Bool
}

public struct InlinePriceRels: Encodable, Sendable {
public let subscriptionPricePoint: PointRef
}

public struct PointRef: Encodable, Sendable {
public let data: ASCResourceIdentifier
}
}

// MARK: - Subscription Price Models

/// Subscription prices list response
Expand All @@ -67,6 +115,34 @@ public struct ASCSubscriptionPricesResponse: Codable, Sendable {
public let links: ASCPagedDocumentLinks?
}

/// Single subscription price response
public struct ASCSubscriptionPriceResponse: Codable, Sendable {
public let data: ASCSubscriptionPrice
}

/// Create subscription price request
public struct CreateSubscriptionPriceRequest: Codable, Sendable {
public let data: CreateData

public struct CreateData: Codable, Sendable {
public let type: String = "subscriptionPrices"
public let relationships: Relationships
}

public struct Relationships: Codable, Sendable {
public let subscription: SubscriptionRelationship
public let subscriptionPricePoint: SubscriptionPricePointRelationship
}

public struct SubscriptionRelationship: Codable, Sendable {
public let data: ASCResourceIdentifier
}

public struct SubscriptionPricePointRelationship: Codable, Sendable {
public let data: ASCResourceIdentifier
}
}

/// Subscription price resource
public struct ASCSubscriptionPrice: Codable, Sendable {
public let type: String
Expand Down Expand Up @@ -256,3 +332,54 @@ public struct CreateSubscriptionSubmissionRequest: Codable, Sendable {
public let data: ASCResourceIdentifier
}
}

// MARK: - Subscription Availability Models

/// Create subscription availability request (enables subscription in all territories)
public struct CreateSubscriptionAvailabilityRequest: Codable, Sendable {
public let data: CreateData
public let included: [TerritoryResource]?

public struct CreateData: Codable, Sendable {
public let type: String = "subscriptionAvailabilities"
public let attributes: Attributes
public let relationships: Relationships
}

public struct Attributes: Codable, Sendable {
public let availableInNewTerritories: Bool
}

public struct Relationships: Codable, Sendable {
public let subscription: SubscriptionRelationship2
public let availableTerritories: TerritoriesRelationship
}

public struct SubscriptionRelationship2: Codable, Sendable {
public let data: ASCResourceIdentifier
}

public struct TerritoriesRelationship: Codable, Sendable {
public let data: [ASCResourceIdentifier]
}

public struct TerritoryResource: Codable, Sendable {
public let type: String
public let id: String
}
}

/// Subscription availability response
public struct ASCSubscriptionAvailabilityResponse: Codable, Sendable {
public let data: ASCSubscriptionAvailability
}

public struct ASCSubscriptionAvailability: Codable, Sendable {
public let type: String
public let id: String
public let attributes: AvailabilityAttributes?

public struct AvailabilityAttributes: Codable, Sendable {
public let availableInNewTerritories: Bool?
}
}
2 changes: 1 addition & 1 deletion Sources/asc-mcp/Services/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ extension HTTPClient {
}

/// PATCH request for updating resources
public func patch<T: Codable & Sendable, R: Codable & Sendable>(_ endpoint: String, body: T, as responseType: R.Type) async throws -> R {
public func patch<T: Encodable & Sendable, R: Codable & Sendable>(_ endpoint: String, body: T, as responseType: R.Type) async throws -> R {
let bodyData: Data
do {
bodyData = try JSONEncoder().encode(body)
Expand Down
Loading