payload-mcp-toolkit 0.3.4 → 0.7.4
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/README.md +253 -151
- package/dist/api-keys.d.ts +46 -0
- package/dist/api-keys.js +308 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/auth-strategy.d.ts +96 -0
- package/dist/auth-strategy.js +261 -0
- package/dist/auth-strategy.js.map +1 -0
- package/dist/components/CollectionScopesMatrix.d.ts +8 -0
- package/dist/components/CollectionScopesMatrix.js +32 -0
- package/dist/components/CollectionScopesMatrix.js.map +1 -0
- package/dist/components/GlobalScopesMatrix.d.ts +8 -0
- package/dist/components/GlobalScopesMatrix.js +28 -0
- package/dist/components/GlobalScopesMatrix.js.map +1 -0
- package/dist/components/ScopesTable.d.ts +19 -0
- package/dist/components/ScopesTable.js +285 -0
- package/dist/components/ScopesTable.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -0
- package/dist/conflict-detection.d.ts +13 -0
- package/dist/conflict-detection.js +41 -0
- package/dist/conflict-detection.js.map +1 -0
- package/dist/draft-workflow.d.ts +46 -48
- package/dist/draft-workflow.js +53 -135
- package/dist/draft-workflow.js.map +1 -1
- package/dist/endpoint.d.ts +35 -0
- package/dist/endpoint.js +105 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/hash.d.ts +21 -0
- package/dist/hash.js +36 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +9 -9
- package/dist/index.js +167 -69
- package/dist/index.js.map +1 -1
- package/dist/introspection.d.ts +17 -3
- package/dist/introspection.js +95 -36
- package/dist/introspection.js.map +1 -1
- package/dist/prompts.js +5 -5
- package/dist/prompts.js.map +1 -1
- package/dist/registry.d.ts +50 -0
- package/dist/registry.js +169 -0
- package/dist/registry.js.map +1 -0
- package/dist/resources.d.ts +5 -3
- package/dist/resources.js +23 -11
- package/dist/resources.js.map +1 -1
- package/dist/scope/audit-log.d.ts +18 -0
- package/dist/scope/audit-log.js +50 -0
- package/dist/scope/audit-log.js.map +1 -0
- package/dist/scope/policy.d.ts +73 -0
- package/dist/scope/policy.js +218 -0
- package/dist/scope/policy.js.map +1 -0
- package/dist/tools/_helpers.d.ts +62 -1
- package/dist/tools/_helpers.js +181 -0
- package/dist/tools/_helpers.js.map +1 -1
- package/dist/tools/_layout-helpers.d.ts +43 -0
- package/dist/tools/_layout-helpers.js +159 -0
- package/dist/tools/_layout-helpers.js.map +1 -0
- package/dist/tools/create-document.d.ts +5 -5
- package/dist/tools/create-document.js +25 -21
- package/dist/tools/create-document.js.map +1 -1
- package/dist/tools/delete-document.d.ts +25 -0
- package/dist/tools/delete-document.js +49 -0
- package/dist/tools/delete-document.js.map +1 -0
- package/dist/tools/find-document.d.ts +33 -0
- package/dist/tools/find-document.js +97 -0
- package/dist/tools/find-document.js.map +1 -0
- package/dist/tools/find-global.d.ts +26 -0
- package/dist/tools/find-global.js +122 -0
- package/dist/tools/find-global.js.map +1 -0
- package/dist/tools/global-versions.d.ts +39 -0
- package/dist/tools/global-versions.js +132 -0
- package/dist/tools/global-versions.js.map +1 -0
- package/dist/tools/patch-global-layout.d.ts +31 -0
- package/dist/tools/patch-global-layout.js +127 -0
- package/dist/tools/patch-global-layout.js.map +1 -0
- package/dist/tools/patch-layout.d.ts +5 -8
- package/dist/tools/patch-layout.js +18 -100
- package/dist/tools/patch-layout.js.map +1 -1
- package/dist/tools/publish-draft.d.ts +5 -4
- package/dist/tools/publish-draft.js +39 -2
- package/dist/tools/publish-draft.js.map +1 -1
- package/dist/tools/publish-global-draft.d.ts +20 -0
- package/dist/tools/publish-global-draft.js +79 -0
- package/dist/tools/publish-global-draft.js.map +1 -0
- package/dist/tools/resolve-reference.d.ts +5 -4
- package/dist/tools/resolve-reference.js +4 -0
- package/dist/tools/resolve-reference.js.map +1 -1
- package/dist/tools/safe-delete.d.ts +5 -5
- package/dist/tools/safe-delete.js +20 -15
- package/dist/tools/safe-delete.js.map +1 -1
- package/dist/tools/schedule-publish.d.ts +5 -5
- package/dist/tools/schedule-publish.js +23 -19
- package/dist/tools/schedule-publish.js.map +1 -1
- package/dist/tools/search-content.d.ts +5 -9
- package/dist/tools/search-content.js +16 -12
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/update-document.d.ts +5 -5
- package/dist/tools/update-document.js +10 -5
- package/dist/tools/update-document.js.map +1 -1
- package/dist/tools/update-global.d.ts +27 -0
- package/dist/tools/update-global.js +72 -0
- package/dist/tools/update-global.js.map +1 -0
- package/dist/tools/upload-media.d.ts +5 -4
- package/dist/tools/upload-media.js +6 -1
- package/dist/tools/upload-media.js.map +1 -1
- package/dist/tools/versions.d.ts +10 -9
- package/dist/tools/versions.js +15 -7
- package/dist/tools/versions.js.map +1 -1
- package/dist/types.d.ts +56 -3
- package/dist/types.js +13 -6
- package/dist/types.js.map +1 -1
- package/package.json +39 -18
- package/dist/__tests__/introspection.test.js +0 -459
- package/dist/__tests__/introspection.test.js.map +0 -1
- package/dist/__tests__/url-validator.test.js +0 -326
- package/dist/__tests__/url-validator.test.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { introspectCollections, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph } from './introspection';
|
|
1
|
+
import { introspectCollections, introspectGlobals, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph } from './introspection';
|
|
3
2
|
import { generatePrompts } from './prompts';
|
|
4
3
|
import { generateResources } from './resources';
|
|
5
|
-
import {
|
|
4
|
+
import { computeDraftCollections, computeDraftGlobals } from './draft-workflow';
|
|
5
|
+
import { createApiKeysCollection, API_KEYS_DEFAULT_SLUG } from './api-keys';
|
|
6
|
+
import { createBearerStrategy } from './auth-strategy';
|
|
7
|
+
import { createMcpEndpoints } from './endpoint';
|
|
8
|
+
import { createInitializeServer } from './registry';
|
|
9
|
+
import { assertNoSlugConflict, assertNoUpstreamPlugin } from './conflict-detection';
|
|
6
10
|
import { createCreateDocumentTool } from './tools/create-document';
|
|
11
|
+
import { createDeleteDocumentTool } from './tools/delete-document';
|
|
12
|
+
import { createFindDocumentTool } from './tools/find-document';
|
|
13
|
+
import { createFindGlobalTool } from './tools/find-global';
|
|
14
|
+
import { createUpdateGlobalTool } from './tools/update-global';
|
|
15
|
+
import { createPatchGlobalLayoutTool } from './tools/patch-global-layout';
|
|
16
|
+
import { createPublishGlobalDraftTool } from './tools/publish-global-draft';
|
|
17
|
+
import { createListGlobalVersionsTool, createRestoreGlobalVersionTool } from './tools/global-versions';
|
|
7
18
|
import { createPatchLayoutTool } from './tools/patch-layout';
|
|
8
19
|
import { createPublishDraftTool } from './tools/publish-draft';
|
|
9
20
|
import { createResolveReferenceTool } from './tools/resolve-reference';
|
|
@@ -13,53 +24,102 @@ import { createSearchContentTool } from './tools/search-content';
|
|
|
13
24
|
import { createUpdateDocumentTool } from './tools/update-document';
|
|
14
25
|
import { createUploadMediaTool } from './tools/upload-media';
|
|
15
26
|
import { createListVersionsTool, createRestoreVersionTool } from './tools/versions';
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the user collection slug for API-key linkage.
|
|
29
|
+
*
|
|
30
|
+
* Resolution order: explicit `options.apiKeyCollection.userCollection` →
|
|
31
|
+
* explicit `options.userCollection` → `incomingConfig.admin.user` →
|
|
32
|
+
* 'users' as a last resort.
|
|
33
|
+
*/ function resolveUserCollection(options, incomingConfig) {
|
|
34
|
+
return options.apiKeyCollection?.userCollection ?? options.userCollection ?? incomingConfig.admin?.user ?? 'users';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* payload-mcp-toolkit — standalone Payload v3 MCP plugin.
|
|
38
|
+
*
|
|
39
|
+
* Owns the `/api/mcp` endpoint, the `payload-mcp-api-keys` collection,
|
|
40
|
+
* bearer authentication via Payload's `auth.strategies` extension point,
|
|
41
|
+
* and the per-tool scope check. Upstream `@payloadcms/plugin-mcp` is no
|
|
42
|
+
* longer required (and is incompatible — see `assertNoUpstreamPlugin`).
|
|
43
|
+
*
|
|
44
|
+
* Zero-config usage:
|
|
45
|
+
* ```ts
|
|
46
|
+
* plugins: [mcpToolkitPlugin()]
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* See `ContentToolkitOptions` for the (entirely optional) escape hatches.
|
|
50
|
+
*/ export function mcpToolkitPlugin(options = {}) {
|
|
31
51
|
return (incomingConfig)=>{
|
|
52
|
+
const apiKeysSlug = options.apiKeyCollection?.slug ?? API_KEYS_DEFAULT_SLUG;
|
|
53
|
+
// Conflict detection — fail fast with actionable messages.
|
|
54
|
+
assertNoUpstreamPlugin(incomingConfig.plugins);
|
|
55
|
+
assertNoSlugConflict(incomingConfig.collections, apiKeysSlug);
|
|
32
56
|
const collections = incomingConfig.collections ?? [];
|
|
57
|
+
const globals = incomingConfig.globals ?? [];
|
|
33
58
|
const allBlocks = incomingConfig.blocks ?? [];
|
|
34
59
|
const collectionSchemas = introspectCollections(collections);
|
|
60
|
+
const globalSchemas = introspectGlobals(globals);
|
|
35
61
|
const blockCatalog = introspectBlocks(allBlocks);
|
|
36
|
-
const blockNesting = buildBlockNestingMap(collections, allBlocks);
|
|
37
62
|
const relationships = buildRelationshipGraph(collectionSchemas);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const prompts = generatePrompts(collectionSchemas, blockCatalog, blockNesting, relationships, options.domainPrompts);
|
|
42
|
-
const resources = generateResources(collectionSchemas, blockCatalog, blockNesting, relationships);
|
|
43
|
-
const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {
|
|
44
|
-
siteUrl: previewSiteUrl,
|
|
63
|
+
const previewSiteUrl = options.preview?.disabled ? undefined : options.preview?.siteUrl ?? incomingConfig.serverURL ?? process.env.NEXT_PUBLIC_SERVER_URL ?? process.env.SITE_URL;
|
|
64
|
+
const previewDisabled = options.preview?.disabled === true;
|
|
65
|
+
const { draftCollections, excluded } = computeDraftCollections(collections, {
|
|
45
66
|
draftBehavior: options.draftBehavior,
|
|
46
67
|
excludeCollections: options.exclude?.collections,
|
|
47
|
-
|
|
68
|
+
apiKeysSlug
|
|
48
69
|
});
|
|
49
|
-
const
|
|
70
|
+
const { draftGlobals, excluded: excludedGlobals } = computeDraftGlobals(globals, {
|
|
71
|
+
draftBehavior: options.draftBehavior,
|
|
72
|
+
excludeGlobals: options.exclude?.globals
|
|
73
|
+
});
|
|
74
|
+
// Build blockNesting from exclusion-filtered inputs so excluded
|
|
75
|
+
// collections/globals never appear in patchLayout/patchGlobalLayout slug
|
|
76
|
+
// enums or in the `blocks://nesting` resource body.
|
|
77
|
+
const exposedCollectionsForNesting = collections.filter((c)=>!excluded.has(c.slug));
|
|
78
|
+
const exposedGlobalsForNesting = globals.filter((g)=>!excludedGlobals.has(g.slug));
|
|
79
|
+
const blockNesting = buildBlockNestingMap(exposedCollectionsForNesting, exposedGlobalsForNesting, allBlocks);
|
|
80
|
+
// Build a slug → CollectionConfig map for tools that need access to
|
|
81
|
+
// collection-level admin config (preview functions, etc).
|
|
82
|
+
const collectionsBySlug = new Map();
|
|
83
|
+
for (const c of collections){
|
|
84
|
+
if (!excluded.has(c.slug)) collectionsBySlug.set(c.slug, c);
|
|
85
|
+
}
|
|
86
|
+
// Schemas-without-excluded view, used by the polymorphic tool factories
|
|
87
|
+
// so excluded collections don't appear in tool descriptions.
|
|
88
|
+
const exposedSchemas = new Map();
|
|
50
89
|
for (const [slug, schema] of collectionSchemas){
|
|
90
|
+
if (!excluded.has(slug)) exposedSchemas.set(slug, schema);
|
|
91
|
+
}
|
|
92
|
+
// Same shape for globals — excluded slugs are stripped at registration
|
|
93
|
+
// time so they never reach Zod input enums, the globals://schema
|
|
94
|
+
// resource, or availableGlobals on the admin matrix. composeScopes
|
|
95
|
+
// stays exclusion-unaware (mirrors the collection mechanism).
|
|
96
|
+
const exposedGlobalSchemas = new Map();
|
|
97
|
+
const globalsBySlug = new Map();
|
|
98
|
+
for (const [slug, schema] of globalSchemas){
|
|
99
|
+
if (excludedGlobals.has(slug)) continue;
|
|
100
|
+
exposedGlobalSchemas.set(slug, schema);
|
|
101
|
+
}
|
|
102
|
+
for (const g of globals){
|
|
103
|
+
if (!excludedGlobals.has(g.slug)) globalsBySlug.set(g.slug, g);
|
|
104
|
+
}
|
|
105
|
+
const prompts = generatePrompts(exposedSchemas, blockCatalog, blockNesting, relationships, options.domainPrompts);
|
|
106
|
+
const resources = generateResources(exposedSchemas, blockCatalog, blockNesting, relationships, exposedGlobalSchemas);
|
|
107
|
+
const searchableCollections = new Map();
|
|
108
|
+
for (const [slug, schema] of exposedSchemas){
|
|
51
109
|
if (schema.searchableFields.length > 0) {
|
|
52
110
|
searchableCollections.set(slug, schema.searchableFields);
|
|
53
111
|
}
|
|
54
112
|
}
|
|
55
113
|
const tools = [
|
|
56
|
-
createCreateDocumentTool(
|
|
114
|
+
createCreateDocumentTool(exposedSchemas, draftCollections),
|
|
115
|
+
createDeleteDocumentTool(exposedSchemas),
|
|
116
|
+
createFindDocumentTool(exposedSchemas, draftCollections, collectionsBySlug, previewSiteUrl, previewDisabled),
|
|
57
117
|
createPatchLayoutTool(blockCatalog, blockNesting, draftCollections),
|
|
58
118
|
createPublishDraftTool(draftCollections),
|
|
59
119
|
createResolveReferenceTool(searchableCollections),
|
|
60
120
|
createSafeDeleteTool(relationships),
|
|
61
|
-
createSearchContentTool(
|
|
62
|
-
createUpdateDocumentTool(
|
|
121
|
+
createSearchContentTool(exposedSchemas),
|
|
122
|
+
createUpdateDocumentTool(exposedSchemas, draftCollections),
|
|
63
123
|
createUploadMediaTool({
|
|
64
124
|
maxFileSize: options.mediaUpload?.maxFileSize,
|
|
65
125
|
collectionSlug: options.mediaUpload?.collectionSlug
|
|
@@ -67,47 +127,85 @@ import { createListVersionsTool, createRestoreVersionTool } from './tools/versio
|
|
|
67
127
|
createListVersionsTool(draftCollections),
|
|
68
128
|
createRestoreVersionTool(draftCollections)
|
|
69
129
|
];
|
|
70
|
-
const schedulePublish = createSchedulePublishTool(
|
|
130
|
+
const schedulePublish = createSchedulePublishTool(exposedSchemas, draftCollections);
|
|
71
131
|
if (schedulePublish) tools.push(schedulePublish);
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
mcpGlobals[global.slug] = {
|
|
84
|
-
enabled: {
|
|
85
|
-
find: true,
|
|
86
|
-
update: false
|
|
87
|
-
},
|
|
88
|
-
description: `Read ${global.slug} global settings`
|
|
89
|
-
};
|
|
132
|
+
// Global tools — registered only when at least one global is exposed.
|
|
133
|
+
if (exposedGlobalSchemas.size > 0) {
|
|
134
|
+
tools.push(createFindGlobalTool(exposedGlobalSchemas, draftGlobals, globalsBySlug, previewSiteUrl, previewDisabled), createUpdateGlobalTool(exposedGlobalSchemas, draftGlobals));
|
|
135
|
+
const patchGlobalLayout = createPatchGlobalLayoutTool(blockCatalog, blockNesting, draftGlobals);
|
|
136
|
+
if (patchGlobalLayout) tools.push(patchGlobalLayout);
|
|
137
|
+
const publishGlobalDraft = createPublishGlobalDraftTool(draftGlobals);
|
|
138
|
+
if (publishGlobalDraft) tools.push(publishGlobalDraft);
|
|
139
|
+
const listGlobalVersions = createListGlobalVersionsTool(draftGlobals);
|
|
140
|
+
if (listGlobalVersions) tools.push(listGlobalVersions);
|
|
141
|
+
const restoreGlobalVersion = createRestoreGlobalVersionTool(draftGlobals);
|
|
142
|
+
if (restoreGlobalVersion) tools.push(restoreGlobalVersion);
|
|
90
143
|
}
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
144
|
+
// Build the per-request initializer that mcp-handler invokes.
|
|
145
|
+
const buildInitializeServer = createInitializeServer({
|
|
146
|
+
tools,
|
|
147
|
+
prompts: prompts,
|
|
148
|
+
resources: resources
|
|
149
|
+
});
|
|
150
|
+
// Attach API-keys collection. Available collections / tools are
|
|
151
|
+
// snapshotted now so the admin UI's scope dropdowns reflect the host
|
|
152
|
+
// config at boot time. Adding a collection requires a dev restart for
|
|
153
|
+
// it to surface as a scope option.
|
|
154
|
+
const userCollection = resolveUserCollection(options, incomingConfig);
|
|
155
|
+
const availableCollections = collections.map((c)=>c.slug).filter((s)=>s !== apiKeysSlug);
|
|
156
|
+
const availableGlobals = [
|
|
157
|
+
...exposedGlobalSchemas.keys()
|
|
158
|
+
];
|
|
159
|
+
const availableTools = tools.map((t)=>t.name);
|
|
160
|
+
const apiKeysCollection = createApiKeysCollection({
|
|
161
|
+
slug: apiKeysSlug,
|
|
162
|
+
userCollection,
|
|
163
|
+
availableCollections,
|
|
164
|
+
availableGlobals,
|
|
165
|
+
availableTools
|
|
166
|
+
});
|
|
167
|
+
const updatedCollections = [
|
|
168
|
+
...collections,
|
|
169
|
+
apiKeysCollection
|
|
170
|
+
];
|
|
171
|
+
// Attach the bearer strategy to the user collection's auth config.
|
|
172
|
+
const bearerStrategy = createBearerStrategy({
|
|
173
|
+
collectionSlug: apiKeysSlug,
|
|
174
|
+
userCollection
|
|
175
|
+
});
|
|
176
|
+
const collectionsWithStrategy = updatedCollections.map((c)=>{
|
|
177
|
+
if (c.slug !== userCollection) return c;
|
|
178
|
+
const existingAuth = c.auth;
|
|
179
|
+
if (!existingAuth) return c;
|
|
180
|
+
const authConfig = typeof existingAuth === 'object' && existingAuth !== null ? {
|
|
181
|
+
...existingAuth
|
|
182
|
+
} : {
|
|
183
|
+
useAPIKey: existingAuth === true ? false : false
|
|
184
|
+
};
|
|
185
|
+
const existingStrategies = Array.isArray(authConfig.strategies) ? authConfig.strategies : [];
|
|
186
|
+
authConfig.strategies = [
|
|
187
|
+
...existingStrategies,
|
|
188
|
+
bearerStrategy
|
|
189
|
+
];
|
|
190
|
+
return {
|
|
191
|
+
...c,
|
|
192
|
+
auth: authConfig
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
// Attach the MCP endpoints additively to the host config.
|
|
196
|
+
const mcpEndpoints = createMcpEndpoints({
|
|
197
|
+
buildInitializeServer,
|
|
198
|
+
allowedOrigins: options.auth?.allowedOrigins,
|
|
199
|
+
serverURL: incomingConfig.serverURL
|
|
109
200
|
});
|
|
110
|
-
return
|
|
201
|
+
return {
|
|
202
|
+
...incomingConfig,
|
|
203
|
+
collections: collectionsWithStrategy,
|
|
204
|
+
endpoints: [
|
|
205
|
+
...incomingConfig.endpoints ?? [],
|
|
206
|
+
...mcpEndpoints
|
|
207
|
+
]
|
|
208
|
+
};
|
|
111
209
|
};
|
|
112
210
|
}
|
|
113
211
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Block, CollectionConfig, Config, Plugin } from 'payload'\nimport { mcpPlugin } from '@payloadcms/plugin-mcp'\nimport type { ContentToolkitOptions } from './types'\nimport {\n introspectCollections,\n introspectBlocks,\n buildBlockNestingMap,\n buildRelationshipGraph,\n} from './introspection'\nimport { generatePrompts } from './prompts'\nimport { generateResources } from './resources'\nimport { generateMcpCollectionConfigs } from './draft-workflow'\nimport { createCreateDocumentTool } from './tools/create-document'\nimport { createPatchLayoutTool } from './tools/patch-layout'\nimport { createPublishDraftTool } from './tools/publish-draft'\nimport { createResolveReferenceTool } from './tools/resolve-reference'\nimport { createSafeDeleteTool } from './tools/safe-delete'\nimport { createSchedulePublishTool } from './tools/schedule-publish'\nimport { createSearchContentTool } from './tools/search-content'\nimport { createUpdateDocumentTool } from './tools/update-document'\nimport { createUploadMediaTool } from './tools/upload-media'\nimport { createListVersionsTool, createRestoreVersionTool } from './tools/versions'\n\n/**\n * Payload MCP Toolkit\n *\n * Layered on top of the official @payloadcms/plugin-mcp. The toolkit\n * introspects your Payload config and registers schema-aware prompts,\n * resources, and tools so AI clients can drive the CMS without\n * hand-built plumbing.\n *\n * Zero-config usage:\n * ```ts\n * plugins: [contentToolkitPlugin()]\n * ```\n *\n * Every option below is an optional escape hatch — see ContentToolkitOptions.\n */\nexport function contentToolkitPlugin(options: ContentToolkitOptions = {}): Plugin {\n return (incomingConfig: Config): Config => {\n const collections = (incomingConfig.collections ?? []) as CollectionConfig[]\n const allBlocks = (incomingConfig.blocks ?? []) as Block[]\n\n const collectionSchemas = introspectCollections(collections)\n const blockCatalog = introspectBlocks(allBlocks)\n const blockNesting = buildBlockNestingMap(collections, allBlocks)\n const relationships = buildRelationshipGraph(collectionSchemas)\n\n // Preview siteUrl resolves: explicit option → Payload serverURL → env vars.\n // May be undefined; relative-path preview URLs are skipped in that case.\n const previewSiteUrl =\n options.preview?.siteUrl ??\n incomingConfig.serverURL ??\n process.env.NEXT_PUBLIC_SERVER_URL ??\n process.env.SITE_URL\n\n const prompts = generatePrompts(\n collectionSchemas,\n blockCatalog,\n blockNesting,\n relationships,\n options.domainPrompts,\n )\n const resources = generateResources(\n collectionSchemas,\n blockCatalog,\n blockNesting,\n relationships,\n )\n\n const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {\n siteUrl: previewSiteUrl,\n draftBehavior: options.draftBehavior,\n excludeCollections: options.exclude?.collections,\n previewDisabled: options.preview?.disabled,\n })\n\n const searchableCollections = new Map<string, string[]>()\n for (const [slug, schema] of collectionSchemas) {\n if (schema.searchableFields.length > 0) {\n searchableCollections.set(slug, schema.searchableFields)\n }\n }\n\n const tools: any[] = [\n createCreateDocumentTool(collectionSchemas, draftCollections),\n createPatchLayoutTool(blockCatalog, blockNesting, draftCollections),\n createPublishDraftTool(draftCollections),\n createResolveReferenceTool(searchableCollections),\n createSafeDeleteTool(relationships),\n createSearchContentTool(collectionSchemas),\n createUpdateDocumentTool(collectionSchemas, draftCollections),\n createUploadMediaTool({\n maxFileSize: options.mediaUpload?.maxFileSize,\n collectionSlug: options.mediaUpload?.collectionSlug,\n }),\n createListVersionsTool(draftCollections),\n createRestoreVersionTool(draftCollections),\n ]\n\n const schedulePublish = createSchedulePublishTool(collectionSchemas, draftCollections)\n if (schedulePublish) tools.push(schedulePublish)\n\n // Globals get `find` only. The official plugin's `update<Global>` tool\n // hits the same `convertCollectionSchemaToZod` path that crashes on\n // richText / upload / blocks fields (here it throws\n // `Cannot convert undefined or null to object` because globals/update.ts\n // calls `Object.entries(convertedFields.shape)` and the fallback\n // `z.record()` has no `.shape`). Until the toolkit's `updateDocument`\n // gains global support, edit globals via the admin panel.\n const mcpGlobals: Record<\n string,\n { enabled: { find: boolean; update: boolean }; description?: string }\n > = {}\n const excludeGlobalSlugs = new Set(options.exclude?.globals ?? [])\n for (const global of (incomingConfig.globals ?? []) as Array<{ slug: string }>) {\n if (excludeGlobalSlugs.has(global.slug)) continue\n mcpGlobals[global.slug] = {\n enabled: { find: true, update: false },\n description: `Read ${global.slug} global settings`,\n }\n }\n\n // overrideAuth rebinds req.user from the API key's linked user so our\n // custom tools' `overrideAccess: false` checks run against the right\n // identity. userCollection passthrough lets the official plugin fall\n // back to `incomingConfig.admin.user`.\n const withMcp = mcpPlugin({\n collections: mcpCollections as any,\n globals: mcpGlobals as any,\n userCollection: options.userCollection as any,\n mcp: {\n tools: tools as any[],\n prompts: prompts as any[],\n resources: resources as any[],\n },\n overrideAuth: async (req, getDefault) => {\n const settings = await getDefault()\n req.user = (settings as any).user\n return settings\n },\n })\n\n return withMcp(incomingConfig)\n }\n}\n\nexport type {\n ContentToolkitOptions,\n DomainPrompt,\n CollectionSchema,\n BlockCatalog,\n BlockSchema,\n BlockNestingMap,\n BlockNestingEdge,\n RelationshipEdge,\n FieldSchema,\n} from './types'\n"],"names":["mcpPlugin","introspectCollections","introspectBlocks","buildBlockNestingMap","buildRelationshipGraph","generatePrompts","generateResources","generateMcpCollectionConfigs","createCreateDocumentTool","createPatchLayoutTool","createPublishDraftTool","createResolveReferenceTool","createSafeDeleteTool","createSchedulePublishTool","createSearchContentTool","createUpdateDocumentTool","createUploadMediaTool","createListVersionsTool","createRestoreVersionTool","contentToolkitPlugin","options","incomingConfig","collections","allBlocks","blocks","collectionSchemas","blockCatalog","blockNesting","relationships","previewSiteUrl","preview","siteUrl","serverURL","process","env","NEXT_PUBLIC_SERVER_URL","SITE_URL","prompts","domainPrompts","resources","mcpCollections","draftCollections","draftBehavior","excludeCollections","exclude","previewDisabled","disabled","searchableCollections","Map","slug","schema","searchableFields","length","set","tools","maxFileSize","mediaUpload","collectionSlug","schedulePublish","push","mcpGlobals","excludeGlobalSlugs","Set","globals","global","has","enabled","find","update","description","withMcp","userCollection","mcp","overrideAuth","req","getDefault","settings","user"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Block, CollectionConfig, Config, GlobalConfig, Plugin } from 'payload'\r\nimport type { ContentToolkitOptions, GlobalSchema } from './types'\r\nimport {\r\n introspectCollections,\r\n introspectGlobals,\r\n introspectBlocks,\r\n buildBlockNestingMap,\r\n buildRelationshipGraph,\r\n} from './introspection'\r\nimport { generatePrompts } from './prompts'\r\nimport { generateResources } from './resources'\r\nimport { computeDraftCollections, computeDraftGlobals } from './draft-workflow'\r\nimport { createApiKeysCollection, API_KEYS_DEFAULT_SLUG } from './api-keys'\r\nimport { createBearerStrategy } from './auth-strategy'\r\nimport { createMcpEndpoints } from './endpoint'\r\nimport {\r\n createInitializeServer,\r\n type ToolFactoryOutput,\r\n} from './registry'\r\nimport { assertNoSlugConflict, assertNoUpstreamPlugin } from './conflict-detection'\r\nimport { createCreateDocumentTool } from './tools/create-document'\r\nimport { createDeleteDocumentTool } from './tools/delete-document'\r\nimport { createFindDocumentTool } from './tools/find-document'\r\nimport { createFindGlobalTool } from './tools/find-global'\r\nimport { createUpdateGlobalTool } from './tools/update-global'\r\nimport { createPatchGlobalLayoutTool } from './tools/patch-global-layout'\r\nimport { createPublishGlobalDraftTool } from './tools/publish-global-draft'\r\nimport {\r\n createListGlobalVersionsTool,\r\n createRestoreGlobalVersionTool,\r\n} from './tools/global-versions'\r\nimport { createPatchLayoutTool } from './tools/patch-layout'\r\nimport { createPublishDraftTool } from './tools/publish-draft'\r\nimport { createResolveReferenceTool } from './tools/resolve-reference'\r\nimport { createSafeDeleteTool } from './tools/safe-delete'\r\nimport { createSchedulePublishTool } from './tools/schedule-publish'\r\nimport { createSearchContentTool } from './tools/search-content'\r\nimport { createUpdateDocumentTool } from './tools/update-document'\r\nimport { createUploadMediaTool } from './tools/upload-media'\r\nimport { createListVersionsTool, createRestoreVersionTool } from './tools/versions'\r\n\r\n/**\r\n * Resolves the user collection slug for API-key linkage.\r\n *\r\n * Resolution order: explicit `options.apiKeyCollection.userCollection` →\r\n * explicit `options.userCollection` → `incomingConfig.admin.user` →\r\n * 'users' as a last resort.\r\n */\r\nfunction resolveUserCollection(\r\n options: ContentToolkitOptions,\r\n incomingConfig: Config,\r\n): string {\r\n return (\r\n options.apiKeyCollection?.userCollection ??\r\n options.userCollection ??\r\n (incomingConfig.admin?.user as string | undefined) ??\r\n 'users'\r\n )\r\n}\r\n\r\n/**\r\n * payload-mcp-toolkit — standalone Payload v3 MCP plugin.\r\n *\r\n * Owns the `/api/mcp` endpoint, the `payload-mcp-api-keys` collection,\r\n * bearer authentication via Payload's `auth.strategies` extension point,\r\n * and the per-tool scope check. Upstream `@payloadcms/plugin-mcp` is no\r\n * longer required (and is incompatible — see `assertNoUpstreamPlugin`).\r\n *\r\n * Zero-config usage:\r\n * ```ts\r\n * plugins: [mcpToolkitPlugin()]\r\n * ```\r\n *\r\n * See `ContentToolkitOptions` for the (entirely optional) escape hatches.\r\n */\r\nexport function mcpToolkitPlugin(options: ContentToolkitOptions = {}): Plugin {\r\n return (incomingConfig: Config): Config => {\r\n const apiKeysSlug = options.apiKeyCollection?.slug ?? API_KEYS_DEFAULT_SLUG\r\n\r\n // Conflict detection — fail fast with actionable messages.\r\n assertNoUpstreamPlugin(incomingConfig.plugins)\r\n assertNoSlugConflict(incomingConfig.collections as CollectionConfig[] | undefined, apiKeysSlug)\r\n\r\n const collections = (incomingConfig.collections ?? []) as CollectionConfig[]\r\n const globals = (incomingConfig.globals ?? []) as GlobalConfig[]\r\n const allBlocks = (incomingConfig.blocks ?? []) as Block[]\r\n\r\n const collectionSchemas = introspectCollections(collections)\r\n const globalSchemas = introspectGlobals(globals)\r\n const blockCatalog = introspectBlocks(allBlocks)\r\n const relationships = buildRelationshipGraph(collectionSchemas)\r\n\r\n const previewSiteUrl = options.preview?.disabled\r\n ? undefined\r\n : options.preview?.siteUrl ??\r\n incomingConfig.serverURL ??\r\n process.env.NEXT_PUBLIC_SERVER_URL ??\r\n process.env.SITE_URL\r\n const previewDisabled = options.preview?.disabled === true\r\n\r\n const { draftCollections, excluded } = computeDraftCollections(collections, {\r\n draftBehavior: options.draftBehavior,\r\n excludeCollections: options.exclude?.collections,\r\n apiKeysSlug,\r\n })\r\n\r\n const { draftGlobals, excluded: excludedGlobals } = computeDraftGlobals(globals, {\r\n draftBehavior: options.draftBehavior,\r\n excludeGlobals: options.exclude?.globals,\r\n })\r\n\r\n // Build blockNesting from exclusion-filtered inputs so excluded\r\n // collections/globals never appear in patchLayout/patchGlobalLayout slug\r\n // enums or in the `blocks://nesting` resource body.\r\n const exposedCollectionsForNesting = collections.filter((c) => !excluded.has(c.slug))\r\n const exposedGlobalsForNesting = globals.filter((g) => !excludedGlobals.has(g.slug))\r\n const blockNesting = buildBlockNestingMap(\r\n exposedCollectionsForNesting,\r\n exposedGlobalsForNesting,\r\n allBlocks,\r\n )\r\n\r\n // Build a slug → CollectionConfig map for tools that need access to\r\n // collection-level admin config (preview functions, etc).\r\n const collectionsBySlug = new Map<string, CollectionConfig>()\r\n for (const c of collections) {\r\n if (!excluded.has(c.slug)) collectionsBySlug.set(c.slug, c)\r\n }\r\n\r\n // Schemas-without-excluded view, used by the polymorphic tool factories\r\n // so excluded collections don't appear in tool descriptions.\r\n const exposedSchemas = new Map<string, ReturnType<typeof introspectCollections> extends Map<string, infer V> ? V : never>()\r\n for (const [slug, schema] of collectionSchemas) {\r\n if (!excluded.has(slug)) exposedSchemas.set(slug, schema)\r\n }\r\n\r\n // Same shape for globals — excluded slugs are stripped at registration\r\n // time so they never reach Zod input enums, the globals://schema\r\n // resource, or availableGlobals on the admin matrix. composeScopes\r\n // stays exclusion-unaware (mirrors the collection mechanism).\r\n const exposedGlobalSchemas = new Map<string, GlobalSchema>()\r\n const globalsBySlug = new Map<string, GlobalConfig>()\r\n for (const [slug, schema] of globalSchemas) {\r\n if (excludedGlobals.has(slug)) continue\r\n exposedGlobalSchemas.set(slug, schema)\r\n }\r\n for (const g of globals) {\r\n if (!excludedGlobals.has(g.slug)) globalsBySlug.set(g.slug, g)\r\n }\r\n\r\n const prompts = generatePrompts(\r\n exposedSchemas,\r\n blockCatalog,\r\n blockNesting,\r\n relationships,\r\n options.domainPrompts,\r\n )\r\n const resources = generateResources(\r\n exposedSchemas,\r\n blockCatalog,\r\n blockNesting,\r\n relationships,\r\n exposedGlobalSchemas,\r\n )\r\n\r\n const searchableCollections = new Map<string, string[]>()\r\n for (const [slug, schema] of exposedSchemas) {\r\n if (schema.searchableFields.length > 0) {\r\n searchableCollections.set(slug, schema.searchableFields)\r\n }\r\n }\r\n\r\n const tools: ToolFactoryOutput[] = [\r\n createCreateDocumentTool(exposedSchemas, draftCollections),\r\n createDeleteDocumentTool(exposedSchemas),\r\n createFindDocumentTool(\r\n exposedSchemas,\r\n draftCollections,\r\n collectionsBySlug,\r\n previewSiteUrl,\r\n previewDisabled,\r\n ),\r\n createPatchLayoutTool(blockCatalog, blockNesting, draftCollections),\r\n createPublishDraftTool(draftCollections),\r\n createResolveReferenceTool(searchableCollections),\r\n createSafeDeleteTool(relationships),\r\n createSearchContentTool(exposedSchemas),\r\n createUpdateDocumentTool(exposedSchemas, draftCollections),\r\n createUploadMediaTool({\r\n maxFileSize: options.mediaUpload?.maxFileSize,\r\n collectionSlug: options.mediaUpload?.collectionSlug,\r\n }),\r\n createListVersionsTool(draftCollections),\r\n createRestoreVersionTool(draftCollections),\r\n ]\r\n\r\n const schedulePublish = createSchedulePublishTool(exposedSchemas, draftCollections)\r\n if (schedulePublish) tools.push(schedulePublish)\r\n\r\n // Global tools — registered only when at least one global is exposed.\r\n if (exposedGlobalSchemas.size > 0) {\r\n tools.push(\r\n createFindGlobalTool(\r\n exposedGlobalSchemas,\r\n draftGlobals,\r\n globalsBySlug,\r\n previewSiteUrl,\r\n previewDisabled,\r\n ),\r\n createUpdateGlobalTool(exposedGlobalSchemas, draftGlobals),\r\n )\r\n\r\n const patchGlobalLayout = createPatchGlobalLayoutTool(blockCatalog, blockNesting, draftGlobals)\r\n if (patchGlobalLayout) tools.push(patchGlobalLayout)\r\n\r\n const publishGlobalDraft = createPublishGlobalDraftTool(draftGlobals)\r\n if (publishGlobalDraft) tools.push(publishGlobalDraft)\r\n\r\n const listGlobalVersions = createListGlobalVersionsTool(draftGlobals)\r\n if (listGlobalVersions) tools.push(listGlobalVersions)\r\n\r\n const restoreGlobalVersion = createRestoreGlobalVersionTool(draftGlobals)\r\n if (restoreGlobalVersion) tools.push(restoreGlobalVersion)\r\n }\r\n\r\n // Build the per-request initializer that mcp-handler invokes.\r\n const buildInitializeServer = createInitializeServer({\r\n tools,\r\n prompts: prompts as never,\r\n resources: resources as never,\r\n })\r\n\r\n // Attach API-keys collection. Available collections / tools are\r\n // snapshotted now so the admin UI's scope dropdowns reflect the host\r\n // config at boot time. Adding a collection requires a dev restart for\r\n // it to surface as a scope option.\r\n const userCollection = resolveUserCollection(options, incomingConfig)\r\n const availableCollections = collections\r\n .map((c) => c.slug)\r\n .filter((s) => s !== apiKeysSlug)\r\n const availableGlobals = [...exposedGlobalSchemas.keys()]\r\n const availableTools = tools.map((t) => t.name)\r\n const apiKeysCollection = createApiKeysCollection({\r\n slug: apiKeysSlug,\r\n userCollection,\r\n availableCollections,\r\n availableGlobals,\r\n availableTools,\r\n })\r\n const updatedCollections: CollectionConfig[] = [...collections, apiKeysCollection]\r\n\r\n // Attach the bearer strategy to the user collection's auth config.\r\n const bearerStrategy = createBearerStrategy({\r\n collectionSlug: apiKeysSlug,\r\n userCollection,\r\n })\r\n const collectionsWithStrategy = updatedCollections.map((c) => {\r\n if (c.slug !== userCollection) return c\r\n const existingAuth = c.auth\r\n if (!existingAuth) return c\r\n const authConfig =\r\n typeof existingAuth === 'object' && existingAuth !== null\r\n ? { ...existingAuth }\r\n : { useAPIKey: existingAuth === true ? false : false }\r\n const existingStrategies = Array.isArray((authConfig as { strategies?: unknown[] }).strategies)\r\n ? ((authConfig as { strategies: unknown[] }).strategies as unknown[])\r\n : []\r\n ;(authConfig as { strategies: unknown[] }).strategies = [\r\n ...existingStrategies,\r\n bearerStrategy,\r\n ]\r\n return { ...c, auth: authConfig } as CollectionConfig\r\n })\r\n\r\n // Attach the MCP endpoints additively to the host config.\r\n const mcpEndpoints = createMcpEndpoints({\r\n buildInitializeServer,\r\n allowedOrigins: options.auth?.allowedOrigins,\r\n serverURL: incomingConfig.serverURL,\r\n })\r\n\r\n return {\r\n ...incomingConfig,\r\n collections: collectionsWithStrategy,\r\n endpoints: [...(incomingConfig.endpoints ?? []), ...mcpEndpoints],\r\n }\r\n }\r\n}\r\n\r\nexport type {\r\n ContentToolkitOptions,\r\n DomainPrompt,\r\n CollectionSchema,\r\n GlobalSchema,\r\n BlockCatalog,\r\n BlockSchema,\r\n BlockNestingMap,\r\n BlockNestingEdge,\r\n RelationshipEdge,\r\n FieldSchema,\r\n CollectionAction,\r\n GlobalAction,\r\n KeyScopes,\r\n ScopePreset,\r\n} from './types'\r\n"],"names":["introspectCollections","introspectGlobals","introspectBlocks","buildBlockNestingMap","buildRelationshipGraph","generatePrompts","generateResources","computeDraftCollections","computeDraftGlobals","createApiKeysCollection","API_KEYS_DEFAULT_SLUG","createBearerStrategy","createMcpEndpoints","createInitializeServer","assertNoSlugConflict","assertNoUpstreamPlugin","createCreateDocumentTool","createDeleteDocumentTool","createFindDocumentTool","createFindGlobalTool","createUpdateGlobalTool","createPatchGlobalLayoutTool","createPublishGlobalDraftTool","createListGlobalVersionsTool","createRestoreGlobalVersionTool","createPatchLayoutTool","createPublishDraftTool","createResolveReferenceTool","createSafeDeleteTool","createSchedulePublishTool","createSearchContentTool","createUpdateDocumentTool","createUploadMediaTool","createListVersionsTool","createRestoreVersionTool","resolveUserCollection","options","incomingConfig","apiKeyCollection","userCollection","admin","user","mcpToolkitPlugin","apiKeysSlug","slug","plugins","collections","globals","allBlocks","blocks","collectionSchemas","globalSchemas","blockCatalog","relationships","previewSiteUrl","preview","disabled","undefined","siteUrl","serverURL","process","env","NEXT_PUBLIC_SERVER_URL","SITE_URL","previewDisabled","draftCollections","excluded","draftBehavior","excludeCollections","exclude","draftGlobals","excludedGlobals","excludeGlobals","exposedCollectionsForNesting","filter","c","has","exposedGlobalsForNesting","g","blockNesting","collectionsBySlug","Map","set","exposedSchemas","schema","exposedGlobalSchemas","globalsBySlug","prompts","domainPrompts","resources","searchableCollections","searchableFields","length","tools","maxFileSize","mediaUpload","collectionSlug","schedulePublish","push","size","patchGlobalLayout","publishGlobalDraft","listGlobalVersions","restoreGlobalVersion","buildInitializeServer","availableCollections","map","s","availableGlobals","keys","availableTools","t","name","apiKeysCollection","updatedCollections","bearerStrategy","collectionsWithStrategy","existingAuth","auth","authConfig","useAPIKey","existingStrategies","Array","isArray","strategies","mcpEndpoints","allowedOrigins","endpoints"],"mappings":"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"}
|
package/dist/introspection.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Block, CollectionConfig } from 'payload';
|
|
2
|
-
import type { BlockCatalog, BlockNestingMap, CollectionSchema, RelationshipEdge } from './types';
|
|
1
|
+
import type { Block, CollectionConfig, GlobalConfig } from 'payload';
|
|
2
|
+
import type { BlockCatalog, BlockNestingMap, CollectionSchema, GlobalSchema, RelationshipEdge } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* True if the collection has Payload drafts enabled in its versions config.
|
|
5
5
|
*/
|
|
@@ -12,6 +12,20 @@ export declare function introspectCollection(collection: CollectionConfig): Coll
|
|
|
12
12
|
* Introspect all collections into a map keyed by slug.
|
|
13
13
|
*/
|
|
14
14
|
export declare function introspectCollections(collections: CollectionConfig[]): Map<string, CollectionSchema>;
|
|
15
|
+
/**
|
|
16
|
+
* True if the global has Payload drafts enabled in its versions config.
|
|
17
|
+
* Mirrors `hasCollectionDrafts` — globals use the same `versions.drafts`
|
|
18
|
+
* shape as collections.
|
|
19
|
+
*/
|
|
20
|
+
export declare function hasGlobalDrafts(global: GlobalConfig): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Introspect a Payload global config into structured metadata.
|
|
23
|
+
*/
|
|
24
|
+
export declare function introspectGlobal(global: GlobalConfig): GlobalSchema;
|
|
25
|
+
/**
|
|
26
|
+
* Introspect all globals into a map keyed by slug.
|
|
27
|
+
*/
|
|
28
|
+
export declare function introspectGlobals(globals: GlobalConfig[]): Map<string, GlobalSchema>;
|
|
15
29
|
/**
|
|
16
30
|
* Build a flat catalog of every block in the schema. Whether a block can
|
|
17
31
|
* nest other blocks is represented separately in the BlockNestingMap, not
|
|
@@ -27,7 +41,7 @@ export declare function introspectBlocks(blocks: Block[]): BlockCatalog;
|
|
|
27
41
|
* slugs the relevant field accepts, picks one, then if that block has
|
|
28
42
|
* its own `blocks` fields it recurses against the same map.
|
|
29
43
|
*/
|
|
30
|
-
export declare function buildBlockNestingMap(collections: CollectionConfig[],
|
|
44
|
+
export declare function buildBlockNestingMap(collections: CollectionConfig[], globalsOrBlocks: GlobalConfig[] | Block[], blocks?: Block[]): BlockNestingMap;
|
|
31
45
|
/**
|
|
32
46
|
* Build a relationship graph from introspected collection schemas.
|
|
33
47
|
*/
|
package/dist/introspection.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* True if the collection has Payload drafts enabled in its versions config.
|
|
1
|
+
/**
|
|
2
|
+
* True if the collection has Payload drafts enabled in its versions config.
|
|
3
3
|
*/ export function hasCollectionDrafts(collection) {
|
|
4
4
|
const versions = collection.versions;
|
|
5
5
|
return typeof versions === 'object' && versions !== null && 'drafts' in versions && Boolean(versions.drafts);
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Introspect a Payload collection config into structured metadata.
|
|
7
|
+
/**
|
|
8
|
+
* Introspect a Payload collection config into structured metadata.
|
|
9
9
|
*/ export function introspectCollection(collection) {
|
|
10
10
|
const fields = extractFields(collection.fields);
|
|
11
11
|
const relationships = extractRelationships(collection.fields);
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
searchableFields
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Introspect all collections into a map keyed by slug.
|
|
30
|
+
/**
|
|
31
|
+
* Introspect all collections into a map keyed by slug.
|
|
32
32
|
*/ export function introspectCollections(collections) {
|
|
33
33
|
const map = new Map();
|
|
34
34
|
for (const collection of collections){
|
|
@@ -36,11 +36,40 @@
|
|
|
36
36
|
}
|
|
37
37
|
return map;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* as
|
|
43
|
-
|
|
39
|
+
/**
|
|
40
|
+
* True if the global has Payload drafts enabled in its versions config.
|
|
41
|
+
* Mirrors `hasCollectionDrafts` — globals use the same `versions.drafts`
|
|
42
|
+
* shape as collections.
|
|
43
|
+
*/ export function hasGlobalDrafts(global) {
|
|
44
|
+
const versions = global.versions;
|
|
45
|
+
return typeof versions === 'object' && versions !== null && 'drafts' in versions && Boolean(versions.drafts);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Introspect a Payload global config into structured metadata.
|
|
49
|
+
*/ export function introspectGlobal(global) {
|
|
50
|
+
const fields = extractFields(global.fields);
|
|
51
|
+
const hasLivePreview = !!(global.admin && typeof global.admin === 'object' && 'livePreview' in global.admin && global.admin.livePreview);
|
|
52
|
+
return {
|
|
53
|
+
slug: global.slug,
|
|
54
|
+
fields,
|
|
55
|
+
hasDrafts: hasGlobalDrafts(global),
|
|
56
|
+
hasLivePreview
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Introspect all globals into a map keyed by slug.
|
|
61
|
+
*/ export function introspectGlobals(globals) {
|
|
62
|
+
const map = new Map();
|
|
63
|
+
for (const global of globals){
|
|
64
|
+
map.set(global.slug, introspectGlobal(global));
|
|
65
|
+
}
|
|
66
|
+
return map;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a flat catalog of every block in the schema. Whether a block can
|
|
70
|
+
* nest other blocks is represented separately in the BlockNestingMap, not
|
|
71
|
+
* as a section/leaf classification — the AI reads both and composes
|
|
72
|
+
* arbitrarily-nested layouts from there.
|
|
44
73
|
*/ export function introspectBlocks(blocks) {
|
|
45
74
|
const catalog = blocks.map((block)=>({
|
|
46
75
|
slug: block.slug,
|
|
@@ -50,15 +79,19 @@
|
|
|
50
79
|
blocks: catalog
|
|
51
80
|
};
|
|
52
81
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Walk every collection and every block, recording each `blocks`-typed
|
|
55
|
-
* field's owner, dotted path, and accepted slugs.
|
|
56
|
-
*
|
|
57
|
-
* The AI uses this to compose layouts at any depth: it looks up which
|
|
58
|
-
* slugs the relevant field accepts, picks one, then if that block has
|
|
59
|
-
* its own `blocks` fields it recurses against the same map.
|
|
60
|
-
*/ export function buildBlockNestingMap(collections, blocks) {
|
|
61
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Walk every collection and every block, recording each `blocks`-typed
|
|
84
|
+
* field's owner, dotted path, and accepted slugs.
|
|
85
|
+
*
|
|
86
|
+
* The AI uses this to compose layouts at any depth: it looks up which
|
|
87
|
+
* slugs the relevant field accepts, picks one, then if that block has
|
|
88
|
+
* its own `blocks` fields it recurses against the same map.
|
|
89
|
+
*/ export function buildBlockNestingMap(collections, globalsOrBlocks, blocks) {
|
|
90
|
+
// Back-compat overload: when called with two args, the second is `blocks`
|
|
91
|
+
// and there are no globals. The new shape is `(collections, globals, blocks)`.
|
|
92
|
+
const globals = blocks === undefined ? [] : globalsOrBlocks;
|
|
93
|
+
const effectiveBlocks = blocks === undefined ? globalsOrBlocks : blocks;
|
|
94
|
+
const knownSlugs = new Set(effectiveBlocks.map((b)=>b.slug));
|
|
62
95
|
const edges = [];
|
|
63
96
|
for (const collection of collections){
|
|
64
97
|
edges.push(...collectBlocksFieldEdges(collection.fields, {
|
|
@@ -68,7 +101,15 @@
|
|
|
68
101
|
knownSlugs
|
|
69
102
|
}));
|
|
70
103
|
}
|
|
71
|
-
for (const
|
|
104
|
+
for (const global of globals){
|
|
105
|
+
edges.push(...collectBlocksFieldEdges(global.fields, {
|
|
106
|
+
owner: global.slug,
|
|
107
|
+
ownerType: 'global',
|
|
108
|
+
prefix: '',
|
|
109
|
+
knownSlugs
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
for (const block of effectiveBlocks){
|
|
72
113
|
edges.push(...collectBlocksFieldEdges(block.fields, {
|
|
73
114
|
owner: block.slug,
|
|
74
115
|
ownerType: 'block',
|
|
@@ -76,10 +117,28 @@
|
|
|
76
117
|
knownSlugs
|
|
77
118
|
}));
|
|
78
119
|
}
|
|
120
|
+
assertBlockNestingMapInvariant(edges);
|
|
79
121
|
return edges;
|
|
80
122
|
}
|
|
81
|
-
/**
|
|
82
|
-
*
|
|
123
|
+
/**
|
|
124
|
+
* Guard against `(owner, fieldPath)` collisions across different `ownerType`
|
|
125
|
+
* values. Payload's own slug registry forbids collection/global slug
|
|
126
|
+
* collisions, but a defensive check here keeps the validator lookup in
|
|
127
|
+
* `patchLayout` / `patchGlobalLayout` unambiguous and surfaces config
|
|
128
|
+
* mistakes loudly instead of silently picking the first match.
|
|
129
|
+
*/ function assertBlockNestingMapInvariant(edges) {
|
|
130
|
+
const seen = new Map();
|
|
131
|
+
for (const edge of edges){
|
|
132
|
+
const key = `${edge.owner}.${edge.fieldPath}`;
|
|
133
|
+
const prior = seen.get(key);
|
|
134
|
+
if (prior && prior !== edge.ownerType) {
|
|
135
|
+
throw new Error(`[payload-mcp-toolkit] Block-nesting map invariant violated: ` + `"${key}" appears as both "${prior}" and "${edge.ownerType}". ` + `A collection and a global cannot share a slug; rename one.`);
|
|
136
|
+
}
|
|
137
|
+
seen.set(key, edge.ownerType);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build a relationship graph from introspected collection schemas.
|
|
83
142
|
*/ export function buildRelationshipGraph(schemas) {
|
|
84
143
|
const edges = [];
|
|
85
144
|
for (const [slug, schema] of schemas){
|
|
@@ -95,9 +154,9 @@
|
|
|
95
154
|
return edges;
|
|
96
155
|
}
|
|
97
156
|
// ─── Internal helpers ──────────────────────────────────────────────
|
|
98
|
-
/**
|
|
99
|
-
* Recursively extract field metadata from a Payload fields array.
|
|
100
|
-
* Handles tabs, groups, arrays, rows, and collapsible containers.
|
|
157
|
+
/**
|
|
158
|
+
* Recursively extract field metadata from a Payload fields array.
|
|
159
|
+
* Handles tabs, groups, arrays, rows, and collapsible containers.
|
|
101
160
|
*/ function extractFields(fields) {
|
|
102
161
|
const result = [];
|
|
103
162
|
for (const field of fields){
|
|
@@ -145,8 +204,8 @@
|
|
|
145
204
|
}
|
|
146
205
|
return result;
|
|
147
206
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Extract relationship metadata from fields (for the relationship graph).
|
|
207
|
+
/**
|
|
208
|
+
* Extract relationship metadata from fields (for the relationship graph).
|
|
150
209
|
*/ function extractRelationships(fields, prefix = '') {
|
|
151
210
|
const rels = [];
|
|
152
211
|
for (const field of fields){
|
|
@@ -187,10 +246,10 @@
|
|
|
187
246
|
}
|
|
188
247
|
return rels;
|
|
189
248
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Walk fields recording every `blocks`-typed field encountered, including
|
|
192
|
-
* those nested in tabs/rows/groups/arrays/collapsibles. Each entry carries
|
|
193
|
-
* the dotted path from the owner root to the field.
|
|
249
|
+
/**
|
|
250
|
+
* Walk fields recording every `blocks`-typed field encountered, including
|
|
251
|
+
* those nested in tabs/rows/groups/arrays/collapsibles. Each entry carries
|
|
252
|
+
* the dotted path from the owner root to the field.
|
|
194
253
|
*/ function collectBlocksFieldEdges(fields, ctx) {
|
|
195
254
|
const edges = [];
|
|
196
255
|
for (const field of fields){
|
|
@@ -249,10 +308,10 @@
|
|
|
249
308
|
}
|
|
250
309
|
return edges;
|
|
251
310
|
}
|
|
252
|
-
/**
|
|
253
|
-
* Read block slugs from a blocks-typed field, handling both resolved
|
|
254
|
-
* (field.blocks contains objects) and unresolved (field.blockReferences
|
|
255
|
-
* holds slug strings) forms.
|
|
311
|
+
/**
|
|
312
|
+
* Read block slugs from a blocks-typed field, handling both resolved
|
|
313
|
+
* (field.blocks contains objects) and unresolved (field.blockReferences
|
|
314
|
+
* holds slug strings) forms.
|
|
256
315
|
*/ function readBlockSlugs(field) {
|
|
257
316
|
const f = field;
|
|
258
317
|
if (Array.isArray(f.blocks) && f.blocks.length > 0 && typeof f.blocks[0] === 'object' && f.blocks[0]?.slug) {
|