@withpica/mcp-server 2.11.0 → 2.23.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +639 -1
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +3 -2
  4. package/dist/config.js.map +1 -1
  5. package/dist/prompts/index.d.ts +48 -16
  6. package/dist/prompts/index.d.ts.map +1 -1
  7. package/dist/prompts/index.js +320 -607
  8. package/dist/prompts/index.js.map +1 -1
  9. package/dist/tools/agreement-types.d.ts.map +1 -1
  10. package/dist/tools/agreement-types.js +14 -129
  11. package/dist/tools/agreement-types.js.map +1 -1
  12. package/dist/tools/agreements.d.ts.map +1 -1
  13. package/dist/tools/agreements.js +11 -109
  14. package/dist/tools/agreements.js.map +1 -1
  15. package/dist/tools/assets.d.ts.map +1 -1
  16. package/dist/tools/assets.js +17 -257
  17. package/dist/tools/assets.js.map +1 -1
  18. package/dist/tools/audit.d.ts +19 -0
  19. package/dist/tools/audit.d.ts.map +1 -0
  20. package/dist/tools/audit.js +57 -0
  21. package/dist/tools/audit.js.map +1 -0
  22. package/dist/tools/bulk.d.ts +7 -1
  23. package/dist/tools/bulk.d.ts.map +1 -1
  24. package/dist/tools/bulk.js +24 -2
  25. package/dist/tools/bulk.js.map +1 -1
  26. package/dist/tools/credits.d.ts.map +1 -1
  27. package/dist/tools/credits.js +27 -13
  28. package/dist/tools/credits.js.map +1 -1
  29. package/dist/tools/discovery.d.ts.map +1 -1
  30. package/dist/tools/discovery.js +25 -0
  31. package/dist/tools/discovery.js.map +1 -1
  32. package/dist/tools/enrichment.d.ts +47 -0
  33. package/dist/tools/enrichment.d.ts.map +1 -1
  34. package/dist/tools/enrichment.js +416 -19
  35. package/dist/tools/enrichment.js.map +1 -1
  36. package/dist/tools/index.d.ts.map +1 -1
  37. package/dist/tools/index.js +17 -0
  38. package/dist/tools/index.js.map +1 -1
  39. package/dist/tools/labels.d.ts +20 -0
  40. package/dist/tools/labels.d.ts.map +1 -0
  41. package/dist/tools/labels.js +47 -0
  42. package/dist/tools/labels.js.map +1 -0
  43. package/dist/tools/metadata.d.ts.map +1 -1
  44. package/dist/tools/metadata.js +93 -0
  45. package/dist/tools/metadata.js.map +1 -1
  46. package/dist/tools/multimedia.d.ts.map +1 -1
  47. package/dist/tools/multimedia.js +86 -11
  48. package/dist/tools/multimedia.js.map +1 -1
  49. package/dist/tools/notes.d.ts.map +1 -1
  50. package/dist/tools/notes.js +4 -1
  51. package/dist/tools/notes.js.map +1 -1
  52. package/dist/tools/people.d.ts +228 -4
  53. package/dist/tools/people.d.ts.map +1 -1
  54. package/dist/tools/people.js +238 -262
  55. package/dist/tools/people.js.map +1 -1
  56. package/dist/tools/projects.d.ts +1 -0
  57. package/dist/tools/projects.d.ts.map +1 -1
  58. package/dist/tools/projects.js +188 -17
  59. package/dist/tools/projects.js.map +1 -1
  60. package/dist/tools/publishers.d.ts +15 -1
  61. package/dist/tools/publishers.d.ts.map +1 -1
  62. package/dist/tools/publishers.js +43 -9
  63. package/dist/tools/publishers.js.map +1 -1
  64. package/dist/tools/recordings.d.ts +53 -2
  65. package/dist/tools/recordings.d.ts.map +1 -1
  66. package/dist/tools/recordings.js +185 -122
  67. package/dist/tools/recordings.js.map +1 -1
  68. package/dist/tools/recovery-hints.d.ts.map +1 -1
  69. package/dist/tools/recovery-hints.js +49 -0
  70. package/dist/tools/recovery-hints.js.map +1 -1
  71. package/dist/tools/releases.d.ts +19 -1
  72. package/dist/tools/releases.d.ts.map +1 -1
  73. package/dist/tools/releases.js +538 -32
  74. package/dist/tools/releases.js.map +1 -1
  75. package/dist/tools/sessions.d.ts.map +1 -1
  76. package/dist/tools/sessions.js +42 -8
  77. package/dist/tools/sessions.js.map +1 -1
  78. package/dist/tools/settings.d.ts +4 -0
  79. package/dist/tools/settings.d.ts.map +1 -1
  80. package/dist/tools/settings.js +155 -1
  81. package/dist/tools/settings.js.map +1 -1
  82. package/dist/tools/share-links.d.ts.map +1 -1
  83. package/dist/tools/share-links.js +19 -53
  84. package/dist/tools/share-links.js.map +1 -1
  85. package/dist/tools/split-sheets.d.ts.map +1 -1
  86. package/dist/tools/split-sheets.js +3 -42
  87. package/dist/tools/split-sheets.js.map +1 -1
  88. package/dist/tools/team.d.ts.map +1 -1
  89. package/dist/tools/team.js +9 -4
  90. package/dist/tools/team.js.map +1 -1
  91. package/dist/tools/works.d.ts +27 -1
  92. package/dist/tools/works.d.ts.map +1 -1
  93. package/dist/tools/works.js +119 -219
  94. package/dist/tools/works.js.map +1 -1
  95. package/package.json +3 -2
  96. package/server.json +2 -2
@@ -1,5 +1,72 @@
1
1
  // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
2
  import { formatSuccess, formatStructuredList } from "@withpica/mcp-utils";
3
+ /**
4
+ * Canonical write-schema for `releases`. Every property below must
5
+ * correspond to a real column on `public.releases`. Keep this list
6
+ * tight — reintroducing a phantom field is the exact regression
7
+ * ADR-174 Phase 2 exists to prevent.
8
+ */
9
+ const RELEASE_WRITE_PROPERTIES = {
10
+ title: {
11
+ type: "string",
12
+ description: "Release title",
13
+ },
14
+ release_date: {
15
+ type: "string",
16
+ description: "Release date (YYYY-MM-DD)",
17
+ },
18
+ release_type: {
19
+ type: "string",
20
+ description: "Release type",
21
+ enum: ["album", "ep", "single", "compilation"],
22
+ },
23
+ pre_release_date: {
24
+ type: "string",
25
+ description: "Pre-release / announcement date (YYYY-MM-DD)",
26
+ },
27
+ upc: {
28
+ type: "string",
29
+ description: "Universal Product Code (UPC/EAN barcode)",
30
+ },
31
+ catalog_number: {
32
+ type: "string",
33
+ description: "Label catalog number",
34
+ },
35
+ label_organization_id: {
36
+ type: "string",
37
+ description: "organisations.id of the label (must be an org with org_type='label'). " +
38
+ "Resolve a label name to an ID via pica_labels_query first — never pass a free-text name.",
39
+ },
40
+ distributor_organization_id: {
41
+ type: "string",
42
+ description: "organisations.id of the distributor. Resolve a distributor name via pica_search_all first.",
43
+ },
44
+ artwork_url: {
45
+ type: "string",
46
+ description: "Album artwork URL",
47
+ },
48
+ spotify_album_uri: {
49
+ type: "string",
50
+ description: "Spotify album URI (e.g. spotify:album:…)",
51
+ },
52
+ spotify_url: {
53
+ type: "string",
54
+ description: "Spotify album web URL",
55
+ },
56
+ apple_music_url: {
57
+ type: "string",
58
+ description: "Apple Music album URL",
59
+ },
60
+ other_urls: {
61
+ type: "object",
62
+ description: "DSP URLs keyed by service name (e.g. { tidal: '…', deezer: '…' })",
63
+ additionalProperties: { type: "string" },
64
+ },
65
+ notes: {
66
+ type: "string",
67
+ description: "Internal notes",
68
+ },
69
+ };
3
70
  export class ReleasesTools {
4
71
  pica;
5
72
  constructor(pica) {
@@ -10,7 +77,7 @@ export class ReleasesTools {
10
77
  {
11
78
  definition: {
12
79
  name: "pica_releases_list",
13
- description: "List releases — albums, EPs, and singles with release dates, labels, and UPCs.",
80
+ description: "List releases — albums, EPs, and singles with release dates, labels, distributors, and UPCs.",
14
81
  inputSchema: {
15
82
  type: "object",
16
83
  properties: {
@@ -40,69 +107,478 @@ export class ReleasesTools {
40
107
  {
41
108
  definition: {
42
109
  name: "pica_releases_create",
43
- description: "Create a new release — set the title, date, release type, label, and UPC.",
110
+ description: "Create a new release — set title, dates, type, catalog number, " +
111
+ "label/distributor organisation IDs, artwork, DSP URLs, and notes. " +
112
+ "For label_organization_id use pica_labels_query first to resolve a name to an ID " +
113
+ "(freetext label names are not accepted — the DB stores label as an FK to organisations).",
114
+ inputSchema: {
115
+ type: "object",
116
+ properties: RELEASE_WRITE_PROPERTIES,
117
+ required: ["title"],
118
+ },
119
+ },
120
+ executor: this.createRelease.bind(this),
121
+ },
122
+ {
123
+ definition: {
124
+ name: "pica_releases_update",
125
+ description: "Update release details — any field in the release write schema. " +
126
+ "For label_organization_id use pica_labels_query first to resolve a name to an ID.",
44
127
  inputSchema: {
45
128
  type: "object",
46
129
  properties: {
47
- title: {
130
+ id: { type: "string", description: "Release ID" },
131
+ ...RELEASE_WRITE_PROPERTIES,
132
+ },
133
+ required: ["id"],
134
+ },
135
+ },
136
+ executor: this.updateRelease.bind(this),
137
+ },
138
+ // ADR-173: release-track primitives + compound workflow
139
+ {
140
+ definition: {
141
+ name: "pica_releases_attach_track",
142
+ description: "Attach a recording and/or work to a release at a specific (disc, track) position. " +
143
+ "At least one of recording_id or work_id must be provided. Idempotent on " +
144
+ "(release_id, disc_number, track_number) — a second call at the same position " +
145
+ "updates the existing row rather than creating a duplicate. " +
146
+ "→ then: pica_releases_list_tracks (verify tracklist), pica_releases_reorder_tracks (change order)",
147
+ inputSchema: {
148
+ type: "object",
149
+ properties: {
150
+ release_id: { type: "string", description: "Release ID" },
151
+ recording_id: {
48
152
  type: "string",
49
- description: "Release title",
153
+ description: "Recording ID (optional — at least one of recording_id or work_id must be provided)",
50
154
  },
51
- release_date: {
155
+ work_id: {
52
156
  type: "string",
53
- description: "Release date (YYYY-MM-DD)",
157
+ description: "Work ID (optional — omit for licensed-masters releases where no composition record exists)",
54
158
  },
55
- release_type: {
56
- type: "string",
57
- description: "Release type (album, ep, single, compilation)",
159
+ track_number: {
160
+ type: "number",
161
+ description: "Position on disc (1-based)",
58
162
  },
59
- upc: {
60
- type: "string",
61
- description: "Universal Product Code (UPC/EAN barcode)",
163
+ disc_number: {
164
+ type: "number",
165
+ description: "Disc number (default 1)",
62
166
  },
63
- catalog_number: {
64
- type: "string",
65
- description: "Label catalog number",
167
+ },
168
+ required: ["release_id", "track_number"],
169
+ },
170
+ },
171
+ executor: this.attachTrack.bind(this),
172
+ },
173
+ {
174
+ definition: {
175
+ name: "pica_releases_list_tracks",
176
+ description: "List tracks on a release in (disc_number, track_number) order with " +
177
+ "inlined title, artist, ISRC, and duration from the linked recording and work. " +
178
+ "→ then: pica_releases_reorder_tracks (change order), pica_recordings_inspect (deep-dive a track)",
179
+ inputSchema: {
180
+ type: "object",
181
+ properties: {
182
+ release_id: { type: "string", description: "Release ID" },
183
+ },
184
+ required: ["release_id"],
185
+ },
186
+ },
187
+ executor: this.listTracks.bind(this),
188
+ },
189
+ {
190
+ definition: {
191
+ name: "pica_releases_detach_track",
192
+ description: "Remove a track from a release by position. Soft-confirmation: call without " +
193
+ "confirm:true to see a preview of the row that would be removed, then call " +
194
+ "again with confirm:true to execute.",
195
+ inputSchema: {
196
+ type: "object",
197
+ properties: {
198
+ release_id: { type: "string", description: "Release ID" },
199
+ track_number: {
200
+ type: "number",
201
+ description: "Position on disc (1-based)",
202
+ },
203
+ disc_number: {
204
+ type: "number",
205
+ description: "Disc number (default 1)",
206
+ },
207
+ confirm: {
208
+ type: "boolean",
209
+ description: "Set to true to execute the detach after reviewing the preview",
66
210
  },
67
211
  },
68
- required: ["title"],
212
+ required: ["release_id", "track_number"],
69
213
  },
70
214
  },
71
- executor: this.createRelease.bind(this),
215
+ executor: this.detachTrack.bind(this),
72
216
  },
73
217
  {
74
218
  definition: {
75
- name: "pica_releases_update",
76
- description: "Update release details title, date, release type, label, UPC, or catalog number.",
219
+ name: "pica_releases_reorder_tracks",
220
+ description: "Reorder tracks on a release atomically. Pass the full new order as an array " +
221
+ "of { track_id, track_number, disc_number } — the entire reorder commits or " +
222
+ "rolls back as one transaction. Duplicate (disc_number, track_number) pairs " +
223
+ "in the payload are rejected before any DB write.",
77
224
  inputSchema: {
78
225
  type: "object",
79
226
  properties: {
80
- id: { type: "string", description: "Release ID" },
81
- title: { type: "string", description: "Updated title" },
82
- release_date: {
83
- type: "string",
84
- description: "Updated release date (YYYY-MM-DD)",
227
+ release_id: { type: "string", description: "Release ID" },
228
+ new_order: {
229
+ type: "array",
230
+ description: "New ordering of tracks. Every existing track should appear here.",
231
+ items: {
232
+ type: "object",
233
+ properties: {
234
+ track_id: {
235
+ type: "string",
236
+ description: "release_tracks.id",
237
+ },
238
+ track_number: { type: "number" },
239
+ disc_number: {
240
+ type: "number",
241
+ description: "Default 1",
242
+ },
243
+ },
244
+ required: ["track_id", "track_number"],
245
+ },
246
+ },
247
+ },
248
+ required: ["release_id", "new_order"],
249
+ },
250
+ },
251
+ executor: this.reorderTracks.bind(this),
252
+ },
253
+ {
254
+ definition: {
255
+ name: "pica_releases_attach_recording_with_work",
256
+ description: "Attach a recording to a release at a specific position, automatically " +
257
+ "pulling the recording's linked work (if any) onto the same track row. " +
258
+ "Saves agents the recordings.work_id lookup that pica_releases_attach_track " +
259
+ "would otherwise require. Idempotent on (release_id, disc_number, track_number).",
260
+ inputSchema: {
261
+ type: "object",
262
+ properties: {
263
+ release_id: { type: "string", description: "Release ID" },
264
+ recording_id: { type: "string", description: "Recording ID" },
265
+ track_number: {
266
+ type: "number",
267
+ description: "Position on disc (1-based)",
268
+ },
269
+ disc_number: {
270
+ type: "number",
271
+ description: "Disc number (default 1)",
272
+ },
273
+ },
274
+ required: ["release_id", "recording_id", "track_number"],
275
+ },
276
+ },
277
+ executor: this.attachRecordingWithWork.bind(this),
278
+ },
279
+ // ADR-175: release workflow tools — compound operations on the primitives
280
+ {
281
+ definition: {
282
+ name: "pica_releases_bulk_attach_tracks",
283
+ description: "Attach many tracks to a release in a single atomic call. Same " +
284
+ "idempotency semantics as pica_releases_attach_track — a duplicate " +
285
+ "(disc_number, track_number) position updates rather than duplicates. " +
286
+ "Duplicate positions WITHIN the payload are rejected before any DB " +
287
+ "write. Use when you have an existing release and the full tracklist " +
288
+ "at once; use pica_releases_create_with_tracks when the release does " +
289
+ "not yet exist.",
290
+ inputSchema: {
291
+ type: "object",
292
+ properties: {
293
+ release_id: { type: "string", description: "Release ID" },
294
+ tracks: {
295
+ type: "array",
296
+ description: "Tracks to attach. Each entry needs at least one of recording_id " +
297
+ "or work_id, plus track_number (1-based).",
298
+ items: {
299
+ type: "object",
300
+ properties: {
301
+ recording_id: {
302
+ type: "string",
303
+ description: "Recording ID (optional if work_id is present)",
304
+ },
305
+ work_id: {
306
+ type: "string",
307
+ description: "Work ID (optional if recording_id is present)",
308
+ },
309
+ track_number: {
310
+ type: "number",
311
+ description: "Position on disc (1-based)",
312
+ },
313
+ disc_number: {
314
+ type: "number",
315
+ description: "Disc number (default 1)",
316
+ },
317
+ },
318
+ required: ["track_number"],
319
+ },
320
+ },
321
+ },
322
+ required: ["release_id", "tracks"],
323
+ },
324
+ },
325
+ executor: this.bulkAttachTracks.bind(this),
326
+ },
327
+ {
328
+ definition: {
329
+ name: "pica_releases_move_track",
330
+ description: "Swap two track positions on a release atomically. Internally reads " +
331
+ "both rows and writes them with exchanged (disc_number, track_number) " +
332
+ "in one transaction. For the rare full re-sequence, use " +
333
+ "pica_releases_reorder_tracks instead.",
334
+ inputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ release_id: { type: "string", description: "Release ID" },
338
+ from: {
339
+ type: "object",
340
+ description: "Source position",
341
+ properties: {
342
+ track_number: { type: "number" },
343
+ disc_number: { type: "number", description: "Default 1" },
344
+ },
345
+ required: ["track_number"],
85
346
  },
86
- release_type: {
347
+ to: {
348
+ type: "object",
349
+ description: "Destination position (must be occupied — this is a swap, not a slide)",
350
+ properties: {
351
+ track_number: { type: "number" },
352
+ disc_number: { type: "number", description: "Default 1" },
353
+ },
354
+ required: ["track_number"],
355
+ },
356
+ },
357
+ required: ["release_id", "from", "to"],
358
+ },
359
+ },
360
+ executor: this.moveTrack.bind(this),
361
+ },
362
+ {
363
+ definition: {
364
+ name: "pica_releases_attach_track_by_identifier",
365
+ description: "Attach a track by external identifier (ISRC, Spotify URL, Spotify " +
366
+ "track URI, YouTube video id, MusicBrainz recording id, MLC " +
367
+ "recording id). The identifier is resolved to an internal recording " +
368
+ "id in the caller's organisation before attaching. This tool does " +
369
+ "NOT create recordings — if the identifier resolves to nothing the " +
370
+ "call fails with a recovery hint pointing at pica_import_streaming_" +
371
+ "link or the relevant import tool. Intended for the post-import " +
372
+ "attach step. → then: pica_releases_list_tracks (verify tracklist).",
373
+ inputSchema: {
374
+ type: "object",
375
+ properties: {
376
+ release_id: { type: "string", description: "Release ID" },
377
+ identifier_type: {
87
378
  type: "string",
88
- description: "Updated release type (album, ep, single, compilation)",
379
+ enum: [
380
+ "isrc",
381
+ "spotify_url",
382
+ "spotify_track_uri",
383
+ "youtube_video_id",
384
+ "musicbrainz_recording_id",
385
+ "mlc_recording_id",
386
+ ],
387
+ description: "Type of external identifier to resolve",
89
388
  },
90
- upc: {
389
+ value: {
91
390
  type: "string",
92
- description: "Updated UPC",
391
+ description: "The identifier value itself",
392
+ },
393
+ track_number: {
394
+ type: "number",
395
+ description: "Position on disc (1-based)",
396
+ },
397
+ disc_number: {
398
+ type: "number",
399
+ description: "Disc number (default 1)",
93
400
  },
94
- catalog_number: {
401
+ },
402
+ required: [
403
+ "release_id",
404
+ "identifier_type",
405
+ "value",
406
+ "track_number",
407
+ ],
408
+ },
409
+ },
410
+ executor: this.attachTrackByIdentifier.bind(this),
411
+ },
412
+ {
413
+ definition: {
414
+ name: "pica_releases_create_with_tracks",
415
+ description: "Create a release and its tracks in a single atomic transaction. " +
416
+ "Any failure — FK violation, identifier lookup miss, invalid track " +
417
+ "entry — rolls the entire release back. No partial releases. Each " +
418
+ "track entry can pass recording_id/work_id directly OR pass an " +
419
+ "identifier_lookup object which the tool resolves server-side " +
420
+ "(same semantics as pica_releases_attach_track_by_identifier). " +
421
+ "Use this when importing a new album — use pica_releases_bulk_" +
422
+ "attach_tracks when the release already exists.",
423
+ inputSchema: {
424
+ type: "object",
425
+ properties: {
426
+ release: {
427
+ type: "object",
428
+ description: "Release fields (same shape as pica_releases_create). " +
429
+ "title is required; release_type defaults to 'single'.",
430
+ properties: {
431
+ title: { type: "string", description: "Release title" },
432
+ release_type: {
433
+ type: "string",
434
+ enum: ["album", "ep", "single", "compilation"],
435
+ description: "Release type (default 'single')",
436
+ },
437
+ release_date: {
438
+ type: "string",
439
+ description: "Release date (YYYY-MM-DD)",
440
+ },
441
+ pre_release_date: {
442
+ type: "string",
443
+ description: "Pre-release / announcement date (YYYY-MM-DD)",
444
+ },
445
+ upc: { type: "string", description: "UPC / EAN barcode" },
446
+ catalog_number: {
447
+ type: "string",
448
+ description: "Label catalog number",
449
+ },
450
+ label_organization_id: {
451
+ type: "string",
452
+ description: "organisations.id of the label — resolve via pica_labels_query first.",
453
+ },
454
+ distributor_organization_id: {
455
+ type: "string",
456
+ description: "organisations.id of the distributor.",
457
+ },
458
+ artwork_url: {
459
+ type: "string",
460
+ description: "Album artwork URL",
461
+ },
462
+ spotify_album_uri: { type: "string" },
463
+ spotify_url: { type: "string" },
464
+ apple_music_url: { type: "string" },
465
+ other_urls: {
466
+ type: "object",
467
+ additionalProperties: { type: "string" },
468
+ },
469
+ notes: { type: "string" },
470
+ },
471
+ required: ["title"],
472
+ },
473
+ tracks: {
474
+ type: "array",
475
+ description: "Track entries. Each needs track_number plus one of: " +
476
+ "recording_id, work_id, or identifier_lookup.",
477
+ items: {
478
+ type: "object",
479
+ properties: {
480
+ recording_id: { type: "string" },
481
+ work_id: { type: "string" },
482
+ identifier_lookup: {
483
+ type: "object",
484
+ description: "Resolve a recording by external identifier in " +
485
+ "the caller's org before inserting the track. " +
486
+ "Misses and collisions abort the whole create.",
487
+ properties: {
488
+ identifier_type: {
489
+ type: "string",
490
+ enum: [
491
+ "isrc",
492
+ "spotify_url",
493
+ "spotify_track_uri",
494
+ "youtube_video_id",
495
+ "musicbrainz_recording_id",
496
+ "mlc_recording_id",
497
+ ],
498
+ },
499
+ value: { type: "string" },
500
+ },
501
+ required: ["identifier_type", "value"],
502
+ },
503
+ track_number: {
504
+ type: "number",
505
+ description: "Position on disc (1-based)",
506
+ },
507
+ disc_number: {
508
+ type: "number",
509
+ description: "Disc number (default 1)",
510
+ },
511
+ },
512
+ required: ["track_number"],
513
+ },
514
+ },
515
+ },
516
+ required: ["release", "tracks"],
517
+ },
518
+ },
519
+ executor: this.createWithTracks.bind(this),
520
+ },
521
+ {
522
+ definition: {
523
+ name: "pica_releases_completeness_check",
524
+ description: "Run the release-scoped completeness diligence report. Pass a " +
525
+ "release_id for a single-release report, or omit for a scan of " +
526
+ "every release in the caller's organisation. Returns a list of " +
527
+ "issues per release with severity (critical/warning/suggestion) " +
528
+ "and a recovery hint pointing at the right tool. Issues also " +
529
+ "surface in pica_catalog_diligence; this tool gives release-" +
530
+ "level granularity.",
531
+ inputSchema: {
532
+ type: "object",
533
+ properties: {
534
+ release_id: {
95
535
  type: "string",
96
- description: "Updated catalog number",
536
+ description: "Optional a single release to check. Omit to scan " +
537
+ "every release in the organisation.",
97
538
  },
98
539
  },
99
- required: ["id"],
100
540
  },
101
541
  },
102
- executor: this.updateRelease.bind(this),
542
+ executor: this.completenessCheck.bind(this),
103
543
  },
104
544
  ];
105
545
  }
546
+ async attachTrack(args) {
547
+ const track = await this.pica.releases.attachTrack(args.release_id, {
548
+ recording_id: args.recording_id ?? null,
549
+ work_id: args.work_id ?? null,
550
+ track_number: args.track_number,
551
+ disc_number: args.disc_number,
552
+ });
553
+ return formatSuccess("Track attached to release", track);
554
+ }
555
+ async listTracks(args) {
556
+ const tracks = await this.pica.releases.listTracks(args.release_id);
557
+ return formatSuccess("Release tracks retrieved", tracks);
558
+ }
559
+ async detachTrack(args) {
560
+ const result = await this.pica.releases.detachTrack(args.release_id, {
561
+ track_number: args.track_number,
562
+ disc_number: args.disc_number,
563
+ confirm: args.confirm,
564
+ });
565
+ const message = result?.data?.confirmed
566
+ ? "Track detached from release"
567
+ : "Preview of track to be detached — call again with confirm:true to execute";
568
+ return formatSuccess(message, result);
569
+ }
570
+ async reorderTracks(args) {
571
+ const tracks = await this.pica.releases.reorderTracks(args.release_id, args.new_order);
572
+ return formatSuccess("Release tracks reordered", tracks);
573
+ }
574
+ async attachRecordingWithWork(args) {
575
+ const track = await this.pica.releases.attachRecordingWithWork(args.release_id, {
576
+ recording_id: args.recording_id,
577
+ track_number: args.track_number,
578
+ disc_number: args.disc_number,
579
+ });
580
+ return formatSuccess("Recording attached to release with work", track);
581
+ }
106
582
  async listReleases(args) {
107
583
  const result = await this.pica.releases.list({
108
584
  limit: args.limit,
@@ -124,5 +600,35 @@ export class ReleasesTools {
124
600
  const release = await this.pica.releases.update(id, updates);
125
601
  return formatSuccess("Release updated", release);
126
602
  }
603
+ // ADR-175 workflow executors
604
+ async bulkAttachTracks(args) {
605
+ const tracks = await this.pica.releases.bulkAttachTracks(args.release_id, args.tracks);
606
+ return formatSuccess(`Bulk attached ${Array.isArray(args.tracks) ? args.tracks.length : 0} tracks`, tracks);
607
+ }
608
+ async moveTrack(args) {
609
+ const tracks = await this.pica.releases.moveTrack(args.release_id, args.from, args.to);
610
+ return formatSuccess("Tracks swapped", tracks);
611
+ }
612
+ async attachTrackByIdentifier(args) {
613
+ const result = await this.pica.releases.attachTrackByIdentifier(args.release_id, {
614
+ identifier_type: args.identifier_type,
615
+ value: args.value,
616
+ track_number: args.track_number,
617
+ disc_number: args.disc_number,
618
+ });
619
+ return formatSuccess("Track attached by identifier", result);
620
+ }
621
+ async createWithTracks(args) {
622
+ const result = await this.pica.releases.createWithTracks({
623
+ release: args.release,
624
+ tracks: args.tracks,
625
+ });
626
+ const count = Array.isArray(args.tracks) ? args.tracks.length : 0;
627
+ return formatSuccess(`Release created with ${count} tracks`, result);
628
+ }
629
+ async completenessCheck(args) {
630
+ const report = await this.pica.releases.completenessCheck(args.release_id ? { release_id: args.release_id } : undefined);
631
+ return formatSuccess("Release completeness report", report);
632
+ }
127
633
  }
128
634
  //# sourceMappingURL=releases.js.map