@payloadcms/plugin-mcp 4.0.0-internal.40de3ec → 4.0.0-internal.435b2d6
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/LICENSE.md +1 -1
- package/bin.js +7 -0
- package/dist/defaultAccess.d.ts +3 -0
- package/dist/defaultAccess.d.ts.map +1 -0
- package/dist/defaultAccess.js +3 -0
- package/dist/defaultAccess.js.map +1 -0
- package/dist/defineTool.d.ts +8 -8
- package/dist/defineTool.d.ts.map +1 -1
- package/dist/endpoint/access.d.ts +12 -5
- package/dist/endpoint/access.d.ts.map +1 -1
- package/dist/endpoint/access.js +75 -88
- package/dist/endpoint/access.js.map +1 -1
- package/dist/endpoint/index.d.ts +1 -1
- package/dist/endpoint/index.d.ts.map +1 -1
- package/dist/endpoint/index.js +68 -17
- package/dist/endpoint/index.js.map +1 -1
- package/dist/exports/client.d.ts +1 -1
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +1 -1
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/internal.d.ts +2 -0
- package/dist/exports/internal.d.ts.map +1 -0
- package/dist/exports/internal.js +3 -0
- package/dist/exports/internal.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -20
- package/dist/index.js.map +1 -1
- package/dist/mcp/buildMcpServer.d.ts +4 -4
- package/dist/mcp/buildMcpServer.d.ts.map +1 -1
- package/dist/mcp/buildMcpServer.js +133 -80
- package/dist/mcp/buildMcpServer.js.map +1 -1
- package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/authTools.js +44 -6
- package/dist/mcp/builtin/collections/authTools.js.map +1 -1
- package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/countTool.js +68 -0
- package/dist/mcp/builtin/collections/countTool.js.map +1 -0
- package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/countVersionsTool.js +64 -0
- package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
- 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 +50 -45
- 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 +15 -22
- package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
- package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/duplicateTool.js +96 -0
- package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
- package/dist/mcp/builtin/collections/fileInput.d.ts +20 -0
- package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/fileInput.js +100 -0
- package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
- package/dist/mcp/builtin/collections/fileInput.spec.js +72 -0
- package/dist/mcp/builtin/collections/fileInput.spec.js.map +1 -0
- package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/findDistinctTool.js +92 -0
- package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
- 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 +52 -47
- package/dist/mcp/builtin/collections/findTool.js.map +1 -1
- package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/findVersionByIDTool.js +83 -0
- package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
- package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/findVersionsTool.js +98 -0
- package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
- 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 +86 -0
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
- package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/restoreVersionTool.js +81 -0
- package/dist/mcp/builtin/collections/restoreVersionTool.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 +106 -93
- 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 +68 -0
- package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
- package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
- package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
- package/dist/mcp/builtin/globals/countVersionsTool.js +64 -0
- package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
- package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
- package/dist/mcp/builtin/globals/findTool.js +20 -23
- package/dist/mcp/builtin/globals/findTool.js.map +1 -1
- package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
- package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
- package/dist/mcp/builtin/globals/findVersionByIDTool.js +79 -0
- package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
- package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
- package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
- package/dist/mcp/builtin/globals/findVersionsTool.js +94 -0
- package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
- 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 +65 -0
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
- package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
- package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
- package/dist/mcp/builtin/globals/restoreVersionTool.js +77 -0
- package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
- package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
- package/dist/mcp/builtin/globals/updateTool.js +33 -42
- 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 +144 -19
- package/dist/mcp/builtinTools.d.ts.map +1 -1
- package/dist/mcp/builtinTools.js +119 -20
- package/dist/mcp/builtinTools.js.map +1 -1
- package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
- package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
- package/dist/mcp/sanitizeMCPConfig.js +121 -58
- package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
- package/dist/stdio.d.ts +3 -3
- package/dist/stdio.d.ts.map +1 -1
- package/dist/stdio.js +82 -45
- package/dist/stdio.js.map +1 -1
- package/dist/types.d.ts +112 -110
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -4
- package/dist/types.js.map +1 -1
- package/dist/utils/camelCase.d.ts.map +1 -1
- package/dist/utils/getPluginConfig.d.ts +1 -1
- package/dist/utils/getPluginConfig.js +1 -1
- package/dist/utils/getPluginConfig.js.map +1 -1
- package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
- package/dist/utils/schemaConversion/filterFieldsByAccess.d.ts +28 -0
- package/dist/utils/schemaConversion/filterFieldsByAccess.d.ts.map +1 -0
- package/dist/utils/schemaConversion/filterFieldsByAccess.js +71 -0
- package/dist/utils/schemaConversion/filterFieldsByAccess.js.map +1 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.d.ts +13 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +1 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.js +66 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.js.map +1 -0
- package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +5 -10
- package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +1 -1
- package/dist/utils/schemaConversion/sanitizeEntitySchema.js +15 -95
- package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +1 -1
- package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +15 -40
- package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +1 -1
- package/dist/utils/toStandardSchema.d.ts.map +1 -1
- 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 +10 -15
- package/src/defaultAccess.ts +3 -0
- package/src/endpoint/access.ts +81 -101
- package/src/endpoint/index.ts +58 -16
- package/src/exports/client.ts +2 -1
- package/src/exports/internal.ts +1 -0
- package/src/index.ts +12 -20
- package/src/mcp/buildMcpServer.ts +170 -115
- package/src/mcp/builtin/collections/authTools.ts +45 -9
- package/src/mcp/builtin/collections/countTool.ts +73 -0
- package/src/mcp/builtin/collections/countVersionsTool.ts +69 -0
- package/src/mcp/builtin/collections/createTool.ts +67 -62
- package/src/mcp/builtin/collections/deleteTool.ts +20 -18
- package/src/mcp/builtin/collections/duplicateTool.ts +134 -0
- package/src/mcp/builtin/collections/fileInput.spec.ts +79 -0
- package/src/mcp/builtin/collections/fileInput.ts +139 -0
- package/src/mcp/builtin/collections/findDistinctTool.ts +119 -0
- package/src/mcp/builtin/collections/findTool.ts +66 -37
- package/src/mcp/builtin/collections/findVersionByIDTool.ts +109 -0
- package/src/mcp/builtin/collections/findVersionsTool.ts +154 -0
- package/src/mcp/builtin/collections/formatCollectionError.ts +84 -0
- package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +76 -0
- package/src/mcp/builtin/collections/restoreVersionTool.ts +107 -0
- package/src/mcp/builtin/collections/updateTool.ts +132 -115
- package/src/mcp/builtin/getConfigInfoTool.ts +67 -0
- package/src/mcp/builtin/globals/countVersionsTool.ts +68 -0
- package/src/mcp/builtin/globals/findTool.ts +28 -19
- package/src/mcp/builtin/globals/findVersionByIDTool.ts +103 -0
- package/src/mcp/builtin/globals/findVersionsTool.ts +147 -0
- package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +63 -0
- package/src/mcp/builtin/globals/restoreVersionTool.ts +99 -0
- package/src/mcp/builtin/globals/updateTool.ts +52 -57
- package/src/mcp/builtin/validateEntityData.ts +132 -0
- package/src/mcp/builtinTools.ts +111 -41
- package/src/mcp/sanitizeMCPConfig.ts +117 -78
- package/src/stdio.ts +77 -45
- package/src/types.ts +128 -115
- package/src/utils/getPluginConfig.ts +1 -1
- package/src/utils/schemaConversion/filterFieldsByAccess.ts +123 -0
- package/src/utils/schemaConversion/getEntityInputSchema.ts +96 -0
- package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +12 -19
- package/src/utils/schemaConversion/sanitizeEntitySchema.ts +17 -114
- package/src/utils/whereSchema.ts +24 -0
- package/dist/@types/assets.d.js +0 -2
- package/dist/@types/assets.d.js.map +0 -1
- package/dist/collection/getAccessField.d.ts +0 -12
- package/dist/collection/getAccessField.d.ts.map +0 -1
- package/dist/collection/getAccessField.js +0 -57
- package/dist/collection/getAccessField.js.map +0 -1
- package/dist/collection/index.d.ts +0 -6
- package/dist/collection/index.d.ts.map +0 -1
- package/dist/collection/index.js +0 -60
- package/dist/collection/index.js.map +0 -1
- package/dist/components/AccessField/index.client.d.ts +0 -10
- package/dist/components/AccessField/index.client.d.ts.map +0 -1
- package/dist/components/AccessField/index.client.js +0 -305
- package/dist/components/AccessField/index.client.js.map +0 -1
- package/dist/components/AccessField/index.css +0 -93
- package/dist/utils/localAPIDefaults.d.ts +0 -20
- package/dist/utils/localAPIDefaults.d.ts.map +0 -1
- package/dist/utils/localAPIDefaults.js +0 -19
- package/dist/utils/localAPIDefaults.js.map +0 -1
- package/dist/utils/schemaConversion/buildToolInput.d.ts +0 -29
- package/dist/utils/schemaConversion/buildToolInput.d.ts.map +0 -1
- package/dist/utils/schemaConversion/buildToolInput.js +0 -51
- package/dist/utils/schemaConversion/buildToolInput.js.map +0 -1
- package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
- package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
- package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
- package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
- package/src/@types/assets.d.ts +0 -3
- package/src/collection/getAccessField.ts +0 -64
- package/src/collection/index.ts +0 -64
- package/src/components/AccessField/index.client.tsx +0 -344
- package/src/components/AccessField/index.css +0 -93
- package/src/utils/localAPIDefaults.ts +0 -22
- package/src/utils/schemaConversion/buildToolInput.ts +0 -68
- package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defaultAccess } from '../defaultAccess.js';
|
|
2
|
+
import { COLLECTION_AUTH_BUILTIN_ENTRIES, COLLECTION_AUTH_BUILTINS, COLLECTION_BUILTIN_ENTRIES, COLLECTION_BUILTINS, GLOBAL_BUILTIN_ENTRIES, GLOBAL_BUILTINS, TOOL_BUILTIN_ENTRIES, TOOL_BUILTINS } from './builtinTools.js';
|
|
2
3
|
/**
|
|
3
4
|
* Converts the user-configured `MCPPluginConfig` into a `SanitizedMCPPluginConfig`:
|
|
4
5
|
* - Flattens `tools` / `prompts` / `resources` / per-collection / per-global
|
|
5
6
|
* tool maps into a single `items` array.
|
|
6
7
|
* - Applies built-in tools for collections and globals, respecting opt-out user overrides.
|
|
7
|
-
* - Applies the `userCollection` default
|
|
8
8
|
*
|
|
9
9
|
* Called once during plugin init. After that, `plugins['@payloadcms/plugin-mcp']
|
|
10
10
|
* ?.options` holds the sanitized result
|
|
@@ -22,83 +22,137 @@ import { COLLECTION_AUTH_BUILTIN_ENTRIES, COLLECTION_AUTH_BUILTINS, COLLECTION_B
|
|
|
22
22
|
pluginConfig
|
|
23
23
|
}));
|
|
24
24
|
}
|
|
25
|
-
for (const [
|
|
25
|
+
for (const [configKey, { mcpName, tool }] of TOOL_BUILTIN_ENTRIES){
|
|
26
26
|
items.push({
|
|
27
27
|
type: 'tool',
|
|
28
|
-
|
|
29
|
-
label:
|
|
30
|
-
|
|
28
|
+
configKey,
|
|
29
|
+
label: tool.annotations?.title ?? configKey,
|
|
30
|
+
mcpName,
|
|
31
|
+
tool: {
|
|
32
|
+
...tool,
|
|
33
|
+
access: tool.access ?? defaultAccess
|
|
34
|
+
}
|
|
31
35
|
});
|
|
32
36
|
}
|
|
33
|
-
for (const [
|
|
37
|
+
for (const [configKey, tool] of Object.entries(pluginConfig.tools ?? {})){
|
|
38
|
+
if (configKey in TOOL_BUILTINS) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
items.push({
|
|
42
|
+
type: 'tool',
|
|
43
|
+
configKey,
|
|
44
|
+
label: tool.annotations?.title ?? configKey,
|
|
45
|
+
mcpName: configKey,
|
|
46
|
+
tool: {
|
|
47
|
+
...tool,
|
|
48
|
+
access: tool.access ?? defaultAccess
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
for (const [configKey, prompt] of Object.entries(pluginConfig.prompts ?? {})){
|
|
34
53
|
items.push({
|
|
35
54
|
type: 'prompt',
|
|
36
|
-
|
|
37
|
-
label: prompt.title ??
|
|
38
|
-
|
|
55
|
+
configKey,
|
|
56
|
+
label: prompt.title ?? configKey,
|
|
57
|
+
mcpName: configKey,
|
|
58
|
+
prompt: {
|
|
59
|
+
...prompt,
|
|
60
|
+
access: prompt.access ?? defaultAccess
|
|
61
|
+
}
|
|
39
62
|
});
|
|
40
63
|
}
|
|
41
|
-
for (const [
|
|
64
|
+
for (const [configKey, resource] of Object.entries(pluginConfig.resources ?? {})){
|
|
42
65
|
items.push({
|
|
43
66
|
type: 'resource',
|
|
44
|
-
|
|
45
|
-
label: resource.title ??
|
|
46
|
-
|
|
67
|
+
configKey,
|
|
68
|
+
label: resource.title ?? configKey,
|
|
69
|
+
mcpName: configKey,
|
|
70
|
+
resource: {
|
|
71
|
+
...resource,
|
|
72
|
+
access: resource.access ?? defaultAccess
|
|
73
|
+
}
|
|
47
74
|
});
|
|
48
75
|
}
|
|
49
|
-
// Mirror Payload's own admin.user detection (sanitize.ts) since plugins run first.
|
|
50
|
-
const firstCollectionWithAuth = config.collections.find(({ auth })=>Boolean(auth));
|
|
51
76
|
return {
|
|
52
77
|
disabled: pluginConfig.disabled,
|
|
78
|
+
hooks: pluginConfig.hooks,
|
|
53
79
|
items,
|
|
54
80
|
mcp: pluginConfig.mcp,
|
|
55
|
-
|
|
56
|
-
overrideAuth: pluginConfig.overrideAuth,
|
|
57
|
-
userCollection: pluginConfig.userCollection ?? config.admin?.user ?? firstCollectionWithAuth?.slug ?? 'users'
|
|
81
|
+
overrideGetAuthorizedMCP: pluginConfig.overrideGetAuthorizedMCP
|
|
58
82
|
};
|
|
59
83
|
};
|
|
60
84
|
const sanitizeCollectionConfig = ({ collection, pluginConfig })=>{
|
|
61
|
-
if (collection.slug === 'payload-mcp-api-keys') {
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
85
|
const slug = collection.slug;
|
|
65
86
|
const collectionPluginConfig = pluginConfig.collections?.[slug];
|
|
66
87
|
const items = [];
|
|
67
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Payload disables duplicate for auth collections by default unless the
|
|
90
|
+
* collection explicitly opts back in with `disableDuplicate: false`.
|
|
91
|
+
*/ const isDuplicateDisabled = collection.disableDuplicate === true || Boolean(collection.auth) && collection.disableDuplicate !== false;
|
|
92
|
+
for (const [toolKey, { mcpName, requiresDuplicateEnabled, requiresVersions, tool }] of COLLECTION_BUILTIN_ENTRIES){
|
|
93
|
+
if (requiresVersions && !collection.versions) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (requiresDuplicateEnabled && isDuplicateDisabled) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
68
99
|
const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey];
|
|
69
100
|
if (matchedConfigEntry === false) {
|
|
70
101
|
continue;
|
|
71
102
|
}
|
|
103
|
+
const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined;
|
|
104
|
+
const annotations = {
|
|
105
|
+
...tool.annotations,
|
|
106
|
+
...override?.annotations
|
|
107
|
+
};
|
|
72
108
|
items.push({
|
|
73
109
|
type: 'collectionTool',
|
|
74
110
|
collectionSlug: slug,
|
|
75
|
-
|
|
76
|
-
label:
|
|
77
|
-
|
|
111
|
+
configKey: toolKey,
|
|
112
|
+
label: annotations.title ?? toolKey,
|
|
113
|
+
mcpName,
|
|
114
|
+
tool: {
|
|
115
|
+
...tool,
|
|
116
|
+
access: override?.access ?? tool.access ?? defaultAccess,
|
|
117
|
+
annotations,
|
|
118
|
+
description: override?.description ?? tool.description,
|
|
119
|
+
overrideResponse: override?.overrideResponse ?? collectionPluginConfig?.overrideResponse ?? tool.overrideResponse
|
|
120
|
+
}
|
|
78
121
|
});
|
|
79
122
|
}
|
|
80
123
|
if (collection.auth) {
|
|
81
|
-
for (const [authToolKey, {
|
|
124
|
+
for (const [authToolKey, { mcpName, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES){
|
|
82
125
|
const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey];
|
|
83
126
|
if (!matchedConfigEntry) {
|
|
84
127
|
continue;
|
|
85
128
|
}
|
|
86
129
|
// `true` means "enable, no override"; only the object form carries fields.
|
|
87
130
|
const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined;
|
|
131
|
+
const annotations = {
|
|
132
|
+
...tool.annotations,
|
|
133
|
+
...override?.annotations
|
|
134
|
+
};
|
|
88
135
|
items.push({
|
|
89
136
|
type: 'collectionTool',
|
|
90
137
|
collectionSlug: slug,
|
|
91
|
-
|
|
92
|
-
label,
|
|
93
|
-
|
|
138
|
+
configKey: authToolKey,
|
|
139
|
+
label: annotations.title ?? authToolKey,
|
|
140
|
+
mcpName,
|
|
141
|
+
tool: {
|
|
142
|
+
...tool,
|
|
143
|
+
access: override?.access ?? tool.access ?? defaultAccess,
|
|
144
|
+
annotations,
|
|
145
|
+
description: override?.description ?? tool.description,
|
|
146
|
+
overrideResponse: override?.overrideResponse ?? collectionPluginConfig?.overrideResponse ?? tool.overrideResponse
|
|
147
|
+
}
|
|
94
148
|
});
|
|
95
149
|
}
|
|
96
150
|
}
|
|
97
151
|
// Cast: builtin keys are filtered out below, so the remaining values are
|
|
98
152
|
// always custom tools (`CollectionTool`) or undefined
|
|
99
153
|
const customEntries = Object.entries(collectionPluginConfig?.tools ?? {});
|
|
100
|
-
for (const [
|
|
101
|
-
if (
|
|
154
|
+
for (const [configKey, customTool] of customEntries){
|
|
155
|
+
if (configKey in COLLECTION_BUILTINS || configKey in COLLECTION_AUTH_BUILTINS) {
|
|
102
156
|
continue;
|
|
103
157
|
}
|
|
104
158
|
if (!customTool) {
|
|
@@ -107,9 +161,13 @@ const sanitizeCollectionConfig = ({ collection, pluginConfig })=>{
|
|
|
107
161
|
items.push({
|
|
108
162
|
type: 'collectionTool',
|
|
109
163
|
collectionSlug: slug,
|
|
110
|
-
|
|
111
|
-
label:
|
|
112
|
-
|
|
164
|
+
configKey,
|
|
165
|
+
label: customTool.annotations?.title ?? configKey,
|
|
166
|
+
mcpName: configKey,
|
|
167
|
+
tool: {
|
|
168
|
+
...customTool,
|
|
169
|
+
access: customTool.access ?? defaultAccess
|
|
170
|
+
}
|
|
113
171
|
});
|
|
114
172
|
}
|
|
115
173
|
return items;
|
|
@@ -118,22 +176,37 @@ const sanitizeGlobalConfig = ({ global, pluginConfig })=>{
|
|
|
118
176
|
const slug = global.slug;
|
|
119
177
|
const globalPluginConfig = pluginConfig.globals?.[slug];
|
|
120
178
|
const items = [];
|
|
121
|
-
for (const [toolKey,
|
|
179
|
+
for (const [toolKey, { mcpName, requiresVersions, tool }] of GLOBAL_BUILTIN_ENTRIES){
|
|
180
|
+
if (requiresVersions && !global.versions) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
122
183
|
const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey];
|
|
123
184
|
if (matchedConfigEntry === false) {
|
|
124
185
|
continue;
|
|
125
186
|
}
|
|
187
|
+
const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined;
|
|
188
|
+
const annotations = {
|
|
189
|
+
...tool.annotations,
|
|
190
|
+
...override?.annotations
|
|
191
|
+
};
|
|
126
192
|
items.push({
|
|
127
193
|
type: 'globalTool',
|
|
194
|
+
configKey: toolKey,
|
|
128
195
|
globalSlug: slug,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
tool:
|
|
196
|
+
label: annotations.title ?? toolKey,
|
|
197
|
+
mcpName,
|
|
198
|
+
tool: {
|
|
199
|
+
...tool,
|
|
200
|
+
access: override?.access ?? tool.access ?? defaultAccess,
|
|
201
|
+
annotations,
|
|
202
|
+
description: override?.description ?? tool.description,
|
|
203
|
+
overrideResponse: override?.overrideResponse ?? globalPluginConfig?.overrideResponse ?? tool.overrideResponse
|
|
204
|
+
}
|
|
132
205
|
});
|
|
133
206
|
}
|
|
134
207
|
const customEntries = Object.entries(globalPluginConfig?.tools ?? {});
|
|
135
|
-
for (const [
|
|
136
|
-
if (
|
|
208
|
+
for (const [configKey, customTool] of customEntries){
|
|
209
|
+
if (configKey in GLOBAL_BUILTINS) {
|
|
137
210
|
continue;
|
|
138
211
|
}
|
|
139
212
|
if (!customTool) {
|
|
@@ -141,27 +214,17 @@ const sanitizeGlobalConfig = ({ global, pluginConfig })=>{
|
|
|
141
214
|
}
|
|
142
215
|
items.push({
|
|
143
216
|
type: 'globalTool',
|
|
217
|
+
configKey,
|
|
144
218
|
globalSlug: slug,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
tool:
|
|
219
|
+
label: customTool.annotations?.title ?? configKey,
|
|
220
|
+
mcpName: configKey,
|
|
221
|
+
tool: {
|
|
222
|
+
...customTool,
|
|
223
|
+
access: customTool.access ?? defaultAccess
|
|
224
|
+
}
|
|
148
225
|
});
|
|
149
226
|
}
|
|
150
227
|
return items;
|
|
151
228
|
};
|
|
152
|
-
const capitalize = (s)=>s.charAt(0).toUpperCase() + s.slice(1);
|
|
153
|
-
/**
|
|
154
|
-
* Spread the static built-in tool and apply consumer-side overrides.
|
|
155
|
-
* Precedence: per-tool override > scope-level (collection/global) override >
|
|
156
|
-
* the static tool's defaults. `toolEntry` is whatever the user put under
|
|
157
|
-
* `tools: { find: ... }` — could be `true`, `false`, or an override object —
|
|
158
|
-
* so it's narrowed internally.
|
|
159
|
-
*/ const overrideBuiltinTool = (tool, toolOverride, entityPluginConfig)=>{
|
|
160
|
-
return {
|
|
161
|
-
...tool,
|
|
162
|
-
description: toolOverride?.description ?? entityPluginConfig?.description ?? tool.description,
|
|
163
|
-
overrideResponse: toolOverride?.overrideResponse ?? entityPluginConfig?.overrideResponse ?? tool.overrideResponse
|
|
164
|
-
};
|
|
165
|
-
};
|
|
166
229
|
|
|
167
230
|
//# sourceMappingURL=sanitizeMCPConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/sanitizeMCPConfig.ts"],"sourcesContent":["import type {\n CollectionConfig,\n Config,\n GlobalConfig,\n SanitizedCollectionConfig,\n SanitizedConfig,\n SanitizedGlobalConfig,\n} from 'payload'\n\nimport type {\n CollectionTool,\n GlobalTool,\n MCPBuiltInToolOverride,\n MCPItem,\n MCPPluginCollectionConfig,\n MCPPluginConfig,\n MCPPluginGlobalConfig,\n SanitizedMCPPluginConfig,\n} from '../types.js'\n\nimport {\n COLLECTION_AUTH_BUILTIN_ENTRIES,\n COLLECTION_AUTH_BUILTINS,\n COLLECTION_BUILTIN_ENTRIES,\n COLLECTION_BUILTINS,\n GLOBAL_BUILTIN_ENTRIES,\n GLOBAL_BUILTINS,\n} from './builtinTools.js'\n\n/**\n * Converts the user-configured `MCPPluginConfig` into a `SanitizedMCPPluginConfig`:\n * - Flattens `tools` / `prompts` / `resources` / per-collection / per-global\n * tool maps into a single `items` array.\n * - Applies built-in tools for collections and globals, respecting opt-out user overrides.\n * - Applies the `userCollection` default\n *\n * Called once during plugin init. After that, `plugins['@payloadcms/plugin-mcp']\n * ?.options` holds the sanitized result\n */\nexport const sanitizeMCPConfig = ({\n config,\n pluginConfig,\n}: {\n config: Config | SanitizedConfig\n pluginConfig: MCPPluginConfig\n}): SanitizedMCPPluginConfig => {\n const items: MCPItem[] = []\n\n for (const collection of config.collections ?? []) {\n items.push(...sanitizeCollectionConfig({ collection, pluginConfig }))\n }\n\n for (const global of config.globals ?? []) {\n items.push(...sanitizeGlobalConfig({ global, pluginConfig }))\n }\n\n for (const [key, tool] of Object.entries(pluginConfig.tools ?? {})) {\n items.push({\n type: 'tool',\n key,\n label: key,\n tool,\n })\n }\n\n for (const [key, prompt] of Object.entries(pluginConfig.prompts ?? {})) {\n items.push({\n type: 'prompt',\n key,\n label: prompt.title ?? key,\n prompt,\n })\n }\n\n for (const [key, resource] of Object.entries(pluginConfig.resources ?? {})) {\n items.push({\n type: 'resource',\n key,\n label: resource.title ?? key,\n resource,\n })\n }\n\n // Mirror Payload's own admin.user detection (sanitize.ts) since plugins run first.\n const firstCollectionWithAuth = config.collections!.find(({ auth }) => Boolean(auth))\n\n return {\n disabled: pluginConfig.disabled,\n items,\n mcp: pluginConfig.mcp,\n overrideApiKeyCollection: pluginConfig.overrideApiKeyCollection,\n overrideAuth: pluginConfig.overrideAuth,\n userCollection:\n pluginConfig.userCollection ?? config.admin?.user ?? firstCollectionWithAuth?.slug ?? 'users',\n }\n}\n\nconst sanitizeCollectionConfig = ({\n collection,\n pluginConfig,\n}: {\n collection: CollectionConfig | SanitizedCollectionConfig\n pluginConfig: MCPPluginConfig\n}): MCPItem[] => {\n if (collection.slug === 'payload-mcp-api-keys') {\n return []\n }\n const slug = collection.slug\n const collectionPluginConfig = pluginConfig.collections?.[slug]\n const items: MCPItem[] = []\n\n for (const [toolKey, tool] of COLLECTION_BUILTIN_ENTRIES) {\n const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n key: toolKey,\n label: capitalize(toolKey),\n tool: overrideBuiltinTool(tool, matchedConfigEntry, collectionPluginConfig),\n })\n }\n\n if (collection.auth) {\n for (const [authToolKey, { label, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES) {\n const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey]\n if (!matchedConfigEntry) {\n continue\n }\n // `true` means \"enable, no override\"; only the object form carries fields.\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n key: authToolKey,\n label,\n tool: overrideBuiltinTool(tool, override, collectionPluginConfig),\n })\n }\n }\n\n // Cast: builtin keys are filtered out below, so the remaining values are\n // always custom tools (`CollectionTool`) or undefined\n const customEntries = Object.entries(collectionPluginConfig?.tools ?? {}) as Array<\n [string, CollectionTool | undefined]\n >\n for (const [key, customTool] of customEntries) {\n if (key in COLLECTION_BUILTINS || key in COLLECTION_AUTH_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n key,\n label: key,\n tool: customTool,\n })\n }\n\n return items\n}\n\nconst sanitizeGlobalConfig = ({\n global,\n pluginConfig,\n}: {\n global: GlobalConfig | SanitizedGlobalConfig\n pluginConfig: MCPPluginConfig\n}): MCPItem[] => {\n const slug = global.slug\n const globalPluginConfig = pluginConfig.globals?.[slug]\n const items: MCPItem[] = []\n\n for (const [toolKey, baseTool] of GLOBAL_BUILTIN_ENTRIES) {\n const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n items.push({\n type: 'globalTool',\n globalSlug: slug,\n key: toolKey,\n label: capitalize(toolKey),\n tool: overrideBuiltinTool(baseTool, matchedConfigEntry, globalPluginConfig),\n })\n }\n\n const customEntries = Object.entries(globalPluginConfig?.tools ?? {}) as Array<\n [string, GlobalTool | undefined]\n >\n for (const [key, customTool] of customEntries) {\n if (key in GLOBAL_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'globalTool',\n globalSlug: slug,\n key,\n label: key,\n tool: customTool,\n })\n }\n\n return items\n}\n\nconst capitalize = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1)\n\n/**\n * Spread the static built-in tool and apply consumer-side overrides.\n * Precedence: per-tool override > scope-level (collection/global) override >\n * the static tool's defaults. `toolEntry` is whatever the user put under\n * `tools: { find: ... }` — could be `true`, `false`, or an override object —\n * so it's narrowed internally.\n */\nconst overrideBuiltinTool = <TTool extends CollectionTool | GlobalTool>(\n tool: TTool,\n toolOverride?: MCPBuiltInToolOverride,\n entityPluginConfig?: TTool extends CollectionTool\n ? MCPPluginCollectionConfig<any>\n : MCPPluginGlobalConfig,\n): TTool => {\n return {\n ...tool,\n description: toolOverride?.description ?? entityPluginConfig?.description ?? tool.description,\n overrideResponse:\n toolOverride?.overrideResponse ??\n entityPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n }\n}\n"],"names":["COLLECTION_AUTH_BUILTIN_ENTRIES","COLLECTION_AUTH_BUILTINS","COLLECTION_BUILTIN_ENTRIES","COLLECTION_BUILTINS","GLOBAL_BUILTIN_ENTRIES","GLOBAL_BUILTINS","sanitizeMCPConfig","config","pluginConfig","items","collection","collections","push","sanitizeCollectionConfig","global","globals","sanitizeGlobalConfig","key","tool","Object","entries","tools","type","label","prompt","prompts","title","resource","resources","firstCollectionWithAuth","find","auth","Boolean","disabled","mcp","overrideApiKeyCollection","overrideAuth","userCollection","admin","user","slug","collectionPluginConfig","toolKey","matchedConfigEntry","collectionSlug","capitalize","overrideBuiltinTool","authToolKey","override","undefined","customEntries","customTool","globalPluginConfig","baseTool","globalSlug","s","charAt","toUpperCase","slice","toolOverride","entityPluginConfig","description","overrideResponse"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/sanitizeMCPConfig.ts"],"sourcesContent":["import type {\n CollectionConfig,\n Config,\n GlobalConfig,\n SanitizedCollectionConfig,\n SanitizedConfig,\n SanitizedGlobalConfig,\n} from 'payload'\n\nimport type {\n CollectionMCPItem,\n CollectionTool,\n GlobalMCPItem,\n GlobalTool,\n MCPItem,\n MCPPluginConfig,\n SanitizedMCPPluginConfig,\n} from '../types.js'\n\nimport { defaultAccess } from '../defaultAccess.js'\nimport {\n COLLECTION_AUTH_BUILTIN_ENTRIES,\n COLLECTION_AUTH_BUILTINS,\n COLLECTION_BUILTIN_ENTRIES,\n COLLECTION_BUILTINS,\n GLOBAL_BUILTIN_ENTRIES,\n GLOBAL_BUILTINS,\n TOOL_BUILTIN_ENTRIES,\n TOOL_BUILTINS,\n} from './builtinTools.js'\n\n/**\n * Converts the user-configured `MCPPluginConfig` into a `SanitizedMCPPluginConfig`:\n * - Flattens `tools` / `prompts` / `resources` / per-collection / per-global\n * tool maps into a single `items` array.\n * - Applies built-in tools for collections and globals, respecting opt-out user overrides.\n *\n * Called once during plugin init. After that, `plugins['@payloadcms/plugin-mcp']\n * ?.options` holds the sanitized result\n */\nexport const sanitizeMCPConfig = ({\n config,\n pluginConfig,\n}: {\n config: Config | SanitizedConfig\n pluginConfig: MCPPluginConfig\n}): SanitizedMCPPluginConfig => {\n const items: MCPItem[] = []\n\n for (const collection of config.collections ?? []) {\n items.push(...sanitizeCollectionConfig({ collection, pluginConfig }))\n }\n\n for (const global of config.globals ?? []) {\n items.push(...sanitizeGlobalConfig({ global, pluginConfig }))\n }\n\n for (const [configKey, { mcpName, tool }] of TOOL_BUILTIN_ENTRIES) {\n items.push({\n type: 'tool',\n configKey,\n label: tool.annotations?.title ?? configKey,\n mcpName,\n tool: { ...tool, access: tool.access ?? defaultAccess },\n })\n }\n\n for (const [configKey, tool] of Object.entries(pluginConfig.tools ?? {})) {\n if (configKey in TOOL_BUILTINS) {\n continue\n }\n items.push({\n type: 'tool',\n configKey,\n label: tool.annotations?.title ?? configKey,\n mcpName: configKey,\n tool: { ...tool, access: tool.access ?? defaultAccess },\n })\n }\n\n for (const [configKey, prompt] of Object.entries(pluginConfig.prompts ?? {})) {\n items.push({\n type: 'prompt',\n configKey,\n label: prompt.title ?? configKey,\n mcpName: configKey,\n prompt: { ...prompt, access: prompt.access ?? defaultAccess },\n })\n }\n\n for (const [configKey, resource] of Object.entries(pluginConfig.resources ?? {})) {\n items.push({\n type: 'resource',\n configKey,\n label: resource.title ?? configKey,\n mcpName: configKey,\n resource: { ...resource, access: resource.access ?? defaultAccess },\n })\n }\n\n return {\n disabled: pluginConfig.disabled,\n hooks: pluginConfig.hooks,\n items,\n mcp: pluginConfig.mcp,\n overrideGetAuthorizedMCP: pluginConfig.overrideGetAuthorizedMCP,\n }\n}\n\nconst sanitizeCollectionConfig = ({\n collection,\n pluginConfig,\n}: {\n collection: CollectionConfig | SanitizedCollectionConfig\n pluginConfig: MCPPluginConfig\n}): CollectionMCPItem[] => {\n const slug = collection.slug\n const collectionPluginConfig = pluginConfig.collections?.[slug]\n const items: CollectionMCPItem[] = []\n /**\n * Payload disables duplicate for auth collections by default unless the\n * collection explicitly opts back in with `disableDuplicate: false`.\n */\n const isDuplicateDisabled =\n collection.disableDuplicate === true ||\n (Boolean(collection.auth) && collection.disableDuplicate !== false)\n\n for (const [\n toolKey,\n { mcpName, requiresDuplicateEnabled, requiresVersions, tool },\n ] of COLLECTION_BUILTIN_ENTRIES) {\n if (requiresVersions && !collection.versions) {\n continue\n }\n if (requiresDuplicateEnabled && isDuplicateDisabled) {\n continue\n }\n const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n const annotations = { ...tool.annotations, ...override?.annotations }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n configKey: toolKey,\n label: annotations.title ?? toolKey,\n mcpName,\n tool: {\n ...tool,\n access: override?.access ?? tool.access ?? defaultAccess,\n annotations,\n description: override?.description ?? tool.description,\n overrideResponse:\n override?.overrideResponse ??\n collectionPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n },\n })\n }\n\n if (collection.auth) {\n for (const [authToolKey, { mcpName, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES) {\n const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey]\n if (!matchedConfigEntry) {\n continue\n }\n // `true` means \"enable, no override\"; only the object form carries fields.\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n const annotations = { ...tool.annotations, ...override?.annotations }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n configKey: authToolKey,\n label: annotations.title ?? authToolKey,\n mcpName,\n tool: {\n ...tool,\n access: override?.access ?? tool.access ?? defaultAccess,\n annotations,\n description: override?.description ?? tool.description,\n overrideResponse:\n override?.overrideResponse ??\n collectionPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n },\n })\n }\n }\n\n // Cast: builtin keys are filtered out below, so the remaining values are\n // always custom tools (`CollectionTool`) or undefined\n const customEntries = Object.entries(collectionPluginConfig?.tools ?? {}) as Array<\n [string, CollectionTool | undefined]\n >\n for (const [configKey, customTool] of customEntries) {\n if (configKey in COLLECTION_BUILTINS || configKey in COLLECTION_AUTH_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n configKey,\n label: customTool.annotations?.title ?? configKey,\n mcpName: configKey,\n tool: { ...customTool, access: customTool.access ?? defaultAccess },\n })\n }\n\n return items\n}\n\nconst sanitizeGlobalConfig = ({\n global,\n pluginConfig,\n}: {\n global: GlobalConfig | SanitizedGlobalConfig\n pluginConfig: MCPPluginConfig\n}): GlobalMCPItem[] => {\n const slug = global.slug\n const globalPluginConfig = pluginConfig.globals?.[slug]\n const items: GlobalMCPItem[] = []\n\n for (const [toolKey, { mcpName, requiresVersions, tool }] of GLOBAL_BUILTIN_ENTRIES) {\n if (requiresVersions && !global.versions) {\n continue\n }\n const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n const annotations = { ...tool.annotations, ...override?.annotations }\n items.push({\n type: 'globalTool',\n configKey: toolKey,\n globalSlug: slug,\n label: annotations.title ?? toolKey,\n mcpName,\n tool: {\n ...tool,\n access: override?.access ?? tool.access ?? defaultAccess,\n annotations,\n description: override?.description ?? tool.description,\n overrideResponse:\n override?.overrideResponse ??\n globalPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n },\n })\n }\n\n const customEntries = Object.entries(globalPluginConfig?.tools ?? {}) as Array<\n [string, GlobalTool | undefined]\n >\n for (const [configKey, customTool] of customEntries) {\n if (configKey in GLOBAL_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'globalTool',\n configKey,\n globalSlug: slug,\n label: customTool.annotations?.title ?? configKey,\n mcpName: configKey,\n tool: { ...customTool, access: customTool.access ?? defaultAccess },\n })\n }\n\n return items\n}\n"],"names":["defaultAccess","COLLECTION_AUTH_BUILTIN_ENTRIES","COLLECTION_AUTH_BUILTINS","COLLECTION_BUILTIN_ENTRIES","COLLECTION_BUILTINS","GLOBAL_BUILTIN_ENTRIES","GLOBAL_BUILTINS","TOOL_BUILTIN_ENTRIES","TOOL_BUILTINS","sanitizeMCPConfig","config","pluginConfig","items","collection","collections","push","sanitizeCollectionConfig","global","globals","sanitizeGlobalConfig","configKey","mcpName","tool","type","label","annotations","title","access","Object","entries","tools","prompt","prompts","resource","resources","disabled","hooks","mcp","overrideGetAuthorizedMCP","slug","collectionPluginConfig","isDuplicateDisabled","disableDuplicate","Boolean","auth","toolKey","requiresDuplicateEnabled","requiresVersions","versions","matchedConfigEntry","override","undefined","collectionSlug","description","overrideResponse","authToolKey","customEntries","customTool","globalPluginConfig","globalSlug"],"mappings":"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"}
|
package/dist/stdio.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Starts Payload's MCP server over stdin and stdout.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,
|
|
5
|
+
* `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.
|
|
6
6
|
*/
|
|
7
7
|
export declare const runMcpStdio: () => Promise<void>;
|
|
8
8
|
//# sourceMappingURL=stdio.d.ts.map
|
package/dist/stdio.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CA2GhD,CAAA"}
|
package/dist/stdio.js
CHANGED
|
@@ -1,76 +1,113 @@
|
|
|
1
|
-
/* eslint-disable no-console */ import {
|
|
1
|
+
/* eslint-disable no-console */ import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
3
|
import { createLocalReq, getPayload } from 'payload';
|
|
4
4
|
import { findConfig } from 'payload/node';
|
|
5
|
+
import { getAuthorizedMCP } from './endpoint/access.js';
|
|
5
6
|
import { buildMcpServer } from './mcp/buildMcpServer.js';
|
|
6
7
|
import { sanitizeMCPConfig } from './mcp/sanitizeMCPConfig.js';
|
|
7
8
|
import { getPluginConfig } from './utils/getPluginConfig.js';
|
|
8
9
|
import { resolveProjectRoot } from './utils/resolveProjectRoot.js';
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Starts Payload's MCP server over stdin and stdout.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,
|
|
14
|
+
* `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.
|
|
14
15
|
*/ export const runMcpStdio = async ()=>{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* its own. This module always lives inside the project's `node_modules`, so
|
|
19
|
-
* we derive the project root from its own path and `chdir` into it.
|
|
20
|
-
*
|
|
21
|
-
* An absolute `PAYLOAD_CONFIG_PATH` still overrides everything; unusual
|
|
22
|
-
* layouts (e.g. a monorepo where the package is hoisted above the app) can
|
|
23
|
-
* fall back to it. See the implementation of findConfig for details.
|
|
24
|
-
*/ const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url));
|
|
16
|
+
// MCP clients may start this command from another folder. Move to the Payload
|
|
17
|
+
// project so findConfig() can find the config.
|
|
18
|
+
const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url));
|
|
25
19
|
if (projectRoot) {
|
|
26
20
|
process.chdir(projectRoot);
|
|
27
21
|
}
|
|
28
22
|
const configPath = findConfig();
|
|
29
23
|
const configModule = await import(pathToFileURL(configPath).toString());
|
|
30
24
|
const config = await (configModule.default ?? configModule);
|
|
25
|
+
/**
|
|
26
|
+
* stdout is only for MCP messages. The spec says the server must not write
|
|
27
|
+
* logs there and may write them to stderr instead.
|
|
28
|
+
*
|
|
29
|
+
* The 2.0 client library recovers from stray stdout logs, but that behavior is not
|
|
30
|
+
* guaranteed. Move Payload logs to stderr so Payload does not add invalid data
|
|
31
|
+
* to stdout. Keep options from configurable loggers. Replace pre-built loggers
|
|
32
|
+
* because their output cannot be redirected.
|
|
33
|
+
*
|
|
34
|
+
* @see https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#stdio
|
|
35
|
+
*/ const loggerConfig = config;
|
|
36
|
+
if (loggerConfig.logger && typeof loggerConfig.logger === 'object' && 'options' in loggerConfig.logger) {
|
|
37
|
+
loggerConfig.logger.destination = process.stderr;
|
|
38
|
+
} else {
|
|
39
|
+
loggerConfig.logger = {
|
|
40
|
+
destination: process.stderr,
|
|
41
|
+
options: {}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
31
44
|
const payload = await getPayload({
|
|
32
|
-
config
|
|
45
|
+
config,
|
|
46
|
+
cron: false,
|
|
47
|
+
disableOnInit: true
|
|
33
48
|
});
|
|
34
|
-
|
|
35
|
-
* If the user added `mcpPlugin({...})` to their `plugins` array, read the
|
|
36
|
-
* sanitized config they registered. Otherwise fall back to the defaults. That way,
|
|
37
|
-
* the mcp works on any project that has `@payloadcms/plugin-mcp` installed,
|
|
38
|
-
* even if the plugin is not added to the plugins array.
|
|
39
|
-
*/ let pluginConfig;
|
|
49
|
+
let pluginConfig;
|
|
40
50
|
try {
|
|
41
51
|
pluginConfig = getPluginConfig({
|
|
42
52
|
config: payload.config
|
|
43
53
|
});
|
|
44
54
|
} catch {
|
|
55
|
+
// The command also works when mcpPlugin() is not in the Payload config.
|
|
45
56
|
pluginConfig = sanitizeMCPConfig({
|
|
46
57
|
config: payload.config,
|
|
47
58
|
pluginConfig: {}
|
|
48
59
|
});
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
const fallbackPlugin = (config)=>config;
|
|
61
|
+
Object.assign(fallbackPlugin, {
|
|
62
|
+
slug: '@payloadcms/plugin-mcp',
|
|
63
|
+
sanitizedOptions: pluginConfig
|
|
64
|
+
});
|
|
65
|
+
payload.config.plugins ??= [];
|
|
66
|
+
payload.config.plugins.push(fallbackPlugin);
|
|
54
67
|
}
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
68
|
+
const overrideAccessEnv = process.env.PAYLOAD_MCP_OVERRIDE_ACCESS;
|
|
69
|
+
let overrideAccess = false;
|
|
70
|
+
if (overrideAccessEnv === 'true') {
|
|
71
|
+
overrideAccess = true;
|
|
72
|
+
} else if (overrideAccessEnv && overrideAccessEnv !== 'false') {
|
|
73
|
+
throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS must be "true" or "false".');
|
|
74
|
+
}
|
|
75
|
+
if (overrideAccess && process.env.NODE_ENV !== 'development') {
|
|
76
|
+
throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS is only available in development.');
|
|
77
|
+
}
|
|
78
|
+
const headers = new Headers();
|
|
79
|
+
if (process.env.PAYLOAD_MCP_AUTHORIZATION) {
|
|
80
|
+
headers.set('Authorization', process.env.PAYLOAD_MCP_AUTHORIZATION);
|
|
81
|
+
}
|
|
82
|
+
const req = await createLocalReq({
|
|
83
|
+
req: {
|
|
84
|
+
headers
|
|
85
|
+
}
|
|
86
|
+
}, payload);
|
|
61
87
|
req.payloadAPI = 'MCP';
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
pluginConfig,
|
|
88
|
+
const authorizedMCP = await getAuthorizedMCP({
|
|
89
|
+
overrideAccess,
|
|
65
90
|
req
|
|
66
91
|
});
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
const stdioServer = serveStdio(()=>buildMcpServer({
|
|
93
|
+
authorizedMCP,
|
|
94
|
+
pluginConfig,
|
|
95
|
+
req
|
|
96
|
+
}), {
|
|
97
|
+
onerror: (err)=>{
|
|
98
|
+
// MCP messages use stdout, so write SDK errors to stderr.
|
|
99
|
+
console.error('[payload-mcp] error serving MCP over stdio:', err);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// Close the MCP server and Payload when the client disconnects or the process stops.
|
|
103
|
+
let isShuttingDown = false;
|
|
104
|
+
const shutdown = async ()=>{
|
|
105
|
+
if (isShuttingDown) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
isShuttingDown = true;
|
|
72
109
|
try {
|
|
73
|
-
await
|
|
110
|
+
await stdioServer.close();
|
|
74
111
|
} catch (err) {
|
|
75
112
|
console.error('[payload-mcp] error closing server:', err);
|
|
76
113
|
}
|
|
@@ -79,11 +116,11 @@ import { resolveProjectRoot } from './utils/resolveProjectRoot.js';
|
|
|
79
116
|
} catch (err) {
|
|
80
117
|
console.error('[payload-mcp] error destroying payload:', err);
|
|
81
118
|
}
|
|
82
|
-
process.exit(
|
|
119
|
+
process.exit(0);
|
|
83
120
|
};
|
|
84
|
-
process.
|
|
85
|
-
process.
|
|
86
|
-
process.stdin.
|
|
121
|
+
process.once('SIGINT', ()=>void shutdown());
|
|
122
|
+
process.once('SIGTERM', ()=>void shutdown());
|
|
123
|
+
process.stdin.once('close', ()=>void shutdown());
|
|
87
124
|
};
|
|
88
125
|
|
|
89
126
|
//# sourceMappingURL=stdio.js.map
|
package/dist/stdio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stdio.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["../src/stdio.ts"],"sourcesContent":["import type { Config, Plugin, SanitizedConfig } from 'payload'\n\n/* eslint-disable no-console */\nimport { serveStdio } from '@modelcontextprotocol/server/stdio'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport { createLocalReq, getPayload } from 'payload'\nimport { findConfig } from 'payload/node'\n\nimport type { SanitizedMCPPluginConfig } from './types.js'\n\nimport { getAuthorizedMCP } from './endpoint/access.js'\nimport { buildMcpServer } from './mcp/buildMcpServer.js'\nimport { sanitizeMCPConfig } from './mcp/sanitizeMCPConfig.js'\nimport { getPluginConfig } from './utils/getPluginConfig.js'\nimport { resolveProjectRoot } from './utils/resolveProjectRoot.js'\n\n/**\n * Starts Payload's MCP server over stdin and stdout.\n *\n * Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,\n * `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.\n */\nexport const runMcpStdio = async (): Promise<void> => {\n // MCP clients may start this command from another folder. Move to the Payload\n // project so findConfig() can find the config.\n const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url))\n if (projectRoot) {\n process.chdir(projectRoot)\n }\n\n const configPath = findConfig()\n const configModule = await import(pathToFileURL(configPath).toString())\n const config = (await (configModule.default ?? configModule)) as SanitizedConfig\n\n /**\n * stdout is only for MCP messages. The spec says the server must not write\n * logs there and may write them to stderr instead.\n *\n * The 2.0 client library recovers from stray stdout logs, but that behavior is not\n * guaranteed. Move Payload logs to stderr so Payload does not add invalid data\n * to stdout. Keep options from configurable loggers. Replace pre-built loggers\n * because their output cannot be redirected.\n *\n * @see https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#stdio\n */\n const loggerConfig: { logger?: Config['logger'] } = config\n if (\n loggerConfig.logger &&\n typeof loggerConfig.logger === 'object' &&\n 'options' in loggerConfig.logger\n ) {\n loggerConfig.logger.destination = process.stderr\n } else {\n loggerConfig.logger = { destination: process.stderr, options: {} }\n }\n\n const payload = await getPayload({ config, cron: false, disableOnInit: true })\n\n let pluginConfig: SanitizedMCPPluginConfig\n try {\n pluginConfig = getPluginConfig({ config: payload.config })\n } catch {\n // The command also works when mcpPlugin() is not in the Payload config.\n pluginConfig = sanitizeMCPConfig({ config: payload.config, pluginConfig: {} })\n\n const fallbackPlugin: Plugin = (config) => config\n Object.assign(fallbackPlugin, {\n slug: '@payloadcms/plugin-mcp',\n sanitizedOptions: pluginConfig,\n })\n payload.config.plugins ??= []\n payload.config.plugins.push(fallbackPlugin)\n }\n\n const overrideAccessEnv = process.env.PAYLOAD_MCP_OVERRIDE_ACCESS\n let overrideAccess = false\n\n if (overrideAccessEnv === 'true') {\n overrideAccess = true\n } else if (overrideAccessEnv && overrideAccessEnv !== 'false') {\n throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS must be \"true\" or \"false\".')\n }\n\n if (overrideAccess && process.env.NODE_ENV !== 'development') {\n throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS is only available in development.')\n }\n\n const headers = new Headers()\n if (process.env.PAYLOAD_MCP_AUTHORIZATION) {\n headers.set('Authorization', process.env.PAYLOAD_MCP_AUTHORIZATION)\n }\n\n const req = await createLocalReq({ req: { headers } }, payload)\n req.payloadAPI = 'MCP' as const\n const authorizedMCP = await getAuthorizedMCP({ overrideAccess, req })\n\n const stdioServer = serveStdio(() => buildMcpServer({ authorizedMCP, pluginConfig, req }), {\n onerror: (err) => {\n // MCP messages use stdout, so write SDK errors to stderr.\n console.error('[payload-mcp] error serving MCP over stdio:', err)\n },\n })\n\n // Close the MCP server and Payload when the client disconnects or the process stops.\n let isShuttingDown = false\n const shutdown = async () => {\n if (isShuttingDown) {\n return\n }\n isShuttingDown = true\n\n try {\n await stdioServer.close()\n } catch (err) {\n console.error('[payload-mcp] error closing server:', err)\n }\n\n try {\n await payload.destroy()\n } catch (err) {\n console.error('[payload-mcp] error destroying payload:', err)\n }\n\n process.exit(0)\n }\n\n process.once('SIGINT', () => void shutdown())\n process.once('SIGTERM', () => void shutdown())\n process.stdin.once('close', () => void shutdown())\n}\n"],"names":["serveStdio","fileURLToPath","pathToFileURL","createLocalReq","getPayload","findConfig","getAuthorizedMCP","buildMcpServer","sanitizeMCPConfig","getPluginConfig","resolveProjectRoot","runMcpStdio","projectRoot","url","process","chdir","configPath","configModule","toString","config","default","loggerConfig","logger","destination","stderr","options","payload","cron","disableOnInit","pluginConfig","fallbackPlugin","Object","assign","slug","sanitizedOptions","plugins","push","overrideAccessEnv","env","PAYLOAD_MCP_OVERRIDE_ACCESS","overrideAccess","Error","NODE_ENV","headers","Headers","PAYLOAD_MCP_AUTHORIZATION","set","req","payloadAPI","authorizedMCP","stdioServer","onerror","err","console","error","isShuttingDown","shutdown","close","destroy","exit","once","stdin"],"mappings":"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"}
|