@payloadcms/plugin-mcp 4.0.0-internal.a0ef1b8 → 4.0.0-internal.a1317b2

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 (240) hide show
  1. package/LICENSE.md +1 -1
  2. package/bin.js +7 -0
  3. package/dist/defaultAccess.d.ts +3 -0
  4. package/dist/defaultAccess.d.ts.map +1 -0
  5. package/dist/defaultAccess.js +3 -0
  6. package/dist/defaultAccess.js.map +1 -0
  7. package/dist/defineTool.d.ts +8 -8
  8. package/dist/defineTool.d.ts.map +1 -1
  9. package/dist/endpoint/access.d.ts +12 -5
  10. package/dist/endpoint/access.d.ts.map +1 -1
  11. package/dist/endpoint/access.js +75 -88
  12. package/dist/endpoint/access.js.map +1 -1
  13. package/dist/endpoint/index.d.ts +1 -1
  14. package/dist/endpoint/index.d.ts.map +1 -1
  15. package/dist/endpoint/index.js +68 -17
  16. package/dist/endpoint/index.js.map +1 -1
  17. package/dist/exports/client.d.ts +1 -1
  18. package/dist/exports/client.d.ts.map +1 -1
  19. package/dist/exports/client.js +1 -1
  20. package/dist/exports/client.js.map +1 -1
  21. package/dist/exports/internal.d.ts +2 -0
  22. package/dist/exports/internal.d.ts.map +1 -0
  23. package/dist/exports/internal.js +3 -0
  24. package/dist/exports/internal.js.map +1 -0
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +15 -20
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp/buildMcpServer.d.ts +5 -5
  30. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  31. package/dist/mcp/buildMcpServer.js +145 -80
  32. package/dist/mcp/buildMcpServer.js.map +1 -1
  33. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  34. package/dist/mcp/builtin/collections/authTools.js +122 -61
  35. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  36. package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
  37. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
  38. package/dist/mcp/builtin/collections/countTool.js +63 -0
  39. package/dist/mcp/builtin/collections/countTool.js.map +1 -0
  40. package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
  41. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
  42. package/dist/mcp/builtin/collections/countVersionsTool.js +60 -0
  43. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
  44. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  45. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  46. package/dist/mcp/builtin/collections/createTool.js +111 -68
  47. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  48. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  49. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  50. package/dist/mcp/builtin/collections/deleteTool.js +51 -61
  51. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  52. package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
  53. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
  54. package/dist/mcp/builtin/collections/duplicateTool.js +86 -0
  55. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
  56. package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
  57. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
  58. package/dist/mcp/builtin/collections/fileInput.js +123 -0
  59. package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
  60. package/dist/mcp/builtin/collections/fileInput.spec.js +86 -0
  61. package/dist/mcp/builtin/collections/fileInput.spec.js.map +1 -0
  62. package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
  63. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
  64. package/dist/mcp/builtin/collections/findDistinctTool.js +77 -0
  65. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
  66. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  67. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  68. package/dist/mcp/builtin/collections/findTool.js +55 -72
  69. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  70. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
  71. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
  72. package/dist/mcp/builtin/collections/findVersionByIDTool.js +74 -0
  73. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
  74. package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
  75. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
  76. package/dist/mcp/builtin/collections/findVersionsTool.js +84 -0
  77. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
  78. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  79. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  80. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +87 -0
  81. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  82. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
  83. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
  84. package/dist/mcp/builtin/collections/restoreVersionTool.js +69 -0
  85. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -0
  86. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  87. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  88. package/dist/mcp/builtin/collections/updateTool.js +104 -136
  89. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  90. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  91. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  92. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  93. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  94. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  95. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  96. package/dist/mcp/builtin/formatEntityError.js +55 -0
  97. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  98. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  99. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  100. package/dist/mcp/builtin/getConfigInfoTool.js +69 -0
  101. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  102. package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
  103. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
  104. package/dist/mcp/builtin/globals/countVersionsTool.js +60 -0
  105. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
  106. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  107. package/dist/mcp/builtin/globals/findTool.js +27 -36
  108. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  109. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
  110. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
  111. package/dist/mcp/builtin/globals/findVersionByIDTool.js +68 -0
  112. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
  113. package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
  114. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
  115. package/dist/mcp/builtin/globals/findVersionsTool.js +78 -0
  116. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
  117. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  118. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  119. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +65 -0
  120. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  121. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
  122. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
  123. package/dist/mcp/builtin/globals/restoreVersionTool.js +70 -0
  124. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
  125. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  126. package/dist/mcp/builtin/globals/updateTool.js +44 -62
  127. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  128. package/dist/mcp/builtinTools.d.ts +150 -19
  129. package/dist/mcp/builtinTools.d.ts.map +1 -1
  130. package/dist/mcp/builtinTools.js +125 -20
  131. package/dist/mcp/builtinTools.js.map +1 -1
  132. package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
  133. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  134. package/dist/mcp/sanitizeMCPConfig.js +124 -58
  135. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  136. package/dist/stdio.d.ts +3 -3
  137. package/dist/stdio.d.ts.map +1 -1
  138. package/dist/stdio.js +84 -46
  139. package/dist/stdio.js.map +1 -1
  140. package/dist/types.d.ts +114 -112
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/types.js +2 -4
  143. package/dist/types.js.map +1 -1
  144. package/dist/utils/camelCase.d.ts.map +1 -1
  145. package/dist/utils/getPluginConfig.d.ts +1 -1
  146. package/dist/utils/getPluginConfig.js +1 -1
  147. package/dist/utils/getPluginConfig.js.map +1 -1
  148. package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
  149. package/dist/utils/toStandardSchema.d.ts.map +1 -1
  150. package/package.json +11 -18
  151. package/src/defaultAccess.ts +3 -0
  152. package/src/endpoint/access.ts +80 -101
  153. package/src/endpoint/index.ts +58 -16
  154. package/src/exports/client.ts +2 -1
  155. package/src/exports/internal.ts +1 -0
  156. package/src/index.ts +12 -20
  157. package/src/mcp/buildMcpServer.ts +179 -114
  158. package/src/mcp/builtin/collections/authTools.ts +119 -75
  159. package/src/mcp/builtin/collections/countTool.ts +61 -0
  160. package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
  161. package/src/mcp/builtin/collections/createTool.ts +104 -88
  162. package/src/mcp/builtin/collections/deleteTool.ts +48 -74
  163. package/src/mcp/builtin/collections/duplicateTool.ts +83 -0
  164. package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
  165. package/src/mcp/builtin/collections/fileInput.ts +169 -0
  166. package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
  167. package/src/mcp/builtin/collections/findTool.ts +55 -108
  168. package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
  169. package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
  170. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +80 -0
  171. package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
  172. package/src/mcp/builtin/collections/updateTool.ts +101 -142
  173. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  174. package/src/mcp/builtin/formatEntityError.ts +77 -0
  175. package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
  176. package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
  177. package/src/mcp/builtin/globals/findTool.ts +27 -57
  178. package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
  179. package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
  180. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +63 -0
  181. package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
  182. package/src/mcp/builtin/globals/updateTool.ts +51 -79
  183. package/src/mcp/builtinTools.ts +118 -41
  184. package/src/mcp/sanitizeMCPConfig.ts +120 -78
  185. package/src/stdio.ts +79 -46
  186. package/src/types.ts +130 -117
  187. package/src/utils/getPluginConfig.ts +1 -1
  188. package/dist/@types/assets.d.js +0 -2
  189. package/dist/@types/assets.d.js.map +0 -1
  190. package/dist/collection/getAccessField.d.ts +0 -12
  191. package/dist/collection/getAccessField.d.ts.map +0 -1
  192. package/dist/collection/getAccessField.js +0 -57
  193. package/dist/collection/getAccessField.js.map +0 -1
  194. package/dist/collection/index.d.ts +0 -6
  195. package/dist/collection/index.d.ts.map +0 -1
  196. package/dist/collection/index.js +0 -60
  197. package/dist/collection/index.js.map +0 -1
  198. package/dist/components/AccessField/index.client.d.ts +0 -10
  199. package/dist/components/AccessField/index.client.d.ts.map +0 -1
  200. package/dist/components/AccessField/index.client.js +0 -305
  201. package/dist/components/AccessField/index.client.js.map +0 -1
  202. package/dist/components/AccessField/index.css +0 -93
  203. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  204. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  205. package/dist/utils/getVirtualFieldNames.js +0 -35
  206. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  207. package/dist/utils/localAPIDefaults.d.ts +0 -20
  208. package/dist/utils/localAPIDefaults.d.ts.map +0 -1
  209. package/dist/utils/localAPIDefaults.js +0 -19
  210. package/dist/utils/localAPIDefaults.js.map +0 -1
  211. package/dist/utils/schemaConversion/buildToolInput.d.ts +0 -29
  212. package/dist/utils/schemaConversion/buildToolInput.d.ts.map +0 -1
  213. package/dist/utils/schemaConversion/buildToolInput.js +0 -51
  214. package/dist/utils/schemaConversion/buildToolInput.js.map +0 -1
  215. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
  216. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
  217. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
  218. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
  219. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -15
  220. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  221. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -464
  222. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  223. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -158
  224. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  225. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  226. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  227. package/dist/utils/transformPointDataToPayload.js +0 -28
  228. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  229. package/src/@types/assets.d.ts +0 -3
  230. package/src/collection/getAccessField.ts +0 -64
  231. package/src/collection/index.ts +0 -64
  232. package/src/components/AccessField/index.client.tsx +0 -344
  233. package/src/components/AccessField/index.css +0 -93
  234. package/src/utils/getVirtualFieldNames.ts +0 -53
  235. package/src/utils/localAPIDefaults.ts +0 -22
  236. package/src/utils/schemaConversion/buildToolInput.ts +0 -68
  237. package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
  238. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -103
  239. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -529
  240. package/src/utils/transformPointDataToPayload.ts +0 -40
@@ -0,0 +1,169 @@
1
+ import type { CollectionSlug, File, FileData, PayloadRequest } from 'payload'
2
+
3
+ import { APIError, z } from 'payload'
4
+ import { getExternalFile, getFileFromUploadInstructions, isURLAllowed } from 'payload/internal'
5
+ import { sanitizeFilename } from 'payload/shared'
6
+
7
+ const mimeTypeSchema = z
8
+ .string()
9
+ .check(
10
+ z.regex(
11
+ /^[!#$%&'*+.^`|~\w-]+\/[!#$%&'*+.^`|~\w-]+$/,
12
+ 'MIME type must use the type/subtype format',
13
+ ),
14
+ )
15
+
16
+ const uploadFileSchema = z.strictObject({
17
+ filename: z.string(),
18
+ mimeType: z.string(),
19
+ size: z.int().check(z.nonnegative()),
20
+ uploadReference: z.record(z.string(), z.unknown()),
21
+ })
22
+
23
+ export const fileInputSchema = z
24
+ .discriminatedUnion('source', [
25
+ z.strictObject({
26
+ name: z.string().check(z.minLength(1), z.describe('The file name, including its extension.')),
27
+ data: z
28
+ .string()
29
+ .check(z.describe('The base64-encoded file bytes, without a data URL prefix.')),
30
+ mimeType: mimeTypeSchema.check(z.describe('The file MIME type, for example image/png.')),
31
+ source: z.literal('base64'),
32
+ }),
33
+ z.strictObject({
34
+ name: z.optional(z.string().check(z.minLength(1))).check(z.describe('File name override.')),
35
+ source: z.literal('externalURL'),
36
+ url: z.url().check(z.describe('The http or https URL to download.')),
37
+ }),
38
+ z.strictObject({
39
+ file: uploadFileSchema.check(z.describe('getUploadInstructions file field post-upload.')),
40
+ source: z.literal('uploadReference'),
41
+ }),
42
+ ])
43
+ .check(
44
+ z.describe(
45
+ 'A file for an upload collection. Prefer uploadReference after its upload succeeds; use base64 only for small local files or externalURL for an online file.',
46
+ ),
47
+ )
48
+
49
+ type FileInput = z.infer<typeof fileInputSchema>
50
+
51
+ export async function resolveFile({
52
+ slug,
53
+ input,
54
+ req,
55
+ }: {
56
+ input?: FileInput
57
+ req: PayloadRequest
58
+ slug: CollectionSlug
59
+ }): Promise<File | undefined> {
60
+ if (!input) {
61
+ return undefined
62
+ }
63
+
64
+ if (input.source === 'uploadReference') {
65
+ try {
66
+ return await getFileFromUploadInstructions({ collectionSlug: slug, file: input.file, req })
67
+ } catch (error) {
68
+ if (error instanceof Error && error.message === 'Staged upload was not found.') {
69
+ throw new APIError(
70
+ 'Staged upload not found. Complete the upload action first, or use base64 for small local files.',
71
+ 400,
72
+ )
73
+ }
74
+ throw error
75
+ }
76
+ }
77
+
78
+ const uploadConfig = req.payload.collections[slug]?.config.upload
79
+
80
+ if (!uploadConfig) {
81
+ throw new APIError(`Collection "${slug}" does not support file uploads.`, 400)
82
+ }
83
+
84
+ const maxFileSize = req.payload.config.upload.limits?.fileSize
85
+ let file: File
86
+
87
+ if (input.source === 'base64') {
88
+ const data = decodeBase64({ maxFileSize, value: input.data })
89
+
90
+ file = {
91
+ name: sanitizeFilename(input.name),
92
+ data,
93
+ mimetype: input.mimeType,
94
+ size: data.length,
95
+ }
96
+ } else {
97
+ if (uploadConfig.pasteURL === false) {
98
+ throw new APIError(`Uploading files from URLs is disabled for collection "${slug}".`, 400)
99
+ }
100
+
101
+ const url = new URL(input.url)
102
+
103
+ if (!['http:', 'https:'].includes(url.protocol)) {
104
+ throw new APIError('File URLs must use http or https.', 400)
105
+ }
106
+
107
+ if (
108
+ typeof uploadConfig.pasteURL === 'object' &&
109
+ !isURLAllowed(input.url, uploadConfig.pasteURL.allowList)
110
+ ) {
111
+ throw new APIError('The provided file URL is not allowed.', 400)
112
+ }
113
+
114
+ file = await getExternalFile({
115
+ data: {
116
+ filename: sanitizeFilename(input.name || getURLFilename(url)),
117
+ url: input.url,
118
+ } as FileData,
119
+ req,
120
+ uploadConfig: {
121
+ ...uploadConfig,
122
+ externalFileHeaderFilter: uploadConfig.externalFileHeaderFilter ?? (() => ({})),
123
+ },
124
+ })
125
+ file.mimetype = file.mimetype?.split(';')[0] || 'application/octet-stream'
126
+ file.size = file.data.length
127
+ }
128
+
129
+ if (maxFileSize !== undefined && Number.isFinite(maxFileSize) && file.size > maxFileSize) {
130
+ throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400)
131
+ }
132
+
133
+ return file
134
+ }
135
+
136
+ function decodeBase64({ maxFileSize, value }: { maxFileSize?: number; value: string }): Buffer {
137
+ const normalized = value.replace(/\s/g, '')
138
+
139
+ if (!/^[a-z0-9+/]*={0,2}$/i.test(normalized) || normalized.length % 4 === 1) {
140
+ throw new APIError('File data must be valid base64.', 400)
141
+ }
142
+
143
+ if (maxFileSize !== undefined && Number.isFinite(maxFileSize)) {
144
+ const paddingLength = normalized.endsWith('==') ? 2 : normalized.endsWith('=') ? 1 : 0
145
+ const decodedSize = Math.floor((normalized.length * 3) / 4) - paddingLength
146
+
147
+ if (decodedSize > maxFileSize) {
148
+ throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400)
149
+ }
150
+ }
151
+
152
+ const data = Buffer.from(normalized, 'base64')
153
+
154
+ if (data.toString('base64').replace(/=+$/, '') !== normalized.replace(/=+$/, '')) {
155
+ throw new APIError('File data must be valid base64.', 400)
156
+ }
157
+
158
+ return data
159
+ }
160
+
161
+ function getURLFilename(url: URL): string {
162
+ const pathSegment = url.pathname.split('/').pop() || 'upload'
163
+
164
+ try {
165
+ return decodeURIComponent(pathSegment)
166
+ } catch {
167
+ return pathSegment
168
+ }
169
+ }
@@ -0,0 +1,67 @@
1
+ import { findDistinctInputSchema } from 'payload'
2
+
3
+ import { defaultAccess } from '../../../defaultAccess.js'
4
+ import { defineCollectionTool } from '../../../defineTool.js'
5
+ import { getLogger } from '../../../utils/getLogger.js'
6
+
7
+ const DEFAULT_DESCRIPTION =
8
+ 'Find distinct values for a field in any collection by passing the collection slug and field path.'
9
+
10
+ export const findDistinctTool = defineCollectionTool({
11
+ access: (args) =>
12
+ defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.read),
13
+ annotations: {
14
+ destructiveHint: false,
15
+ idempotentHint: true,
16
+ openWorldHint: false,
17
+ readOnlyHint: true,
18
+ title: 'Find Distinct',
19
+ },
20
+ description: DEFAULT_DESCRIPTION,
21
+ input: findDistinctInputSchema,
22
+ }).handler(async ({ slug, authorizedMCP, input, req }) => {
23
+ const payload = req.payload
24
+ const logger = getLogger({ payload })
25
+ const { depth, field, limit, locale, page, populate, sort, trash, where } = input
26
+
27
+ logger.info(`Finding distinct values in collection: ${slug}, field: ${field}`)
28
+
29
+ try {
30
+ const result = await payload.findDistinct({
31
+ collection: slug,
32
+ depth,
33
+ field,
34
+ overrideAccess: authorizedMCP.overrideAccess,
35
+ req,
36
+ ...(limit ? { limit } : {}),
37
+ ...(locale ? { locale } : {}),
38
+ ...(page ? { page } : {}),
39
+ ...(populate ? { populate } : {}),
40
+ ...(sort ? { sort } : {}),
41
+ ...(trash !== undefined ? { trash } : {}),
42
+ ...(where ? { where } : {}),
43
+ })
44
+
45
+ return {
46
+ content: [
47
+ {
48
+ type: 'text',
49
+ text: `Distinct values for "${field}" in collection "${slug}":\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``,
50
+ },
51
+ ],
52
+ doc: result as unknown as Record<string, unknown>,
53
+ }
54
+ } catch (error) {
55
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
56
+ logger.error(`Error finding distinct values in ${slug}: ${errorMessage}`)
57
+ return {
58
+ content: [
59
+ {
60
+ type: 'text',
61
+ text: `❌ **Error finding distinct values in collection "${slug}":** ${errorMessage}`,
62
+ },
63
+ ],
64
+ isError: true,
65
+ }
66
+ }
67
+ })
@@ -1,140 +1,83 @@
1
- import type { SelectType } from 'payload'
2
-
3
- import { z } from 'zod'
1
+ import { findDocumentsInputSchema } from 'payload'
4
2
 
3
+ import { defaultAccess } from '../../../defaultAccess.js'
5
4
  import { defineCollectionTool } from '../../../defineTool.js'
6
5
  import { getLogger } from '../../../utils/getLogger.js'
7
- import { localAPIDefaults } from '../../../utils/localAPIDefaults.js'
8
6
 
9
7
  const DEFAULT_DESCRIPTION =
10
- 'Find documents in a collection by ID or where clause using Find or FindByID.'
8
+ 'Find documents in any collection by passing the collection slug and optional ID or where clause.'
11
9
 
12
- export const findCollectionTool = defineCollectionTool({
10
+ export const findDocumentsTool = defineCollectionTool({
11
+ access: (args) =>
12
+ defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.read),
13
+ annotations: {
14
+ destructiveHint: false,
15
+ idempotentHint: true,
16
+ openWorldHint: false,
17
+ readOnlyHint: true,
18
+ title: 'Find Documents',
19
+ },
13
20
  description: DEFAULT_DESCRIPTION,
14
- input: z.object({
15
- id: z
16
- .union([z.string(), z.number()])
17
- .describe(
18
- 'Optional: specific document ID to retrieve. If not provided, returns all documents',
19
- )
20
- .optional(),
21
- depth: z
22
- .number()
23
- .int()
24
- .min(0)
25
- .max(10)
26
- .describe('How many levels deep to populate relationships (default: 0)')
27
- .optional()
28
- .default(0),
29
- draft: z
30
- .boolean()
31
- .describe(
32
- 'Optional: whether the document should be queried from the versions table/collection or not.',
33
- )
34
- .optional(),
35
- fallbackLocale: z
36
- .string()
37
- .describe('Optional: fallback locale code to use when requested locale is not available')
38
- .optional(),
39
- limit: z
40
- .number()
41
- .int()
42
- .min(1)
43
- .max(100)
44
- .describe('Maximum number of documents to return (default: 10, max: 100)')
45
- .optional()
46
- .default(10),
47
- locale: z
48
- .string()
49
- .describe(
50
- 'Optional: locale code to retrieve data in (e.g., "en", "es"). Use "all" to retrieve all locales for localized fields',
51
- )
52
- .optional(),
53
- page: z
54
- .number()
55
- .int()
56
- .min(1)
57
- .describe('Page number for pagination (default: 1)')
58
- .optional()
59
- .default(1),
60
- select: z
61
- .string()
62
- .describe(
63
- "Optional: define exactly which fields you'd like to return in the response (JSON), e.g., '{\"title\": true}'",
64
- )
65
- .optional(),
66
- sort: z
67
- .string()
68
- .describe('Field to sort by (e.g., "createdAt", "-updatedAt" for descending)')
69
- .optional(),
70
- where: z
71
- .string()
72
- .describe(
73
- 'Optional JSON string for where clause filtering (e.g., \'{"title": {"contains": "test"}}\')',
74
- )
75
- .optional(),
76
- }),
77
- }).handler(async ({ authorizedMCP, collectionSlug, input, req }) => {
21
+ input: findDocumentsInputSchema,
22
+ }).handler(async ({ slug, authorizedMCP, input, req }) => {
78
23
  const payload = req.payload
79
24
  const logger = getLogger({ payload })
80
25
 
81
- const { id, depth, draft, fallbackLocale, limit, locale, page, select, sort, where } = input
26
+ const {
27
+ id,
28
+ depth,
29
+ draft,
30
+ fallbackLocale,
31
+ joins,
32
+ limit,
33
+ locale,
34
+ page,
35
+ pagination,
36
+ populate,
37
+ select,
38
+ sort,
39
+ trash,
40
+ where,
41
+ } = input
82
42
 
83
43
  logger.info(
84
- `Reading document from collection: ${collectionSlug}${id ? ` with ID: ${id}` : ''}, limit: ${limit}, page: ${page}${locale ? `, locale: ${locale}` : ''}`,
44
+ `Reading document from collection: ${slug}${id ? ` with ID: ${id}` : ''}, limit: ${limit}, page: ${page}${locale ? `, locale: ${locale}` : ''}`,
85
45
  )
86
46
 
87
47
  try {
88
- let whereClause: Record<string, unknown> = {}
89
- if (where) {
90
- try {
91
- whereClause = JSON.parse(where) as Record<string, unknown>
92
- } catch {
93
- logger.warn(`Invalid where clause JSON: ${where}`)
94
- return { content: [{ type: 'text', text: 'Error: Invalid JSON in where clause' }] }
95
- }
96
- }
97
-
98
- let selectClause: SelectType | undefined
99
- if (select) {
100
- try {
101
- selectClause = JSON.parse(select) as SelectType
102
- } catch {
103
- logger.warn(`Invalid select clause JSON: ${select}`)
104
- return { content: [{ type: 'text', text: 'Error: Invalid JSON in select clause' }] }
105
- }
106
- }
107
-
108
48
  if (id) {
109
49
  try {
110
50
  const doc = await payload.findByID({
111
51
  id,
112
- collection: collectionSlug,
52
+ collection: slug,
113
53
  depth,
54
+ overrideAccess: authorizedMCP.overrideAccess,
114
55
  req,
115
- ...localAPIDefaults(authorizedMCP),
116
- ...(selectClause && { select: selectClause }),
56
+ ...(select && { select }),
57
+ ...(populate && { populate }),
58
+ ...(joins !== undefined && { joins }),
117
59
  ...(locale && { locale }),
118
- ...(fallbackLocale && { fallbackLocale }),
60
+ ...(fallbackLocale !== undefined && { fallbackLocale }),
119
61
  ...(draft !== undefined && { draft }),
62
+ ...(trash !== undefined && { trash }),
120
63
  })
121
64
 
122
65
  return {
123
66
  content: [
124
67
  {
125
68
  type: 'text',
126
- text: `Document from collection "${collectionSlug}":\n${JSON.stringify(doc)}`,
69
+ text: `Document from collection "${slug}":\n${JSON.stringify(doc)}`,
127
70
  },
128
71
  ],
129
72
  doc: doc as Record<string, unknown>,
130
73
  }
131
74
  } catch {
132
- logger.warn(`Document not found with ID: ${id} in collection: ${collectionSlug}`)
75
+ logger.warn(`Document not found with ID: ${id} in collection: ${slug}`)
133
76
  return {
134
77
  content: [
135
78
  {
136
79
  type: 'text',
137
- text: `Error: Document with ID "${id}" not found in collection "${collectionSlug}"`,
80
+ text: `Error: Document with ID "${id}" not found in collection "${slug}"`,
138
81
  },
139
82
  ],
140
83
  }
@@ -142,28 +85,32 @@ export const findCollectionTool = defineCollectionTool({
142
85
  }
143
86
 
144
87
  const findOptions: Parameters<typeof payload.find>[0] = {
145
- collection: collectionSlug,
88
+ collection: slug,
146
89
  depth,
147
90
  limit,
91
+ overrideAccess: authorizedMCP.overrideAccess,
148
92
  page,
149
93
  req,
150
- ...localAPIDefaults(authorizedMCP),
151
- ...(selectClause && { select: selectClause }),
94
+ ...(select && { select }),
95
+ ...(populate && { populate }),
96
+ ...(joins !== undefined && { joins }),
152
97
  ...(locale && { locale }),
153
- ...(fallbackLocale && { fallbackLocale }),
98
+ ...(fallbackLocale !== undefined && { fallbackLocale }),
154
99
  ...(draft !== undefined && { draft }),
100
+ ...(pagination !== undefined && { pagination }),
101
+ ...(trash !== undefined && { trash }),
155
102
  }
156
103
 
157
104
  if (sort) {
158
105
  findOptions.sort = sort
159
106
  }
160
- if (Object.keys(whereClause).length > 0) {
161
- findOptions.where = whereClause as Parameters<typeof payload.find>[0]['where']
107
+ if (where) {
108
+ findOptions.where = where
162
109
  }
163
110
 
164
111
  const result = await payload.find(findOptions)
165
112
 
166
- let responseText = `Collection: "${collectionSlug}"\nTotal: ${result.totalDocs} documents\nPage: ${result.page} of ${result.totalPages}\n`
113
+ let responseText = `Collection: "${slug}"\nTotal: ${result.totalDocs} documents\nPage: ${result.page} of ${result.totalPages}\n`
167
114
  for (const doc of result.docs) {
168
115
  responseText += `\n\`\`\`json\n${JSON.stringify(doc)}\n\`\`\``
169
116
  }
@@ -174,12 +121,12 @@ export const findCollectionTool = defineCollectionTool({
174
121
  }
175
122
  } catch (error) {
176
123
  const errorMessage = error instanceof Error ? error.message : 'Unknown error'
177
- logger.error(`Error reading documents from collection ${collectionSlug}: ${errorMessage}`)
124
+ logger.error(`Error reading documents from collection ${slug}: ${errorMessage}`)
178
125
  return {
179
126
  content: [
180
127
  {
181
128
  type: 'text',
182
- text: `❌ **Error reading documents from collection "${collectionSlug}":** ${errorMessage}`,
129
+ text: `❌ **Error reading documents from collection "${slug}":** ${errorMessage}`,
183
130
  },
184
131
  ],
185
132
  }
@@ -0,0 +1,66 @@
1
+ import { findVersionByIDInputSchema } from 'payload'
2
+
3
+ import { defaultAccess } from '../../../defaultAccess.js'
4
+ import { defineCollectionTool } from '../../../defineTool.js'
5
+ import { getLogger } from '../../../utils/getLogger.js'
6
+
7
+ const DEFAULT_DESCRIPTION =
8
+ 'Find a specific document version in any version-enabled collection by passing the collection slug and version ID.'
9
+
10
+ export const findVersionByIDTool = defineCollectionTool({
11
+ access: (args) =>
12
+ defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.readVersions),
13
+ annotations: {
14
+ destructiveHint: false,
15
+ idempotentHint: true,
16
+ openWorldHint: false,
17
+ readOnlyHint: true,
18
+ title: 'Find Version By ID',
19
+ },
20
+ description: DEFAULT_DESCRIPTION,
21
+ input: findVersionByIDInputSchema,
22
+ }).handler(async ({ slug, authorizedMCP, input, req }) => {
23
+ const payload = req.payload
24
+ const logger = getLogger({ payload })
25
+ const { id, depth, draft, fallbackLocale, locale, populate, select, trash } = input
26
+
27
+ logger.info(`Finding version in collection: ${slug} with ID: ${id}`)
28
+
29
+ try {
30
+ const result = await payload.findVersionByID({
31
+ id: String(id),
32
+ collection: slug,
33
+ depth,
34
+ overrideAccess: authorizedMCP.overrideAccess,
35
+ req,
36
+ ...(draft !== undefined ? { draft } : {}),
37
+ ...(fallbackLocale !== undefined ? { fallbackLocale } : {}),
38
+ ...(locale ? { locale } : {}),
39
+ ...(populate ? { populate } : {}),
40
+ ...(select ? { select } : {}),
41
+ ...(trash !== undefined ? { trash } : {}),
42
+ })
43
+
44
+ return {
45
+ content: [
46
+ {
47
+ type: 'text',
48
+ text: `Version "${id}" from collection "${slug}":\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``,
49
+ },
50
+ ],
51
+ doc: result as unknown as Record<string, unknown>,
52
+ }
53
+ } catch (error) {
54
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
55
+ logger.error(`Error finding version ${id} in ${slug}: ${errorMessage}`)
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: `❌ **Error finding version "${id}" in collection "${slug}":** ${errorMessage}`,
61
+ },
62
+ ],
63
+ isError: true,
64
+ }
65
+ }
66
+ })
@@ -0,0 +1,83 @@
1
+ import { findVersionsInputSchema } from 'payload'
2
+
3
+ import { defaultAccess } from '../../../defaultAccess.js'
4
+ import { defineCollectionTool } from '../../../defineTool.js'
5
+ import { getLogger } from '../../../utils/getLogger.js'
6
+
7
+ const DEFAULT_DESCRIPTION =
8
+ 'Find document versions in any version-enabled collection by passing the collection slug and optional where clause.'
9
+
10
+ export const findVersionsTool = defineCollectionTool({
11
+ access: (args) =>
12
+ defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.readVersions),
13
+ annotations: {
14
+ destructiveHint: false,
15
+ idempotentHint: true,
16
+ openWorldHint: false,
17
+ readOnlyHint: true,
18
+ title: 'Find Versions',
19
+ },
20
+ description: DEFAULT_DESCRIPTION,
21
+ input: findVersionsInputSchema,
22
+ }).handler(async ({ slug, authorizedMCP, input, req }) => {
23
+ const payload = req.payload
24
+ const logger = getLogger({ payload })
25
+ const {
26
+ depth,
27
+ draft,
28
+ fallbackLocale,
29
+ limit,
30
+ locale,
31
+ page,
32
+ pagination,
33
+ populate,
34
+ select,
35
+ sort,
36
+ trash,
37
+ where,
38
+ } = input
39
+
40
+ logger.info(`Finding versions in collection: ${slug}, limit: ${limit}, page: ${page}`)
41
+
42
+ try {
43
+ const result = await payload.findVersions({
44
+ collection: slug,
45
+ depth,
46
+ limit,
47
+ overrideAccess: authorizedMCP.overrideAccess,
48
+ page,
49
+ req,
50
+ ...(draft !== undefined ? { draft } : {}),
51
+ ...(fallbackLocale !== undefined ? { fallbackLocale } : {}),
52
+ ...(locale ? { locale } : {}),
53
+ ...(pagination !== undefined ? { pagination } : {}),
54
+ ...(populate ? { populate } : {}),
55
+ ...(select ? { select } : {}),
56
+ ...(sort ? { sort } : {}),
57
+ ...(trash !== undefined ? { trash } : {}),
58
+ ...(where ? { where } : {}),
59
+ })
60
+
61
+ return {
62
+ content: [
63
+ {
64
+ type: 'text',
65
+ text: `Versions for collection "${slug}":\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``,
66
+ },
67
+ ],
68
+ doc: result as unknown as Record<string, unknown>,
69
+ }
70
+ } catch (error) {
71
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
72
+ logger.error(`Error finding versions in ${slug}: ${errorMessage}`)
73
+ return {
74
+ content: [
75
+ {
76
+ type: 'text',
77
+ text: `❌ **Error finding versions in collection "${slug}":** ${errorMessage}`,
78
+ },
79
+ ],
80
+ isError: true,
81
+ }
82
+ }
83
+ })