@withpica/mcp-sdk 1.39.0 → 1.40.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/CHANGELOG.md CHANGED
@@ -11,6 +11,78 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.40.0] - 2026-06-12
15
+
16
+ ADR-264 WS2 verify-before-assert + WS4 bulk recording credits + ADR-268 WS1
17
+ party claims / WS2 label identifiers / WS5 ownership transfer types. Removes
18
+ the never-functional `TeamResource`.
19
+
20
+ ### Changed
21
+
22
+ - **`RecordingCustodyResource.assign`** now accepts the ownership transfer
23
+ types `assignment` and `full_transfer` (ADR-268 WS5, the ADR-262
24
+ fast-follow): the `transfer_type` union widens from
25
+ `delivery|license|admin_grant` to all five types. Ownership legs also move
26
+ the recording's master ownership splits append-only inside the same
27
+ transaction (the transferor's active era closes, the transferee's opens;
28
+ the credit layer never moves). Ambiguous or over-share partial transfers
29
+ are refused as client-recoverable 400s
30
+ (`PARTIAL_TRANSFER_AMBIGUOUS` / `INSUFFICIENT_TRANSFEROR_SHARE` /
31
+ `INVALID_PERCENTAGE`).
32
+ - **`LabelsResource.create`** accepts an optional `identifiers` object
33
+ (`musicbrainz_label_id` / `isni` / `label_code` / `discogs_label_id` /
34
+ `wikidata_id` / `deezer_label_id`) — ADR-268 WS2 identifier-first dedup;
35
+ `LabelOrganisation` gains `global_label_id` (the bridge to the resolved
36
+ global label identity).
37
+
38
+ ### Removed
39
+
40
+ - **`TeamResource` (`client.team.*` — list/get/invite/update/remove)** —
41
+ existed solely to back the `pica_team_*` MCP tools, which were removed
42
+ 2026-06-11 (founder decision). `/api/admin/team/*` is session-only by
43
+ design (ADR-248: team membership is human-only), so every bearer SDK call
44
+ through this resource always received a hard 401; removal is honesty, not
45
+ a regression. Team membership is managed by humans in `/settings`.
46
+
47
+ ### Added
48
+
49
+ - **`PartyClaimsResource` (`client.partyClaims.*`)** — ADR-268 WS1.
50
+ `invite({target_organisation_id, invited_email, release_id?, recording_id?})`
51
+ POSTs `/admin/party-claims` (send a claim invite for an unclaimed party
52
+ org; resolves the unwrapped `{invite, disputed, claim_url}` object) and
53
+ `list({target_organisation_id?, status?, limit?})` GETs the calling org's
54
+ sent claims. Backs the new `pica_party_claim_invite` /
55
+ `pica_party_claim_status` tools.
56
+
57
+ - **`RecordingCreditsResource.bulkUpdate(input)`** — ADR-264 WS4 prerequisite.
58
+ POST `/admin/recording-credits/bulk` with exactly one of `release_id`
59
+ (targets every recording on the release's tracklist) / `recording_ids`,
60
+ plus the `credits[]` set applied to every target recording and optional
61
+ `dry_run`. Returns per-row `created` / `skipped_existing` (or
62
+ `would_create` / `would_skip_existing` on dry-run) results plus summary
63
+ counts. Typed against the unwrapped route payload.
64
+ - **`WorksResource.verifyRegistrations(input)`** — ADR-264 WS2 verify-before-assert.
65
+ POST `/admin/works/verify-registrations` with `work_ids` (1-25), `mode`
66
+ (`"check"` runs an MLC lookup per work; `"confirm_unregistered"` records the
67
+ user's own assertion, optional `society_code`). Returns per-work results
68
+ (`already_registered` / `found_registered` / `verified_absent` /
69
+ `lookup_failed` / `insufficient_identifiers` / `confirmed` / `not_found`)
70
+ plus summary counts. Lookup errors and unmatchable works write nothing.
71
+
72
+ ## [1.39.1] - 2026-06-10
73
+
74
+ ADR-264 WS1.3 type widening — releases/recordings dedup + merge coverage.
75
+
76
+ ### Changed
77
+
78
+ - **`DuplicatesResource.findDuplicates`** param widened from
79
+ `"works" | "people"` → `"works" | "people" | "releases" | "recordings"` to
80
+ match the backend enum (ISRC-keyed recording groups, UPC-keyed release groups).
81
+ - **`DuplicatesResource.merge`** `entityType` param widened from
82
+ `"work" | "person"` → `"work" | "person" | "recording" | "release"` so
83
+ callers can merge recording/release duplicates via the same SDK method.
84
+ Type-only widening; no new HTTP endpoints or methods.
85
+
14
86
  ## [1.39.0] - 2026-06-10
15
87
 
16
88
  ADR-265 release parties and invites — label/distributor org creation + recording/release-grain invite targets.
@@ -29,6 +101,21 @@ ADR-265 release parties and invites — label/distributor org creation + recordi
29
101
  enforces the invariant. The `CollaboratorInvite` row type's `work_id` is now
30
102
  nullable, with optional `recording_id` / `release_id`.
31
103
 
104
+ ### Fixed
105
+
106
+ - **`labels.list` double-unwrap** — `BaseResource.request()` auto-unwraps the
107
+ route's `{ success, data }` envelope, but `labels.list` read `.data` a second
108
+ time off the already-unwrapped array, so it returned `[]` on every call —
109
+ `pica_labels_query` had returned "Found 0 labels" regardless of catalog
110
+ state since 1.34.x. Now tolerates both the raw array and the envelope
111
+ (same class and fix pattern as the 1.37.1 `listEnquiries` fix).
112
+ - **`labels.create` envelope handling** — same double-read: on the live wire
113
+ `request()` returns the unwrapped label object, so the old
114
+ `{ data, already_existed }` return type was never populated. `create` now
115
+ resolves the label object directly (or `null`), with `already_existed`
116
+ carried inside it (the route duplicates it into `data` so the dedup signal
117
+ survives the unwrap).
118
+
32
119
  ## [1.38.0] - 2026-06-10
33
120
 
34
121
  ADR-264 WS1 trust floor support.
package/dist/index.d.ts CHANGED
@@ -730,6 +730,30 @@ declare class WorksResource extends BaseResource {
730
730
  certificate_id?: string;
731
731
  } | null;
732
732
  }>;
733
+ /**
734
+ * ADR-264 WS2 — verify (or confirm) registration status for up to 25
735
+ * works. mode "check" runs an MLC lookup per work and records what the
736
+ * lookup proved (found → positive society fact; clean not-found →
737
+ * negative assertion); mode "confirm_unregistered" records the user's
738
+ * own assertion (society_code optional — omitted means "not registered
739
+ * anywhere"). Lookup errors and unmatchable works write nothing.
740
+ */
741
+ verifyRegistrations(input: {
742
+ work_ids: string[];
743
+ mode: "check" | "confirm_unregistered";
744
+ society_code?: string;
745
+ }): Promise<{
746
+ mode: "check" | "confirm_unregistered";
747
+ results: Array<{
748
+ work_id: string;
749
+ result: "already_registered" | "found_registered" | "verified_absent" | "lookup_failed" | "insufficient_identifiers" | "confirmed" | "not_found";
750
+ society_code?: string | null;
751
+ external_id?: string;
752
+ query_kind?: "iswc" | "title_writer_ipi" | "title_artist_name";
753
+ detail?: string;
754
+ }>;
755
+ summary: Record<string, number>;
756
+ }>;
733
757
  }
734
758
  declare class PeopleResource extends BaseResource {
735
759
  list(params?: {
@@ -1100,13 +1124,6 @@ declare class NotesResource extends BaseResource {
1100
1124
  }): Promise<any>;
1101
1125
  delete(id: string): Promise<void>;
1102
1126
  }
1103
- declare class TeamResource extends BaseResource {
1104
- list(): Promise<any>;
1105
- get(id: string): Promise<any>;
1106
- invite(data: Record<string, any>): Promise<any>;
1107
- update(id: string, data: Record<string, any>): Promise<any>;
1108
- remove(id: string): Promise<any>;
1109
- }
1110
1127
  declare class RecordingsResource extends BaseResource {
1111
1128
  list(params?: {
1112
1129
  limit?: number;
@@ -1381,8 +1398,8 @@ declare class DashboardResource extends BaseResource {
1381
1398
  /**
1382
1399
  * Actionable items projection — `{class, entity_id, entity_type,
1383
1400
  * entity_label, failure_mode, urgency: {score, components,
1384
- * model_version}, drill_down_skill, suggested_skill (alias, slated
1385
- * for removal)}` per row, sorted by `urgency.score` desc, capped at
1401
+ * model_version}, drill_down_skill}` per row (the suggested_skill
1402
+ * alias was removed 2026-06-11), sorted by `urgency.score` desc, capped at
1386
1403
  * `limit`. Backed by the shared actionable-items source so counts
1387
1404
  * agree with `pica_dashboard_briefing.critical_issues_total` and
1388
1405
  * `pica_count_explain.sources[].count` for the same org/moment.
@@ -2083,8 +2100,8 @@ declare class ExportResource extends BaseResource {
2083
2100
  }): Promise<any>;
2084
2101
  }
2085
2102
  declare class DuplicatesResource extends BaseResource {
2086
- findDuplicates(entityType: "works" | "people"): Promise<any>;
2087
- merge(entityType: "work" | "person", winnerId: string, loserIds: string[]): Promise<any>;
2103
+ findDuplicates(entityType: "works" | "people" | "releases" | "recordings"): Promise<any>;
2104
+ merge(entityType: "work" | "person" | "recording" | "release", winnerId: string, loserIds: string[]): Promise<any>;
2088
2105
  }
2089
2106
  declare class EntityContextResource extends BaseResource {
2090
2107
  getWorkFull(workId: string): Promise<Record<string, unknown>>;
@@ -2431,6 +2448,27 @@ interface CollaboratorInvite {
2431
2448
  created_at: string;
2432
2449
  updated_at: string;
2433
2450
  }
2451
+ /**
2452
+ * ADR-264 W2d: response shape for POST /admin/collaborators/invite.
2453
+ * The route appends person identity fields so the MCP tool can evaluate
2454
+ * the collaborator_missing_identifiers hint without a second round-trip.
2455
+ *
2456
+ * person_id is null when the recipient is a brand-new person (not yet in the
2457
+ * people table — they are created on invite acceptance, not at send time).
2458
+ * has_ipi / has_isni are false when person_id is null or when the lookup
2459
+ * fails (conservative defaults: never suppress the hint on uncertainty).
2460
+ */
2461
+ export interface CollaboratorInviteResponse extends Record<string, unknown> {
2462
+ /** The created collaboration_invites row id. */
2463
+ id: string;
2464
+ /** Resolved people.id for the recipient in the caller's org, if they already
2465
+ * exist. null for brand-new invitees. */
2466
+ person_id: string | null;
2467
+ /** True if the person already has an IPI entry in person_identifiers. */
2468
+ has_ipi: boolean;
2469
+ /** True if the person already has an ISNI entry in person_identifiers. */
2470
+ has_isni: boolean;
2471
+ }
2434
2472
  declare class CollaboratorsResource extends BaseResource {
2435
2473
  invite(params: {
2436
2474
  /** ADR-265: exactly one of workId / recordingId / releaseId. */
@@ -2452,7 +2490,7 @@ declare class CollaboratorsResource extends BaseResource {
2452
2490
  kind: "email" | "handle";
2453
2491
  value: string;
2454
2492
  };
2455
- }): Promise<Record<string, unknown>>;
2493
+ }): Promise<CollaboratorInviteResponse>;
2456
2494
  listInvites(params?: {
2457
2495
  status?: "pending" | "verified" | "confirmed" | "expired" | "cancelled" | "disputed";
2458
2496
  workId?: string;
@@ -2739,6 +2777,42 @@ export interface RecordingCreditCreateInput {
2739
2777
  display_order?: number;
2740
2778
  notes?: string | null;
2741
2779
  }
2780
+ export interface RecordingCreditsBulkCreditInput {
2781
+ credited_name: string;
2782
+ role: string;
2783
+ person_id?: string | null;
2784
+ split_percentage?: number | null;
2785
+ split_type?: string | null;
2786
+ display_order?: number;
2787
+ notes?: string | null;
2788
+ }
2789
+ export interface RecordingCreditsBulkUpdateInput {
2790
+ /** Target every recording on this release. Exactly one of release_id / recording_ids. */
2791
+ release_id?: string;
2792
+ recording_ids?: string[];
2793
+ /** The credit set applied to EVERY target recording. */
2794
+ credits: RecordingCreditsBulkCreditInput[];
2795
+ /** Preview the create/skip decision set without writing. */
2796
+ dry_run?: boolean;
2797
+ }
2798
+ export type RecordingCreditsBulkRowStatus = "created" | "skipped_existing" | "would_create" | "would_skip_existing";
2799
+ export interface RecordingCreditsBulkRowResult {
2800
+ recording_id: string;
2801
+ credited_name: string;
2802
+ role: string;
2803
+ status: RecordingCreditsBulkRowStatus;
2804
+ credit_id?: string;
2805
+ }
2806
+ export interface RecordingCreditsBulkUpdateResult {
2807
+ dry_run: boolean;
2808
+ target_recording_count: number;
2809
+ total_rows: number;
2810
+ created: number;
2811
+ skipped_existing: number;
2812
+ would_create: number;
2813
+ would_skip_existing: number;
2814
+ results: RecordingCreditsBulkRowResult[];
2815
+ }
2742
2816
  declare class RecordingCreditsResource extends BaseResource {
2743
2817
  list(recordingId: string): Promise<RecordingCredit[]>;
2744
2818
  create(recordingId: string, input: RecordingCreditCreateInput): Promise<RecordingCredit>;
@@ -2754,6 +2828,13 @@ declare class RecordingCreditsResource extends BaseResource {
2754
2828
  * success. AC-2 strict semantics for pica_credit_remove.
2755
2829
  */
2756
2830
  atomicRemove(recordingId: string, creditId: string): Promise<AtomicCreditResult>;
2831
+ /**
2832
+ * ADR-264 WS4 prerequisite — apply one credit set across many recordings
2833
+ * (a release's tracklist via release_id, or an explicit recording_ids
2834
+ * list) in a single call. Existing (recording, credited_name, role) rows
2835
+ * are skipped, never overwritten; dry_run previews the same decision set.
2836
+ */
2837
+ bulkUpdate(input: RecordingCreditsBulkUpdateInput): Promise<RecordingCreditsBulkUpdateResult>;
2757
2838
  }
2758
2839
  export type ShareTraceEntityType = "work" | "recording" | "person";
2759
2840
  export interface ShareTraceQuery {
@@ -2908,11 +2989,25 @@ export interface LabelOrganisation {
2908
2989
  country: string | null;
2909
2990
  website: string | null;
2910
2991
  verification_status: string | null;
2992
+ org_type: string;
2993
+ contact_email?: string | null;
2994
+ /** ADR-268 WS2 bridge to the enrichment-side global label identity. */
2995
+ global_label_id?: string | null;
2996
+ }
2997
+ /** External label identifiers accepted by labels.create (ADR-268 WS2). */
2998
+ export interface LabelIdentifiers {
2999
+ musicbrainz_label_id?: string;
3000
+ isni?: string;
3001
+ label_code?: string;
3002
+ discogs_label_id?: string;
3003
+ wikidata_id?: string;
3004
+ deezer_label_id?: string;
2911
3005
  }
2912
3006
  declare class LabelsResource extends BaseResource {
2913
3007
  list(params?: {
2914
3008
  query?: string;
2915
3009
  limit?: number;
3010
+ org_type?: "label" | "distributor";
2916
3011
  }): Promise<LabelOrganisation[]>;
2917
3012
  create(params: {
2918
3013
  name: string;
@@ -2920,13 +3015,62 @@ declare class LabelsResource extends BaseResource {
2920
3015
  country?: string;
2921
3016
  website?: string;
2922
3017
  contact_email?: string;
2923
- }): Promise<{
2924
- success?: boolean;
2925
- data?: LabelOrganisation & {
2926
- org_type: string;
2927
- };
3018
+ /**
3019
+ * ADR-268 WS2: when supplied, server-side dedup is identifier-based —
3020
+ * an org already bridged to the resolved global label identity is
3021
+ * returned (already_existed) even if the name differs.
3022
+ */
3023
+ identifiers?: LabelIdentifiers;
3024
+ }): Promise<(LabelOrganisation & {
3025
+ org_type: string;
2928
3026
  already_existed?: boolean;
2929
- }>;
3027
+ }) | null>;
3028
+ }
3029
+ export interface PartyClaimInviteSummary {
3030
+ id: string;
3031
+ organisation_id: string;
3032
+ source: "invite" | "unsolicited";
3033
+ invited_by_organisation_id: string | null;
3034
+ invited_email: string;
3035
+ status: string;
3036
+ expires_at: string | null;
3037
+ claimed_at: string | null;
3038
+ claim_code: string | null;
3039
+ release_id: string | null;
3040
+ recording_id: string | null;
3041
+ created_at: string;
3042
+ organisation?: {
3043
+ id: string;
3044
+ name: string;
3045
+ org_type: string;
3046
+ verification_status: string | null;
3047
+ };
3048
+ }
3049
+ export interface PartyClaimInviteResult {
3050
+ invite: PartyClaimInviteSummary;
3051
+ disputed: boolean;
3052
+ claim_url: string | null;
3053
+ }
3054
+ /**
3055
+ * ADR-268 WS1 — party org claim flow. `invite()` sends an artist-anchored
3056
+ * claim invite for an unclaimed label/distributor/publisher org;
3057
+ * `list()` reads the claims the calling org has sent.
3058
+ *
3059
+ * Typed against the UNWRAPPED reality (ADR-265 lesson): BaseResource.request
3060
+ * auto-unwraps the `{success,data}` envelope, so methods see `data` itself.
3061
+ */
3062
+ declare class PartyClaimsResource extends BaseResource {
3063
+ invite(params: {
3064
+ target_organisation_id: string;
3065
+ invited_email: string;
3066
+ release_id?: string;
3067
+ recording_id?: string;
3068
+ }): Promise<PartyClaimInviteResult | null>;
3069
+ list(params?: {
3070
+ target_organisation_id?: string;
3071
+ status?: string;
3072
+ limit?: number;
3073
+ }): Promise<PartyClaimInviteSummary[]>;
2930
3074
  }
2931
3075
  declare class ReleasesResource extends BaseResource {
2932
3076
  list(params?: {
@@ -3189,17 +3333,19 @@ declare class CustodyResource extends BaseResource {
3189
3333
  }
3190
3334
  declare class RecordingCustodyResource extends BaseResource {
3191
3335
  /**
3192
- * Assign (transfer) custody of a recording. Admin/owner only. v1 supports
3193
- * control/possession transfer types only (delivery|license|admin_grant);
3194
- * ownership transfer (assignment|full_transfer) is a fast-follow and the
3195
- * route/service reject it. At least one custodian target must be supplied
3196
- * (org, global publisher, or global creator).
3336
+ * Assign (transfer) custody of a recording. Admin/owner only.
3337
+ * Control/possession types (delivery|license|admin_grant) leave ownership
3338
+ * unchanged. Ownership types (assignment|full_transfer ADR-268 WS5) also
3339
+ * move the master ownership splits append-only in the same transaction:
3340
+ * the transferor's active era closes, the transferee's opens; ambiguous or
3341
+ * over-share partial transfers are refused (400). At least one custodian
3342
+ * target must be supplied (org, global publisher, or global creator).
3197
3343
  */
3198
3344
  assign(body: {
3199
3345
  recording_id: string;
3200
3346
  percentage: number;
3201
3347
  territory: string;
3202
- transfer_type: "delivery" | "license" | "admin_grant";
3348
+ transfer_type: "delivery" | "license" | "admin_grant" | "assignment" | "full_transfer";
3203
3349
  reason: string;
3204
3350
  new_custodian_org_id?: string | null;
3205
3351
  new_custodian_global_publisher_id?: string | null;
@@ -3376,7 +3522,6 @@ export declare class PicaClient {
3376
3522
  sessions: SessionsResource;
3377
3523
  assets: AssetsResource;
3378
3524
  notes: NotesResource;
3379
- team: TeamResource;
3380
3525
  projects: ProjectsResource;
3381
3526
  releases: ReleasesResource;
3382
3527
  splitSheets: SplitSheetsResource;
@@ -3387,6 +3532,7 @@ export declare class PicaClient {
3387
3532
  audioPipelineStatus: AudioPipelineStatusResource;
3388
3533
  publishers: PublishersResource;
3389
3534
  labels: LabelsResource;
3535
+ partyClaims: PartyClaimsResource;
3390
3536
  agreementTemplates: AgreementTemplatesResource;
3391
3537
  producerAgreements: ProducerAgreementsResource;
3392
3538
  workForHire: WorkForHireResource;