@payloadcms/plugin-mcp 4.0.0-canary.1 → 4.0.0-canary.11
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 +34 -10
- 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.map +1 -1
- package/dist/mcp/builtin/collections/createTool.js +26 -24
- package/dist/mcp/builtin/collections/createTool.js.map +1 -1
- package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/deleteTool.js +10 -2
- 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.map +1 -1
- package/dist/mcp/builtin/collections/findTool.js +46 -26
- 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 +1 -1
- package/dist/mcp/builtin/collections/formatCollectionError.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/formatCollectionError.js.map +1 -1
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +55 -4
- package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -1
- 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.map +1 -1
- package/dist/mcp/builtin/collections/updateTool.js +37 -32
- package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
- package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -1
- package/dist/mcp/builtin/getConfigInfoTool.js +27 -8
- package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -1
- 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 +19 -22
- 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.map +1 -1
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +32 -2
- package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -1
- 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.js +14 -21
- package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
- package/dist/mcp/builtin/validateEntityData.d.ts.map +1 -1
- package/dist/mcp/builtin/validateEntityData.js +1 -1
- package/dist/mcp/builtin/validateEntityData.js.map +1 -1
- package/dist/mcp/builtinTools.d.ts +69 -12
- package/dist/mcp/builtinTools.d.ts.map +1 -1
- package/dist/mcp/builtinTools.js +66 -10
- 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 +69 -27
- 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 +100 -87
- 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 +5 -3
- package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +1 -1
- package/dist/utils/schemaConversion/getEntityInputSchema.js +44 -12
- package/dist/utils/schemaConversion/getEntityInputSchema.js.map +1 -1
- 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/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 +48 -21
- 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 +27 -18
- package/src/mcp/builtin/collections/deleteTool.ts +11 -2
- 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 +59 -20
- 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 +1 -1
- package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +51 -3
- package/src/mcp/builtin/collections/restoreVersionTool.ts +107 -0
- package/src/mcp/builtin/collections/updateTool.ts +39 -23
- package/src/mcp/builtin/getConfigInfoTool.ts +27 -4
- package/src/mcp/builtin/globals/countVersionsTool.ts +68 -0
- package/src/mcp/builtin/globals/findTool.ts +27 -18
- 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 +37 -2
- package/src/mcp/builtin/globals/restoreVersionTool.ts +99 -0
- package/src/mcp/builtin/globals/updateTool.ts +16 -16
- package/src/mcp/builtin/validateEntityData.ts +1 -1
- package/src/mcp/builtinTools.ts +72 -16
- package/src/mcp/sanitizeMCPConfig.ts +48 -30
- package/src/stdio.ts +77 -45
- package/src/types.ts +108 -90
- package/src/utils/getPluginConfig.ts +1 -1
- package/src/utils/schemaConversion/filterFieldsByAccess.ts +123 -0
- package/src/utils/schemaConversion/getEntityInputSchema.ts +45 -27
- package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +12 -19
- package/src/utils/schemaConversion/sanitizeEntitySchema.ts +17 -114
- 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/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/collection/getAccessField.ts +0 -64
- package/src/collection/index.ts +0 -64
- package/src/components/AccessField/index.client.tsx +0 -347
- package/src/components/AccessField/index.css +0 -93
- package/src/utils/localAPIDefaults.ts +0 -22
- package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
package/src/types.ts
CHANGED
|
@@ -6,18 +6,18 @@ 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,
|
|
19
18
|
} from 'payload'
|
|
20
19
|
|
|
20
|
+
import type { GetAuthorizedMCPArgs } from './endpoint/access.js'
|
|
21
21
|
import type {
|
|
22
22
|
MCPCollectionAuthToolName,
|
|
23
23
|
MCPCollectionBuiltinName,
|
|
@@ -26,34 +26,16 @@ import type {
|
|
|
26
26
|
|
|
27
27
|
export type { MCPCollectionAuthToolName, MCPCollectionBuiltinName, MCPGlobalBuiltinName }
|
|
28
28
|
|
|
29
|
-
/** Re-exported from `@modelcontextprotocol/server` —
|
|
30
|
-
export type { JsonSchemaType, StandardSchemaWithJSON }
|
|
29
|
+
/** Re-exported from `@modelcontextprotocol/server` — common MCP types used in plugin config. */
|
|
30
|
+
export type { JsonSchemaType, StandardSchemaWithJSON, ToolAnnotations }
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* 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, …).
|
|
34
|
+
* JSON Schema 2020-12 literal, or a Standard Schema instance (Zod, Valibot, …).
|
|
35
|
+
* Raw schemas may omit `$schema`; when present it must declare the 2020-12 dialect.
|
|
35
36
|
*/
|
|
36
37
|
export type ToolInputSchema = JsonSchemaType | StandardSchemaWithJSON
|
|
37
38
|
|
|
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
|
-
configKey: string
|
|
50
|
-
description: string
|
|
51
|
-
globalSlug?: string
|
|
52
|
-
label: string
|
|
53
|
-
type: 'collectionTool' | 'globalTool' | 'prompt' | 'resource' | 'tool'
|
|
54
|
-
}>
|
|
55
|
-
}
|
|
56
|
-
|
|
57
39
|
export type MCPToolResponse = {
|
|
58
40
|
content: ContentBlock[]
|
|
59
41
|
/**
|
|
@@ -70,6 +52,13 @@ export type MCPResponseOverride = (
|
|
|
70
52
|
req: PayloadRequest,
|
|
71
53
|
) => MCPToolResponse
|
|
72
54
|
|
|
55
|
+
export type MCPAfterToolCallHook = (args: {
|
|
56
|
+
input: unknown
|
|
57
|
+
req: PayloadRequest
|
|
58
|
+
response: MCPToolResponse
|
|
59
|
+
toolName: string
|
|
60
|
+
}) => MaybePromise<MCPToolResponse>
|
|
61
|
+
|
|
73
62
|
/**
|
|
74
63
|
* The handler's `input` type. A specific Standard Schema (Zod, Valibot, …) gets
|
|
75
64
|
* its inferred output; anything else falls back to `Record<string, unknown>`.
|
|
@@ -80,6 +69,19 @@ export type TypedInput<TSchema> = TSchema extends StandardSchemaWithJSON
|
|
|
80
69
|
: StandardSchemaWithJSON.InferOutput<TSchema>
|
|
81
70
|
: Record<string, unknown>
|
|
82
71
|
|
|
72
|
+
export type MCPAccessArgs = {
|
|
73
|
+
permissions?: SanitizedPermissions
|
|
74
|
+
req: PayloadRequest
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type CollectionMCPAccessArgs = {
|
|
78
|
+
collectionSlug: CollectionSlug
|
|
79
|
+
} & MCPAccessArgs
|
|
80
|
+
|
|
81
|
+
export type GlobalMCPAccessArgs = {
|
|
82
|
+
globalSlug: GlobalSlug
|
|
83
|
+
} & MCPAccessArgs
|
|
84
|
+
|
|
83
85
|
export type ToolHandlerArgs<TSchema = undefined> = {
|
|
84
86
|
authorizedMCP: AuthorizedMCP
|
|
85
87
|
input: TypedInput<TSchema>
|
|
@@ -96,6 +98,13 @@ export type GlobalToolHandlerArgs<TSchema = undefined> = {
|
|
|
96
98
|
} & ToolHandlerArgs<TSchema>
|
|
97
99
|
|
|
98
100
|
export type Tool<TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined> = {
|
|
101
|
+
/**
|
|
102
|
+
* Runs while authorizing each MCP request, before the tool is advertised or called. Return
|
|
103
|
+
* `false` to make the tool unavailable for that request. This is skipped when `overrideAccess`
|
|
104
|
+
* is enabled.
|
|
105
|
+
*/
|
|
106
|
+
access?: (args: MCPAccessArgs) => MaybePromise<boolean>
|
|
107
|
+
annotations?: ToolAnnotations
|
|
99
108
|
description: string
|
|
100
109
|
handler: (args: ToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
101
110
|
input?: TSchema
|
|
@@ -108,22 +117,54 @@ export type Tool<TSchema extends ToolInputSchema | undefined = ToolInputSchema |
|
|
|
108
117
|
export type CollectionTool<
|
|
109
118
|
TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined,
|
|
110
119
|
> = {
|
|
120
|
+
/**
|
|
121
|
+
* Runs while authorizing each MCP request for this collection. Return `false` to reject calls
|
|
122
|
+
* to this tool for the collection. The shared tool is not advertised when no collections allow
|
|
123
|
+
* it, but can remain advertised when it is available for another collection. This is skipped
|
|
124
|
+
* when `overrideAccess` is enabled.
|
|
125
|
+
*/
|
|
126
|
+
access?: (args: CollectionMCPAccessArgs) => MaybePromise<boolean>
|
|
111
127
|
handler: (args: CollectionToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
112
128
|
input?: TSchema
|
|
113
|
-
} & Pick<Tool, 'description' | 'overrideResponse'>
|
|
129
|
+
} & Pick<Tool, 'annotations' | 'description' | 'overrideResponse'>
|
|
114
130
|
|
|
115
131
|
export type GlobalTool<TSchema extends ToolInputSchema | undefined = ToolInputSchema | undefined> =
|
|
116
132
|
{
|
|
133
|
+
/**
|
|
134
|
+
* Runs while authorizing each MCP request for this global. Return `false` to reject calls to
|
|
135
|
+
* this tool for the global. The shared tool is not advertised when no globals allow it, but
|
|
136
|
+
* can remain advertised when it is available for another global. This is skipped when
|
|
137
|
+
* `overrideAccess` is enabled.
|
|
138
|
+
*/
|
|
139
|
+
access?: (args: GlobalMCPAccessArgs) => MaybePromise<boolean>
|
|
117
140
|
handler: (args: GlobalToolHandlerArgs<TSchema>) => MaybePromise<MCPToolResponse>
|
|
118
141
|
input?: TSchema
|
|
119
|
-
} & Pick<Tool, 'description' | 'overrideResponse'>
|
|
142
|
+
} & Pick<Tool, 'annotations' | 'description' | 'overrideResponse'>
|
|
120
143
|
|
|
121
144
|
/**
|
|
122
145
|
* Configures (or disables) a built-in tool without replacing it.
|
|
123
146
|
* `handler?: never` prevents a full `CollectionTool`/`GlobalTool` (which has a
|
|
124
147
|
* required handler) from being silently accepted at a built-in key slot.
|
|
125
148
|
*/
|
|
126
|
-
export type
|
|
149
|
+
export type MCPBuiltInCollectionToolOverride = {
|
|
150
|
+
/**
|
|
151
|
+
* Replaces the built-in tool's access check. Return `false` to make the tool unavailable for
|
|
152
|
+
* this collection. This is skipped when `overrideAccess` is enabled.
|
|
153
|
+
*/
|
|
154
|
+
access?: (args: CollectionMCPAccessArgs) => MaybePromise<boolean>
|
|
155
|
+
annotations?: ToolAnnotations
|
|
156
|
+
description?: string
|
|
157
|
+
handler?: never
|
|
158
|
+
overrideResponse?: MCPResponseOverride
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type MCPBuiltInGlobalToolOverride = {
|
|
162
|
+
/**
|
|
163
|
+
* Replaces the built-in tool's access check. Return `false` to make the tool unavailable for
|
|
164
|
+
* this global. This is skipped when `overrideAccess` is enabled.
|
|
165
|
+
*/
|
|
166
|
+
access?: (args: GlobalMCPAccessArgs) => MaybePromise<boolean>
|
|
167
|
+
annotations?: ToolAnnotations
|
|
127
168
|
description?: string
|
|
128
169
|
handler?: never
|
|
129
170
|
overrideResponse?: MCPResponseOverride
|
|
@@ -137,13 +178,13 @@ export type MCPBuiltInToolOverride = {
|
|
|
137
178
|
export type MCPTopLevelToolEntry = Tool
|
|
138
179
|
|
|
139
180
|
export type MCPCollectionToolsMap = {
|
|
140
|
-
[customToolName: string]: boolean | CollectionTool |
|
|
181
|
+
[customToolName: string]: boolean | CollectionTool | MCPBuiltInCollectionToolOverride | undefined
|
|
141
182
|
} & {
|
|
142
|
-
[K in MCPCollectionBuiltinName]?: false |
|
|
183
|
+
[K in MCPCollectionBuiltinName]?: false | MCPBuiltInCollectionToolOverride
|
|
143
184
|
}
|
|
144
185
|
|
|
145
186
|
export type MCPAuthCollectionToolsMap = {
|
|
146
|
-
[K in MCPCollectionAuthToolName]?:
|
|
187
|
+
[K in MCPCollectionAuthToolName]?: MCPBuiltInCollectionToolOverride | true
|
|
147
188
|
} & MCPCollectionToolsMap
|
|
148
189
|
|
|
149
190
|
/** Auth-enabled collections get auth-tool name autocomplete; others get CRUD-only. */
|
|
@@ -152,9 +193,9 @@ export type MCPToolsMapForCollection<Slug extends CollectionSlug> = Slug extends
|
|
|
152
193
|
: MCPCollectionToolsMap
|
|
153
194
|
|
|
154
195
|
export type MCPGlobalToolsMap = {
|
|
155
|
-
[customToolName: string]: boolean | GlobalTool |
|
|
196
|
+
[customToolName: string]: boolean | GlobalTool | MCPBuiltInGlobalToolOverride | undefined
|
|
156
197
|
} & {
|
|
157
|
-
[K in MCPGlobalBuiltinName]?: false |
|
|
198
|
+
[K in MCPGlobalBuiltinName]?: false | MCPBuiltInGlobalToolOverride
|
|
158
199
|
}
|
|
159
200
|
|
|
160
201
|
export type MCPTopLevelToolsMap = Record<string, Tool>
|
|
@@ -166,6 +207,12 @@ export type PromptHandlerArgs<TSchema = undefined> = {
|
|
|
166
207
|
}
|
|
167
208
|
|
|
168
209
|
export type Prompt<TSchema extends ToolInputSchema = ToolInputSchema> = {
|
|
210
|
+
/**
|
|
211
|
+
* Runs while authorizing each MCP request, before the prompt is advertised or used. Return
|
|
212
|
+
* `false` to make the prompt unavailable for that request. This is skipped when
|
|
213
|
+
* `overrideAccess` is enabled.
|
|
214
|
+
*/
|
|
215
|
+
access?: (args: MCPAccessArgs) => MaybePromise<boolean>
|
|
169
216
|
argsSchema: TSchema
|
|
170
217
|
description: string
|
|
171
218
|
handler: (args: PromptHandlerArgs<TSchema>) => MaybePromise<{
|
|
@@ -183,6 +230,12 @@ export type ResourceHandlerArgs = {
|
|
|
183
230
|
}
|
|
184
231
|
|
|
185
232
|
export type Resource = {
|
|
233
|
+
/**
|
|
234
|
+
* Runs while authorizing each MCP request, before the resource is advertised or read. Return
|
|
235
|
+
* `false` to make the resource unavailable for that request. This is skipped when
|
|
236
|
+
* `overrideAccess` is enabled.
|
|
237
|
+
*/
|
|
238
|
+
access?: (args: MCPAccessArgs) => MaybePromise<boolean>
|
|
186
239
|
description: string
|
|
187
240
|
handler: (args: ResourceHandlerArgs) => MaybePromise<{
|
|
188
241
|
contents: Array<{ text: string; uri: string }>
|
|
@@ -216,34 +269,39 @@ export type MCPPluginConfig = {
|
|
|
216
269
|
collections?: {
|
|
217
270
|
[Slug in CollectionSlug]?: MCPPluginCollectionConfig<Slug>
|
|
218
271
|
}
|
|
219
|
-
/** Skip MCP registration.
|
|
272
|
+
/** Skip MCP endpoint registration. */
|
|
220
273
|
disabled?: boolean
|
|
221
274
|
globals?: {
|
|
222
275
|
[Slug in GlobalSlug]?: MCPPluginGlobalConfig
|
|
223
276
|
}
|
|
277
|
+
hooks?: {
|
|
278
|
+
/** Transform a tool response after its handler returns */
|
|
279
|
+
afterToolCall?: MCPAfterToolCallHook[]
|
|
280
|
+
}
|
|
224
281
|
mcp?: {
|
|
225
282
|
serverOptions?: MCPServerOptions
|
|
226
283
|
verboseLogs?: boolean
|
|
227
284
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Replace the default MCP authorization resolver.
|
|
287
|
+
*
|
|
288
|
+
* This hook replaces the default authentication flow. It must set `req.user` to the
|
|
289
|
+
* authenticated Payload user, or to `null` for an anonymous caller, before returning.
|
|
290
|
+
*/
|
|
291
|
+
overrideGetAuthorizedMCP?: (
|
|
292
|
+
args: {
|
|
293
|
+
pluginConfig: SanitizedMCPPluginConfig
|
|
294
|
+
} & GetAuthorizedMCPArgs,
|
|
295
|
+
) => MaybePromise<AuthorizedMCP>
|
|
236
296
|
prompts?: Record<string, Prompt>
|
|
237
297
|
resources?: Record<string, Resource>
|
|
238
298
|
/** Cross-cutting tools (not scoped to any collection or global). */
|
|
239
299
|
tools?: MCPTopLevelToolsMap
|
|
240
|
-
userCollection?: CollectionSlug
|
|
241
300
|
}
|
|
242
301
|
|
|
243
302
|
export type SanitizedMCPPluginConfig = {
|
|
244
303
|
items: MCPItem[]
|
|
245
|
-
|
|
246
|
-
} & Pick<MCPPluginConfig, 'disabled' | 'mcp' | 'overrideApiKeyCollection' | 'overrideAuth'>
|
|
304
|
+
} & Pick<MCPPluginConfig, 'disabled' | 'hooks' | 'mcp' | 'overrideGetAuthorizedMCP'>
|
|
247
305
|
|
|
248
306
|
export type MCPServerOptions = {
|
|
249
307
|
options?: ConstructorParameters<typeof McpServer>[1]
|
|
@@ -251,49 +309,12 @@ export type MCPServerOptions = {
|
|
|
251
309
|
}
|
|
252
310
|
|
|
253
311
|
/**
|
|
254
|
-
*
|
|
255
|
-
* A `false` leaf disables that tool; missing keys defer to
|
|
256
|
-
* defaults (built-in CRUD is on, opt-in tools are off).
|
|
257
|
-
*/
|
|
258
|
-
export type MCPAPIKeysDocAccessTree = {
|
|
259
|
-
collections?: {
|
|
260
|
-
[CollectionSlug: CollectionSlug]: {
|
|
261
|
-
[ToolKey: string]: boolean
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
globals?: {
|
|
265
|
-
[GlobalSlug: GlobalSlug]: {
|
|
266
|
-
[ToolKey: string]: boolean
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
prompts?: {
|
|
270
|
-
[PromptKey: string]: boolean
|
|
271
|
-
}
|
|
272
|
-
resources?: {
|
|
273
|
-
[ResourceKey: string]: boolean
|
|
274
|
-
}
|
|
275
|
-
tools?: {
|
|
276
|
-
[ToolKey: string]: boolean
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Stored on `payload-mcp-api-keys` docs
|
|
282
|
-
*/
|
|
283
|
-
export type MCPAPIKeysDoc = {
|
|
284
|
-
access: MCPAPIKeysDocAccessTree
|
|
285
|
-
id: DefaultDocumentIDType
|
|
286
|
-
overrideAccess?: boolean
|
|
287
|
-
user: null | TypedUser
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* One MCP primitive plus the metadata needed for access checks, admin UI, and
|
|
312
|
+
* One MCP primitive plus the metadata needed for config-driven filtering and
|
|
292
313
|
* registration.
|
|
293
314
|
*
|
|
294
|
-
* - `configKey`: the config
|
|
315
|
+
* - `configKey`: the config identifier, e.g. `find` or `echo`.
|
|
295
316
|
* - `mcpName`: the MCP wire name, e.g. `findDocuments` or `echo`.
|
|
296
|
-
* - `label`: human-readable
|
|
317
|
+
* - `label`: human-readable display text.
|
|
297
318
|
*/
|
|
298
319
|
export type MCPItemBase = {
|
|
299
320
|
configKey: string
|
|
@@ -330,13 +351,10 @@ export type MCPItem =
|
|
|
330
351
|
| GlobalMCPItem
|
|
331
352
|
|
|
332
353
|
/**
|
|
333
|
-
* The
|
|
334
|
-
*
|
|
335
|
-
* receive this via `args.authorizedMCP` so they can spread
|
|
336
|
-
* `localAPIDefaults(authorizedMCP)` into every local API call.
|
|
354
|
+
* The MCP items and access mode authorized for this request. The authenticated user is available
|
|
355
|
+
* as `req.user`.
|
|
337
356
|
*/
|
|
338
357
|
export type AuthorizedMCP = {
|
|
339
358
|
items: MCPItem[]
|
|
340
359
|
overrideAccess: boolean
|
|
341
|
-
user: null | TypedUser
|
|
342
360
|
}
|
|
@@ -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,123 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FlattenedBlock,
|
|
3
|
+
FlattenedField,
|
|
4
|
+
Operation,
|
|
5
|
+
SanitizedDocumentPermissions,
|
|
6
|
+
SanitizedFieldsPermissions,
|
|
7
|
+
} from 'payload'
|
|
8
|
+
|
|
9
|
+
type FieldOperation = Exclude<Operation, 'delete'>
|
|
10
|
+
type AllowedFieldOperations = Record<FieldOperation, boolean>
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Filters fields using the access results in `permissions`. `shouldExcludeField` receives the
|
|
14
|
+
* operations allowed by the entity, field, and its parents and decides whether to remove the
|
|
15
|
+
* field.
|
|
16
|
+
*
|
|
17
|
+
* Arrays, groups, named tabs, and blocks are filtered recursively. Rows and unnamed tabs are
|
|
18
|
+
* flattened by Payload, so their children are filtered at the containing level.
|
|
19
|
+
*
|
|
20
|
+
* @example Include fields writable through any collection operation the user can perform.
|
|
21
|
+
* ```ts
|
|
22
|
+
* filterFieldsByAccess({
|
|
23
|
+
* fields,
|
|
24
|
+
* permissions: collectionPermissions,
|
|
25
|
+
* shouldExcludeField: ({ create, update }) => !create && !update,
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const filterFieldsByAccess = ({
|
|
30
|
+
blocks,
|
|
31
|
+
fields,
|
|
32
|
+
permissions,
|
|
33
|
+
shouldExcludeField,
|
|
34
|
+
}: {
|
|
35
|
+
blocks?: FlattenedBlock[]
|
|
36
|
+
fields: FlattenedField[]
|
|
37
|
+
permissions: SanitizedDocumentPermissions
|
|
38
|
+
shouldExcludeField: (allowedOperations: AllowedFieldOperations) => boolean
|
|
39
|
+
}): FlattenedField[] => {
|
|
40
|
+
const filterFields = (
|
|
41
|
+
nestedFields: FlattenedField[],
|
|
42
|
+
nestedPermissions: SanitizedFieldsPermissions | undefined,
|
|
43
|
+
parentAllowedOperations: AllowedFieldOperations,
|
|
44
|
+
): FlattenedField[] => {
|
|
45
|
+
const accessibleFields: FlattenedField[] = []
|
|
46
|
+
|
|
47
|
+
for (const field of nestedFields) {
|
|
48
|
+
const fieldPermissions = nestedPermissions === true ? true : nestedPermissions?.[field.name]
|
|
49
|
+
const isOperationAllowed = (operation: FieldOperation): boolean =>
|
|
50
|
+
parentAllowedOperations[operation] &&
|
|
51
|
+
(fieldPermissions === true || fieldPermissions?.[operation] === true)
|
|
52
|
+
const allowedOperations: AllowedFieldOperations = {
|
|
53
|
+
create: isOperationAllowed('create'),
|
|
54
|
+
read: isOperationAllowed('read'),
|
|
55
|
+
update: isOperationAllowed('update'),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (shouldExcludeField(allowedOperations)) {
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (field.type === 'blocks') {
|
|
63
|
+
const accessibleBlocks: (FlattenedBlock | string)[] = []
|
|
64
|
+
|
|
65
|
+
for (const blockOrReference of field.blocks) {
|
|
66
|
+
const block =
|
|
67
|
+
typeof blockOrReference === 'string'
|
|
68
|
+
? blocks?.find(({ slug }) => slug === blockOrReference)
|
|
69
|
+
: blockOrReference
|
|
70
|
+
|
|
71
|
+
if (!block) {
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const blockPermissions =
|
|
76
|
+
fieldPermissions === true
|
|
77
|
+
? true
|
|
78
|
+
: fieldPermissions?.blocks === true
|
|
79
|
+
? true
|
|
80
|
+
: fieldPermissions?.blocks?.[block.slug]
|
|
81
|
+
|
|
82
|
+
accessibleBlocks.push({
|
|
83
|
+
...block,
|
|
84
|
+
flattenedFields: filterFields(
|
|
85
|
+
block.flattenedFields,
|
|
86
|
+
blockPermissions === true ? true : blockPermissions?.fields,
|
|
87
|
+
allowedOperations,
|
|
88
|
+
),
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (accessibleBlocks.length === 0) {
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
accessibleFields.push({ ...field, blocks: accessibleBlocks })
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (field.type === 'array' || field.type === 'group' || field.type === 'tab') {
|
|
101
|
+
accessibleFields.push({
|
|
102
|
+
...field,
|
|
103
|
+
flattenedFields: filterFields(
|
|
104
|
+
field.flattenedFields,
|
|
105
|
+
fieldPermissions === true ? true : fieldPermissions?.fields,
|
|
106
|
+
allowedOperations,
|
|
107
|
+
),
|
|
108
|
+
})
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
accessibleFields.push(field)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return accessibleFields
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return filterFields(fields, permissions.fields, {
|
|
119
|
+
create: 'create' in permissions && permissions.create === true,
|
|
120
|
+
read: permissions.read === true,
|
|
121
|
+
update: permissions.update === true,
|
|
122
|
+
})
|
|
123
|
+
}
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type CollectionSlug,
|
|
3
3
|
entityToStandaloneJSONSchema,
|
|
4
|
+
type FlattenedField,
|
|
4
5
|
type GlobalSlug,
|
|
5
6
|
type PayloadRequest,
|
|
6
7
|
type SanitizedCollectionConfig,
|
|
8
|
+
type SanitizedCollectionPermission,
|
|
7
9
|
type SanitizedGlobalConfig,
|
|
10
|
+
type SanitizedGlobalPermission,
|
|
8
11
|
} from 'payload'
|
|
9
12
|
|
|
10
13
|
import type { JsonSchemaType } from '../../types.js'
|
|
11
14
|
|
|
12
|
-
import {
|
|
13
|
-
getCollectionVirtualFieldNames,
|
|
14
|
-
getGlobalVirtualFieldNames,
|
|
15
|
-
} from '../getVirtualFieldNames.js'
|
|
16
|
-
import { removeVirtualFieldsFromSchema } from './removeVirtualFieldsFromSchema.js'
|
|
15
|
+
import { filterFieldsByAccess } from './filterFieldsByAccess.js'
|
|
17
16
|
import { sanitizeEntitySchema } from './sanitizeEntitySchema.js'
|
|
18
17
|
|
|
19
18
|
export const getCollectionInputSchema = ({
|
|
20
19
|
collectionSlug,
|
|
20
|
+
permissions,
|
|
21
21
|
req,
|
|
22
22
|
}: {
|
|
23
23
|
collectionSlug: CollectionSlug
|
|
24
|
+
permissions?: SanitizedCollectionPermission
|
|
24
25
|
req: PayloadRequest
|
|
25
26
|
}): JsonSchemaType | null => {
|
|
26
27
|
const collection = req.payload.collections[collectionSlug]?.config
|
|
@@ -29,18 +30,27 @@ export const getCollectionInputSchema = ({
|
|
|
29
30
|
return null
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
entity: collection,
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (!permissions) {
|
|
34
|
+
return buildEntityInputSchema({ entity: collection, req })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fieldsAllowedByAccess = filterFieldsByAccess({
|
|
38
|
+
blocks: req.payload.config.blocks,
|
|
39
|
+
fields: collection.flattenedFields,
|
|
40
|
+
permissions,
|
|
41
|
+
shouldExcludeField: ({ create, update }) => !create && !update,
|
|
36
42
|
})
|
|
43
|
+
|
|
44
|
+
return buildEntityInputSchema({ entity: collection, fields: fieldsAllowedByAccess, req })
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export const getGlobalInputSchema = ({
|
|
40
48
|
globalSlug,
|
|
49
|
+
permissions,
|
|
41
50
|
req,
|
|
42
51
|
}: {
|
|
43
52
|
globalSlug: GlobalSlug
|
|
53
|
+
permissions?: SanitizedGlobalPermission
|
|
44
54
|
req: PayloadRequest
|
|
45
55
|
}): JsonSchemaType | null => {
|
|
46
56
|
const global = req.payload.config.globals.find((globalConfig) => globalConfig.slug === globalSlug)
|
|
@@ -49,30 +59,38 @@ export const getGlobalInputSchema = ({
|
|
|
49
59
|
return null
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
entity: global,
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
if (!permissions) {
|
|
63
|
+
return buildEntityInputSchema({ entity: global, req })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const fieldsAllowedByAccess = filterFieldsByAccess({
|
|
67
|
+
blocks: req.payload.config.blocks,
|
|
68
|
+
fields: global.flattenedFields,
|
|
69
|
+
permissions,
|
|
70
|
+
shouldExcludeField: ({ create, update }) => !create && !update,
|
|
56
71
|
})
|
|
72
|
+
|
|
73
|
+
return buildEntityInputSchema({ entity: global, fields: fieldsAllowedByAccess, req })
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
const buildEntityInputSchema = ({
|
|
60
77
|
entity,
|
|
78
|
+
fields = entity.flattenedFields,
|
|
61
79
|
req,
|
|
62
|
-
virtualFieldNames,
|
|
63
80
|
}: {
|
|
64
81
|
entity: SanitizedCollectionConfig | SanitizedGlobalConfig
|
|
82
|
+
fields?: FlattenedField[]
|
|
65
83
|
req: PayloadRequest
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
}): JsonSchemaType => {
|
|
85
|
+
// The core schema generator reads flattenedFields from the entity and has no fields argument.
|
|
86
|
+
const entityForSchema = { ...entity, flattenedFields: fields }
|
|
87
|
+
const schema = entityToStandaloneJSONSchema({
|
|
88
|
+
config: req.payload.config,
|
|
89
|
+
defaultIDType: req.payload.db.defaultIDType,
|
|
90
|
+
entity: entityForSchema,
|
|
91
|
+
i18n: req.i18n,
|
|
92
|
+
variant: 'input',
|
|
93
|
+
}) as unknown as JsonSchemaType
|
|
94
|
+
|
|
95
|
+
return sanitizeEntitySchema(schema)
|
|
96
|
+
}
|
|
@@ -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 timestamps are excluded upstream by the `input` variant. `id` stays because it's
|
|
80
|
+
// a valid optional input (a client may supply a custom ID).
|
|
81
|
+
id: { type: 'string' },
|
|
89
82
|
content: {
|
|
90
83
|
type: 'object',
|
|
91
84
|
properties: {
|