@payloadcms/plugin-mcp 4.0.0-internal.ccea85d → 4.0.0-internal.cd47d20

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 (171) hide show
  1. package/dist/endpoint/access.js +2 -2
  2. package/dist/endpoint/access.js.map +1 -1
  3. package/dist/mcp/buildMcpServer.d.ts +2 -2
  4. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  5. package/dist/mcp/buildMcpServer.js +36 -24
  6. package/dist/mcp/buildMcpServer.js.map +1 -1
  7. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  8. package/dist/mcp/builtin/collections/authTools.js +80 -56
  9. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  10. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -1
  11. package/dist/mcp/builtin/collections/countTool.js +9 -14
  12. package/dist/mcp/builtin/collections/countTool.js.map +1 -1
  13. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -1
  14. package/dist/mcp/builtin/collections/countVersionsTool.js +9 -13
  15. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -1
  16. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  17. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  18. package/dist/mcp/builtin/collections/createTool.js +104 -63
  19. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  20. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  21. package/dist/mcp/builtin/collections/deleteTool.js +44 -43
  22. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  23. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -1
  24. package/dist/mcp/builtin/collections/duplicateTool.js +31 -37
  25. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -1
  26. package/dist/mcp/builtin/collections/fileInput.d.ts +21 -13
  27. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -1
  28. package/dist/mcp/builtin/collections/fileInput.js +52 -64
  29. package/dist/mcp/builtin/collections/fileInput.js.map +1 -1
  30. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -1
  31. package/dist/mcp/builtin/collections/findDistinctTool.js +11 -26
  32. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -1
  33. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  34. package/dist/mcp/builtin/collections/findTool.js +27 -45
  35. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  36. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -1
  37. package/dist/mcp/builtin/collections/findVersionByIDTool.js +17 -26
  38. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -1
  39. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -1
  40. package/dist/mcp/builtin/collections/findVersionsTool.js +16 -30
  41. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -1
  42. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -1
  43. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +15 -14
  44. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -1
  45. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -1
  46. package/dist/mcp/builtin/collections/restoreVersionTool.js +14 -26
  47. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -1
  48. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  49. package/dist/mcp/builtin/collections/updateTool.js +58 -90
  50. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  51. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  52. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  53. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  54. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  55. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  56. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  57. package/dist/mcp/builtin/formatEntityError.js +55 -0
  58. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  59. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -1
  60. package/dist/mcp/builtin/getConfigInfoTool.js +3 -2
  61. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -1
  62. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -1
  63. package/dist/mcp/builtin/globals/countVersionsTool.js +9 -13
  64. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -1
  65. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  66. package/dist/mcp/builtin/globals/findTool.js +10 -16
  67. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  68. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -1
  69. package/dist/mcp/builtin/globals/findVersionByIDTool.js +13 -24
  70. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -1
  71. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -1
  72. package/dist/mcp/builtin/globals/findVersionsTool.js +13 -29
  73. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -1
  74. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -1
  75. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +11 -11
  76. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -1
  77. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -1
  78. package/dist/mcp/builtin/globals/restoreVersionTool.js +15 -22
  79. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -1
  80. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  81. package/dist/mcp/builtin/globals/updateTool.js +27 -36
  82. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  83. package/dist/mcp/builtinTools.d.ts +6 -0
  84. package/dist/mcp/builtinTools.d.ts.map +1 -1
  85. package/dist/mcp/builtinTools.js +9 -3
  86. package/dist/mcp/builtinTools.js.map +1 -1
  87. package/dist/mcp/sanitizeMCPConfig.js +4 -1
  88. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  89. package/dist/stdio.d.ts.map +1 -1
  90. package/dist/stdio.js +6 -4
  91. package/dist/stdio.js.map +1 -1
  92. package/dist/types.d.ts +4 -4
  93. package/dist/types.d.ts.map +1 -1
  94. package/dist/types.js.map +1 -1
  95. package/package.json +6 -8
  96. package/src/endpoint/access.ts +2 -3
  97. package/src/mcp/buildMcpServer.ts +40 -30
  98. package/src/mcp/builtin/collections/authTools.ts +80 -69
  99. package/src/mcp/builtin/collections/countTool.ts +9 -21
  100. package/src/mcp/builtin/collections/countVersionsTool.ts +9 -18
  101. package/src/mcp/builtin/collections/createTool.ts +110 -86
  102. package/src/mcp/builtin/collections/deleteTool.ts +36 -64
  103. package/src/mcp/builtin/collections/duplicateTool.ts +35 -85
  104. package/src/mcp/builtin/collections/fileInput.spec.ts +20 -6
  105. package/src/mcp/builtin/collections/fileInput.ts +64 -76
  106. package/src/mcp/builtin/collections/findDistinctTool.ts +11 -63
  107. package/src/mcp/builtin/collections/findTool.ts +23 -105
  108. package/src/mcp/builtin/collections/findVersionByIDTool.ts +15 -58
  109. package/src/mcp/builtin/collections/findVersionsTool.ts +14 -85
  110. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +16 -12
  111. package/src/mcp/builtin/collections/restoreVersionTool.ts +14 -56
  112. package/src/mcp/builtin/collections/updateTool.ts +57 -111
  113. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  114. package/src/mcp/builtin/formatEntityError.ts +77 -0
  115. package/src/mcp/builtin/getConfigInfoTool.ts +2 -1
  116. package/src/mcp/builtin/globals/countVersionsTool.ts +9 -17
  117. package/src/mcp/builtin/globals/findTool.ts +12 -51
  118. package/src/mcp/builtin/globals/findVersionByIDTool.ts +13 -52
  119. package/src/mcp/builtin/globals/findVersionsTool.ts +14 -92
  120. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +10 -10
  121. package/src/mcp/builtin/globals/restoreVersionTool.ts +14 -45
  122. package/src/mcp/builtin/globals/updateTool.ts +37 -60
  123. package/src/mcp/builtinTools.ts +9 -2
  124. package/src/mcp/sanitizeMCPConfig.ts +4 -1
  125. package/src/stdio.ts +4 -3
  126. package/src/types.ts +4 -4
  127. package/dist/mcp/builtin/collections/fileInput.spec.js +0 -72
  128. package/dist/mcp/builtin/collections/fileInput.spec.js.map +0 -1
  129. package/dist/mcp/builtin/collections/formatCollectionError.d.ts +0 -9
  130. package/dist/mcp/builtin/collections/formatCollectionError.d.ts.map +0 -1
  131. package/dist/mcp/builtin/collections/formatCollectionError.js +0 -60
  132. package/dist/mcp/builtin/collections/formatCollectionError.js.map +0 -1
  133. package/dist/mcp/builtin/validateEntityData.d.ts +0 -14
  134. package/dist/mcp/builtin/validateEntityData.d.ts.map +0 -1
  135. package/dist/mcp/builtin/validateEntityData.js +0 -82
  136. package/dist/mcp/builtin/validateEntityData.js.map +0 -1
  137. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  138. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  139. package/dist/utils/getVirtualFieldNames.js +0 -35
  140. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  141. package/dist/utils/schemaConversion/filterFieldsByAccess.d.ts +0 -28
  142. package/dist/utils/schemaConversion/filterFieldsByAccess.d.ts.map +0 -1
  143. package/dist/utils/schemaConversion/filterFieldsByAccess.js +0 -71
  144. package/dist/utils/schemaConversion/filterFieldsByAccess.js.map +0 -1
  145. package/dist/utils/schemaConversion/getEntityInputSchema.d.ts +0 -13
  146. package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +0 -1
  147. package/dist/utils/schemaConversion/getEntityInputSchema.js +0 -66
  148. package/dist/utils/schemaConversion/getEntityInputSchema.js.map +0 -1
  149. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -10
  150. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  151. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -384
  152. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  153. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -133
  154. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  155. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  156. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  157. package/dist/utils/transformPointDataToPayload.js +0 -28
  158. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  159. package/dist/utils/whereSchema.d.ts +0 -9
  160. package/dist/utils/whereSchema.d.ts.map +0 -1
  161. package/dist/utils/whereSchema.js +0 -13
  162. package/dist/utils/whereSchema.js.map +0 -1
  163. package/src/mcp/builtin/collections/formatCollectionError.ts +0 -84
  164. package/src/mcp/builtin/validateEntityData.ts +0 -132
  165. package/src/utils/getVirtualFieldNames.ts +0 -53
  166. package/src/utils/schemaConversion/filterFieldsByAccess.ts +0 -123
  167. package/src/utils/schemaConversion/getEntityInputSchema.ts +0 -96
  168. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -96
  169. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -432
  170. package/src/utils/transformPointDataToPayload.ts +0 -40
  171. package/src/utils/whereSchema.ts +0 -24
@@ -1 +0,0 @@
1
- {"version":3,"file":"whereSchema.d.ts","sourceRoot":"","sources":["../../src/utils/whereSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAGpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CASqD,CAAA"}
@@ -1,13 +0,0 @@
1
- import { validOperators } from 'payload/shared';
2
- import { z } from 'zod';
3
- const whereFieldSchema = z.partialRecord(z.enum(validOperators), z.unknown()).describe('Field query operators');
4
- /**
5
- * - Validates the `where` input of collection tools against Payload's `Where` shape
6
- * - Field keys map to operator objects restricted to `validOperators`
7
- * - `and` / `or` nest recursively
8
- */ export const whereSchema = z.lazy(()=>z.object({
9
- and: z.array(whereSchema).optional(),
10
- or: z.array(whereSchema).optional()
11
- }).catchall(whereFieldSchema)).describe('Where clause using field names with Payload query operators, plus and/or groups');
12
-
13
- //# sourceMappingURL=whereSchema.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils/whereSchema.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport { validOperators } from 'payload/shared'\nimport { z } from 'zod'\n\nconst whereFieldSchema = z\n .partialRecord(z.enum(validOperators), z.unknown())\n .describe('Field query operators')\n\n/**\n * - Validates the `where` input of collection tools against Payload's `Where` shape\n * - Field keys map to operator objects restricted to `validOperators`\n * - `and` / `or` nest recursively\n */\nexport const whereSchema: z.ZodType<Where> = z\n .lazy(() =>\n z\n .object({\n and: z.array(whereSchema).optional(),\n or: z.array(whereSchema).optional(),\n })\n .catchall(whereFieldSchema),\n )\n .describe('Where clause using field names with Payload query operators, plus and/or groups')\n"],"names":["validOperators","z","whereFieldSchema","partialRecord","enum","unknown","describe","whereSchema","lazy","object","and","array","optional","or","catchall"],"mappings":"AAEA,SAASA,cAAc,QAAQ,iBAAgB;AAC/C,SAASC,CAAC,QAAQ,MAAK;AAEvB,MAAMC,mBAAmBD,EACtBE,aAAa,CAACF,EAAEG,IAAI,CAACJ,iBAAiBC,EAAEI,OAAO,IAC/CC,QAAQ,CAAC;AAEZ;;;;CAIC,GACD,OAAO,MAAMC,cAAgCN,EAC1CO,IAAI,CAAC,IACJP,EACGQ,MAAM,CAAC;QACNC,KAAKT,EAAEU,KAAK,CAACJ,aAAaK,QAAQ;QAClCC,IAAIZ,EAAEU,KAAK,CAACJ,aAAaK,QAAQ;IACnC,GACCE,QAAQ,CAACZ,mBAEbI,QAAQ,CAAC,mFAAkF"}
@@ -1,84 +0,0 @@
1
- import type { CollectionSlug, PayloadRequest } from 'payload'
2
-
3
- import type { MCPToolResponse } from '../../../types.js'
4
-
5
- import { getCollectionInputSchema } from '../../../utils/schemaConversion/getEntityInputSchema.js'
6
-
7
- const getValidationErrors = (error: unknown): undefined | unknown[] => {
8
- if (!error || typeof error !== 'object') {
9
- return undefined
10
- }
11
-
12
- const data = 'data' in error ? error.data : undefined
13
- if (!data || typeof data !== 'object' || !('errors' in data) || !Array.isArray(data.errors)) {
14
- return undefined
15
- }
16
-
17
- return data.errors
18
- }
19
-
20
- const isSchemaError = (error: unknown, message: string): boolean => {
21
- if (getValidationErrors(error)) {
22
- return true
23
- }
24
-
25
- const name = error && typeof error === 'object' && 'name' in error ? error.name : undefined
26
-
27
- return (
28
- name === 'CastError' ||
29
- name === 'ValidationError' ||
30
- message.includes('Cast to ') ||
31
- message.includes('validation failed')
32
- )
33
- }
34
-
35
- export const formatCollectionError = ({
36
- action,
37
- collectionSlug,
38
- error,
39
- req,
40
- }: {
41
- action: 'creating' | 'duplicating' | 'updating'
42
- collectionSlug: CollectionSlug
43
- error: unknown
44
- req: PayloadRequest
45
- }): MCPToolResponse => {
46
- const errorMessage = error instanceof Error ? error.message : 'Unknown error'
47
- const errors = getValidationErrors(error) ?? [{ message: errorMessage }]
48
-
49
- if (!isSchemaError(error, errorMessage)) {
50
- return {
51
- content: [
52
- {
53
- type: 'text',
54
- text: `Error ${action} document in collection "${collectionSlug}": ${errorMessage}`,
55
- },
56
- ],
57
- isError: true,
58
- }
59
- }
60
-
61
- const schema = getCollectionInputSchema({ collectionSlug, req })
62
- const schemaText = schema
63
- ? `\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``
64
- : ''
65
-
66
- return {
67
- content: [
68
- {
69
- type: 'text',
70
- text: `Error ${action} document in collection "${collectionSlug}": ${errorMessage}${schemaText}`,
71
- },
72
- ],
73
- isError: true,
74
- ...(schema
75
- ? {
76
- structuredContent: {
77
- collectionSlug,
78
- errors,
79
- schema,
80
- },
81
- }
82
- : {}),
83
- }
84
- }
@@ -1,132 +0,0 @@
1
- import type { CollectionSlug, GlobalSlug, PayloadRequest, SanitizedConfig } from 'payload'
2
-
3
- import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'
4
-
5
- import type { JsonSchemaType, MCPToolResponse } from '../../types.js'
6
-
7
- import {
8
- getCollectionInputSchema,
9
- getGlobalInputSchema,
10
- } from '../../utils/schemaConversion/getEntityInputSchema.js'
11
-
12
- /**
13
- * Keep the default `shortcircuit: true` - `false` makes validating nested rich text exponentially slow.
14
- */
15
- const validator = new CfWorkerJsonSchemaValidator({ shortcircuit: true })
16
-
17
- type EntityValidator = {
18
- schema: JsonSchemaType
19
- validate: ReturnType<(typeof validator)['getValidator']>
20
- }
21
-
22
- /**
23
- * - Caches each entity's schema and validator, so both are only built on the first request.
24
- * - Keyed by payload config, then by entity type, slug, partial and language.
25
- */
26
- const validatorCache = new WeakMap<SanitizedConfig, Map<string, EntityValidator>>()
27
-
28
- export const validateCollectionData = ({
29
- collectionSlug,
30
- data,
31
- partial,
32
- req,
33
- }: {
34
- collectionSlug: CollectionSlug
35
- data: Record<string, unknown>
36
- partial?: boolean
37
- req: PayloadRequest
38
- }): MCPToolResponse | null =>
39
- validateData({
40
- slug: collectionSlug,
41
- buildSchema: () => getCollectionInputSchema({ collectionSlug, req }),
42
- data,
43
- entity: 'collection',
44
- partial,
45
- req,
46
- })
47
-
48
- export const validateGlobalData = ({
49
- data,
50
- globalSlug,
51
- req,
52
- }: {
53
- data: Record<string, unknown>
54
- globalSlug: GlobalSlug
55
- req: PayloadRequest
56
- }): MCPToolResponse | null =>
57
- validateData({
58
- slug: globalSlug,
59
- buildSchema: () => getGlobalInputSchema({ globalSlug, req }),
60
- data,
61
- entity: 'global',
62
- partial: true,
63
- req,
64
- })
65
-
66
- const validateData = ({
67
- slug,
68
- buildSchema,
69
- data,
70
- entity,
71
- partial,
72
- req,
73
- }: {
74
- buildSchema: () => JsonSchemaType | null
75
- data: Record<string, unknown>
76
- entity: 'collection' | 'global'
77
- partial?: boolean
78
- req: PayloadRequest
79
- slug: string
80
- }): MCPToolResponse | null => {
81
- let cache = validatorCache.get(req.payload.config)
82
- if (!cache) {
83
- cache = new Map()
84
- validatorCache.set(req.payload.config, cache)
85
- }
86
-
87
- const cacheKey = `${entity}:${slug}:${partial ? 'partial' : 'full'}:${req.i18n.language}`
88
- let entityValidator = cache.get(cacheKey)
89
-
90
- if (!entityValidator) {
91
- const schema = buildSchema()
92
-
93
- if (!schema) {
94
- return null
95
- }
96
-
97
- entityValidator = {
98
- schema,
99
- validate: validator.getValidator(partial ? withoutRequired(schema) : schema),
100
- }
101
- cache.set(cacheKey, entityValidator)
102
- }
103
-
104
- const result = entityValidator.validate(data)
105
-
106
- if (result.valid) {
107
- return null
108
- }
109
-
110
- const { schema } = entityValidator
111
-
112
- return {
113
- content: [
114
- {
115
- type: 'text',
116
- text: `Invalid data for ${entity} "${slug}": ${result.errorMessage}\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``,
117
- },
118
- ],
119
- isError: true,
120
- structuredContent: {
121
- [`${entity}Slug`]: slug,
122
- errors: [{ message: result.errorMessage }],
123
- schema,
124
- },
125
- }
126
- }
127
-
128
- /** Updates are partial — drop the schema's top-level `required` before validating. */
129
- const withoutRequired = (schema: JsonSchemaType): JsonSchemaType => {
130
- const { required: _required, ...rest } = schema
131
- return rest
132
- }
@@ -1,53 +0,0 @@
1
- import type { SanitizedConfig } from 'payload'
2
-
3
- import { fieldIsVirtual } from 'payload/shared'
4
-
5
- /**
6
- * Returns the names of all top-level virtual fields for a given collection slug.
7
- */
8
- export function getCollectionVirtualFieldNames(config: SanitizedConfig, slug: string): string[] {
9
- const collection = config.collections.find((c) => c.slug === slug)
10
-
11
- if (!collection) {
12
- return []
13
- }
14
-
15
- return collection.flattenedFields
16
- .filter((field) => 'name' in field && fieldIsVirtual(field))
17
- .map((field) => (field as { name: string }).name)
18
- }
19
-
20
- /**
21
- * Returns the names of all top-level virtual fields for a given global slug.
22
- */
23
- export function getGlobalVirtualFieldNames(config: SanitizedConfig, slug: string): string[] {
24
- const global = config.globals.find((g) => g.slug === slug)
25
-
26
- if (!global) {
27
- return []
28
- }
29
-
30
- return global.flattenedFields
31
- .filter((field) => 'name' in field && fieldIsVirtual(field))
32
- .map((field) => (field as { name: string }).name)
33
- }
34
-
35
- /**
36
- * Strips virtual field values from a data object given a list of virtual field names.
37
- */
38
- export function stripVirtualFields(
39
- data: Record<string, unknown>,
40
- virtualFieldNames: string[],
41
- ): Record<string, unknown> {
42
- if (virtualFieldNames.length === 0) {
43
- return data
44
- }
45
-
46
- const stripped = { ...data }
47
-
48
- for (const name of virtualFieldNames) {
49
- delete stripped[name]
50
- }
51
-
52
- return stripped
53
- }
@@ -1,123 +0,0 @@
1
- import type {
2
- FlattenedBlock,
3
- FlattenedField,
4
- Operation,
5
- SanitizedDocumentPermissions,
6
- SanitizedFieldsPermissions,
7
- } from 'payload'
8
-
9
- type FieldOperation = Exclude<Operation, 'delete'>
10
- type AllowedFieldOperations = Record<FieldOperation, boolean>
11
-
12
- /**
13
- * Filters fields using the access results in `permissions`. `shouldExcludeField` receives the
14
- * operations allowed by the entity, field, and its parents and decides whether to remove the
15
- * field.
16
- *
17
- * Arrays, groups, named tabs, and blocks are filtered recursively. Rows and unnamed tabs are
18
- * flattened by Payload, so their children are filtered at the containing level.
19
- *
20
- * @example Include fields writable through any collection operation the user can perform.
21
- * ```ts
22
- * filterFieldsByAccess({
23
- * fields,
24
- * permissions: collectionPermissions,
25
- * shouldExcludeField: ({ create, update }) => !create && !update,
26
- * })
27
- * ```
28
- */
29
- export const filterFieldsByAccess = ({
30
- blocks,
31
- fields,
32
- permissions,
33
- shouldExcludeField,
34
- }: {
35
- blocks?: FlattenedBlock[]
36
- fields: FlattenedField[]
37
- permissions: SanitizedDocumentPermissions
38
- shouldExcludeField: (allowedOperations: AllowedFieldOperations) => boolean
39
- }): FlattenedField[] => {
40
- const filterFields = (
41
- nestedFields: FlattenedField[],
42
- nestedPermissions: SanitizedFieldsPermissions | undefined,
43
- parentAllowedOperations: AllowedFieldOperations,
44
- ): FlattenedField[] => {
45
- const accessibleFields: FlattenedField[] = []
46
-
47
- for (const field of nestedFields) {
48
- const fieldPermissions = nestedPermissions === true ? true : nestedPermissions?.[field.name]
49
- const isOperationAllowed = (operation: FieldOperation): boolean =>
50
- parentAllowedOperations[operation] &&
51
- (fieldPermissions === true || fieldPermissions?.[operation] === true)
52
- const allowedOperations: AllowedFieldOperations = {
53
- create: isOperationAllowed('create'),
54
- read: isOperationAllowed('read'),
55
- update: isOperationAllowed('update'),
56
- }
57
-
58
- if (shouldExcludeField(allowedOperations)) {
59
- continue
60
- }
61
-
62
- if (field.type === 'blocks') {
63
- const accessibleBlocks: (FlattenedBlock | string)[] = []
64
-
65
- for (const blockOrReference of field.blocks) {
66
- const block =
67
- typeof blockOrReference === 'string'
68
- ? blocks?.find(({ slug }) => slug === blockOrReference)
69
- : blockOrReference
70
-
71
- if (!block) {
72
- continue
73
- }
74
-
75
- const blockPermissions =
76
- fieldPermissions === true
77
- ? true
78
- : fieldPermissions?.blocks === true
79
- ? true
80
- : fieldPermissions?.blocks?.[block.slug]
81
-
82
- accessibleBlocks.push({
83
- ...block,
84
- flattenedFields: filterFields(
85
- block.flattenedFields,
86
- blockPermissions === true ? true : blockPermissions?.fields,
87
- allowedOperations,
88
- ),
89
- })
90
- }
91
-
92
- if (accessibleBlocks.length === 0) {
93
- continue
94
- }
95
-
96
- accessibleFields.push({ ...field, blocks: accessibleBlocks })
97
- continue
98
- }
99
-
100
- if (field.type === 'array' || field.type === 'group' || field.type === 'tab') {
101
- accessibleFields.push({
102
- ...field,
103
- flattenedFields: filterFields(
104
- field.flattenedFields,
105
- fieldPermissions === true ? true : fieldPermissions?.fields,
106
- allowedOperations,
107
- ),
108
- })
109
- continue
110
- }
111
-
112
- accessibleFields.push(field)
113
- }
114
-
115
- return accessibleFields
116
- }
117
-
118
- return filterFields(fields, permissions.fields, {
119
- create: 'create' in permissions && permissions.create === true,
120
- read: permissions.read === true,
121
- update: permissions.update === true,
122
- })
123
- }
@@ -1,96 +0,0 @@
1
- import {
2
- type CollectionSlug,
3
- entityToStandaloneJSONSchema,
4
- type FlattenedField,
5
- type GlobalSlug,
6
- type PayloadRequest,
7
- type SanitizedCollectionConfig,
8
- type SanitizedCollectionPermission,
9
- type SanitizedGlobalConfig,
10
- type SanitizedGlobalPermission,
11
- } from 'payload'
12
-
13
- import type { JsonSchemaType } from '../../types.js'
14
-
15
- import { filterFieldsByAccess } from './filterFieldsByAccess.js'
16
- import { sanitizeEntitySchema } from './sanitizeEntitySchema.js'
17
-
18
- export const getCollectionInputSchema = ({
19
- collectionSlug,
20
- permissions,
21
- req,
22
- }: {
23
- collectionSlug: CollectionSlug
24
- permissions?: SanitizedCollectionPermission
25
- req: PayloadRequest
26
- }): JsonSchemaType | null => {
27
- const collection = req.payload.collections[collectionSlug]?.config
28
-
29
- if (!collection) {
30
- return null
31
- }
32
-
33
- if (!permissions) {
34
- return buildEntityInputSchema({ entity: collection, req })
35
- }
36
-
37
- const fieldsAllowedByAccess = filterFieldsByAccess({
38
- blocks: req.payload.config.blocks,
39
- fields: collection.flattenedFields,
40
- permissions,
41
- shouldExcludeField: ({ create, update }) => !create && !update,
42
- })
43
-
44
- return buildEntityInputSchema({ entity: collection, fields: fieldsAllowedByAccess, req })
45
- }
46
-
47
- export const getGlobalInputSchema = ({
48
- globalSlug,
49
- permissions,
50
- req,
51
- }: {
52
- globalSlug: GlobalSlug
53
- permissions?: SanitizedGlobalPermission
54
- req: PayloadRequest
55
- }): JsonSchemaType | null => {
56
- const global = req.payload.config.globals.find((globalConfig) => globalConfig.slug === globalSlug)
57
-
58
- if (!global) {
59
- return null
60
- }
61
-
62
- if (!permissions) {
63
- return buildEntityInputSchema({ entity: global, req })
64
- }
65
-
66
- const fieldsAllowedByAccess = filterFieldsByAccess({
67
- blocks: req.payload.config.blocks,
68
- fields: global.flattenedFields,
69
- permissions,
70
- shouldExcludeField: ({ create, update }) => !create && !update,
71
- })
72
-
73
- return buildEntityInputSchema({ entity: global, fields: fieldsAllowedByAccess, req })
74
- }
75
-
76
- const buildEntityInputSchema = ({
77
- entity,
78
- fields = entity.flattenedFields,
79
- req,
80
- }: {
81
- entity: SanitizedCollectionConfig | SanitizedGlobalConfig
82
- fields?: FlattenedField[]
83
- req: PayloadRequest
84
- }): JsonSchemaType => {
85
- // The core schema generator reads flattenedFields from the entity and has no fields argument.
86
- const entityForSchema = { ...entity, flattenedFields: fields }
87
- const schema = entityToStandaloneJSONSchema({
88
- config: req.payload.config,
89
- defaultIDType: req.payload.db.defaultIDType,
90
- entity: entityForSchema,
91
- i18n: req.i18n,
92
- variant: 'input',
93
- }) as unknown as JsonSchemaType
94
-
95
- return sanitizeEntitySchema(schema)
96
- }
@@ -1,96 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
-
3
- import type { JsonSchemaType } from '../../types.js'
4
-
5
- import { sanitizeEntitySchema } from './sanitizeEntitySchema.js'
6
-
7
- describe('sanitizeEntitySchema', () => {
8
- it('renames the Lexical node-union definition to a short name and keeps it a strict oneOf', () => {
9
- // Shaped like the schema the MCP server prepares for a collection with a Lexical richText field
10
- // (Payload's `input` variant): a `$defs` node union (a `oneOf` of node shapes). The input variant
11
- // already types the relationship node's `value` as a bare ID - there is no populated-doc `$ref` to
12
- // reduce here (the variant did that upstream), so sanitize leaves the value untouched.
13
- const standalone: JsonSchemaType = {
14
- type: 'object',
15
- $defs: {
16
- LexicalNodes_ABCDEF12: {
17
- oneOf: [
18
- {
19
- type: 'object',
20
- additionalProperties: false,
21
- properties: { type: { const: 'paragraph' } },
22
- required: ['type'],
23
- },
24
- {
25
- type: 'object',
26
- additionalProperties: false,
27
- properties: {
28
- type: { const: 'relationship' },
29
- value: { type: 'string', description: 'The related document ID.' },
30
- },
31
- required: ['type'],
32
- },
33
- ],
34
- },
35
- },
36
- properties: {
37
- id: { type: 'string' },
38
- content: {
39
- type: 'object',
40
- properties: {
41
- root: {
42
- type: 'object',
43
- properties: {
44
- children: { type: 'array', items: { $ref: '#/$defs/LexicalNodes_ABCDEF12' } },
45
- },
46
- },
47
- },
48
- },
49
- },
50
- }
51
-
52
- expect(sanitizeEntitySchema(standalone)).toStrictEqual({
53
- type: 'object',
54
- $defs: {
55
- // The node union is renamed to a short, readable `node`, and stays a strict discriminated
56
- // `oneOf` - not loosened to `anyOf`.
57
- node: {
58
- oneOf: [
59
- {
60
- type: 'object',
61
- additionalProperties: false,
62
- properties: { type: { const: 'paragraph' } },
63
- required: ['type'],
64
- },
65
- {
66
- type: 'object',
67
- additionalProperties: false,
68
- properties: {
69
- type: { const: 'relationship' },
70
- // The value is already ID-only from the input variant - left as-is.
71
- value: { type: 'string', description: 'The related document ID.' },
72
- },
73
- required: ['type'],
74
- },
75
- ],
76
- },
77
- },
78
- properties: {
79
- // Managed timestamps are excluded upstream by the `input` variant. `id` stays because it's
80
- // a valid optional input (a client may supply a custom ID).
81
- id: { type: 'string' },
82
- content: {
83
- type: 'object',
84
- properties: {
85
- root: {
86
- type: 'object',
87
- properties: {
88
- children: { type: 'array', items: { $ref: '#/$defs/node' } },
89
- },
90
- },
91
- },
92
- },
93
- },
94
- })
95
- })
96
- })