@withpica/mcp-sdk 3.7.1 → 3.9.0

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/dist/index.js CHANGED
@@ -298,7 +298,15 @@ class BaseResource {
298
298
  // `count` / items.length so untouched callers keep their prior behaviour.
299
299
  const total = json.pagination?.total ?? json.total ?? json.count ?? items.length;
300
300
  const hasMore = json.pagination?.hasMore ?? json.hasMore ?? total > items.length;
301
- return { data: items, total, hasMore };
301
+ // Carry the server's zero-result guidance through — silently dropping it
302
+ // here left agents staring at a bare empty list (ops_issue 689a8e9a).
303
+ const recovery_hint = typeof json.recovery_hint === "string" ? json.recovery_hint : undefined;
304
+ return {
305
+ data: items,
306
+ total,
307
+ hasMore,
308
+ ...(recovery_hint !== undefined && { recovery_hint }),
309
+ };
302
310
  }
303
311
  }
304
312
  class WorksResource extends BaseResource {
@@ -432,8 +440,12 @@ class WorksResource extends BaseResource {
432
440
  * release-context, not physical-asset provenance.
433
441
  */
434
442
  async listReleases(workId) {
443
+ // request() already unwraps the route's { success, data } envelope to the
444
+ // array, so reading `.data` again yielded undefined → `[]` on every call —
445
+ // the work→releases view looked empty even when the work was attached.
446
+ // Mirrors the recordings-side fix (cea22b5e); FIX_LOG 2026-07-16 sweep.
435
447
  const result = await this.request("GET", `/admin/works/${workId}/releases`);
436
- return result?.data || [];
448
+ return Array.isArray(result) ? result : (result?.data ?? []);
437
449
  }
438
450
  /**
439
451
  * Assemble the full song dossier for one work — everything PICA holds about
@@ -811,6 +823,33 @@ class AudioFilesResource extends BaseResource {
811
823
  async get(id) {
812
824
  return this.request("GET", `/admin/audio-files/${id}`);
813
825
  }
826
+ /**
827
+ * Attach ALL orphan audio files whose suggested match is ISRC-high and
828
+ * non-ambiguous, in one call. Pairings are recomputed server-side at
829
+ * execution time — nothing is sent, so stale suggestions can't be applied
830
+ * and dismissed pairs stay excluded; `possible` (title-tier) matches are
831
+ * never bulk-attached. Wraps POST /admin/audio-files/bulk-assign-suggested
832
+ * (the route behind the /inspect "attach all high" strip; agent feedback
833
+ * 20f592ff asked for the same action tool-side).
834
+ *
835
+ * NB the route responds `{ success, result }` with no `data` key, so
836
+ * `request()`'s `.data || whole` unwrap yields the full envelope — read
837
+ * `.result` here rather than typing against a payload that isn't there
838
+ * (the ADR-265 double-unwrap lesson).
839
+ */
840
+ async bulkAssignSuggested(params) {
841
+ const res = await this.request("POST", "/admin/audio-files/bulk-assign-suggested", params?.dry_run ? { dry_run: true } : undefined);
842
+ return res.result;
843
+ }
844
+ /**
845
+ * Reject a suggested (audio_file → recording) match so the pair never
846
+ * resurfaces in suggestions or bulk attach. The file stays an orphan —
847
+ * only THIS pairing is suppressed. Repeat dismissals are no-op successes
848
+ * server-side. Wraps POST /admin/audio-files/{id}/dismiss-match.
849
+ */
850
+ async dismissMatch(id, recordingId) {
851
+ await this.request("POST", `/admin/audio-files/${id}/dismiss-match`, { recording_id: recordingId });
852
+ }
814
853
  async update(id, body) {
815
854
  return this.request("PATCH", `/admin/audio-files/${id}`, body);
816
855
  }
@@ -1235,8 +1274,20 @@ class RecordingsResource extends BaseResource {
1235
1274
  if (prefix.length < 5)
1236
1275
  return null;
1237
1276
  try {
1238
- const result = await this.request("GET", `/admin/isrc/prefix/${prefix}`);
1239
- return result.data ?? null;
1277
+ // request() unwraps {success,data} to the registry ROW on a hit, but
1278
+ // on a MISS the route returns data:null (falsy) and `data.data || data`
1279
+ // falls back to the FULL envelope — so discriminate the shape instead
1280
+ // of retyping (FIX_LOG 2026-07-16 double-unwrap sweep; same guard as
1281
+ // AccessSimulateResource.simulate). Reading `.data` unconditionally
1282
+ // made every hit return null, silently killing prefix_intel.
1283
+ const result = (await this.request("GET", `/admin/isrc/prefix/${prefix}`));
1284
+ if (result &&
1285
+ typeof result === "object" &&
1286
+ "success" in result &&
1287
+ "data" in result) {
1288
+ return (result.data ?? null);
1289
+ }
1290
+ return result ?? null;
1240
1291
  }
1241
1292
  catch {
1242
1293
  return null;
@@ -2713,6 +2764,12 @@ class CollaborationsResource extends BaseResource {
2713
2764
  * Cross-org discovery surface that lets the sender's agent invite a
2714
2765
  * collaborator by `@handle` without ever learning the recipient's
2715
2766
  * email (resolution happens server-side).
2767
+ *
2768
+ * Typed against the UNWRAPPED reality (ADR-265 lesson): BaseResource.request
2769
+ * auto-unwraps the `{success,data}` envelope, so methods see `data` itself.
2770
+ * Declaring `T = { data: X }` and returning `res.data` double-unwraps and
2771
+ * resolves `undefined` on the live wire (FIX_LOG 2026-07-16 — the
2772
+ * get_my_profile "reading 'public_handle'" crash).
2716
2773
  */
2717
2774
  class UsersResource extends BaseResource {
2718
2775
  /**
@@ -2723,27 +2780,26 @@ class UsersResource extends BaseResource {
2723
2780
  * proceed with an invite.
2724
2781
  */
2725
2782
  async findByHandle(handle) {
2726
- const res = await this.request("POST", "/admin/users/find-by-handle", { handle });
2727
- return res.data;
2783
+ return this.request("POST", "/admin/users/find-by-handle", { handle });
2728
2784
  }
2729
2785
  async setHandle(handle) {
2730
- const res = await this.request("POST", "/admin/users/handle", { handle });
2731
- return res.data;
2786
+ return this.request("POST", "/admin/users/handle", {
2787
+ handle,
2788
+ });
2732
2789
  }
2733
2790
  async clearHandle() {
2734
2791
  await this.request("DELETE", "/admin/users/handle");
2735
2792
  }
2736
2793
  async getMyProfile() {
2737
- const res = await this.request("GET", "/admin/users/profile");
2738
- return res.data;
2794
+ return this.request("GET", "/admin/users/profile");
2739
2795
  }
2740
2796
  async setBio(bio) {
2741
- const res = await this.request("POST", "/admin/users/bio", { bio });
2742
- return res.data;
2797
+ return this.request("POST", "/admin/users/bio", {
2798
+ bio,
2799
+ });
2743
2800
  }
2744
2801
  async setAvatarUrl(avatarUrl) {
2745
- const res = await this.request("POST", "/admin/users/avatar", { avatar_url: avatarUrl });
2746
- return res.data;
2802
+ return this.request("POST", "/admin/users/avatar", { avatar_url: avatarUrl });
2747
2803
  }
2748
2804
  /**
2749
2805
  * Copy `people.biography` into `user_profiles.bio`. Returns the new
@@ -2753,8 +2809,7 @@ class UsersResource extends BaseResource {
2753
2809
  * `overwrite: true`.
2754
2810
  */
2755
2811
  async importBioFromPerson(params) {
2756
- const res = await this.request("POST", "/admin/users/import-bio-from-person", params ?? {});
2757
- return res.data;
2812
+ return this.request("POST", "/admin/users/import-bio-from-person", params ?? {});
2758
2813
  }
2759
2814
  }
2760
2815
  class DirectoryResource extends BaseResource {