@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,80 @@
1
+ import { getAccessResults, getCollectionInputSchema, getCollectionSchemaInputSchema } from 'payload'
2
+
3
+ import { defaultAccess } from '../../../defaultAccess.js'
4
+ import { defineCollectionTool } from '../../../defineTool.js'
5
+
6
+ export const getCollectionSchemaTool = defineCollectionTool({
7
+ access: (args) => {
8
+ const permissions = args.permissions?.collections?.[args.slug]
9
+
10
+ return defaultAccess(args) && Boolean(permissions?.create || permissions?.update)
11
+ },
12
+ annotations: {
13
+ destructiveHint: false,
14
+ idempotentHint: true,
15
+ openWorldHint: false,
16
+ readOnlyHint: true,
17
+ title: 'Get Collection Schema',
18
+ },
19
+ description: 'Get the input schema for creating or updating documents in a collection.',
20
+ input: getCollectionSchemaInputSchema,
21
+ }).handler(async ({ slug, authorizedMCP, req }) => {
22
+ const permissions = authorizedMCP.overrideAccess
23
+ ? null
24
+ : (await getAccessResults({ req })).collections?.[slug]
25
+
26
+ if (!authorizedMCP.overrideAccess && !permissions?.create && !permissions?.update) {
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: `Error: MCP access to "getCollectionSchema" is not enabled for collection "${slug}"`,
32
+ },
33
+ ],
34
+ isError: true,
35
+ }
36
+ }
37
+
38
+ const inputSchema = getCollectionInputSchema({
39
+ collectionSlug: slug,
40
+ req,
41
+ ...(permissions ? { permissions } : {}),
42
+ })
43
+
44
+ if (!inputSchema) {
45
+ return {
46
+ content: [{ type: 'text', text: `Error: Collection "${slug}" not found` }],
47
+ isError: true,
48
+ }
49
+ }
50
+
51
+ const uploadConfig = req.payload.collections[slug]?.config.upload
52
+ const maxFileSize = req.payload.config.upload.limits?.fileSize
53
+ const upload = uploadConfig
54
+ ? {
55
+ enabled: true,
56
+ filesRequiredOnCreate: uploadConfig.filesRequiredOnCreate !== false,
57
+ mimeTypes: uploadConfig.mimeTypes ?? ['*/*'],
58
+ sources: [
59
+ ...(uploadConfig.pasteURL !== false ? ['externalURL'] : []),
60
+ 'base64',
61
+ 'uploadReference',
62
+ ],
63
+ ...(typeof maxFileSize === 'number' && Number.isFinite(maxFileSize) ? { maxFileSize } : {}),
64
+ }
65
+ : { enabled: false }
66
+
67
+ return {
68
+ content: [
69
+ {
70
+ type: 'text',
71
+ text: `Schema for collection "${slug}":\n\`\`\`json\n${JSON.stringify(inputSchema)}\n\`\`\`\nUpload configuration:\n\`\`\`json\n${JSON.stringify(upload)}\n\`\`\``,
72
+ },
73
+ ],
74
+ structuredContent: {
75
+ slug,
76
+ schema: inputSchema,
77
+ upload,
78
+ },
79
+ }
80
+ })
@@ -0,0 +1,65 @@
1
+ import { restoreVersionInputSchema } 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
+ 'Restore a document from a previous version in any version-enabled collection.'
9
+
10
+ export const restoreVersionTool = defineCollectionTool({
11
+ access: (args) =>
12
+ defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.update),
13
+ annotations: {
14
+ destructiveHint: true,
15
+ idempotentHint: false,
16
+ openWorldHint: false,
17
+ readOnlyHint: false,
18
+ title: 'Restore Version',
19
+ },
20
+ description: DEFAULT_DESCRIPTION,
21
+ input: restoreVersionInputSchema,
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 } = input
26
+
27
+ logger.info(`Restoring version in collection: ${slug} with ID: ${id}`)
28
+
29
+ try {
30
+ const result = await payload.restoreVersion({
31
+ id: String(id),
32
+ collection: slug,
33
+ depth,
34
+ draft,
35
+ overrideAccess: authorizedMCP.overrideAccess,
36
+ req,
37
+ ...(fallbackLocale !== undefined ? { fallbackLocale } : {}),
38
+ ...(locale ? { locale } : {}),
39
+ ...(populate ? { populate } : {}),
40
+ ...(select ? { select } : {}),
41
+ })
42
+
43
+ return {
44
+ content: [
45
+ {
46
+ type: 'text',
47
+ text: `Version "${id}" restored successfully in collection "${slug}"!\nRestored document:\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``,
48
+ },
49
+ ],
50
+ doc: result as Record<string, unknown>,
51
+ }
52
+ } catch (error) {
53
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
54
+ logger.error(`Error restoring version ${id} in ${slug}: ${errorMessage}`)
55
+ return {
56
+ content: [
57
+ {
58
+ type: 'text',
59
+ text: `❌ **Error restoring version "${id}" in collection "${slug}":** ${errorMessage}`,
60
+ },
61
+ ],
62
+ isError: true,
63
+ }
64
+ }
65
+ })
@@ -1,84 +1,36 @@
1
- import type { SelectType } from 'payload'
1
+ import type { Where } from 'payload'
2
2
 
3
- import { z } from 'zod'
4
-
5
- import { defineCollectionTool } from '../../../defineTool.js'
6
- import { getLogger } from '../../../utils/getLogger.js'
7
3
  import {
4
+ getCollectionInputSchema,
8
5
  getCollectionVirtualFieldNames,
9
6
  stripVirtualFields,
10
- } from '../../../utils/getVirtualFieldNames.js'
11
- import { localAPIDefaults } from '../../../utils/localAPIDefaults.js'
12
- import { buildToolInput } from '../../../utils/schemaConversion/buildToolInput.js'
13
- import { sanitizeEntitySchema } from '../../../utils/schemaConversion/sanitizeEntitySchema.js'
14
- import { transformPointDataToPayload } from '../../../utils/transformPointDataToPayload.js'
15
-
16
- const DEFAULT_DESCRIPTION = 'Update documents in a collection by ID or where clause.'
7
+ transformPointDataToPayload,
8
+ updateDocumentInputSchema,
9
+ validateCollectionData,
10
+ } from 'payload'
17
11
 
18
- export const updateCollectionTool = defineCollectionTool({
19
- description: DEFAULT_DESCRIPTION,
20
- input: ({ collectionSchema }) => {
21
- const partialSchema = sanitizeEntitySchema(collectionSchema)
22
-
23
- // Collection updates do not require all required fields to be passed => delete .required.
24
- //
25
- // Local API equivalent: packages/payload/src/collections/operations/local/update.ts#BaseOptions#data:
26
- // data: DeepPartial<RequiredDataFromCollectionSlug<TSlug>>
27
- delete partialSchema.required
28
-
29
- return buildToolInput({
30
- controls: {
31
- id: z
32
- .union([z.string(), z.number()])
33
- .describe('The ID of the document to update')
34
- .optional(),
35
- depth: z
36
- .number()
37
- .describe('How many levels deep to populate relationships')
38
- .optional()
39
- .default(0),
40
- draft: z
41
- .boolean()
42
- .describe('Whether to update the document as a draft')
43
- .optional()
44
- .default(false),
45
- fallbackLocale: z
46
- .string()
47
- .describe('Optional: fallback locale code to use when requested locale is not available')
48
- .optional(),
49
- filePath: z.string().describe('File path for file uploads').optional(),
50
- locale: z
51
- .string()
52
- .describe(
53
- 'Optional: locale code to update the document in (e.g., "en", "es"). Defaults to the default locale',
54
- )
55
- .optional(),
56
- overrideLock: z
57
- .boolean()
58
- .describe('Whether to override document locks')
59
- .optional()
60
- .default(true),
61
- overwriteExistingFiles: z
62
- .boolean()
63
- .describe('Whether to overwrite existing files')
64
- .optional()
65
- .default(false),
66
- select: z
67
- .string()
68
- .describe(
69
- 'Optional: define exactly which fields you\'d like to return in the response (JSON), e.g., \'{"title": "My Post"}\'',
70
- )
71
- .optional(),
72
- where: z
73
- .string()
74
- .describe('JSON string for where clause to update multiple documents')
75
- .optional(),
76
- },
77
- dataDescription: 'The fields to update',
78
- dataSchema: partialSchema,
79
- })
12
+ import { defaultAccess } from '../../../defaultAccess.js'
13
+ import { defineCollectionTool } from '../../../defineTool.js'
14
+ import { getLogger } from '../../../utils/getLogger.js'
15
+ import { formatEntityError } from '../formatEntityError.js'
16
+ import { fileInputSchema, resolveFile } from './fileInput.js'
17
+
18
+ const DEFAULT_DESCRIPTION =
19
+ 'Update documents. Prefer uploadReference after upload, externalURL for URLs, or base64 for small local files.'
20
+
21
+ export const updateDocumentTool = defineCollectionTool({
22
+ access: (args) =>
23
+ defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.update),
24
+ annotations: {
25
+ destructiveHint: true,
26
+ idempotentHint: false,
27
+ openWorldHint: false,
28
+ readOnlyHint: false,
29
+ title: 'Update Document',
80
30
  },
81
- }).handler(async ({ authorizedMCP, collectionSlug, input, req }) => {
31
+ description: DEFAULT_DESCRIPTION,
32
+ input: updateDocumentInputSchema({ file: fileInputSchema }),
33
+ }).handler(async ({ slug, authorizedMCP, input, req }) => {
82
34
  const payload = req.payload
83
35
  const logger = getLogger({ payload })
84
36
 
@@ -88,107 +40,121 @@ export const updateCollectionTool = defineCollectionTool({
88
40
  depth,
89
41
  draft,
90
42
  fallbackLocale,
91
- filePath,
43
+ file: fileInput,
44
+ limit,
92
45
  locale,
93
46
  overrideLock,
94
- overwriteExistingFiles,
47
+ populate,
48
+ publishAllLocales,
95
49
  select,
50
+ sort,
51
+ trash,
52
+ unpublishAllLocales,
96
53
  where,
97
54
  } = input
98
55
 
99
56
  logger.info(
100
- `Updating document in collection: ${collectionSlug}${id ? ` with ID: ${id}` : ' with where clause'}, draft: ${draft}${locale ? `, locale: ${locale}` : ''}`,
57
+ `Updating document in collection: ${slug}${id ? ` with ID: ${id}` : ' with where clause'}, draft: ${draft}${locale ? `, locale: ${locale}` : ''}`,
101
58
  )
102
59
 
103
60
  try {
104
- if (!id && !where) {
105
- return {
106
- content: [{ type: 'text', text: 'Error: Either id or where clause must be provided' }],
107
- }
108
- }
61
+ const virtualFieldNames = getCollectionVirtualFieldNames(payload.config, slug)
62
+ const inputData = stripVirtualFields(data, virtualFieldNames)
63
+ validateCollectionData({
64
+ slug,
65
+ data: inputData,
66
+ partial: true,
67
+ req,
68
+ })
109
69
 
110
- let parsedData = transformPointDataToPayload(data)
111
- const virtualFieldNames = getCollectionVirtualFieldNames(payload.config, collectionSlug)
112
- parsedData = stripVirtualFields(parsedData, virtualFieldNames)
113
-
114
- let whereClause: Record<string, unknown> = {}
115
- if (where) {
116
- try {
117
- whereClause = JSON.parse(where) as Record<string, unknown>
118
- } catch {
119
- logger.error(`Invalid where clause JSON: ${where}`)
120
- return { content: [{ type: 'text', text: 'Error: Invalid JSON in where clause' }] }
121
- }
122
- }
70
+ const parsedData = transformPointDataToPayload(inputData)
71
+ const file = await resolveFile({ slug, input: fileInput, req })
123
72
 
124
- let selectClause: SelectType | undefined
125
- if (select) {
126
- try {
127
- selectClause = JSON.parse(select) as SelectType
128
- } catch {
129
- logger.warn(`Invalid select clause JSON: ${select}`)
130
- return { content: [{ type: 'text', text: 'Error: Invalid JSON in select clause' }] }
131
- }
132
- }
73
+ const whereClause: Where = where ?? {}
133
74
 
134
- if (id) {
135
- const updateOptions = {
75
+ if (id !== undefined) {
76
+ const result = await payload.update({
136
77
  id,
137
- collection: collectionSlug,
78
+ collection: slug,
138
79
  data: parsedData,
139
80
  depth,
140
81
  draft,
82
+ fallbackLocale,
83
+ locale,
84
+ overrideAccess: authorizedMCP.overrideAccess,
141
85
  overrideLock,
86
+ populate,
87
+ publishAllLocales,
142
88
  req,
143
- ...localAPIDefaults(authorizedMCP),
144
- ...(filePath ? { filePath } : {}),
145
- ...(overwriteExistingFiles ? { overwriteExistingFiles } : {}),
146
- ...(locale ? { locale } : {}),
147
- ...(fallbackLocale ? { fallbackLocale } : {}),
148
- ...(selectClause ? { select: selectClause } : {}),
149
- }
150
-
151
- const result = await payload.update(updateOptions as any)
89
+ select,
90
+ trash,
91
+ unpublishAllLocales,
92
+ ...(file ? { file } : {}),
93
+ })
152
94
 
153
95
  return {
154
96
  content: [
155
97
  {
156
98
  type: 'text',
157
- text: `Document updated successfully in collection "${collectionSlug}"!\nUpdated document:\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``,
99
+ text: `Document updated successfully in collection "${slug}"!\nUpdated document:\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``,
158
100
  },
159
101
  ],
160
102
  doc: result as Record<string, unknown>,
161
103
  }
162
104
  }
163
105
 
164
- const updateOptions = {
165
- collection: collectionSlug,
106
+ const result = await payload.update({
107
+ collection: slug,
166
108
  data: parsedData,
167
109
  depth,
168
110
  draft,
111
+ fallbackLocale,
112
+ limit,
113
+ locale,
114
+ overrideAccess: authorizedMCP.overrideAccess,
169
115
  overrideLock,
116
+ populate,
117
+ publishAllLocales,
170
118
  req,
171
- ...localAPIDefaults(authorizedMCP),
119
+ select,
120
+ sort,
121
+ trash,
122
+ unpublishAllLocales,
172
123
  where: whereClause,
173
- ...(filePath ? { filePath } : {}),
174
- ...(overwriteExistingFiles ? { overwriteExistingFiles } : {}),
175
- ...(locale ? { locale } : {}),
176
- ...(fallbackLocale ? { fallbackLocale } : {}),
177
- ...(selectClause ? { select: selectClause } : {}),
178
- }
179
-
180
- const result = await payload.update(updateOptions as any)
124
+ ...(file ? { file } : {}),
125
+ })
181
126
 
182
- const bulkResult = result as { docs?: unknown[]; errors?: unknown[] }
183
- const docs = bulkResult.docs || []
184
- const errors = bulkResult.errors || []
127
+ const docs = result.docs || []
128
+ const errors = result.errors || []
185
129
 
186
- let responseText = `Multiple documents updated in collection "${collectionSlug}"!\nUpdated: ${docs.length} documents\nErrors: ${errors.length}\n---`
130
+ let responseText = `Multiple documents updated in collection "${slug}"!\nUpdated: ${docs.length} documents\nErrors: ${errors.length}\n---`
187
131
  if (docs.length > 0) {
188
132
  responseText += `\n\nUpdated documents:\n\`\`\`json\n${JSON.stringify(docs)}\n\`\`\``
189
133
  }
190
134
  if (errors.length > 0) {
191
135
  responseText += `\n\nErrors:\n\`\`\`json\n${JSON.stringify(errors)}\n\`\`\``
136
+
137
+ const errorSchema = getCollectionInputSchema({ collectionSlug: slug, req })
138
+
139
+ if (errorSchema) {
140
+ responseText += `\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(errorSchema)}\n\`\`\``
141
+ }
142
+
143
+ return {
144
+ content: [{ type: 'text', text: responseText }],
145
+ doc: { docs, errors } as unknown as Record<string, unknown>,
146
+ isError: true,
147
+ ...(errorSchema
148
+ ? {
149
+ structuredContent: {
150
+ slug,
151
+ docs,
152
+ errors,
153
+ schema: errorSchema,
154
+ },
155
+ }
156
+ : {}),
157
+ }
192
158
  }
193
159
 
194
160
  return {
@@ -197,14 +163,7 @@ export const updateCollectionTool = defineCollectionTool({
197
163
  }
198
164
  } catch (error) {
199
165
  const errorMessage = error instanceof Error ? error.message : 'Unknown error'
200
- logger.error(`Error updating document in ${collectionSlug}: ${errorMessage}`)
201
- return {
202
- content: [
203
- {
204
- type: 'text',
205
- text: `Error updating document in collection "${collectionSlug}": ${errorMessage}`,
206
- },
207
- ],
208
- }
166
+ logger.error(`Error updating document in ${slug}: ${errorMessage}`)
167
+ return formatEntityError({ slug, action: 'updating', entity: 'collection', error, req })
209
168
  }
210
169
  })
@@ -0,0 +1,64 @@
1
+ import { strictObject, z } from 'payload'
2
+ import { getUploadInstructions as getPayloadUploadInstructions } from 'payload/internal'
3
+
4
+ import { defaultAccess } from '../../../defaultAccess.js'
5
+ import { defineCollectionTool } from '../../../defineTool.js'
6
+
7
+ export const getUploadInstructionsTool = defineCollectionTool({
8
+ access: (args) =>
9
+ defaultAccess(args) &&
10
+ Boolean(
11
+ args.permissions?.collections?.[args.slug]?.create ||
12
+ args.permissions?.collections?.[args.slug]?.update,
13
+ ),
14
+ annotations: {
15
+ destructiveHint: false,
16
+ idempotentHint: false,
17
+ openWorldHint: true,
18
+ readOnlyHint: false,
19
+ title: 'Get Upload Instructions',
20
+ },
21
+ description:
22
+ 'Prepare uploads for createDocuments or updateDocument. This does not upload bytes; finish the returned action before use.',
23
+ input: strictObject({
24
+ docPrefix: z.optional(z.string()).check(z.describe('Document folder or prefix.')),
25
+ filename: z.string().check(z.describe('The original file name.')),
26
+ filesize: z.int().check(z.nonnegative(), z.describe('The file size in bytes.')),
27
+ mimeType: z.string().check(z.describe('The file MIME type.')),
28
+ }),
29
+ }).handler(async ({ slug, authorizedMCP, input, req }) => {
30
+ try {
31
+ const instructions = await getPayloadUploadInstructions({
32
+ ...input,
33
+ collectionSlug: slug,
34
+ overrideAccess: authorizedMCP.overrideAccess,
35
+ req,
36
+ })
37
+
38
+ const nextStep =
39
+ instructions.type === 'http'
40
+ ? 'Upload bytes with instructions.request. After success, pass { source: "uploadReference", file: instructions.file }.'
41
+ : `Call "${instructions.name}" with file and data. After success, pass { source: "uploadReference", file: instructions.file }.`
42
+
43
+ return {
44
+ content: [
45
+ {
46
+ type: 'text',
47
+ text: `Upload instructions for collection "${slug}":\n\`\`\`json\n${JSON.stringify(instructions)}\n\`\`\`\n${nextStep}`,
48
+ },
49
+ ],
50
+ structuredContent: { instructions },
51
+ }
52
+ } catch (error) {
53
+ const message = error instanceof Error ? error.message : 'Unknown error'
54
+ return {
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: `Error getting upload instructions for collection "${slug}": ${message}`,
59
+ },
60
+ ],
61
+ isError: true,
62
+ }
63
+ }
64
+ })
@@ -0,0 +1,77 @@
1
+ import type { CollectionSlug, GlobalSlug, PayloadRequest } from 'payload'
2
+
3
+ import { getCollectionInputSchema, getGlobalInputSchema, ValidationError } from 'payload'
4
+
5
+ import type { MCPToolResponse } from '../../types.js'
6
+
7
+ export const formatEntityError = ({
8
+ slug,
9
+ action,
10
+ entity,
11
+ error,
12
+ req,
13
+ }:
14
+ | {
15
+ action: 'creating' | 'duplicating' | 'updating'
16
+ entity: 'collection'
17
+ error: unknown
18
+ req: PayloadRequest
19
+ slug: CollectionSlug
20
+ }
21
+ | {
22
+ action: 'updating'
23
+ entity: 'global'
24
+ error: unknown
25
+ req: PayloadRequest
26
+ slug: GlobalSlug
27
+ }): MCPToolResponse => {
28
+ const message = error instanceof Error ? error.message : 'Unknown error'
29
+ const target = entity === 'collection' ? `document in collection "${slug}"` : `global "${slug}"`
30
+
31
+ if (!isValidationError(error, message)) {
32
+ return {
33
+ content: [{ type: 'text', text: `Error ${action} ${target}: ${message}` }],
34
+ isError: true,
35
+ }
36
+ }
37
+
38
+ const schema =
39
+ entity === 'collection'
40
+ ? getCollectionInputSchema({ collectionSlug: slug, req })
41
+ : getGlobalInputSchema({ globalSlug: slug, req })
42
+ const errors = error instanceof ValidationError ? error.data.errors : [{ message }]
43
+ const validationMessage = errors
44
+ .map((issue) => `${'path' in issue ? `${issue.path}: ` : ''}${issue.message}`)
45
+ .join('\n')
46
+ const schemaText = schema
47
+ ? `\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``
48
+ : ''
49
+
50
+ return {
51
+ content: [
52
+ { type: 'text', text: `Error ${action} ${target}: ${validationMessage}${schemaText}` },
53
+ ],
54
+ isError: true,
55
+ ...(schema
56
+ ? {
57
+ structuredContent: {
58
+ slug,
59
+ errors,
60
+ schema,
61
+ },
62
+ }
63
+ : {}),
64
+ }
65
+ }
66
+
67
+ const isValidationError = (error: unknown, message: string): boolean => {
68
+ if (error instanceof ValidationError) {
69
+ return true
70
+ }
71
+
72
+ const name = error && typeof error === 'object' && 'name' in error ? error.name : undefined
73
+
74
+ return (
75
+ name === 'CastError' || message.includes('Cast to ') || message.includes('validation failed')
76
+ )
77
+ }
@@ -0,0 +1,68 @@
1
+ import { getAccessResults, getConfigInfoInputSchema, isEntityHidden } from 'payload'
2
+
3
+ import { defineTool } from '../../defineTool.js'
4
+
5
+ export const getConfigInfoTool = defineTool({
6
+ annotations: {
7
+ destructiveHint: false,
8
+ idempotentHint: true,
9
+ openWorldHint: false,
10
+ readOnlyHint: true,
11
+ title: 'Config Info',
12
+ },
13
+ description: 'List the Payload collection and global slugs visible to this MCP client.',
14
+ input: getConfigInfoInputSchema,
15
+ }).handler(async ({ authorizedMCP, req }) => {
16
+ const user = req.user
17
+ const permissions = authorizedMCP.overrideAccess ? null : await getAccessResults({ req })
18
+ const authorizedCollectionSlugs = new Set<string>()
19
+ const authorizedGlobalSlugs = new Set<string>()
20
+
21
+ for (const item of authorizedMCP.items) {
22
+ if (item.type === 'collectionTool') {
23
+ authorizedCollectionSlugs.add(item.collectionSlug)
24
+ } else if (item.type === 'globalTool') {
25
+ authorizedGlobalSlugs.add(item.globalSlug)
26
+ }
27
+ }
28
+
29
+ const collections: string[] = []
30
+ const globals: string[] = []
31
+
32
+ for (const collection of req.payload.config.collections) {
33
+ if (!authorizedCollectionSlugs.has(collection.slug)) {
34
+ continue
35
+ }
36
+ if (user && isEntityHidden({ hidden: collection.admin.hidden, user })) {
37
+ continue
38
+ }
39
+ if (!authorizedMCP.overrideAccess && !permissions?.collections?.[collection.slug]?.read) {
40
+ continue
41
+ }
42
+
43
+ collections.push(collection.slug)
44
+ }
45
+
46
+ for (const global of req.payload.config.globals) {
47
+ if (!authorizedGlobalSlugs.has(global.slug)) {
48
+ continue
49
+ }
50
+ if (user && isEntityHidden({ hidden: global.admin.hidden, user })) {
51
+ continue
52
+ }
53
+ if (!authorizedMCP.overrideAccess && !permissions?.globals?.[global.slug]?.read) {
54
+ continue
55
+ }
56
+
57
+ globals.push(global.slug)
58
+ }
59
+
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: `Collections: ${collections.join(', ') || 'none'}\nGlobals: ${globals.join(', ') || 'none'}`,
65
+ },
66
+ ],
67
+ }
68
+ })