@payloadcms/plugin-mcp 3.70.0 → 3.71.0-canary.3

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 (56) hide show
  1. package/dist/collections/createApiKeysCollection.d.ts +1 -1
  2. package/dist/collections/createApiKeysCollection.d.ts.map +1 -1
  3. package/dist/collections/createApiKeysCollection.js +10 -75
  4. package/dist/collections/createApiKeysCollection.js.map +1 -1
  5. package/dist/endpoints/mcp.d.ts.map +1 -1
  6. package/dist/endpoints/mcp.js +1 -0
  7. package/dist/endpoints/mcp.js.map +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +4 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/mcp/getMcpHandler.d.ts.map +1 -1
  12. package/dist/mcp/getMcpHandler.js +24 -10
  13. package/dist/mcp/getMcpHandler.js.map +1 -1
  14. package/dist/mcp/tools/global/find.d.ts +5 -0
  15. package/dist/mcp/tools/global/find.d.ts.map +1 -0
  16. package/dist/mcp/tools/global/find.js +59 -0
  17. package/dist/mcp/tools/global/find.js.map +1 -0
  18. package/dist/mcp/tools/global/update.d.ts +6 -0
  19. package/dist/mcp/tools/global/update.d.ts.map +1 -0
  20. package/dist/mcp/tools/global/update.js +97 -0
  21. package/dist/mcp/tools/global/update.js.map +1 -0
  22. package/dist/mcp/tools/resource/find.d.ts.map +1 -1
  23. package/dist/mcp/tools/resource/find.js +9 -3
  24. package/dist/mcp/tools/resource/find.js.map +1 -1
  25. package/dist/mcp/tools/schemas.d.ts +58 -17
  26. package/dist/mcp/tools/schemas.d.ts.map +1 -1
  27. package/dist/mcp/tools/schemas.js +19 -0
  28. package/dist/mcp/tools/schemas.js.map +1 -1
  29. package/dist/types.d.ts +40 -1
  30. package/dist/types.d.ts.map +1 -1
  31. package/dist/types.js.map +1 -1
  32. package/dist/utils/adminEntitySettings.d.ts +17 -0
  33. package/dist/utils/adminEntitySettings.d.ts.map +1 -0
  34. package/dist/utils/adminEntitySettings.js +41 -0
  35. package/dist/utils/adminEntitySettings.js.map +1 -0
  36. package/dist/utils/createApiKeyFields.d.ts +15 -0
  37. package/dist/utils/createApiKeyFields.d.ts.map +1 -0
  38. package/dist/utils/createApiKeyFields.js +57 -0
  39. package/dist/utils/createApiKeyFields.js.map +1 -0
  40. package/dist/utils/getEnabledSlugs.d.ts +13 -0
  41. package/dist/utils/getEnabledSlugs.d.ts.map +1 -0
  42. package/dist/utils/getEnabledSlugs.js +32 -0
  43. package/dist/utils/getEnabledSlugs.js.map +1 -0
  44. package/package.json +3 -3
  45. package/src/collections/createApiKeysCollection.ts +11 -111
  46. package/src/endpoints/mcp.ts +1 -0
  47. package/src/index.ts +4 -0
  48. package/src/mcp/getMcpHandler.ts +62 -26
  49. package/src/mcp/tools/global/find.ts +104 -0
  50. package/src/mcp/tools/global/update.ts +168 -0
  51. package/src/mcp/tools/resource/find.ts +5 -2
  52. package/src/mcp/tools/schemas.ts +56 -0
  53. package/src/types.ts +59 -1
  54. package/src/utils/adminEntitySettings.ts +40 -0
  55. package/src/utils/createApiKeyFields.ts +72 -0
  56. package/src/utils/getEnabledSlugs.ts +42 -0
@@ -3,119 +3,11 @@ import type { CollectionConfig } from 'payload'
3
3
  import type { PluginMCPServerConfig } from '../types.js'
4
4
 
5
5
  import { toCamelCase } from '../utils/camelCase.js'
6
-
7
- const addEnabledCollectionTools = (collections: PluginMCPServerConfig['collections']) => {
8
- const enabledCollectionSlugs = Object.keys(collections || {}).filter((collection) => {
9
- const fullyEnabled =
10
- typeof collections?.[collection]?.enabled === 'boolean' && collections?.[collection]?.enabled
11
-
12
- if (fullyEnabled) {
13
- return true
14
- }
15
-
16
- const partiallyEnabled =
17
- typeof collections?.[collection]?.enabled !== 'boolean' &&
18
- ((typeof collections?.[collection]?.enabled?.find === 'boolean' &&
19
- collections?.[collection]?.enabled?.find === true) ||
20
- (typeof collections?.[collection]?.enabled?.create === 'boolean' &&
21
- collections?.[collection]?.enabled?.create === true) ||
22
- (typeof collections?.[collection]?.enabled?.update === 'boolean' &&
23
- collections?.[collection]?.enabled?.update === true) ||
24
- (typeof collections?.[collection]?.enabled?.delete === 'boolean' &&
25
- collections?.[collection]?.enabled?.delete === true))
26
-
27
- if (partiallyEnabled) {
28
- return true
29
- }
30
- })
31
- return enabledCollectionSlugs.map((enabledCollectionSlug) => ({
32
- type: 'collapsible' as const,
33
- admin: {
34
- description: `Manage client access to ${enabledCollectionSlug}`,
35
- position: 'sidebar' as const,
36
- },
37
- fields: [
38
- {
39
- name: `${toCamelCase(enabledCollectionSlug)}`,
40
- type: 'group' as const,
41
- fields: [
42
- ...(collections?.[enabledCollectionSlug]?.enabled === true ||
43
- (typeof collections?.[enabledCollectionSlug]?.enabled !== 'boolean' &&
44
- typeof collections?.[enabledCollectionSlug]?.enabled?.find === 'boolean' &&
45
- collections?.[enabledCollectionSlug]?.enabled?.find === true)
46
- ? [
47
- {
48
- name: `find`,
49
- type: 'checkbox' as const,
50
- admin: {
51
- description: `Allow clients to find ${enabledCollectionSlug}.`,
52
- },
53
- defaultValue: false,
54
- label: 'Find',
55
- },
56
- ]
57
- : []),
58
-
59
- ...(collections?.[enabledCollectionSlug]?.enabled === true ||
60
- (typeof collections?.[enabledCollectionSlug]?.enabled !== 'boolean' &&
61
- typeof collections?.[enabledCollectionSlug]?.enabled?.create === 'boolean' &&
62
- collections?.[enabledCollectionSlug]?.enabled?.create === true)
63
- ? [
64
- {
65
- name: `create`,
66
- type: 'checkbox' as const,
67
- admin: {
68
- description: `Allow clients to create ${enabledCollectionSlug}.`,
69
- },
70
- defaultValue: false,
71
- label: 'Create',
72
- },
73
- ]
74
- : []),
75
-
76
- ...(collections?.[enabledCollectionSlug]?.enabled === true ||
77
- (typeof collections?.[enabledCollectionSlug]?.enabled !== 'boolean' &&
78
- typeof collections?.[enabledCollectionSlug]?.enabled?.update === 'boolean' &&
79
- collections?.[enabledCollectionSlug]?.enabled?.update === true)
80
- ? [
81
- {
82
- name: `update`,
83
- type: 'checkbox' as const,
84
- admin: {
85
- description: `Allow clients to update ${enabledCollectionSlug}.`,
86
- },
87
- defaultValue: false,
88
- label: 'Update',
89
- },
90
- ]
91
- : []),
92
-
93
- ...(collections?.[enabledCollectionSlug]?.enabled === true ||
94
- (typeof collections?.[enabledCollectionSlug]?.enabled !== 'boolean' &&
95
- typeof collections?.[enabledCollectionSlug]?.enabled?.delete === 'boolean' &&
96
- collections?.[enabledCollectionSlug]?.enabled?.delete === true)
97
- ? [
98
- {
99
- name: `delete`,
100
- type: 'checkbox' as const,
101
- admin: {
102
- description: `Allow clients to delete ${enabledCollectionSlug}.`,
103
- },
104
- defaultValue: false,
105
- label: 'Delete',
106
- },
107
- ]
108
- : []),
109
- ],
110
- label: false as const,
111
- },
112
- ],
113
- label: `${enabledCollectionSlug.charAt(0).toUpperCase() + toCamelCase(enabledCollectionSlug).slice(1)}`,
114
- }))
115
- }
6
+ import { createApiKeyFields } from '../utils/createApiKeyFields.js'
116
7
 
117
8
  export const createAPIKeysCollection = (
118
9
  collections: PluginMCPServerConfig['collections'],
10
+ globals: PluginMCPServerConfig['globals'],
119
11
  customTools: Array<{ description: string; name: string }> = [],
120
12
  experimentalTools: NonNullable<PluginMCPServerConfig['experimental']>['tools'] = {},
121
13
  pluginOptions: PluginMCPServerConfig,
@@ -204,7 +96,15 @@ export const createAPIKeysCollection = (
204
96
  },
205
97
  },
206
98
 
207
- ...addEnabledCollectionTools(collections),
99
+ ...createApiKeyFields({
100
+ config: collections,
101
+ configType: 'collection',
102
+ }),
103
+
104
+ ...createApiKeyFields({
105
+ config: globals,
106
+ configType: 'global',
107
+ }),
208
108
 
209
109
  ...(customTools.length > 0
210
110
  ? [
@@ -44,6 +44,7 @@ export const initializeMCPHandler = (pluginOptions: PluginMCPServerConfig) => {
44
44
 
45
45
  const { docs } = await payload.find({
46
46
  collection: 'payload-mcp-api-keys',
47
+ depth: 1,
47
48
  limit: 1,
48
49
  pagination: false,
49
50
  where,
package/src/index.ts CHANGED
@@ -27,6 +27,8 @@ export const mcpPlugin =
27
27
 
28
28
  // Collections
29
29
  const collections = pluginOptions.collections || {}
30
+ // Globals
31
+ const globals = pluginOptions.globals || {}
30
32
  // Extract custom tools for the global config
31
33
  const customTools =
32
34
  pluginOptions.mcp?.tools?.map((tool) => ({
@@ -46,11 +48,13 @@ export const mcpPlugin =
46
48
  * For example:
47
49
  * - If a collection has all of its capabilities enabled, admins can allow or disallow the create, update, delete, and find capabilities on that collection.
48
50
  * - If a collection only has the find capability enabled, admins can only allow or disallow the find capability on that collection.
51
+ * - If a global has all of its capabilities enabled, admins can allow or disallow the find and update capabilities on that global.
49
52
  * - If a custom tool has gone haywire, admins can disallow that tool.
50
53
  *
51
54
  */
52
55
  const apiKeyCollection = createAPIKeysCollection(
53
56
  collections,
57
+ globals,
54
58
  customTools,
55
59
  experimentalTools,
56
60
  pluginOptions,
@@ -7,9 +7,12 @@ import { APIError, configToJSONSchema, type PayloadRequest, type TypedUser } fro
7
7
  import type { MCPAccessSettings, PluginMCPServerConfig } from '../types.js'
8
8
 
9
9
  import { toCamelCase } from '../utils/camelCase.js'
10
+ import { getEnabledSlugs } from '../utils/getEnabledSlugs.js'
10
11
  import { registerTool } from './registerTool.js'
11
12
 
12
13
  // Tools
14
+ import { findGlobalTool } from './tools/global/find.js'
15
+ import { updateGlobalTool } from './tools/global/update.js'
13
16
  import { createResourceTool } from './tools/resource/create.js'
14
17
  import { deleteResourceTool } from './tools/resource/delete.js'
15
18
  import { findResourceTool } from './tools/resource/find.js'
@@ -81,6 +84,7 @@ export const getMCPHandler = (
81
84
  const experimentalTools: NonNullable<PluginMCPServerConfig['experimental']>['tools'] =
82
85
  pluginOptions?.experimental?.tools || {}
83
86
  const collectionsPluginConfig = pluginOptions.collections || {}
87
+ const globalsPluginConfig = pluginOptions.globals || {}
84
88
  const collectionsDirPath =
85
89
  experimentalTools && experimentalTools.collections?.collectionsDirPath
86
90
  ? experimentalTools.collections.collectionsDirPath
@@ -97,32 +101,8 @@ export const getMCPHandler = (
97
101
  try {
98
102
  return createMcpHandler(
99
103
  (server) => {
100
- const enabledCollectionSlugs = Object.keys(collectionsPluginConfig || {}).filter(
101
- (collection) => {
102
- const fullyEnabled =
103
- typeof collectionsPluginConfig?.[collection]?.enabled === 'boolean' &&
104
- collectionsPluginConfig?.[collection]?.enabled
105
-
106
- if (fullyEnabled) {
107
- return true
108
- }
109
-
110
- const partiallyEnabled =
111
- typeof collectionsPluginConfig?.[collection]?.enabled !== 'boolean' &&
112
- ((typeof collectionsPluginConfig?.[collection]?.enabled?.find === 'boolean' &&
113
- collectionsPluginConfig?.[collection]?.enabled?.find === true) ||
114
- (typeof collectionsPluginConfig?.[collection]?.enabled?.create === 'boolean' &&
115
- collectionsPluginConfig?.[collection]?.enabled?.create === true) ||
116
- (typeof collectionsPluginConfig?.[collection]?.enabled?.update === 'boolean' &&
117
- collectionsPluginConfig?.[collection]?.enabled?.update === true) ||
118
- (typeof collectionsPluginConfig?.[collection]?.enabled?.delete === 'boolean' &&
119
- collectionsPluginConfig?.[collection]?.enabled?.delete === true))
120
-
121
- if (partiallyEnabled) {
122
- return true
123
- }
124
- },
125
- )
104
+ // Get enabled collections
105
+ const enabledCollectionSlugs = getEnabledSlugs(collectionsPluginConfig, 'collection')
126
106
 
127
107
  // Collection Operation Tools
128
108
  enabledCollectionSlugs.forEach((enabledCollectionSlug) => {
@@ -215,6 +195,62 @@ export const getMCPHandler = (
215
195
  }
216
196
  })
217
197
 
198
+ // Global Operation Tools
199
+ const enabledGlobalSlugs = getEnabledSlugs(globalsPluginConfig, 'global')
200
+
201
+ enabledGlobalSlugs.forEach((enabledGlobalSlug) => {
202
+ try {
203
+ const schema = configSchema.definitions?.[enabledGlobalSlug] as JSONSchema4
204
+
205
+ const toolCapabilities = mcpAccessSettings?.[
206
+ `${toCamelCase(enabledGlobalSlug)}`
207
+ ] as Record<string, unknown>
208
+ const allowFind: boolean | undefined = toolCapabilities?.['find'] as boolean
209
+ const allowUpdate: boolean | undefined = toolCapabilities?.['update'] as boolean
210
+
211
+ if (allowFind) {
212
+ registerTool(
213
+ allowFind,
214
+ `Find ${enabledGlobalSlug}`,
215
+ () =>
216
+ findGlobalTool(
217
+ server,
218
+ req,
219
+ user,
220
+ useVerboseLogs,
221
+ enabledGlobalSlug,
222
+ globalsPluginConfig,
223
+ ),
224
+ payload,
225
+ useVerboseLogs,
226
+ )
227
+ }
228
+ if (allowUpdate) {
229
+ registerTool(
230
+ allowUpdate,
231
+ `Update ${enabledGlobalSlug}`,
232
+ () =>
233
+ updateGlobalTool(
234
+ server,
235
+ req,
236
+ user,
237
+ useVerboseLogs,
238
+ enabledGlobalSlug,
239
+ globalsPluginConfig,
240
+ schema,
241
+ ),
242
+ payload,
243
+ useVerboseLogs,
244
+ )
245
+ }
246
+ } catch (error) {
247
+ throw new APIError(
248
+ `Error registering tools for global ${enabledGlobalSlug}: ${String(error)}`,
249
+ 500,
250
+ )
251
+ }
252
+ })
253
+
218
254
  // Custom tools
219
255
  customMCPTools.forEach((tool) => {
220
256
  const camelCasedToolName = toCamelCase(tool.name)
@@ -0,0 +1,104 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import type { PayloadRequest, TypedUser } from 'payload'
3
+
4
+ import type { PluginMCPServerConfig } from '../../../types.js'
5
+
6
+ import { toCamelCase } from '../../../utils/camelCase.js'
7
+ import { toolSchemas } from '../schemas.js'
8
+
9
+ export const findGlobalTool = (
10
+ server: McpServer,
11
+ req: PayloadRequest,
12
+ user: TypedUser,
13
+ verboseLogs: boolean,
14
+ globalSlug: string,
15
+ globals: PluginMCPServerConfig['globals'],
16
+ ) => {
17
+ const tool = async (
18
+ depth: number = 0,
19
+ locale?: string,
20
+ fallbackLocale?: string,
21
+ ): Promise<{
22
+ content: Array<{
23
+ text: string
24
+ type: 'text'
25
+ }>
26
+ }> => {
27
+ const payload = req.payload
28
+
29
+ if (verboseLogs) {
30
+ payload.logger.info(
31
+ `[payload-mcp] Reading global: ${globalSlug}, depth: ${depth}${locale ? `, locale: ${locale}` : ''}`,
32
+ )
33
+ }
34
+
35
+ try {
36
+ const findOptions: Parameters<typeof payload.findGlobal>[0] = {
37
+ slug: globalSlug,
38
+ depth,
39
+ user,
40
+ }
41
+
42
+ // Add locale parameters if provided
43
+ if (locale) {
44
+ findOptions.locale = locale
45
+ }
46
+ if (fallbackLocale) {
47
+ findOptions.fallbackLocale = fallbackLocale
48
+ }
49
+
50
+ const result = await payload.findGlobal(findOptions)
51
+
52
+ if (verboseLogs) {
53
+ payload.logger.info(`[payload-mcp] Found global: ${globalSlug}`)
54
+ }
55
+
56
+ const response = {
57
+ content: [
58
+ {
59
+ type: 'text' as const,
60
+ text: `Global "${globalSlug}":
61
+ \`\`\`json
62
+ ${JSON.stringify(result, null, 2)}
63
+ \`\`\``,
64
+ },
65
+ ],
66
+ }
67
+
68
+ return (globals?.[globalSlug]?.overrideResponse?.(response, result, req) || response) as {
69
+ content: Array<{
70
+ text: string
71
+ type: 'text'
72
+ }>
73
+ }
74
+ } catch (error) {
75
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
76
+ payload.logger.error(`[payload-mcp] Error reading global ${globalSlug}: ${errorMessage}`)
77
+ const response = {
78
+ content: [
79
+ {
80
+ type: 'text' as const,
81
+ text: `❌ **Error reading global "${globalSlug}":** ${errorMessage}`,
82
+ },
83
+ ],
84
+ }
85
+ return (globals?.[globalSlug]?.overrideResponse?.(response, {}, req) || response) as {
86
+ content: Array<{
87
+ text: string
88
+ type: 'text'
89
+ }>
90
+ }
91
+ }
92
+ }
93
+
94
+ if (globals?.[globalSlug]?.enabled) {
95
+ server.tool(
96
+ `find${globalSlug.charAt(0).toUpperCase() + toCamelCase(globalSlug).slice(1)}`,
97
+ `${toolSchemas.findGlobal.description.trim()}\n\n${globals?.[globalSlug]?.description || ''}`,
98
+ toolSchemas.findGlobal.parameters.shape,
99
+ async ({ depth, fallbackLocale, locale }) => {
100
+ return await tool(depth, locale, fallbackLocale)
101
+ },
102
+ )
103
+ }
104
+ }
@@ -0,0 +1,168 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import type { JSONSchema4 } from 'json-schema'
3
+ import type { PayloadRequest, TypedUser } from 'payload'
4
+
5
+ import { z } from 'zod'
6
+
7
+ import type { PluginMCPServerConfig } from '../../../types.js'
8
+
9
+ import { toCamelCase } from '../../../utils/camelCase.js'
10
+ import { convertCollectionSchemaToZod } from '../../../utils/convertCollectionSchemaToZod.js'
11
+ import { toolSchemas } from '../schemas.js'
12
+
13
+ export const updateGlobalTool = (
14
+ server: McpServer,
15
+ req: PayloadRequest,
16
+ user: TypedUser,
17
+ verboseLogs: boolean,
18
+ globalSlug: string,
19
+ globals: PluginMCPServerConfig['globals'],
20
+ schema: JSONSchema4,
21
+ ) => {
22
+ const tool = async (
23
+ data: string,
24
+ draft: boolean = false,
25
+ depth: number = 0,
26
+ locale?: string,
27
+ fallbackLocale?: string,
28
+ ): Promise<{
29
+ content: Array<{
30
+ text: string
31
+ type: 'text'
32
+ }>
33
+ }> => {
34
+ const payload = req.payload
35
+
36
+ if (verboseLogs) {
37
+ payload.logger.info(
38
+ `[payload-mcp] Updating global: ${globalSlug}, draft: ${draft}${locale ? `, locale: ${locale}` : ''}`,
39
+ )
40
+ }
41
+
42
+ try {
43
+ // Parse the data JSON
44
+ let parsedData: Record<string, unknown>
45
+ try {
46
+ parsedData = JSON.parse(data)
47
+ if (verboseLogs) {
48
+ payload.logger.info(
49
+ `[payload-mcp] Parsed data for ${globalSlug}: ${JSON.stringify(parsedData)}`,
50
+ )
51
+ }
52
+ } catch (_parseError) {
53
+ payload.logger.error(`[payload-mcp] Invalid JSON data provided: ${data}`)
54
+ const response = {
55
+ content: [{ type: 'text' as const, text: 'Error: Invalid JSON data provided' }],
56
+ }
57
+ return (globals?.[globalSlug]?.overrideResponse?.(response, {}, req) || response) as {
58
+ content: Array<{
59
+ text: string
60
+ type: 'text'
61
+ }>
62
+ }
63
+ }
64
+
65
+ const updateOptions: Parameters<typeof payload.updateGlobal>[0] = {
66
+ slug: globalSlug,
67
+ data: parsedData,
68
+ depth,
69
+ draft,
70
+ user,
71
+ }
72
+
73
+ // Add locale parameters if provided
74
+ if (locale) {
75
+ updateOptions.locale = locale
76
+ }
77
+ if (fallbackLocale) {
78
+ updateOptions.fallbackLocale = fallbackLocale
79
+ }
80
+
81
+ const result = await payload.updateGlobal(updateOptions)
82
+
83
+ if (verboseLogs) {
84
+ payload.logger.info(`[payload-mcp] Successfully updated global: ${globalSlug}`)
85
+ }
86
+
87
+ const response = {
88
+ content: [
89
+ {
90
+ type: 'text' as const,
91
+ text: `Global "${globalSlug}" updated successfully!
92
+ \`\`\`json
93
+ ${JSON.stringify(result, null, 2)}
94
+ \`\`\``,
95
+ },
96
+ ],
97
+ }
98
+
99
+ return (globals?.[globalSlug]?.overrideResponse?.(response, result, req) || response) as {
100
+ content: Array<{
101
+ text: string
102
+ type: 'text'
103
+ }>
104
+ }
105
+ } catch (error) {
106
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
107
+ payload.logger.error(`[payload-mcp] Error updating global ${globalSlug}: ${errorMessage}`)
108
+
109
+ const response = {
110
+ content: [
111
+ {
112
+ type: 'text' as const,
113
+ text: `Error updating global "${globalSlug}": ${errorMessage}`,
114
+ },
115
+ ],
116
+ }
117
+
118
+ return (globals?.[globalSlug]?.overrideResponse?.(response, {}, req) || response) as {
119
+ content: Array<{
120
+ text: string
121
+ type: 'text'
122
+ }>
123
+ }
124
+ }
125
+ }
126
+
127
+ if (globals?.[globalSlug]?.enabled) {
128
+ const convertedFields = convertCollectionSchemaToZod(schema)
129
+
130
+ // Make all fields optional for partial updates (PATCH-style)
131
+ const optionalFields = Object.fromEntries(
132
+ Object.entries(convertedFields.shape).map(([key, value]) => [key, (value as any).optional()]),
133
+ )
134
+
135
+ const updateGlobalSchema = z.object({
136
+ ...optionalFields,
137
+ depth: z.number().optional().describe('Optional: Depth of relationships to populate'),
138
+ draft: z.boolean().optional().describe('Optional: Whether to save as draft (default: false)'),
139
+ fallbackLocale: z
140
+ .string()
141
+ .optional()
142
+ .describe('Optional: fallback locale code to use when requested locale is not available'),
143
+ locale: z
144
+ .string()
145
+ .optional()
146
+ .describe(
147
+ 'Optional: locale code to update data in (e.g., "en", "es"). Use "all" to update all locales for localized fields',
148
+ ),
149
+ })
150
+
151
+ server.tool(
152
+ `update${globalSlug.charAt(0).toUpperCase() + toCamelCase(globalSlug).slice(1)}`,
153
+ `${toolSchemas.updateGlobal.description.trim()}\n\n${globals?.[globalSlug]?.description || ''}`,
154
+ updateGlobalSchema.shape,
155
+ async (params: Record<string, unknown>) => {
156
+ const { depth, draft, fallbackLocale, locale, ...rest } = params
157
+ const data = JSON.stringify(rest)
158
+ return await tool(
159
+ data,
160
+ draft as boolean,
161
+ depth as number,
162
+ locale as string,
163
+ fallbackLocale as string,
164
+ )
165
+ },
166
+ )
167
+ }
168
+ }
@@ -22,6 +22,7 @@ export const findResourceTool = (
22
22
  where?: string,
23
23
  locale?: string,
24
24
  fallbackLocale?: string,
25
+ draft?: boolean,
25
26
  ): Promise<{
26
27
  content: Array<{
27
28
  text: string
@@ -71,6 +72,7 @@ export const findResourceTool = (
71
72
  user,
72
73
  ...(locale && { locale }),
73
74
  ...(fallbackLocale && { fallbackLocale }),
75
+ ...(draft !== undefined && { draft }),
74
76
  })
75
77
 
76
78
  if (verboseLogs) {
@@ -126,6 +128,7 @@ ${JSON.stringify(doc, null, 2)}`,
126
128
  user,
127
129
  ...(locale && { locale }),
128
130
  ...(fallbackLocale && { fallbackLocale }),
131
+ ...(draft !== undefined && { draft }),
129
132
  }
130
133
 
131
134
  if (sort) {
@@ -196,8 +199,8 @@ Page: ${result.page} of ${result.totalPages}
196
199
  `find${collectionSlug.charAt(0).toUpperCase() + toCamelCase(collectionSlug).slice(1)}`,
197
200
  `${collections?.[collectionSlug]?.description || toolSchemas.findResources.description.trim()}`,
198
201
  toolSchemas.findResources.parameters.shape,
199
- async ({ id, fallbackLocale, limit, locale, page, sort, where }) => {
200
- return await tool(id, limit, page, sort, where, locale, fallbackLocale)
202
+ async ({ id, draft, fallbackLocale, limit, locale, page, sort, where }) => {
203
+ return await tool(id, limit, page, sort, where, locale, fallbackLocale, draft)
201
204
  },
202
205
  )
203
206
  }
@@ -1,6 +1,30 @@
1
1
  import { z } from 'zod'
2
2
 
3
3
  export const toolSchemas = {
4
+ findGlobal: {
5
+ description: 'Find a Payload global singleton configuration.',
6
+ parameters: z.object({
7
+ depth: z
8
+ .number()
9
+ .int()
10
+ .min(0)
11
+ .max(10)
12
+ .optional()
13
+ .default(0)
14
+ .describe('Depth of population for relationships'),
15
+ fallbackLocale: z
16
+ .string()
17
+ .optional()
18
+ .describe('Optional: fallback locale code to use when requested locale is not available'),
19
+ locale: z
20
+ .string()
21
+ .optional()
22
+ .describe(
23
+ 'Optional: locale code to retrieve data in (e.g., "en", "es"). Use "all" to retrieve all locales for localized fields',
24
+ ),
25
+ }),
26
+ },
27
+
4
28
  findResources: {
5
29
  description: 'Find documents in a collection by ID or where clause using Find or FindByID.',
6
30
  parameters: z.object({
@@ -10,6 +34,12 @@ export const toolSchemas = {
10
34
  .describe(
11
35
  'Optional: specific document ID to retrieve. If not provided, returns all documents',
12
36
  ),
37
+ draft: z
38
+ .boolean()
39
+ .optional()
40
+ .describe(
41
+ 'Optional: Whether the document should be queried from the versions table/collection or not.',
42
+ ),
13
43
  fallbackLocale: z
14
44
  .string()
15
45
  .optional()
@@ -147,6 +177,32 @@ export const toolSchemas = {
147
177
  }),
148
178
  },
149
179
 
180
+ updateGlobal: {
181
+ description: 'Update a Payload global singleton configuration.',
182
+ parameters: z.object({
183
+ data: z.string().describe('JSON string containing the data to update'),
184
+ depth: z
185
+ .number()
186
+ .int()
187
+ .min(0)
188
+ .max(10)
189
+ .optional()
190
+ .default(0)
191
+ .describe('Depth of population for relationships'),
192
+ draft: z.boolean().optional().default(false).describe('Whether to update as a draft'),
193
+ fallbackLocale: z
194
+ .string()
195
+ .optional()
196
+ .describe('Optional: fallback locale code to use when requested locale is not available'),
197
+ locale: z
198
+ .string()
199
+ .optional()
200
+ .describe(
201
+ 'Optional: locale code to update data in (e.g., "en", "es"). Use "all" to update all locales for localized fields',
202
+ ),
203
+ }),
204
+ },
205
+
150
206
  // Experimental Below This Line
151
207
  createCollection: {
152
208
  description: 'Creates a new collection with specified fields and configuration.',