fix(apps): allow version metadata reads when relationship linkage is absent#6
Conversation
…absent
apps_get_metadata and apps_list_localizations always failed with
"does not belong to app" / "Apple returned a localization outside version
context", even for a version id the server's own list tools just returned.
Root cause: versionBelongsToApp() and localizationBelongsToVersion() require
the JSON:API resource linkage (relationships.app.data /
relationships.appStoreVersion.data). App Store Connect only serializes that
linkage when the request asks for it with include=. These read paths do not
send include=, so Apple returns a links-only relationship with no data member,
the parsed id is absent, and the guard treated absent as a mismatch and
rejected every response.
Fix: treat an absent linkage as "cannot verify, allow" and reject only on a
positive mismatch (linkage present but pointing at a different id). The
sub-resources are fetched under /appStoreVersions/{id}/... and the version is
fetched by id, so the URL already scopes them; the guard remains useful for the
include= case without producing false negatives on the default response shape.
Adds AppsMetadataRelationshipGuardTests exercising both tools against a
recorded response with the relationship linkage omitted (the default Apple
shape). The tests fail before this change and pass after. Fixtures use dummy
UUIDs only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thank you for the detailed live reproduction and root-cause analysis — it directly led to the v4.1.3 fix. We shipped a containment-preserving variant: the affected reads now explicitly request Apple relationship linkage with include=app or include=appStoreVersion, while missing or mismatched linkage still fails closed. The same issue class was also corrected across version metadata/localizations, review details and attachments, review submissions, lifecycle submission, and legacy age-rating lookup. Verification completed on the tagged release: 1,847 tests, zero-warning release build, strict App Store Connect OpenAPI 4.4.1 contract, Mint installation from the tag, and a real MCP stdio smoke. You are credited in the changelog and release notes. Released: https://github.com/zelentsov-dev/asc-mcp/releases/tag/v4.1.3 Closing this PR as resolved by v4.1.3. Thanks again for finding and documenting it so clearly. |
Summary
Every tool that reads version-level App Store metadata fails for all apps and all versions, even when passed a
version_idthe server's own list tools just returned.apps_get_metadataandapps_list_localizationsare effectively dead, so the keyword field, description, promotional text, and "What's New" (which live only onappStoreVersionLocalizations) cannot be read back at all.Root cause
One-line: the two relationship guards require a JSON:API resource linkage that App Store Connect only serializes when the request sends
include=, so on the default response shape the parsed id is absent and the guard treats "absent" as "mismatch."versionBelongsToApp()andlocalizationBelongsToVersion()inAppsWorker+Handlers.swiftgate the read paths on:version.relationships.app.data.id == appIdlocalization.relationships.appStoreVersion.data.id == versionIdApp Store Connect only includes a relationship's
datalinkage when the request explicitly asks for it viainclude=app/include=appStoreVersion. These read paths do not sendinclude=, so Apple returns a links-only relationship:There is no
datamember, so the parsed id is absent. The guards'guard let ... else { return false }then treated the absent linkage as a mismatch and rejected every response, producing:apps_get_metadata->Apple returned a localization outside version '<id>' contextapps_list_localizations->Version '<id>' does not belong to app '<id>'App-level reads (
app_info_listviaappInfoLocalizations) andapps_list_versions/apps_get_detailswere unaffected because they do not hit these guards, which is why the failure was isolated to the version-localization code path.Reproduction
Against a live account:
The
<valid-version>id in each case was returned byapps_list_versions/app_versions_listmoments earlier.Fix
Treat an absent linkage as "cannot verify, allow" and reject only on a positive mismatch (linkage present but pointing at a different id). The sub-resources are fetched under
/appStoreVersions/{id}/appStoreVersionLocalizationsand the version is fetched by id, so the URL already scopes them; the guard stays meaningful for theinclude=case without producing false negatives on the default response shape. The change isreturn false->return truein theelsebranch of each guard, plus explaining comments.Tests
Adds
AppsMetadataRelationshipGuardTests, which drives bothapps_get_metadataandapps_list_localizationsthrough a recorded App Store Connect response whose relationship linkage is omitted (the default Apple shape) and asserts the tools succeed and return the metadata (e.g. thekeywordsfield).These tests fail before the change (guard fires,
isError == true) and pass after. All fixtures use dummy UUIDs; no real ids are committed.The existing
AppsMetadataGetSelectionTestscontinue to pass: their fixtures include the linkage, so the positive-match path is still covered.swift test-> all tests pass (1168 tests / 108 suites, including the 2 new ones).Notes
include=params were added (the smaller, safer fix, since the URL already scopes the resource).