@payloadcms/plugin-mcp 4.0.0-internal.c2b57ce → 4.0.0-internal.c6494b5
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/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 +98 -18
- 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 +6 -6
- package/dist/mcp/buildMcpServer.d.ts.map +1 -1
- package/dist/mcp/buildMcpServer.js +146 -74
- 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 +123 -61
- 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 +63 -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 +60 -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 +114 -64
- 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 +55 -61
- 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 +90 -0
- package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
- package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
- package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/fileInput.js +88 -0
- package/dist/mcp/builtin/collections/fileInput.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 +77 -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 +61 -74
- 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 +74 -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 +84 -0
- package/dist/mcp/builtin/collections/findVersionsTool.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 +103 -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 +69 -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 +118 -133
- package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
- package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
- package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
- package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
- package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
- package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
- package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
- package/dist/mcp/builtin/formatEntityError.js +55 -0
- package/dist/mcp/builtin/formatEntityError.js.map +1 -0
- 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 +69 -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 +60 -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 +27 -36
- 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 +68 -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 +78 -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 +81 -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 +70 -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 +44 -58
- package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
- package/dist/mcp/builtinTools.d.ts +150 -19
- package/dist/mcp/builtinTools.d.ts.map +1 -1
- package/dist/mcp/builtinTools.js +125 -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 +124 -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 +86 -47
- package/dist/stdio.js.map +1 -1
- package/dist/types.d.ts +123 -114
- 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/toStandardSchema.d.ts.map +1 -1
- package/package.json +14 -21
- package/src/defaultAccess.ts +3 -0
- package/src/endpoint/access.ts +80 -101
- package/src/endpoint/index.ts +88 -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 +180 -110
- package/src/mcp/builtin/collections/authTools.ts +122 -75
- package/src/mcp/builtin/collections/countTool.ts +61 -0
- package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
- package/src/mcp/builtin/collections/createTool.ts +117 -88
- package/src/mcp/builtin/collections/deleteTool.ts +48 -74
- package/src/mcp/builtin/collections/duplicateTool.ts +84 -0
- package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
- package/src/mcp/builtin/collections/fileInput.ts +127 -0
- package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
- package/src/mcp/builtin/collections/findTool.ts +57 -110
- package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
- package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
- package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +91 -0
- package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
- package/src/mcp/builtin/collections/updateTool.ts +106 -138
- package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
- package/src/mcp/builtin/formatEntityError.ts +77 -0
- package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
- package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
- package/src/mcp/builtin/globals/findTool.ts +27 -57
- package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
- package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
- package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +73 -0
- package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
- package/src/mcp/builtin/globals/updateTool.ts +51 -77
- package/src/mcp/builtinTools.ts +118 -41
- package/src/mcp/sanitizeMCPConfig.ts +120 -78
- package/src/stdio.ts +80 -47
- package/src/types.ts +141 -120
- package/src/utils/getPluginConfig.ts +1 -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 -59
- 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/getVirtualFieldNames.d.ts +0 -14
- package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
- package/dist/utils/getVirtualFieldNames.js +0 -35
- package/dist/utils/getVirtualFieldNames.js.map +0 -1
- 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/prepareCollectionSchema.d.ts +0 -7
- package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts.map +0 -1
- package/dist/utils/schemaConversion/prepareCollectionSchema.js +0 -37
- package/dist/utils/schemaConversion/prepareCollectionSchema.js.map +0 -1
- package/dist/utils/schemaConversion/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/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts +0 -13
- package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts.map +0 -1
- package/dist/utils/schemaConversion/sanitizeJsonSchema.js +0 -56
- package/dist/utils/schemaConversion/sanitizeJsonSchema.js.map +0 -1
- package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts +0 -20
- package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts.map +0 -1
- package/dist/utils/schemaConversion/simplifyRelationshipFields.js +0 -56
- package/dist/utils/schemaConversion/simplifyRelationshipFields.js.map +0 -1
- package/dist/utils/schemaConversion/transformPointFields.d.ts +0 -3
- package/dist/utils/schemaConversion/transformPointFields.d.ts.map +0 -1
- package/dist/utils/schemaConversion/transformPointFields.js +0 -57
- package/dist/utils/schemaConversion/transformPointFields.js.map +0 -1
- package/dist/utils/transformPointDataToPayload.d.ts +0 -7
- package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
- package/dist/utils/transformPointDataToPayload.js +0 -28
- package/dist/utils/transformPointDataToPayload.js.map +0 -1
- package/src/collection/getAccessField.ts +0 -64
- package/src/collection/index.ts +0 -63
- package/src/components/AccessField/index.client.tsx +0 -344
- package/src/components/AccessField/index.css +0 -93
- package/src/utils/getVirtualFieldNames.ts +0 -53
- package/src/utils/localAPIDefaults.ts +0 -22
- package/src/utils/schemaConversion/prepareCollectionSchema.ts +0 -39
- package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
- package/src/utils/schemaConversion/sanitizeJsonSchema.ts +0 -62
- package/src/utils/schemaConversion/simplifyRelationshipFields.ts +0 -70
- package/src/utils/schemaConversion/transformPointFields.ts +0 -56
- package/src/utils/transformPointDataToPayload.ts +0 -40
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { JSONFieldClientProps } from 'payload'
|
|
4
|
-
|
|
5
|
-
import { CheckboxInput, Collapsible, useField } from '@payloadcms/ui'
|
|
6
|
-
import React from 'react'
|
|
7
|
-
|
|
8
|
-
import type { ClientMCPPluginConfig, MCPAPIKeysDocAccessTree } from '../../types.js'
|
|
9
|
-
|
|
10
|
-
import './index.css'
|
|
11
|
-
|
|
12
|
-
const baseClass = 'mcp-access-field'
|
|
13
|
-
|
|
14
|
-
type ClientItem = ClientMCPPluginConfig['items'][number]
|
|
15
|
-
type ScopeKey = 'collections' | 'globals'
|
|
16
|
-
type FlatKey = 'prompts' | 'resources' | 'tools'
|
|
17
|
-
|
|
18
|
-
type Props = {
|
|
19
|
-
pluginConfig: ClientMCPPluginConfig
|
|
20
|
-
} & JSONFieldClientProps
|
|
21
|
-
|
|
22
|
-
/** Drop a key from an object and return a new object — or `undefined` if it'd be empty. */
|
|
23
|
-
const without = <T extends Record<string, unknown>>(
|
|
24
|
-
obj: T | undefined,
|
|
25
|
-
key: string,
|
|
26
|
-
): T | undefined => {
|
|
27
|
-
if (!obj || !(key in obj)) {
|
|
28
|
-
return obj
|
|
29
|
-
}
|
|
30
|
-
const { [key]: _omitted, ...rest } = obj
|
|
31
|
-
return Object.keys(rest).length === 0 ? undefined : (rest as T)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const setKey = <T extends Record<string, unknown>>(
|
|
35
|
-
obj: T | undefined,
|
|
36
|
-
key: string,
|
|
37
|
-
value: unknown,
|
|
38
|
-
): T => ({ ...(obj ?? {}), [key]: value }) as T
|
|
39
|
-
|
|
40
|
-
export const AccessField: React.FC<Props> = ({ path, pluginConfig }) => {
|
|
41
|
-
const { setValue, value } = useField<MCPAPIKeysDocAccessTree>({ path })
|
|
42
|
-
const access = value ?? {}
|
|
43
|
-
|
|
44
|
-
// Bucket items for rendering. (Bucketing is cheap and runs once per render;
|
|
45
|
-
// memoizing would mean managing inputs/refs for marginal benefit.)
|
|
46
|
-
const collectionsBySlug: Record<string, ClientItem[]> = {}
|
|
47
|
-
const globalsBySlug: Record<string, ClientItem[]> = {}
|
|
48
|
-
const tools: ClientItem[] = []
|
|
49
|
-
const prompts: ClientItem[] = []
|
|
50
|
-
const resources: ClientItem[] = []
|
|
51
|
-
for (const item of pluginConfig.items) {
|
|
52
|
-
switch (item.type) {
|
|
53
|
-
case 'collectionTool':
|
|
54
|
-
;(collectionsBySlug[item.collectionSlug!] ??= []).push(item)
|
|
55
|
-
break
|
|
56
|
-
case 'globalTool':
|
|
57
|
-
;(globalsBySlug[item.globalSlug!] ??= []).push(item)
|
|
58
|
-
break
|
|
59
|
-
case 'prompt':
|
|
60
|
-
prompts.push(item)
|
|
61
|
-
break
|
|
62
|
-
case 'resource':
|
|
63
|
-
resources.push(item)
|
|
64
|
-
break
|
|
65
|
-
case 'tool':
|
|
66
|
-
tools.push(item)
|
|
67
|
-
break
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const isScopedAllowed = (scope: ScopeKey, slug: string, key: string): boolean =>
|
|
72
|
-
access[scope]?.[slug]?.[key] !== false
|
|
73
|
-
|
|
74
|
-
const isFlatAllowed = (scope: FlatKey, key: string): boolean => access[scope]?.[key] !== false
|
|
75
|
-
|
|
76
|
-
const toggleScoped = (scope: ScopeKey, slug: string, key: string, allow: boolean) => {
|
|
77
|
-
if (allow) {
|
|
78
|
-
const slugBucket = without(access[scope]?.[slug], key)
|
|
79
|
-
const scopeBucket = slugBucket
|
|
80
|
-
? setKey(access[scope], slug, slugBucket)
|
|
81
|
-
: without(access[scope], slug)
|
|
82
|
-
setValue(scopeBucket ? setKey(access, scope, scopeBucket) : (without(access, scope) ?? {}))
|
|
83
|
-
} else {
|
|
84
|
-
setValue(
|
|
85
|
-
setKey(
|
|
86
|
-
access,
|
|
87
|
-
scope,
|
|
88
|
-
setKey(access[scope], slug, setKey(access[scope]?.[slug], key, false)),
|
|
89
|
-
),
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const toggleFlat = (scope: FlatKey, key: string, allow: boolean) => {
|
|
95
|
-
if (allow) {
|
|
96
|
-
const bucket = without(access[scope], key)
|
|
97
|
-
setValue(bucket ? setKey(access, scope, bucket) : (without(access, scope) ?? {}))
|
|
98
|
-
} else {
|
|
99
|
-
setValue(setKey(access, scope, setKey(access[scope], key, false)))
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const setAllScoped = (scope: ScopeKey, slug: string, leaves: ClientItem[], allow: boolean) => {
|
|
104
|
-
if (allow) {
|
|
105
|
-
const scopeBucket = without(access[scope], slug)
|
|
106
|
-
setValue(scopeBucket ? setKey(access, scope, scopeBucket) : (without(access, scope) ?? {}))
|
|
107
|
-
} else {
|
|
108
|
-
const slugBucket = leaves.reduce<Record<string, boolean>>(
|
|
109
|
-
(acc, leaf) => ({ ...acc, [leaf.key]: false }),
|
|
110
|
-
{},
|
|
111
|
-
)
|
|
112
|
-
setValue(setKey(access, scope, setKey(access[scope], slug, slugBucket)))
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const setAllFlat = (scope: FlatKey, leaves: ClientItem[], allow: boolean) => {
|
|
117
|
-
if (allow) {
|
|
118
|
-
setValue(without(access, scope) ?? {})
|
|
119
|
-
} else {
|
|
120
|
-
const bucket = leaves.reduce<Record<string, boolean>>(
|
|
121
|
-
(acc, leaf) => ({ ...acc, [leaf.key]: false }),
|
|
122
|
-
{},
|
|
123
|
-
)
|
|
124
|
-
setValue(setKey(access, scope, bucket))
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const collectionSlugs = Object.keys(collectionsBySlug)
|
|
129
|
-
const globalSlugs = Object.keys(globalsBySlug)
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<div className={baseClass}>
|
|
133
|
-
{collectionSlugs.length > 0 && (
|
|
134
|
-
<section className={`${baseClass}__section`}>
|
|
135
|
-
<header className={`${baseClass}__section-header`}>
|
|
136
|
-
{/* TODO: needs i18n once design is finalized */}
|
|
137
|
-
<h4>Collection-level permissions</h4>
|
|
138
|
-
{/* TODO: needs i18n once design is finalized */}
|
|
139
|
-
<p>Allow MCP clients to perform the following actions within these collections:</p>
|
|
140
|
-
</header>
|
|
141
|
-
{collectionSlugs.map((slug) => {
|
|
142
|
-
const leaves = collectionsBySlug[slug]!
|
|
143
|
-
return (
|
|
144
|
-
<Collapsible
|
|
145
|
-
actions={
|
|
146
|
-
<GroupActions
|
|
147
|
-
onSetAll={(allow) => setAllScoped('collections', slug, leaves, allow)}
|
|
148
|
-
/>
|
|
149
|
-
}
|
|
150
|
-
className={`${baseClass}__group`}
|
|
151
|
-
header={<span className={`${baseClass}__group-label`}>{titleCase(slug)}</span>}
|
|
152
|
-
initCollapsed
|
|
153
|
-
key={`collection-${slug}`}
|
|
154
|
-
>
|
|
155
|
-
<ul className={`${baseClass}__list`}>
|
|
156
|
-
{leaves.map((leaf) => (
|
|
157
|
-
<li key={leaf.key}>
|
|
158
|
-
<CheckboxInput
|
|
159
|
-
checked={isScopedAllowed('collections', slug, leaf.key)}
|
|
160
|
-
id={`${path}.collections.${slug}.${leaf.key}`}
|
|
161
|
-
label={leaf.label}
|
|
162
|
-
onToggle={(e) =>
|
|
163
|
-
toggleScoped('collections', slug, leaf.key, e.target.checked)
|
|
164
|
-
}
|
|
165
|
-
tooltip={leaf.description}
|
|
166
|
-
/>
|
|
167
|
-
</li>
|
|
168
|
-
))}
|
|
169
|
-
</ul>
|
|
170
|
-
</Collapsible>
|
|
171
|
-
)
|
|
172
|
-
})}
|
|
173
|
-
</section>
|
|
174
|
-
)}
|
|
175
|
-
|
|
176
|
-
{globalSlugs.length > 0 && (
|
|
177
|
-
<section className={`${baseClass}__section`}>
|
|
178
|
-
<header className={`${baseClass}__section-header`}>
|
|
179
|
-
{/* TODO: needs i18n once design is finalized */}
|
|
180
|
-
<h4>Global-level permissions</h4>
|
|
181
|
-
{/* TODO: needs i18n once design is finalized */}
|
|
182
|
-
<p>Allow MCP clients to perform the following actions on these globals:</p>
|
|
183
|
-
</header>
|
|
184
|
-
{globalSlugs.map((slug) => {
|
|
185
|
-
const leaves = globalsBySlug[slug]!
|
|
186
|
-
return (
|
|
187
|
-
<Collapsible
|
|
188
|
-
actions={
|
|
189
|
-
<GroupActions
|
|
190
|
-
onSetAll={(allow) => setAllScoped('globals', slug, leaves, allow)}
|
|
191
|
-
/>
|
|
192
|
-
}
|
|
193
|
-
className={`${baseClass}__group`}
|
|
194
|
-
header={<span className={`${baseClass}__group-label`}>{titleCase(slug)}</span>}
|
|
195
|
-
initCollapsed
|
|
196
|
-
key={`global-${slug}`}
|
|
197
|
-
>
|
|
198
|
-
<ul className={`${baseClass}__list`}>
|
|
199
|
-
{leaves.map((leaf) => (
|
|
200
|
-
<li key={leaf.key}>
|
|
201
|
-
<CheckboxInput
|
|
202
|
-
checked={isScopedAllowed('globals', slug, leaf.key)}
|
|
203
|
-
id={`${path}.globals.${slug}.${leaf.key}`}
|
|
204
|
-
label={leaf.label}
|
|
205
|
-
onToggle={(e) => toggleScoped('globals', slug, leaf.key, e.target.checked)}
|
|
206
|
-
tooltip={leaf.description}
|
|
207
|
-
/>
|
|
208
|
-
</li>
|
|
209
|
-
))}
|
|
210
|
-
</ul>
|
|
211
|
-
</Collapsible>
|
|
212
|
-
)
|
|
213
|
-
})}
|
|
214
|
-
</section>
|
|
215
|
-
)}
|
|
216
|
-
|
|
217
|
-
{(tools.length > 0 || prompts.length > 0 || resources.length > 0) && (
|
|
218
|
-
<section className={`${baseClass}__section`}>
|
|
219
|
-
<header className={`${baseClass}__section-header`}>
|
|
220
|
-
{/* TODO: needs i18n once design is finalized */}
|
|
221
|
-
<h4>Project-level permissions</h4>
|
|
222
|
-
{/* TODO: needs i18n once design is finalized */}
|
|
223
|
-
<p>Cross-cutting tools, prompts, and resources not scoped to a single collection.</p>
|
|
224
|
-
</header>
|
|
225
|
-
{tools.length > 0 && (
|
|
226
|
-
<Collapsible
|
|
227
|
-
actions={<GroupActions onSetAll={(allow) => setAllFlat('tools', tools, allow)} />}
|
|
228
|
-
className={`${baseClass}__group`}
|
|
229
|
-
header={
|
|
230
|
-
/* TODO: needs i18n once design is finalized */
|
|
231
|
-
<span className={`${baseClass}__group-label`}>Tools</span>
|
|
232
|
-
}
|
|
233
|
-
initCollapsed
|
|
234
|
-
>
|
|
235
|
-
<ul className={`${baseClass}__list`}>
|
|
236
|
-
{tools.map((leaf) => (
|
|
237
|
-
<li key={leaf.key}>
|
|
238
|
-
<CheckboxInput
|
|
239
|
-
checked={isFlatAllowed('tools', leaf.key)}
|
|
240
|
-
id={`${path}.tools.${leaf.key}`}
|
|
241
|
-
label={leaf.label}
|
|
242
|
-
onToggle={(e) => toggleFlat('tools', leaf.key, e.target.checked)}
|
|
243
|
-
tooltip={leaf.description}
|
|
244
|
-
/>
|
|
245
|
-
</li>
|
|
246
|
-
))}
|
|
247
|
-
</ul>
|
|
248
|
-
</Collapsible>
|
|
249
|
-
)}
|
|
250
|
-
{prompts.length > 0 && (
|
|
251
|
-
<Collapsible
|
|
252
|
-
actions={<GroupActions onSetAll={(allow) => setAllFlat('prompts', prompts, allow)} />}
|
|
253
|
-
className={`${baseClass}__group`}
|
|
254
|
-
header={
|
|
255
|
-
/* TODO: needs i18n once design is finalized */
|
|
256
|
-
<span className={`${baseClass}__group-label`}>Prompts</span>
|
|
257
|
-
}
|
|
258
|
-
initCollapsed
|
|
259
|
-
>
|
|
260
|
-
<ul className={`${baseClass}__list`}>
|
|
261
|
-
{prompts.map((leaf) => (
|
|
262
|
-
<li key={leaf.key}>
|
|
263
|
-
<CheckboxInput
|
|
264
|
-
checked={isFlatAllowed('prompts', leaf.key)}
|
|
265
|
-
id={`${path}.prompts.${leaf.key}`}
|
|
266
|
-
label={leaf.label}
|
|
267
|
-
onToggle={(e) => toggleFlat('prompts', leaf.key, e.target.checked)}
|
|
268
|
-
tooltip={leaf.description}
|
|
269
|
-
/>
|
|
270
|
-
</li>
|
|
271
|
-
))}
|
|
272
|
-
</ul>
|
|
273
|
-
</Collapsible>
|
|
274
|
-
)}
|
|
275
|
-
{resources.length > 0 && (
|
|
276
|
-
<Collapsible
|
|
277
|
-
actions={
|
|
278
|
-
<GroupActions onSetAll={(allow) => setAllFlat('resources', resources, allow)} />
|
|
279
|
-
}
|
|
280
|
-
className={`${baseClass}__group`}
|
|
281
|
-
header={
|
|
282
|
-
/* TODO: needs i18n once design is finalized */
|
|
283
|
-
<span className={`${baseClass}__group-label`}>Resources</span>
|
|
284
|
-
}
|
|
285
|
-
initCollapsed
|
|
286
|
-
>
|
|
287
|
-
<ul className={`${baseClass}__list`}>
|
|
288
|
-
{resources.map((leaf) => (
|
|
289
|
-
<li key={leaf.key}>
|
|
290
|
-
<CheckboxInput
|
|
291
|
-
checked={isFlatAllowed('resources', leaf.key)}
|
|
292
|
-
id={`${path}.resources.${leaf.key}`}
|
|
293
|
-
label={leaf.label}
|
|
294
|
-
onToggle={(e) => toggleFlat('resources', leaf.key, e.target.checked)}
|
|
295
|
-
tooltip={leaf.description}
|
|
296
|
-
/>
|
|
297
|
-
</li>
|
|
298
|
-
))}
|
|
299
|
-
</ul>
|
|
300
|
-
</Collapsible>
|
|
301
|
-
)}
|
|
302
|
-
</section>
|
|
303
|
-
)}
|
|
304
|
-
</div>
|
|
305
|
-
)
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const GroupActions: React.FC<{ onSetAll: (allow: boolean) => void }> = ({ onSetAll }) => (
|
|
309
|
-
// TODO: button labels + aria-labels need i18n once design is finalized
|
|
310
|
-
<div className={`${baseClass}__group-actions`}>
|
|
311
|
-
<button
|
|
312
|
-
aria-label="Select all"
|
|
313
|
-
className={`${baseClass}__action`}
|
|
314
|
-
onClick={(e) => {
|
|
315
|
-
e.stopPropagation()
|
|
316
|
-
onSetAll(true)
|
|
317
|
-
}}
|
|
318
|
-
title="Select all"
|
|
319
|
-
type="button"
|
|
320
|
-
>
|
|
321
|
-
all
|
|
322
|
-
</button>
|
|
323
|
-
<span aria-hidden className={`${baseClass}__action-sep`}>
|
|
324
|
-
/
|
|
325
|
-
</span>
|
|
326
|
-
<button
|
|
327
|
-
aria-label="Clear all"
|
|
328
|
-
className={`${baseClass}__action`}
|
|
329
|
-
onClick={(e) => {
|
|
330
|
-
e.stopPropagation()
|
|
331
|
-
onSetAll(false)
|
|
332
|
-
}}
|
|
333
|
-
title="Clear all"
|
|
334
|
-
type="button"
|
|
335
|
-
>
|
|
336
|
-
none
|
|
337
|
-
</button>
|
|
338
|
-
</div>
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
const titleCase = (slug: string): string =>
|
|
342
|
-
slug.replace(/(^|[-_])(.)/g, (_, sep: string, ch: string) =>
|
|
343
|
-
sep ? ` ${ch.toUpperCase()}` : ch.toUpperCase(),
|
|
344
|
-
)
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
@layer payload-default {
|
|
2
|
-
.mcp-access-field {
|
|
3
|
-
display: flex;
|
|
4
|
-
flex-direction: column;
|
|
5
|
-
gap: var(--base);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.mcp-access-field__section {
|
|
9
|
-
display: flex;
|
|
10
|
-
flex-direction: column;
|
|
11
|
-
gap: calc(var(--base) / 2);
|
|
12
|
-
padding-bottom: var(--base);
|
|
13
|
-
/* Full-bleed separator: extend past the sidebar's horizontal gutter so the
|
|
14
|
-
border-block-end spans the entire sidebar width. */
|
|
15
|
-
margin-inline-start: calc(-1 * var(--sidebar-gutter-h-left, var(--gutter-h)));
|
|
16
|
-
margin-inline-end: calc(-1 * var(--sidebar-gutter-h-right, var(--gutter-h)));
|
|
17
|
-
padding-inline-start: var(--sidebar-gutter-h-left, var(--gutter-h));
|
|
18
|
-
padding-inline-end: var(--sidebar-gutter-h-right, var(--gutter-h));
|
|
19
|
-
border-block-end: 1px solid var(--theme-elevation-100);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.mcp-access-field__section:last-child {
|
|
23
|
-
border-block-end: none;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.mcp-access-field__section-header {
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-direction: column;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.mcp-access-field__section-header h4 {
|
|
32
|
-
margin: 0;
|
|
33
|
-
color: var(--color-text);
|
|
34
|
-
font-family: var(--text-heading-small-font-family);
|
|
35
|
-
font-size: var(--text-heading-small-font-size);
|
|
36
|
-
font-weight: var(--text-heading-small-font-weight);
|
|
37
|
-
line-height: var(--text-heading-small-line-height);
|
|
38
|
-
letter-spacing: var(--text-heading-small-letter-spacing);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.mcp-access-field__section-header p {
|
|
42
|
-
margin: 0;
|
|
43
|
-
color: var(--color-text-secondary);
|
|
44
|
-
font-family: var(--text-body-medium-font-family);
|
|
45
|
-
font-size: var(--text-body-medium-font-size);
|
|
46
|
-
font-weight: var(--text-body-medium-font-weight);
|
|
47
|
-
line-height: var(--text-body-medium-line-height);
|
|
48
|
-
letter-spacing: var(--text-body-medium-letter-spacing);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.mcp-access-field__group {
|
|
52
|
-
margin-block-end: calc(var(--base) / 2);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.mcp-access-field__group-label {
|
|
56
|
-
font-weight: 600;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.mcp-access-field__group-actions {
|
|
60
|
-
display: flex;
|
|
61
|
-
align-items: center;
|
|
62
|
-
gap: calc(var(--base) / 6);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.mcp-access-field__action {
|
|
66
|
-
appearance: none;
|
|
67
|
-
background: none;
|
|
68
|
-
border: none;
|
|
69
|
-
color: var(--theme-elevation-500);
|
|
70
|
-
cursor: pointer;
|
|
71
|
-
font-size: 0.75rem;
|
|
72
|
-
padding: 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
.mcp-access-field__action:hover {
|
|
76
|
-
color: var(--theme-text);
|
|
77
|
-
text-decoration: underline;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.mcp-access-field__action-sep {
|
|
81
|
-
color: var(--theme-elevation-300);
|
|
82
|
-
font-size: 0.75rem;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.mcp-access-field__list {
|
|
86
|
-
list-style: none;
|
|
87
|
-
margin: 0;
|
|
88
|
-
padding: 0;
|
|
89
|
-
display: flex;
|
|
90
|
-
flex-direction: column;
|
|
91
|
-
gap: calc(var(--base) / 3);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { SanitizedConfig } from 'payload'
|
|
2
|
-
|
|
3
|
-
import { fieldIsVirtual } from 'payload/shared'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns the names of all top-level virtual fields for a given collection slug.
|
|
7
|
-
*/
|
|
8
|
-
export function getCollectionVirtualFieldNames(config: SanitizedConfig, slug: string): string[] {
|
|
9
|
-
const collection = config.collections.find((c) => c.slug === slug)
|
|
10
|
-
|
|
11
|
-
if (!collection) {
|
|
12
|
-
return []
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return collection.flattenedFields
|
|
16
|
-
.filter((field) => 'name' in field && fieldIsVirtual(field))
|
|
17
|
-
.map((field) => (field as { name: string }).name)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Returns the names of all top-level virtual fields for a given global slug.
|
|
22
|
-
*/
|
|
23
|
-
export function getGlobalVirtualFieldNames(config: SanitizedConfig, slug: string): string[] {
|
|
24
|
-
const global = config.globals.find((g) => g.slug === slug)
|
|
25
|
-
|
|
26
|
-
if (!global) {
|
|
27
|
-
return []
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return global.flattenedFields
|
|
31
|
-
.filter((field) => 'name' in field && fieldIsVirtual(field))
|
|
32
|
-
.map((field) => (field as { name: string }).name)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Strips virtual field values from a data object given a list of virtual field names.
|
|
37
|
-
*/
|
|
38
|
-
export function stripVirtualFields(
|
|
39
|
-
data: Record<string, unknown>,
|
|
40
|
-
virtualFieldNames: string[],
|
|
41
|
-
): Record<string, unknown> {
|
|
42
|
-
if (virtualFieldNames.length === 0) {
|
|
43
|
-
return data
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const stripped = { ...data }
|
|
47
|
-
|
|
48
|
-
for (const name of virtualFieldNames) {
|
|
49
|
-
delete stripped[name]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return stripped
|
|
53
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { AuthorizedMCP } from '../types.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns the `user` and `overrideAccess` arguments every Payload local API call
|
|
5
|
-
* should receive when initiated from an MCP request. Spread the result into the
|
|
6
|
-
* call's arg object so the two pieces of caller state can't drift apart.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* await req.payload.create({
|
|
11
|
-
* collection: 'posts',
|
|
12
|
-
* data,
|
|
13
|
-
* ...localAPIDefaults(authorizedMCP),
|
|
14
|
-
* })
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
export const localAPIDefaults = (
|
|
18
|
-
authorizedMCP: AuthorizedMCP,
|
|
19
|
-
): { overrideAccess: boolean; user: AuthorizedMCP['user'] } => ({
|
|
20
|
-
overrideAccess: authorizedMCP.overrideAccess,
|
|
21
|
-
user: authorizedMCP.user,
|
|
22
|
-
})
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { JsonSchemaType } from '../../types.js'
|
|
2
|
-
|
|
3
|
-
import { sanitizeJsonSchema } from './sanitizeJsonSchema.js'
|
|
4
|
-
import { simplifyRelationshipFields } from './simplifyRelationshipFields.js'
|
|
5
|
-
import { transformPointFieldsForMCP } from './transformPointFields.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Fields Payload manages automatically — clients should never be required to provide
|
|
9
|
-
* them. Stripped from `required` and `properties` so they don't appear in tool
|
|
10
|
-
* inputs.
|
|
11
|
-
*/
|
|
12
|
-
const PAYLOAD_MANAGED_FIELDS = new Set(['_status', 'createdAt', 'id', 'updatedAt'])
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Sanitizes Payload's auto-generated collection JSON Schema so it can be safely
|
|
16
|
-
* fed into the MCP server's `fromJsonSchema()` adapter (no zod intermediate step).
|
|
17
|
-
*/
|
|
18
|
-
export const prepareCollectionSchema = (schema: JsonSchemaType): JsonSchemaType => {
|
|
19
|
-
// Clone to avoid mutating the original schema (used elsewhere for tool listing)
|
|
20
|
-
const schemaClone = JSON.parse(JSON.stringify(schema)) as JsonSchemaType
|
|
21
|
-
|
|
22
|
-
const sanitized = sanitizeJsonSchema(schemaClone)
|
|
23
|
-
const pointTransformed = transformPointFieldsForMCP(sanitized)
|
|
24
|
-
const simplified = simplifyRelationshipFields(pointTransformed)
|
|
25
|
-
|
|
26
|
-
if (simplified.properties) {
|
|
27
|
-
for (const field of PAYLOAD_MANAGED_FIELDS) {
|
|
28
|
-
delete simplified.properties[field]
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (Array.isArray(simplified.required)) {
|
|
32
|
-
simplified.required = simplified.required.filter((name) => !PAYLOAD_MANAGED_FIELDS.has(name))
|
|
33
|
-
if (simplified.required.length === 0) {
|
|
34
|
-
delete simplified.required
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return simplified
|
|
39
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { JsonSchemaType } from '../../types.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Removes virtual fields from a JSON Schema by name so they don't appear
|
|
5
|
-
* in the generated MCP tool input schema.
|
|
6
|
-
*/
|
|
7
|
-
export function removeVirtualFieldsFromSchema(
|
|
8
|
-
schema: JsonSchemaType,
|
|
9
|
-
virtualFieldNames: string[],
|
|
10
|
-
): JsonSchemaType {
|
|
11
|
-
if (virtualFieldNames.length === 0) {
|
|
12
|
-
return schema
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
for (const name of virtualFieldNames) {
|
|
16
|
-
delete schema?.properties?.[name]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (Array.isArray(schema.required)) {
|
|
20
|
-
schema.required = schema.required.filter((field) => !virtualFieldNames.includes(field))
|
|
21
|
-
if (schema.required.length === 0) {
|
|
22
|
-
delete schema.required
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return schema
|
|
27
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { JsonSchemaType } from '../../types.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Removes internal Payload properties (id, createdAt, updatedAt) from a
|
|
5
|
-
* JSON Schema so they don't appear in the generated Zod validation schema.
|
|
6
|
-
* Also strips `id` from the `required` array when present.
|
|
7
|
-
*
|
|
8
|
-
* Additionally normalizes nullable type arrays (e.g. `['array', 'null']` →
|
|
9
|
-
* `'array'`) throughout the schema tree. Without this, `json-schema-to-zod`
|
|
10
|
-
* emits a Zod union which the MCP SDK serialises back as `anyOf`, stripping
|
|
11
|
-
* the concrete `type` from the output and breaking schema introspection.
|
|
12
|
-
*/
|
|
13
|
-
export function sanitizeJsonSchema(schema: JsonSchemaType): JsonSchemaType {
|
|
14
|
-
delete schema?.properties?.id
|
|
15
|
-
delete schema?.properties?.createdAt
|
|
16
|
-
delete schema?.properties?.updatedAt
|
|
17
|
-
|
|
18
|
-
if (Array.isArray(schema.required)) {
|
|
19
|
-
schema.required = schema.required.filter((field) => field !== 'id')
|
|
20
|
-
if (schema.required.length === 0) {
|
|
21
|
-
delete schema.required
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (schema.properties && typeof schema.properties === 'object') {
|
|
26
|
-
for (const key of Object.keys(schema.properties)) {
|
|
27
|
-
const prop = schema.properties[key] as JsonSchemaType
|
|
28
|
-
if (!prop || typeof prop !== 'object') {
|
|
29
|
-
continue
|
|
30
|
-
}
|
|
31
|
-
normalizeNullableType(prop)
|
|
32
|
-
if (prop.properties) {
|
|
33
|
-
sanitizeJsonSchema(prop)
|
|
34
|
-
}
|
|
35
|
-
if (prop.items && typeof prop.items === 'object' && !Array.isArray(prop.items)) {
|
|
36
|
-
sanitizeJsonSchema(prop.items)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return schema
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Strips `'null'` from a `type` array only when the remaining type is a
|
|
46
|
-
* complex structural type (`array` or `object`).
|
|
47
|
-
*
|
|
48
|
-
* Simple scalar types (`string`, `number`, `boolean`) are intentionally
|
|
49
|
-
* preserved as `['string', 'null']` so that the MCP SDK serialises them as a
|
|
50
|
-
* compact inline `type` array. Complex types however cause `zodToJsonSchema`
|
|
51
|
-
* to emit `anyOf: [{ type: 'array', items: ... }, { type: 'null' }]`, which
|
|
52
|
-
* has no top-level `type` property and breaks schema introspection by clients.
|
|
53
|
-
*/
|
|
54
|
-
function normalizeNullableType(schema: JsonSchemaType): void {
|
|
55
|
-
if (!Array.isArray(schema.type)) {
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
const nonNullTypes = schema.type.filter((t) => t !== 'null')
|
|
59
|
-
if (nonNullTypes.length === 1 && (nonNullTypes[0] === 'array' || nonNullTypes[0] === 'object')) {
|
|
60
|
-
schema.type = nonNullTypes[0]
|
|
61
|
-
}
|
|
62
|
-
}
|