Skip to content

Commit fcfa12f

Browse files
Merge pull request #27 from zelentsov-dev/bugs/review-submissions-item-limit
fix(review-submissions): send limit[items] only when items are included
2 parents e485a95 + 4c26a15 commit fcfa12f

6 files changed

Lines changed: 82 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [4.1.6] - 2026-08-22
11+
12+
### Fixed
13+
14+
- Send `limit[items]` on review submission reads only when `items` is part of `include`. Apple ignores the nested item limit for other includes and omits it from `links.next`, so `review_submissions_list` with an explicit `include` that excluded `items` rejected Apple's own next page with `does not preserve required query parameter 'limit[items]'` whenever the collection exceeded the page limit. `item_limit` is now applied only together with `items`, and continuation links are validated against the parameters that were actually sent.
15+
16+
### Compatibility
17+
18+
- The public catalog remains at 502 tools with no renamed inputs or removed response fields. `item_limit` passed with an `include` that lacks `items` is no longer forwarded to Apple; continuation calls repeat the same arguments as before.
19+
1020
## [4.1.5] - 2026-08-22
1121

1222
### Fixed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ The recommended setup stores App Store Connect credentials once in a private loc
5858

5959
```bash
6060
brew install mint
61-
mint install zelentsov-dev/asc-mcp@v4.1.5
61+
mint install zelentsov-dev/asc-mcp@v4.1.6
6262
~/.mint/bin/asc-mcp --version
6363
```
6464

@@ -149,13 +149,13 @@ If the connection or request fails, see [Troubleshooting](#troubleshooting).
149149

150150
```bash
151151
brew install mint
152-
mint install zelentsov-dev/asc-mcp@v4.1.5
152+
mint install zelentsov-dev/asc-mcp@v4.1.6
153153
```
154154

155155
Update or reinstall the pinned release:
156156

157157
```bash
158-
mint install zelentsov-dev/asc-mcp@v4.1.5 --force
158+
mint install zelentsov-dev/asc-mcp@v4.1.6 --force
159159
```
160160

161161
Stable users should install a version tag. Installing `main` or `develop` is intended only for maintainers and pre-release testing.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Foundation
22

33
enum ServerVersion {
4-
static let current = "4.1.5"
4+
static let current = "4.1.6"
55
}

Sources/asc-mcp/Workers/ReviewSubmissionsWorker/ReviewSubmissionsWorker+Handlers.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,15 +819,18 @@ extension ReviewSubmissionsWorker {
819819
}
820820

821821
private func submissionQuery(includes: [String], itemLimit: Int) -> [String: String] {
822-
[
822+
var query = [
823823
"fields[reviewSubmissions]": Self.submissionFields,
824824
"fields[apps]": Self.appFields,
825825
"fields[reviewSubmissionItems]": Self.itemIdentityFields,
826826
"fields[appStoreVersions]": Self.appStoreVersionFields,
827827
"fields[actors]": Self.actorFields,
828-
"include": includes.joined(separator: ","),
829-
"limit[items]": String(itemLimit)
828+
"include": includes.joined(separator: ",")
830829
]
830+
if includes.contains("items") {
831+
query["limit[items]"] = String(itemLimit)
832+
}
833+
return query
831834
}
832835

833836
private func itemListQuery() -> [String: String] {

Sources/asc-mcp/Workers/ReviewSubmissionsWorker/ReviewSubmissionsWorker+ToolDefinitions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension ReviewSubmissionsWorker {
2424
values: Self.submissionIncludes
2525
),
2626
"item_limit": integerSchema(
27-
description: "Maximum included review items (default: 50, max: 50)",
27+
description: "Maximum included review items; applies only when include contains items (default: 50, max: 50)",
2828
maximum: 50,
2929
defaultValue: 50
3030
),
@@ -59,7 +59,7 @@ extension ReviewSubmissionsWorker {
5959
values: Self.submissionIncludes
6060
),
6161
"item_limit": integerSchema(
62-
description: "Maximum included review items (default: 50, max: 50)",
62+
description: "Maximum included review items; applies only when include contains items (default: 50, max: 50)",
6363
maximum: 50,
6464
defaultValue: 50
6565
)

Tests/ASCMCPTests/Workers/ReviewSubmissionsWorkerContractTests.swift

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,64 @@ struct ReviewSubmissionsWorkerContractTests {
13041304
#expect(forwarded == reorderedQuery)
13051305
}
13061306

1307+
@Test("item_limit is sent only with items and Apple's next link without it is accepted")
1308+
func itemLimitFollowsItemsInclude() async throws {
1309+
var query = reviewSubmissionListQuery(appID: "app-1", includes: "app,submittedByActor", itemLimit: nil)
1310+
query["cursor"] = "page-2"
1311+
let next = reviewSubmissionURL(path: "/v1/reviewSubmissions", query: query)
1312+
let arguments: [String: Value] = [
1313+
"app_id": .string("app-1"),
1314+
"include": .string("app,submittedByActor"),
1315+
"item_limit": .int(3)
1316+
]
1317+
1318+
let firstPageTransport = TestHTTPTransport(responses: [
1319+
.init(statusCode: 200, body: reviewSubmissionsListBody(nextURL: next, limit: 25))
1320+
])
1321+
let firstPageWorker = try await makeReviewSubmissionsWorker(transport: firstPageTransport)
1322+
let firstPage = try await firstPageWorker.handleTool(.init(name: "review_submissions_list", arguments: arguments))
1323+
#expect(firstPage.isError != true)
1324+
let sentQuery = try reviewSubmissionQuery(try #require(await firstPageTransport.recordedRequests().first))
1325+
#expect(sentQuery["include"] == "app,submittedByActor")
1326+
#expect(sentQuery["limit[items]"] == nil)
1327+
#expect(try reviewSubmissionObject(firstPage.structuredContent)["next_url"] == .string(next))
1328+
1329+
let continuationTransport = TestHTTPTransport(responses: [
1330+
.init(statusCode: 200, body: #"{"data":[],"links":{"self":"https://api.example.test/v1/reviewSubmissions"}}"#)
1331+
])
1332+
let continuationWorker = try await makeReviewSubmissionsWorker(transport: continuationTransport)
1333+
var continuationArguments = arguments
1334+
continuationArguments["next_url"] = .string(next)
1335+
let continuation = try await continuationWorker.handleTool(.init(
1336+
name: "review_submissions_list",
1337+
arguments: continuationArguments
1338+
))
1339+
#expect(continuation.isError != true)
1340+
#expect(try reviewSubmissionQuery(try #require(await continuationTransport.recordedRequests().first)) == query)
1341+
1342+
var missingItemLimit = reviewSubmissionListQuery(appID: "app-1", includes: "app,items", itemLimit: nil)
1343+
missingItemLimit["cursor"] = "page-2"
1344+
try await expectRejectedReviewSubmissionContinuation(
1345+
tool: "review_submissions_list",
1346+
arguments: ["app_id": .string("app-1"), "include": .string("app,items"), "item_limit": .int(3)],
1347+
path: "/v1/reviewSubmissions",
1348+
query: missingItemLimit
1349+
)
1350+
1351+
let getTransport = TestHTTPTransport(responses: [
1352+
.init(statusCode: 200, body: reviewSubmissionBody(id: "sub-1"))
1353+
])
1354+
let getWorker = try await makeReviewSubmissionsWorker(transport: getTransport)
1355+
let get = try await getWorker.handleTool(.init(
1356+
name: "review_submissions_get",
1357+
arguments: ["submission_id": .string("sub-1"), "include": .string("app"), "item_limit": .int(7)]
1358+
))
1359+
#expect(get.isError != true)
1360+
let getQuery = try reviewSubmissionQuery(try #require(await getTransport.recordedRequests().first))
1361+
#expect(getQuery["include"] == "app")
1362+
#expect(getQuery["limit[items]"] == nil)
1363+
}
1364+
13071365
@Test("continuation links still reject token drift inside set-valued query values")
13081366
func multiValueContinuationStillRejectsTokenDrift() async throws {
13091367
let baseQuery = reviewSubmissionListQuery(appID: "app-1")
@@ -2226,7 +2284,7 @@ private func reviewSubmissionListQuery(
22262284
states: String? = nil,
22272285
platforms: String? = nil,
22282286
includes: String = "app,items,appStoreVersionForReview,submittedByActor,lastUpdatedByActor",
2229-
itemLimit: Int = 50,
2287+
itemLimit: Int? = 50,
22302288
limit: Int = 25
22312289
) -> [String: String] {
22322290
var query: [String: String] = [
@@ -2236,10 +2294,10 @@ private func reviewSubmissionListQuery(
22362294
"fields[appStoreVersions]": reviewSubmissionAppVersionFields,
22372295
"fields[actors]": reviewSubmissionActorFields,
22382296
"include": includes,
2239-
"limit[items]": String(itemLimit),
22402297
"filter[app]": appID,
22412298
"limit": String(limit)
22422299
]
2300+
query["limit[items]"] = itemLimit.map(String.init)
22432301
query["filter[state]"] = states
22442302
query["filter[platform]"] = platforms
22452303
return query

0 commit comments

Comments
 (0)