@collage-dam/mcp-server 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.10] — 2026-05-22
11
+
12
+ ### Fixed
13
+ - **`view_category_contents` silently truncated folder listings** when
14
+ upstream paginated below the caller's `page_size`. Reported by Ross
15
+ 2026-05-19 on the "lifestyle photography" folder (38 assets total,
16
+ 27 returned, 11 unreachable; LLM treated the partial set as
17
+ complete). Root cause: the prior `next_cursor` math assumed upstream
18
+ returned `page_size` items per call (`offset + pageSize < total`)
19
+ but `digital-assets/category/view-files-with-category` paginates at
20
+ its own internal size (~25/page) regardless of `page_size`. The
21
+ inequality came out false on page 1 of small folders → `next_cursor:
22
+ null` → LLM saw "no more pages."
23
+
24
+ Fix: cursor is now a page-based opaque base64url JSON (matching the
25
+ `search_assets` cursor pattern). The handler decodes the cursor as
26
+ an upstream page number directly, fetches that page, and emits
27
+ `has_more` + `next_cursor` based on the upstream-reported `last_page`
28
+ field — which is the actual ground truth for "more pages exist."
29
+ Caller's `page_size` is now documented as advisory (upstream owns
30
+ page size).
31
+
32
+ ### Added
33
+ - `view_category_contents` response now surfaces `page`, `total_pages`,
34
+ and `has_more` alongside the existing `next_cursor` and
35
+ `total_estimate`. LLM consumers no longer have to interpret cursor
36
+ presence — `has_more: true` is the explicit signal that walking
37
+ more pages is required to enumerate the folder.
38
+
39
+ - Live contract test in `folder-tools.contract.test.ts` walks the
40
+ cursor on a multi-page BREZ folder until `next_cursor` is null and
41
+ asserts `seenIds.size === total_estimate` — i.e. the documented
42
+ contract reaches every asset, with no duplicates or gaps.
43
+
44
+ - `encodePageCursor` / `decodePageCursor` in `src/tools/search/cursor.ts`
45
+ for tools where upstream owns the page size and only the page number
46
+ needs to round-trip in the cursor. Same opaque base64url JSON shape
47
+ as the search cursor (`{v, p}` rather than `{v, p, n}`).
48
+
10
49
  ## [0.1.9] — 2026-05-22
11
50
 
12
51
  ### Fixed
@@ -35,4 +35,11 @@ export declare function budgetMaxPerPage(projection: Projection): number;
35
35
  * the projection-specific budget max.
36
36
  */
37
37
  export declare function effectivePerPage(requested: number, projection: Projection): number;
38
+ /** Encode an upstream page number as an opaque continuation cursor. */
39
+ export declare function encodePageCursor(page: number): string;
40
+ /**
41
+ * Decode a page-only cursor. Returns null on malformed input so callers
42
+ * can fall back to page 1.
43
+ */
44
+ export declare function decodePageCursor(cursor: string): number | null;
38
45
  //# sourceMappingURL=cursor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../src/tools/search/cursor.ts"],"names":[],"mappings":"AAuBA,kEAAkE;AAClE,eAAO,MAAM,cAAc,IAAI,CAAC;AAQhC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CASvE;AAkBD,eAAO,MAAM,sBAAsB;;;CAGzB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,sBAAsB,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,GACrB,MAAM,CAER"}
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../src/tools/search/cursor.ts"],"names":[],"mappings":"AAuBA,kEAAkE;AAClE,eAAO,MAAM,cAAc,IAAI,CAAC;AAQhC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CASvE;AAkBD,eAAO,MAAM,sBAAsB;;;CAGzB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,sBAAsB,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,GACrB,MAAM,CAER;AAgBD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9D"}
@@ -92,4 +92,37 @@ export function budgetMaxPerPage(projection) {
92
92
  export function effectivePerPage(requested, projection) {
93
93
  return Math.min(requested, budgetMaxPerPage(projection));
94
94
  }
95
+ // ── Page-only cursor (for tools where upstream owns the page size) ──
96
+ //
97
+ // `view_category_contents` paginates through
98
+ // `digital-assets/category/view-files-with-category`, which uses
99
+ // upstream's own internal page size (~25 in production). Our caller
100
+ // can't control that, so we don't carry `per_page` in the cursor —
101
+ // just the upstream page number. Same opaque base64url-JSON shape as
102
+ // `encodeSearchCursor` for consistency, with `n: 0` as a sentinel.
103
+ const PageCursorPayloadSchema = z.object({
104
+ v: z.literal(1),
105
+ p: z.number().int().min(1),
106
+ });
107
+ /** Encode an upstream page number as an opaque continuation cursor. */
108
+ export function encodePageCursor(page) {
109
+ const json = JSON.stringify({ v: CURSOR_VERSION, p: page });
110
+ return Buffer.from(json, 'utf-8').toString('base64url');
111
+ }
112
+ /**
113
+ * Decode a page-only cursor. Returns null on malformed input so callers
114
+ * can fall back to page 1.
115
+ */
116
+ export function decodePageCursor(cursor) {
117
+ try {
118
+ const decoded = Buffer.from(cursor, 'base64url').toString('utf-8');
119
+ const parsed = PageCursorPayloadSchema.safeParse(JSON.parse(decoded));
120
+ if (!parsed.success)
121
+ return null;
122
+ return parsed.data.p;
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ }
95
128
  //# sourceMappingURL=cursor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/tools/search/cursor.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,mEAAmE;AACnE,+DAA+D;AAC/D,uDAAuD;AACvD,EAAE;AACF,+CAA+C;AAC/C,oEAAoE;AACpE,4CAA4C;AAC5C,qEAAqE;AACrE,sEAAsE;AACtE,sCAAsC;AACtC,EAAE;AACF,gEAAgE;AAChE,uEAAuE;AACvE,qEAAqE;AACrE,sEAAsE;AACtE,kEAAkE;AAClE,6DAA6D;AAE7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACf,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAOH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,CAAC,EAAE,cAAc;QACjB,CAAC,EAAE,OAAO,CAAC,IAAI;QACf,CAAC,EAAE,OAAO,CAAC,QAAQ;KACpB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,EAAE;AACF,qEAAqE;AACrE,yEAAyE;AACzE,qEAAqE;AACrE,sDAAsD;AACtD,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,wDAAwD;AACxD,EAAE;AACF,wEAAwE;AACxE,qEAAqE;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAEvE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,GAAG;CACJ,CAAC;AAIX;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAsB;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,UAAsB;IAEtB,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/tools/search/cursor.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,mEAAmE;AACnE,+DAA+D;AAC/D,uDAAuD;AACvD,EAAE;AACF,+CAA+C;AAC/C,oEAAoE;AACpE,4CAA4C;AAC5C,qEAAqE;AACrE,sEAAsE;AACtE,sCAAsC;AACtC,EAAE;AACF,gEAAgE;AAChE,uEAAuE;AACvE,qEAAqE;AACrE,sEAAsE;AACtE,kEAAkE;AAClE,6DAA6D;AAE7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACf,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAOH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,CAAC,EAAE,cAAc;QACjB,CAAC,EAAE,OAAO,CAAC,IAAI;QACf,CAAC,EAAE,OAAO,CAAC,QAAQ;KACpB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,EAAE;AACF,qEAAqE;AACrE,yEAAyE;AACzE,qEAAqE;AACrE,sDAAsD;AACtD,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,wDAAwD;AACxD,EAAE;AACF,wEAAwE;AACxE,qEAAqE;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAEvE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,GAAG;CACJ,CAAC;AAIX;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAsB;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,UAAsB;IAEtB,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,uEAAuE;AACvE,EAAE;AACF,6CAA6C;AAC7C,iEAAiE;AACjE,oEAAoE;AACpE,mEAAmE;AACnE,qEAAqE;AACrE,mEAAmE;AAEnE,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACf,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QACjC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -99,12 +99,27 @@ export declare const ViewCategoryContentsOutputSchema: z.ZodObject<{
99
99
  tag_name: string;
100
100
  }>, "many">>;
101
101
  }, z.ZodTypeAny, "passthrough">>, "many">;
102
+ /** Opaque continuation cursor. Pass back as `cursor` for the next page. */
102
103
  next_cursor: z.ZodNullable<z.ZodString>;
104
+ /** Upstream's reported total asset count for this folder, or null when unknown. */
103
105
  total_estimate: z.ZodNullable<z.ZodNumber>;
106
+ /**
107
+ * Number of upstream pages reported by the API. Useful for callers
108
+ * that want to know how many cursor round-trips they're committing to.
109
+ */
110
+ total_pages: z.ZodNumber;
111
+ /** 1-based upstream page number returned in this response. */
112
+ page: z.ZodNumber;
113
+ /**
114
+ * True when `next_cursor !== null`. Surfaced explicitly so LLM
115
+ * consumers don't have to interpret cursor presence themselves.
116
+ */
117
+ has_more: z.ZodBoolean;
104
118
  request_id: z.ZodString;
105
119
  }, "strip", z.ZodTypeAny, {
106
120
  request_id: string;
107
121
  category_id: string | number;
122
+ page: number;
108
123
  assets: z.objectOutputType<{
109
124
  id: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
110
125
  display_file_name: z.ZodOptional<z.ZodString>;
@@ -134,9 +149,12 @@ export declare const ViewCategoryContentsOutputSchema: z.ZodObject<{
134
149
  category_path?: string | null | undefined;
135
150
  integration_type?: number | undefined;
136
151
  }[];
152
+ total_pages: number;
153
+ has_more: boolean;
137
154
  }, {
138
155
  request_id: string;
139
156
  category_id: string | number;
157
+ page: number;
140
158
  assets: z.objectInputType<{
141
159
  id: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
142
160
  display_file_name: z.ZodOptional<z.ZodString>;
@@ -166,6 +184,8 @@ export declare const ViewCategoryContentsOutputSchema: z.ZodObject<{
166
184
  category_path?: string | null | undefined;
167
185
  integration_type?: number | undefined;
168
186
  }[];
187
+ total_pages: number;
188
+ has_more: boolean;
169
189
  }>;
170
190
  export type ViewCategoryContentsOutput = z.infer<typeof ViewCategoryContentsOutputSchema>;
171
191
  export type ViewCategoryContentsClient = Pick<CollageClient, 'listSubCategories' | 'listAssetsByCategory'>;
@@ -196,6 +216,7 @@ export declare function buildViewCategoryContentsHandler(client: ViewCategoryCon
196
216
  structuredContent: {
197
217
  request_id: string;
198
218
  category_id: string | number;
219
+ page: number;
199
220
  assets: z.objectOutputType<{
200
221
  id: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
201
222
  display_file_name: z.ZodOptional<z.ZodString>;
@@ -225,6 +246,8 @@ export declare function buildViewCategoryContentsHandler(client: ViewCategoryCon
225
246
  category_path?: string | null | undefined;
226
247
  integration_type?: number | undefined;
227
248
  }[];
249
+ total_pages: number;
250
+ has_more: boolean;
228
251
  };
229
252
  isError?: undefined;
230
253
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"view-category-contents.d.ts","sourceRoot":"","sources":["../../src/tools/view-category-contents.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAalD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;EAM1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,+BAA+B,CACvC,CAAC;AAEF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAC9C,OAAO,gCAAgC,CACxC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,aAAa,EACb,mBAAmB,GAAG,sBAAsB,CAC7C,CAAC;AAEF,eAAO,MAAM,kCAAkC,QAGc,CAAC;AAE9D,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,0BAA0B,IAEpB,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2E9C"}
1
+ {"version":3,"file":"view-category-contents.d.ts","sourceRoot":"","sources":["../../src/tools/view-category-contents.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAalD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;EAM1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,+BAA+B,CACvC,CAAC;AAEF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAI3C,2EAA2E;;IAE3E,mFAAmF;;IAEnF;;;OAGG;;IAEH,8DAA8D;;IAE9D;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGH,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAC9C,OAAO,gCAAgC,CACxC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,aAAa,EACb,mBAAmB,GAAG,sBAAsB,CAC7C,CAAC;AAEF,eAAO,MAAM,kCAAkC,QAQU,CAAC;AAE1D,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,0BAA0B,IAEpB,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgF9C"}
@@ -5,11 +5,22 @@
5
5
  // - GET /digital-assets/category/view-files-with-category (paginated)
6
6
  //
7
7
  // Sub-categories are returned in full on every call (folder children are
8
- // typically small). Assets are upstream-paginated (1-based pages); we map
9
- // our base64 offset cursor onto upstream page numbers the same way
10
- // list_share_links does.
8
+ // typically small). Assets are upstream-paginated (1-based pages) at the
9
+ // upstream's own internal page size, which we cannot control the
10
+ // caller's `page_size` arg is honored only as a CAP on what we surface,
11
+ // not a guarantee the upstream will return that many. The cursor
12
+ // encodes the next *upstream* page directly, so a caller can walk to
13
+ // completion by round-tripping `next_cursor` until it's null.
14
+ //
15
+ // Previous shape (pre-0.1.10): offset-based cursor with the
16
+ // continuation math `offset + pageSize < total`. That math assumed
17
+ // upstream returned `pageSize` items per page, but upstream pages at
18
+ // ~25/page regardless of `pageSize`. Caller saw next_cursor=null when
19
+ // total=38, returned=27 — leaving 11 assets unreachable. Reported by
20
+ // Ross 2026-05-19 (lifestyle photography folder).
11
21
  import { z } from 'zod';
12
- import { buildPaginatedResponse, clampPageSize, decodeCursor, } from '../conventions/pagination.js';
22
+ import { clampPageSize } from '../conventions/pagination.js';
23
+ import { decodePageCursor, encodePageCursor, } from './search/cursor.js';
13
24
  import { CategorySchema, PaginatedAssetRowSchema, } from '../types.js';
14
25
  export const ViewCategoryContentsInputSchema = z.object({
15
26
  category_id: z.union([z.number().int().positive(), z.string().min(1)]),
@@ -22,18 +33,41 @@ export const ViewCategoryContentsOutputSchema = z.object({
22
33
  category_id: z.union([z.number(), z.string()]),
23
34
  subcategories: z.array(CategorySchema),
24
35
  assets: z.array(PaginatedAssetRowSchema),
36
+ /** Opaque continuation cursor. Pass back as `cursor` for the next page. */
25
37
  next_cursor: z.string().nullable(),
38
+ /** Upstream's reported total asset count for this folder, or null when unknown. */
26
39
  total_estimate: z.number().nullable(),
40
+ /**
41
+ * Number of upstream pages reported by the API. Useful for callers
42
+ * that want to know how many cursor round-trips they're committing to.
43
+ */
44
+ total_pages: z.number().int().nonnegative(),
45
+ /** 1-based upstream page number returned in this response. */
46
+ page: z.number().int().min(1),
47
+ /**
48
+ * True when `next_cursor !== null`. Surfaced explicitly so LLM
49
+ * consumers don't have to interpret cursor presence themselves.
50
+ */
51
+ has_more: z.boolean(),
27
52
  request_id: z.string(),
28
53
  });
29
54
  export const VIEW_CATEGORY_CONTENTS_DESCRIPTION = 'Browse one folder. Returns the folder\'s immediate sub-folders (full ' +
30
- 'list) and a cursor-paginated page of assets that live directly inside ' +
31
- 'it. Pass `category_id` from `list_categories`. Read-only.';
55
+ 'list) and a page of assets that live directly inside it. ' +
56
+ 'Pagination: upstream owns the page size (~25 assets/page). Pass back ' +
57
+ '`next_cursor` as `cursor` to fetch the next upstream page. Walk until ' +
58
+ '`has_more` is false or `next_cursor` is null. `total_estimate` is the ' +
59
+ 'upstream-reported total assets for the folder; `total_pages` is how ' +
60
+ 'many cursor round-trips you\'ll need to enumerate everything. ' +
61
+ 'Pass `category_id` from `list_categories`. Read-only.';
32
62
  export function buildViewCategoryContentsHandler(client) {
33
63
  return async (args) => {
34
- const pageSize = clampPageSize(args.page_size);
35
- const offset = args.cursor !== undefined ? decodeCursor(args.cursor) : 0;
36
- const page = Math.floor(offset / pageSize) + 1;
64
+ // `page_size` is advisory — upstream controls actual page size.
65
+ // Clamp it so a hostile or careless caller can't ask for absurd
66
+ // values via the input schema.
67
+ clampPageSize(args.page_size);
68
+ const page = args.cursor !== undefined
69
+ ? decodePageCursor(args.cursor) ?? 1
70
+ : 1;
37
71
  const subResult = await client.listSubCategories(args.category_id);
38
72
  if (!subResult.ok) {
39
73
  return {
@@ -74,13 +108,17 @@ export function buildViewCategoryContentsHandler(client) {
74
108
  const subcategories = subResult.data;
75
109
  const assets = assetResult.data.assets;
76
110
  const total = assetResult.data.total;
77
- const paged = buildPaginatedResponse(assets, offset, pageSize, total);
111
+ const lastPage = assetResult.data.lastPage;
112
+ const hasMore = page < lastPage;
78
113
  const output = {
79
114
  category_id: args.category_id,
80
115
  subcategories,
81
- assets: paged.items,
82
- next_cursor: paged.next_cursor,
83
- total_estimate: paged.total_estimate,
116
+ assets,
117
+ next_cursor: hasMore ? encodePageCursor(page + 1) : null,
118
+ total_estimate: total ?? null,
119
+ total_pages: lastPage,
120
+ page,
121
+ has_more: hasMore,
84
122
  request_id: assetResult.request_id,
85
123
  };
86
124
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"view-category-contents.js","sourceRoot":"","sources":["../../src/tools/view-category-contents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view-category-contents.js","sourceRoot":"","sources":["../../src/tools/view-category-contents.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@collage-dam/mcp-server",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Model Context Protocol (MCP) server for the Collage Digital Asset Management platform. Exposes Collage workspaces — assets, folders, collections, share links, audit prompts — to AI clients (Claude Desktop, Cursor, ChatGPT) through a typed tool surface.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",