@strapi/upload 5.52.3 → 5.54.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 (220) hide show
  1. package/dist/admin/future/components/Drawer.js +6 -3
  2. package/dist/admin/future/components/Drawer.js.map +1 -1
  3. package/dist/admin/future/components/Drawer.mjs +6 -3
  4. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  5. package/dist/admin/future/components/UploadProgressDialog.js +9 -8
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +9 -8
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  9. package/dist/admin/future/pages/Assets/AssetsPage.js +16 -4
  10. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  11. package/dist/admin/future/pages/Assets/AssetsPage.mjs +16 -4
  12. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  13. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +5 -0
  14. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  15. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +5 -0
  16. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +19 -4
  18. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +19 -4
  20. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +35 -1
  22. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +35 -1
  24. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +10 -0
  26. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +10 -0
  28. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsTable.js +9 -0
  30. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +10 -1
  32. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +132 -88
  34. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +135 -91
  36. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +7 -0
  38. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +7 -0
  40. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +4 -1
  42. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +4 -1
  44. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js +181 -0
  46. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js.map +1 -0
  47. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs +179 -0
  48. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -0
  49. package/dist/admin/future/pages/Assets/constants.js +22 -0
  50. package/dist/admin/future/pages/Assets/constants.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/constants.mjs +19 -1
  52. package/dist/admin/future/pages/Assets/constants.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +65 -10
  54. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +66 -11
  56. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js +111 -0
  58. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs +109 -0
  60. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs.map +1 -0
  61. package/dist/admin/future/services/api.js +17 -0
  62. package/dist/admin/future/services/api.js.map +1 -1
  63. package/dist/admin/future/services/api.mjs +18 -1
  64. package/dist/admin/future/services/api.mjs.map +1 -1
  65. package/dist/admin/future/store/uploadProgress.js +31 -22
  66. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  67. package/dist/admin/future/store/uploadProgress.mjs +31 -22
  68. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  69. package/dist/admin/index.js +11 -10
  70. package/dist/admin/index.js.map +1 -1
  71. package/dist/admin/index.mjs +11 -10
  72. package/dist/admin/index.mjs.map +1 -1
  73. package/dist/admin/src/future/components/Drawer.d.ts +10 -0
  74. package/dist/admin/src/future/pages/Assets/components/MainAreaContextMenu.d.ts +28 -0
  75. package/dist/admin/src/future/pages/Assets/constants.d.ts +20 -0
  76. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +4 -0
  77. package/dist/admin/src/future/pages/Assets/hooks/useListScrollRestoration.d.ts +15 -0
  78. package/dist/admin/src/future/store/uploadProgress.d.ts +14 -20
  79. package/dist/admin/translations/en.json.js +1 -3
  80. package/dist/admin/translations/en.json.js.map +1 -1
  81. package/dist/admin/translations/en.json.mjs +1 -3
  82. package/dist/admin/translations/en.json.mjs.map +1 -1
  83. package/dist/server/bootstrap.js +11 -0
  84. package/dist/server/bootstrap.js.map +1 -1
  85. package/dist/server/bootstrap.mjs +11 -0
  86. package/dist/server/bootstrap.mjs.map +1 -1
  87. package/dist/server/controllers/admin-upload.js +31 -1
  88. package/dist/server/controllers/admin-upload.js.map +1 -1
  89. package/dist/server/controllers/admin-upload.mjs +31 -1
  90. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  91. package/dist/server/controllers/utils/find-entity-and-check-permissions.js +15 -4
  92. package/dist/server/controllers/utils/find-entity-and-check-permissions.js.map +1 -1
  93. package/dist/server/controllers/utils/find-entity-and-check-permissions.mjs +15 -4
  94. package/dist/server/controllers/utils/find-entity-and-check-permissions.mjs.map +1 -1
  95. package/dist/server/controllers/utils/folders.js +1 -1
  96. package/dist/server/controllers/utils/folders.js.map +1 -1
  97. package/dist/server/controllers/utils/folders.mjs +1 -1
  98. package/dist/server/controllers/utils/folders.mjs.map +1 -1
  99. package/dist/server/mcp/ambient-instance.js +46 -0
  100. package/dist/server/mcp/ambient-instance.js.map +1 -0
  101. package/dist/server/mcp/ambient-instance.mjs +43 -0
  102. package/dist/server/mcp/ambient-instance.mjs.map +1 -0
  103. package/dist/server/mcp/handlers/constants.js +60 -0
  104. package/dist/server/mcp/handlers/constants.js.map +1 -0
  105. package/dist/server/mcp/handlers/constants.mjs +45 -0
  106. package/dist/server/mcp/handlers/constants.mjs.map +1 -0
  107. package/dist/server/mcp/handlers/folder-handlers.js +308 -0
  108. package/dist/server/mcp/handlers/folder-handlers.js.map +1 -0
  109. package/dist/server/mcp/handlers/folder-handlers.mjs +303 -0
  110. package/dist/server/mcp/handlers/folder-handlers.mjs.map +1 -0
  111. package/dist/server/mcp/handlers/read-handlers.js +134 -0
  112. package/dist/server/mcp/handlers/read-handlers.js.map +1 -0
  113. package/dist/server/mcp/handlers/read-handlers.mjs +130 -0
  114. package/dist/server/mcp/handlers/read-handlers.mjs.map +1 -0
  115. package/dist/server/mcp/handlers/write-handlers.js +315 -0
  116. package/dist/server/mcp/handlers/write-handlers.js.map +1 -0
  117. package/dist/server/mcp/handlers/write-handlers.mjs +311 -0
  118. package/dist/server/mcp/handlers/write-handlers.mjs.map +1 -0
  119. package/dist/server/mcp/permissions.js +36 -0
  120. package/dist/server/mcp/permissions.js.map +1 -0
  121. package/dist/server/mcp/permissions.mjs +33 -0
  122. package/dist/server/mcp/permissions.mjs.map +1 -0
  123. package/dist/server/mcp/register-upload-mcp-tools.js +235 -0
  124. package/dist/server/mcp/register-upload-mcp-tools.js.map +1 -0
  125. package/dist/server/mcp/register-upload-mcp-tools.mjs +232 -0
  126. package/dist/server/mcp/register-upload-mcp-tools.mjs.map +1 -0
  127. package/dist/server/mcp/sanitizers/sanitize-media.js +97 -0
  128. package/dist/server/mcp/sanitizers/sanitize-media.js.map +1 -0
  129. package/dist/server/mcp/sanitizers/sanitize-media.mjs +93 -0
  130. package/dist/server/mcp/sanitizers/sanitize-media.mjs.map +1 -0
  131. package/dist/server/mcp/schemas/input-schemas.js +206 -0
  132. package/dist/server/mcp/schemas/input-schemas.js.map +1 -0
  133. package/dist/server/mcp/schemas/input-schemas.mjs +190 -0
  134. package/dist/server/mcp/schemas/input-schemas.mjs.map +1 -0
  135. package/dist/server/mcp/schemas/output-schemas.js +158 -0
  136. package/dist/server/mcp/schemas/output-schemas.js.map +1 -0
  137. package/dist/server/mcp/schemas/output-schemas.mjs +142 -0
  138. package/dist/server/mcp/schemas/output-schemas.mjs.map +1 -0
  139. package/dist/server/mcp/utils.js +14 -0
  140. package/dist/server/mcp/utils.js.map +1 -0
  141. package/dist/server/mcp/utils.mjs +12 -0
  142. package/dist/server/mcp/utils.mjs.map +1 -0
  143. package/dist/server/media-library-default-notice.js +42 -0
  144. package/dist/server/media-library-default-notice.js.map +1 -0
  145. package/dist/server/media-library-default-notice.mjs +40 -0
  146. package/dist/server/media-library-default-notice.mjs.map +1 -0
  147. package/dist/server/register.js +5 -0
  148. package/dist/server/register.js.map +1 -1
  149. package/dist/server/register.mjs +5 -0
  150. package/dist/server/register.mjs.map +1 -1
  151. package/dist/server/services/folder.js.map +1 -1
  152. package/dist/server/services/folder.mjs.map +1 -1
  153. package/dist/server/services/upload.js +34 -10
  154. package/dist/server/services/upload.js.map +1 -1
  155. package/dist/server/services/upload.mjs +34 -10
  156. package/dist/server/services/upload.mjs.map +1 -1
  157. package/dist/server/src/bootstrap.d.ts.map +1 -1
  158. package/dist/server/src/controllers/admin-upload.d.ts +1 -0
  159. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  160. package/dist/server/src/controllers/utils/find-entity-and-check-permissions.d.ts +14 -1
  161. package/dist/server/src/controllers/utils/find-entity-and-check-permissions.d.ts.map +1 -1
  162. package/dist/server/src/controllers/utils/folders.d.ts +2 -1
  163. package/dist/server/src/controllers/utils/folders.d.ts.map +1 -1
  164. package/dist/server/src/index.d.ts +10 -2
  165. package/dist/server/src/index.d.ts.map +1 -1
  166. package/dist/server/src/mcp/ambient-instance.d.ts +65 -0
  167. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -0
  168. package/dist/server/src/mcp/handlers/constants.d.ts +48 -0
  169. package/dist/server/src/mcp/handlers/constants.d.ts.map +1 -0
  170. package/dist/server/src/mcp/handlers/folder-handlers.d.ts +49 -0
  171. package/dist/server/src/mcp/handlers/folder-handlers.d.ts.map +1 -0
  172. package/dist/server/src/mcp/handlers/index.d.ts +5 -0
  173. package/dist/server/src/mcp/handlers/index.d.ts.map +1 -0
  174. package/dist/server/src/mcp/handlers/read-handlers.d.ts +35 -0
  175. package/dist/server/src/mcp/handlers/read-handlers.d.ts.map +1 -0
  176. package/dist/server/src/mcp/handlers/write-handlers.d.ts +66 -0
  177. package/dist/server/src/mcp/handlers/write-handlers.d.ts.map +1 -0
  178. package/dist/server/src/mcp/index.d.ts +3 -0
  179. package/dist/server/src/mcp/index.d.ts.map +1 -0
  180. package/dist/server/src/mcp/permissions.d.ts +22 -0
  181. package/dist/server/src/mcp/permissions.d.ts.map +1 -0
  182. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts +29 -0
  183. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts.map +1 -0
  184. package/dist/server/src/mcp/sanitizers/sanitize-media.d.ts +63 -0
  185. package/dist/server/src/mcp/sanitizers/sanitize-media.d.ts.map +1 -0
  186. package/dist/server/src/mcp/schemas/index.d.ts +3 -0
  187. package/dist/server/src/mcp/schemas/index.d.ts.map +1 -0
  188. package/dist/server/src/mcp/schemas/input-schemas.d.ts +148 -0
  189. package/dist/server/src/mcp/schemas/input-schemas.d.ts.map +1 -0
  190. package/dist/server/src/mcp/schemas/output-schemas.d.ts +276 -0
  191. package/dist/server/src/mcp/schemas/output-schemas.d.ts.map +1 -0
  192. package/dist/server/src/mcp/types.d.ts +19 -0
  193. package/dist/server/src/mcp/types.d.ts.map +1 -0
  194. package/dist/server/src/mcp/utils.d.ts +4 -0
  195. package/dist/server/src/mcp/utils.d.ts.map +1 -0
  196. package/dist/server/src/media-library-default-notice.d.ts +19 -0
  197. package/dist/server/src/media-library-default-notice.d.ts.map +1 -0
  198. package/dist/server/src/register.d.ts.map +1 -1
  199. package/dist/server/src/services/folder.d.ts +6 -2
  200. package/dist/server/src/services/folder.d.ts.map +1 -1
  201. package/dist/server/src/services/index.d.ts +10 -2
  202. package/dist/server/src/services/index.d.ts.map +1 -1
  203. package/dist/server/src/services/upload.d.ts +10 -1
  204. package/dist/server/src/services/upload.d.ts.map +1 -1
  205. package/dist/server/src/types.d.ts +2 -2
  206. package/dist/server/src/types.d.ts.map +1 -1
  207. package/dist/server/src/utils/index.d.ts +2 -1
  208. package/dist/server/src/utils/index.d.ts.map +1 -1
  209. package/dist/server/utils/index.js +2 -2
  210. package/dist/server/utils/index.js.map +1 -1
  211. package/dist/server/utils/index.mjs +2 -2
  212. package/dist/server/utils/index.mjs.map +1 -1
  213. package/dist/shared/contracts/files.d.ts +22 -0
  214. package/dist/shared/contracts/files.d.ts.map +1 -1
  215. package/package.json +7 -6
  216. package/dist/admin/future/components/BetaNotice.js +0 -72
  217. package/dist/admin/future/components/BetaNotice.js.map +0 -1
  218. package/dist/admin/future/components/BetaNotice.mjs +0 -70
  219. package/dist/admin/future/components/BetaNotice.mjs.map +0 -1
  220. package/dist/admin/src/future/components/BetaNotice.d.ts +0 -11
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ /** Coerces a value to a string, or null when it is absent. Timestamps arrive as Date or string. */ const toNullableString = (value)=>{
4
+ if (value === null || value === undefined) return null;
5
+ if (value instanceof Date) return value.toISOString();
6
+ return String(value);
7
+ };
8
+ const toNullableNumber = (value)=>{
9
+ if (value === null || value === undefined) return null;
10
+ const parsed = Number(value);
11
+ return Number.isFinite(parsed) ? parsed : null;
12
+ };
13
+ /**
14
+ * Projects a raw file row onto the MCP asset shape.
15
+ *
16
+ * This is an allowlist: every exposed key is named explicitly, so `provider`,
17
+ * `provider_metadata` (which can carry provider credentials), `hash`, `formats`, `related`,
18
+ * and the private `folderPath` never reach an MCP client — including after new fields are
19
+ * added to the file content-type.
20
+ */ const sanitizeMediaAsset = (asset)=>{
21
+ const folder = asset.folder;
22
+ return {
23
+ id: Number(asset.id),
24
+ name: String(asset.name ?? ''),
25
+ alternativeText: toNullableString(asset.alternativeText),
26
+ caption: toNullableString(asset.caption),
27
+ url: String(asset.url ?? ''),
28
+ mime: String(asset.mime ?? ''),
29
+ size: Number(asset.size ?? 0),
30
+ width: toNullableNumber(asset.width),
31
+ height: toNullableNumber(asset.height),
32
+ ext: toNullableString(asset.ext),
33
+ folder: folder !== null && folder !== undefined && folder.id !== undefined ? {
34
+ id: Number(folder.id),
35
+ name: String(folder.name ?? '')
36
+ } : null,
37
+ createdAt: toNullableString(asset.createdAt),
38
+ updatedAt: toNullableString(asset.updatedAt)
39
+ };
40
+ };
41
+ /**
42
+ * Projects the recursive output of `folder.getStructure()` onto `{ id, name, children }`,
43
+ * dropping `path` and `pathId` (internal materialized-path bookkeeping).
44
+ */ const sanitizeMediaFolderTree = (nodes)=>{
45
+ if (Array.isArray(nodes) === false) return [];
46
+ return nodes.map((node)=>({
47
+ id: Number(node.id),
48
+ name: String(node.name ?? ''),
49
+ children: sanitizeMediaFolderTree(node.children)
50
+ }));
51
+ };
52
+ /**
53
+ * Projects a raw folder row onto the MCP folder shape.
54
+ *
55
+ * An allowlist for the same reason as `sanitizeMediaAsset`: `path` and `pathId` are the internal
56
+ * materialized-path bookkeeping the folder service uses to cascade moves and deletes. Exposing
57
+ * them would invite an agent to treat them as addressable, and any field added to the folder
58
+ * content-type later stays invisible until it is named here on purpose.
59
+ */ const sanitizeMediaFolder = (folder)=>{
60
+ if (folder === null || folder === undefined) {
61
+ return null;
62
+ }
63
+ const parent = folder.parent;
64
+ /**
65
+ * `parent: null` is a claim, not a default: the output contract defines it as "this folder
66
+ * sits at the media library root". A row selected without the relation cannot support that
67
+ * claim — it knows nothing about the parent either way — so the key is omitted rather than
68
+ * asserted as null, which would report a nested folder as root-level.
69
+ *
70
+ * A populated relation arrives either as an object or as a bare id, depending on the query.
71
+ */ const parentValue = (()=>{
72
+ if ('parent' in folder === false || parent === undefined) return undefined;
73
+ if (parent === null) return null;
74
+ if (typeof parent === 'number') return {
75
+ id: parent
76
+ };
77
+ if (parent.id === undefined) return null;
78
+ return {
79
+ id: Number(parent.id),
80
+ name: String(parent.name ?? '')
81
+ };
82
+ })();
83
+ return {
84
+ id: Number(folder.id),
85
+ name: String(folder.name ?? ''),
86
+ ...parentValue === undefined ? {} : {
87
+ parent: parentValue
88
+ },
89
+ createdAt: toNullableString(folder.createdAt),
90
+ updatedAt: toNullableString(folder.updatedAt)
91
+ };
92
+ };
93
+
94
+ exports.sanitizeMediaAsset = sanitizeMediaAsset;
95
+ exports.sanitizeMediaFolder = sanitizeMediaFolder;
96
+ exports.sanitizeMediaFolderTree = sanitizeMediaFolderTree;
97
+ //# sourceMappingURL=sanitize-media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-media.js","sources":["../../../../server/src/mcp/sanitizers/sanitize-media.ts"],"sourcesContent":["import type { MediaFolderNode } from '../schemas';\n\ntype RawFolder = {\n id?: unknown;\n name?: unknown;\n children?: unknown;\n};\n\ntype RawAsset = Record<string, unknown> & {\n folder?: RawFolder | null;\n};\n\n/** Coerces a value to a string, or null when it is absent. Timestamps arrive as Date or string. */\nconst toNullableString = (value: unknown): string | null => {\n if (value === null || value === undefined) return null;\n if (value instanceof Date) return value.toISOString();\n return String(value);\n};\n\nconst toNullableNumber = (value: unknown): number | null => {\n if (value === null || value === undefined) return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\n/**\n * Projects a raw file row onto the MCP asset shape.\n *\n * This is an allowlist: every exposed key is named explicitly, so `provider`,\n * `provider_metadata` (which can carry provider credentials), `hash`, `formats`, `related`,\n * and the private `folderPath` never reach an MCP client — including after new fields are\n * added to the file content-type.\n */\nexport const sanitizeMediaAsset = (asset: RawAsset) => {\n const folder = asset.folder;\n\n return {\n id: Number(asset.id),\n name: String(asset.name ?? ''),\n alternativeText: toNullableString(asset.alternativeText),\n caption: toNullableString(asset.caption),\n url: String(asset.url ?? ''),\n mime: String(asset.mime ?? ''),\n size: Number(asset.size ?? 0),\n width: toNullableNumber(asset.width),\n height: toNullableNumber(asset.height),\n ext: toNullableString(asset.ext),\n folder:\n folder !== null && folder !== undefined && folder.id !== undefined\n ? { id: Number(folder.id), name: String(folder.name ?? '') }\n : null,\n createdAt: toNullableString(asset.createdAt),\n updatedAt: toNullableString(asset.updatedAt),\n };\n};\n\n/**\n * Projects the recursive output of `folder.getStructure()` onto `{ id, name, children }`,\n * dropping `path` and `pathId` (internal materialized-path bookkeeping).\n */\nexport const sanitizeMediaFolderTree = (nodes: unknown): MediaFolderNode[] => {\n if (Array.isArray(nodes) === false) return [];\n\n return (nodes as RawFolder[]).map((node) => ({\n id: Number(node.id),\n name: String(node.name ?? ''),\n children: sanitizeMediaFolderTree(node.children),\n }));\n};\n\n/**\n * Projects a raw folder row onto the MCP folder shape.\n *\n * An allowlist for the same reason as `sanitizeMediaAsset`: `path` and `pathId` are the internal\n * materialized-path bookkeeping the folder service uses to cascade moves and deletes. Exposing\n * them would invite an agent to treat them as addressable, and any field added to the folder\n * content-type later stays invisible until it is named here on purpose.\n */\nexport const sanitizeMediaFolder = (\n folder: (RawFolder & Record<string, unknown>) | null | undefined\n) => {\n if (folder === null || folder === undefined) {\n return null;\n }\n\n const parent = folder.parent as RawFolder | number | null | undefined;\n\n /**\n * `parent: null` is a claim, not a default: the output contract defines it as \"this folder\n * sits at the media library root\". A row selected without the relation cannot support that\n * claim — it knows nothing about the parent either way — so the key is omitted rather than\n * asserted as null, which would report a nested folder as root-level.\n *\n * A populated relation arrives either as an object or as a bare id, depending on the query.\n */\n const parentValue = (() => {\n if ('parent' in folder === false || parent === undefined) return undefined;\n if (parent === null) return null;\n if (typeof parent === 'number') return { id: parent };\n if (parent.id === undefined) return null;\n return { id: Number(parent.id), name: String(parent.name ?? '') };\n })();\n\n return {\n id: Number(folder.id),\n name: String(folder.name ?? ''),\n ...(parentValue === undefined ? {} : { parent: parentValue }),\n createdAt: toNullableString(folder.createdAt),\n updatedAt: toNullableString(folder.updatedAt),\n };\n};\n"],"names":["toNullableString","value","undefined","Date","toISOString","String","toNullableNumber","parsed","Number","isFinite","sanitizeMediaAsset","asset","folder","id","name","alternativeText","caption","url","mime","size","width","height","ext","createdAt","updatedAt","sanitizeMediaFolderTree","nodes","Array","isArray","map","node","children","sanitizeMediaFolder","parent","parentValue"],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /** Coerces a value to a string, or null when it is absent. Timestamps arrive as Date or string. */ const toNullableString = (value)=>{
2
+ if (value === null || value === undefined) return null;
3
+ if (value instanceof Date) return value.toISOString();
4
+ return String(value);
5
+ };
6
+ const toNullableNumber = (value)=>{
7
+ if (value === null || value === undefined) return null;
8
+ const parsed = Number(value);
9
+ return Number.isFinite(parsed) ? parsed : null;
10
+ };
11
+ /**
12
+ * Projects a raw file row onto the MCP asset shape.
13
+ *
14
+ * This is an allowlist: every exposed key is named explicitly, so `provider`,
15
+ * `provider_metadata` (which can carry provider credentials), `hash`, `formats`, `related`,
16
+ * and the private `folderPath` never reach an MCP client — including after new fields are
17
+ * added to the file content-type.
18
+ */ const sanitizeMediaAsset = (asset)=>{
19
+ const folder = asset.folder;
20
+ return {
21
+ id: Number(asset.id),
22
+ name: String(asset.name ?? ''),
23
+ alternativeText: toNullableString(asset.alternativeText),
24
+ caption: toNullableString(asset.caption),
25
+ url: String(asset.url ?? ''),
26
+ mime: String(asset.mime ?? ''),
27
+ size: Number(asset.size ?? 0),
28
+ width: toNullableNumber(asset.width),
29
+ height: toNullableNumber(asset.height),
30
+ ext: toNullableString(asset.ext),
31
+ folder: folder !== null && folder !== undefined && folder.id !== undefined ? {
32
+ id: Number(folder.id),
33
+ name: String(folder.name ?? '')
34
+ } : null,
35
+ createdAt: toNullableString(asset.createdAt),
36
+ updatedAt: toNullableString(asset.updatedAt)
37
+ };
38
+ };
39
+ /**
40
+ * Projects the recursive output of `folder.getStructure()` onto `{ id, name, children }`,
41
+ * dropping `path` and `pathId` (internal materialized-path bookkeeping).
42
+ */ const sanitizeMediaFolderTree = (nodes)=>{
43
+ if (Array.isArray(nodes) === false) return [];
44
+ return nodes.map((node)=>({
45
+ id: Number(node.id),
46
+ name: String(node.name ?? ''),
47
+ children: sanitizeMediaFolderTree(node.children)
48
+ }));
49
+ };
50
+ /**
51
+ * Projects a raw folder row onto the MCP folder shape.
52
+ *
53
+ * An allowlist for the same reason as `sanitizeMediaAsset`: `path` and `pathId` are the internal
54
+ * materialized-path bookkeeping the folder service uses to cascade moves and deletes. Exposing
55
+ * them would invite an agent to treat them as addressable, and any field added to the folder
56
+ * content-type later stays invisible until it is named here on purpose.
57
+ */ const sanitizeMediaFolder = (folder)=>{
58
+ if (folder === null || folder === undefined) {
59
+ return null;
60
+ }
61
+ const parent = folder.parent;
62
+ /**
63
+ * `parent: null` is a claim, not a default: the output contract defines it as "this folder
64
+ * sits at the media library root". A row selected without the relation cannot support that
65
+ * claim — it knows nothing about the parent either way — so the key is omitted rather than
66
+ * asserted as null, which would report a nested folder as root-level.
67
+ *
68
+ * A populated relation arrives either as an object or as a bare id, depending on the query.
69
+ */ const parentValue = (()=>{
70
+ if ('parent' in folder === false || parent === undefined) return undefined;
71
+ if (parent === null) return null;
72
+ if (typeof parent === 'number') return {
73
+ id: parent
74
+ };
75
+ if (parent.id === undefined) return null;
76
+ return {
77
+ id: Number(parent.id),
78
+ name: String(parent.name ?? '')
79
+ };
80
+ })();
81
+ return {
82
+ id: Number(folder.id),
83
+ name: String(folder.name ?? ''),
84
+ ...parentValue === undefined ? {} : {
85
+ parent: parentValue
86
+ },
87
+ createdAt: toNullableString(folder.createdAt),
88
+ updatedAt: toNullableString(folder.updatedAt)
89
+ };
90
+ };
91
+
92
+ export { sanitizeMediaAsset, sanitizeMediaFolder, sanitizeMediaFolderTree };
93
+ //# sourceMappingURL=sanitize-media.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-media.mjs","sources":["../../../../server/src/mcp/sanitizers/sanitize-media.ts"],"sourcesContent":["import type { MediaFolderNode } from '../schemas';\n\ntype RawFolder = {\n id?: unknown;\n name?: unknown;\n children?: unknown;\n};\n\ntype RawAsset = Record<string, unknown> & {\n folder?: RawFolder | null;\n};\n\n/** Coerces a value to a string, or null when it is absent. Timestamps arrive as Date or string. */\nconst toNullableString = (value: unknown): string | null => {\n if (value === null || value === undefined) return null;\n if (value instanceof Date) return value.toISOString();\n return String(value);\n};\n\nconst toNullableNumber = (value: unknown): number | null => {\n if (value === null || value === undefined) return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\n/**\n * Projects a raw file row onto the MCP asset shape.\n *\n * This is an allowlist: every exposed key is named explicitly, so `provider`,\n * `provider_metadata` (which can carry provider credentials), `hash`, `formats`, `related`,\n * and the private `folderPath` never reach an MCP client — including after new fields are\n * added to the file content-type.\n */\nexport const sanitizeMediaAsset = (asset: RawAsset) => {\n const folder = asset.folder;\n\n return {\n id: Number(asset.id),\n name: String(asset.name ?? ''),\n alternativeText: toNullableString(asset.alternativeText),\n caption: toNullableString(asset.caption),\n url: String(asset.url ?? ''),\n mime: String(asset.mime ?? ''),\n size: Number(asset.size ?? 0),\n width: toNullableNumber(asset.width),\n height: toNullableNumber(asset.height),\n ext: toNullableString(asset.ext),\n folder:\n folder !== null && folder !== undefined && folder.id !== undefined\n ? { id: Number(folder.id), name: String(folder.name ?? '') }\n : null,\n createdAt: toNullableString(asset.createdAt),\n updatedAt: toNullableString(asset.updatedAt),\n };\n};\n\n/**\n * Projects the recursive output of `folder.getStructure()` onto `{ id, name, children }`,\n * dropping `path` and `pathId` (internal materialized-path bookkeeping).\n */\nexport const sanitizeMediaFolderTree = (nodes: unknown): MediaFolderNode[] => {\n if (Array.isArray(nodes) === false) return [];\n\n return (nodes as RawFolder[]).map((node) => ({\n id: Number(node.id),\n name: String(node.name ?? ''),\n children: sanitizeMediaFolderTree(node.children),\n }));\n};\n\n/**\n * Projects a raw folder row onto the MCP folder shape.\n *\n * An allowlist for the same reason as `sanitizeMediaAsset`: `path` and `pathId` are the internal\n * materialized-path bookkeeping the folder service uses to cascade moves and deletes. Exposing\n * them would invite an agent to treat them as addressable, and any field added to the folder\n * content-type later stays invisible until it is named here on purpose.\n */\nexport const sanitizeMediaFolder = (\n folder: (RawFolder & Record<string, unknown>) | null | undefined\n) => {\n if (folder === null || folder === undefined) {\n return null;\n }\n\n const parent = folder.parent as RawFolder | number | null | undefined;\n\n /**\n * `parent: null` is a claim, not a default: the output contract defines it as \"this folder\n * sits at the media library root\". A row selected without the relation cannot support that\n * claim — it knows nothing about the parent either way — so the key is omitted rather than\n * asserted as null, which would report a nested folder as root-level.\n *\n * A populated relation arrives either as an object or as a bare id, depending on the query.\n */\n const parentValue = (() => {\n if ('parent' in folder === false || parent === undefined) return undefined;\n if (parent === null) return null;\n if (typeof parent === 'number') return { id: parent };\n if (parent.id === undefined) return null;\n return { id: Number(parent.id), name: String(parent.name ?? '') };\n })();\n\n return {\n id: Number(folder.id),\n name: String(folder.name ?? ''),\n ...(parentValue === undefined ? {} : { parent: parentValue }),\n createdAt: toNullableString(folder.createdAt),\n updatedAt: toNullableString(folder.updatedAt),\n };\n};\n"],"names":["toNullableString","value","undefined","Date","toISOString","String","toNullableNumber","parsed","Number","isFinite","sanitizeMediaAsset","asset","folder","id","name","alternativeText","caption","url","mime","size","width","height","ext","createdAt","updatedAt","sanitizeMediaFolderTree","nodes","Array","isArray","map","node","children","sanitizeMediaFolder","parent","parentValue"],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@strapi/utils');
4
+ var constants = require('../../constants.js');
5
+
6
+ /**
7
+ * Media files and folders are NOT documents: they are plain entities keyed by a numeric `id`,
8
+ * so there is no `documentId` and no draft/published pair. Every media identifier in the MCP
9
+ * surface is this numeric id.
10
+ */ const mediaIdSchema = utils.z.number().int().min(1).describe('Numeric media asset id (e.g. 42). Media files are not documents — they have no documentId and no draft/published versions.');
11
+ const folderIdSchema = utils.z.number().int().min(1).describe('Numeric media folder id (e.g. 3). Folders are not documents — they use numeric ids.');
12
+ const pageSchema = utils.z.number().int().min(1).optional().describe('Page number (1-indexed, default: 1).');
13
+ const pageSizeSchema = utils.z.number().int().min(1).max(100).optional().describe('Items per page (default: 25, max: 100).');
14
+ /**
15
+ * Sort is constrained to the same whitelist the Media Library admin uses, so MCP callers
16
+ * cannot sort by private columns such as `folderPath`.
17
+ */ const sortSchema = utils.z.enum(constants.ALLOWED_SORT_STRINGS).optional().describe(`Sort expression. One of: ${constants.ALLOWED_SORT_STRINGS.join(', ')}. Defaults to "createdAt:DESC".`);
18
+ const mediaListAssetsInputSchema = utils.z.object({
19
+ folderId: folderIdSchema.optional().describe('Only return assets directly inside this folder. Omit for every folder; pass null for assets at the media library root.').nullable(),
20
+ mime: utils.z.string().min(1).optional().describe('Filter by mime type prefix or exact value (e.g. "image", "image/png", "application/pdf").'),
21
+ name: utils.z.string().min(1).optional().describe('Case-insensitive substring search on the asset name.'),
22
+ page: pageSchema,
23
+ pageSize: pageSizeSchema,
24
+ sort: sortSchema
25
+ });
26
+ const mediaGetAssetInputSchema = utils.z.object({
27
+ id: mediaIdSchema
28
+ });
29
+ utils.z.object({});
30
+ const FOLDER_INPUT_KEYS = [
31
+ 'folder',
32
+ 'folderId',
33
+ 'folderPath'
34
+ ];
35
+ /**
36
+ * `media_update_asset` input — the only writable asset metadata.
37
+ *
38
+ * `.strict()` turns an out-of-scope field into an error rather than a silent no-op. The custom
39
+ * object error directs folder-shaped inputs to `media_move_assets`; other unknown keys are named by
40
+ * Zod's default error, so the agent can correct the call without a round-trip.
41
+ *
42
+ * The "at least one field" rule is enforced in the handler, not here: a `.refine()` would turn
43
+ * this into a `ZodEffects`, which the MCP tool registry cannot expose as an input schema.
44
+ */ const mediaUpdateAssetInputSchema = utils.z.object({
45
+ id: mediaIdSchema,
46
+ name: utils.z.string().min(1).optional().describe('New asset name as shown in the Media Library. Renames the entry only — the stored file and its URL are unchanged.'),
47
+ alternativeText: utils.z.string().nullable().optional().describe('Alt text used by the frontend for accessibility. Pass null to clear it; the field then reads back as an empty string.'),
48
+ caption: utils.z.string().nullable().optional().describe('Caption shown alongside the asset. Pass null to clear it; the field then reads back as an empty string.')
49
+ }, {
50
+ error (issue) {
51
+ if (issue.code === 'unrecognized_keys' && FOLDER_INPUT_KEYS.some((key)=>issue.keys.includes(key))) {
52
+ return 'Folder changes are not supported by media_update_asset. Use media_move_assets to move an asset between folders.';
53
+ }
54
+ return undefined;
55
+ }
56
+ }).strict();
57
+ /**
58
+ * Folder name, validated to the same rules as the admin folder controller
59
+ * (`controllers/validation/admin/folder.ts`): non-empty, no slashes, no surrounding whitespace.
60
+ *
61
+ * Enforcing the shape here rather than only in the handler means the agent is corrected by the
62
+ * tool schema — before a call is made — instead of by a service-level error afterwards. The
63
+ * uniqueness rule cannot live here: it needs a DB read, so it stays in the handler.
64
+ */ const folderNameSchema = utils.z.string().min(1).regex(/^[^/]+$/, 'Folder name cannot contain slashes.').regex(/^(?! ).+(?<! )$/, 'Folder name cannot start or end with a whitespace.').describe('Folder name as shown in the Media Library. Cannot contain slashes or start/end with a space, and must be unique among its siblings.');
65
+ /**
66
+ * `parent` is nullable-with-meaning: null is the media library root, an id nests the folder.
67
+ * The two are distinct from *omitting* the key, which `media_move_folder` forbids (a move needs a
68
+ * destination) and `media_create_folder` reads as the root.
69
+ */ const parentFolderIdSchema = folderIdSchema.nullable().describe('Numeric id of the containing folder. Pass null for the media library root. Use media_list_folders to discover folder ids.');
70
+ const mediaCreateFolderInputSchema = utils.z.object({
71
+ name: folderNameSchema,
72
+ parent: parentFolderIdSchema.optional().describe('Numeric id of the parent folder. Omit or pass null to create the folder at the media library root.')
73
+ }).strict();
74
+ /**
75
+ * `media_rename_folder` deliberately takes no `parent`: renaming and moving are separate tools, so an
76
+ * agent picks by intent. A `parent` here is a sign the caller wanted `media_move_folder`, and the
77
+ * custom object error says so rather than letting Zod's generic "unrecognized key" stand.
78
+ */ const mediaRenameFolderInputSchema = utils.z.object({
79
+ id: folderIdSchema,
80
+ name: folderNameSchema
81
+ }, {
82
+ error (issue) {
83
+ if (issue.code === 'unrecognized_keys' && issue.keys.includes('parent')) {
84
+ return 'media_rename_folder only changes a folder name. Use media_move_folder to change a folder location.';
85
+ }
86
+ return undefined;
87
+ }
88
+ }).strict();
89
+ /**
90
+ * `media_move_folder` requires `parent` — including an explicit null for the root. Making it required
91
+ * is what keeps a mistyped move from silently becoming a no-op update.
92
+ */ const mediaMoveFolderInputSchema = utils.z.object({
93
+ id: folderIdSchema,
94
+ parent: parentFolderIdSchema
95
+ }, {
96
+ error (issue) {
97
+ if (issue.code === 'unrecognized_keys' && issue.keys.includes('name')) {
98
+ return 'media_move_folder only changes a folder location. Use media_rename_folder to change a folder name.';
99
+ }
100
+ return undefined;
101
+ }
102
+ }).strict();
103
+ /**
104
+ * `media_delete_folder` input.
105
+ *
106
+ * `dryRun` defaults to true: the safe branch is the one an agent gets when it omits the flag, so
107
+ * a destructive cascade is never the path of least resistance (the initiative card's mitigation
108
+ * for irreversible MCP operations). Deleting requires saying `dryRun: false` on purpose.
109
+ *
110
+ * That default lives in the handler (`folder-handlers.ts`, `dryRun = true`), NOT in this schema:
111
+ * `.default(true)` here is deliberately avoided so an omitted flag is a preview no matter how a
112
+ * client serialises the advertised schema. The `.describe()` text below is what tells an agent
113
+ * the default, so the two must be kept in step — do not "fix" this to `.default(true)`.
114
+ */ const mediaDeleteFolderInputSchema = utils.z.object({
115
+ ids: utils.z.array(folderIdSchema).min(1).max(100).describe('Numeric ids of the folders to delete (1-100). FOLDER ids only — asset ids are a separate namespace of integers and are rejected here; use media_delete_assets for assets.'),
116
+ dryRun: utils.z.boolean().optional().describe('When true (the default), nothing is deleted and the tool only reports how many folders and files WOULD be removed. Pass false to actually perform the irreversible deletion.')
117
+ }).strict();
118
+ /**
119
+ * `media_move_assets` input.
120
+ *
121
+ * Bulk by design: an agent reorganising a library moves many assets at once, and one call with a
122
+ * per-id report is cheaper and easier to recover from than N sequential calls. There is no
123
+ * single-asset variant — the array length is the only difference, and a second tool would add a
124
+ * choice without removing a mistake. The admin REST API agrees: `/actions/bulk-move` is the only
125
+ * move route.
126
+ *
127
+ * `folder` is required, including an explicit null for the root, for the same reason `media_move_folder`
128
+ * requires `parent`: a move needs a destination, and an omitted key would silently become a no-op.
129
+ *
130
+ * ASSET ids only, and the schema CANNOT enforce it. Asset ids and folder ids are independently
131
+ * numbered, so the same integer routinely names both; the handler resolves ids in the file table,
132
+ * which means a folder id whose number collides with an asset moves that asset — and reports it
133
+ * as a success. Only an id matching no asset at all is reported as failed.
134
+ *
135
+ * This is an accepted risk, not an oversight: with a bare `ids: number[]` there is no way for the
136
+ * caller to say which namespace it meant, and refusing every colliding id would make those assets
137
+ * permanently unmovable over MCP. The mitigation is the tool description. The durable fix is
138
+ * namespaced handles (`asset:1` / `folder:1`) across the whole media surface, which is a breaking
139
+ * change to the read tools and belongs to its own ticket.
140
+ */ const mediaMoveAssetsInputSchema = utils.z.object({
141
+ ids: utils.z.array(mediaIdSchema).min(1).max(100).describe('Numeric ids of the assets to move (1-100). ASSET ids only, taken from media_list_assets or media_get_asset — never from media_list_folders. Folder ids are numbered separately and the same number often names both an asset and a folder, so a folder id here moves the asset sharing that number; use media_move_folder to move a folder.'),
142
+ folder: folderIdSchema.nullable().describe('Numeric id of the destination folder. Pass null to move the assets to the media library root. Required — including the explicit null — so a move always names a destination. Use media_list_folders to discover folder ids.')
143
+ }, {
144
+ error (issue) {
145
+ if (issue.code === 'unrecognized_keys' && issue.keys.some((key)=>key === 'id' || key === 'fileIds')) {
146
+ return 'media_move_assets moves assets in bulk: pass `ids` as an array of numeric asset ids, even for a single asset.';
147
+ }
148
+ if (issue.code === 'unrecognized_keys' && issue.keys.includes('folderIds')) {
149
+ return 'media_move_assets moves assets only. Use media_move_folder to move a folder.';
150
+ }
151
+ return undefined;
152
+ }
153
+ }).strict();
154
+ /**
155
+ * `media_delete_assets` input.
156
+ *
157
+ * Bulk-only, like `media_move_assets`: the admin REST API has no single-asset delete route either
158
+ * (`/actions/bulk-delete` is the only one), and single-vs-bulk is an array length rather than a
159
+ * distinction an agent can get wrong.
160
+ *
161
+ * `dryRun` defaults to true in the handler, not here — same reasoning as `media_delete_folder`: the
162
+ * safe branch is what an agent gets when it omits the flag, so an irreversible delete is never
163
+ * the path of least resistance. A schema-level `.default(true)` would publish as a JSON Schema
164
+ * default a client could serialise away.
165
+ *
166
+ * ASSET ids only, and the schema CANNOT enforce it. Asset ids and folder ids are independently
167
+ * numbered, so the same integer routinely names both; the handler resolves ids in the file table,
168
+ * which means a folder id whose number collides with an asset deletes that asset. Only an id
169
+ * matching no asset at all is reported as failed.
170
+ *
171
+ * This is an accepted risk, not an oversight: with a bare `ids: number[]` there is no way for the
172
+ * caller to say which namespace it meant, and refusing every colliding id would make those assets
173
+ * permanently undeletable over MCP. The mitigations are the dry run and the tool description. The
174
+ * durable fix is namespaced handles (`asset:1` / `folder:1`) across the whole media surface, which
175
+ * is a breaking change to the read tools and belongs to its own ticket.
176
+ */ const mediaDeleteAssetsInputSchema = utils.z.object({
177
+ ids: utils.z.array(mediaIdSchema).min(1).max(100).describe('Numeric ids of the assets to delete (1-100). ASSET ids only, taken from media_list_assets or media_get_asset — never from media_list_folders. Folder ids are numbered separately and the same number often names both an asset and a folder, so a folder id here deletes the asset sharing that number; use media_delete_folder for folders.'),
178
+ dryRun: utils.z.boolean().optional().describe('When true (the default), NOTHING is deleted and the tool only reports which assets WOULD be permanently removed. Pass false to actually perform the irreversible deletion.')
179
+ }, {
180
+ error (issue) {
181
+ if (issue.code === 'unrecognized_keys' && issue.keys.some((key)=>key === 'id' || key === 'fileIds')) {
182
+ return 'media_delete_assets deletes assets in bulk: pass `ids` as an array of numeric asset ids, even for a single asset.';
183
+ }
184
+ if (issue.code === 'unrecognized_keys' && issue.keys.includes('folderIds')) {
185
+ return 'media_delete_assets deletes assets only. Use media_delete_folder to delete a folder.';
186
+ }
187
+ return undefined;
188
+ }
189
+ }).strict();
190
+
191
+ exports.folderIdSchema = folderIdSchema;
192
+ exports.folderNameSchema = folderNameSchema;
193
+ exports.mediaCreateFolderInputSchema = mediaCreateFolderInputSchema;
194
+ exports.mediaDeleteAssetsInputSchema = mediaDeleteAssetsInputSchema;
195
+ exports.mediaDeleteFolderInputSchema = mediaDeleteFolderInputSchema;
196
+ exports.mediaGetAssetInputSchema = mediaGetAssetInputSchema;
197
+ exports.mediaIdSchema = mediaIdSchema;
198
+ exports.mediaListAssetsInputSchema = mediaListAssetsInputSchema;
199
+ exports.mediaMoveAssetsInputSchema = mediaMoveAssetsInputSchema;
200
+ exports.mediaMoveFolderInputSchema = mediaMoveFolderInputSchema;
201
+ exports.mediaRenameFolderInputSchema = mediaRenameFolderInputSchema;
202
+ exports.mediaUpdateAssetInputSchema = mediaUpdateAssetInputSchema;
203
+ exports.pageSchema = pageSchema;
204
+ exports.pageSizeSchema = pageSizeSchema;
205
+ exports.sortSchema = sortSchema;
206
+ //# sourceMappingURL=input-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-schemas.js","sources":["../../../../server/src/mcp/schemas/input-schemas.ts"],"sourcesContent":["import { z } from '@strapi/utils';\n\nimport { ALLOWED_SORT_STRINGS } from '../../constants';\n\n/**\n * Media files and folders are NOT documents: they are plain entities keyed by a numeric `id`,\n * so there is no `documentId` and no draft/published pair. Every media identifier in the MCP\n * surface is this numeric id.\n */\nexport const mediaIdSchema = z\n .number()\n .int()\n .min(1)\n .describe(\n 'Numeric media asset id (e.g. 42). Media files are not documents — they have no documentId and no draft/published versions.'\n );\n\nexport const folderIdSchema = z\n .number()\n .int()\n .min(1)\n .describe('Numeric media folder id (e.g. 3). Folders are not documents — they use numeric ids.');\n\nexport const pageSchema = z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Page number (1-indexed, default: 1).');\n\nexport const pageSizeSchema = z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe('Items per page (default: 25, max: 100).');\n\n/**\n * Sort is constrained to the same whitelist the Media Library admin uses, so MCP callers\n * cannot sort by private columns such as `folderPath`.\n */\nexport const sortSchema = z\n .enum(ALLOWED_SORT_STRINGS as [string, ...string[]])\n .optional()\n .describe(\n `Sort expression. One of: ${ALLOWED_SORT_STRINGS.join(', ')}. Defaults to \"createdAt:DESC\".`\n );\n\nexport const mediaListAssetsInputSchema = z.object({\n folderId: folderIdSchema\n .optional()\n .describe(\n 'Only return assets directly inside this folder. Omit for every folder; pass null for assets at the media library root.'\n )\n .nullable(),\n mime: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'Filter by mime type prefix or exact value (e.g. \"image\", \"image/png\", \"application/pdf\").'\n ),\n name: z\n .string()\n .min(1)\n .optional()\n .describe('Case-insensitive substring search on the asset name.'),\n page: pageSchema,\n pageSize: pageSizeSchema,\n sort: sortSchema,\n});\n\nexport const mediaGetAssetInputSchema = z.object({\n id: mediaIdSchema,\n});\n\nexport const mediaListFoldersInputSchema = z.object({});\n\nconst FOLDER_INPUT_KEYS = ['folder', 'folderId', 'folderPath'] as const;\n\n/**\n * `media_update_asset` input — the only writable asset metadata.\n *\n * `.strict()` turns an out-of-scope field into an error rather than a silent no-op. The custom\n * object error directs folder-shaped inputs to `media_move_assets`; other unknown keys are named by\n * Zod's default error, so the agent can correct the call without a round-trip.\n *\n * The \"at least one field\" rule is enforced in the handler, not here: a `.refine()` would turn\n * this into a `ZodEffects`, which the MCP tool registry cannot expose as an input schema.\n */\nexport const mediaUpdateAssetInputSchema = z\n .object(\n {\n id: mediaIdSchema,\n name: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'New asset name as shown in the Media Library. Renames the entry only — the stored file and its URL are unchanged.'\n ),\n alternativeText: z\n .string()\n .nullable()\n .optional()\n .describe(\n 'Alt text used by the frontend for accessibility. Pass null to clear it; the field then reads back as an empty string.'\n ),\n caption: z\n .string()\n .nullable()\n .optional()\n .describe(\n 'Caption shown alongside the asset. Pass null to clear it; the field then reads back as an empty string.'\n ),\n },\n {\n error(issue) {\n if (\n issue.code === 'unrecognized_keys' &&\n FOLDER_INPUT_KEYS.some((key) => issue.keys.includes(key))\n ) {\n return 'Folder changes are not supported by media_update_asset. Use media_move_assets to move an asset between folders.';\n }\n\n return undefined;\n },\n }\n )\n .strict();\n\n/**\n * Folder name, validated to the same rules as the admin folder controller\n * (`controllers/validation/admin/folder.ts`): non-empty, no slashes, no surrounding whitespace.\n *\n * Enforcing the shape here rather than only in the handler means the agent is corrected by the\n * tool schema — before a call is made — instead of by a service-level error afterwards. The\n * uniqueness rule cannot live here: it needs a DB read, so it stays in the handler.\n */\nexport const folderNameSchema = z\n .string()\n .min(1)\n .regex(/^[^/]+$/, 'Folder name cannot contain slashes.')\n .regex(/^(?! ).+(?<! )$/, 'Folder name cannot start or end with a whitespace.')\n .describe(\n 'Folder name as shown in the Media Library. Cannot contain slashes or start/end with a space, and must be unique among its siblings.'\n );\n\n/**\n * `parent` is nullable-with-meaning: null is the media library root, an id nests the folder.\n * The two are distinct from *omitting* the key, which `media_move_folder` forbids (a move needs a\n * destination) and `media_create_folder` reads as the root.\n */\nconst parentFolderIdSchema = folderIdSchema\n .nullable()\n .describe(\n 'Numeric id of the containing folder. Pass null for the media library root. Use media_list_folders to discover folder ids.'\n );\n\nexport const mediaCreateFolderInputSchema = z\n .object({\n name: folderNameSchema,\n parent: parentFolderIdSchema\n .optional()\n .describe(\n 'Numeric id of the parent folder. Omit or pass null to create the folder at the media library root.'\n ),\n })\n .strict();\n\n/**\n * `media_rename_folder` deliberately takes no `parent`: renaming and moving are separate tools, so an\n * agent picks by intent. A `parent` here is a sign the caller wanted `media_move_folder`, and the\n * custom object error says so rather than letting Zod's generic \"unrecognized key\" stand.\n */\nexport const mediaRenameFolderInputSchema = z\n .object(\n {\n id: folderIdSchema,\n name: folderNameSchema,\n },\n {\n error(issue) {\n if (issue.code === 'unrecognized_keys' && issue.keys.includes('parent')) {\n return 'media_rename_folder only changes a folder name. Use media_move_folder to change a folder location.';\n }\n\n return undefined;\n },\n }\n )\n .strict();\n\n/**\n * `media_move_folder` requires `parent` — including an explicit null for the root. Making it required\n * is what keeps a mistyped move from silently becoming a no-op update.\n */\nexport const mediaMoveFolderInputSchema = z\n .object(\n {\n id: folderIdSchema,\n parent: parentFolderIdSchema,\n },\n {\n error(issue) {\n if (issue.code === 'unrecognized_keys' && issue.keys.includes('name')) {\n return 'media_move_folder only changes a folder location. Use media_rename_folder to change a folder name.';\n }\n\n return undefined;\n },\n }\n )\n .strict();\n\n/**\n * `media_delete_folder` input.\n *\n * `dryRun` defaults to true: the safe branch is the one an agent gets when it omits the flag, so\n * a destructive cascade is never the path of least resistance (the initiative card's mitigation\n * for irreversible MCP operations). Deleting requires saying `dryRun: false` on purpose.\n *\n * That default lives in the handler (`folder-handlers.ts`, `dryRun = true`), NOT in this schema:\n * `.default(true)` here is deliberately avoided so an omitted flag is a preview no matter how a\n * client serialises the advertised schema. The `.describe()` text below is what tells an agent\n * the default, so the two must be kept in step — do not \"fix\" this to `.default(true)`.\n */\nexport const mediaDeleteFolderInputSchema = z\n .object({\n ids: z\n .array(folderIdSchema)\n .min(1)\n .max(100)\n .describe(\n 'Numeric ids of the folders to delete (1-100). FOLDER ids only — asset ids are a separate namespace of integers and are rejected here; use media_delete_assets for assets.'\n ),\n dryRun: z\n .boolean()\n .optional()\n .describe(\n 'When true (the default), nothing is deleted and the tool only reports how many folders and files WOULD be removed. Pass false to actually perform the irreversible deletion.'\n ),\n })\n .strict();\n\n/**\n * `media_move_assets` input.\n *\n * Bulk by design: an agent reorganising a library moves many assets at once, and one call with a\n * per-id report is cheaper and easier to recover from than N sequential calls. There is no\n * single-asset variant — the array length is the only difference, and a second tool would add a\n * choice without removing a mistake. The admin REST API agrees: `/actions/bulk-move` is the only\n * move route.\n *\n * `folder` is required, including an explicit null for the root, for the same reason `media_move_folder`\n * requires `parent`: a move needs a destination, and an omitted key would silently become a no-op.\n *\n * ASSET ids only, and the schema CANNOT enforce it. Asset ids and folder ids are independently\n * numbered, so the same integer routinely names both; the handler resolves ids in the file table,\n * which means a folder id whose number collides with an asset moves that asset — and reports it\n * as a success. Only an id matching no asset at all is reported as failed.\n *\n * This is an accepted risk, not an oversight: with a bare `ids: number[]` there is no way for the\n * caller to say which namespace it meant, and refusing every colliding id would make those assets\n * permanently unmovable over MCP. The mitigation is the tool description. The durable fix is\n * namespaced handles (`asset:1` / `folder:1`) across the whole media surface, which is a breaking\n * change to the read tools and belongs to its own ticket.\n */\nexport const mediaMoveAssetsInputSchema = z\n .object(\n {\n ids: z\n .array(mediaIdSchema)\n .min(1)\n .max(100)\n .describe(\n 'Numeric ids of the assets to move (1-100). ASSET ids only, taken from media_list_assets or media_get_asset — never from media_list_folders. Folder ids are numbered separately and the same number often names both an asset and a folder, so a folder id here moves the asset sharing that number; use media_move_folder to move a folder.'\n ),\n folder: folderIdSchema\n .nullable()\n .describe(\n 'Numeric id of the destination folder. Pass null to move the assets to the media library root. Required — including the explicit null — so a move always names a destination. Use media_list_folders to discover folder ids.'\n ),\n },\n {\n error(issue) {\n if (\n issue.code === 'unrecognized_keys' &&\n issue.keys.some((key) => key === 'id' || key === 'fileIds')\n ) {\n return 'media_move_assets moves assets in bulk: pass `ids` as an array of numeric asset ids, even for a single asset.';\n }\n\n if (issue.code === 'unrecognized_keys' && issue.keys.includes('folderIds')) {\n return 'media_move_assets moves assets only. Use media_move_folder to move a folder.';\n }\n\n return undefined;\n },\n }\n )\n .strict();\n\n/**\n * `media_delete_assets` input.\n *\n * Bulk-only, like `media_move_assets`: the admin REST API has no single-asset delete route either\n * (`/actions/bulk-delete` is the only one), and single-vs-bulk is an array length rather than a\n * distinction an agent can get wrong.\n *\n * `dryRun` defaults to true in the handler, not here — same reasoning as `media_delete_folder`: the\n * safe branch is what an agent gets when it omits the flag, so an irreversible delete is never\n * the path of least resistance. A schema-level `.default(true)` would publish as a JSON Schema\n * default a client could serialise away.\n *\n * ASSET ids only, and the schema CANNOT enforce it. Asset ids and folder ids are independently\n * numbered, so the same integer routinely names both; the handler resolves ids in the file table,\n * which means a folder id whose number collides with an asset deletes that asset. Only an id\n * matching no asset at all is reported as failed.\n *\n * This is an accepted risk, not an oversight: with a bare `ids: number[]` there is no way for the\n * caller to say which namespace it meant, and refusing every colliding id would make those assets\n * permanently undeletable over MCP. The mitigations are the dry run and the tool description. The\n * durable fix is namespaced handles (`asset:1` / `folder:1`) across the whole media surface, which\n * is a breaking change to the read tools and belongs to its own ticket.\n */\nexport const mediaDeleteAssetsInputSchema = z\n .object(\n {\n ids: z\n .array(mediaIdSchema)\n .min(1)\n .max(100)\n .describe(\n 'Numeric ids of the assets to delete (1-100). ASSET ids only, taken from media_list_assets or media_get_asset — never from media_list_folders. Folder ids are numbered separately and the same number often names both an asset and a folder, so a folder id here deletes the asset sharing that number; use media_delete_folder for folders.'\n ),\n dryRun: z\n .boolean()\n .optional()\n .describe(\n 'When true (the default), NOTHING is deleted and the tool only reports which assets WOULD be permanently removed. Pass false to actually perform the irreversible deletion.'\n ),\n },\n {\n error(issue) {\n if (\n issue.code === 'unrecognized_keys' &&\n issue.keys.some((key) => key === 'id' || key === 'fileIds')\n ) {\n return 'media_delete_assets deletes assets in bulk: pass `ids` as an array of numeric asset ids, even for a single asset.';\n }\n\n if (issue.code === 'unrecognized_keys' && issue.keys.includes('folderIds')) {\n return 'media_delete_assets deletes assets only. Use media_delete_folder to delete a folder.';\n }\n\n return undefined;\n },\n }\n )\n .strict();\n"],"names":["mediaIdSchema","z","number","int","min","describe","folderIdSchema","pageSchema","optional","pageSizeSchema","max","sortSchema","enum","ALLOWED_SORT_STRINGS","join","mediaListAssetsInputSchema","object","folderId","nullable","mime","string","name","page","pageSize","sort","mediaGetAssetInputSchema","id","FOLDER_INPUT_KEYS","mediaUpdateAssetInputSchema","alternativeText","caption","error","issue","code","some","key","keys","includes","undefined","strict","folderNameSchema","regex","parentFolderIdSchema","mediaCreateFolderInputSchema","parent","mediaRenameFolderInputSchema","mediaMoveFolderInputSchema","mediaDeleteFolderInputSchema","ids","array","dryRun","boolean","mediaMoveAssetsInputSchema","folder","mediaDeleteAssetsInputSchema"],"mappings":"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"}