@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,1182 @@
1
+ // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
+ import { WorksTools } from "./works.js";
3
+ import { PeopleTools } from "./people.js";
4
+ import { GroupsTools } from "./groups.js";
5
+ import { RecordingsTools } from "./recordings.js";
6
+ import { SearchTools } from "./search.js";
7
+ import { LicensingTools } from "./licensing.js";
8
+ import { CreditsTools } from "./credits.js";
9
+ import { AudioFilesTools } from "./audio-files.js";
10
+ import { MultimediaTools } from "./multimedia.js";
11
+ import { AgreementsTools } from "./agreements.js";
12
+ import { MemoryTools } from "./memory.js";
13
+ import { EnrichmentTools } from "./enrichment.js";
14
+ import { BulkTools } from "./bulk.js";
15
+ import { ExportTools } from "./exports.js";
16
+ import { DuplicatesTools, coerceMergeEntityType } from "./duplicates.js";
17
+ import { DirectoryTools } from "./directory.js";
18
+ import { CollaboratorsTools } from "./collaborators.js";
19
+ import { ImportDocumentTools } from "./import-documents.js";
20
+ import { UploadTools } from "./uploads.js";
21
+ import { DocumentTools } from "./documents.js";
22
+ import { ImportTools } from "./import.js";
23
+ import { SendTools } from "./send.js";
24
+ import { ComparisonTools } from "./comparisons.js";
25
+ import { AssetsTools } from "./assets.js";
26
+ import { SessionsTools } from "./sessions.js";
27
+ import { AnalyticsTools } from "./analytics.js";
28
+ import { NotificationsTools } from "./notifications.js";
29
+ import { NotesTools } from "./notes.js";
30
+ import { TeamTools } from "./team.js";
31
+ import { ProjectsTools } from "./projects.js";
32
+ import { ReleasesTools } from "./releases.js";
33
+ import { ReleaseRichTools } from "./release-rich.js";
34
+ import { CalendarTools } from "./calendar.js";
35
+ import { SplitSheetsTools } from "./split-sheets.js";
36
+ import { AgreementTypesTools } from "./agreement-types.js";
37
+ import { DashboardTools } from "./dashboard.js";
38
+ import { IntegrationsTools } from "./integrations.js";
39
+ import { SettingsTools } from "./settings.js";
40
+ import { FeedbackTools } from "./feedback.js";
41
+ import { StorageConfigTools } from "./storage-config.js";
42
+ import { SubscriptionTools } from "./subscription.js";
43
+ import { OnboardingTools } from "./onboarding.js";
44
+ import { ReportIssueTools } from "./report-issue.js";
45
+ import { MyReportedIssuesTools } from "./my-reported-issues.js";
46
+ import { AgentIdentityTools } from "./agent-identity.js";
47
+ import { RoyaltiesTools } from "./royalties.js";
48
+ import { ShareLinksTools } from "./share-links.js";
49
+ import { DisputesTools } from "./disputes.js";
50
+ import { CustodyTools } from "./custody.js";
51
+ import { PurchasesTools } from "./purchases.js";
52
+ import { TelegramTools } from "./telegram.js";
53
+ import { PublishersTools } from "./publishers.js";
54
+ import { LabelsTools } from "./labels.js";
55
+ import { AuthTools } from "./auth.js";
56
+ import { SignupTools } from "./signup.js";
57
+ import { AppTools } from "./app-tools.js";
58
+ import { AuditTools } from "./audit.js";
59
+ import { DiscoveryTools } from "./discovery.js";
60
+ import { SyncPlacementsTools } from "./sync-placements.js";
61
+ import { readCredentials } from "@withpica/mcp-utils";
62
+ import { formatError, ToolExecutionError } from "@withpica/mcp-utils";
63
+ import { guardResponseSize } from "@withpica/mcp-utils";
64
+ import { getToolMetadata } from "./metadata.js";
65
+ import { getRecoveryHint } from "./recovery-hints.js";
66
+ import { generateConfirmationToken, validateAndConsumeToken, } from "@withpica/mcp-utils";
67
+ import { buildSessionState, incrementSessionMutations, resetSinceLastBriefing, } from "@withpica/mcp-utils";
68
+ /**
69
+ * Build a tool description with metadata injected.
70
+ * Format: "[category] original description"
71
+ */
72
+ export function injectMetadataIntoDescription(description, metadata) {
73
+ // Category tag helps the agent understand tool grouping.
74
+ // Risk prefixes removed — annotations (readOnlyHint, destructiveHint) handle this structurally.
75
+ // Behavioral guidance (tell user what you're doing) is in server instructions (one copy).
76
+ return `[${metadata.category}] ${description}`;
77
+ }
78
+ export const CATEGORY_DISPLAY = {
79
+ catalog: "manage your catalog",
80
+ enrichment: "enrich your metadata",
81
+ business: "handle business and agreements",
82
+ discovery: "search and explore",
83
+ media: "manage photos, audio, and video",
84
+ comms: "send and share",
85
+ settings: "manage your account and team",
86
+ };
87
+ export class ToolRegistry {
88
+ tools;
89
+ pica;
90
+ config;
91
+ reinitializeCallback;
92
+ signOutCallback;
93
+ auditLogger;
94
+ callerContext;
95
+ constructor(pica, config, reinitializeCallback, callerContext, signOutCallback) {
96
+ this.pica = pica;
97
+ this.config = config;
98
+ this.reinitializeCallback = reinitializeCallback;
99
+ this.signOutCallback = signOutCallback;
100
+ this.callerContext = callerContext ?? {
101
+ callerIdentity: "unknown",
102
+ transport: "stdio",
103
+ };
104
+ this.tools = new Map();
105
+ this.registerAllTools();
106
+ }
107
+ setAuditLogger(logger) {
108
+ this.auditLogger = logger;
109
+ }
110
+ setCallerContext(context) {
111
+ this.callerContext = context;
112
+ }
113
+ /**
114
+ * Register all available tools
115
+ */
116
+ registerAllTools() {
117
+ // Auth tools — always registered (lobby + authenticated mode)
118
+ if (this.config) {
119
+ const authTools = new AuthTools(this.config, this.reinitializeCallback || (() => { }), this.signOutCallback || (() => { }));
120
+ authTools.getTools().forEach((tool) => {
121
+ this.tools.set(tool.definition.name, tool);
122
+ });
123
+ // ADR-211 P1 — Signup tool. Registered in lobby alongside the auth
124
+ // pair so unauthenticated callers (stdio without API key, HTTP
125
+ // without Bearer) have a discoverable signup verb. Stays registered
126
+ // post-auth too — the tool itself is harmless once you're signed in
127
+ // (mints a link for a new account, not your existing one) and that
128
+ // matches the ADR-211 § Primitive A 2026-04-30 amendment which
129
+ // makes Phase 1 lobby additive (3 tools), not replacement.
130
+ //
131
+ // Transport is read from callerContext so stdio + HTTP transports
132
+ // stamp their JWTs correctly without per-call config plumbing.
133
+ const signupTools = new SignupTools({
134
+ apiUrl: this.config.picaApiUrl,
135
+ transport: this.callerContext.transport,
136
+ });
137
+ signupTools.getTools().forEach((tool) => {
138
+ this.tools.set(tool.definition.name, tool);
139
+ });
140
+ }
141
+ // In lobby mode, only auth + signup tools are available
142
+ if (this.config?.lobbyMode || !this.pica) {
143
+ return;
144
+ }
145
+ const pica = this.pica;
146
+ // Works tools
147
+ const worksTools = new WorksTools(pica);
148
+ worksTools.getTools().forEach((tool) => {
149
+ this.tools.set(tool.definition.name, tool);
150
+ });
151
+ // People tools
152
+ const peopleTools = new PeopleTools(pica);
153
+ peopleTools.getTools().forEach((tool) => {
154
+ this.tools.set(tool.definition.name, tool);
155
+ });
156
+ // Groups tools (ADR-223)
157
+ const groupsTools = new GroupsTools(pica);
158
+ groupsTools.getTools().forEach((tool) => {
159
+ this.tools.set(tool.definition.name, tool);
160
+ });
161
+ // Recordings tools
162
+ const recordingsTools = new RecordingsTools(pica);
163
+ recordingsTools.getTools().forEach((tool) => {
164
+ this.tools.set(tool.definition.name, tool);
165
+ });
166
+ // Search tools
167
+ const searchTools = new SearchTools(pica);
168
+ searchTools.getTools().forEach((tool) => {
169
+ this.tools.set(tool.definition.name, tool);
170
+ });
171
+ // Licensing tools
172
+ const licensingTools = new LicensingTools(pica);
173
+ licensingTools.getTools().forEach((tool) => {
174
+ this.tools.set(tool.definition.name, tool);
175
+ });
176
+ // Credits tools
177
+ const creditsTools = new CreditsTools(pica);
178
+ creditsTools.getTools().forEach((tool) => {
179
+ this.tools.set(tool.definition.name, tool);
180
+ });
181
+ // Audio Files tools
182
+ const audioFilesTools = new AudioFilesTools(pica);
183
+ audioFilesTools.getTools().forEach((tool) => {
184
+ this.tools.set(tool.definition.name, tool);
185
+ });
186
+ // Multimedia tools
187
+ const multimediaTools = new MultimediaTools(pica);
188
+ multimediaTools.getTools().forEach((tool) => {
189
+ this.tools.set(tool.definition.name, tool);
190
+ });
191
+ // Agreements tools
192
+ const agreementsTools = new AgreementsTools(pica);
193
+ agreementsTools.getTools().forEach((tool) => {
194
+ this.tools.set(tool.definition.name, tool);
195
+ });
196
+ // Memory tools
197
+ const memoryTools = new MemoryTools(pica);
198
+ memoryTools.getTools().forEach((tool) => {
199
+ this.tools.set(tool.definition.name, tool);
200
+ });
201
+ // Enrichment tools
202
+ const enrichmentTools = new EnrichmentTools(pica);
203
+ enrichmentTools.getTools().forEach((tool) => {
204
+ this.tools.set(tool.definition.name, tool);
205
+ });
206
+ // Bulk operations tools
207
+ const bulkTools = new BulkTools(pica);
208
+ bulkTools.getTools().forEach((tool) => {
209
+ this.tools.set(tool.definition.name, tool);
210
+ });
211
+ // Export tools
212
+ const exportTools = new ExportTools(pica);
213
+ exportTools.getTools().forEach((tool) => {
214
+ this.tools.set(tool.definition.name, tool);
215
+ });
216
+ // Duplicates tools
217
+ const duplicatesTools = new DuplicatesTools(pica);
218
+ duplicatesTools.getTools().forEach((tool) => {
219
+ this.tools.set(tool.definition.name, tool);
220
+ });
221
+ // Directory tools
222
+ const directoryTools = new DirectoryTools(pica);
223
+ directoryTools.getTools().forEach((tool) => {
224
+ this.tools.set(tool.definition.name, tool);
225
+ });
226
+ // Collaborator tools
227
+ const collaboratorsTools = new CollaboratorsTools(pica);
228
+ collaboratorsTools.getTools().forEach((tool) => {
229
+ this.tools.set(tool.definition.name, tool);
230
+ });
231
+ // Upload tools (generic file upload — images, videos, documents)
232
+ const uploadTools = new UploadTools(pica);
233
+ uploadTools.getTools().forEach((tool) => {
234
+ this.tools.set(tool.definition.name, tool);
235
+ });
236
+ // Document tools (AI analysis)
237
+ const documentTools = new DocumentTools(pica);
238
+ documentTools.getTools().forEach((tool) => {
239
+ this.tools.set(tool.definition.name, tool);
240
+ });
241
+ // Bulk import tools (CSV import pipeline)
242
+ const importTools = new ImportTools(pica);
243
+ importTools.getTools().forEach((tool) => {
244
+ this.tools.set(tool.definition.name, tool);
245
+ });
246
+ // Comparison tools (ADR-116 Phase 2: enrichment + registration comparison)
247
+ const comparisonTools = new ComparisonTools(pica);
248
+ comparisonTools.getTools().forEach((tool) => {
249
+ this.tools.set(tool.definition.name, tool);
250
+ });
251
+ // Send Hub tools (messages, invites, file requests, audio shares)
252
+ const sendTools = new SendTools(pica);
253
+ sendTools.getTools().forEach((tool) => {
254
+ this.tools.set(tool.definition.name, tool);
255
+ });
256
+ // Import document tools
257
+ const importDocumentTools = new ImportDocumentTools(pica);
258
+ importDocumentTools.getTools().forEach((tool) => {
259
+ this.tools.set(tool.definition.name, tool);
260
+ });
261
+ // Physical assets tools (equipment, instruments, studio gear)
262
+ const assetsTools = new AssetsTools(pica);
263
+ assetsTools.getTools().forEach((tool) => {
264
+ this.tools.set(tool.definition.name, tool);
265
+ });
266
+ // Work sessions tools (studio session tracking)
267
+ const sessionsTools = new SessionsTools(pica);
268
+ sessionsTools.getTools().forEach((tool) => {
269
+ this.tools.set(tool.definition.name, tool);
270
+ });
271
+ // Analytics tools (carbon, provenance, diligence)
272
+ const analyticsTools = new AnalyticsTools(pica);
273
+ analyticsTools.getTools().forEach((tool) => {
274
+ this.tools.set(tool.definition.name, tool);
275
+ });
276
+ // Notifications tools
277
+ const notificationsTools = new NotificationsTools(pica);
278
+ notificationsTools.getTools().forEach((tool) => {
279
+ this.tools.set(tool.definition.name, tool);
280
+ });
281
+ // Notes tools (catalog notes on works, people, general observations)
282
+ const notesTools = new NotesTools(pica);
283
+ notesTools.getTools().forEach((tool) => {
284
+ this.tools.set(tool.definition.name, tool);
285
+ });
286
+ // Team management tools (invite, list, update, remove members)
287
+ const teamTools = new TeamTools(pica);
288
+ teamTools.getTools().forEach((tool) => {
289
+ this.tools.set(tool.definition.name, tool);
290
+ });
291
+ // Projects tools (albums, EPs, compilations — grouping works)
292
+ const projectsTools = new ProjectsTools(pica);
293
+ projectsTools.getTools().forEach((tool) => {
294
+ this.tools.set(tool.definition.name, tool);
295
+ });
296
+ // Releases tools (albums, EPs, singles with dates, labels, UPCs)
297
+ const releasesTools = new ReleasesTools(pica);
298
+ releasesTools.getTools().forEach((tool) => {
299
+ this.tools.set(tool.definition.name, tool);
300
+ });
301
+ // ADR-208 Phase 3 Primitive D — release rich render (image + markdown)
302
+ const releaseRichTools = new ReleaseRichTools(pica);
303
+ releaseRichTools.getTools().forEach((tool) => {
304
+ this.tools.set(tool.definition.name, tool);
305
+ });
306
+ // Calendar tools
307
+ const calendarTools = new CalendarTools(pica);
308
+ calendarTools.getTools().forEach((tool) => {
309
+ this.tools.set(tool.definition.name, tool);
310
+ });
311
+ // Split sheets & recording splits tools
312
+ const splitSheetsTools = new SplitSheetsTools(pica);
313
+ splitSheetsTools.getTools().forEach((tool) => {
314
+ this.tools.set(tool.definition.name, tool);
315
+ });
316
+ // Agreement types tools (templates, producer agreements, work-for-hire)
317
+ const agreementTypesTools = new AgreementTypesTools(pica);
318
+ agreementTypesTools.getTools().forEach((tool) => {
319
+ this.tools.set(tool.definition.name, tool);
320
+ });
321
+ // Dashboard & discovery tools
322
+ const dashboardTools = new DashboardTools(pica);
323
+ dashboardTools.getTools().forEach((tool) => {
324
+ this.tools.set(tool.definition.name, tool);
325
+ });
326
+ // Integrations tools (connection status)
327
+ const integrationsTools = new IntegrationsTools(pica);
328
+ integrationsTools.getTools().forEach((tool) => {
329
+ this.tools.set(tool.definition.name, tool);
330
+ });
331
+ // Settings tools (credits history, storage, org profile)
332
+ const settingsTools = new SettingsTools(pica);
333
+ settingsTools.getTools().forEach((tool) => {
334
+ this.tools.set(tool.definition.name, tool);
335
+ });
336
+ // Feedback tools (ADR-184 slice 8 — pica_submit_feedback)
337
+ const feedbackTools = new FeedbackTools(pica);
338
+ feedbackTools.getTools().forEach((tool) => {
339
+ this.tools.set(tool.definition.name, tool);
340
+ });
341
+ // Storage config tools (ADR-217 — Primitives B + C; Primitive A
342
+ // pica_storage_status lives in settings.ts because it shipped earlier
343
+ // alongside the original settings cluster).
344
+ const storageConfigTools = new StorageConfigTools(pica);
345
+ storageConfigTools.getTools().forEach((tool) => {
346
+ this.tools.set(tool.definition.name, tool);
347
+ });
348
+ // Subscription tools (ADR-210 Phase 2 — pica_subscription_status +
349
+ // pica_subscription_manage). Explicit billing read + action surface
350
+ // complementing the ambient _meta.session_state.billing_slice from
351
+ // Phase 1.
352
+ const subscriptionTools = new SubscriptionTools(pica);
353
+ subscriptionTools.getTools().forEach((tool) => {
354
+ this.tools.set(tool.definition.name, tool);
355
+ });
356
+ // Onboarding tools — pica_acknowledge_onboarding gates OAuth-arrived
357
+ // users until the agent walks them through identity/security/intent.
358
+ const onboardingTools = new OnboardingTools(pica);
359
+ onboardingTools.getTools().forEach((tool) => {
360
+ this.tools.set(tool.definition.name, tool);
361
+ });
362
+ // Report-issue tool (ADR-196 Phase 3 — pica_report_issue)
363
+ const reportIssueTools = new ReportIssueTools(pica);
364
+ reportIssueTools.getTools().forEach((tool) => {
365
+ this.tools.set(tool.definition.name, tool);
366
+ });
367
+ // My-reported-issues tool (ADR-196 Phase 4a — pica_my_reported_issues)
368
+ // — closed-loop counterpart to pica_report_issue. Org-scoped polling.
369
+ const myReportedIssuesTools = new MyReportedIssuesTools(pica);
370
+ myReportedIssuesTools.getTools().forEach((tool) => {
371
+ this.tools.set(tool.definition.name, tool);
372
+ });
373
+ // Agent identity tools (ADR-185 Part 1 — session-auth-only MCP surface).
374
+ // The three tools refuse grant-auth callers at the HTTP API layer;
375
+ // the stdio path carries an API key so behaviour is identical either
376
+ // way.
377
+ const agentIdentityTools = new AgentIdentityTools(pica);
378
+ agentIdentityTools.getTools().forEach((tool) => {
379
+ this.tools.set(tool.definition.name, tool);
380
+ });
381
+ // Royalties & statements tools (ADR-139 Step 2)
382
+ const royaltiesTools = new RoyaltiesTools(pica);
383
+ royaltiesTools.getTools().forEach((tool) => {
384
+ this.tools.set(tool.definition.name, tool);
385
+ });
386
+ // Share links tools (ADR-139 Step 3)
387
+ const shareLinksTools = new ShareLinksTools(pica);
388
+ shareLinksTools.getTools().forEach((tool) => {
389
+ this.tools.set(tool.definition.name, tool);
390
+ });
391
+ // Disputes tools (ADR-139 Step 3)
392
+ const disputesTools = new DisputesTools(pica);
393
+ disputesTools.getTools().forEach((tool) => {
394
+ this.tools.set(tool.definition.name, tool);
395
+ });
396
+ // Custody tools (ADR-158 Plan C)
397
+ const custodyTools = new CustodyTools(pica);
398
+ custodyTools.getTools().forEach((tool) => {
399
+ this.tools.set(tool.definition.name, tool);
400
+ });
401
+ // Purchases tools (credit checkout)
402
+ const purchasesTools = new PurchasesTools(pica);
403
+ purchasesTools.getTools().forEach((tool) => {
404
+ this.tools.set(tool.definition.name, tool);
405
+ });
406
+ // Telegram tools (user notification channel)
407
+ const telegramTools = new TelegramTools(pica);
408
+ telegramTools.getTools().forEach((tool) => {
409
+ this.tools.set(tool.definition.name, tool);
410
+ });
411
+ // Publishers tools (lookup + create)
412
+ const publishersTools = new PublishersTools(pica);
413
+ publishersTools.getTools().forEach((tool) => {
414
+ this.tools.set(tool.definition.name, tool);
415
+ });
416
+ // Labels tools (read-only lookup; ADR-174 Decision 3)
417
+ const labelsTools = new LabelsTools(pica);
418
+ labelsTools.getTools().forEach((tool) => {
419
+ this.tools.set(tool.definition.name, tool);
420
+ });
421
+ // App tools (MCP Apps — interactive UI cards)
422
+ const appTools = new AppTools(pica);
423
+ appTools.getTools().forEach((tool) => {
424
+ this.tools.set(tool.definition.name, tool);
425
+ });
426
+ // Audit tools (recent MCP tool executions — ADR-154 F6)
427
+ // ADR-181 Phase 1: ported from the (now-retired)
428
+ // @withpica/mcp-server-settings sub-server as part of the customer
429
+ // MCP surface consolidation. The sub-server was the only place this
430
+ // tool shipped before consolidation; every other sub-server tool
431
+ // already existed in mcp-server/ so only pica_audit_list had to move.
432
+ // Phase 3 (2026-04-19) tombstoned the sub-package on npm + registry
433
+ // and deleted the source tree.
434
+ const auditTools = new AuditTools(pica);
435
+ auditTools.getTools().forEach((tool) => {
436
+ this.tools.set(tool.definition.name, tool);
437
+ });
438
+ // ADR-222 sync placements
439
+ const syncPlacementsTools = new SyncPlacementsTools(pica);
440
+ syncPlacementsTools.getTools().forEach((tool) => {
441
+ this.tools.set(tool.definition.name, tool);
442
+ });
443
+ // Discovery meta-tools — registered last so pica_tool_details can look up
444
+ // all tool definitions. Only active when discoveryMode is enabled.
445
+ if (this.config?.discoveryMode) {
446
+ const discoveryTools = new DiscoveryTools((name, args, ctx) => this.executeTool(name, args, ctx), (name) => this.tools.get(name));
447
+ discoveryTools.getTools().forEach((tool) => {
448
+ this.tools.set(tool.definition.name, tool);
449
+ });
450
+ }
451
+ }
452
+ // ── Write-safety classification ──
453
+ // Destructive tools require confirmation before AND summary after.
454
+ // Mutating tools should present what they'll do and confirm after.
455
+ // Safe (read-only) tools need no confirmation.
456
+ static DESTRUCTIVE_PATTERNS = [
457
+ "_delete",
458
+ "_bulk_delete",
459
+ "_merge",
460
+ "_remove",
461
+ "_disconnect",
462
+ ];
463
+ static MUTATING_PATTERNS = [
464
+ "_create",
465
+ "_update",
466
+ "_bulk_update",
467
+ "_invite",
468
+ "_link",
469
+ "_send_",
470
+ "_import_",
471
+ "_execute",
472
+ "_ingest",
473
+ "_enrich",
474
+ "_verify",
475
+ "_mark_",
476
+ "_review",
477
+ "_purchase",
478
+ "_submit",
479
+ "_generate",
480
+ "_set_default",
481
+ "_duplicate",
482
+ "_toggle",
483
+ "_save",
484
+ "_upload",
485
+ "_complete",
486
+ "_analyze",
487
+ "_analyse",
488
+ "_identify",
489
+ "_resend",
490
+ "_notify",
491
+ ];
492
+ // Read-only tools that match mutating patterns by name but are actually safe
493
+ static SAFE_OVERRIDES = new Set([
494
+ "pica_collaborators_invites_list",
495
+ "pica_enrichment_candidates",
496
+ "pica_enrichment_compare",
497
+ "pica_find_duplicates",
498
+ "pica_import_analyze",
499
+ "pica_import_validate",
500
+ "pica_import_fields",
501
+ "pica_import_template",
502
+ "pica_import_documents_query",
503
+ "pica_import_documents_inspect",
504
+ "pica_send_query",
505
+ "pica_share_links_list",
506
+ ]);
507
+ classifyTool(name) {
508
+ if (ToolRegistry.SAFE_OVERRIDES.has(name)) {
509
+ return "safe";
510
+ }
511
+ if (ToolRegistry.DESTRUCTIVE_PATTERNS.some((p) => name.includes(p))) {
512
+ return "destructive";
513
+ }
514
+ if (ToolRegistry.MUTATING_PATTERNS.some((p) => name.includes(p))) {
515
+ return "mutating";
516
+ }
517
+ return "safe";
518
+ }
519
+ /**
520
+ * List all available tools with write-safety prefixes injected.
521
+ * When discoveryMode is enabled, only the 5 handshake-visible tools are returned.
522
+ * All other tools remain registered and callable via pica_execute.
523
+ */
524
+ listTools() {
525
+ let toolsToList = Array.from(this.tools.values());
526
+ if (this.config?.discoveryMode) {
527
+ const META_TOOL_NAMES = new Set([
528
+ "pica_sign_in",
529
+ "pica_sign_out",
530
+ "pica_discover",
531
+ "pica_tool_details",
532
+ "pica_execute",
533
+ ]);
534
+ toolsToList = toolsToList.filter((t) => META_TOOL_NAMES.has(t.definition.name));
535
+ }
536
+ return toolsToList.map((tool) => {
537
+ let definition = tool.definition;
538
+ const metadata = getToolMetadata(definition.name);
539
+ // Inject confirmation_token into destructive tool schemas
540
+ const isDestructive = metadata?.risk === "destructive" ||
541
+ (!metadata && this.classifyTool(definition.name) === "destructive");
542
+ if (isDestructive) {
543
+ definition = {
544
+ ...definition,
545
+ inputSchema: {
546
+ ...definition.inputSchema,
547
+ properties: {
548
+ ...definition.inputSchema.properties,
549
+ confirmation_token: {
550
+ type: "string",
551
+ description: "Confirmation token from a previous call. Required to execute destructive operations. " +
552
+ "Call this tool once without a token to get a preview and token, then call again with the token to confirm.",
553
+ },
554
+ },
555
+ },
556
+ };
557
+ }
558
+ // Normalize _meta.ui.resourceUri → legacy "ui/resourceUri" key for older MCP Apps clients
559
+ if (definition._meta?.ui?.resourceUri &&
560
+ !definition._meta["ui/resourceUri"]) {
561
+ definition = {
562
+ ...definition,
563
+ _meta: {
564
+ ...definition._meta,
565
+ "ui/resourceUri": definition._meta.ui.resourceUri,
566
+ },
567
+ };
568
+ }
569
+ if (metadata) {
570
+ return {
571
+ ...definition,
572
+ description: injectMetadataIntoDescription(tool.definition.description, metadata),
573
+ annotations: {
574
+ title: metadata.display_name,
575
+ readOnlyHint: metadata.risk === "safe",
576
+ destructiveHint: metadata.risk === "destructive",
577
+ idempotentHint: metadata.risk !== "destructive" && metadata.retry_safe,
578
+ openWorldHint: false,
579
+ // ADR-199 extended annotation classes — read by ChatGPT/Claude.ai
580
+ // connectors directly. Derived from existing metadata so existing
581
+ // tools get this for free without per-tool edits.
582
+ ...this.deriveExtendedAnnotations(definition, metadata),
583
+ },
584
+ };
585
+ }
586
+ // Fallback to old pattern-matching for any tool without explicit metadata
587
+ const classification = this.classifyTool(definition.name);
588
+ const annotations = {
589
+ title: definition.name,
590
+ readOnlyHint: classification === "safe",
591
+ destructiveHint: classification === "destructive",
592
+ idempotentHint: false,
593
+ openWorldHint: false,
594
+ // ADR-199: derive the extended classes even without explicit
595
+ // metadata so the fallback tools also surface category/risk to
596
+ // ChatGPT/Claude.ai connectors.
597
+ ...this.deriveExtendedAnnotations(definition, null, classification),
598
+ };
599
+ // No risk prefix needed — annotations handle this structurally
600
+ return { ...definition, annotations };
601
+ });
602
+ }
603
+ /**
604
+ * ADR-199 — derive the extended annotation classes (riskLevel, category,
605
+ * requiresConfirmation, createsExternalSideEffects, returnsPaginated,
606
+ * requiresFollowUpInspection) from existing metadata + tool name patterns.
607
+ *
608
+ * Called from listTools() for every tool. Pure derivation: zero per-tool
609
+ * edits required for the retrofit. Future tool authors can override by
610
+ * setting fields directly on definition.annotations.
611
+ */
612
+ deriveExtendedAnnotations(definition, metadata, classification) {
613
+ const name = definition.name;
614
+ const risk = metadata?.risk ?? classification ?? "safe";
615
+ // Side-effecting external systems: telegram, send-hub, comms, broadcasts.
616
+ // Pattern-derived so new tools inherit without edits.
617
+ const externalSideEffectPatterns = [
618
+ "telegram",
619
+ "_send_",
620
+ "_send",
621
+ "send_message",
622
+ "send_resend",
623
+ "team_comms_send",
624
+ "_notify_user",
625
+ "_invite",
626
+ ];
627
+ const createsExternalSideEffects = externalSideEffectPatterns.some((p) => name.includes(p));
628
+ // Paginated reads: every *_query and *_list tool.
629
+ const returnsPaginated = /_query$|_list$|_search/.test(name);
630
+ // Tools that mutate state are followed by a verifying inspect call when
631
+ // the agent wants to confirm — the gap report flagged this as a useful
632
+ // class for ChatGPT to surface in the UI.
633
+ const requiresFollowUpInspection = risk === "mutating" || risk === "destructive";
634
+ return {
635
+ riskLevel: risk,
636
+ category: metadata?.category,
637
+ requiresConfirmation: risk === "destructive",
638
+ createsExternalSideEffects,
639
+ returnsPaginated,
640
+ requiresFollowUpInspection,
641
+ };
642
+ }
643
+ /**
644
+ * Build a human-readable preview of what a destructive operation will affect.
645
+ */
646
+ async buildDestructivePreview(name, args) {
647
+ const metadata = getToolMetadata(name);
648
+ const displayName = metadata?.display_name || name;
649
+ // buildDestructivePreview is only called during tool execution,
650
+ // which is gated behind lobby mode check — pica is always available here.
651
+ const pica = this.pica;
652
+ try {
653
+ switch (name) {
654
+ case "pica_works_delete": {
655
+ const work = await pica.works.get(args.id).catch(() => null);
656
+ const title = work?.title || args.id;
657
+ return {
658
+ action: "delete work",
659
+ target: title,
660
+ warning: "this will remove linked credits, agreement links, and audio files. recordings will be unlinked. this cannot be undone for works without verified identifiers.",
661
+ display_name: displayName,
662
+ };
663
+ }
664
+ case "pica_works_bulk_delete": {
665
+ const count = args.ids?.length || 0;
666
+ return {
667
+ action: "bulk delete works",
668
+ target: `${count} works`,
669
+ warning: `this will delete ${count} work(s) and cascade to their credits and agreement links. this cannot be undone for works without verified identifiers.`,
670
+ display_name: displayName,
671
+ };
672
+ }
673
+ case "pica_people_delete": {
674
+ const person = await pica.people.get(args.id).catch(() => null);
675
+ const personName = person?.first_name
676
+ ? `${person.first_name} ${person.last_name || ""}`.trim()
677
+ : args.id;
678
+ return {
679
+ action: "remove person",
680
+ target: personName,
681
+ warning: "the person record will be soft-deleted. credits and agreements will remain linked.",
682
+ display_name: displayName,
683
+ };
684
+ }
685
+ case "pica_agreements_delete": {
686
+ const agreementResult = await pica.agreements
687
+ .get(args.id)
688
+ .catch(() => null);
689
+ const title = agreementResult?.agreement?.title || args.id;
690
+ return {
691
+ action: "delete agreement",
692
+ target: title,
693
+ warning: "this will remove all linked work and party records. this cannot be undone.",
694
+ display_name: displayName,
695
+ };
696
+ }
697
+ case "pica_merge_duplicates": {
698
+ const loserCount = args.loser_ids?.length || 0;
699
+ let entityType = "entities";
700
+ try {
701
+ entityType = coerceMergeEntityType(args.entity_type).canonical;
702
+ }
703
+ catch {
704
+ // Unknown entity_type — fall back to generic noun in the warning
705
+ // copy. Tool execution itself will surface the validation error.
706
+ }
707
+ return {
708
+ action: `merge ${entityType}`,
709
+ target: `${loserCount} ${entityType} into winner`,
710
+ warning: `all credits, recordings, and agreements from ${loserCount} loser(s) will be moved to the winner. loser records will be permanently deleted. this cannot be undone.`,
711
+ display_name: displayName,
712
+ };
713
+ }
714
+ case "pica_projects_delete": {
715
+ const project = await pica.projects?.get(args.id).catch(() => null);
716
+ const projectName = project?.name || args.id;
717
+ return {
718
+ action: "delete project",
719
+ target: projectName,
720
+ warning: "linked works will be unlinked but not deleted. this cannot be undone.",
721
+ display_name: displayName,
722
+ };
723
+ }
724
+ case "pica_team_remove": {
725
+ return {
726
+ action: "remove team member",
727
+ target: args.id,
728
+ warning: "their user account will be permanently deleted. works and agreements they contributed will remain. this cannot be undone.",
729
+ display_name: displayName,
730
+ };
731
+ }
732
+ // ── ADR-199 Stream 5b — 7 fall-through cases now specific ──
733
+ case "pica_recordings_delete": {
734
+ const recording = await pica.recordings
735
+ ?.get?.(args.id)
736
+ .catch(() => null);
737
+ const title = recording?.title || args.id;
738
+ const isrc = recording?.isrc;
739
+ return {
740
+ action: "delete recording",
741
+ target: isrc ? `${title} (${isrc})` : title,
742
+ warning: "the recording will be removed from its work and any release. credits attached to this recording will be detached. this cannot be undone.",
743
+ display_name: displayName,
744
+ };
745
+ }
746
+ case "pica_share_links_delete": {
747
+ const link = await pica.shareLinks.get(args.id).catch(() => null);
748
+ const linkData = link?.data || link;
749
+ const linkName = linkData?.name || args.id;
750
+ return {
751
+ action: "delete share link",
752
+ target: linkName,
753
+ warning: "the link will become inaccessible immediately. recipients with the URL will see a 'no longer available' page. view / download history is removed. to keep the audit trail, prefer pica_share_links_update with is_active=false.",
754
+ display_name: displayName,
755
+ };
756
+ }
757
+ case "pica_agreement_types_delete": {
758
+ return {
759
+ action: "delete agreement type",
760
+ target: args.id,
761
+ warning: "the agreement type will be removed from the catalog. existing agreements that reference this type will keep their type string but lose the template definition. this cannot be undone.",
762
+ display_name: displayName,
763
+ };
764
+ }
765
+ case "pica_notes_delete": {
766
+ return {
767
+ action: "delete note",
768
+ target: args.id,
769
+ warning: "the note will be permanently removed. attachments referenced by the note are not deleted. this cannot be undone.",
770
+ display_name: displayName,
771
+ };
772
+ }
773
+ case "pica_physical_assets_delete": {
774
+ const asset = await pica.physicalAssets
775
+ ?.get?.(args.id)
776
+ .catch(() => null);
777
+ const assetName = asset?.name || asset?.title || args.id;
778
+ return {
779
+ action: "delete physical asset",
780
+ target: assetName,
781
+ warning: "the asset record and any linked recording / work associations will be removed. this cannot be undone — re-create from scratch if you need it back.",
782
+ display_name: displayName,
783
+ };
784
+ }
785
+ case "pica_memory_delete": {
786
+ return {
787
+ action: "delete memory entry",
788
+ target: args.id,
789
+ warning: "the memory entry will be permanently removed and will not appear in future agent context. this cannot be undone.",
790
+ display_name: displayName,
791
+ };
792
+ }
793
+ case "pica_delete_my_account": {
794
+ return {
795
+ action: "delete account",
796
+ target: "your account and organisation",
797
+ warning: "this is permanent. your auth user, organisation membership, API keys, OAuth tokens, agent grants, and notification preferences will all be removed. catalog data (works, recordings, credits) is retained per data-retention policy. this cannot be undone.",
798
+ display_name: displayName,
799
+ };
800
+ }
801
+ case "pica_revoke_agent_grant": {
802
+ const grantId = args.grant_id || args.id;
803
+ return {
804
+ action: "revoke agent grant",
805
+ target: grantId,
806
+ warning: "the grant token will stop authenticating immediately. any in-flight agent calls using this grant will fail at next request. this cannot be undone — issue a new grant if needed.",
807
+ display_name: displayName,
808
+ };
809
+ }
810
+ default: {
811
+ return {
812
+ action: displayName,
813
+ target: args.id || "unknown",
814
+ warning: "this cannot be undone.",
815
+ display_name: displayName,
816
+ };
817
+ }
818
+ }
819
+ }
820
+ catch {
821
+ return {
822
+ action: displayName,
823
+ target: args.id || "unknown",
824
+ warning: "this cannot be undone.",
825
+ display_name: displayName,
826
+ };
827
+ }
828
+ }
829
+ /**
830
+ * Sanitize tool parameters for audit logging.
831
+ * Strips confirmation tokens and truncates large string values.
832
+ */
833
+ sanitizeParams(args) {
834
+ const sanitized = { ...args };
835
+ delete sanitized.confirmation_token;
836
+ for (const [key, value] of Object.entries(sanitized)) {
837
+ if (typeof value === "string" && value.length > 500) {
838
+ sanitized[key] = value.substring(0, 500) + "...[truncated]";
839
+ }
840
+ }
841
+ return sanitized;
842
+ }
843
+ /**
844
+ * ADR-208 Primitive B — opaque per-org cache key.
845
+ *
846
+ * Stdio: `config.picaApiKey` is the org-bound API key from credentials,
847
+ * unique per org. HTTP non-discovery: `callerContext.callerIdentity` is
848
+ * `http:${authContext.keyId}` set by `app/api/mcp/route.ts`, also unique
849
+ * per org. This is the cross-org cache isolation invariant required by
850
+ * acceptance criterion 4 — two orgs sharing a worker process never share
851
+ * a cacheKey. Returns `null` (and the wrapper skips session_state) when
852
+ * neither identifier is present.
853
+ */
854
+ getSessionCacheKey() {
855
+ const apiKey = this.config?.picaApiKey;
856
+ if (apiKey)
857
+ return apiKey;
858
+ if (this.callerContext.callerIdentity &&
859
+ this.callerContext.callerIdentity !== "unknown") {
860
+ return this.callerContext.callerIdentity;
861
+ }
862
+ return null;
863
+ }
864
+ /**
865
+ * ADR-208 Primitive B + ADR-208 Phase 2 + ADR-210 Phase 1 — fetch the
866
+ * org-scoped counts + billing slice via `pica.catalogStats()`. The
867
+ * endpoint composes the billing slice server-side via
868
+ * `subscriptionService.getOrgBillingSlice` and the pending-uploads count
869
+ * via the new `organisations.pending_uploads_count` column so stdio +
870
+ * HTTP transports stay symmetric.
871
+ *
872
+ * `completeness_pct` is stubbed at 0 in Phase 1 and will be wired in a
873
+ * Phase 1.5 follow-up. The stub keeps the shape contract green without
874
+ * forcing a separate endpoint extension into this PR.
875
+ *
876
+ * Fail-soft: when `this.pica` is missing (HTTP-discovery without bearer
877
+ * scope, mostly), every count is 0 and the billing slice falls back to
878
+ * a conservative "trial" shape so the agent doesn't see misleading
879
+ * "active" billing for an uninitialized context.
880
+ */
881
+ fetchSessionCounts = async () => {
882
+ if (!this.pica) {
883
+ return {
884
+ works_count: 0,
885
+ recordings_count: 0,
886
+ completeness_pct: 0,
887
+ pending_uploads: 0,
888
+ billing_state: "trial",
889
+ trial_days_remaining: null,
890
+ current_tier: null,
891
+ capacity_pct: 0,
892
+ };
893
+ }
894
+ const stats = await this.pica.catalogStats();
895
+ const billing = stats.billing_slice;
896
+ // ADR-217 Primitive D — storage slice from /api/admin/catalog/stats.
897
+ // Optional in the response so older servers without ADR-217 keep
898
+ // working — when missing, omit the key entirely (vs fabricating a
899
+ // misleading "pica_s3 / untested" envelope that would suppress an
900
+ // agent's "you might want to configure storage" nudge).
901
+ const storage = stats.storage_slice;
902
+ const counts = {
903
+ works_count: stats.works?.total ?? 0,
904
+ recordings_count: stats.recordings?.total ?? 0,
905
+ // Phase 1.5 stub — completeness_pct ships separately. Shape preserved.
906
+ completeness_pct: 0,
907
+ pending_uploads: stats.pending_uploads_count ?? 0,
908
+ // ADR-210 Phase 1 — billing slice from /api/admin/catalog/stats. The
909
+ // endpoint emits a "trial" fallback on its own billing-side outage; if
910
+ // the field is missing entirely (older server pre-Phase-1), mirror the
911
+ // same conservative shape locally rather than fabricate "active".
912
+ billing_state: billing?.billing_state ?? "trial",
913
+ trial_days_remaining: billing?.trial_days_remaining ?? null,
914
+ current_tier: billing?.current_tier ?? null,
915
+ capacity_pct: billing?.capacity_pct ?? 0,
916
+ ...(storage
917
+ ? {
918
+ storage_provider: {
919
+ provider: storage.provider,
920
+ is_byoc: storage.is_byoc,
921
+ last_test_status: storage.last_test_status,
922
+ },
923
+ }
924
+ : {}),
925
+ };
926
+ // ADR-218 Primitive A — propagate the optional notifications summary
927
+ // when the catalog-stats response carries it. Older API servers
928
+ // (pre-ADR-218) omit the field; we leave the envelope key off in that
929
+ // case so the agent doesn't see a fabricated zero.
930
+ if (stats.notifications) {
931
+ counts.notifications = stats.notifications;
932
+ }
933
+ return counts;
934
+ };
935
+ /**
936
+ * P3c — attach `_meta.schemas` (workflow-resource URIs) to a successful
937
+ * workflow-tagged tool result. Mirrors `attachSessionState`'s merge
938
+ * semantics: spread existing `_meta`, add the new key, leave other
939
+ * keys (e.g. `session_state`) untouched.
940
+ *
941
+ * Applies to ALL workflow-tagged tools (Option X), including read
942
+ * companions like `pica_sessions_list`, so the agent gets the schema
943
+ * pointer no matter which verb they hit first. Skips when:
944
+ * - the tool's `workflows` is or contains only `"infrastructure"`
945
+ * (no schema resource exists), or
946
+ * - the result is an error (the workflow didn't progress).
947
+ *
948
+ * Fail-open like `attachSessionState`: derivation is pure today, but
949
+ * any future throw must not turn a successful tool result into a
950
+ * failure.
951
+ */
952
+ attachSchemaHints(result, workflows) {
953
+ try {
954
+ const tags = Array.isArray(workflows) ? workflows : [workflows];
955
+ const schemas = tags
956
+ .filter((t) => t !== "infrastructure")
957
+ .map((t) => `pica://schemas/${t}`);
958
+ if (schemas.length === 0)
959
+ return;
960
+ const existingMeta = result._meta ?? {};
961
+ result._meta = {
962
+ ...existingMeta,
963
+ schemas,
964
+ };
965
+ }
966
+ catch {
967
+ // Match attachSessionState shape — fail-open. The hint is a
968
+ // discoverability aid, never load-bearing for the tool's own work.
969
+ }
970
+ }
971
+ /**
972
+ * ADR-208 Primitive B — bump the session-mutation counters and attach
973
+ * the resolved `_meta.session_state` to a successful write-tool result.
974
+ * Mutates `result` in-place to keep the existing return contract.
975
+ * Fail-open: any error here is swallowed — ambient state is best-effort
976
+ * and must not turn a successful mutation into a tool failure.
977
+ */
978
+ async attachSessionState(result, toolName) {
979
+ const cacheKey = this.getSessionCacheKey();
980
+ if (!cacheKey)
981
+ return;
982
+ try {
983
+ incrementSessionMutations(cacheKey);
984
+ const sessionState = await buildSessionState(cacheKey, this.fetchSessionCounts);
985
+ if (!sessionState)
986
+ return;
987
+ const existingMeta = result._meta ?? {};
988
+ result._meta = {
989
+ ...existingMeta,
990
+ session_state: sessionState,
991
+ };
992
+ }
993
+ catch {
994
+ // Swallow — ADR-208 explicitly mandates fail-open. The counter has
995
+ // already advanced; not attaching session_state on this turn just
996
+ // means the agent gets the bumped counters on the next mutation.
997
+ void toolName;
998
+ }
999
+ }
1000
+ /**
1001
+ * Execute a tool by name. `ctx.server` carries the transport-scoped
1002
+ * MCP `Server` reference so executors that need `server.elicitInput()`
1003
+ * (ADR-200 Phase 1) can reach it. Each per-request HTTP server is
1004
+ * different; passing it through positionally avoids registry-level
1005
+ * mutable state that would race between concurrent requests.
1006
+ */
1007
+ async executeTool(name, args, ctx) {
1008
+ const startTime = Date.now();
1009
+ const tool = this.tools.get(name);
1010
+ if (!tool) {
1011
+ throw new ToolExecutionError(`Tool not found: ${name}`);
1012
+ }
1013
+ const metadata = getToolMetadata(name);
1014
+ // ── Session freshness gate (ADR-151) ──
1015
+ if (metadata?.risk === "destructive" &&
1016
+ this.config &&
1017
+ !this.config.lobbyMode) {
1018
+ const creds = readCredentials(this.config.credentialsPath);
1019
+ if (creds?.authenticated_at) {
1020
+ const authAge = Date.now() - new Date(creds.authenticated_at).getTime();
1021
+ const sevenDays = 7 * 24 * 60 * 60 * 1000;
1022
+ if (authAge > sevenDays) {
1023
+ return {
1024
+ content: [
1025
+ {
1026
+ type: "text",
1027
+ text: 'this action requires re-verification — say "sign me in" to confirm your identity',
1028
+ },
1029
+ ],
1030
+ isError: true,
1031
+ };
1032
+ }
1033
+ }
1034
+ }
1035
+ // ── Destructive confirmation gate ──
1036
+ if (metadata?.risk === "destructive") {
1037
+ const confirmationToken = args.confirmation_token;
1038
+ if (!confirmationToken) {
1039
+ // First call — generate preview and return confirmation challenge
1040
+ const token = generateConfirmationToken(name, args);
1041
+ const preview = await this.buildDestructivePreview(name, args);
1042
+ const response = {
1043
+ content: [
1044
+ {
1045
+ type: "text",
1046
+ text: JSON.stringify({
1047
+ status: "confirmation_required",
1048
+ confirmation_token: token,
1049
+ expires_in: 120,
1050
+ preview,
1051
+ }, null, 2),
1052
+ },
1053
+ ],
1054
+ structuredContent: {
1055
+ status: "confirmation_required",
1056
+ confirmation_token: token,
1057
+ expires_in: 120,
1058
+ preview,
1059
+ },
1060
+ };
1061
+ // Fire-and-forget audit for confirmation challenge
1062
+ this.auditLogger
1063
+ ?.logToolExecution({
1064
+ tool_name: name,
1065
+ tool_category: metadata?.category || "unknown",
1066
+ risk_level: "destructive",
1067
+ parameters: this.sanitizeParams(args),
1068
+ result_status: "confirmation_required",
1069
+ confirmation_token: token,
1070
+ execution_time_ms: Date.now() - startTime,
1071
+ caller_identity: this.callerContext.callerIdentity,
1072
+ transport: this.callerContext.transport,
1073
+ })
1074
+ .catch(() => { }); // Never block on audit
1075
+ return response;
1076
+ }
1077
+ // Second call — validate token
1078
+ const validation = validateAndConsumeToken(confirmationToken, name, args);
1079
+ if (!validation.valid) {
1080
+ return {
1081
+ content: [
1082
+ {
1083
+ type: "text",
1084
+ text: JSON.stringify({
1085
+ error: validation.reason?.includes("expired")
1086
+ ? "CONFIRMATION_EXPIRED"
1087
+ : "CONFIRMATION_INVALID",
1088
+ message: validation.reason,
1089
+ retry_safe: false,
1090
+ suggestion: "start the operation again without the confirmation token",
1091
+ }, null, 2),
1092
+ },
1093
+ ],
1094
+ isError: true,
1095
+ };
1096
+ }
1097
+ // Token valid — fall through to execute
1098
+ }
1099
+ // ── Normal execution ──
1100
+ try {
1101
+ const result = await tool.executor(args, ctx);
1102
+ // Guard response size — truncate oversized payloads with recovery hint
1103
+ if (result?.content) {
1104
+ for (const block of result.content) {
1105
+ if (block.type === "text" && typeof block.text === "string") {
1106
+ block.text = guardResponseSize(block.text, name);
1107
+ }
1108
+ }
1109
+ }
1110
+ // ADR-208 Primitive B — ambient session-state on write-tool results.
1111
+ // Read tools (`risk: safe` ⇒ `readOnlyHint: true`) skip this entirely so
1112
+ // the high-volume read path stays cheap. `result.isError === true` also
1113
+ // skips because a failed mutation didn't change catalog state.
1114
+ const isWriteTool = (metadata?.risk ?? this.classifyTool(name)) !== "safe";
1115
+ if (isWriteTool && result && !result.isError) {
1116
+ await this.attachSessionState(result, name);
1117
+ }
1118
+ // P3c — schema-resource hint on every workflow-tagged tool result
1119
+ // (Option X — applies to read companions too so the agent gets the
1120
+ // pointer regardless of which verb they hit first). Runs after
1121
+ // `attachSessionState` so the two `_meta` keys co-exist via merge.
1122
+ // Skipped on errors (workflow didn't progress) and on infrastructure
1123
+ // tools (handled inside `attachSchemaHints`).
1124
+ if (result && !result.isError) {
1125
+ this.attachSchemaHints(result, tool.definition.workflows);
1126
+ }
1127
+ // ADR-208 acceptance criterion 5 — briefing run resets the
1128
+ // since_last_briefing counter. Handled in the wrapper so the briefing
1129
+ // tool itself stays oblivious to the cacheKey it doesn't own.
1130
+ if (name === "pica_dashboard_briefing" && result && !result.isError) {
1131
+ const cacheKey = this.getSessionCacheKey();
1132
+ if (cacheKey)
1133
+ resetSinceLastBriefing(cacheKey);
1134
+ }
1135
+ // Fire-and-forget audit
1136
+ this.auditLogger
1137
+ ?.logToolExecution({
1138
+ tool_name: name,
1139
+ tool_category: metadata?.category || "unknown",
1140
+ risk_level: metadata?.risk || this.classifyTool(name),
1141
+ parameters: this.sanitizeParams(args),
1142
+ result_status: "success",
1143
+ execution_time_ms: Date.now() - startTime,
1144
+ caller_identity: this.callerContext.callerIdentity,
1145
+ transport: this.callerContext.transport,
1146
+ })
1147
+ .catch(() => { }); // Never block on audit
1148
+ return result;
1149
+ }
1150
+ catch (error) {
1151
+ const formatted = formatError(error);
1152
+ const parsed = JSON.parse(formatted.text);
1153
+ // Attach recovery hint if available for this tool + error code
1154
+ const hint = getRecoveryHint(name, parsed.error);
1155
+ if (hint) {
1156
+ parsed.suggestion = hint.suggestion;
1157
+ if (hint.next_tool) {
1158
+ parsed.next_tool = hint.next_tool;
1159
+ }
1160
+ }
1161
+ // Fire-and-forget audit for errors
1162
+ this.auditLogger
1163
+ ?.logToolExecution({
1164
+ tool_name: name,
1165
+ tool_category: metadata?.category || "unknown",
1166
+ risk_level: metadata?.risk || this.classifyTool(name),
1167
+ parameters: this.sanitizeParams(args),
1168
+ result_status: "error",
1169
+ error_code: parsed.error,
1170
+ execution_time_ms: Date.now() - startTime,
1171
+ caller_identity: this.callerContext.callerIdentity,
1172
+ transport: this.callerContext.transport,
1173
+ })
1174
+ .catch(() => { }); // Never block on audit
1175
+ return {
1176
+ content: [{ type: "text", text: JSON.stringify(parsed, null, 2) }],
1177
+ isError: true,
1178
+ };
1179
+ }
1180
+ }
1181
+ }
1182
+ //# sourceMappingURL=index.js.map