@payloadcms/plugin-mcp 4.0.0-internal.40de3ec → 4.0.0-internal.45cfa3d
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/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/endpoint/access.d.ts +17 -5
- package/dist/endpoint/access.d.ts.map +1 -1
- package/dist/endpoint/access.js +82 -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 +69 -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 +65 -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 +38 -42
- 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 +14 -20
- 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 +97 -0
- package/dist/mcp/builtin/collections/duplicateTool.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 +93 -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 +49 -43
- 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 +84 -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 +99 -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 +47 -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 +82 -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 +87 -86
- 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 +71 -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 +65 -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 +18 -21
- 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 +80 -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 +95 -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 +47 -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 +78 -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 +31 -40
- 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 +75 -110
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -4
- package/dist/types.js.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/schemaConversion/getEntityInputSchema.d.ts +11 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +1 -0
- package/dist/utils/schemaConversion/getEntityInputSchema.js +32 -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/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 +88 -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 +74 -0
- package/src/mcp/builtin/collections/countVersionsTool.ts +73 -0
- package/src/mcp/builtin/collections/createTool.ts +55 -59
- package/src/mcp/builtin/collections/deleteTool.ts +19 -16
- package/src/mcp/builtin/collections/duplicateTool.ts +135 -0
- package/src/mcp/builtin/collections/findDistinctTool.ts +119 -0
- package/src/mcp/builtin/collections/findTool.ts +63 -33
- package/src/mcp/builtin/collections/findVersionByIDTool.ts +110 -0
- package/src/mcp/builtin/collections/findVersionsTool.ts +155 -0
- package/src/mcp/builtin/collections/formatCollectionError.ts +84 -0
- package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +46 -0
- package/src/mcp/builtin/collections/restoreVersionTool.ts +108 -0
- package/src/mcp/builtin/collections/updateTool.ts +111 -110
- package/src/mcp/builtin/getConfigInfoTool.ts +69 -0
- package/src/mcp/builtin/globals/countVersionsTool.ts +72 -0
- package/src/mcp/builtin/globals/findTool.ts +26 -17
- package/src/mcp/builtin/globals/findVersionByIDTool.ts +104 -0
- package/src/mcp/builtin/globals/findVersionsTool.ts +148 -0
- package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +41 -0
- package/src/mcp/builtin/globals/restoreVersionTool.ts +99 -0
- package/src/mcp/builtin/globals/updateTool.ts +50 -55
- 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 +92 -115
- package/src/utils/getPluginConfig.ts +1 -1
- package/src/utils/schemaConversion/getEntityInputSchema.ts +62 -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/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/schemaConversion/buildToolInput.ts +0 -68
- package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
package/src/stdio.ts
CHANGED
|
@@ -1,35 +1,28 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Config, Plugin, SanitizedConfig } from 'payload'
|
|
2
2
|
|
|
3
3
|
/* eslint-disable no-console */
|
|
4
|
-
import {
|
|
4
|
+
import { serveStdio } from '@modelcontextprotocol/server/stdio'
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
6
6
|
import { createLocalReq, getPayload } from 'payload'
|
|
7
7
|
import { findConfig } from 'payload/node'
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { SanitizedMCPPluginConfig } from './types.js'
|
|
10
10
|
|
|
11
|
+
import { getAuthorizedMCP } from './endpoint/access.js'
|
|
11
12
|
import { buildMcpServer } from './mcp/buildMcpServer.js'
|
|
12
13
|
import { sanitizeMCPConfig } from './mcp/sanitizeMCPConfig.js'
|
|
13
14
|
import { getPluginConfig } from './utils/getPluginConfig.js'
|
|
14
15
|
import { resolveProjectRoot } from './utils/resolveProjectRoot.js'
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
+
* Starts Payload's MCP server over stdin and stdout.
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,
|
|
21
|
+
* `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.
|
|
21
22
|
*/
|
|
22
23
|
export const runMcpStdio = async (): Promise<void> => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Payload's cwd-anchored `findConfig()` can't locate the project on
|
|
26
|
-
* its own. This module always lives inside the project's `node_modules`, so
|
|
27
|
-
* we derive the project root from its own path and `chdir` into it.
|
|
28
|
-
*
|
|
29
|
-
* An absolute `PAYLOAD_CONFIG_PATH` still overrides everything; unusual
|
|
30
|
-
* layouts (e.g. a monorepo where the package is hoisted above the app) can
|
|
31
|
-
* fall back to it. See the implementation of findConfig for details.
|
|
32
|
-
*/
|
|
24
|
+
// MCP clients may start this command from another folder. Move to the Payload
|
|
25
|
+
// project so findConfig() can find the config.
|
|
33
26
|
const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url))
|
|
34
27
|
if (projectRoot) {
|
|
35
28
|
process.chdir(projectRoot)
|
|
@@ -37,62 +30,101 @@ export const runMcpStdio = async (): Promise<void> => {
|
|
|
37
30
|
|
|
38
31
|
const configPath = findConfig()
|
|
39
32
|
const configModule = await import(pathToFileURL(configPath).toString())
|
|
40
|
-
const config = (await (configModule.default ?? configModule)) as
|
|
41
|
-
|
|
42
|
-
const payload = await getPayload({ config })
|
|
33
|
+
const config = (await (configModule.default ?? configModule)) as SanitizedConfig
|
|
43
34
|
|
|
44
35
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
36
|
+
* stdout is only for MCP messages. The spec says the server must not write
|
|
37
|
+
* logs there and may write them to stderr instead.
|
|
38
|
+
*
|
|
39
|
+
* The 2.0 client library recovers from stray stdout logs, but that behavior is not
|
|
40
|
+
* guaranteed. Move Payload logs to stderr so Payload does not add invalid data
|
|
41
|
+
* to stdout. Keep options from configurable loggers. Replace pre-built loggers
|
|
42
|
+
* because their output cannot be redirected.
|
|
43
|
+
*
|
|
44
|
+
* @see https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#stdio
|
|
49
45
|
*/
|
|
46
|
+
const loggerConfig: { logger?: Config['logger'] } = config
|
|
47
|
+
if (
|
|
48
|
+
loggerConfig.logger &&
|
|
49
|
+
typeof loggerConfig.logger === 'object' &&
|
|
50
|
+
'options' in loggerConfig.logger
|
|
51
|
+
) {
|
|
52
|
+
loggerConfig.logger.destination = process.stderr
|
|
53
|
+
} else {
|
|
54
|
+
loggerConfig.logger = { destination: process.stderr, options: {} }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const payload = await getPayload({ config, cron: false, disableOnInit: true })
|
|
58
|
+
|
|
50
59
|
let pluginConfig: SanitizedMCPPluginConfig
|
|
51
60
|
try {
|
|
52
61
|
pluginConfig = getPluginConfig({ config: payload.config })
|
|
53
62
|
} catch {
|
|
63
|
+
// The command also works when mcpPlugin() is not in the Payload config.
|
|
54
64
|
pluginConfig = sanitizeMCPConfig({ config: payload.config, pluginConfig: {} })
|
|
55
65
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
const fallbackPlugin: Plugin = (config) => config
|
|
67
|
+
Object.assign(fallbackPlugin, {
|
|
68
|
+
slug: '@payloadcms/plugin-mcp',
|
|
69
|
+
sanitizedOptions: pluginConfig,
|
|
70
|
+
})
|
|
71
|
+
payload.config.plugins ??= []
|
|
72
|
+
payload.config.plugins.push(fallbackPlugin)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const overrideAccessEnv = process.env.PAYLOAD_MCP_OVERRIDE_ACCESS
|
|
76
|
+
let overrideAccess = false
|
|
77
|
+
|
|
78
|
+
if (overrideAccessEnv === 'true') {
|
|
79
|
+
overrideAccess = true
|
|
80
|
+
} else if (overrideAccessEnv && overrideAccessEnv !== 'false') {
|
|
81
|
+
throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS must be "true" or "false".')
|
|
82
|
+
}
|
|
60
83
|
|
|
61
|
-
|
|
62
|
-
|
|
84
|
+
if (overrideAccess && process.env.NODE_ENV !== 'development') {
|
|
85
|
+
throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS is only available in development.')
|
|
63
86
|
}
|
|
64
87
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
user: null,
|
|
88
|
+
const headers = new Headers()
|
|
89
|
+
if (process.env.PAYLOAD_MCP_AUTHORIZATION) {
|
|
90
|
+
headers.set('Authorization', process.env.PAYLOAD_MCP_AUTHORIZATION)
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
const req = await createLocalReq({}, payload)
|
|
93
|
+
const req = await createLocalReq({ req: { headers } }, payload)
|
|
72
94
|
req.payloadAPI = 'MCP' as const
|
|
95
|
+
const authorizedMCP = await getAuthorizedMCP({ overrideAccess, req })
|
|
73
96
|
|
|
74
|
-
const
|
|
97
|
+
const stdioServer = serveStdio(() => buildMcpServer({ authorizedMCP, pluginConfig, req }), {
|
|
98
|
+
onerror: (err) => {
|
|
99
|
+
// MCP messages use stdout, so write SDK errors to stderr.
|
|
100
|
+
console.error('[payload-mcp] error serving MCP over stdio:', err)
|
|
101
|
+
},
|
|
102
|
+
})
|
|
75
103
|
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
// Close the MCP server and Payload when the client disconnects or the process stops.
|
|
105
|
+
let isShuttingDown = false
|
|
106
|
+
const shutdown = async () => {
|
|
107
|
+
if (isShuttingDown) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
isShuttingDown = true
|
|
78
111
|
|
|
79
|
-
// Process now lives until stdin closes. Wire graceful shutdown so DB
|
|
80
|
-
// connections (and any other Payload-managed resources) get released.
|
|
81
|
-
const shutdown = async (code = 0) => {
|
|
82
112
|
try {
|
|
83
|
-
await
|
|
113
|
+
await stdioServer.close()
|
|
84
114
|
} catch (err) {
|
|
85
115
|
console.error('[payload-mcp] error closing server:', err)
|
|
86
116
|
}
|
|
117
|
+
|
|
87
118
|
try {
|
|
88
119
|
await payload.destroy()
|
|
89
120
|
} catch (err) {
|
|
90
121
|
console.error('[payload-mcp] error destroying payload:', err)
|
|
91
122
|
}
|
|
92
|
-
|
|
123
|
+
|
|
124
|
+
process.exit(0)
|
|
93
125
|
}
|
|
94
126
|
|
|
95
|
-
process.
|
|
96
|
-
process.
|
|
97
|
-
process.stdin.
|
|
127
|
+
process.once('SIGINT', () => void shutdown())
|
|
128
|
+
process.once('SIGTERM', () => void shutdown())
|
|
129
|
+
process.stdin.once('close', () => void shutdown())
|
|
98
130
|
}
|
package/src/types.ts
CHANGED
|
@@ -6,18 +6,19 @@ import type {
|
|
|
6
6
|
ResourceTemplate,
|
|
7
7
|
ServerContext,
|
|
8
8
|
StandardSchemaWithJSON,
|
|
9
|
+
ToolAnnotations,
|
|
9
10
|
} from '@modelcontextprotocol/server'
|
|
10
11
|
import type {
|
|
11
12
|
AuthCollectionSlug,
|
|
12
|
-
CollectionConfig,
|
|
13
13
|
CollectionSlug,
|
|
14
|
-
DefaultDocumentIDType,
|
|
15
14
|
GlobalSlug,
|
|
16
15
|
MaybePromise,
|
|
17
16
|
PayloadRequest,
|
|
18
|
-
|
|
17
|
+
SanitizedPermissions,
|
|
18
|
+
User,
|
|
19
19
|
} from 'payload'
|
|
20
20
|
|
|
21
|
+
import type { GetAuthorizedMCPArgs } from './endpoint/access.js'
|
|
21
22
|
import type {
|
|
22
23
|
MCPCollectionAuthToolName,
|
|
23
24
|
MCPCollectionBuiltinName,
|
|
@@ -26,34 +27,16 @@ import type {
|
|
|
26
27
|
|
|
27
28
|
export type { MCPCollectionAuthToolName, MCPCollectionBuiltinName, MCPGlobalBuiltinName }
|
|
28
29
|
|
|
29
|
-
/** Re-exported from `@modelcontextprotocol/server` —
|
|
30
|
-
export type { JsonSchemaType, StandardSchemaWithJSON }
|
|
30
|
+
/** Re-exported from `@modelcontextprotocol/server` — common MCP types used in plugin config. */
|
|
31
|
+
export type { JsonSchemaType, StandardSchemaWithJSON, ToolAnnotations }
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* What a tool's `input` (or a prompt's `argsSchema`) can be — either a raw
|
|
34
|
-
* JSON Schema literal, or a Standard Schema instance (Zod, Valibot, …).
|
|
35
|
+
* JSON Schema 2020-12 literal, or a Standard Schema instance (Zod, Valibot, …).
|
|
36
|
+
* Raw schemas may omit `$schema`; when present it must declare the 2020-12 dialect.
|
|
35
37
|
*/
|
|
36
38
|
export type ToolInputSchema = JsonSchemaType | StandardSchemaWithJSON
|
|
37
39
|
|
|
38
|
-
/**
|
|
39
|
-
* Serializable mirror of `SanitizedMCPPluginConfig` for client components —
|
|
40
|
-
* the full sanitized config carries functions (tool handlers, etc.) that can't
|
|
41
|
-
* cross the server→client boundary. Built by `sanitizeClientPluginConfig` and
|
|
42
|
-
* passed to the `AccessField` component via `clientProps`.
|
|
43
|
-
*
|
|
44
|
-
* @internal
|
|
45
|
-
*/
|
|
46
|
-
export type ClientMCPPluginConfig = {
|
|
47
|
-
items: Array<{
|
|
48
|
-
collectionSlug?: string
|
|
49
|
-
description: string
|
|
50
|
-
globalSlug?: string
|
|
51
|
-
key: string
|
|
52
|
-
label: string
|
|
53
|
-
type: 'collectionTool' | 'globalTool' | 'prompt' | 'resource' | 'tool'
|
|
54
|
-
}>
|
|
55
|
-
}
|
|
56
|
-
|
|
57
40
|
export type MCPToolResponse = {
|
|
58
41
|
content: ContentBlock[]
|
|
59
42
|
/**
|
|
@@ -70,6 +53,13 @@ export type MCPResponseOverride = (
|
|
|
70
53
|
req: PayloadRequest,
|
|
71
54
|
) => MCPToolResponse
|
|
72
55
|
|
|
56
|
+
export type MCPAfterToolCallHook = (args: {
|
|
57
|
+
input: unknown
|
|
58
|
+
req: PayloadRequest
|
|
59
|
+
response: MCPToolResponse
|
|
60
|
+
toolName: string
|
|
61
|
+
}) => MaybePromise<MCPToolResponse>
|
|
62
|
+
|
|
73
63
|
/**
|
|
74
64
|
* The handler's `input` type. A specific Standard Schema (Zod, Valibot, …) gets
|
|
75
65
|
* its inferred output; anything else falls back to `Record<string, unknown>`.
|
|
@@ -80,6 +70,19 @@ export type TypedInput<TSchema> = TSchema extends StandardSchemaWithJSON
|
|
|
80
70
|
: StandardSchemaWithJSON.InferOutput<TSchema>
|
|
81
71
|
: Record<string, unknown>
|
|
82
72
|
|
|
73
|
+
export type MCPAccessArgs = {
|
|
74
|
+
permissions?: SanitizedPermissions
|
|
75
|
+
req: PayloadRequest
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type CollectionMCPAccessArgs = {
|
|
79
|
+
collectionSlug: CollectionSlug
|
|
80
|
+
} & MCPAccessArgs
|
|
81
|
+
|
|
82
|
+
export type GlobalMCPAccessArgs = {
|
|
83
|
+
globalSlug: GlobalSlug
|
|
84
|
+
} & MCPAccessArgs
|
|
85
|
+
|
|
83
86
|
export type ToolHandlerArgs<TSchema = undefined> = {
|
|
84
87
|
authorizedMCP: AuthorizedMCP
|
|
85
88
|
input: TypedInput<TSchema>
|
|
@@ -96,6 +99,8 @@ export type GlobalToolHandlerArgs<TSchema = undefined> = {
|
|
|
96
99
|
} & ToolHandlerArgs<TSchema>
|
|
97
100
|
|
|
98
101
|
export type Tool<TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined> = {
|
|
102
|
+
access?: (args: MCPAccessArgs) => MaybePromise<boolean>
|
|
103
|
+
annotations?: ToolAnnotations
|
|
99
104
|
description: string
|
|
100
105
|
handler: (args: ToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
101
106
|
input?: TSchema
|
|
@@ -105,30 +110,37 @@ export type Tool<TSchema extends ToolInputSchema | undefined = ToolInputSchema |
|
|
|
105
110
|
overrideResponse?: MCPResponseOverride
|
|
106
111
|
}
|
|
107
112
|
|
|
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
113
|
export type CollectionTool<
|
|
114
114
|
TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined,
|
|
115
115
|
> = {
|
|
116
|
+
access?: (args: CollectionMCPAccessArgs) => MaybePromise<boolean>
|
|
116
117
|
handler: (args: CollectionToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
117
|
-
input?:
|
|
118
|
-
} & Pick<Tool, 'description' | 'overrideResponse'>
|
|
118
|
+
input?: TSchema
|
|
119
|
+
} & Pick<Tool, 'annotations' | 'description' | 'overrideResponse'>
|
|
119
120
|
|
|
120
121
|
export type GlobalTool<TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined> =
|
|
121
122
|
{
|
|
123
|
+
access?: (args: GlobalMCPAccessArgs) => MaybePromise<boolean>
|
|
122
124
|
handler: (args: GlobalToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
123
|
-
input?:
|
|
124
|
-
} & Pick<Tool, 'description' | 'overrideResponse'>
|
|
125
|
+
input?: TSchema
|
|
126
|
+
} & Pick<Tool, 'annotations' | 'description' | 'overrideResponse'>
|
|
125
127
|
|
|
126
128
|
/**
|
|
127
129
|
* Configures (or disables) a built-in tool without replacing it.
|
|
128
130
|
* `handler?: never` prevents a full `CollectionTool`/`GlobalTool` (which has a
|
|
129
131
|
* required handler) from being silently accepted at a built-in key slot.
|
|
130
132
|
*/
|
|
131
|
-
export type
|
|
133
|
+
export type MCPBuiltInCollectionToolOverride = {
|
|
134
|
+
access?: (args: CollectionMCPAccessArgs) => MaybePromise<boolean>
|
|
135
|
+
annotations?: ToolAnnotations
|
|
136
|
+
description?: string
|
|
137
|
+
handler?: never
|
|
138
|
+
overrideResponse?: MCPResponseOverride
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export type MCPBuiltInGlobalToolOverride = {
|
|
142
|
+
access?: (args: GlobalMCPAccessArgs) => MaybePromise<boolean>
|
|
143
|
+
annotations?: ToolAnnotations
|
|
132
144
|
description?: string
|
|
133
145
|
handler?: never
|
|
134
146
|
overrideResponse?: MCPResponseOverride
|
|
@@ -142,13 +154,13 @@ export type MCPBuiltInToolOverride = {
|
|
|
142
154
|
export type MCPTopLevelToolEntry = Tool
|
|
143
155
|
|
|
144
156
|
export type MCPCollectionToolsMap = {
|
|
145
|
-
[customToolName: string]: boolean | CollectionTool |
|
|
157
|
+
[customToolName: string]: boolean | CollectionTool | MCPBuiltInCollectionToolOverride | undefined
|
|
146
158
|
} & {
|
|
147
|
-
[K in MCPCollectionBuiltinName]?: false |
|
|
159
|
+
[K in MCPCollectionBuiltinName]?: false | MCPBuiltInCollectionToolOverride
|
|
148
160
|
}
|
|
149
161
|
|
|
150
162
|
export type MCPAuthCollectionToolsMap = {
|
|
151
|
-
[K in MCPCollectionAuthToolName]?:
|
|
163
|
+
[K in MCPCollectionAuthToolName]?: MCPBuiltInCollectionToolOverride | true
|
|
152
164
|
} & MCPCollectionToolsMap
|
|
153
165
|
|
|
154
166
|
/** Auth-enabled collections get auth-tool name autocomplete; others get CRUD-only. */
|
|
@@ -157,9 +169,9 @@ export type MCPToolsMapForCollection<Slug extends CollectionSlug> = Slug extends
|
|
|
157
169
|
: MCPCollectionToolsMap
|
|
158
170
|
|
|
159
171
|
export type MCPGlobalToolsMap = {
|
|
160
|
-
[customToolName: string]: boolean | GlobalTool |
|
|
172
|
+
[customToolName: string]: boolean | GlobalTool | MCPBuiltInGlobalToolOverride | undefined
|
|
161
173
|
} & {
|
|
162
|
-
[K in MCPGlobalBuiltinName]?: false |
|
|
174
|
+
[K in MCPGlobalBuiltinName]?: false | MCPBuiltInGlobalToolOverride
|
|
163
175
|
}
|
|
164
176
|
|
|
165
177
|
export type MCPTopLevelToolsMap = Record<string, Tool>
|
|
@@ -171,6 +183,7 @@ export type PromptHandlerArgs<TSchema = undefined> = {
|
|
|
171
183
|
}
|
|
172
184
|
|
|
173
185
|
export type Prompt<TSchema extends ToolInputSchema = ToolInputSchema> = {
|
|
186
|
+
access?: (args: MCPAccessArgs) => MaybePromise<boolean>
|
|
174
187
|
argsSchema: TSchema
|
|
175
188
|
description: string
|
|
176
189
|
handler: (args: PromptHandlerArgs<TSchema>) => MaybePromise<{
|
|
@@ -188,6 +201,7 @@ export type ResourceHandlerArgs = {
|
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
export type Resource = {
|
|
204
|
+
access?: (args: MCPAccessArgs) => MaybePromise<boolean>
|
|
191
205
|
description: string
|
|
192
206
|
handler: (args: ResourceHandlerArgs) => MaybePromise<{
|
|
193
207
|
contents: Array<{ text: string; uri: string }>
|
|
@@ -221,34 +235,34 @@ export type MCPPluginConfig = {
|
|
|
221
235
|
collections?: {
|
|
222
236
|
[Slug in CollectionSlug]?: MCPPluginCollectionConfig<Slug>
|
|
223
237
|
}
|
|
224
|
-
/** Skip MCP registration.
|
|
238
|
+
/** Skip MCP endpoint registration. */
|
|
225
239
|
disabled?: boolean
|
|
226
240
|
globals?: {
|
|
227
241
|
[Slug in GlobalSlug]?: MCPPluginGlobalConfig
|
|
228
242
|
}
|
|
243
|
+
hooks?: {
|
|
244
|
+
/** Transform a tool response after its handler returns */
|
|
245
|
+
afterToolCall?: MCPAfterToolCallHook[]
|
|
246
|
+
}
|
|
229
247
|
mcp?: {
|
|
230
248
|
serverOptions?: MCPServerOptions
|
|
231
249
|
verboseLogs?: boolean
|
|
232
250
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
req: PayloadRequest
|
|
240
|
-
}) => MaybePromise<AuthorizedMCP>
|
|
251
|
+
/** Replace the default MCP authorization resolver. */
|
|
252
|
+
overrideGetAuthorizedMCP?: (
|
|
253
|
+
args: {
|
|
254
|
+
pluginConfig: SanitizedMCPPluginConfig
|
|
255
|
+
} & GetAuthorizedMCPArgs,
|
|
256
|
+
) => MaybePromise<AuthorizedMCP>
|
|
241
257
|
prompts?: Record<string, Prompt>
|
|
242
258
|
resources?: Record<string, Resource>
|
|
243
259
|
/** Cross-cutting tools (not scoped to any collection or global). */
|
|
244
260
|
tools?: MCPTopLevelToolsMap
|
|
245
|
-
userCollection?: CollectionSlug
|
|
246
261
|
}
|
|
247
262
|
|
|
248
263
|
export type SanitizedMCPPluginConfig = {
|
|
249
264
|
items: MCPItem[]
|
|
250
|
-
|
|
251
|
-
} & Pick<MCPPluginConfig, 'disabled' | 'mcp' | 'overrideApiKeyCollection' | 'overrideAuth'>
|
|
265
|
+
} & Pick<MCPPluginConfig, 'disabled' | 'hooks' | 'mcp' | 'overrideGetAuthorizedMCP'>
|
|
252
266
|
|
|
253
267
|
export type MCPServerOptions = {
|
|
254
268
|
options?: ConstructorParameters<typeof McpServer>[1]
|
|
@@ -256,71 +270,32 @@ export type MCPServerOptions = {
|
|
|
256
270
|
}
|
|
257
271
|
|
|
258
272
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* defaults (built-in CRUD is on, opt-in tools are off).
|
|
262
|
-
*/
|
|
263
|
-
export type MCPAPIKeysDocAccessTree = {
|
|
264
|
-
collections?: {
|
|
265
|
-
[CollectionSlug: CollectionSlug]: {
|
|
266
|
-
[ToolKey: string]: boolean
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
globals?: {
|
|
270
|
-
[GlobalSlug: GlobalSlug]: {
|
|
271
|
-
[ToolKey: string]: boolean
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
prompts?: {
|
|
275
|
-
[PromptKey: string]: boolean
|
|
276
|
-
}
|
|
277
|
-
resources?: {
|
|
278
|
-
[ResourceKey: string]: boolean
|
|
279
|
-
}
|
|
280
|
-
tools?: {
|
|
281
|
-
[ToolKey: string]: boolean
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Stored on `payload-mcp-api-keys` docs
|
|
287
|
-
*/
|
|
288
|
-
export type MCPAPIKeysDoc = {
|
|
289
|
-
access: MCPAPIKeysDocAccessTree
|
|
290
|
-
id: DefaultDocumentIDType
|
|
291
|
-
overrideAccess?: boolean
|
|
292
|
-
user: null | TypedUser
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* One MCP primitive — tool, prompt, or resource — paired with the metadata both
|
|
297
|
-
* the endpoint and the API key collection need. Built by `sanitizeMCPConfig`,
|
|
298
|
-
* filtered by `getAuthorizedMCP`, registered by the MCP endpoint.
|
|
273
|
+
* One MCP primitive plus the metadata needed for config-driven filtering and
|
|
274
|
+
* registration.
|
|
299
275
|
*
|
|
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).
|
|
276
|
+
* - `configKey`: the config identifier, e.g. `find` or `echo`.
|
|
277
|
+
* - `mcpName`: the MCP wire name, e.g. `findDocuments` or `echo`.
|
|
278
|
+
* - `label`: human-readable display text.
|
|
307
279
|
*/
|
|
308
280
|
export type MCPItemBase = {
|
|
309
|
-
|
|
281
|
+
configKey: string
|
|
310
282
|
label: string
|
|
283
|
+
mcpName: string
|
|
311
284
|
}
|
|
312
285
|
|
|
286
|
+
export type CollectionMCPItem = {
|
|
287
|
+
collectionSlug: CollectionSlug
|
|
288
|
+
tool: CollectionTool
|
|
289
|
+
type: 'collectionTool'
|
|
290
|
+
} & MCPItemBase
|
|
291
|
+
|
|
292
|
+
export type GlobalMCPItem = {
|
|
293
|
+
globalSlug: GlobalSlug
|
|
294
|
+
tool: GlobalTool
|
|
295
|
+
type: 'globalTool'
|
|
296
|
+
} & MCPItemBase
|
|
297
|
+
|
|
313
298
|
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
299
|
| ({
|
|
325
300
|
prompt: Prompt
|
|
326
301
|
type: 'prompt'
|
|
@@ -333,15 +308,17 @@ export type MCPItem =
|
|
|
333
308
|
tool: Tool
|
|
334
309
|
type: 'tool'
|
|
335
310
|
} & MCPItemBase)
|
|
311
|
+
| CollectionMCPItem
|
|
312
|
+
| GlobalMCPItem
|
|
336
313
|
|
|
337
314
|
/**
|
|
338
|
-
* The caller's identity + the MCP items
|
|
339
|
-
*
|
|
340
|
-
* receive this via `args.authorizedMCP` so
|
|
341
|
-
* `localAPIDefaults(authorizedMCP)` into every local API call.
|
|
315
|
+
* The caller's identity + the MCP items authorized for this request. Disabled
|
|
316
|
+
* items and items blocked by access callbacks or Payload operation access are
|
|
317
|
+
* absent from `items`. Tool handlers receive this via `args.authorizedMCP` so
|
|
318
|
+
* they can spread `localAPIDefaults(authorizedMCP)` into every local API call.
|
|
342
319
|
*/
|
|
343
320
|
export type AuthorizedMCP = {
|
|
344
321
|
items: MCPItem[]
|
|
345
322
|
overrideAccess: boolean
|
|
346
|
-
user: null |
|
|
323
|
+
user: null | User
|
|
347
324
|
}
|
|
@@ -5,7 +5,7 @@ import type { SanitizedMCPPluginConfig } from '../types.js'
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns the sanitized plugin config — the form produced by
|
|
7
7
|
* `sanitizeMCPConfig` and stashed back on the plugin's `options` during init.
|
|
8
|
-
* Callers downstream of plugin init can rely on `items
|
|
8
|
+
* Callers downstream of plugin init can rely on `items` and other defaults
|
|
9
9
|
* being fully resolved.
|
|
10
10
|
*/
|
|
11
11
|
export const getPluginConfig: (args: { config: SanitizedConfig }) => SanitizedMCPPluginConfig = ({
|
|
@@ -0,0 +1,62 @@
|
|
|
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 { sanitizeEntitySchema } from './sanitizeEntitySchema.js'
|
|
13
|
+
|
|
14
|
+
export const getCollectionInputSchema = ({
|
|
15
|
+
collectionSlug,
|
|
16
|
+
req,
|
|
17
|
+
}: {
|
|
18
|
+
collectionSlug: CollectionSlug
|
|
19
|
+
req: PayloadRequest
|
|
20
|
+
}): JsonSchemaType | null => {
|
|
21
|
+
const collection = req.payload.collections[collectionSlug]?.config
|
|
22
|
+
|
|
23
|
+
if (!collection) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return buildEntityInputSchema({ entity: collection, req })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const getGlobalInputSchema = ({
|
|
31
|
+
globalSlug,
|
|
32
|
+
req,
|
|
33
|
+
}: {
|
|
34
|
+
globalSlug: GlobalSlug
|
|
35
|
+
req: PayloadRequest
|
|
36
|
+
}): JsonSchemaType | null => {
|
|
37
|
+
const global = req.payload.config.globals.find((globalConfig) => globalConfig.slug === globalSlug)
|
|
38
|
+
|
|
39
|
+
if (!global) {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return buildEntityInputSchema({ entity: global, req })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const buildEntityInputSchema = ({
|
|
47
|
+
entity,
|
|
48
|
+
req,
|
|
49
|
+
}: {
|
|
50
|
+
entity: SanitizedCollectionConfig | SanitizedGlobalConfig
|
|
51
|
+
req: PayloadRequest
|
|
52
|
+
}): JsonSchemaType =>
|
|
53
|
+
// The `input` variant is already the write shape; sanitizeEntitySchema only adds MCP-specific passes.
|
|
54
|
+
sanitizeEntitySchema(
|
|
55
|
+
entityToStandaloneJSONSchema({
|
|
56
|
+
config: req.payload.config,
|
|
57
|
+
defaultIDType: req.payload.db.defaultIDType,
|
|
58
|
+
entity,
|
|
59
|
+
i18n: req.i18n,
|
|
60
|
+
variant: 'input',
|
|
61
|
+
}) as unknown as JsonSchemaType,
|
|
62
|
+
)
|
|
@@ -5,10 +5,11 @@ import type { JsonSchemaType } from '../../types.js'
|
|
|
5
5
|
import { sanitizeEntitySchema } from './sanitizeEntitySchema.js'
|
|
6
6
|
|
|
7
7
|
describe('sanitizeEntitySchema', () => {
|
|
8
|
-
it('
|
|
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)
|
|
11
|
-
//
|
|
8
|
+
it('renames the Lexical node-union definition to a short name and keeps it a strict oneOf', () => {
|
|
9
|
+
// Shaped like the schema the MCP server prepares for a collection with a Lexical richText field
|
|
10
|
+
// (Payload's `input` variant): a `$defs` node union (a `oneOf` of node shapes). The input variant
|
|
11
|
+
// already types the relationship node's `value` as a bare ID - there is no populated-doc `$ref` to
|
|
12
|
+
// reduce here (the variant did that upstream), so sanitize leaves the value untouched.
|
|
12
13
|
const standalone: JsonSchemaType = {
|
|
13
14
|
type: 'object',
|
|
14
15
|
$defs: {
|
|
@@ -25,17 +26,12 @@ describe('sanitizeEntitySchema', () => {
|
|
|
25
26
|
additionalProperties: false,
|
|
26
27
|
properties: {
|
|
27
28
|
type: { const: 'relationship' },
|
|
28
|
-
value: {
|
|
29
|
+
value: { type: 'string', description: 'The related document ID.' },
|
|
29
30
|
},
|
|
30
31
|
required: ['type'],
|
|
31
32
|
},
|
|
32
33
|
],
|
|
33
34
|
},
|
|
34
|
-
posts: {
|
|
35
|
-
type: 'object',
|
|
36
|
-
additionalProperties: false,
|
|
37
|
-
properties: { id: { type: 'string' }, title: { type: 'string' } },
|
|
38
|
-
},
|
|
39
35
|
},
|
|
40
36
|
properties: {
|
|
41
37
|
id: { type: 'string' },
|
|
@@ -57,7 +53,7 @@ describe('sanitizeEntitySchema', () => {
|
|
|
57
53
|
type: 'object',
|
|
58
54
|
$defs: {
|
|
59
55
|
// The node union is renamed to a short, readable `node`, and stays a strict discriminated
|
|
60
|
-
// `oneOf` - not loosened to `anyOf
|
|
56
|
+
// `oneOf` - not loosened to `anyOf`.
|
|
61
57
|
node: {
|
|
62
58
|
oneOf: [
|
|
63
59
|
{
|
|
@@ -71,21 +67,18 @@ describe('sanitizeEntitySchema', () => {
|
|
|
71
67
|
additionalProperties: false,
|
|
72
68
|
properties: {
|
|
73
69
|
type: { const: 'relationship' },
|
|
74
|
-
//
|
|
75
|
-
value: { type: 'string', description: 'The
|
|
70
|
+
// The value is already ID-only from the input variant - left as-is.
|
|
71
|
+
value: { type: 'string', description: 'The related document ID.' },
|
|
76
72
|
},
|
|
77
73
|
required: ['type'],
|
|
78
74
|
},
|
|
79
75
|
],
|
|
80
76
|
},
|
|
81
|
-
posts: {
|
|
82
|
-
type: 'object',
|
|
83
|
-
additionalProperties: false,
|
|
84
|
-
properties: { id: { type: 'string' }, title: { type: 'string' } },
|
|
85
|
-
},
|
|
86
77
|
},
|
|
87
78
|
properties: {
|
|
88
|
-
//
|
|
79
|
+
// Managed fields (createdAt/updatedAt/_status) are excluded upstream by the `input` variant;
|
|
80
|
+
// `id` stays because it's a valid optional input (a client may supply a custom ID).
|
|
81
|
+
id: { type: 'string' },
|
|
89
82
|
content: {
|
|
90
83
|
type: 'object',
|
|
91
84
|
properties: {
|