@withpica/mcp-sdk 3.23.0 → 3.23.1
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 +13 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -245,13 +245,20 @@ class BaseResource {
|
|
|
245
245
|
* for routes that stream a generated file (e.g. `text/csv`) rather than
|
|
246
246
|
* `{success, data}`. `request()` unconditionally calls `response.json()`,
|
|
247
247
|
* which throws a SyntaxError parsing CSV text; every such route needs this
|
|
248
|
-
* method instead (
|
|
249
|
-
* precedent this generalises).
|
|
248
|
+
* method instead (`ImportResource.getTemplate()`, `ExportResource.catalogCsv()`).
|
|
250
249
|
*
|
|
251
250
|
* `fetchWithRetry` already throws `ApiError` for non-2xx responses (reading
|
|
252
251
|
* the body as text), so reaching here means `response.ok`. Still guard
|
|
253
252
|
* against a 2xx JSON error envelope (`{success:false}`) via content-type,
|
|
254
253
|
* since a route can commit to status 200 before a later validation step.
|
|
254
|
+
*
|
|
255
|
+
* An HTML page is refused. Neither route that uses this
|
|
256
|
+
* (`/admin/import/template`, `/admin/works/export` CSV formats) answers
|
|
257
|
+
* `text/html`, so one means something in front of PICA answered (on a
|
|
258
|
+
* protected Vercel preview, a missing bypass header ends on Vercel's
|
|
259
|
+
* login page with status 200). Returning that page as the file read as a
|
|
260
|
+
* success (2026-10-06, pica_import_template). Locked by
|
|
261
|
+
* `mcp-server/src/__tests__/sdk-request-text-and-bare-fetch.test.ts`.
|
|
255
262
|
*/
|
|
256
263
|
async requestText(method, path) {
|
|
257
264
|
const url = `${this.baseUrl}${path}`;
|
|
@@ -275,6 +282,9 @@ class BaseResource {
|
|
|
275
282
|
}
|
|
276
283
|
return typeof data === "string" ? data : JSON.stringify(data);
|
|
277
284
|
}
|
|
285
|
+
if (contentType.includes("text/html")) {
|
|
286
|
+
throw new ApiError(`Expected a file from ${path} but got an HTML page; the request did not reach PICA`, response.status, false);
|
|
287
|
+
}
|
|
278
288
|
return response.text();
|
|
279
289
|
}
|
|
280
290
|
/**
|
|
@@ -2548,7 +2558,7 @@ class ExportResource extends BaseResource {
|
|
|
2548
2558
|
// request() unconditionally calls response.json(), which throws parsing
|
|
2549
2559
|
// a CSV body — every pro/distributor/nro call 500'd with INTERNAL_ERROR
|
|
2550
2560
|
// (pica_export_catalog_csv: 5/5 prod errors, ops audit 2026-07-04).
|
|
2551
|
-
// requestText()
|
|
2561
|
+
// requestText() serves ImportResource.getTemplate() too, for
|
|
2552
2562
|
// the same response shape.
|
|
2553
2563
|
if (params.format === "sync-pdf") {
|
|
2554
2564
|
return this.request("GET", path);
|
|
@@ -2847,14 +2857,10 @@ class ImportResource extends BaseResource {
|
|
|
2847
2857
|
});
|
|
2848
2858
|
}
|
|
2849
2859
|
async getTemplate(domain) {
|
|
2850
|
-
//
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
});
|
|
2855
|
-
if (!response.ok)
|
|
2856
|
-
throw new Error(`Template fetch failed: ${response.status}`);
|
|
2857
|
-
return response.text();
|
|
2860
|
+
// The route answers text/csv, so requestText(), not request(). It was a
|
|
2861
|
+
// bare fetch() with no preview-protection bypass header, so on a Vercel
|
|
2862
|
+
// preview it never reached the route (2026-10-06).
|
|
2863
|
+
return this.requestText("GET", `/admin/import/template?domain=${encodeURIComponent(domain)}`);
|
|
2858
2864
|
}
|
|
2859
2865
|
/**
|
|
2860
2866
|
* Emails that reached the organisation's forwarding address, newest first,
|
|
@@ -4346,24 +4352,15 @@ export class PicaClient {
|
|
|
4346
4352
|
// Registration Integrity Floor (spec 2026-07-09, Task 8).
|
|
4347
4353
|
integrity;
|
|
4348
4354
|
async catalogStats(params) {
|
|
4349
|
-
const baseUrl = this.works["baseUrl"];
|
|
4350
|
-
const apiKey = this.works["apiKey"];
|
|
4351
4355
|
const qp = new URLSearchParams();
|
|
4352
4356
|
if (params?.entity)
|
|
4353
4357
|
qp.set("entity", params.entity);
|
|
4354
4358
|
if (params?.group_by)
|
|
4355
4359
|
qp.set("group_by", params.group_by);
|
|
4356
4360
|
const suffix = qp.toString() ? `?${qp.toString()}` : "";
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
"Content-Type": "application/json",
|
|
4361
|
-
},
|
|
4362
|
-
});
|
|
4363
|
-
if (!res.ok)
|
|
4364
|
-
throw new Error(`Catalog stats failed: ${res.status}`);
|
|
4365
|
-
const json = (await res.json());
|
|
4366
|
-
return (json.data || json);
|
|
4361
|
+
// Through the shared request path (bypass header, timeout, retry, typed
|
|
4362
|
+
// errors) rather than a bare fetch() that skipped all four (2026-10-06).
|
|
4363
|
+
return this.works["request"]("GET", `/admin/catalog/stats${suffix}`);
|
|
4367
4364
|
}
|
|
4368
4365
|
constructor(config) {
|
|
4369
4366
|
if (!config.apiKey) {
|