@withpica/mcp-sdk 3.22.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 +53 -0
- package/dist/index.d.ts +210 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +132 -25
- 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
|
/**
|
|
@@ -559,6 +569,25 @@ class PeopleResource extends BaseResource {
|
|
|
559
569
|
async bulkDelete(ids) {
|
|
560
570
|
return this.request("POST", "/admin/people/bulk-delete", { ids });
|
|
561
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* GET /admin/people/{id}/split-identity — what giving this person a new
|
|
574
|
+
* global identity would touch: who shares their current one, unattributed
|
|
575
|
+
* split rows naming it, open custody legs naming it, and which of their
|
|
576
|
+
* own identifiers equal it. Read-only.
|
|
577
|
+
*/
|
|
578
|
+
async splitIdentityPreview(id) {
|
|
579
|
+
return this.request("GET", `/admin/people/${encodeURIComponent(id)}/split-identity`);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* POST /admin/people/{id}/split-identity — give this person a NEW global
|
|
583
|
+
* identity. Body: `{ move_identifiers?, relink_split_ids?,
|
|
584
|
+
* clear_person_identifiers?, undo_external_ids? }`; every list defaults to
|
|
585
|
+
* empty (nothing moves, nothing is relinked). A refusal is a 4xx carrying
|
|
586
|
+
* a `SPLIT_*` code.
|
|
587
|
+
*/
|
|
588
|
+
async splitIdentity(id, body) {
|
|
589
|
+
return this.request("POST", `/admin/people/${encodeURIComponent(id)}/split-identity`, body);
|
|
590
|
+
}
|
|
562
591
|
/**
|
|
563
592
|
* List a person's artist projects — per-stage-name Spotify/YouTube
|
|
564
593
|
* identity + enrichment status (ADR-289 Wave B2 item 4, ports the
|
|
@@ -830,6 +859,7 @@ class CreditsResource extends BaseResource {
|
|
|
830
859
|
work_id: env.work_id ?? workId,
|
|
831
860
|
credit: env.data,
|
|
832
861
|
allocation: env.allocation,
|
|
862
|
+
...(env.person_change && { person_change: env.person_change }),
|
|
833
863
|
};
|
|
834
864
|
}
|
|
835
865
|
/**
|
|
@@ -1208,8 +1238,13 @@ class SyncPlacementsResource extends BaseResource {
|
|
|
1208
1238
|
: "";
|
|
1209
1239
|
await this.request("DELETE", `/admin/sync-placements/${id}${query}`);
|
|
1210
1240
|
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Attach a citation source (POST /api/admin/sync-placements/[id]/cite).
|
|
1243
|
+
* This used to post to `/sources`, which has never existed, so every
|
|
1244
|
+
* pica_sync_placements_cite call answered 404.
|
|
1245
|
+
*/
|
|
1211
1246
|
async addSource(id, source) {
|
|
1212
|
-
return this.request("POST", `/admin/sync-placements/${id}/
|
|
1247
|
+
return this.request("POST", `/admin/sync-placements/${id}/cite`, source);
|
|
1213
1248
|
}
|
|
1214
1249
|
}
|
|
1215
1250
|
class WorkspaceResource extends BaseResource {
|
|
@@ -1262,9 +1297,24 @@ class AuditResource extends BaseResource {
|
|
|
1262
1297
|
}
|
|
1263
1298
|
}
|
|
1264
1299
|
class MemoryResource extends BaseResource {
|
|
1300
|
+
/**
|
|
1301
|
+
* The FIRST page of memories only (newest first, the route's default page
|
|
1302
|
+
* size) — since 2026-10-02 the route pages. Kept for existing callers; use
|
|
1303
|
+
* `listPage` to reach the rest.
|
|
1304
|
+
*/
|
|
1265
1305
|
async list() {
|
|
1266
1306
|
return this.request("GET", "/admin/memory");
|
|
1267
1307
|
}
|
|
1308
|
+
/** One page of memories, most recently changed first, with the total. */
|
|
1309
|
+
async listPage(params) {
|
|
1310
|
+
const query = new URLSearchParams();
|
|
1311
|
+
if (params?.limit)
|
|
1312
|
+
query.set("limit", String(params.limit));
|
|
1313
|
+
if (params?.offset)
|
|
1314
|
+
query.set("offset", String(params.offset));
|
|
1315
|
+
const qs = query.toString();
|
|
1316
|
+
return this.requestPaginated("GET", `/admin/memory${qs ? `?${qs}` : ""}`);
|
|
1317
|
+
}
|
|
1268
1318
|
async search(query) {
|
|
1269
1319
|
return this.request("GET", `/admin/memory/search?q=${encodeURIComponent(query)}`);
|
|
1270
1320
|
}
|
|
@@ -2508,7 +2558,7 @@ class ExportResource extends BaseResource {
|
|
|
2508
2558
|
// request() unconditionally calls response.json(), which throws parsing
|
|
2509
2559
|
// a CSV body — every pro/distributor/nro call 500'd with INTERNAL_ERROR
|
|
2510
2560
|
// (pica_export_catalog_csv: 5/5 prod errors, ops audit 2026-07-04).
|
|
2511
|
-
// requestText()
|
|
2561
|
+
// requestText() serves ImportResource.getTemplate() too, for
|
|
2512
2562
|
// the same response shape.
|
|
2513
2563
|
if (params.format === "sync-pdf") {
|
|
2514
2564
|
return this.request("GET", path);
|
|
@@ -2714,8 +2764,12 @@ class AssetsResource extends BaseResource {
|
|
|
2714
2764
|
async exportAll() {
|
|
2715
2765
|
return this.request("GET", "/admin/assets/export");
|
|
2716
2766
|
}
|
|
2767
|
+
/**
|
|
2768
|
+
* AI-assisted price estimate for the asset's make/model. The route is
|
|
2769
|
+
* POST-only (it spends an AI call each time); this sent GET and got 405.
|
|
2770
|
+
*/
|
|
2717
2771
|
async marketCheck(id) {
|
|
2718
|
-
return this.request("
|
|
2772
|
+
return this.request("POST", `/admin/assets/${id}/market-check`);
|
|
2719
2773
|
}
|
|
2720
2774
|
// --- Production-asset links (ADR-156 operating principle #3) ---
|
|
2721
2775
|
// Typed edges between physical_assets and works/recordings, with
|
|
@@ -2803,14 +2857,28 @@ class ImportResource extends BaseResource {
|
|
|
2803
2857
|
});
|
|
2804
2858
|
}
|
|
2805
2859
|
async getTemplate(domain) {
|
|
2806
|
-
//
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
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)}`);
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* Emails that reached the organisation's forwarding address, newest first,
|
|
2867
|
+
* with what PICA did with each (stored on arrival, waiting for review,
|
|
2868
|
+
* dismissed, failed). Owners and admins only.
|
|
2869
|
+
*/
|
|
2870
|
+
async listForwardedEmails(options) {
|
|
2871
|
+
const params = new URLSearchParams();
|
|
2872
|
+
if (options?.limit !== undefined)
|
|
2873
|
+
params.set("limit", String(options.limit));
|
|
2874
|
+
if (options?.offset !== undefined)
|
|
2875
|
+
params.set("offset", String(options.offset));
|
|
2876
|
+
const qs = params.toString();
|
|
2877
|
+
return this.request("GET", `/admin/email-intake/forwarded${qs ? `?${qs}` : ""}`);
|
|
2878
|
+
}
|
|
2879
|
+
/** One forwarded email, with its text body when PICA kept one. */
|
|
2880
|
+
async getForwardedEmail(id) {
|
|
2881
|
+
return this.request("GET", `/admin/email-intake/forwarded/${encodeURIComponent(id)}`);
|
|
2814
2882
|
}
|
|
2815
2883
|
}
|
|
2816
2884
|
// --- Documents Resource ---
|
|
@@ -3349,6 +3417,22 @@ class RecordingSplitsResource extends BaseResource {
|
|
|
3349
3417
|
async delete(recordingId, splitId) {
|
|
3350
3418
|
return this.request("DELETE", `/admin/recordings/${recordingId}/splits/${splitId}`);
|
|
3351
3419
|
}
|
|
3420
|
+
/**
|
|
3421
|
+
* Edit a split and keep the route's whole envelope: `data` is the row, and
|
|
3422
|
+
* `previous` / `changed_fields` / `verification_cleared` sit beside it —
|
|
3423
|
+
* `request()` would drop them. verification_cleared is true when the edit
|
|
3424
|
+
* cleared the holder's confirmation.
|
|
3425
|
+
*/
|
|
3426
|
+
async edit(recordingId, splitId, data) {
|
|
3427
|
+
return this.requestWithEnvelope("PATCH", `/admin/recordings/${recordingId}/splits/${splitId}`, data);
|
|
3428
|
+
}
|
|
3429
|
+
/**
|
|
3430
|
+
* End a split: sets end_date (default today) so it stops counting toward
|
|
3431
|
+
* the 100% total; the row is kept. Same envelope as `edit`.
|
|
3432
|
+
*/
|
|
3433
|
+
async end(recordingId, splitId, data = {}) {
|
|
3434
|
+
return this.requestWithEnvelope("POST", `/admin/recordings/${recordingId}/splits/${splitId}/end`, data);
|
|
3435
|
+
}
|
|
3352
3436
|
async verify(recordingId, splitId) {
|
|
3353
3437
|
return this.request("POST", `/admin/recordings/${recordingId}/splits/${splitId}/verify`);
|
|
3354
3438
|
}
|
|
@@ -3430,6 +3514,36 @@ class PartyClaimsResource extends BaseResource {
|
|
|
3430
3514
|
return (res?.data ?? []);
|
|
3431
3515
|
}
|
|
3432
3516
|
}
|
|
3517
|
+
/**
|
|
3518
|
+
* Claim your profile: invite people to claim their profile (a preview
|
|
3519
|
+
* unless `confirm`), and read where people stand.
|
|
3520
|
+
*/
|
|
3521
|
+
class ProfileClaimsResource extends BaseResource {
|
|
3522
|
+
async invite(params) {
|
|
3523
|
+
const res = await this.request("POST", "/admin/people/claim-invites", params);
|
|
3524
|
+
if (res && typeof res === "object" && "data" in res && res.data) {
|
|
3525
|
+
return res.data;
|
|
3526
|
+
}
|
|
3527
|
+
return (res ?? null);
|
|
3528
|
+
}
|
|
3529
|
+
async status(params) {
|
|
3530
|
+
const q = new URLSearchParams();
|
|
3531
|
+
for (const id of params?.person_ids ?? [])
|
|
3532
|
+
q.append("person_id", id);
|
|
3533
|
+
if (params?.state)
|
|
3534
|
+
q.set("state", params.state);
|
|
3535
|
+
if (params?.limit)
|
|
3536
|
+
q.set("limit", String(params.limit));
|
|
3537
|
+
const qs = q.toString();
|
|
3538
|
+
const res = await this.request("GET", `/admin/profile-claims${qs ? `?${qs}` : ""}`);
|
|
3539
|
+
const body = res;
|
|
3540
|
+
const inner = body?.people ? body : body?.data;
|
|
3541
|
+
return {
|
|
3542
|
+
people: inner?.people ?? [],
|
|
3543
|
+
truncated: inner?.truncated === true,
|
|
3544
|
+
};
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3433
3547
|
class ReleasesResource extends BaseResource {
|
|
3434
3548
|
async list(params) {
|
|
3435
3549
|
const parts = [];
|
|
@@ -4199,6 +4313,7 @@ export class PicaClient {
|
|
|
4199
4313
|
publishers;
|
|
4200
4314
|
labels;
|
|
4201
4315
|
partyClaims;
|
|
4316
|
+
profileClaims;
|
|
4202
4317
|
agreementTemplates;
|
|
4203
4318
|
producerAgreements;
|
|
4204
4319
|
workForHire;
|
|
@@ -4237,24 +4352,15 @@ export class PicaClient {
|
|
|
4237
4352
|
// Registration Integrity Floor (spec 2026-07-09, Task 8).
|
|
4238
4353
|
integrity;
|
|
4239
4354
|
async catalogStats(params) {
|
|
4240
|
-
const baseUrl = this.works["baseUrl"];
|
|
4241
|
-
const apiKey = this.works["apiKey"];
|
|
4242
4355
|
const qp = new URLSearchParams();
|
|
4243
4356
|
if (params?.entity)
|
|
4244
4357
|
qp.set("entity", params.entity);
|
|
4245
4358
|
if (params?.group_by)
|
|
4246
4359
|
qp.set("group_by", params.group_by);
|
|
4247
4360
|
const suffix = qp.toString() ? `?${qp.toString()}` : "";
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
"Content-Type": "application/json",
|
|
4252
|
-
},
|
|
4253
|
-
});
|
|
4254
|
-
if (!res.ok)
|
|
4255
|
-
throw new Error(`Catalog stats failed: ${res.status}`);
|
|
4256
|
-
const json = (await res.json());
|
|
4257
|
-
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}`);
|
|
4258
4364
|
}
|
|
4259
4365
|
constructor(config) {
|
|
4260
4366
|
if (!config.apiKey) {
|
|
@@ -4317,6 +4423,7 @@ export class PicaClient {
|
|
|
4317
4423
|
this.publishers = new PublishersResource(baseUrl, config.apiKey, debug);
|
|
4318
4424
|
this.labels = new LabelsResource(baseUrl, config.apiKey, debug);
|
|
4319
4425
|
this.partyClaims = new PartyClaimsResource(baseUrl, config.apiKey, debug);
|
|
4426
|
+
this.profileClaims = new ProfileClaimsResource(baseUrl, config.apiKey, debug);
|
|
4320
4427
|
this.agreementTemplates = new AgreementTemplatesResource(baseUrl, config.apiKey, debug);
|
|
4321
4428
|
this.producerAgreements = new ProducerAgreementsResource(baseUrl, config.apiKey, debug);
|
|
4322
4429
|
this.workForHire = new WorkForHireResource(baseUrl, config.apiKey, debug);
|