Skip to content
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [4.1.3] - 2026-07-21

### Fixed

- Restore App Store version metadata reads, localization listing and updates, review-detail resolution, review-attachment reads, review-submission listing and preflight, and legacy age-rating lookup by explicitly requesting the parent relationship linkage that Apple can omit from sparse responses unless `include` is supplied.
- Preserve strict app and version ownership validation: missing or mismatched linkage still fails locally instead of weakening cross-resource containment checks. Thanks to [@muenzpraeger](https://github.com/muenzpraeger) for the live report and regression analysis in [#6](https://github.com/zelentsov-dev/asc-mcp/pull/6).

### Compatibility

- The public catalog remains at 502 tools with no renamed inputs or removed response fields. Existing callers require no configuration changes.

## [4.1.2] - 2026-07-21

### Changed
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
```bash
# 1. Install via Mint
brew install mint
mint install zelentsov-dev/asc-mcp@v4.1.2
mint install zelentsov-dev/asc-mcp@v4.1.3

# 2. Add to Claude Code with env vars (simplest setup)
claude mcp add asc-mcp \
Expand Down Expand Up @@ -88,7 +88,7 @@ Or use a JSON config file — see [Configuration](#configuration) below.
brew install mint

# Install asc-mcp from GitHub
mint install zelentsov-dev/asc-mcp@v4.1.2
mint install zelentsov-dev/asc-mcp@v4.1.3

# Register in Claude Code
claude mcp add asc-mcp -- ~/.mint/bin/asc-mcp
Expand All @@ -99,13 +99,13 @@ To install a specific branch or tag:
```bash
mint install zelentsov-dev/asc-mcp@main # main branch
mint install zelentsov-dev/asc-mcp@develop # develop branch
mint install zelentsov-dev/asc-mcp@v4.1.2 # specific tag
mint install zelentsov-dev/asc-mcp@v4.1.3 # specific tag
```

To update to the latest version:

```bash
mint install zelentsov-dev/asc-mcp@v4.1.2 --force
mint install zelentsov-dev/asc-mcp@v4.1.3 --force
```

### Migrating from v4.0.x
Expand Down Expand Up @@ -451,7 +451,7 @@ swift run asc-mcp openapi-contract-check \

The manifest is pinned to Apple API 4.4.1 by version, SHA-256, path count, and operation count. It currently maps 476 Apple operations, explicitly defers 424, and scopes out 363, covering all 1,263 operations without overlap. CI fails when the Apple document changes, a mapped operation moves or disappears, a public tool or worker drifts from the manifest, an input field loses its binding, response lineage becomes invalid, or a deferred decision expires. Unexposed optional Apple parameters are warnings so they remain visible in the generated backlog.

Manifest schema v2 also accounts for every optional Apple query and request-body input as publicly bound, internally controlled, intentionally omitted with a reviewed reason, or still unclassified. The checked-in `optionalInputCoveragePin` records the exact current totals and a SHA-256 digest of the sorted input identities and dispositions; `--strict` rejects a missing pin or any count- or identity-level drift. The pin makes phased remediation auditable and regression-safe, but it is not a claim that every optional Apple input is already public. The v4.1.2 pin is 2,905 total: 1,108 bound, 40 internally controlled, 1,757 intentionally omitted, and 0 unclassified. Its identity SHA-256 is `2e5eb2ebc1f4ae368dcb26fc8cd9de895866e27c6c22fd96f58e4e573e4af368`.
Manifest schema v2 also accounts for every optional Apple query and request-body input as publicly bound, internally controlled, intentionally omitted with a reviewed reason, or still unclassified. The checked-in `optionalInputCoveragePin` records the exact current totals and a SHA-256 digest of the sorted input identities and dispositions; `--strict` rejects a missing pin or any count- or identity-level drift. The pin makes phased remediation auditable and regression-safe, but it is not a claim that every optional Apple input is already public. The v4.1.3 pin is 2,905 total: 1,122 bound, 40 internally controlled, 1,743 intentionally omitted, and 0 unclassified. Its identity SHA-256 is `c975f4e4eebb62ec87864a73fbf72bb8841f644108e54e6ffb25168bcf2a2766`.

`--strict` is the merge- and tag-time release gate. Every declared `target` or `broken` tool remains an error in reports, and a regression test pins their exact state. The current baseline has no `target` or `broken` implementations and no implementation drift, so any implementation that leaves `asBuilt`, any structural contract error, or any optional-input coverage drift blocks both merges and releases. `--structural-strict` remains available only for local phased remediation work.

Expand Down
2 changes: 1 addition & 1 deletion Sources/asc-mcp/Core/ServerVersion.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation

enum ServerVersion {
static let current = "4.1.2"
static let current = "4.1.3"
}
6 changes: 3 additions & 3 deletions Sources/asc-mcp/Resources/OperationManifest/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
},
"optionalInputCoveragePin": {
"total": 2905,
"bound": 1108,
"bound": 1122,
"internalControl": 40,
"intentionallyOmitted": 1757,
"intentionallyOmitted": 1743,
"unclassified": 0,
"identitySHA256": "2e5eb2ebc1f4ae368dcb26fc8cd9de895866e27c6c22fd96f58e4e573e4af368"
"identitySHA256": "c975f4e4eebb62ec87864a73fbf72bb8841f644108e54e6ffb25168bcf2a2766"
},
"optionalParameterFamilyRules": [
{
Expand Down
42 changes: 36 additions & 6 deletions Sources/asc-mcp/Resources/OperationManifest/tools/apps.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@
"role": "supporting",
"condition": "version_id is supplied",
"optionalParameterClassifications": [
{ "location": "query", "appleName": "include", "disposition": "intentionallyOmitted", "reason": "The ownership and selection check uses a fixed sparse App Store version projection and does not consume included relationships.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionExperiments]", "disposition": "intentionallyOmitted", "reason": "App Store version experiments are not included by this metadata lookup.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionExperimentsV2]", "disposition": "intentionallyOmitted", "reason": "App Store version experiments are not included by this metadata lookup.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionLocalizations]", "disposition": "intentionallyOmitted", "reason": "Localizations are fetched through their dedicated paginated related-resource operation.", "reviewAtSpec": "4.4.1" }
Expand All @@ -146,6 +145,12 @@
"location": "query",
"appleName": "fields[appStoreVersions]",
"fixedValue": ["app", "platform", "versionString", "appVersionState", "appStoreState"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["app"]
}
]
},
Expand Down Expand Up @@ -187,7 +192,6 @@
"path": "/v1/appStoreVersions/{id}/appStoreVersionLocalizations",
"role": "primary",
"optionalParameterClassifications": [
{ "location": "query", "appleName": "include", "disposition": "intentionallyOmitted", "reason": "The metadata aggregate reads base localizations and loads media through dedicated paginated operations.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appPreviewSets]", "disposition": "intentionallyOmitted", "reason": "Preview sets are loaded through their dedicated paginated related-resource operation.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appScreenshotSets]", "disposition": "intentionallyOmitted", "reason": "Screenshot sets are loaded through their dedicated paginated related-resource operation.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[searchKeywords]", "disposition": "intentionallyOmitted", "reason": "Search keyword relationships are outside the metadata aggregate response.", "reviewAtSpec": "4.4.1" }
Expand All @@ -209,6 +213,12 @@
"appleName": "fields[appStoreVersionLocalizations]",
"fixedValue": ["description", "locale", "keywords", "marketingUrl", "promotionalText", "supportUrl", "whatsNew", "appStoreVersion"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["appStoreVersion"]
},
{
"sourceKind": "fixed",
"location": "query",
Expand Down Expand Up @@ -406,7 +416,6 @@
"path": "/v1/appStoreVersions/{id}",
"role": "supporting",
"optionalParameterClassifications": [
{ "location": "query", "appleName": "include", "disposition": "intentionallyOmitted", "reason": "The ownership check reads only the fixed App relationship projection.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionExperiments]", "disposition": "intentionallyOmitted", "reason": "App Store version experiments are not included in the ownership check.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionExperimentsV2]", "disposition": "intentionallyOmitted", "reason": "App Store version experiments are not included in the ownership check.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionLocalizations]", "disposition": "intentionallyOmitted", "reason": "Localizations are fetched by the dedicated primary operation.", "reviewAtSpec": "4.4.1" }
Expand All @@ -417,6 +426,12 @@
"location": "query",
"appleName": "fields[appStoreVersions]",
"fixedValue": ["app"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["app"]
}
]
},
Expand All @@ -426,7 +441,6 @@
"path": "/v1/appStoreVersions/{id}/appStoreVersionLocalizations",
"role": "primary",
"optionalParameterClassifications": [
{ "location": "query", "appleName": "include", "disposition": "intentionallyOmitted", "reason": "The list returns the base localization projection and does not expand relationships.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appPreviewSets]", "disposition": "intentionallyOmitted", "reason": "Preview sets are not included by the localization list.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appScreenshotSets]", "disposition": "intentionallyOmitted", "reason": "Screenshot sets are not included by the localization list.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[searchKeywords]", "disposition": "intentionallyOmitted", "reason": "Search keywords are not included by the localization list.", "reviewAtSpec": "4.4.1" }
Expand All @@ -437,6 +451,12 @@
"location": "query",
"appleName": "fields[appStoreVersionLocalizations]",
"fixedValue": ["locale", "description", "whatsNew", "keywords", "promotionalText", "supportUrl", "marketingUrl", "appStoreVersion"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["appStoreVersion"]
}
]
}
Expand Down Expand Up @@ -715,7 +735,6 @@
"path": "/v1/appStoreVersions/{id}",
"role": "supporting",
"optionalParameterClassifications": [
{ "location": "query", "appleName": "include", "disposition": "intentionallyOmitted", "reason": "The write preflight reads only the fixed version ownership and state projection.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionExperiments]", "disposition": "intentionallyOmitted", "reason": "App Store version experiments are not included in the write preflight.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionExperimentsV2]", "disposition": "intentionallyOmitted", "reason": "App Store version experiments are not included in the write preflight.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appStoreVersionLocalizations]", "disposition": "intentionallyOmitted", "reason": "The target localization is resolved through the dedicated filtered operation.", "reviewAtSpec": "4.4.1" }
Expand All @@ -726,6 +745,12 @@
"location": "query",
"appleName": "fields[appStoreVersions]",
"fixedValue": ["app", "platform", "versionString", "appVersionState", "appStoreState"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["app"]
}
]
},
Expand All @@ -736,7 +761,6 @@
"role": "supporting",
"optionalParameterClassifications": [
{ "location": "query", "appleName": "limit", "disposition": "internalControl", "reason": "The exact-locale write lookup fixes the page size to one before patching the resolved localization.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "include", "disposition": "intentionallyOmitted", "reason": "The exact-locale write lookup uses a fixed base localization projection and does not expand relationships.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appPreviewSets]", "disposition": "intentionallyOmitted", "reason": "Preview sets are not included in the write lookup.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[appScreenshotSets]", "disposition": "intentionallyOmitted", "reason": "Screenshot sets are not included in the write lookup.", "reviewAtSpec": "4.4.1" },
{ "location": "query", "appleName": "limit[searchKeywords]", "disposition": "intentionallyOmitted", "reason": "Search keywords are not included in the write lookup.", "reviewAtSpec": "4.4.1" }
Expand All @@ -747,6 +771,12 @@
"location": "query",
"appleName": "fields[appStoreVersionLocalizations]",
"fixedValue": ["locale", "appStoreVersion"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["appStoreVersion"]
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,6 @@
"method": "get",
"path": "/v1/appStoreReviewAttachments/{id}",
"role": "primary",
"optionalParameterClassifications": [
{
"location": "query",
"appleName": "include",
"disposition": "intentionallyOmitted",
"reason": "The attachment projection exposes its review-detail relationship ID without expanding reviewer contact or demo-account data.",
"reviewAtSpec": "4.4.1"
}
],
"inputs": [
{
"sourceKind": "fixed",
Expand All @@ -80,6 +71,18 @@
"assetDeliveryState",
"appStoreReviewDetail"
]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["appStoreReviewDetail"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "fields[appStoreReviewDetails]",
"fixedValue": ["appStoreVersion"]
}
]
}
Expand Down Expand Up @@ -133,15 +136,6 @@
"method": "get",
"path": "/v1/appStoreReviewDetails/{id}/appStoreReviewAttachments",
"role": "primary",
"optionalParameterClassifications": [
{
"location": "query",
"appleName": "include",
"disposition": "intentionallyOmitted",
"reason": "The list projects each attachment's review-detail relationship ID without repeating reviewer contact or demo-account data in included resources.",
"reviewAtSpec": "4.4.1"
}
],
"inputs": [
{
"sourceKind": "fixed",
Expand All @@ -154,6 +148,18 @@
"assetDeliveryState",
"appStoreReviewDetail"
]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "include",
"fixedValue": ["appStoreReviewDetail"]
},
{
"sourceKind": "fixed",
"location": "query",
"appleName": "fields[appStoreReviewDetails]",
"fixedValue": ["appStoreVersion"]
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@
{ "toolField": "app_id", "sourceKind": "parameter", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "filter[app]" },
{ "toolField": "states", "sourceKind": "parameter", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "filter[state]" },
{ "toolField": "platforms", "sourceKind": "parameter", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "filter[platform]" },
{ "toolField": "include", "sourceKind": "parameter", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "include" },
{ "toolField": "include", "sourceKind": "derived", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "include", "derivedFrom": ["include"], "localRole": "Preserve the caller-selected relationships and always add app so every returned submission can be validated against the required app_id filter." },
{ "toolField": "item_limit", "sourceKind": "parameter", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "limit[items]" },
{ "toolField": "limit", "sourceKind": "parameter", "operationId": "reviewSubmissions_getCollection", "location": "query", "appleName": "limit" },
{ "toolField": "next_url", "sourceKind": "local", "localRole": "Validate the exact origin, collection path, complete originating query, query allowlist, and non-empty cursor." }
Expand Down Expand Up @@ -268,7 +268,7 @@
{ "outputField": "total", "operationId": "reviewSubmissions_getCollection", "jsonPointer": "/meta/paging/total" }
]
},
"note": "App ownership is mandatory; scalar, strict CSV, and unique-array filters are validated before the request, and continuation URLs preserve every originating query value. Apple collection responses are bounded by the effective requested limit, require scoped self/next links, preserve cursor consistency, and reject invalid or duplicate resource identities."
"note": "App ownership is mandatory; scalar, strict CSV, and unique-array filters are validated before the request, and the effective include always contains app even when the caller selects a narrower relationship subset. Continuation URLs preserve every effective originating query value. Apple collection responses are bounded by the effective requested limit, require scoped self/next links, preserve cursor consistency, and reject invalid or duplicate resource identities."
},
{
"tool": "review_submissions_list_items",
Expand Down
Loading