@withpica/mcp-server 1.3.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 (347) hide show
  1. package/.claude/settings.local.json +5 -0
  2. package/CHANGELOG.md +1850 -0
  3. package/README.md +304 -0
  4. package/assets/fonts/GeistSans-Light.woff2 +0 -0
  5. package/assets/fonts/InstrumentSerif-Italic.woff2 +0 -0
  6. package/assets/fonts/InstrumentSerif-Regular.woff2 +0 -0
  7. package/dist/apps/download.d.ts +2 -0
  8. package/dist/apps/download.d.ts.map +1 -0
  9. package/dist/apps/download.js +125 -0
  10. package/dist/apps/download.js.map +1 -0
  11. package/dist/apps/generated/shared-bundle.d.ts +5 -0
  12. package/dist/apps/generated/shared-bundle.d.ts.map +1 -0
  13. package/dist/apps/generated/shared-bundle.js +7 -0
  14. package/dist/apps/generated/shared-bundle.js.map +1 -0
  15. package/dist/apps/release.d.ts +2 -0
  16. package/dist/apps/release.d.ts.map +1 -0
  17. package/dist/apps/release.js +69 -0
  18. package/dist/apps/release.js.map +1 -0
  19. package/dist/apps/shared.d.ts +15 -0
  20. package/dist/apps/shared.d.ts.map +1 -0
  21. package/dist/apps/shared.js +480 -0
  22. package/dist/apps/shared.js.map +1 -0
  23. package/dist/apps/upload.d.ts +2 -0
  24. package/dist/apps/upload.d.ts.map +1 -0
  25. package/dist/apps/upload.js +280 -0
  26. package/dist/apps/upload.js.map +1 -0
  27. package/dist/config.d.ts +14 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +73 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +35 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/prompts/index.d.ts +86 -0
  36. package/dist/prompts/index.d.ts.map +1 -0
  37. package/dist/prompts/index.js +645 -0
  38. package/dist/prompts/index.js.map +1 -0
  39. package/dist/resources/agent-guide.d.ts +15 -0
  40. package/dist/resources/agent-guide.d.ts.map +1 -0
  41. package/dist/resources/agent-guide.js +284 -0
  42. package/dist/resources/agent-guide.js.map +1 -0
  43. package/dist/resources/index.d.ts +66 -0
  44. package/dist/resources/index.d.ts.map +1 -0
  45. package/dist/resources/index.js +510 -0
  46. package/dist/resources/index.js.map +1 -0
  47. package/dist/resources/llms-primer.d.ts +2 -0
  48. package/dist/resources/llms-primer.d.ts.map +1 -0
  49. package/dist/resources/llms-primer.js +68 -0
  50. package/dist/resources/llms-primer.js.map +1 -0
  51. package/dist/resources/required-schemas.generated.d.ts +455 -0
  52. package/dist/resources/required-schemas.generated.d.ts.map +1 -0
  53. package/dist/resources/required-schemas.generated.js +1041 -0
  54. package/dist/resources/required-schemas.generated.js.map +1 -0
  55. package/dist/resources/required-schemas.source.d.ts +53 -0
  56. package/dist/resources/required-schemas.source.d.ts.map +1 -0
  57. package/dist/resources/required-schemas.source.js +493 -0
  58. package/dist/resources/required-schemas.source.js.map +1 -0
  59. package/dist/resources/welcome.d.ts +14 -0
  60. package/dist/resources/welcome.d.ts.map +1 -0
  61. package/dist/resources/welcome.js +26 -0
  62. package/dist/resources/welcome.js.map +1 -0
  63. package/dist/server-instructions.d.ts +60 -0
  64. package/dist/server-instructions.d.ts.map +1 -0
  65. package/dist/server-instructions.js +93 -0
  66. package/dist/server-instructions.js.map +1 -0
  67. package/dist/server.d.ts +52 -0
  68. package/dist/server.d.ts.map +1 -0
  69. package/dist/server.js +334 -0
  70. package/dist/server.js.map +1 -0
  71. package/dist/tools/access-simulate.d.ts +23 -0
  72. package/dist/tools/access-simulate.d.ts.map +1 -0
  73. package/dist/tools/access-simulate.js +162 -0
  74. package/dist/tools/access-simulate.js.map +1 -0
  75. package/dist/tools/agent-identity.d.ts +36 -0
  76. package/dist/tools/agent-identity.d.ts.map +1 -0
  77. package/dist/tools/agent-identity.js +274 -0
  78. package/dist/tools/agent-identity.js.map +1 -0
  79. package/dist/tools/agreement-types.d.ts +27 -0
  80. package/dist/tools/agreement-types.d.ts.map +1 -0
  81. package/dist/tools/agreement-types.js +281 -0
  82. package/dist/tools/agreement-types.js.map +1 -0
  83. package/dist/tools/agreements.d.ts +20 -0
  84. package/dist/tools/agreements.d.ts.map +1 -0
  85. package/dist/tools/agreements.js +282 -0
  86. package/dist/tools/agreements.js.map +1 -0
  87. package/dist/tools/analytics.d.ts +20 -0
  88. package/dist/tools/analytics.d.ts.map +1 -0
  89. package/dist/tools/analytics.js +130 -0
  90. package/dist/tools/analytics.js.map +1 -0
  91. package/dist/tools/app-tools.d.ts +15 -0
  92. package/dist/tools/app-tools.d.ts.map +1 -0
  93. package/dist/tools/app-tools.js +388 -0
  94. package/dist/tools/app-tools.js.map +1 -0
  95. package/dist/tools/assets.d.ts +25 -0
  96. package/dist/tools/assets.d.ts.map +1 -0
  97. package/dist/tools/assets.js +454 -0
  98. package/dist/tools/assets.js.map +1 -0
  99. package/dist/tools/audio-files.d.ts +20 -0
  100. package/dist/tools/audio-files.d.ts.map +1 -0
  101. package/dist/tools/audio-files.js +409 -0
  102. package/dist/tools/audio-files.js.map +1 -0
  103. package/dist/tools/audit.d.ts +19 -0
  104. package/dist/tools/audit.d.ts.map +1 -0
  105. package/dist/tools/audit.js +58 -0
  106. package/dist/tools/audit.js.map +1 -0
  107. package/dist/tools/auth.d.ts +22 -0
  108. package/dist/tools/auth.d.ts.map +1 -0
  109. package/dist/tools/auth.js +212 -0
  110. package/dist/tools/auth.js.map +1 -0
  111. package/dist/tools/bulk.d.ts +22 -0
  112. package/dist/tools/bulk.d.ts.map +1 -0
  113. package/dist/tools/bulk.js +164 -0
  114. package/dist/tools/bulk.js.map +1 -0
  115. package/dist/tools/calendar.d.ts +15 -0
  116. package/dist/tools/calendar.d.ts.map +1 -0
  117. package/dist/tools/calendar.js +68 -0
  118. package/dist/tools/calendar.js.map +1 -0
  119. package/dist/tools/collaborators.d.ts +21 -0
  120. package/dist/tools/collaborators.d.ts.map +1 -0
  121. package/dist/tools/collaborators.js +381 -0
  122. package/dist/tools/collaborators.js.map +1 -0
  123. package/dist/tools/comparisons.d.ts +22 -0
  124. package/dist/tools/comparisons.d.ts.map +1 -0
  125. package/dist/tools/comparisons.js +80 -0
  126. package/dist/tools/comparisons.js.map +1 -0
  127. package/dist/tools/credits.d.ts +39 -0
  128. package/dist/tools/credits.d.ts.map +1 -0
  129. package/dist/tools/credits.js +541 -0
  130. package/dist/tools/credits.js.map +1 -0
  131. package/dist/tools/custody-hints.d.ts +16 -0
  132. package/dist/tools/custody-hints.d.ts.map +1 -0
  133. package/dist/tools/custody-hints.js +27 -0
  134. package/dist/tools/custody-hints.js.map +1 -0
  135. package/dist/tools/custody.d.ts +38 -0
  136. package/dist/tools/custody.d.ts.map +1 -0
  137. package/dist/tools/custody.js +281 -0
  138. package/dist/tools/custody.js.map +1 -0
  139. package/dist/tools/dashboard.d.ts +22 -0
  140. package/dist/tools/dashboard.d.ts.map +1 -0
  141. package/dist/tools/dashboard.js +258 -0
  142. package/dist/tools/dashboard.js.map +1 -0
  143. package/dist/tools/directory.d.ts +15 -0
  144. package/dist/tools/directory.d.ts.map +1 -0
  145. package/dist/tools/directory.js +107 -0
  146. package/dist/tools/directory.js.map +1 -0
  147. package/dist/tools/discovery.d.ts +49 -0
  148. package/dist/tools/discovery.d.ts.map +1 -0
  149. package/dist/tools/discovery.js +851 -0
  150. package/dist/tools/discovery.js.map +1 -0
  151. package/dist/tools/disputes.d.ts +18 -0
  152. package/dist/tools/disputes.d.ts.map +1 -0
  153. package/dist/tools/disputes.js +62 -0
  154. package/dist/tools/disputes.js.map +1 -0
  155. package/dist/tools/documents.d.ts +15 -0
  156. package/dist/tools/documents.d.ts.map +1 -0
  157. package/dist/tools/documents.js +37 -0
  158. package/dist/tools/documents.js.map +1 -0
  159. package/dist/tools/duplicates.d.ts +25 -0
  160. package/dist/tools/duplicates.d.ts.map +1 -0
  161. package/dist/tools/duplicates.js +108 -0
  162. package/dist/tools/duplicates.js.map +1 -0
  163. package/dist/tools/enrichment.d.ts +56 -0
  164. package/dist/tools/enrichment.d.ts.map +1 -0
  165. package/dist/tools/enrichment.js +616 -0
  166. package/dist/tools/enrichment.js.map +1 -0
  167. package/dist/tools/exports.d.ts +19 -0
  168. package/dist/tools/exports.d.ts.map +1 -0
  169. package/dist/tools/exports.js +184 -0
  170. package/dist/tools/exports.js.map +1 -0
  171. package/dist/tools/feedback.d.ts +22 -0
  172. package/dist/tools/feedback.d.ts.map +1 -0
  173. package/dist/tools/feedback.js +100 -0
  174. package/dist/tools/feedback.js.map +1 -0
  175. package/dist/tools/forbidden-keywords.d.ts +62 -0
  176. package/dist/tools/forbidden-keywords.d.ts.map +1 -0
  177. package/dist/tools/forbidden-keywords.js +99 -0
  178. package/dist/tools/forbidden-keywords.js.map +1 -0
  179. package/dist/tools/gap-hints.d.ts +53 -0
  180. package/dist/tools/gap-hints.d.ts.map +1 -0
  181. package/dist/tools/gap-hints.js +245 -0
  182. package/dist/tools/gap-hints.js.map +1 -0
  183. package/dist/tools/groups.d.ts +29 -0
  184. package/dist/tools/groups.d.ts.map +1 -0
  185. package/dist/tools/groups.js +186 -0
  186. package/dist/tools/groups.js.map +1 -0
  187. package/dist/tools/import-documents.d.ts +21 -0
  188. package/dist/tools/import-documents.d.ts.map +1 -0
  189. package/dist/tools/import-documents.js +206 -0
  190. package/dist/tools/import-documents.js.map +1 -0
  191. package/dist/tools/import.d.ts +31 -0
  192. package/dist/tools/import.d.ts.map +1 -0
  193. package/dist/tools/import.js +610 -0
  194. package/dist/tools/import.js.map +1 -0
  195. package/dist/tools/index.d.ts +293 -0
  196. package/dist/tools/index.d.ts.map +1 -0
  197. package/dist/tools/index.js +1182 -0
  198. package/dist/tools/index.js.map +1 -0
  199. package/dist/tools/integrations.d.ts +19 -0
  200. package/dist/tools/integrations.d.ts.map +1 -0
  201. package/dist/tools/integrations.js +120 -0
  202. package/dist/tools/integrations.js.map +1 -0
  203. package/dist/tools/labels.d.ts +20 -0
  204. package/dist/tools/labels.d.ts.map +1 -0
  205. package/dist/tools/labels.js +48 -0
  206. package/dist/tools/labels.js.map +1 -0
  207. package/dist/tools/licensing.d.ts +40 -0
  208. package/dist/tools/licensing.d.ts.map +1 -0
  209. package/dist/tools/licensing.js +436 -0
  210. package/dist/tools/licensing.js.map +1 -0
  211. package/dist/tools/memory.d.ts +21 -0
  212. package/dist/tools/memory.d.ts.map +1 -0
  213. package/dist/tools/memory.js +120 -0
  214. package/dist/tools/memory.js.map +1 -0
  215. package/dist/tools/metadata.d.ts +15 -0
  216. package/dist/tools/metadata.d.ts.map +1 -0
  217. package/dist/tools/metadata.js +1532 -0
  218. package/dist/tools/metadata.js.map +1 -0
  219. package/dist/tools/multimedia.d.ts +19 -0
  220. package/dist/tools/multimedia.d.ts.map +1 -0
  221. package/dist/tools/multimedia.js +371 -0
  222. package/dist/tools/multimedia.js.map +1 -0
  223. package/dist/tools/my-reported-issues.d.ts +32 -0
  224. package/dist/tools/my-reported-issues.d.ts.map +1 -0
  225. package/dist/tools/my-reported-issues.js +123 -0
  226. package/dist/tools/my-reported-issues.js.map +1 -0
  227. package/dist/tools/notes.d.ts +21 -0
  228. package/dist/tools/notes.d.ts.map +1 -0
  229. package/dist/tools/notes.js +115 -0
  230. package/dist/tools/notes.js.map +1 -0
  231. package/dist/tools/notifications.d.ts +28 -0
  232. package/dist/tools/notifications.d.ts.map +1 -0
  233. package/dist/tools/notifications.js +417 -0
  234. package/dist/tools/notifications.js.map +1 -0
  235. package/dist/tools/onboarding.d.ts +24 -0
  236. package/dist/tools/onboarding.d.ts.map +1 -0
  237. package/dist/tools/onboarding.js +81 -0
  238. package/dist/tools/onboarding.js.map +1 -0
  239. package/dist/tools/people.d.ts +254 -0
  240. package/dist/tools/people.d.ts.map +1 -0
  241. package/dist/tools/people.js +481 -0
  242. package/dist/tools/people.js.map +1 -0
  243. package/dist/tools/projects.d.ts +20 -0
  244. package/dist/tools/projects.d.ts.map +1 -0
  245. package/dist/tools/projects.js +316 -0
  246. package/dist/tools/projects.js.map +1 -0
  247. package/dist/tools/public-filter.d.ts +43 -0
  248. package/dist/tools/public-filter.d.ts.map +1 -0
  249. package/dist/tools/public-filter.js +356 -0
  250. package/dist/tools/public-filter.js.map +1 -0
  251. package/dist/tools/publishers.d.ts +30 -0
  252. package/dist/tools/publishers.d.ts.map +1 -0
  253. package/dist/tools/publishers.js +105 -0
  254. package/dist/tools/publishers.js.map +1 -0
  255. package/dist/tools/purchases.d.ts +27 -0
  256. package/dist/tools/purchases.d.ts.map +1 -0
  257. package/dist/tools/purchases.js +9 -0
  258. package/dist/tools/purchases.js.map +1 -0
  259. package/dist/tools/recording-attribution-hints.d.ts +24 -0
  260. package/dist/tools/recording-attribution-hints.d.ts.map +1 -0
  261. package/dist/tools/recording-attribution-hints.js +27 -0
  262. package/dist/tools/recording-attribution-hints.js.map +1 -0
  263. package/dist/tools/recordings.d.ts +96 -0
  264. package/dist/tools/recordings.d.ts.map +1 -0
  265. package/dist/tools/recordings.js +564 -0
  266. package/dist/tools/recordings.js.map +1 -0
  267. package/dist/tools/recovery-hints.d.ts +40 -0
  268. package/dist/tools/recovery-hints.d.ts.map +1 -0
  269. package/dist/tools/recovery-hints.js +827 -0
  270. package/dist/tools/recovery-hints.js.map +1 -0
  271. package/dist/tools/release-rich.d.ts +31 -0
  272. package/dist/tools/release-rich.d.ts.map +1 -0
  273. package/dist/tools/release-rich.js +245 -0
  274. package/dist/tools/release-rich.js.map +1 -0
  275. package/dist/tools/releases.d.ts +36 -0
  276. package/dist/tools/releases.d.ts.map +1 -0
  277. package/dist/tools/releases.js +649 -0
  278. package/dist/tools/releases.js.map +1 -0
  279. package/dist/tools/report-issue.d.ts +21 -0
  280. package/dist/tools/report-issue.d.ts.map +1 -0
  281. package/dist/tools/report-issue.js +101 -0
  282. package/dist/tools/report-issue.js.map +1 -0
  283. package/dist/tools/royalties.d.ts +23 -0
  284. package/dist/tools/royalties.d.ts.map +1 -0
  285. package/dist/tools/royalties.js +262 -0
  286. package/dist/tools/royalties.js.map +1 -0
  287. package/dist/tools/search.d.ts +30 -0
  288. package/dist/tools/search.d.ts.map +1 -0
  289. package/dist/tools/search.js +115 -0
  290. package/dist/tools/search.js.map +1 -0
  291. package/dist/tools/send.d.ts +17 -0
  292. package/dist/tools/send.d.ts.map +1 -0
  293. package/dist/tools/send.js +188 -0
  294. package/dist/tools/send.js.map +1 -0
  295. package/dist/tools/sessions.d.ts +18 -0
  296. package/dist/tools/sessions.d.ts.map +1 -0
  297. package/dist/tools/sessions.js +153 -0
  298. package/dist/tools/sessions.js.map +1 -0
  299. package/dist/tools/settings.d.ts +23 -0
  300. package/dist/tools/settings.d.ts.map +1 -0
  301. package/dist/tools/settings.js +365 -0
  302. package/dist/tools/settings.js.map +1 -0
  303. package/dist/tools/share-links.d.ts +22 -0
  304. package/dist/tools/share-links.d.ts.map +1 -0
  305. package/dist/tools/share-links.js +188 -0
  306. package/dist/tools/share-links.js.map +1 -0
  307. package/dist/tools/signup.d.ts +26 -0
  308. package/dist/tools/signup.d.ts.map +1 -0
  309. package/dist/tools/signup.js +266 -0
  310. package/dist/tools/signup.js.map +1 -0
  311. package/dist/tools/split-sheets.d.ts +25 -0
  312. package/dist/tools/split-sheets.d.ts.map +1 -0
  313. package/dist/tools/split-sheets.js +309 -0
  314. package/dist/tools/split-sheets.js.map +1 -0
  315. package/dist/tools/storage-config.d.ts +13 -0
  316. package/dist/tools/storage-config.d.ts.map +1 -0
  317. package/dist/tools/storage-config.js +245 -0
  318. package/dist/tools/storage-config.js.map +1 -0
  319. package/dist/tools/subscription.d.ts +60 -0
  320. package/dist/tools/subscription.d.ts.map +1 -0
  321. package/dist/tools/subscription.js +440 -0
  322. package/dist/tools/subscription.js.map +1 -0
  323. package/dist/tools/sync-placements.d.ts +31 -0
  324. package/dist/tools/sync-placements.d.ts.map +1 -0
  325. package/dist/tools/sync-placements.js +431 -0
  326. package/dist/tools/sync-placements.js.map +1 -0
  327. package/dist/tools/team.d.ts +22 -0
  328. package/dist/tools/team.d.ts.map +1 -0
  329. package/dist/tools/team.js +144 -0
  330. package/dist/tools/team.js.map +1 -0
  331. package/dist/tools/telegram.d.ts +21 -0
  332. package/dist/tools/telegram.d.ts.map +1 -0
  333. package/dist/tools/telegram.js +144 -0
  334. package/dist/tools/telegram.js.map +1 -0
  335. package/dist/tools/uploads.d.ts +17 -0
  336. package/dist/tools/uploads.d.ts.map +1 -0
  337. package/dist/tools/uploads.js +165 -0
  338. package/dist/tools/uploads.js.map +1 -0
  339. package/dist/tools/works.d.ts +71 -0
  340. package/dist/tools/works.d.ts.map +1 -0
  341. package/dist/tools/works.js +694 -0
  342. package/dist/tools/works.js.map +1 -0
  343. package/package.json +54 -0
  344. package/scripts/build-required-schemas.ts +233 -0
  345. package/scripts/bundle-apps.ts +61 -0
  346. package/scripts/refresh-schema-mirror.ts +182 -0
  347. package/server.json +20 -0
@@ -0,0 +1,694 @@
1
+ // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
+ import { formatSuccess, formatStructuredList, withBillingGate, mapGapsToHints, pushSectionError, } from "@withpica/mcp-utils";
3
+ import { buildCustodyHints } from "./custody-hints.js";
4
+ import { computeWorkGapHints } from "./gap-hints.js";
5
+ /**
6
+ * Canonical write-schema for `works`. Every property below must
7
+ * correspond to a real column on `public.works` — verified against
8
+ * `lib/db/database.types.ts`. Keep this list tight: reintroducing a
9
+ * phantom field is the exact regression ADR-174 Phase 2 exists to
10
+ * prevent. The HTTP route at `app/api/admin/works/[id]/route.ts`
11
+ * carries a broader allow-list that includes admin-UI-posted satellite
12
+ * fields; this list is the narrower MCP-agent contract.
13
+ */
14
+ export const WORK_WRITE_PROPERTIES = {
15
+ title: {
16
+ type: "string",
17
+ description: "Work title",
18
+ },
19
+ work_type: {
20
+ type: "string",
21
+ description: "Type of work",
22
+ enum: ["song", "instrumental", "library", "demo", "sample"],
23
+ },
24
+ work_status: {
25
+ type: "string",
26
+ description: "Work status (state machine validates transitions — demo → unreleased → released, etc.)",
27
+ enum: ["demo", "unreleased", "released", "pitched", "placed", "archived"],
28
+ },
29
+ iswc: {
30
+ type: "string",
31
+ description: "International Standard Musical Work Code (composition)",
32
+ },
33
+ isrc: {
34
+ type: "string",
35
+ description: "International Standard Recording Code. Bridges publishing (works) and masters (recordings).",
36
+ },
37
+ tunecode: {
38
+ type: "string",
39
+ description: "PRS tunecode (e.g. 851348GT)",
40
+ },
41
+ prs_work_id: {
42
+ type: "string",
43
+ description: "PRS work ID",
44
+ },
45
+ spotify_track_uri: {
46
+ type: "string",
47
+ description: "Spotify track URI (e.g. spotify:track:0b8ZtRm9u3xSzRazABeHFF).",
48
+ },
49
+ duration_seconds: {
50
+ type: "number",
51
+ description: "Duration in seconds",
52
+ },
53
+ tempo: {
54
+ type: "number",
55
+ description: "BPM (beats per minute)",
56
+ },
57
+ key: {
58
+ type: "string",
59
+ description: "Musical key (e.g. 'C major', 'Am')",
60
+ },
61
+ genre: {
62
+ type: "array",
63
+ items: { type: "string" },
64
+ description: "Genre tags (e.g. ['R&B', 'Soul'])",
65
+ },
66
+ mood: {
67
+ type: "array",
68
+ items: { type: "string" },
69
+ description: "Mood tags (e.g. ['Uplifting', 'Reflective'])",
70
+ },
71
+ primary_artist: {
72
+ type: "string",
73
+ description: "Primary artist name (freetext display value)",
74
+ },
75
+ primary_artist_id: {
76
+ type: "string",
77
+ description: "Primary artist person ID (FK to people)",
78
+ },
79
+ display_artist: {
80
+ type: "string",
81
+ description: "Display-friendly artist string (e.g. 'Jane Doe ft. John Smith')",
82
+ },
83
+ publisher_name: {
84
+ type: "string",
85
+ description: "Publisher name (legacy freetext). Prefer linking via publisher identity where possible.",
86
+ },
87
+ lyrics: {
88
+ type: "string",
89
+ description: "Lyrics text",
90
+ },
91
+ lyrics_language: {
92
+ type: "string",
93
+ description: "Language of lyrics (ISO 639-1, e.g. 'en')",
94
+ },
95
+ published: {
96
+ type: "boolean",
97
+ description: "Whether the work is published on the public directory",
98
+ },
99
+ notes: {
100
+ type: "string",
101
+ description: "Internal notes",
102
+ },
103
+ // Declared jsonb extension points — ADR-174 Phase 2c inverse-phantom fix.
104
+ // These keys are not columns on public.works; the service routes them to
105
+ // the `work_licensing` satellite via extractSatelliteFields() (see
106
+ // lib/services/works/constants.ts WORK_SATELLITE_FIELDS). Same shape as
107
+ // the parallel fields on `pica_recordings_update` (recordings.ts:193).
108
+ ai_disclosure: {
109
+ type: "object",
110
+ description: "AI disclosure object (UK AI Copyright compliance). Fields: composition, performance, production, mastering ('none'|'assisted'|'generated'), tools_used (string[]), disclosure_date, disclosed_by.",
111
+ },
112
+ provenance_attestation: {
113
+ type: "object",
114
+ description: "Human creation proof. Fields: human_created (bool), attested_by, attested_at, attestation_method, supporting_evidence, ip_address, signature_hash, certificate_id.",
115
+ },
116
+ training_rights: {
117
+ type: "object",
118
+ description: "B2B ML licensing compliance object. Fields: available_for_training (bool), training_terms, minimum_fee, excluded_uses, permitted_uses, geographic_restrictions, requires_attribution, requires_notification, license_duration, updated_at, updated_by.",
119
+ },
120
+ };
121
+ export class WorksTools {
122
+ pica;
123
+ constructor(pica) {
124
+ this.pica = pica;
125
+ }
126
+ /**
127
+ * Get all works tools
128
+ */
129
+ getTools() {
130
+ return [
131
+ {
132
+ definition: {
133
+ name: "pica_works_query",
134
+ description: "Find works by any criteria — text search, verified status, or health filter. " +
135
+ "→ then: pica_works_inspect (details for a specific work)",
136
+ workflows: "infrastructure",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: {
140
+ query: {
141
+ type: "string",
142
+ description: "Search by title, ISWC, or artist name",
143
+ },
144
+ verified: {
145
+ type: "boolean",
146
+ description: "Filter by verification status",
147
+ },
148
+ health_filter: {
149
+ type: "string",
150
+ enum: ["needs_attention", "low_completeness"],
151
+ description: "Filter by health status — needs_attention (missing credits, no ISWC) or low_completeness",
152
+ },
153
+ limit: {
154
+ type: "number",
155
+ description: "Max results (default: 50)",
156
+ },
157
+ offset: {
158
+ type: "number",
159
+ description: "Pagination offset",
160
+ },
161
+ },
162
+ },
163
+ },
164
+ executor: this.queryWorks.bind(this),
165
+ },
166
+ {
167
+ definition: {
168
+ name: "pica_works_inspect",
169
+ description: "Get details and quality data for a specific work. Omit sections for everything. " +
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, audio analysis) " +
172
+ "in one response. " +
173
+ "→ then: pica_credits_update (fix credits), pica_resolve_work (fill identifiers + metadata), pica_run_work_cascade (retry)",
174
+ workflows: "infrastructure",
175
+ inputSchema: {
176
+ type: "object",
177
+ properties: {
178
+ id: {
179
+ type: "string",
180
+ description: "Work ID",
181
+ },
182
+ sections: {
183
+ type: "array",
184
+ items: {
185
+ type: "string",
186
+ enum: [
187
+ "basic",
188
+ "credits",
189
+ "recordings",
190
+ "agreements",
191
+ "health",
192
+ "completeness",
193
+ "registration",
194
+ "score",
195
+ "provenance",
196
+ "enrichment",
197
+ "production_assets",
198
+ "releases",
199
+ ],
200
+ },
201
+ description: 'Which data sections to include. Omit for all sections. Use ["basic"] for lightweight lookup.',
202
+ },
203
+ },
204
+ required: ["id"],
205
+ },
206
+ // ADR-199 — work status state machine mirrored from
207
+ // lib/utils/state-machine.ts workStatusMachine. Build-step
208
+ // assertion (Phase D) catches drift between this map and the
209
+ // DB CHECK constraint on works.status.
210
+ validTransitionsFrom: {
211
+ demo: ["unreleased", "pitched", "archived"],
212
+ unreleased: ["released", "pitched", "archived"],
213
+ pitched: ["placed", "unreleased", "released", "archived"],
214
+ placed: ["released", "archived"],
215
+ released: ["archived"],
216
+ archived: ["unreleased", "demo"],
217
+ },
218
+ nextSteps: [
219
+ {
220
+ tool: "pica_credits_update",
221
+ reason: "Fix or fill writer/composer credits when the inspect surfaces gaps.",
222
+ when: "on_success",
223
+ },
224
+ {
225
+ tool: "pica_resolve_work",
226
+ reason: "Fill identifier gaps (ISWC, ISRC) and metadata via the enrichment cascade.",
227
+ when: "on_success",
228
+ },
229
+ ],
230
+ },
231
+ executor: this.inspectWork.bind(this),
232
+ },
233
+ {
234
+ definition: {
235
+ name: "pica_works_create",
236
+ description: "Create a new musical work. IMPORTANT: call pica_works_query first to check for duplicates — same-title works cause split and registration issues. " +
237
+ "Album and release metadata (album_name, album_art_url, track_number, upc, spotify_url) live on `pica_releases_create/update`, not here. " +
238
+ "For label metadata, set it on the release via pica_releases_*.label_organization_id (resolve the name via pica_labels_query first). " +
239
+ "After several mutations, surface a pica_dashboard_briefing to the user. " +
240
+ "→ before: pica_works_query (check duplicates) → then: pica_credits_update (add writers), pica_works_inspect (check gaps), pica_resolve_work (fill identifiers)",
241
+ workflows: ["work-required"],
242
+ inputSchema: {
243
+ type: "object",
244
+ properties: WORK_WRITE_PROPERTIES,
245
+ required: ["title", "work_type"],
246
+ },
247
+ nextSteps: [
248
+ {
249
+ tool: "pica_credits_update",
250
+ reason: "Add writer/composer credits to the new work — required before registration.",
251
+ when: "on_success",
252
+ },
253
+ {
254
+ tool: "pica_works_inspect",
255
+ reason: "Read back the created work to confirm fields persisted and surface any gaps.",
256
+ when: "on_success",
257
+ },
258
+ ],
259
+ },
260
+ executor: withBillingGate(this.pica, "work creation", this.createWork.bind(this)),
261
+ },
262
+ {
263
+ definition: {
264
+ name: "pica_works_update",
265
+ description: "Update an existing musical work. Only real `works` columns are accepted — " +
266
+ "album and release metadata (album_name, album_art_url, track_number, upc, spotify_url) must go through pica_releases_create/update, not here. " +
267
+ "Audio-trait satellite fields (energy, vocal_type, available_for_licensing, danceability) are not exposed through MCP; set them through a release or recording path. " +
268
+ "Three jsonb compliance fields (ai_disclosure, provenance_attestation, training_rights) are satellite-routed to work_licensing and ARE set-able here. " +
269
+ "→ then: pica_works_inspect (verify changes persisted)",
270
+ workflows: ["work-required"],
271
+ inputSchema: {
272
+ type: "object",
273
+ properties: {
274
+ id: {
275
+ type: "string",
276
+ description: "Work ID (required)",
277
+ },
278
+ ...WORK_WRITE_PROPERTIES,
279
+ },
280
+ required: ["id"],
281
+ },
282
+ nextSteps: [
283
+ {
284
+ tool: "pica_works_inspect",
285
+ reason: "Read back the work after update to confirm every field round-tripped — silent persistence drops are real.",
286
+ when: "on_success",
287
+ },
288
+ ],
289
+ },
290
+ executor: withBillingGate(this.pica, "work update", this.updateWork.bind(this)),
291
+ },
292
+ {
293
+ definition: {
294
+ name: "pica_works_delete",
295
+ description: "Delete a musical work",
296
+ workflows: "infrastructure",
297
+ inputSchema: {
298
+ type: "object",
299
+ properties: {
300
+ id: {
301
+ type: "string",
302
+ description: "Work ID",
303
+ },
304
+ },
305
+ required: ["id"],
306
+ },
307
+ },
308
+ executor: withBillingGate(this.pica, "work deletion", this.deleteWork.bind(this)),
309
+ },
310
+ {
311
+ definition: {
312
+ name: "pica_works_verify",
313
+ description: "Mark a work as verified",
314
+ workflows: "infrastructure",
315
+ inputSchema: {
316
+ type: "object",
317
+ properties: {
318
+ id: {
319
+ type: "string",
320
+ description: "Work ID",
321
+ },
322
+ },
323
+ required: ["id"],
324
+ },
325
+ },
326
+ executor: withBillingGate(this.pica, "work verification", this.verifyWork.bind(this)),
327
+ },
328
+ {
329
+ definition: {
330
+ name: "pica_works_bulk_delete",
331
+ description: "Delete multiple works at once",
332
+ workflows: "infrastructure",
333
+ inputSchema: {
334
+ type: "object",
335
+ properties: {
336
+ ids: {
337
+ type: "array",
338
+ description: "Array of work IDs to delete",
339
+ items: {
340
+ type: "string",
341
+ },
342
+ },
343
+ },
344
+ required: ["ids"],
345
+ },
346
+ },
347
+ executor: withBillingGate(this.pica, "bulk work deletion", this.bulkDeleteWorks.bind(this)),
348
+ },
349
+ ];
350
+ }
351
+ /**
352
+ * Query works — unified search, list, and health filter
353
+ */
354
+ async queryWorks(args) {
355
+ const limit = args.limit || 50;
356
+ // Health filter routes to health/completeness APIs
357
+ if (args.health_filter === "needs_attention") {
358
+ const result = await this.pica.health.getWorksHealth();
359
+ const works = result?.works || result || [];
360
+ return formatStructuredList(Array.isArray(works) ? works : [works], "work", {
361
+ filter: "needs_attention",
362
+ total: Array.isArray(works) ? works.length : 1,
363
+ });
364
+ }
365
+ if (args.health_filter === "low_completeness") {
366
+ const result = await this.pica.health.getLowScoreWorks();
367
+ const works = Array.isArray(result) ? result : result?.works || [];
368
+ return formatStructuredList(works, "work", {
369
+ filter: "low_completeness",
370
+ total: works.length,
371
+ });
372
+ }
373
+ // Text search or list
374
+ if (args.query) {
375
+ const result = await this.pica.works.search({ query: args.query, limit });
376
+ let items = result.data || result;
377
+ if (!Array.isArray(items))
378
+ items = [];
379
+ return formatStructuredList(items, "work", {
380
+ query: args.query,
381
+ total: result.total || items.length,
382
+ });
383
+ }
384
+ // Default: list with filters
385
+ const result = await this.pica.works.search({ query: args.search, limit });
386
+ let items = result.data || result;
387
+ if (!Array.isArray(items))
388
+ items = [];
389
+ // Client-side verified filter
390
+ if (args.verified !== undefined) {
391
+ items = items.filter((work) => work.is_verified === args.verified);
392
+ }
393
+ return formatStructuredList(items, "work", {
394
+ total: result.total || items.length,
395
+ limit,
396
+ hasMore: result.hasMore,
397
+ });
398
+ }
399
+ /**
400
+ * Inspect work — unified detail view with selectable sections
401
+ */
402
+ async inspectWork(args) {
403
+ const sections = args.sections;
404
+ const allSections = !sections || sections.length === 0;
405
+ // ADR-201 closeout (2026-04-27): `health` and `score` are org-wide
406
+ // metrics that don't take a workId and cost ~25s and ~2s respectively
407
+ // on staging. Inspect-one-work shouldn't pay that — agents that want
408
+ // org-wide metrics call them explicitly. Keep them off the default
409
+ // `allSections` surface but still callable via explicit `sections`.
410
+ const ORG_WIDE_OPT_IN = new Set(["health", "score"]);
411
+ const has = (s) => allSections ? !ORG_WIDE_OPT_IN.has(s) : sections.includes(s);
412
+ const result = {};
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";
424
+ // Basic — always fetched (either via entityContext.getWorkFull or works.get)
425
+ if (allSections) {
426
+ const full = await this.pica.entityContext.getWorkFull(args.id);
427
+ const data = full;
428
+ result.work = data?.work || data;
429
+ result.credits = data?.credits;
430
+ result.recordings = data?.recordings;
431
+ result.agreements = data?.agreements;
432
+ }
433
+ else if (has("basic")) {
434
+ const work = await this.pica.works.get(args.id);
435
+ result.work = work;
436
+ // Enrich with ISRC prefix intelligence if work has ISRC
437
+ if (work?.isrc) {
438
+ try {
439
+ result.prefix_intel = await this.pica.recordings.prefixLookup(work.isrc);
440
+ }
441
+ catch {
442
+ /* best-effort */
443
+ }
444
+ }
445
+ }
446
+ // ADR-201 closeout (2026-04-27): the section sub-calls used to run
447
+ // sequentially. With ~8 calls each taking ~1s and one (works-health)
448
+ // sometimes 25s+, the function would frequently exceed Vercel's 30s
449
+ // maxDuration on the default `allSections` path. Fan out everything
450
+ // in parallel — each branch still owns its own try/catch fallback so
451
+ // a single failing sub-call doesn't poison the whole inspect.
452
+ const sectionTasks = [];
453
+ // Targeted basic sections — only when caller listed them explicitly.
454
+ if (!allSections) {
455
+ if (has("credits") && !result.credits) {
456
+ sectionTasks.push((async () => {
457
+ try {
458
+ result.credits = await this.pica.credits.listForWork(args.id);
459
+ }
460
+ catch (e) {
461
+ result.credits = useStructured ? null : [];
462
+ pushSectionError(errors, TOOL_NAME, "credits", e, args.id);
463
+ }
464
+ })());
465
+ }
466
+ if (has("recordings") && !result.recordings) {
467
+ sectionTasks.push((async () => {
468
+ try {
469
+ result.recordings = await this.pica.recordings.getByWork(args.id);
470
+ }
471
+ catch (e) {
472
+ result.recordings = useStructured ? null : [];
473
+ pushSectionError(errors, TOOL_NAME, "recordings", e, args.id);
474
+ }
475
+ })());
476
+ }
477
+ if (has("agreements") && !result.agreements) {
478
+ sectionTasks.push((async () => {
479
+ try {
480
+ result.agreements = await this.pica.agreements.list();
481
+ }
482
+ catch (e) {
483
+ result.agreements = useStructured ? null : [];
484
+ pushSectionError(errors, TOOL_NAME, "agreements", e, args.id);
485
+ }
486
+ })());
487
+ }
488
+ }
489
+ // Quality sections — fan out in parallel.
490
+ if (has("health")) {
491
+ sectionTasks.push((async () => {
492
+ try {
493
+ result.health = await this.pica.health.getWorksHealth();
494
+ }
495
+ catch (e) {
496
+ result.health = null;
497
+ pushSectionError(errors, TOOL_NAME, "health", e, args.id);
498
+ }
499
+ })());
500
+ }
501
+ if (has("completeness")) {
502
+ sectionTasks.push((async () => {
503
+ try {
504
+ const completeness = await this.pica.health.getWorkCompleteness(args.id);
505
+ result.completeness = completeness;
506
+ hints.push(...mapGapsToHints(completeness?.gaps || completeness?.missing || []));
507
+ }
508
+ catch (e) {
509
+ result.completeness = null;
510
+ pushSectionError(errors, TOOL_NAME, "completeness", e, args.id);
511
+ }
512
+ })());
513
+ }
514
+ if (has("registration")) {
515
+ sectionTasks.push((async () => {
516
+ try {
517
+ result.registration =
518
+ await this.pica.registration.getWorkCascadeStatus(args.id);
519
+ }
520
+ catch (e) {
521
+ result.registration = null;
522
+ pushSectionError(errors, TOOL_NAME, "registration", e, args.id);
523
+ }
524
+ })());
525
+ }
526
+ if (has("score")) {
527
+ sectionTasks.push((async () => {
528
+ try {
529
+ result.score = await this.pica.picaScore.get();
530
+ }
531
+ catch (e) {
532
+ result.score = null;
533
+ pushSectionError(errors, TOOL_NAME, "score", e, args.id);
534
+ }
535
+ })());
536
+ }
537
+ if (has("provenance")) {
538
+ sectionTasks.push((async () => {
539
+ try {
540
+ result.provenance = await this.pica.analytics.provenanceWork(args.id);
541
+ }
542
+ catch (e) {
543
+ result.provenance = null;
544
+ pushSectionError(errors, TOOL_NAME, "provenance", e, args.id);
545
+ }
546
+ })());
547
+ }
548
+ if (has("enrichment")) {
549
+ sectionTasks.push((async () => {
550
+ try {
551
+ result.enrichment =
552
+ await this.pica.enrichment.getWorkEnrichmentStatus(args.id);
553
+ }
554
+ catch (e) {
555
+ result.enrichment = null;
556
+ pushSectionError(errors, TOOL_NAME, "enrichment", e, args.id);
557
+ }
558
+ })());
559
+ }
560
+ // ADR-156 op #3 Session B Task 5 — surface linked physical assets
561
+ // so the inspect tool is discoverable from the work side.
562
+ if (has("production_assets")) {
563
+ sectionTasks.push((async () => {
564
+ try {
565
+ result.production_assets =
566
+ await this.pica.works.listProductionAssets(args.id);
567
+ }
568
+ catch (e) {
569
+ result.production_assets = useStructured ? null : [];
570
+ pushSectionError(errors, TOOL_NAME, "production_assets", e, args.id);
571
+ }
572
+ })());
573
+ }
574
+ // ADR-173 Decision 2 — every release this work appears on, with
575
+ // its track position. Distinct from the `recordings` section,
576
+ // which lists recordings regardless of release context.
577
+ if (has("releases")) {
578
+ sectionTasks.push((async () => {
579
+ try {
580
+ result.releases = await this.pica.works.listReleases(args.id);
581
+ }
582
+ catch (e) {
583
+ result.releases = useStructured ? null : [];
584
+ pushSectionError(errors, TOOL_NAME, "releases", e, args.id);
585
+ }
586
+ })());
587
+ }
588
+ await Promise.all(sectionTasks);
589
+ // ADR-193 Surface 2 — proactive peer-MCP hints when the flag is on
590
+ // and a real gap exists. Additive on top of the CompletionHint path.
591
+ const gap_hints = computeWorkGapHints(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;
600
+ return formatSuccess(`Work details${sections ? ` (sections: ${sections.join(", ")})` : ""}`, enriched, hints);
601
+ }
602
+ /**
603
+ * Create work
604
+ */
605
+ async createWork(args) {
606
+ const work = await this.pica.works.create(args);
607
+ // Generate completion hints for the newly created work
608
+ const hints = [];
609
+ if (work?.id) {
610
+ try {
611
+ const completeness = await this.pica.health.getWorkCompleteness(work.id);
612
+ hints.push(...mapGapsToHints(completeness?.gaps || completeness?.missing || []));
613
+ }
614
+ catch {
615
+ // Best-effort — don't fail the create
616
+ }
617
+ // Hint about AI disclosure if not set
618
+ if (!args.ai_disclosure) {
619
+ hints.push({
620
+ gap: "no_ai_disclosure",
621
+ suggestion: "Set AI disclosure — is this work human-made, AI-assisted, or AI-generated? (pica_works_update with ai_disclosure: none/assisted/generated)",
622
+ severity: "important",
623
+ });
624
+ }
625
+ }
626
+ // ADR-158 Plan C Phase 2 — append custody-claim hints so every new work
627
+ // prompts the rights conversation by default.
628
+ hints.push(...buildCustodyHints(work));
629
+ return formatSuccess("Work created successfully", work, hints);
630
+ }
631
+ /**
632
+ * Update work
633
+ */
634
+ async updateWork(args) {
635
+ const { id, ...updates } = args;
636
+ const work = await this.pica.works.update(id, updates);
637
+ return formatSuccess("Work updated successfully", work);
638
+ }
639
+ /**
640
+ * Delete work
641
+ */
642
+ async deleteWork(args) {
643
+ await this.pica.works.delete(args.id);
644
+ return formatSuccess(`Work ${args.id} deleted successfully`);
645
+ }
646
+ /**
647
+ * Verify work
648
+ */
649
+ async verifyWork(args) {
650
+ const work = await this.pica.works.verify(args.id);
651
+ // ADR-146: Also check export readiness
652
+ let exportReadiness;
653
+ try {
654
+ const baseUrl = this.pica.works["baseUrl"];
655
+ const apiKey = this.pica.works["apiKey"];
656
+ const res = await fetch(`${baseUrl}/admin/export-readiness/${args.id}`, {
657
+ headers: {
658
+ Authorization: `Bearer ${apiKey}`,
659
+ "Content-Type": "application/json",
660
+ },
661
+ });
662
+ if (res.ok) {
663
+ const json = (await res.json());
664
+ exportReadiness = json.data ?? json;
665
+ }
666
+ }
667
+ catch {
668
+ // Readiness check is best-effort — don't fail verification
669
+ }
670
+ const summaryParts = ["Work verified successfully"];
671
+ if (exportReadiness) {
672
+ summaryParts.push(`Export readiness: ${exportReadiness.ready ? "ready" : "not ready"}`, `Evidence: ${exportReadiness.evidenceStrength}`, `Attestation: ${exportReadiness.attestationCoverage}%`);
673
+ if (Array.isArray(exportReadiness.blockers) &&
674
+ exportReadiness.blockers.length > 0) {
675
+ summaryParts.push(`Blockers (${exportReadiness.blockers.length}):`);
676
+ for (const b of exportReadiness.blockers) {
677
+ summaryParts.push(` ${b.type}: ${b.resolutionHint}`);
678
+ }
679
+ }
680
+ }
681
+ return formatSuccess(summaryParts.join("\n"), {
682
+ ...work,
683
+ exportReadiness,
684
+ });
685
+ }
686
+ /**
687
+ * Bulk delete works
688
+ */
689
+ async bulkDeleteWorks(args) {
690
+ await this.pica.works.bulkDelete(args.ids);
691
+ return formatSuccess(`${args.ids.length} works deleted successfully`);
692
+ }
693
+ }
694
+ //# sourceMappingURL=works.js.map