@withpica/mcp-server 2.46.0 → 2.48.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 (94) hide show
  1. package/CHANGELOG.md +81 -2
  2. package/dist/apps/generated/shared-bundle.d.ts +1 -1
  3. package/dist/apps/generated/shared-bundle.d.ts.map +1 -1
  4. package/dist/apps/generated/shared-bundle.js +1 -1
  5. package/dist/apps/generated/shared-bundle.js.map +1 -1
  6. package/dist/resources/required-schemas.generated.d.ts +65 -0
  7. package/dist/resources/required-schemas.generated.d.ts.map +1 -1
  8. package/dist/resources/required-schemas.generated.js +131 -0
  9. package/dist/resources/required-schemas.generated.js.map +1 -1
  10. package/dist/resources/required-schemas.source.d.ts.map +1 -1
  11. package/dist/resources/required-schemas.source.js +76 -0
  12. package/dist/resources/required-schemas.source.js.map +1 -1
  13. package/dist/tools/agreement-types.js +1 -1
  14. package/dist/tools/agreement-types.js.map +1 -1
  15. package/dist/tools/agreements.js +1 -1
  16. package/dist/tools/agreements.js.map +1 -1
  17. package/dist/tools/app-tools.js +2 -2
  18. package/dist/tools/app-tools.js.map +1 -1
  19. package/dist/tools/assets.d.ts.map +1 -1
  20. package/dist/tools/assets.js +40 -14
  21. package/dist/tools/assets.js.map +1 -1
  22. package/dist/tools/audio-files.js +3 -3
  23. package/dist/tools/audio-files.js.map +1 -1
  24. package/dist/tools/bulk.js +2 -2
  25. package/dist/tools/bulk.js.map +1 -1
  26. package/dist/tools/collaborators.js +6 -6
  27. package/dist/tools/collaborators.js.map +1 -1
  28. package/dist/tools/credits.js +3 -3
  29. package/dist/tools/credits.js.map +1 -1
  30. package/dist/tools/discovery.d.ts.map +1 -1
  31. package/dist/tools/discovery.js +26 -0
  32. package/dist/tools/discovery.js.map +1 -1
  33. package/dist/tools/enrichment.js +13 -13
  34. package/dist/tools/enrichment.js.map +1 -1
  35. package/dist/tools/feedback.js +1 -1
  36. package/dist/tools/feedback.js.map +1 -1
  37. package/dist/tools/forbidden-keywords.d.ts +62 -0
  38. package/dist/tools/forbidden-keywords.d.ts.map +1 -0
  39. package/dist/tools/forbidden-keywords.js +99 -0
  40. package/dist/tools/forbidden-keywords.js.map +1 -0
  41. package/dist/tools/groups.d.ts +29 -0
  42. package/dist/tools/groups.d.ts.map +1 -0
  43. package/dist/tools/groups.js +186 -0
  44. package/dist/tools/groups.js.map +1 -0
  45. package/dist/tools/index.d.ts +1 -1
  46. package/dist/tools/index.d.ts.map +1 -1
  47. package/dist/tools/index.js +13 -1
  48. package/dist/tools/index.js.map +1 -1
  49. package/dist/tools/metadata.d.ts.map +1 -1
  50. package/dist/tools/metadata.js +62 -0
  51. package/dist/tools/metadata.js.map +1 -1
  52. package/dist/tools/notifications.js +3 -3
  53. package/dist/tools/notifications.js.map +1 -1
  54. package/dist/tools/people.d.ts.map +1 -1
  55. package/dist/tools/people.js +5 -0
  56. package/dist/tools/people.js.map +1 -1
  57. package/dist/tools/projects.d.ts.map +1 -1
  58. package/dist/tools/projects.js +6 -7
  59. package/dist/tools/projects.js.map +1 -1
  60. package/dist/tools/public-filter.d.ts.map +1 -1
  61. package/dist/tools/public-filter.js +8 -36
  62. package/dist/tools/public-filter.js.map +1 -1
  63. package/dist/tools/recordings.d.ts.map +1 -1
  64. package/dist/tools/recordings.js +75 -27
  65. package/dist/tools/recordings.js.map +1 -1
  66. package/dist/tools/recovery-hints.d.ts +26 -0
  67. package/dist/tools/recovery-hints.d.ts.map +1 -1
  68. package/dist/tools/recovery-hints.js +615 -0
  69. package/dist/tools/recovery-hints.js.map +1 -1
  70. package/dist/tools/release-rich.d.ts.map +1 -1
  71. package/dist/tools/release-rich.js +10 -6
  72. package/dist/tools/release-rich.js.map +1 -1
  73. package/dist/tools/settings.d.ts.map +1 -1
  74. package/dist/tools/settings.js +14 -15
  75. package/dist/tools/settings.js.map +1 -1
  76. package/dist/tools/signup.js +1 -1
  77. package/dist/tools/signup.js.map +1 -1
  78. package/dist/tools/split-sheets.js +1 -1
  79. package/dist/tools/split-sheets.js.map +1 -1
  80. package/dist/tools/storage-config.js +2 -2
  81. package/dist/tools/storage-config.js.map +1 -1
  82. package/dist/tools/subscription.d.ts.map +1 -1
  83. package/dist/tools/subscription.js +4 -6
  84. package/dist/tools/subscription.js.map +1 -1
  85. package/dist/tools/sync-placements.d.ts +31 -0
  86. package/dist/tools/sync-placements.d.ts.map +1 -0
  87. package/dist/tools/sync-placements.js +431 -0
  88. package/dist/tools/sync-placements.js.map +1 -0
  89. package/dist/tools/works.d.ts.map +1 -1
  90. package/dist/tools/works.js +50 -22
  91. package/dist/tools/works.js.map +1 -1
  92. package/package.json +3 -3
  93. package/server.json +2 -2
  94. package/.claude/settings.local.json +0 -5
@@ -0,0 +1,431 @@
1
+ // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
+ import { formatAsText, formatList, formatSuccess } from "@withpica/mcp-utils";
3
+ import { withBillingGate } from "@withpica/mcp-utils";
4
+ export class SyncPlacementsTools {
5
+ pica;
6
+ constructor(pica) {
7
+ this.pica = pica;
8
+ }
9
+ getTools() {
10
+ return [
11
+ {
12
+ definition: {
13
+ name: "pica_sync_placements_query",
14
+ description: "List sync placements for the org. Filters: work_id, status, verification_status, brand, production_company, person_id, recording_id, source_license_enquiry_id, min_fee, currency. Returns up to `limit` (default 50) with cursor support via `offset`.",
15
+ workflows: ["sync-placement-required"],
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ work_id: {
20
+ type: "string",
21
+ description: "Filter by work id",
22
+ },
23
+ status: {
24
+ type: "string",
25
+ enum: ["licensed", "aired", "expired", "renewed", "terminated"],
26
+ description: "Filter by placement lifecycle status",
27
+ },
28
+ verification_status: {
29
+ type: "string",
30
+ enum: [
31
+ "unverified",
32
+ "self_attested",
33
+ "evidence_attached",
34
+ "operator_verified",
35
+ "disputed",
36
+ ],
37
+ description: "Filter by evidence verification state",
38
+ },
39
+ brand: {
40
+ type: "string",
41
+ description: "Filter by advertiser/brand name",
42
+ },
43
+ production_company: {
44
+ type: "string",
45
+ description: "Filter by production company name",
46
+ },
47
+ person_id: {
48
+ type: "string",
49
+ description: "Filter to placements where this person is a contact (music supervisor, etc.)",
50
+ },
51
+ recording_id: {
52
+ type: "string",
53
+ description: "Filter to placements that licensed this recording",
54
+ },
55
+ source_license_enquiry_id: {
56
+ type: "string",
57
+ description: "Filter to placements that originated from this license enquiry",
58
+ },
59
+ min_fee: {
60
+ type: "number",
61
+ description: "Minimum fee_amount (pre-pagination SQL filter)",
62
+ },
63
+ currency: {
64
+ type: "string",
65
+ description: "ISO-4217 currency code (e.g. USD, GBP)",
66
+ },
67
+ limit: {
68
+ type: "number",
69
+ description: "Max results (default 50)",
70
+ },
71
+ offset: {
72
+ type: "number",
73
+ description: "Cursor offset",
74
+ },
75
+ },
76
+ },
77
+ },
78
+ executor: this.query.bind(this),
79
+ },
80
+ {
81
+ definition: {
82
+ name: "pica_sync_placements_inspect",
83
+ description: "Get one sync placement by id, including all linked recordings, evidence sources, the underlying agreement, and party rows.",
84
+ workflows: ["sync-placement-required"],
85
+ inputSchema: {
86
+ type: "object",
87
+ properties: {
88
+ id: {
89
+ type: "string",
90
+ description: "Sync placement id",
91
+ },
92
+ },
93
+ required: ["id"],
94
+ },
95
+ },
96
+ executor: this.inspect.bind(this),
97
+ },
98
+ {
99
+ definition: {
100
+ name: "pica_sync_placements_create",
101
+ description: "Create a sync placement (atomic — placement + draft sync_license agreement + at least one agreement_party + recordings + evidence sources, all in one Postgres transaction). Requires at least one entry in `contacts[]`. Empty contacts rejects with structured MISSING_CONTACT (next_tool: pica_people_query). New people can be auto-created inline by passing `person_name` instead of `person_id`.",
102
+ workflows: ["sync-placement-required"],
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: {
106
+ work_id: {
107
+ type: "string",
108
+ description: "Work being licensed for the placement",
109
+ },
110
+ production_title: {
111
+ type: "string",
112
+ description: "The film/series/ad/game name the work is licensed into",
113
+ },
114
+ production_type: {
115
+ type: "string",
116
+ description: "Film, TV, advertising, trailer, video game, etc.",
117
+ },
118
+ brand: {
119
+ type: "string",
120
+ description: "Advertiser brand (for ad placements)",
121
+ },
122
+ production_company: {
123
+ type: "string",
124
+ description: "Production company name",
125
+ },
126
+ status: {
127
+ type: "string",
128
+ enum: ["licensed", "aired", "expired", "renewed", "terminated"],
129
+ description: "Initial status (default 'licensed')",
130
+ },
131
+ fee_amount: {
132
+ type: "number",
133
+ description: "Total fee in minor units of fee_currency",
134
+ },
135
+ fee_currency: {
136
+ type: "string",
137
+ description: "ISO-4217 currency code",
138
+ },
139
+ use_start_date: {
140
+ type: "string",
141
+ description: "ISO-8601 date — first day of licensed use",
142
+ },
143
+ use_end_date: {
144
+ type: "string",
145
+ description: "ISO-8601 date — last day of licensed use",
146
+ },
147
+ territory: {
148
+ type: "array",
149
+ items: { type: "string" },
150
+ description: "ISO-3166 country codes; ['WW'] = worldwide",
151
+ },
152
+ media_channels: {
153
+ type: "array",
154
+ items: { type: "string" },
155
+ description: "tv, online, theatrical, social, in-game, in-store, etc.",
156
+ },
157
+ notes: {
158
+ type: "string",
159
+ description: "Free-text notes",
160
+ },
161
+ confidentiality_level: {
162
+ type: "string",
163
+ enum: ["public", "redacted_in_public_car", "private"],
164
+ description: "Public-CAR redaction policy (default 'redacted_in_public_car')",
165
+ },
166
+ contacts: {
167
+ type: "array",
168
+ description: "At least one contact required. Pass `person_id` for an existing person OR `person_name` (+ optional email/company/position) to auto-create.",
169
+ items: {
170
+ type: "object",
171
+ properties: {
172
+ person_id: { type: "string" },
173
+ person_name: { type: "string" },
174
+ role: {
175
+ type: "string",
176
+ description: "music_supervisor | brand_marketing_lead | sync_agent | clearance_attorney | film_producer | director | …",
177
+ },
178
+ email: { type: "string" },
179
+ company: { type: "string" },
180
+ position: { type: "string" },
181
+ split_percentage: {
182
+ type: "number",
183
+ description: "Optional commission split (0–100)",
184
+ },
185
+ },
186
+ required: ["role"],
187
+ },
188
+ minItems: 1,
189
+ },
190
+ recordings: {
191
+ type: "array",
192
+ description: "Specific recordings being licensed under the placement (optional but typical for sync — one master per recording).",
193
+ items: {
194
+ type: "object",
195
+ properties: {
196
+ recording_id: { type: "string" },
197
+ role: {
198
+ type: "string",
199
+ description: "main_master | re_record | instrumental | edit | trailer_cut | …",
200
+ },
201
+ master_fee: { type: "number" },
202
+ master_share_licensed_pct: { type: "number" },
203
+ notes: { type: "string" },
204
+ },
205
+ required: ["recording_id", "role"],
206
+ },
207
+ },
208
+ sources: {
209
+ type: "array",
210
+ description: "Evidence sources backing the placement. Auto-advances verification_status: any 'cue_sheet'/'invoice'/'agreement_pdf'/'email'/'screenshot'/'web'/'spreadsheet_import' → evidence_attached; 'user_attestation' only → self_attested; none → unverified.",
211
+ items: {
212
+ type: "object",
213
+ properties: {
214
+ url: { type: "string" },
215
+ kind: {
216
+ type: "string",
217
+ enum: [
218
+ "user_attestation",
219
+ "agreement_pdf",
220
+ "cue_sheet",
221
+ "invoice",
222
+ "email",
223
+ "screenshot",
224
+ "web",
225
+ "spreadsheet_import",
226
+ ],
227
+ },
228
+ agent_id: { type: "string" },
229
+ field_coverage: {
230
+ type: "array",
231
+ items: { type: "string" },
232
+ },
233
+ notes: { type: "string" },
234
+ },
235
+ required: ["url", "kind"],
236
+ },
237
+ },
238
+ },
239
+ required: ["work_id", "production_title", "contacts"],
240
+ },
241
+ },
242
+ executor: withBillingGate(this.pica, "sync placement creation", this.create.bind(this)),
243
+ },
244
+ {
245
+ definition: {
246
+ name: "pica_sync_placements_update",
247
+ description: "Patch a sync placement. Most fields are unconditional; `verification_status` (when set to 'operator_verified' or 'disputed') and `confidentiality_level` are operator-gated and will throw OPERATOR_REQUIRED for customer agents — use a soft transition like status:'terminated' for self-service retirement instead. Status transitions follow the placement state machine; illegal transitions throw INVALID_TRANSITION (inspect first to see the current state).",
248
+ workflows: ["sync-placement-required"],
249
+ inputSchema: {
250
+ type: "object",
251
+ properties: {
252
+ id: {
253
+ type: "string",
254
+ description: "Sync placement id",
255
+ },
256
+ status: {
257
+ type: "string",
258
+ enum: ["licensed", "aired", "expired", "renewed", "terminated"],
259
+ description: "Placement lifecycle status — transitions follow the sync_placement_status state machine; setting status:'terminated' is the customer-facing soft-delete path.",
260
+ },
261
+ verification_status: {
262
+ type: "string",
263
+ enum: [
264
+ "unverified",
265
+ "self_attested",
266
+ "evidence_attached",
267
+ "operator_verified",
268
+ "disputed",
269
+ ],
270
+ description: "Evidence verification state. Operator-only when set to 'operator_verified' or 'disputed'; other values throw OPERATOR_REQUIRED for customer agents.",
271
+ },
272
+ confidentiality_level: {
273
+ type: "string",
274
+ enum: ["public", "redacted_in_public_car", "private"],
275
+ description: "Public-CAR redaction policy. Operator-only — changing this throws OPERATOR_REQUIRED for customer agents.",
276
+ },
277
+ production_title: {
278
+ type: "string",
279
+ description: "The film/series/ad/game name the work is licensed into",
280
+ },
281
+ production_type: {
282
+ type: "string",
283
+ description: "Film, TV, advertising, trailer, video game, etc.",
284
+ },
285
+ brand: {
286
+ type: "string",
287
+ description: "Advertiser brand (for ad placements)",
288
+ },
289
+ production_company: {
290
+ type: "string",
291
+ description: "Production company name",
292
+ },
293
+ fee_amount: {
294
+ type: "number",
295
+ description: "Total fee in minor units of fee_currency",
296
+ },
297
+ fee_currency: {
298
+ type: "string",
299
+ description: "ISO-4217 currency code",
300
+ },
301
+ use_start_date: {
302
+ type: "string",
303
+ description: "ISO-8601 date — first day of licensed use",
304
+ },
305
+ use_end_date: {
306
+ type: "string",
307
+ description: "ISO-8601 date — last day of licensed use",
308
+ },
309
+ territory: {
310
+ type: "array",
311
+ items: { type: "string" },
312
+ description: "ISO-3166 country codes; ['WW'] = worldwide",
313
+ },
314
+ media_channels: {
315
+ type: "array",
316
+ items: { type: "string" },
317
+ description: "tv, online, theatrical, social, in-game, in-store, etc.",
318
+ },
319
+ notes: {
320
+ type: "string",
321
+ description: "Free-text notes",
322
+ },
323
+ },
324
+ required: ["id"],
325
+ },
326
+ },
327
+ executor: withBillingGate(this.pica, "sync placement update", this.update.bind(this)),
328
+ },
329
+ {
330
+ definition: {
331
+ name: "pica_sync_placements_delete",
332
+ description: "Hard-delete a sync placement. Operator-only — customer agents will receive structured OPERATOR_REQUIRED. For self-service retirement, use pica_sync_placements_update with status:'terminated' (soft-delete). Two-step confirmation pattern: first call returns a confirmation_token; second call with the token executes the delete.",
333
+ workflows: ["sync-placement-required"],
334
+ inputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ id: {
338
+ type: "string",
339
+ description: "Sync placement id",
340
+ },
341
+ confirmation_token: {
342
+ type: "string",
343
+ description: "Token from a prior call; required to actually execute the delete",
344
+ },
345
+ },
346
+ required: ["id"],
347
+ },
348
+ },
349
+ executor: withBillingGate(this.pica, "sync placement deletion", this.delete.bind(this)),
350
+ },
351
+ {
352
+ definition: {
353
+ name: "pica_sync_placements_cite",
354
+ description: "Attach an evidence source to a sync placement. Auto-recomputes verification_status — any non-attestation kind ('cue_sheet'/'invoice'/'agreement_pdf'/'email'/'screenshot'/'web'/'spreadsheet_import') advances unverified/self_attested → evidence_attached. operator_verified is sticky (only an explicit operator action exits it).",
355
+ workflows: ["sync-placement-required"],
356
+ inputSchema: {
357
+ type: "object",
358
+ properties: {
359
+ id: {
360
+ type: "string",
361
+ description: "Sync placement id",
362
+ },
363
+ url: {
364
+ type: "string",
365
+ description: "Evidence URL",
366
+ },
367
+ kind: {
368
+ type: "string",
369
+ enum: [
370
+ "user_attestation",
371
+ "agreement_pdf",
372
+ "cue_sheet",
373
+ "invoice",
374
+ "email",
375
+ "screenshot",
376
+ "web",
377
+ "spreadsheet_import",
378
+ ],
379
+ description: "Source kind. Any non-attestation kind advances verification_status to evidence_attached on first attach.",
380
+ },
381
+ agent_id: {
382
+ type: "string",
383
+ description: "Optional agent identity that supplied the source",
384
+ },
385
+ field_coverage: {
386
+ type: "array",
387
+ items: { type: "string" },
388
+ description: "Which placement fields this source attests to (used for evidence attribution in exports)",
389
+ },
390
+ notes: {
391
+ type: "string",
392
+ description: "Free-text notes",
393
+ },
394
+ },
395
+ required: ["id", "url", "kind"],
396
+ },
397
+ },
398
+ executor: withBillingGate(this.pica, "sync placement evidence", this.cite.bind(this)),
399
+ },
400
+ ];
401
+ }
402
+ // ── executors ──────────────────────────────────────────────────────────
403
+ async query(args) {
404
+ const result = await this.pica.syncPlacements.list(args);
405
+ return formatList(result.data, { total: result.total });
406
+ }
407
+ async inspect(args) {
408
+ const placement = await this.pica.syncPlacements.get(args.id);
409
+ return formatAsText(placement);
410
+ }
411
+ async create(args) {
412
+ const placement = await this.pica.syncPlacements.create(args);
413
+ return formatSuccess("Sync placement created", placement);
414
+ }
415
+ async update(args) {
416
+ const { id, ...patch } = args;
417
+ const placement = await this.pica.syncPlacements.update(id, patch);
418
+ return formatSuccess("Sync placement updated", placement);
419
+ }
420
+ async delete(args) {
421
+ const { id, confirmation_token } = args;
422
+ await this.pica.syncPlacements.delete(id, { confirmation_token });
423
+ return formatSuccess(`Sync placement ${id} deleted`);
424
+ }
425
+ async cite(args) {
426
+ const { id, ...source } = args;
427
+ const result = await this.pica.syncPlacements.addSource(id, source);
428
+ return formatSuccess("Evidence attached to sync placement", result);
429
+ }
430
+ }
431
+ //# sourceMappingURL=sync-placements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-placements.js","sourceRoot":"","sources":["../../src/tools/sync-placements.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"works.d.ts","sourceRoot":"","sources":["../../src/tools/works.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAc,MAAM,YAAY,CAAC;AAWtE;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkHzE,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAa;gBAEb,IAAI,EAAE,UAAU;IAI5B;;OAEG;IACH,QAAQ,IAAI,KAAK,CAAC;QAAE,UAAU,EAAE,cAAc,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,CAAC;IA8PzE;;OAEG;YACW,UAAU;IAsDxB;;OAEG;YACW,WAAW;IAsNzB;;OAEG;YACW,UAAU;IAkCxB;;OAEG;YACW,UAAU;IAMxB;;OAEG;YACW,UAAU;IAKxB;;OAEG;YACW,UAAU;IAiDxB;;OAEG;YACW,eAAe;CAM9B"}
1
+ {"version":3,"file":"works.d.ts","sourceRoot":"","sources":["../../src/tools/works.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAc,MAAM,YAAY,CAAC;AAatE;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkHzE,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAa;gBAEb,IAAI,EAAE,UAAU;IAI5B;;OAEG;IACH,QAAQ,IAAI,KAAK,CAAC;QAAE,UAAU,EAAE,cAAc,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,CAAC;IA8PzE;;OAEG;YACW,UAAU;IAsDxB;;OAEG;YACW,WAAW;IAwPzB;;OAEG;YACW,UAAU;IAkCxB;;OAEG;YACW,UAAU;IAMxB;;OAEG;YACW,UAAU;IAKxB;;OAEG;YACW,UAAU;IAiDxB;;OAEG;YACW,eAAe;CAM9B"}
@@ -1,5 +1,5 @@
1
1
  // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
- import { formatSuccess, formatStructuredList, withBillingGate, mapGapsToHints, } from "@withpica/mcp-utils";
2
+ import { formatSuccess, formatStructuredList, withBillingGate, mapGapsToHints, pushSectionError, } from "@withpica/mcp-utils";
3
3
  import { buildCustodyHints } from "./custody-hints.js";
4
4
  import { computeWorkGapHints } from "./gap-hints.js";
5
5
  /**
@@ -32,7 +32,7 @@ export const WORK_WRITE_PROPERTIES = {
32
32
  },
33
33
  isrc: {
34
34
  type: "string",
35
- description: "International Standard Recording Code. Bridges publishing (works) and masters (recordings). ADR-165 Phase 5 will move this to recordings; for now still settable on works.",
35
+ description: "International Standard Recording Code. Bridges publishing (works) and masters (recordings).",
36
36
  },
37
37
  tunecode: {
38
38
  type: "string",
@@ -44,7 +44,7 @@ export const WORK_WRITE_PROPERTIES = {
44
44
  },
45
45
  spotify_track_uri: {
46
46
  type: "string",
47
- description: "Spotify track URI (e.g. spotify:track:0b8ZtRm9u3xSzRazABeHFF). ADR-165 Phase 5 will move this to recordings.",
47
+ description: "Spotify track URI (e.g. spotify:track:0b8ZtRm9u3xSzRazABeHFF).",
48
48
  },
49
49
  duration_seconds: {
50
50
  type: "number",
@@ -168,7 +168,7 @@ export class WorksTools {
168
168
  name: "pica_works_inspect",
169
169
  description: "Get details and quality data for a specific work. Omit sections for everything. " +
170
170
  "Use sections=['enrichment'] to see cascade provenance (which source wrote each field), " +
171
- "completeness_score, completeness_breakdown, AND live enrichment-job statuses (spotify/mlc/acrcloud/railway) " +
171
+ "completeness_score, completeness_breakdown, AND live enrichment-job statuses (spotify, mlc, acrcloud, audio analysis) " +
172
172
  "in one response. " +
173
173
  "→ then: pica_credits_update (fix credits), pica_resolve_work (fill identifiers + metadata), pica_run_work_cascade (retry)",
174
174
  workflows: "infrastructure",
@@ -282,7 +282,7 @@ export class WorksTools {
282
282
  nextSteps: [
283
283
  {
284
284
  tool: "pica_works_inspect",
285
- reason: "Read back the work after update to confirm every field round-tripped (ADR-198 sister check).",
285
+ reason: "Read back the work after update to confirm every field round-tripped silent persistence drops are real.",
286
286
  when: "on_success",
287
287
  },
288
288
  ],
@@ -411,6 +411,16 @@ export class WorksTools {
411
411
  const has = (s) => allSections ? !ORG_WIDE_OPT_IN.has(s) : sections.includes(s);
412
412
  const result = {};
413
413
  const hints = [];
414
+ // ADR-224 — section-level failures push to errors[] and the host
415
+ // log pipeline (via pushSectionError) regardless of flag state.
416
+ // The flag controls whether the public response advertises errors[]
417
+ // and whether failed sections set the field to null vs the legacy
418
+ // empty-fallback. Default OFF in Phase 1; flips to default-on at
419
+ // Phase 4 after the soak window catches any caller depending on
420
+ // the empty-array shape.
421
+ const useStructured = process.env.ADR_224_STRUCTURED_ERRORS === "1";
422
+ const errors = [];
423
+ const TOOL_NAME = "pica_works_inspect";
414
424
  // Basic — always fetched (either via entityContext.getWorkFull or works.get)
415
425
  if (allSections) {
416
426
  const full = await this.pica.entityContext.getWorkFull(args.id);
@@ -447,8 +457,9 @@ export class WorksTools {
447
457
  try {
448
458
  result.credits = await this.pica.credits.listForWork(args.id);
449
459
  }
450
- catch {
451
- result.credits = [];
460
+ catch (e) {
461
+ result.credits = useStructured ? null : [];
462
+ pushSectionError(errors, TOOL_NAME, "credits", e, args.id);
452
463
  }
453
464
  })());
454
465
  }
@@ -457,8 +468,9 @@ export class WorksTools {
457
468
  try {
458
469
  result.recordings = await this.pica.recordings.getByWork(args.id);
459
470
  }
460
- catch {
461
- result.recordings = [];
471
+ catch (e) {
472
+ result.recordings = useStructured ? null : [];
473
+ pushSectionError(errors, TOOL_NAME, "recordings", e, args.id);
462
474
  }
463
475
  })());
464
476
  }
@@ -467,8 +479,9 @@ export class WorksTools {
467
479
  try {
468
480
  result.agreements = await this.pica.agreements.list();
469
481
  }
470
- catch {
471
- result.agreements = [];
482
+ catch (e) {
483
+ result.agreements = useStructured ? null : [];
484
+ pushSectionError(errors, TOOL_NAME, "agreements", e, args.id);
472
485
  }
473
486
  })());
474
487
  }
@@ -479,8 +492,9 @@ export class WorksTools {
479
492
  try {
480
493
  result.health = await this.pica.health.getWorksHealth();
481
494
  }
482
- catch {
495
+ catch (e) {
483
496
  result.health = null;
497
+ pushSectionError(errors, TOOL_NAME, "health", e, args.id);
484
498
  }
485
499
  })());
486
500
  }
@@ -491,8 +505,9 @@ export class WorksTools {
491
505
  result.completeness = completeness;
492
506
  hints.push(...mapGapsToHints(completeness?.gaps || completeness?.missing || []));
493
507
  }
494
- catch {
508
+ catch (e) {
495
509
  result.completeness = null;
510
+ pushSectionError(errors, TOOL_NAME, "completeness", e, args.id);
496
511
  }
497
512
  })());
498
513
  }
@@ -502,8 +517,9 @@ export class WorksTools {
502
517
  result.registration =
503
518
  await this.pica.registration.getWorkCascadeStatus(args.id);
504
519
  }
505
- catch {
520
+ catch (e) {
506
521
  result.registration = null;
522
+ pushSectionError(errors, TOOL_NAME, "registration", e, args.id);
507
523
  }
508
524
  })());
509
525
  }
@@ -512,8 +528,9 @@ export class WorksTools {
512
528
  try {
513
529
  result.score = await this.pica.picaScore.get();
514
530
  }
515
- catch {
531
+ catch (e) {
516
532
  result.score = null;
533
+ pushSectionError(errors, TOOL_NAME, "score", e, args.id);
517
534
  }
518
535
  })());
519
536
  }
@@ -522,8 +539,9 @@ export class WorksTools {
522
539
  try {
523
540
  result.provenance = await this.pica.analytics.provenanceWork(args.id);
524
541
  }
525
- catch {
542
+ catch (e) {
526
543
  result.provenance = null;
544
+ pushSectionError(errors, TOOL_NAME, "provenance", e, args.id);
527
545
  }
528
546
  })());
529
547
  }
@@ -533,8 +551,9 @@ export class WorksTools {
533
551
  result.enrichment =
534
552
  await this.pica.enrichment.getWorkEnrichmentStatus(args.id);
535
553
  }
536
- catch {
554
+ catch (e) {
537
555
  result.enrichment = null;
556
+ pushSectionError(errors, TOOL_NAME, "enrichment", e, args.id);
538
557
  }
539
558
  })());
540
559
  }
@@ -546,8 +565,9 @@ export class WorksTools {
546
565
  result.production_assets =
547
566
  await this.pica.works.listProductionAssets(args.id);
548
567
  }
549
- catch {
550
- result.production_assets = [];
568
+ catch (e) {
569
+ result.production_assets = useStructured ? null : [];
570
+ pushSectionError(errors, TOOL_NAME, "production_assets", e, args.id);
551
571
  }
552
572
  })());
553
573
  }
@@ -559,8 +579,9 @@ export class WorksTools {
559
579
  try {
560
580
  result.releases = await this.pica.works.listReleases(args.id);
561
581
  }
562
- catch {
563
- result.releases = [];
582
+ catch (e) {
583
+ result.releases = useStructured ? null : [];
584
+ pushSectionError(errors, TOOL_NAME, "releases", e, args.id);
564
585
  }
565
586
  })());
566
587
  }
@@ -568,7 +589,14 @@ export class WorksTools {
568
589
  // ADR-193 Surface 2 — proactive peer-MCP hints when the flag is on
569
590
  // and a real gap exists. Additive on top of the CompletionHint path.
570
591
  const gap_hints = computeWorkGapHints(result);
571
- const enriched = gap_hints.length > 0 ? { ...result, gap_hints } : result;
592
+ const withHints = gap_hints.length > 0 ? { ...result, gap_hints } : result;
593
+ // ADR-224 — append errors[] to the public response only when the
594
+ // structured-errors flag is on. In legacy mode, section failures are
595
+ // still pushed to the errors[] array (so stderr observability fires
596
+ // via pushSectionError) but the array is dropped from the response
597
+ // shape so the breaking change ([] → null on failed sections) doesn't
598
+ // ship until Phase 4's flip.
599
+ const enriched = useStructured ? { ...withHints, errors } : withHints;
572
600
  return formatSuccess(`Work details${sections ? ` (sections: ${sections.join(", ")})` : ""}`, enriched, hints);
573
601
  }
574
602
  /**