@payloadcms/plugin-mcp 4.0.0-canary.0 → 4.0.0-canary.1
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.
- package/dist/collection/getAccessField.js +1 -1
- package/dist/collection/getAccessField.js.map +1 -1
- package/dist/collection/index.d.ts.map +1 -1
- package/dist/collection/index.js +2 -1
- package/dist/collection/index.js.map +1 -1
- package/dist/components/AccessField/index.client.d.ts.map +1 -1
- package/dist/components/AccessField/index.client.js +30 -30
- package/dist/components/AccessField/index.client.js.map +1 -1
- package/dist/endpoint/access.js +5 -5
- package/dist/endpoint/access.js.map +1 -1
- package/dist/mcp/buildMcpServer.d.ts.map +1 -1
- package/dist/mcp/buildMcpServer.js +100 -64
- package/dist/mcp/buildMcpServer.js.map +1 -1
- package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
- package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/createTool.js +28 -21
- package/dist/mcp/builtin/collections/createTool.js.map +1 -1
- package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
- package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/deleteTool.js +5 -20
- package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
- package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
- package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/findTool.js +6 -21
- package/dist/mcp/builtin/collections/findTool.js.map +1 -1
- package/dist/mcp/builtin/collections/formatCollectionError.d.ts +9 -0
- package/dist/mcp/builtin/collections/formatCollectionError.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/formatCollectionError.js +60 -0
- package/dist/mcp/builtin/collections/formatCollectionError.js.map +1 -0
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +35 -0
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
- package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
- package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/updateTool.js +74 -62
- package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
- package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
- package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
- package/dist/mcp/builtin/getConfigInfoTool.js +49 -0
- package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
- package/dist/mcp/builtin/globals/findTool.js +1 -1
- package/dist/mcp/builtin/globals/findTool.js.map +1 -1
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +35 -0
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
- package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
- package/dist/mcp/builtin/globals/updateTool.js +21 -19
- package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
- package/dist/mcp/builtin/validateEntityData.d.ts +14 -0
- package/dist/mcp/builtin/validateEntityData.d.ts.map +1 -0
- package/dist/mcp/builtin/validateEntityData.js +82 -0
- package/dist/mcp/builtin/validateEntityData.js.map +1 -0
- package/dist/mcp/builtinTools.d.ts +84 -16
- package/dist/mcp/builtinTools.d.ts.map +1 -1
- package/dist/mcp/builtinTools.js +54 -11
- package/dist/mcp/builtinTools.js.map +1 -1
- package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
- package/dist/mcp/sanitizeMCPConfig.js +61 -40
- package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
- package/dist/types.d.ts +16 -27
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/schemaConversion/getEntityInputSchema.d.ts +11 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +1 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.js +34 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.js.map +1 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +15 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +1 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.js +464 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +1 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +158 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +1 -0
- package/dist/utils/whereSchema.d.ts +9 -0
- package/dist/utils/whereSchema.d.ts.map +1 -0
- package/dist/utils/whereSchema.js +13 -0
- package/dist/utils/whereSchema.js.map +1 -0
- package/package.json +5 -5
- package/src/collection/getAccessField.ts +1 -1
- package/src/collection/index.ts +1 -0
- package/src/components/AccessField/index.client.tsx +34 -31
- package/src/endpoint/access.ts +5 -5
- package/src/mcp/buildMcpServer.ts +123 -90
- package/src/mcp/builtin/collections/createTool.ts +46 -50
- package/src/mcp/builtin/collections/deleteTool.ts +9 -16
- package/src/mcp/builtin/collections/findTool.ts +7 -17
- package/src/mcp/builtin/collections/formatCollectionError.ts +84 -0
- package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +28 -0
- package/src/mcp/builtin/collections/updateTool.ts +97 -91
- package/src/mcp/builtin/getConfigInfoTool.ts +44 -0
- package/src/mcp/builtin/globals/findTool.ts +1 -1
- package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +28 -0
- package/src/mcp/builtin/globals/updateTool.ts +40 -43
- package/src/mcp/builtin/validateEntityData.ts +132 -0
- package/src/mcp/builtinTools.ts +52 -38
- package/src/mcp/sanitizeMCPConfig.ts +78 -57
- package/src/types.ts +24 -29
- package/src/utils/schemaConversion/getEntityInputSchema.ts +78 -0
- package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +103 -0
- package/src/utils/schemaConversion/sanitizeEntitySchema.ts +529 -0
- package/src/utils/whereSchema.ts +24 -0
- package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts +0 -7
- package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts.map +0 -1
- package/dist/utils/schemaConversion/prepareCollectionSchema.js +0 -37
- package/dist/utils/schemaConversion/prepareCollectionSchema.js.map +0 -1
- package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts +0 -13
- package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts.map +0 -1
- package/dist/utils/schemaConversion/sanitizeJsonSchema.js +0 -56
- package/dist/utils/schemaConversion/sanitizeJsonSchema.js.map +0 -1
- package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts +0 -20
- package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts.map +0 -1
- package/dist/utils/schemaConversion/simplifyRelationshipFields.js +0 -56
- package/dist/utils/schemaConversion/simplifyRelationshipFields.js.map +0 -1
- package/dist/utils/schemaConversion/transformPointFields.d.ts +0 -3
- package/dist/utils/schemaConversion/transformPointFields.d.ts.map +0 -1
- package/dist/utils/schemaConversion/transformPointFields.js +0 -57
- package/dist/utils/schemaConversion/transformPointFields.js.map +0 -1
- package/src/utils/schemaConversion/prepareCollectionSchema.ts +0 -39
- package/src/utils/schemaConversion/sanitizeJsonSchema.ts +0 -62
- package/src/utils/schemaConversion/simplifyRelationshipFields.ts +0 -70
- package/src/utils/schemaConversion/transformPointFields.ts +0 -56
package/src/mcp/builtinTools.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CollectionTool, GlobalTool } from '../types.js'
|
|
1
|
+
import type { CollectionTool, GlobalTool, Tool } from '../types.js'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
authCollectionTool,
|
|
@@ -8,62 +8,72 @@ import {
|
|
|
8
8
|
unlockCollectionTool,
|
|
9
9
|
verifyCollectionTool,
|
|
10
10
|
} from './builtin/collections/authTools.js'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
11
|
+
import { createDocumentTool } from './builtin/collections/createTool.js'
|
|
12
|
+
import { deleteDocumentsTool } from './builtin/collections/deleteTool.js'
|
|
13
|
+
import { findDocumentsTool } from './builtin/collections/findTool.js'
|
|
14
|
+
import { getCollectionSchemaTool } from './builtin/collections/getCollectionSchemaTool.js'
|
|
15
|
+
import { updateDocumentTool } from './builtin/collections/updateTool.js'
|
|
16
|
+
import { getConfigInfoTool } from './builtin/getConfigInfoTool.js'
|
|
15
17
|
import { findGlobalTool } from './builtin/globals/findTool.js'
|
|
18
|
+
import { getGlobalSchemaTool } from './builtin/globals/getGlobalSchemaTool.js'
|
|
16
19
|
import { updateGlobalTool } from './builtin/globals/updateTool.js'
|
|
17
20
|
|
|
21
|
+
export const TOOL_BUILTINS = {
|
|
22
|
+
getConfigInfo: { label: 'Config Info', mcpName: 'getConfigInfo', tool: getConfigInfoTool },
|
|
23
|
+
} satisfies Record<string, { label: string; mcpName: string; tool: Tool }>
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* The static built-in collection CRUD tools. Keys here are the source of truth
|
|
20
27
|
* for `MCPCollectionBuiltinName` — adding/removing an entry updates the type
|
|
21
28
|
* automatically.
|
|
22
29
|
*/
|
|
23
|
-
export const COLLECTION_BUILTINS
|
|
24
|
-
create:
|
|
25
|
-
delete:
|
|
26
|
-
find:
|
|
27
|
-
|
|
28
|
-
}
|
|
30
|
+
export const COLLECTION_BUILTINS = {
|
|
31
|
+
create: { mcpName: 'createDocument', tool: createDocumentTool },
|
|
32
|
+
delete: { mcpName: 'deleteDocuments', tool: deleteDocumentsTool },
|
|
33
|
+
find: { mcpName: 'findDocuments', tool: findDocumentsTool },
|
|
34
|
+
getCollectionSchema: { mcpName: 'getCollectionSchema', tool: getCollectionSchemaTool },
|
|
35
|
+
update: { mcpName: 'updateDocument', tool: updateDocumentTool },
|
|
36
|
+
} satisfies Record<string, { mcpName: string; tool: CollectionTool }>
|
|
29
37
|
|
|
30
38
|
/**
|
|
31
39
|
* The static auth tools surfaced under auth-enabled collections. Each entry
|
|
32
40
|
* carries the admin-UI label alongside the tool. Keys are the source of truth
|
|
33
41
|
* for `MCPCollectionAuthToolName`.
|
|
34
42
|
*/
|
|
35
|
-
export const COLLECTION_AUTH_BUILTINS
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
export const COLLECTION_AUTH_BUILTINS = {
|
|
44
|
+
auth: { label: 'Check Auth Status', mcpName: 'auth', tool: authCollectionTool },
|
|
45
|
+
forgotPassword: {
|
|
46
|
+
label: 'Forgot Password',
|
|
47
|
+
mcpName: 'forgotPassword',
|
|
48
|
+
tool: forgotPasswordCollectionTool,
|
|
49
|
+
},
|
|
50
|
+
login: { label: 'User Login', mcpName: 'login', tool: loginCollectionTool },
|
|
51
|
+
resetPassword: {
|
|
52
|
+
label: 'Reset Password',
|
|
53
|
+
mcpName: 'resetPassword',
|
|
54
|
+
tool: resetPasswordCollectionTool,
|
|
55
|
+
},
|
|
56
|
+
unlock: { label: 'Unlock Account', mcpName: 'unlock', tool: unlockCollectionTool },
|
|
57
|
+
verify: { label: 'Email Verification', mcpName: 'verify', tool: verifyCollectionTool },
|
|
58
|
+
} satisfies Record<string, { label: string; mcpName: string; tool: CollectionTool }>
|
|
46
59
|
|
|
47
60
|
/**
|
|
48
61
|
* The static built-in global tools. Keys are the source of truth for
|
|
49
62
|
* `MCPGlobalBuiltinName`.
|
|
50
63
|
*/
|
|
51
|
-
export const GLOBAL_BUILTINS
|
|
52
|
-
find: findGlobalTool,
|
|
53
|
-
|
|
54
|
-
}
|
|
64
|
+
export const GLOBAL_BUILTINS = {
|
|
65
|
+
find: { mcpName: 'findGlobal', tool: findGlobalTool },
|
|
66
|
+
getGlobalSchema: { mcpName: 'getGlobalSchema', tool: getGlobalSchemaTool },
|
|
67
|
+
update: { mcpName: 'updateGlobal', tool: updateGlobalTool },
|
|
68
|
+
} satisfies Record<string, { mcpName: string; tool: GlobalTool }>
|
|
69
|
+
|
|
70
|
+
export type MCPCollectionBuiltinName = keyof typeof COLLECTION_BUILTINS
|
|
55
71
|
|
|
56
|
-
export type
|
|
72
|
+
export type MCPCollectionAuthToolName = keyof typeof COLLECTION_AUTH_BUILTINS
|
|
57
73
|
|
|
58
|
-
export type
|
|
59
|
-
| 'auth'
|
|
60
|
-
| 'forgotPassword'
|
|
61
|
-
| 'login'
|
|
62
|
-
| 'resetPassword'
|
|
63
|
-
| 'unlock'
|
|
64
|
-
| 'verify'
|
|
74
|
+
export type MCPGlobalBuiltinName = keyof typeof GLOBAL_BUILTINS
|
|
65
75
|
|
|
66
|
-
export type
|
|
76
|
+
export type MCPTopLevelBuiltinName = keyof typeof TOOL_BUILTINS
|
|
67
77
|
|
|
68
78
|
/**
|
|
69
79
|
* Pre-typed `Object.entries` for each registry. The cast from `string` to the
|
|
@@ -71,14 +81,18 @@ export type MCPGlobalBuiltinName = 'find' | 'update'
|
|
|
71
81
|
* cast — TypeScript's `Object.entries` always widens keys to `string`, which
|
|
72
82
|
* defeats the narrow type lookups on `MCPCollectionToolsMap`.
|
|
73
83
|
*/
|
|
84
|
+
export const TOOL_BUILTIN_ENTRIES = Object.entries(TOOL_BUILTINS) as Array<
|
|
85
|
+
[MCPTopLevelBuiltinName, (typeof TOOL_BUILTINS)[MCPTopLevelBuiltinName]]
|
|
86
|
+
>
|
|
87
|
+
|
|
74
88
|
export const COLLECTION_BUILTIN_ENTRIES = Object.entries(COLLECTION_BUILTINS) as Array<
|
|
75
|
-
[MCPCollectionBuiltinName,
|
|
89
|
+
[MCPCollectionBuiltinName, (typeof COLLECTION_BUILTINS)[MCPCollectionBuiltinName]]
|
|
76
90
|
>
|
|
77
91
|
|
|
78
92
|
export const COLLECTION_AUTH_BUILTIN_ENTRIES = Object.entries(COLLECTION_AUTH_BUILTINS) as Array<
|
|
79
|
-
[MCPCollectionAuthToolName,
|
|
93
|
+
[MCPCollectionAuthToolName, (typeof COLLECTION_AUTH_BUILTINS)[MCPCollectionAuthToolName]]
|
|
80
94
|
>
|
|
81
95
|
|
|
82
96
|
export const GLOBAL_BUILTIN_ENTRIES = Object.entries(GLOBAL_BUILTINS) as Array<
|
|
83
|
-
[MCPGlobalBuiltinName,
|
|
97
|
+
[MCPGlobalBuiltinName, (typeof GLOBAL_BUILTINS)[MCPGlobalBuiltinName]]
|
|
84
98
|
>
|
|
@@ -8,13 +8,12 @@ import type {
|
|
|
8
8
|
} from 'payload'
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
|
+
CollectionMCPItem,
|
|
11
12
|
CollectionTool,
|
|
13
|
+
GlobalMCPItem,
|
|
12
14
|
GlobalTool,
|
|
13
|
-
MCPBuiltInToolOverride,
|
|
14
15
|
MCPItem,
|
|
15
|
-
MCPPluginCollectionConfig,
|
|
16
16
|
MCPPluginConfig,
|
|
17
|
-
MCPPluginGlobalConfig,
|
|
18
17
|
SanitizedMCPPluginConfig,
|
|
19
18
|
} from '../types.js'
|
|
20
19
|
|
|
@@ -25,6 +24,8 @@ import {
|
|
|
25
24
|
COLLECTION_BUILTINS,
|
|
26
25
|
GLOBAL_BUILTIN_ENTRIES,
|
|
27
26
|
GLOBAL_BUILTINS,
|
|
27
|
+
TOOL_BUILTIN_ENTRIES,
|
|
28
|
+
TOOL_BUILTINS,
|
|
28
29
|
} from './builtinTools.js'
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -54,29 +55,45 @@ export const sanitizeMCPConfig = ({
|
|
|
54
55
|
items.push(...sanitizeGlobalConfig({ global, pluginConfig }))
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
for (const [
|
|
58
|
+
for (const [configKey, { label, mcpName, tool }] of TOOL_BUILTIN_ENTRIES) {
|
|
58
59
|
items.push({
|
|
59
60
|
type: 'tool',
|
|
60
|
-
|
|
61
|
-
label
|
|
61
|
+
configKey,
|
|
62
|
+
label,
|
|
63
|
+
mcpName,
|
|
62
64
|
tool,
|
|
63
65
|
})
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
for (const [
|
|
68
|
+
for (const [configKey, tool] of Object.entries(pluginConfig.tools ?? {})) {
|
|
69
|
+
if (configKey in TOOL_BUILTINS) {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
items.push({
|
|
73
|
+
type: 'tool',
|
|
74
|
+
configKey,
|
|
75
|
+
label: configKey,
|
|
76
|
+
mcpName: configKey,
|
|
77
|
+
tool,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const [configKey, prompt] of Object.entries(pluginConfig.prompts ?? {})) {
|
|
67
82
|
items.push({
|
|
68
83
|
type: 'prompt',
|
|
69
|
-
|
|
70
|
-
label: prompt.title ??
|
|
84
|
+
configKey,
|
|
85
|
+
label: prompt.title ?? configKey,
|
|
86
|
+
mcpName: configKey,
|
|
71
87
|
prompt,
|
|
72
88
|
})
|
|
73
89
|
}
|
|
74
90
|
|
|
75
|
-
for (const [
|
|
91
|
+
for (const [configKey, resource] of Object.entries(pluginConfig.resources ?? {})) {
|
|
76
92
|
items.push({
|
|
77
93
|
type: 'resource',
|
|
78
|
-
|
|
79
|
-
label: resource.title ??
|
|
94
|
+
configKey,
|
|
95
|
+
label: resource.title ?? configKey,
|
|
96
|
+
mcpName: configKey,
|
|
80
97
|
resource,
|
|
81
98
|
})
|
|
82
99
|
}
|
|
@@ -101,30 +118,39 @@ const sanitizeCollectionConfig = ({
|
|
|
101
118
|
}: {
|
|
102
119
|
collection: CollectionConfig | SanitizedCollectionConfig
|
|
103
120
|
pluginConfig: MCPPluginConfig
|
|
104
|
-
}):
|
|
121
|
+
}): CollectionMCPItem[] => {
|
|
105
122
|
if (collection.slug === 'payload-mcp-api-keys') {
|
|
106
123
|
return []
|
|
107
124
|
}
|
|
108
125
|
const slug = collection.slug
|
|
109
126
|
const collectionPluginConfig = pluginConfig.collections?.[slug]
|
|
110
|
-
const items:
|
|
127
|
+
const items: CollectionMCPItem[] = []
|
|
111
128
|
|
|
112
|
-
for (const [toolKey, tool] of COLLECTION_BUILTIN_ENTRIES) {
|
|
129
|
+
for (const [toolKey, { mcpName, tool }] of COLLECTION_BUILTIN_ENTRIES) {
|
|
113
130
|
const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey]
|
|
114
131
|
if (matchedConfigEntry === false) {
|
|
115
132
|
continue
|
|
116
133
|
}
|
|
134
|
+
const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined
|
|
117
135
|
items.push({
|
|
118
136
|
type: 'collectionTool',
|
|
119
137
|
collectionSlug: slug,
|
|
120
|
-
|
|
138
|
+
configKey: toolKey,
|
|
121
139
|
label: capitalize(toolKey),
|
|
122
|
-
|
|
140
|
+
mcpName,
|
|
141
|
+
tool: {
|
|
142
|
+
...tool,
|
|
143
|
+
description: override?.description ?? tool.description,
|
|
144
|
+
overrideResponse:
|
|
145
|
+
override?.overrideResponse ??
|
|
146
|
+
collectionPluginConfig?.overrideResponse ??
|
|
147
|
+
tool.overrideResponse,
|
|
148
|
+
},
|
|
123
149
|
})
|
|
124
150
|
}
|
|
125
151
|
|
|
126
152
|
if (collection.auth) {
|
|
127
|
-
for (const [authToolKey, { label, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES) {
|
|
153
|
+
for (const [authToolKey, { label, mcpName, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES) {
|
|
128
154
|
const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey]
|
|
129
155
|
if (!matchedConfigEntry) {
|
|
130
156
|
continue
|
|
@@ -134,9 +160,17 @@ const sanitizeCollectionConfig = ({
|
|
|
134
160
|
items.push({
|
|
135
161
|
type: 'collectionTool',
|
|
136
162
|
collectionSlug: slug,
|
|
137
|
-
|
|
163
|
+
configKey: authToolKey,
|
|
138
164
|
label,
|
|
139
|
-
|
|
165
|
+
mcpName,
|
|
166
|
+
tool: {
|
|
167
|
+
...tool,
|
|
168
|
+
description: override?.description ?? tool.description,
|
|
169
|
+
overrideResponse:
|
|
170
|
+
override?.overrideResponse ??
|
|
171
|
+
collectionPluginConfig?.overrideResponse ??
|
|
172
|
+
tool.overrideResponse,
|
|
173
|
+
},
|
|
140
174
|
})
|
|
141
175
|
}
|
|
142
176
|
}
|
|
@@ -146,8 +180,8 @@ const sanitizeCollectionConfig = ({
|
|
|
146
180
|
const customEntries = Object.entries(collectionPluginConfig?.tools ?? {}) as Array<
|
|
147
181
|
[string, CollectionTool | undefined]
|
|
148
182
|
>
|
|
149
|
-
for (const [
|
|
150
|
-
if (
|
|
183
|
+
for (const [configKey, customTool] of customEntries) {
|
|
184
|
+
if (configKey in COLLECTION_BUILTINS || configKey in COLLECTION_AUTH_BUILTINS) {
|
|
151
185
|
continue
|
|
152
186
|
}
|
|
153
187
|
if (!customTool) {
|
|
@@ -156,8 +190,9 @@ const sanitizeCollectionConfig = ({
|
|
|
156
190
|
items.push({
|
|
157
191
|
type: 'collectionTool',
|
|
158
192
|
collectionSlug: slug,
|
|
159
|
-
|
|
160
|
-
label:
|
|
193
|
+
configKey,
|
|
194
|
+
label: configKey,
|
|
195
|
+
mcpName: configKey,
|
|
161
196
|
tool: customTool,
|
|
162
197
|
})
|
|
163
198
|
}
|
|
@@ -171,30 +206,39 @@ const sanitizeGlobalConfig = ({
|
|
|
171
206
|
}: {
|
|
172
207
|
global: GlobalConfig | SanitizedGlobalConfig
|
|
173
208
|
pluginConfig: MCPPluginConfig
|
|
174
|
-
}):
|
|
209
|
+
}): GlobalMCPItem[] => {
|
|
175
210
|
const slug = global.slug
|
|
176
211
|
const globalPluginConfig = pluginConfig.globals?.[slug]
|
|
177
|
-
const items:
|
|
212
|
+
const items: GlobalMCPItem[] = []
|
|
178
213
|
|
|
179
|
-
for (const [toolKey,
|
|
214
|
+
for (const [toolKey, { mcpName, tool }] of GLOBAL_BUILTIN_ENTRIES) {
|
|
180
215
|
const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey]
|
|
181
216
|
if (matchedConfigEntry === false) {
|
|
182
217
|
continue
|
|
183
218
|
}
|
|
219
|
+
const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined
|
|
184
220
|
items.push({
|
|
185
221
|
type: 'globalTool',
|
|
222
|
+
configKey: toolKey,
|
|
186
223
|
globalSlug: slug,
|
|
187
|
-
key: toolKey,
|
|
188
224
|
label: capitalize(toolKey),
|
|
189
|
-
|
|
225
|
+
mcpName,
|
|
226
|
+
tool: {
|
|
227
|
+
...tool,
|
|
228
|
+
description: override?.description ?? tool.description,
|
|
229
|
+
overrideResponse:
|
|
230
|
+
override?.overrideResponse ??
|
|
231
|
+
globalPluginConfig?.overrideResponse ??
|
|
232
|
+
tool.overrideResponse,
|
|
233
|
+
},
|
|
190
234
|
})
|
|
191
235
|
}
|
|
192
236
|
|
|
193
237
|
const customEntries = Object.entries(globalPluginConfig?.tools ?? {}) as Array<
|
|
194
238
|
[string, GlobalTool | undefined]
|
|
195
239
|
>
|
|
196
|
-
for (const [
|
|
197
|
-
if (
|
|
240
|
+
for (const [configKey, customTool] of customEntries) {
|
|
241
|
+
if (configKey in GLOBAL_BUILTINS) {
|
|
198
242
|
continue
|
|
199
243
|
}
|
|
200
244
|
if (!customTool) {
|
|
@@ -202,9 +246,10 @@ const sanitizeGlobalConfig = ({
|
|
|
202
246
|
}
|
|
203
247
|
items.push({
|
|
204
248
|
type: 'globalTool',
|
|
249
|
+
configKey,
|
|
205
250
|
globalSlug: slug,
|
|
206
|
-
|
|
207
|
-
|
|
251
|
+
label: configKey,
|
|
252
|
+
mcpName: configKey,
|
|
208
253
|
tool: customTool,
|
|
209
254
|
})
|
|
210
255
|
}
|
|
@@ -213,27 +258,3 @@ const sanitizeGlobalConfig = ({
|
|
|
213
258
|
}
|
|
214
259
|
|
|
215
260
|
const capitalize = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1)
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Spread the static built-in tool and apply consumer-side overrides.
|
|
219
|
-
* Precedence: per-tool override > scope-level (collection/global) override >
|
|
220
|
-
* the static tool's defaults. `toolEntry` is whatever the user put under
|
|
221
|
-
* `tools: { find: ... }` — could be `true`, `false`, or an override object —
|
|
222
|
-
* so it's narrowed internally.
|
|
223
|
-
*/
|
|
224
|
-
const overrideBuiltinTool = <TTool extends CollectionTool | GlobalTool>(
|
|
225
|
-
tool: TTool,
|
|
226
|
-
toolOverride?: MCPBuiltInToolOverride,
|
|
227
|
-
entityPluginConfig?: TTool extends CollectionTool
|
|
228
|
-
? MCPPluginCollectionConfig<any>
|
|
229
|
-
: MCPPluginGlobalConfig,
|
|
230
|
-
): TTool => {
|
|
231
|
-
return {
|
|
232
|
-
...tool,
|
|
233
|
-
description: toolOverride?.description ?? entityPluginConfig?.description ?? tool.description,
|
|
234
|
-
overrideResponse:
|
|
235
|
-
toolOverride?.overrideResponse ??
|
|
236
|
-
entityPluginConfig?.overrideResponse ??
|
|
237
|
-
tool.overrideResponse,
|
|
238
|
-
}
|
|
239
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -46,9 +46,9 @@ export type ToolInputSchema = JsonSchemaType | StandardSchemaWithJSON
|
|
|
46
46
|
export type ClientMCPPluginConfig = {
|
|
47
47
|
items: Array<{
|
|
48
48
|
collectionSlug?: string
|
|
49
|
+
configKey: string
|
|
49
50
|
description: string
|
|
50
51
|
globalSlug?: string
|
|
51
|
-
key: string
|
|
52
52
|
label: string
|
|
53
53
|
type: 'collectionTool' | 'globalTool' | 'prompt' | 'resource' | 'tool'
|
|
54
54
|
}>
|
|
@@ -105,22 +105,17 @@ export type Tool<TSchema extends ToolInputSchema | undefined = ToolInputSchema |
|
|
|
105
105
|
overrideResponse?: MCPResponseOverride
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
/**
|
|
109
|
-
* `TSchema` is the schema itself (Standard Schema, raw JSON Schema, or undefined).
|
|
110
|
-
* The function-form variant of `input` carries a concrete `{ collectionSchema: JsonSchemaType }`
|
|
111
|
-
* parameter type so callers can write `({ collectionSchema }) => …` without annotating it.
|
|
112
|
-
*/
|
|
113
108
|
export type CollectionTool<
|
|
114
109
|
TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined,
|
|
115
110
|
> = {
|
|
116
111
|
handler: (args: CollectionToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
117
|
-
input?:
|
|
112
|
+
input?: TSchema
|
|
118
113
|
} & Pick<Tool, 'description' | 'overrideResponse'>
|
|
119
114
|
|
|
120
115
|
export type GlobalTool<TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined> =
|
|
121
116
|
{
|
|
122
117
|
handler: (args: GlobalToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
123
|
-
input?:
|
|
118
|
+
input?: TSchema
|
|
124
119
|
} & Pick<Tool, 'description' | 'overrideResponse'>
|
|
125
120
|
|
|
126
121
|
/**
|
|
@@ -293,34 +288,32 @@ export type MCPAPIKeysDoc = {
|
|
|
293
288
|
}
|
|
294
289
|
|
|
295
290
|
/**
|
|
296
|
-
* One MCP primitive
|
|
297
|
-
*
|
|
298
|
-
* filtered by `getAuthorizedMCP`, registered by the MCP endpoint.
|
|
291
|
+
* One MCP primitive plus the metadata needed for access checks, admin UI, and
|
|
292
|
+
* registration.
|
|
299
293
|
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* registration time.
|
|
304
|
-
* - `label`: human-readable admin-UI display text for the checkbox.
|
|
305
|
-
* - `tool` / `prompt` / `resource`: the live primitive (its own
|
|
306
|
-
* `description` is what both MCP clients and the admin UI surface).
|
|
294
|
+
* - `configKey`: the config/API-key identifier, e.g. `find` or `echo`.
|
|
295
|
+
* - `mcpName`: the MCP wire name, e.g. `findDocuments` or `echo`.
|
|
296
|
+
* - `label`: human-readable admin checkbox text.
|
|
307
297
|
*/
|
|
308
298
|
export type MCPItemBase = {
|
|
309
|
-
|
|
299
|
+
configKey: string
|
|
310
300
|
label: string
|
|
301
|
+
mcpName: string
|
|
311
302
|
}
|
|
312
303
|
|
|
304
|
+
export type CollectionMCPItem = {
|
|
305
|
+
collectionSlug: CollectionSlug
|
|
306
|
+
tool: CollectionTool
|
|
307
|
+
type: 'collectionTool'
|
|
308
|
+
} & MCPItemBase
|
|
309
|
+
|
|
310
|
+
export type GlobalMCPItem = {
|
|
311
|
+
globalSlug: GlobalSlug
|
|
312
|
+
tool: GlobalTool
|
|
313
|
+
type: 'globalTool'
|
|
314
|
+
} & MCPItemBase
|
|
315
|
+
|
|
313
316
|
export type MCPItem =
|
|
314
|
-
| ({
|
|
315
|
-
collectionSlug: CollectionSlug
|
|
316
|
-
tool: CollectionTool
|
|
317
|
-
type: 'collectionTool'
|
|
318
|
-
} & MCPItemBase)
|
|
319
|
-
| ({
|
|
320
|
-
globalSlug: GlobalSlug
|
|
321
|
-
tool: GlobalTool
|
|
322
|
-
type: 'globalTool'
|
|
323
|
-
} & MCPItemBase)
|
|
324
317
|
| ({
|
|
325
318
|
prompt: Prompt
|
|
326
319
|
type: 'prompt'
|
|
@@ -333,6 +326,8 @@ export type MCPItem =
|
|
|
333
326
|
tool: Tool
|
|
334
327
|
type: 'tool'
|
|
335
328
|
} & MCPItemBase)
|
|
329
|
+
| CollectionMCPItem
|
|
330
|
+
| GlobalMCPItem
|
|
336
331
|
|
|
337
332
|
/**
|
|
338
333
|
* The caller's identity + the MCP items they can use for this request. Returned
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CollectionSlug,
|
|
3
|
+
entityToStandaloneJSONSchema,
|
|
4
|
+
type GlobalSlug,
|
|
5
|
+
type PayloadRequest,
|
|
6
|
+
type SanitizedCollectionConfig,
|
|
7
|
+
type SanitizedGlobalConfig,
|
|
8
|
+
} from 'payload'
|
|
9
|
+
|
|
10
|
+
import type { JsonSchemaType } from '../../types.js'
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
getCollectionVirtualFieldNames,
|
|
14
|
+
getGlobalVirtualFieldNames,
|
|
15
|
+
} from '../getVirtualFieldNames.js'
|
|
16
|
+
import { removeVirtualFieldsFromSchema } from './removeVirtualFieldsFromSchema.js'
|
|
17
|
+
import { sanitizeEntitySchema } from './sanitizeEntitySchema.js'
|
|
18
|
+
|
|
19
|
+
export const getCollectionInputSchema = ({
|
|
20
|
+
collectionSlug,
|
|
21
|
+
req,
|
|
22
|
+
}: {
|
|
23
|
+
collectionSlug: CollectionSlug
|
|
24
|
+
req: PayloadRequest
|
|
25
|
+
}): JsonSchemaType | null => {
|
|
26
|
+
const collection = req.payload.collections[collectionSlug]?.config
|
|
27
|
+
|
|
28
|
+
if (!collection) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return buildEntityInputSchema({
|
|
33
|
+
entity: collection,
|
|
34
|
+
req,
|
|
35
|
+
virtualFieldNames: getCollectionVirtualFieldNames(req.payload.config, collectionSlug),
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const getGlobalInputSchema = ({
|
|
40
|
+
globalSlug,
|
|
41
|
+
req,
|
|
42
|
+
}: {
|
|
43
|
+
globalSlug: GlobalSlug
|
|
44
|
+
req: PayloadRequest
|
|
45
|
+
}): JsonSchemaType | null => {
|
|
46
|
+
const global = req.payload.config.globals.find((globalConfig) => globalConfig.slug === globalSlug)
|
|
47
|
+
|
|
48
|
+
if (!global) {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return buildEntityInputSchema({
|
|
53
|
+
entity: global,
|
|
54
|
+
req,
|
|
55
|
+
virtualFieldNames: getGlobalVirtualFieldNames(req.payload.config, globalSlug),
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const buildEntityInputSchema = ({
|
|
60
|
+
entity,
|
|
61
|
+
req,
|
|
62
|
+
virtualFieldNames,
|
|
63
|
+
}: {
|
|
64
|
+
entity: SanitizedCollectionConfig | SanitizedGlobalConfig
|
|
65
|
+
req: PayloadRequest
|
|
66
|
+
virtualFieldNames: string[]
|
|
67
|
+
}): JsonSchemaType =>
|
|
68
|
+
sanitizeEntitySchema(
|
|
69
|
+
removeVirtualFieldsFromSchema(
|
|
70
|
+
entityToStandaloneJSONSchema({
|
|
71
|
+
config: req.payload.config,
|
|
72
|
+
defaultIDType: req.payload.db.defaultIDType,
|
|
73
|
+
entity,
|
|
74
|
+
i18n: req.i18n,
|
|
75
|
+
}) as unknown as JsonSchemaType,
|
|
76
|
+
virtualFieldNames,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
@@ -0,0 +1,103 @@
|
|
|
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('keeps a Lexical node union strict (oneOf) while simplifying relationship values to IDs', () => {
|
|
9
|
+
// Shaped like the schema the MCP server prepares for a collection with a Lexical richText field:
|
|
10
|
+
// a `$defs` node union (a `oneOf` of node shapes) where a relationship node's `value` is either
|
|
11
|
+
// an ID or a `$ref` to the populated related collection.
|
|
12
|
+
const standalone: JsonSchemaType = {
|
|
13
|
+
type: 'object',
|
|
14
|
+
$defs: {
|
|
15
|
+
LexicalNodes_ABCDEF12: {
|
|
16
|
+
oneOf: [
|
|
17
|
+
{
|
|
18
|
+
type: 'object',
|
|
19
|
+
additionalProperties: false,
|
|
20
|
+
properties: { type: { const: 'paragraph' } },
|
|
21
|
+
required: ['type'],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'object',
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
properties: {
|
|
27
|
+
type: { const: 'relationship' },
|
|
28
|
+
value: { oneOf: [{ type: 'string' }, { $ref: '#/$defs/posts' }] },
|
|
29
|
+
},
|
|
30
|
+
required: ['type'],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
posts: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
properties: { id: { type: 'string' }, title: { type: 'string' } },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
properties: {
|
|
41
|
+
id: { type: 'string' },
|
|
42
|
+
content: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
root: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
children: { type: 'array', items: { $ref: '#/$defs/LexicalNodes_ABCDEF12' } },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
expect(sanitizeEntitySchema(standalone)).toStrictEqual({
|
|
57
|
+
type: 'object',
|
|
58
|
+
$defs: {
|
|
59
|
+
// The node union is renamed to a short, readable `node`, and stays a strict discriminated
|
|
60
|
+
// `oneOf` - not loosened to `anyOf`...
|
|
61
|
+
node: {
|
|
62
|
+
oneOf: [
|
|
63
|
+
{
|
|
64
|
+
type: 'object',
|
|
65
|
+
additionalProperties: false,
|
|
66
|
+
properties: { type: { const: 'paragraph' } },
|
|
67
|
+
required: ['type'],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'object',
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
properties: {
|
|
73
|
+
type: { const: 'relationship' },
|
|
74
|
+
// ...while the relationship `value` is simplified to a bare ID (the populated-doc `$ref` is dropped).
|
|
75
|
+
value: { type: 'string', description: 'The ID of the related "posts" document.' },
|
|
76
|
+
},
|
|
77
|
+
required: ['type'],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
posts: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
properties: { id: { type: 'string' }, title: { type: 'string' } },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
properties: {
|
|
88
|
+
// `id` is dropped - it's a Payload-managed field MCP clients never set.
|
|
89
|
+
content: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
root: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
children: { type: 'array', items: { $ref: '#/$defs/node' } },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})
|