payload-mcp-toolkit 0.3.0 → 0.3.2
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 +3 -3
- package/dist/draft-workflow.d.ts +5 -4
- package/dist/draft-workflow.js +15 -7
- package/dist/draft-workflow.js.map +1 -1
- package/dist/index.js +6 -19
- package/dist/index.js.map +1 -1
- package/dist/introspection.d.ts +4 -0
- package/dist/introspection.js +7 -2
- package/dist/introspection.js.map +1 -1
- package/dist/resources.js +1 -8
- package/dist/resources.js.map +1 -1
- package/dist/tools/_helpers.d.ts +14 -0
- package/dist/tools/_helpers.js +35 -0
- package/dist/tools/_helpers.js.map +1 -0
- package/dist/tools/patch-layout.d.ts +3 -9
- package/dist/tools/patch-layout.js +29 -48
- package/dist/tools/patch-layout.js.map +1 -1
- package/dist/tools/publish-draft.d.ts +1 -11
- package/dist/tools/publish-draft.js +8 -39
- package/dist/tools/publish-draft.js.map +1 -1
- package/dist/tools/resolve-reference.d.ts +1 -12
- package/dist/tools/resolve-reference.js +45 -85
- package/dist/tools/resolve-reference.js.map +1 -1
- package/dist/tools/safe-delete.d.ts +8 -13
- package/dist/tools/safe-delete.js +68 -100
- package/dist/tools/safe-delete.js.map +1 -1
- package/dist/tools/schedule-publish.d.ts +11 -21
- package/dist/tools/schedule-publish.js +18 -61
- package/dist/tools/schedule-publish.js.map +1 -1
- package/dist/tools/search-content.d.ts +1 -6
- package/dist/tools/search-content.js +52 -64
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/update-document.d.ts +4 -14
- package/dist/tools/update-document.js +23 -72
- package/dist/tools/update-document.js.map +1 -1
- package/dist/tools/upload-media.d.ts +1 -10
- package/dist/tools/upload-media.js +11 -54
- package/dist/tools/upload-media.js.map +1 -1
- package/dist/tools/versions.d.ts +7 -20
- package/dist/tools/versions.js +25 -82
- package/dist/tools/versions.js.map +1 -1
- package/package.json +1 -1
- package/dist/rate-limiter.d.ts +0 -25
- package/dist/rate-limiter.js +0 -51
- package/dist/rate-limiter.js.map +0 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ The official Payload MCP plugin gives every collection a generic CRUD surface. T
|
|
|
42
42
|
- `safeDelete` — relationship-aware delete. Walks the relationship graph, refuses with a structured impact summary if other documents reference the target. Fail-closed on permission errors. Override with `confirm: true`.
|
|
43
43
|
|
|
44
44
|
**Draft workflow** wired into the official plugin's `mcpCollections`:
|
|
45
|
-
-
|
|
45
|
+
- The official plugin's per-collection raw `update<Resource>` tool is disabled for every collection. Updates flow through `updateDocument` / `patchLayout` (local-API based), which preserve draft semantics for draftable collections and survive the upload-field / schema-conversion bugs in the official plugin's update path.
|
|
46
46
|
- Appends preview URLs to draft responses by calling each collection's own `admin.livePreview.url` or `admin.preview` function — no separate path config needed.
|
|
47
47
|
|
|
48
48
|
## Install
|
|
@@ -68,7 +68,7 @@ export default buildConfig({
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
That's it. The toolkit infers everything from your Payload config:
|
|
71
|
-
- **Draft behavior** — collections with `versions.drafts` get `always-draft` (
|
|
71
|
+
- **Draft behavior** — collections with `versions.drafts` get `always-draft` semantics (clients flow through `publishDraft` / `patchLayout` / `updateDocument`); others publish immediately. The official plugin's raw `update<Resource>` tool is disabled across the board — `updateDocument` replaces it.
|
|
72
72
|
- **Preview URLs** — pulled from each collection's `admin.livePreview.url` (or `admin.preview` as a fallback). If neither is set, draft responses just get a generic admin-panel hint.
|
|
73
73
|
- **Block nesting** — for every blocks-typed field, anywhere in the schema, the toolkit records which slugs are allowed. The AI composes layouts at any depth from that map.
|
|
74
74
|
- **Auth collection** — comes from `admin.user` (the standard Payload setting). The official plugin handles this directly.
|
|
@@ -84,7 +84,7 @@ contentToolkitPlugin({
|
|
|
84
84
|
disabled: false, // set true to suppress preview URLs entirely
|
|
85
85
|
},
|
|
86
86
|
draftBehavior: {
|
|
87
|
-
posts: 'always-publish', //
|
|
87
|
+
posts: 'always-publish', // publish immediately on update instead of saving a draft
|
|
88
88
|
},
|
|
89
89
|
userCollection: 'admins', // override admin.user
|
|
90
90
|
exclude: {
|
package/dist/draft-workflow.d.ts
CHANGED
|
@@ -47,10 +47,11 @@ export declare function getDraftBehavior(collection: CollectionConfig, options?:
|
|
|
47
47
|
* Generates the mcpCollections config object for the official mcpPlugin.
|
|
48
48
|
*
|
|
49
49
|
* For each non-excluded collection:
|
|
50
|
-
* -
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
50
|
+
* - Enables `find` / `create` / `delete`; disables the official plugin's
|
|
51
|
+
* raw `update<Resource>` tool universally. The toolkit's `updateDocument`
|
|
52
|
+
* and `patchLayout` cover updates via the local API (and survive the
|
|
53
|
+
* upload-field / schema-conversion bugs in the official plugin's update
|
|
54
|
+
* path). Draft semantics are preserved by `publishDraft`.
|
|
54
55
|
* - For draft collections: attaches an `overrideResponse` that appends a
|
|
55
56
|
* preview URL — sourced from the collection's own livePreview/preview
|
|
56
57
|
* function — to draft documents. Falls back to a generic admin-panel
|
package/dist/draft-workflow.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hasCollectionDrafts } from './introspection';
|
|
1
2
|
/**
|
|
2
3
|
* Determines the draft behavior for a collection.
|
|
3
4
|
*
|
|
@@ -5,8 +6,7 @@
|
|
|
5
6
|
* - Drafts configured + override given → use override
|
|
6
7
|
* - Drafts configured + no override → 'always-draft' (raw update locked)
|
|
7
8
|
*/ export function getDraftBehavior(collection, options) {
|
|
8
|
-
|
|
9
|
-
if (!hasDrafts) return 'publish';
|
|
9
|
+
if (!hasCollectionDrafts(collection)) return 'publish';
|
|
10
10
|
const override = options?.draftBehavior?.[collection.slug];
|
|
11
11
|
if (override) return override;
|
|
12
12
|
return 'always-draft';
|
|
@@ -91,10 +91,11 @@ function createOverrideResponse(collection, siteUrl) {
|
|
|
91
91
|
* Generates the mcpCollections config object for the official mcpPlugin.
|
|
92
92
|
*
|
|
93
93
|
* For each non-excluded collection:
|
|
94
|
-
* -
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
94
|
+
* - Enables `find` / `create` / `delete`; disables the official plugin's
|
|
95
|
+
* raw `update<Resource>` tool universally. The toolkit's `updateDocument`
|
|
96
|
+
* and `patchLayout` cover updates via the local API (and survive the
|
|
97
|
+
* upload-field / schema-conversion bugs in the official plugin's update
|
|
98
|
+
* path). Draft semantics are preserved by `publishDraft`.
|
|
98
99
|
* - For draft collections: attaches an `overrideResponse` that appends a
|
|
99
100
|
* preview URL — sourced from the collection's own livePreview/preview
|
|
100
101
|
* function — to draft documents. Falls back to a generic admin-panel
|
|
@@ -116,10 +117,17 @@ function createOverrideResponse(collection, siteUrl) {
|
|
|
116
117
|
if (behavior !== 'publish') {
|
|
117
118
|
draftCollections.add(collection.slug);
|
|
118
119
|
}
|
|
120
|
+
// Always disable the official plugin's per-collection `update<Resource>` tool.
|
|
121
|
+
// It calls `convertCollectionSchemaToZod` and crashes on any collection
|
|
122
|
+
// whose JSON schema can't be losslessly converted (richText, upload,
|
|
123
|
+
// blocks fields → fallback returns `z.record()`, then `.partial()` throws).
|
|
124
|
+
// The toolkit's `updateDocument` and `patchLayout` cover updates via the
|
|
125
|
+
// local API and survive the upload-field / schema-conversion bugs, so the
|
|
126
|
+
// raw update tool is redundant. See README "What it adds → updateDocument".
|
|
119
127
|
const enabled = {
|
|
120
128
|
find: true,
|
|
121
129
|
create: true,
|
|
122
|
-
update:
|
|
130
|
+
update: false,
|
|
123
131
|
delete: true
|
|
124
132
|
};
|
|
125
133
|
const config = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/draft-workflow.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest } from 'payload'\n\n/** MCP response shape used by overrideResponse */\ninterface McpResponse {\n content: Array<{ text: string; type: string }>\n}\n\n/** Per-collection MCP config with enabled operations and optional overrideResponse */\ninterface McpCollectionConfig {\n description: string\n enabled: {\n create: boolean\n delete: boolean\n find: boolean\n update: boolean\n }\n overrideResponse?: (\n response: McpResponse,\n doc: Record<string, unknown>,\n req: PayloadRequest,\n ) => McpResponse | Promise<McpResponse>\n}\n\ninterface GenerateOptions {\n /**\n * Optional absolute base URL prepended to relative preview paths returned\n * by the collection's own preview URL function. Resolved upstream from\n * (in order): `options.preview.siteUrl`, `incomingConfig.serverURL`,\n * `process.env.NEXT_PUBLIC_SERVER_URL`, `process.env.SITE_URL`. May be\n * undefined — relative-path returns will then be skipped.\n */\n siteUrl?: string\n /** Per-collection draft behavior overrides */\n draftBehavior?: Record<string, 'always-draft' | 'always-publish'>\n /** Collection slugs to exclude from MCP */\n excludeCollections?: string[]\n /** Disable preview URL injection entirely */\n previewDisabled?: boolean\n}\n\n/**\n * Determines the draft behavior for a collection.\n *\n * - No drafts configured → 'publish' (raw update allowed; no draft concept)\n * - Drafts configured + override given → use override\n * - Drafts configured + no override → 'always-draft' (raw update locked)\n */\nexport function getDraftBehavior(\n collection: CollectionConfig,\n options?: { draftBehavior?: Record<string, 'always-draft' | 'always-publish'> },\n): 'always-draft' | 'always-publish' | 'publish' {\n const hasDrafts =\n typeof collection.versions === 'object' &&\n collection.versions !== null &&\n 'drafts' in collection.versions &&\n Boolean(collection.versions.drafts)\n\n if (!hasDrafts) return 'publish'\n\n const override = options?.draftBehavior?.[collection.slug]\n if (override) return override\n\n return 'always-draft'\n}\n\n/**\n * Build a preview URL for a draft document by delegating to the collection's\n * own configured preview URL function. Tries `admin.livePreview.url` first\n * (the modern API), then `admin.preview` (the older `GeneratePreviewURL`).\n *\n * If neither is configured, or the function returns null/undefined/empty,\n * returns null and the override response will skip preview injection.\n */\nasync function resolvePreviewUrl(\n collection: CollectionConfig,\n doc: Record<string, unknown>,\n req: PayloadRequest,\n siteUrl: string | undefined,\n): Promise<string | null> {\n const admin = (collection.admin ?? {}) as Record<string, any>\n const locale = (req as any).locale ?? 'en'\n\n let raw: string | null | undefined\n\n const livePreviewUrl = admin.livePreview?.url\n if (typeof livePreviewUrl === 'function') {\n try {\n raw = await livePreviewUrl({\n data: doc,\n locale: { code: locale, label: locale },\n req,\n payload: req.payload,\n collectionConfig: collection as any,\n })\n } catch {\n raw = null\n }\n } else if (typeof livePreviewUrl === 'string') {\n raw = livePreviewUrl\n }\n\n if (!raw && typeof admin.preview === 'function') {\n try {\n raw = await admin.preview(doc, {\n locale,\n req,\n token: null,\n })\n } catch {\n raw = null\n }\n }\n\n if (!raw || typeof raw !== 'string') return null\n\n if (raw.startsWith('http://') || raw.startsWith('https://')) {\n return raw\n }\n\n if (!siteUrl) return null\n\n const base = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl\n const path = raw.startsWith('/') ? raw : `/${raw}`\n return `${base}${path}`\n}\n\nfunction createOverrideResponse(\n collection: CollectionConfig,\n siteUrl: string | undefined,\n): McpCollectionConfig['overrideResponse'] {\n return async (response, doc, req): Promise<McpResponse> => {\n if (doc._status !== 'draft') return response\n\n const previewUrl = await resolvePreviewUrl(collection, doc, req, siteUrl)\n if (!previewUrl) {\n return {\n content: [\n ...response.content,\n {\n type: 'text',\n text: '\\n📋 This document is a draft. Use the admin panel to preview it.',\n },\n ],\n }\n }\n\n return {\n content: [\n ...response.content,\n {\n type: 'text',\n text: `\\n📋 This document is a draft. Preview it here: ${previewUrl}`,\n },\n ],\n }\n }\n}\n\n/**\n * Generates the mcpCollections config object for the official mcpPlugin.\n *\n * For each non-excluded collection:\n * - Determines enabled CRUD operations based on draft behavior\n * - For 'always-draft' collections: disables raw `update` to force clients\n * through publishDraft / patchLayout / updateDocument (which preserve\n * draft semantics)\n * - For draft collections: attaches an `overrideResponse` that appends a\n * preview URL — sourced from the collection's own livePreview/preview\n * function — to draft documents. Falls back to a generic admin-panel\n * message when no preview function is configured.\n *\n * @returns Map of slug → MCP collection config, plus the set of draft slugs\n */\nexport function generateMcpCollectionConfigs(\n collections: CollectionConfig[],\n options: GenerateOptions,\n): {\n mcpCollections: Record<string, McpCollectionConfig>\n draftCollections: Set<string>\n} {\n const mcpCollections: Record<string, McpCollectionConfig> = {}\n const draftCollections = new Set<string>()\n\n const excludeSlugs = new Set([\n 'payload-mcp-api-keys',\n ...(options.excludeCollections ?? []),\n ])\n\n for (const collection of collections) {\n if (excludeSlugs.has(collection.slug)) continue\n\n // Auth-enabled collections are users — never expose them via MCP.\n if ((collection as any).auth) continue\n\n const behavior = getDraftBehavior(collection, options)\n\n if (behavior !== 'publish') {\n draftCollections.add(collection.slug)\n }\n\n const enabled = {\n find: true,\n create: true,\n update: behavior !== 'always-draft',\n delete: true,\n }\n\n const config: McpCollectionConfig = {\n description: `Manage ${collection.slug} content`,\n enabled,\n }\n\n if (draftCollections.has(collection.slug) && !options.previewDisabled) {\n config.overrideResponse = createOverrideResponse(collection, options.siteUrl)\n }\n\n mcpCollections[collection.slug] = config\n }\n\n return { mcpCollections, draftCollections }\n}\n"],"names":["getDraftBehavior","collection","options","hasDrafts","versions","Boolean","drafts","override","draftBehavior","slug","resolvePreviewUrl","doc","req","siteUrl","admin","locale","raw","livePreviewUrl","livePreview","url","data","code","label","payload","collectionConfig","preview","token","startsWith","base","endsWith","slice","path","createOverrideResponse","response","_status","previewUrl","content","type","text","generateMcpCollectionConfigs","collections","mcpCollections","draftCollections","Set","excludeSlugs","excludeCollections","has","auth","behavior","add","enabled","find","create","update","delete","config","description","previewDisabled","overrideResponse"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/draft-workflow.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest } from 'payload'\nimport { hasCollectionDrafts } from './introspection'\n\n/** MCP response shape used by overrideResponse */\ninterface McpResponse {\n content: Array<{ text: string; type: string }>\n}\n\n/** Per-collection MCP config with enabled operations and optional overrideResponse */\ninterface McpCollectionConfig {\n description: string\n enabled: {\n create: boolean\n delete: boolean\n find: boolean\n update: boolean\n }\n overrideResponse?: (\n response: McpResponse,\n doc: Record<string, unknown>,\n req: PayloadRequest,\n ) => McpResponse | Promise<McpResponse>\n}\n\ninterface GenerateOptions {\n /**\n * Optional absolute base URL prepended to relative preview paths returned\n * by the collection's own preview URL function. Resolved upstream from\n * (in order): `options.preview.siteUrl`, `incomingConfig.serverURL`,\n * `process.env.NEXT_PUBLIC_SERVER_URL`, `process.env.SITE_URL`. May be\n * undefined — relative-path returns will then be skipped.\n */\n siteUrl?: string\n /** Per-collection draft behavior overrides */\n draftBehavior?: Record<string, 'always-draft' | 'always-publish'>\n /** Collection slugs to exclude from MCP */\n excludeCollections?: string[]\n /** Disable preview URL injection entirely */\n previewDisabled?: boolean\n}\n\n/**\n * Determines the draft behavior for a collection.\n *\n * - No drafts configured → 'publish' (raw update allowed; no draft concept)\n * - Drafts configured + override given → use override\n * - Drafts configured + no override → 'always-draft' (raw update locked)\n */\nexport function getDraftBehavior(\n collection: CollectionConfig,\n options?: { draftBehavior?: Record<string, 'always-draft' | 'always-publish'> },\n): 'always-draft' | 'always-publish' | 'publish' {\n if (!hasCollectionDrafts(collection)) return 'publish'\n\n const override = options?.draftBehavior?.[collection.slug]\n if (override) return override\n\n return 'always-draft'\n}\n\n/**\n * Build a preview URL for a draft document by delegating to the collection's\n * own configured preview URL function. Tries `admin.livePreview.url` first\n * (the modern API), then `admin.preview` (the older `GeneratePreviewURL`).\n *\n * If neither is configured, or the function returns null/undefined/empty,\n * returns null and the override response will skip preview injection.\n */\nasync function resolvePreviewUrl(\n collection: CollectionConfig,\n doc: Record<string, unknown>,\n req: PayloadRequest,\n siteUrl: string | undefined,\n): Promise<string | null> {\n const admin = (collection.admin ?? {}) as Record<string, any>\n const locale = (req as any).locale ?? 'en'\n\n let raw: string | null | undefined\n\n const livePreviewUrl = admin.livePreview?.url\n if (typeof livePreviewUrl === 'function') {\n try {\n raw = await livePreviewUrl({\n data: doc,\n locale: { code: locale, label: locale },\n req,\n payload: req.payload,\n collectionConfig: collection as any,\n })\n } catch {\n raw = null\n }\n } else if (typeof livePreviewUrl === 'string') {\n raw = livePreviewUrl\n }\n\n if (!raw && typeof admin.preview === 'function') {\n try {\n raw = await admin.preview(doc, {\n locale,\n req,\n token: null,\n })\n } catch {\n raw = null\n }\n }\n\n if (!raw || typeof raw !== 'string') return null\n\n if (raw.startsWith('http://') || raw.startsWith('https://')) {\n return raw\n }\n\n if (!siteUrl) return null\n\n const base = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl\n const path = raw.startsWith('/') ? raw : `/${raw}`\n return `${base}${path}`\n}\n\nfunction createOverrideResponse(\n collection: CollectionConfig,\n siteUrl: string | undefined,\n): McpCollectionConfig['overrideResponse'] {\n return async (response, doc, req): Promise<McpResponse> => {\n if (doc._status !== 'draft') return response\n\n const previewUrl = await resolvePreviewUrl(collection, doc, req, siteUrl)\n if (!previewUrl) {\n return {\n content: [\n ...response.content,\n {\n type: 'text',\n text: '\\n📋 This document is a draft. Use the admin panel to preview it.',\n },\n ],\n }\n }\n\n return {\n content: [\n ...response.content,\n {\n type: 'text',\n text: `\\n📋 This document is a draft. Preview it here: ${previewUrl}`,\n },\n ],\n }\n }\n}\n\n/**\n * Generates the mcpCollections config object for the official mcpPlugin.\n *\n * For each non-excluded collection:\n * - Enables `find` / `create` / `delete`; disables the official plugin's\n * raw `update<Resource>` tool universally. The toolkit's `updateDocument`\n * and `patchLayout` cover updates via the local API (and survive the\n * upload-field / schema-conversion bugs in the official plugin's update\n * path). Draft semantics are preserved by `publishDraft`.\n * - For draft collections: attaches an `overrideResponse` that appends a\n * preview URL — sourced from the collection's own livePreview/preview\n * function — to draft documents. Falls back to a generic admin-panel\n * message when no preview function is configured.\n *\n * @returns Map of slug → MCP collection config, plus the set of draft slugs\n */\nexport function generateMcpCollectionConfigs(\n collections: CollectionConfig[],\n options: GenerateOptions,\n): {\n mcpCollections: Record<string, McpCollectionConfig>\n draftCollections: Set<string>\n} {\n const mcpCollections: Record<string, McpCollectionConfig> = {}\n const draftCollections = new Set<string>()\n\n const excludeSlugs = new Set([\n 'payload-mcp-api-keys',\n ...(options.excludeCollections ?? []),\n ])\n\n for (const collection of collections) {\n if (excludeSlugs.has(collection.slug)) continue\n\n // Auth-enabled collections are users — never expose them via MCP.\n if ((collection as any).auth) continue\n\n const behavior = getDraftBehavior(collection, options)\n\n if (behavior !== 'publish') {\n draftCollections.add(collection.slug)\n }\n\n // Always disable the official plugin's per-collection `update<Resource>` tool.\n // It calls `convertCollectionSchemaToZod` and crashes on any collection\n // whose JSON schema can't be losslessly converted (richText, upload,\n // blocks fields → fallback returns `z.record()`, then `.partial()` throws).\n // The toolkit's `updateDocument` and `patchLayout` cover updates via the\n // local API and survive the upload-field / schema-conversion bugs, so the\n // raw update tool is redundant. See README \"What it adds → updateDocument\".\n const enabled = {\n find: true,\n create: true,\n update: false,\n delete: true,\n }\n\n const config: McpCollectionConfig = {\n description: `Manage ${collection.slug} content`,\n enabled,\n }\n\n if (draftCollections.has(collection.slug) && !options.previewDisabled) {\n config.overrideResponse = createOverrideResponse(collection, options.siteUrl)\n }\n\n mcpCollections[collection.slug] = config\n }\n\n return { mcpCollections, draftCollections }\n}\n"],"names":["hasCollectionDrafts","getDraftBehavior","collection","options","override","draftBehavior","slug","resolvePreviewUrl","doc","req","siteUrl","admin","locale","raw","livePreviewUrl","livePreview","url","data","code","label","payload","collectionConfig","preview","token","startsWith","base","endsWith","slice","path","createOverrideResponse","response","_status","previewUrl","content","type","text","generateMcpCollectionConfigs","collections","mcpCollections","draftCollections","Set","excludeSlugs","excludeCollections","has","auth","behavior","add","enabled","find","create","update","delete","config","description","previewDisabled","overrideResponse"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -30,33 +30,27 @@ import { createListVersionsTool, createRestoreVersionTool } from './tools/versio
|
|
|
30
30
|
return (incomingConfig)=>{
|
|
31
31
|
const collections = incomingConfig.collections ?? [];
|
|
32
32
|
const allBlocks = incomingConfig.blocks ?? [];
|
|
33
|
-
// 1. Schema introspection
|
|
34
33
|
const collectionSchemas = introspectCollections(collections);
|
|
35
34
|
const blockCatalog = introspectBlocks(allBlocks);
|
|
36
35
|
const blockNesting = buildBlockNestingMap(collections, allBlocks);
|
|
37
36
|
const relationships = buildRelationshipGraph(collectionSchemas);
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// that's fine; relative-path preview URLs are skipped in that case.
|
|
37
|
+
// Preview siteUrl resolves: explicit option → Payload serverURL → env vars.
|
|
38
|
+
// May be undefined; relative-path preview URLs are skipped in that case.
|
|
41
39
|
const previewSiteUrl = options.preview?.siteUrl ?? incomingConfig.serverURL ?? process.env.NEXT_PUBLIC_SERVER_URL ?? process.env.SITE_URL;
|
|
42
|
-
// 3. Generate prompts and resources
|
|
43
40
|
const prompts = generatePrompts(collectionSchemas, blockCatalog, blockNesting, relationships, options.domainPrompts);
|
|
44
41
|
const resources = generateResources(collectionSchemas, blockCatalog, blockNesting, relationships);
|
|
45
|
-
// 4. Build MCP collection configs (preview URL + draft behavior)
|
|
46
42
|
const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {
|
|
47
43
|
siteUrl: previewSiteUrl,
|
|
48
44
|
draftBehavior: options.draftBehavior,
|
|
49
45
|
excludeCollections: options.exclude?.collections,
|
|
50
46
|
previewDisabled: options.preview?.disabled
|
|
51
47
|
});
|
|
52
|
-
// 5. Build the searchable-fields map for resolveReference
|
|
53
48
|
const searchableCollections = new Map();
|
|
54
49
|
for (const [slug, schema] of collectionSchemas){
|
|
55
50
|
if (schema.searchableFields.length > 0) {
|
|
56
51
|
searchableCollections.set(slug, schema.searchableFields);
|
|
57
52
|
}
|
|
58
53
|
}
|
|
59
|
-
// 6. Tools
|
|
60
54
|
const tools = [
|
|
61
55
|
createPatchLayoutTool(blockCatalog, blockNesting, draftCollections),
|
|
62
56
|
createPublishDraftTool(draftCollections),
|
|
@@ -73,7 +67,6 @@ import { createListVersionsTool, createRestoreVersionTool } from './tools/versio
|
|
|
73
67
|
];
|
|
74
68
|
const schedulePublish = createSchedulePublishTool(collectionSchemas, draftCollections);
|
|
75
69
|
if (schedulePublish) tools.push(schedulePublish);
|
|
76
|
-
// 7. Globals — expose every non-excluded global with default capabilities
|
|
77
70
|
const mcpGlobals = {};
|
|
78
71
|
const excludeGlobalSlugs = new Set(options.exclude?.globals ?? []);
|
|
79
72
|
for (const global of incomingConfig.globals ?? []){
|
|
@@ -83,16 +76,10 @@ import { createListVersionsTool, createRestoreVersionTool } from './tools/versio
|
|
|
83
76
|
description: `Manage ${global.slug} global settings`
|
|
84
77
|
};
|
|
85
78
|
}
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// userCollection
|
|
89
|
-
// back to `incomingConfig.admin.user
|
|
90
|
-
// Payload way to declare your auth collection.
|
|
91
|
-
//
|
|
92
|
-
// overrideAuth: rebinds req.user from the API key's linked user so our
|
|
93
|
-
// custom tools' overrideAccess: false runs against the right identity.
|
|
94
|
-
// Safe — getDefault() throws inside the official plugin if the API key
|
|
95
|
-
// has no linked user, so settings.user is guaranteed here.
|
|
79
|
+
// overrideAuth rebinds req.user from the API key's linked user so our
|
|
80
|
+
// custom tools' `overrideAccess: false` checks run against the right
|
|
81
|
+
// identity. userCollection passthrough lets the official plugin fall
|
|
82
|
+
// back to `incomingConfig.admin.user`.
|
|
96
83
|
const withMcp = mcpPlugin({
|
|
97
84
|
collections: mcpCollections,
|
|
98
85
|
globals: mcpGlobals,
|
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 { 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
|
|
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 { 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 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 const mcpGlobals: Record<string, { enabled: boolean; description?: string }> = {}\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: true,\n description: `Manage ${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","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","description","withMcp","userCollection","mcp","overrideAuth","req","getDefault","settings","user"],"mappings":"AACA,SAASA,SAAS,QAAQ,yBAAwB;AAElD,SACEC,qBAAqB,EACrBC,gBAAgB,EAChBC,oBAAoB,EACpBC,sBAAsB,QACjB,kBAAiB;AACxB,SAASC,eAAe,QAAQ,YAAW;AAC3C,SAASC,iBAAiB,QAAQ,cAAa;AAC/C,SAASC,4BAA4B,QAAQ,mBAAkB;AAC/D,SAASC,qBAAqB,QAAQ,uBAAsB;AAC5D,SAASC,sBAAsB,QAAQ,wBAAuB;AAC9D,SAASC,0BAA0B,QAAQ,4BAA2B;AACtE,SAASC,oBAAoB,QAAQ,sBAAqB;AAC1D,SAASC,yBAAyB,QAAQ,2BAA0B;AACpE,SAASC,uBAAuB,QAAQ,yBAAwB;AAChE,SAASC,wBAAwB,QAAQ,0BAAyB;AAClE,SAASC,qBAAqB,QAAQ,uBAAsB;AAC5D,SAASC,sBAAsB,EAAEC,wBAAwB,QAAQ,mBAAkB;AAEnF;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,qBAAqBC,UAAiC,CAAC,CAAC;IACtE,OAAO,CAACC;QACN,MAAMC,cAAeD,eAAeC,WAAW,IAAI,EAAE;QACrD,MAAMC,YAAaF,eAAeG,MAAM,IAAI,EAAE;QAE9C,MAAMC,oBAAoBvB,sBAAsBoB;QAChD,MAAMI,eAAevB,iBAAiBoB;QACtC,MAAMI,eAAevB,qBAAqBkB,aAAaC;QACvD,MAAMK,gBAAgBvB,uBAAuBoB;QAE7C,4EAA4E;QAC5E,yEAAyE;QACzE,MAAMI,iBACJT,QAAQU,OAAO,EAAEC,WACjBV,eAAeW,SAAS,IACxBC,QAAQC,GAAG,CAACC,sBAAsB,IAClCF,QAAQC,GAAG,CAACE,QAAQ;QAEtB,MAAMC,UAAU/B,gBACdmB,mBACAC,cACAC,cACAC,eACAR,QAAQkB,aAAa;QAEvB,MAAMC,YAAYhC,kBAChBkB,mBACAC,cACAC,cACAC;QAGF,MAAM,EAAEY,cAAc,EAAEC,gBAAgB,EAAE,GAAGjC,6BAA6Bc,aAAa;YACrFS,SAASF;YACTa,eAAetB,QAAQsB,aAAa;YACpCC,oBAAoBvB,QAAQwB,OAAO,EAAEtB;YACrCuB,iBAAiBzB,QAAQU,OAAO,EAAEgB;QACpC;QAEA,MAAMC,wBAAwB,IAAIC;QAClC,KAAK,MAAM,CAACC,MAAMC,OAAO,IAAIzB,kBAAmB;YAC9C,IAAIyB,OAAOC,gBAAgB,CAACC,MAAM,GAAG,GAAG;gBACtCL,sBAAsBM,GAAG,CAACJ,MAAMC,OAAOC,gBAAgB;YACzD;QACF;QAEA,MAAMG,QAAe;YACnB7C,sBAAsBiB,cAAcC,cAAcc;YAClD/B,uBAAuB+B;YACvB9B,2BAA2BoC;YAC3BnC,qBAAqBgB;YACrBd,wBAAwBW;YACxBV,yBAAyBU,mBAAmBgB;YAC5CzB,sBAAsB;gBACpBuC,aAAanC,QAAQoC,WAAW,EAAED;gBAClCE,gBAAgBrC,QAAQoC,WAAW,EAAEC;YACvC;YACAxC,uBAAuBwB;YACvBvB,yBAAyBuB;SAC1B;QAED,MAAMiB,kBAAkB7C,0BAA0BY,mBAAmBgB;QACrE,IAAIiB,iBAAiBJ,MAAMK,IAAI,CAACD;QAEhC,MAAME,aAAyE,CAAC;QAChF,MAAMC,qBAAqB,IAAIC,IAAI1C,QAAQwB,OAAO,EAAEmB,WAAW,EAAE;QACjE,KAAK,MAAMC,UAAW3C,eAAe0C,OAAO,IAAI,EAAE,CAA8B;YAC9E,IAAIF,mBAAmBI,GAAG,CAACD,OAAOf,IAAI,GAAG;YACzCW,UAAU,CAACI,OAAOf,IAAI,CAAC,GAAG;gBACxBiB,SAAS;gBACTC,aAAa,CAAC,OAAO,EAAEH,OAAOf,IAAI,CAAC,gBAAgB,CAAC;YACtD;QACF;QAEA,sEAAsE;QACtE,qEAAqE;QACrE,qEAAqE;QACrE,uCAAuC;QACvC,MAAMmB,UAAUnE,UAAU;YACxBqB,aAAakB;YACbuB,SAASH;YACTS,gBAAgBjD,QAAQiD,cAAc;YACtCC,KAAK;gBACHhB,OAAOA;gBACPjB,SAASA;gBACTE,WAAWA;YACb;YACAgC,cAAc,OAAOC,KAAKC;gBACxB,MAAMC,WAAW,MAAMD;gBACvBD,IAAIG,IAAI,GAAG,AAACD,SAAiBC,IAAI;gBACjC,OAAOD;YACT;QACF;QAEA,OAAON,QAAQ/C;IACjB;AACF"}
|
package/dist/introspection.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Block, CollectionConfig } from 'payload';
|
|
2
2
|
import type { BlockCatalog, BlockNestingMap, CollectionSchema, RelationshipEdge } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* True if the collection has Payload drafts enabled in its versions config.
|
|
5
|
+
*/
|
|
6
|
+
export declare function hasCollectionDrafts(collection: CollectionConfig): boolean;
|
|
3
7
|
/**
|
|
4
8
|
* Introspect a Payload collection config into structured metadata.
|
|
5
9
|
*/
|
package/dist/introspection.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True if the collection has Payload drafts enabled in its versions config.
|
|
3
|
+
*/ export function hasCollectionDrafts(collection) {
|
|
4
|
+
const versions = collection.versions;
|
|
5
|
+
return typeof versions === 'object' && versions !== null && 'drafts' in versions && Boolean(versions.drafts);
|
|
6
|
+
}
|
|
1
7
|
/**
|
|
2
8
|
* Introspect a Payload collection config into structured metadata.
|
|
3
9
|
*/ export function introspectCollection(collection) {
|
|
@@ -11,12 +17,11 @@
|
|
|
11
17
|
'title',
|
|
12
18
|
'slug'
|
|
13
19
|
].includes(f.name)).map((f)=>f.name);
|
|
14
|
-
const hasDrafts = !!(collection.versions && typeof collection.versions === 'object' && 'drafts' in collection.versions && collection.versions.drafts);
|
|
15
20
|
const hasLivePreview = !!(collection.admin && typeof collection.admin === 'object' && 'livePreview' in collection.admin && collection.admin.livePreview);
|
|
16
21
|
return {
|
|
17
22
|
slug: collection.slug,
|
|
18
23
|
fields,
|
|
19
|
-
hasDrafts,
|
|
24
|
+
hasDrafts: hasCollectionDrafts(collection),
|
|
20
25
|
hasLivePreview,
|
|
21
26
|
relationships,
|
|
22
27
|
searchableFields
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/introspection.ts"],"sourcesContent":["import type { Block, CollectionConfig, Field } from 'payload'\nimport type {\n BlockCatalog,\n BlockNestingMap,\n BlockSchema,\n CollectionSchema,\n FieldSchema,\n RelationshipEdge,\n} from './types'\n\n/**\n * Introspect a Payload collection config into structured metadata.\n */\nexport function introspectCollection(collection: CollectionConfig): CollectionSchema {\n const fields = extractFields(collection.fields)\n const relationships = extractRelationships(collection.fields)\n const searchableFields = fields\n .filter((f) => ['text', 'email'].includes(f.type) && ['name', 'title', 'slug'].includes(f.name))\n .map((f) => f.name)\n\n const hasDrafts = !!(\n collection.versions &&\n typeof collection.versions === 'object' &&\n 'drafts' in collection.versions &&\n collection.versions.drafts\n )\n const hasLivePreview = !!(\n collection.admin &&\n typeof collection.admin === 'object' &&\n 'livePreview' in collection.admin &&\n collection.admin.livePreview\n )\n\n return {\n slug: collection.slug,\n fields,\n hasDrafts,\n hasLivePreview,\n relationships,\n searchableFields,\n }\n}\n\n/**\n * Introspect all collections into a map keyed by slug.\n */\nexport function introspectCollections(\n collections: CollectionConfig[],\n): Map<string, CollectionSchema> {\n const map = new Map<string, CollectionSchema>()\n for (const collection of collections) {\n map.set(collection.slug, introspectCollection(collection))\n }\n return map\n}\n\n/**\n * Build a flat catalog of every block in the schema. Whether a block can\n * nest other blocks is represented separately in the BlockNestingMap, not\n * as a section/leaf classification — the AI reads both and composes\n * arbitrarily-nested layouts from there.\n */\nexport function introspectBlocks(blocks: Block[]): BlockCatalog {\n const catalog: BlockSchema[] = blocks.map((block) => ({\n slug: block.slug,\n fields: extractFields(block.fields),\n }))\n return { blocks: catalog }\n}\n\n/**\n * Walk every collection and every block, recording each `blocks`-typed\n * field's owner, dotted path, and accepted slugs.\n *\n * The AI uses this to compose layouts at any depth: it looks up which\n * slugs the relevant field accepts, picks one, then if that block has\n * its own `blocks` fields it recurses against the same map.\n */\nexport function buildBlockNestingMap(\n collections: CollectionConfig[],\n blocks: Block[],\n): BlockNestingMap {\n const knownSlugs = new Set(blocks.map((b) => b.slug))\n const edges: BlockNestingMap = []\n\n for (const collection of collections) {\n edges.push(\n ...collectBlocksFieldEdges(collection.fields, {\n owner: collection.slug,\n ownerType: 'collection',\n prefix: '',\n knownSlugs,\n }),\n )\n }\n\n for (const block of blocks) {\n edges.push(\n ...collectBlocksFieldEdges(block.fields, {\n owner: block.slug,\n ownerType: 'block',\n prefix: '',\n knownSlugs,\n }),\n )\n }\n\n return edges\n}\n\n/**\n * Build a relationship graph from introspected collection schemas.\n */\nexport function buildRelationshipGraph(\n schemas: Map<string, CollectionSchema>,\n): RelationshipEdge[] {\n const edges: RelationshipEdge[] = []\n for (const [slug, schema] of schemas) {\n for (const rel of schema.relationships) {\n edges.push({\n fromCollection: slug,\n fieldName: rel.fieldName,\n toCollection: rel.relationTo,\n hasMany: rel.hasMany,\n })\n }\n }\n return edges\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────\n\n/**\n * Recursively extract field metadata from a Payload fields array.\n * Handles tabs, groups, arrays, rows, and collapsible containers.\n */\nfunction extractFields(fields: Field[]): FieldSchema[] {\n const result: FieldSchema[] = []\n\n for (const field of fields) {\n if ('name' in field && field.name) {\n const schema: FieldSchema = {\n name: field.name,\n type: field.type,\n }\n\n if ('required' in field && field.required) schema.required = true\n if ('hasMany' in field && field.hasMany) schema.hasMany = true\n if ('relationTo' in field && field.relationTo) {\n schema.relationTo = field.relationTo as string | string[]\n }\n if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows\n\n if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {\n schema.options = field.options.map((opt) =>\n typeof opt === 'string'\n ? { label: opt, value: opt }\n : { label: String(opt.label), value: String(opt.value) },\n )\n }\n\n if (field.type === 'array' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n if (field.type === 'group' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n\n result.push(schema)\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...extractFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Extract relationship metadata from fields (for the relationship graph).\n */\nfunction extractRelationships(\n fields: Field[],\n prefix = '',\n): Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> {\n const rels: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> = []\n\n for (const field of fields) {\n const fieldName = 'name' in field && field.name ? `${prefix}${field.name}` : prefix\n\n if (field.type === 'relationship' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string | string[],\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'upload' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string,\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n rels.push(...extractRelationships(tab.fields, prefix))\n }\n }\n }\n if (field.type === 'group' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}.`))\n }\n if (field.type === 'array' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}[].`))\n }\n if (field.type === 'row' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n }\n\n return rels\n}\n\ninterface NestingScanContext {\n owner: string\n ownerType: 'collection' | 'block'\n prefix: string\n knownSlugs: Set<string>\n}\n\n/**\n * Walk fields recording every `blocks`-typed field encountered, including\n * those nested in tabs/rows/groups/arrays/collapsibles. Each entry carries\n * the dotted path from the owner root to the field.\n */\nfunction collectBlocksFieldEdges(fields: Field[], ctx: NestingScanContext): BlockNestingMap {\n const edges: BlockNestingMap = []\n\n for (const field of fields) {\n if (field.type === 'blocks') {\n const fieldName = 'name' in field && field.name ? field.name : ''\n const fullPath = ctx.prefix ? `${ctx.prefix}.${fieldName}` : fieldName\n const allSlugs = readBlockSlugs(field as Field & { type: 'blocks' })\n const acceptedSlugs = allSlugs.filter((s) => ctx.knownSlugs.has(s))\n\n const edge: BlockNestingMap[number] = {\n owner: ctx.owner,\n ownerType: ctx.ownerType,\n fieldPath: fullPath,\n acceptedBlockSlugs: acceptedSlugs,\n }\n const maxRows = (field as Field & { type: 'blocks' }).maxRows\n if (typeof maxRows === 'number') edge.maxRows = maxRows\n edges.push(edge)\n continue\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if (!('fields' in tab)) continue\n const tabName = 'name' in tab && tab.name ? tab.name : ''\n const tabPrefix = tabName\n ? ctx.prefix\n ? `${ctx.prefix}.${tabName}`\n : tabName\n : ctx.prefix\n edges.push(...collectBlocksFieldEdges(tab.fields, { ...ctx, prefix: tabPrefix }))\n }\n continue\n }\n if (field.type === 'row' && 'fields' in field) {\n edges.push(...collectBlocksFieldEdges(field.fields, ctx))\n continue\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n edges.push(...collectBlocksFieldEdges(field.fields, ctx))\n continue\n }\n if (field.type === 'group' && 'fields' in field && 'name' in field && field.name) {\n const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}` : field.name\n edges.push(...collectBlocksFieldEdges(field.fields, { ...ctx, prefix: newPrefix }))\n continue\n }\n if (field.type === 'array' && 'fields' in field && 'name' in field && field.name) {\n const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}[]` : `${field.name}[]`\n edges.push(...collectBlocksFieldEdges(field.fields, { ...ctx, prefix: newPrefix }))\n continue\n }\n }\n\n return edges\n}\n\n/**\n * Read block slugs from a blocks-typed field, handling both resolved\n * (field.blocks contains objects) and unresolved (field.blockReferences\n * holds slug strings) forms.\n */\nfunction readBlockSlugs(field: Field & { type: 'blocks' }): string[] {\n const f = field as any\n\n if (\n Array.isArray(f.blocks) &&\n f.blocks.length > 0 &&\n typeof f.blocks[0] === 'object' &&\n f.blocks[0]?.slug\n ) {\n return f.blocks.map((b: { slug: string }) => b.slug)\n }\n\n if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {\n return f.blockReferences.filter((ref: unknown) => typeof ref === 'string') as string[]\n }\n\n return []\n}\n"],"names":["introspectCollection","collection","fields","extractFields","relationships","extractRelationships","searchableFields","filter","f","includes","type","name","map","hasDrafts","versions","drafts","hasLivePreview","admin","livePreview","slug","introspectCollections","collections","Map","set","introspectBlocks","blocks","catalog","block","buildBlockNestingMap","knownSlugs","Set","b","edges","push","collectBlocksFieldEdges","owner","ownerType","prefix","buildRelationshipGraph","schemas","schema","rel","fromCollection","fieldName","toCollection","relationTo","hasMany","result","field","required","maxRows","Array","isArray","options","opt","label","value","String","tab","tabs","rels","ctx","fullPath","allSlugs","readBlockSlugs","acceptedSlugs","s","has","edge","fieldPath","acceptedBlockSlugs","tabName","tabPrefix","newPrefix","length","blockReferences","ref"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/introspection.ts"],"sourcesContent":["import type { Block, CollectionConfig, Field } from 'payload'\nimport type {\n BlockCatalog,\n BlockNestingMap,\n BlockSchema,\n CollectionSchema,\n FieldSchema,\n RelationshipEdge,\n} from './types'\n\n/**\n * True if the collection has Payload drafts enabled in its versions config.\n */\nexport function hasCollectionDrafts(collection: CollectionConfig): boolean {\n const versions = collection.versions\n return (\n typeof versions === 'object' &&\n versions !== null &&\n 'drafts' in versions &&\n Boolean(versions.drafts)\n )\n}\n\n/**\n * Introspect a Payload collection config into structured metadata.\n */\nexport function introspectCollection(collection: CollectionConfig): CollectionSchema {\n const fields = extractFields(collection.fields)\n const relationships = extractRelationships(collection.fields)\n const searchableFields = fields\n .filter((f) => ['text', 'email'].includes(f.type) && ['name', 'title', 'slug'].includes(f.name))\n .map((f) => f.name)\n\n const hasLivePreview = !!(\n collection.admin &&\n typeof collection.admin === 'object' &&\n 'livePreview' in collection.admin &&\n collection.admin.livePreview\n )\n\n return {\n slug: collection.slug,\n fields,\n hasDrafts: hasCollectionDrafts(collection),\n hasLivePreview,\n relationships,\n searchableFields,\n }\n}\n\n/**\n * Introspect all collections into a map keyed by slug.\n */\nexport function introspectCollections(\n collections: CollectionConfig[],\n): Map<string, CollectionSchema> {\n const map = new Map<string, CollectionSchema>()\n for (const collection of collections) {\n map.set(collection.slug, introspectCollection(collection))\n }\n return map\n}\n\n/**\n * Build a flat catalog of every block in the schema. Whether a block can\n * nest other blocks is represented separately in the BlockNestingMap, not\n * as a section/leaf classification — the AI reads both and composes\n * arbitrarily-nested layouts from there.\n */\nexport function introspectBlocks(blocks: Block[]): BlockCatalog {\n const catalog: BlockSchema[] = blocks.map((block) => ({\n slug: block.slug,\n fields: extractFields(block.fields),\n }))\n return { blocks: catalog }\n}\n\n/**\n * Walk every collection and every block, recording each `blocks`-typed\n * field's owner, dotted path, and accepted slugs.\n *\n * The AI uses this to compose layouts at any depth: it looks up which\n * slugs the relevant field accepts, picks one, then if that block has\n * its own `blocks` fields it recurses against the same map.\n */\nexport function buildBlockNestingMap(\n collections: CollectionConfig[],\n blocks: Block[],\n): BlockNestingMap {\n const knownSlugs = new Set(blocks.map((b) => b.slug))\n const edges: BlockNestingMap = []\n\n for (const collection of collections) {\n edges.push(\n ...collectBlocksFieldEdges(collection.fields, {\n owner: collection.slug,\n ownerType: 'collection',\n prefix: '',\n knownSlugs,\n }),\n )\n }\n\n for (const block of blocks) {\n edges.push(\n ...collectBlocksFieldEdges(block.fields, {\n owner: block.slug,\n ownerType: 'block',\n prefix: '',\n knownSlugs,\n }),\n )\n }\n\n return edges\n}\n\n/**\n * Build a relationship graph from introspected collection schemas.\n */\nexport function buildRelationshipGraph(\n schemas: Map<string, CollectionSchema>,\n): RelationshipEdge[] {\n const edges: RelationshipEdge[] = []\n for (const [slug, schema] of schemas) {\n for (const rel of schema.relationships) {\n edges.push({\n fromCollection: slug,\n fieldName: rel.fieldName,\n toCollection: rel.relationTo,\n hasMany: rel.hasMany,\n })\n }\n }\n return edges\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────\n\n/**\n * Recursively extract field metadata from a Payload fields array.\n * Handles tabs, groups, arrays, rows, and collapsible containers.\n */\nfunction extractFields(fields: Field[]): FieldSchema[] {\n const result: FieldSchema[] = []\n\n for (const field of fields) {\n if ('name' in field && field.name) {\n const schema: FieldSchema = {\n name: field.name,\n type: field.type,\n }\n\n if ('required' in field && field.required) schema.required = true\n if ('hasMany' in field && field.hasMany) schema.hasMany = true\n if ('relationTo' in field && field.relationTo) {\n schema.relationTo = field.relationTo as string | string[]\n }\n if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows\n\n if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {\n schema.options = field.options.map((opt) =>\n typeof opt === 'string'\n ? { label: opt, value: opt }\n : { label: String(opt.label), value: String(opt.value) },\n )\n }\n\n if (field.type === 'array' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n if (field.type === 'group' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n\n result.push(schema)\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...extractFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Extract relationship metadata from fields (for the relationship graph).\n */\nfunction extractRelationships(\n fields: Field[],\n prefix = '',\n): Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> {\n const rels: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> = []\n\n for (const field of fields) {\n const fieldName = 'name' in field && field.name ? `${prefix}${field.name}` : prefix\n\n if (field.type === 'relationship' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string | string[],\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'upload' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string,\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n rels.push(...extractRelationships(tab.fields, prefix))\n }\n }\n }\n if (field.type === 'group' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}.`))\n }\n if (field.type === 'array' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}[].`))\n }\n if (field.type === 'row' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n }\n\n return rels\n}\n\ninterface NestingScanContext {\n owner: string\n ownerType: 'collection' | 'block'\n prefix: string\n knownSlugs: Set<string>\n}\n\n/**\n * Walk fields recording every `blocks`-typed field encountered, including\n * those nested in tabs/rows/groups/arrays/collapsibles. Each entry carries\n * the dotted path from the owner root to the field.\n */\nfunction collectBlocksFieldEdges(fields: Field[], ctx: NestingScanContext): BlockNestingMap {\n const edges: BlockNestingMap = []\n\n for (const field of fields) {\n if (field.type === 'blocks') {\n const fieldName = 'name' in field && field.name ? field.name : ''\n const fullPath = ctx.prefix ? `${ctx.prefix}.${fieldName}` : fieldName\n const allSlugs = readBlockSlugs(field as Field & { type: 'blocks' })\n const acceptedSlugs = allSlugs.filter((s) => ctx.knownSlugs.has(s))\n\n const edge: BlockNestingMap[number] = {\n owner: ctx.owner,\n ownerType: ctx.ownerType,\n fieldPath: fullPath,\n acceptedBlockSlugs: acceptedSlugs,\n }\n const maxRows = (field as Field & { type: 'blocks' }).maxRows\n if (typeof maxRows === 'number') edge.maxRows = maxRows\n edges.push(edge)\n continue\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if (!('fields' in tab)) continue\n const tabName = 'name' in tab && tab.name ? tab.name : ''\n const tabPrefix = tabName\n ? ctx.prefix\n ? `${ctx.prefix}.${tabName}`\n : tabName\n : ctx.prefix\n edges.push(...collectBlocksFieldEdges(tab.fields, { ...ctx, prefix: tabPrefix }))\n }\n continue\n }\n if (field.type === 'row' && 'fields' in field) {\n edges.push(...collectBlocksFieldEdges(field.fields, ctx))\n continue\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n edges.push(...collectBlocksFieldEdges(field.fields, ctx))\n continue\n }\n if (field.type === 'group' && 'fields' in field && 'name' in field && field.name) {\n const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}` : field.name\n edges.push(...collectBlocksFieldEdges(field.fields, { ...ctx, prefix: newPrefix }))\n continue\n }\n if (field.type === 'array' && 'fields' in field && 'name' in field && field.name) {\n const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}[]` : `${field.name}[]`\n edges.push(...collectBlocksFieldEdges(field.fields, { ...ctx, prefix: newPrefix }))\n continue\n }\n }\n\n return edges\n}\n\n/**\n * Read block slugs from a blocks-typed field, handling both resolved\n * (field.blocks contains objects) and unresolved (field.blockReferences\n * holds slug strings) forms.\n */\nfunction readBlockSlugs(field: Field & { type: 'blocks' }): string[] {\n const f = field as any\n\n if (\n Array.isArray(f.blocks) &&\n f.blocks.length > 0 &&\n typeof f.blocks[0] === 'object' &&\n f.blocks[0]?.slug\n ) {\n return f.blocks.map((b: { slug: string }) => b.slug)\n }\n\n if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {\n return f.blockReferences.filter((ref: unknown) => typeof ref === 'string') as string[]\n }\n\n return []\n}\n"],"names":["hasCollectionDrafts","collection","versions","Boolean","drafts","introspectCollection","fields","extractFields","relationships","extractRelationships","searchableFields","filter","f","includes","type","name","map","hasLivePreview","admin","livePreview","slug","hasDrafts","introspectCollections","collections","Map","set","introspectBlocks","blocks","catalog","block","buildBlockNestingMap","knownSlugs","Set","b","edges","push","collectBlocksFieldEdges","owner","ownerType","prefix","buildRelationshipGraph","schemas","schema","rel","fromCollection","fieldName","toCollection","relationTo","hasMany","result","field","required","maxRows","Array","isArray","options","opt","label","value","String","tab","tabs","rels","ctx","fullPath","allSlugs","readBlockSlugs","acceptedSlugs","s","has","edge","fieldPath","acceptedBlockSlugs","tabName","tabPrefix","newPrefix","length","blockReferences","ref"],"mappings":"AAUA;;CAEC,GACD,OAAO,SAASA,oBAAoBC,UAA4B;IAC9D,MAAMC,WAAWD,WAAWC,QAAQ;IACpC,OACE,OAAOA,aAAa,YACpBA,aAAa,QACb,YAAYA,YACZC,QAAQD,SAASE,MAAM;AAE3B;AAEA;;CAEC,GACD,OAAO,SAASC,qBAAqBJ,UAA4B;IAC/D,MAAMK,SAASC,cAAcN,WAAWK,MAAM;IAC9C,MAAME,gBAAgBC,qBAAqBR,WAAWK,MAAM;IAC5D,MAAMI,mBAAmBJ,OACtBK,MAAM,CAAC,CAACC,IAAM;YAAC;YAAQ;SAAQ,CAACC,QAAQ,CAACD,EAAEE,IAAI,KAAK;YAAC;YAAQ;YAAS;SAAO,CAACD,QAAQ,CAACD,EAAEG,IAAI,GAC7FC,GAAG,CAAC,CAACJ,IAAMA,EAAEG,IAAI;IAEpB,MAAME,iBAAiB,CAAC,CACtBhB,CAAAA,WAAWiB,KAAK,IAChB,OAAOjB,WAAWiB,KAAK,KAAK,YAC5B,iBAAiBjB,WAAWiB,KAAK,IACjCjB,WAAWiB,KAAK,CAACC,WAAW,AAAD;IAG7B,OAAO;QACLC,MAAMnB,WAAWmB,IAAI;QACrBd;QACAe,WAAWrB,oBAAoBC;QAC/BgB;QACAT;QACAE;IACF;AACF;AAEA;;CAEC,GACD,OAAO,SAASY,sBACdC,WAA+B;IAE/B,MAAMP,MAAM,IAAIQ;IAChB,KAAK,MAAMvB,cAAcsB,YAAa;QACpCP,IAAIS,GAAG,CAACxB,WAAWmB,IAAI,EAAEf,qBAAqBJ;IAChD;IACA,OAAOe;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASU,iBAAiBC,MAAe;IAC9C,MAAMC,UAAyBD,OAAOX,GAAG,CAAC,CAACa,QAAW,CAAA;YACpDT,MAAMS,MAAMT,IAAI;YAChBd,QAAQC,cAAcsB,MAAMvB,MAAM;QACpC,CAAA;IACA,OAAO;QAAEqB,QAAQC;IAAQ;AAC3B;AAEA;;;;;;;CAOC,GACD,OAAO,SAASE,qBACdP,WAA+B,EAC/BI,MAAe;IAEf,MAAMI,aAAa,IAAIC,IAAIL,OAAOX,GAAG,CAAC,CAACiB,IAAMA,EAAEb,IAAI;IACnD,MAAMc,QAAyB,EAAE;IAEjC,KAAK,MAAMjC,cAAcsB,YAAa;QACpCW,MAAMC,IAAI,IACLC,wBAAwBnC,WAAWK,MAAM,EAAE;YAC5C+B,OAAOpC,WAAWmB,IAAI;YACtBkB,WAAW;YACXC,QAAQ;YACRR;QACF;IAEJ;IAEA,KAAK,MAAMF,SAASF,OAAQ;QAC1BO,MAAMC,IAAI,IACLC,wBAAwBP,MAAMvB,MAAM,EAAE;YACvC+B,OAAOR,MAAMT,IAAI;YACjBkB,WAAW;YACXC,QAAQ;YACRR;QACF;IAEJ;IAEA,OAAOG;AACT;AAEA;;CAEC,GACD,OAAO,SAASM,uBACdC,OAAsC;IAEtC,MAAMP,QAA4B,EAAE;IACpC,KAAK,MAAM,CAACd,MAAMsB,OAAO,IAAID,QAAS;QACpC,KAAK,MAAME,OAAOD,OAAOlC,aAAa,CAAE;YACtC0B,MAAMC,IAAI,CAAC;gBACTS,gBAAgBxB;gBAChByB,WAAWF,IAAIE,SAAS;gBACxBC,cAAcH,IAAII,UAAU;gBAC5BC,SAASL,IAAIK,OAAO;YACtB;QACF;IACF;IACA,OAAOd;AACT;AAEA,sEAAsE;AAEtE;;;CAGC,GACD,SAAS3B,cAAcD,MAAe;IACpC,MAAM2C,SAAwB,EAAE;IAEhC,KAAK,MAAMC,SAAS5C,OAAQ;QAC1B,IAAI,UAAU4C,SAASA,MAAMnC,IAAI,EAAE;YACjC,MAAM2B,SAAsB;gBAC1B3B,MAAMmC,MAAMnC,IAAI;gBAChBD,MAAMoC,MAAMpC,IAAI;YAClB;YAEA,IAAI,cAAcoC,SAASA,MAAMC,QAAQ,EAAET,OAAOS,QAAQ,GAAG;YAC7D,IAAI,aAAaD,SAASA,MAAMF,OAAO,EAAEN,OAAOM,OAAO,GAAG;YAC1D,IAAI,gBAAgBE,SAASA,MAAMH,UAAU,EAAE;gBAC7CL,OAAOK,UAAU,GAAGG,MAAMH,UAAU;YACtC;YACA,IAAI,aAAaG,SAASA,MAAME,OAAO,EAAEV,OAAOU,OAAO,GAAGF,MAAME,OAAO;YAEvE,IAAIF,MAAMpC,IAAI,KAAK,YAAY,aAAaoC,SAASG,MAAMC,OAAO,CAACJ,MAAMK,OAAO,GAAG;gBACjFb,OAAOa,OAAO,GAAGL,MAAMK,OAAO,CAACvC,GAAG,CAAC,CAACwC,MAClC,OAAOA,QAAQ,WACX;wBAAEC,OAAOD;wBAAKE,OAAOF;oBAAI,IACzB;wBAAEC,OAAOE,OAAOH,IAAIC,KAAK;wBAAGC,OAAOC,OAAOH,IAAIE,KAAK;oBAAE;YAE7D;YAEA,IAAIR,MAAMpC,IAAI,KAAK,WAAW,YAAYoC,OAAO;gBAC/CR,OAAOpC,MAAM,GAAGC,cAAc2C,MAAM5C,MAAM;YAC5C;YACA,IAAI4C,MAAMpC,IAAI,KAAK,WAAW,YAAYoC,OAAO;gBAC/CR,OAAOpC,MAAM,GAAGC,cAAc2C,MAAM5C,MAAM;YAC5C;YAEA2C,OAAOd,IAAI,CAACO;QACd;QAEA,IAAIQ,MAAMpC,IAAI,KAAK,UAAU,UAAUoC,OAAO;YAC5C,KAAK,MAAMU,OAAOV,MAAMW,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBX,OAAOd,IAAI,IAAI5B,cAAcqD,IAAItD,MAAM;gBACzC;YACF;QACF;QACA,IAAI4C,MAAMpC,IAAI,KAAK,SAAS,YAAYoC,OAAO;YAC7CD,OAAOd,IAAI,IAAI5B,cAAc2C,MAAM5C,MAAM;QAC3C;QACA,IAAI4C,MAAMpC,IAAI,KAAK,iBAAiB,YAAYoC,OAAO;YACrDD,OAAOd,IAAI,IAAI5B,cAAc2C,MAAM5C,MAAM;QAC3C;IACF;IAEA,OAAO2C;AACT;AAEA;;CAEC,GACD,SAASxC,qBACPH,MAAe,EACfiC,SAAS,EAAE;IAEX,MAAMuB,OAAsF,EAAE;IAE9F,KAAK,MAAMZ,SAAS5C,OAAQ;QAC1B,MAAMuC,YAAY,UAAUK,SAASA,MAAMnC,IAAI,GAAG,GAAGwB,SAASW,MAAMnC,IAAI,EAAE,GAAGwB;QAE7E,IAAIW,MAAMpC,IAAI,KAAK,kBAAkB,gBAAgBoC,OAAO;YAC1DY,KAAK3B,IAAI,CAAC;gBACRU;gBACAE,YAAYG,MAAMH,UAAU;gBAC5BC,SAAS,CAAC,CAAE,CAAA,aAAaE,SAASA,MAAMF,OAAO,AAAD;YAChD;QACF;QAEA,IAAIE,MAAMpC,IAAI,KAAK,YAAY,gBAAgBoC,OAAO;YACpDY,KAAK3B,IAAI,CAAC;gBACRU;gBACAE,YAAYG,MAAMH,UAAU;gBAC5BC,SAAS,CAAC,CAAE,CAAA,aAAaE,SAASA,MAAMF,OAAO,AAAD;YAChD;QACF;QAEA,IAAIE,MAAMpC,IAAI,KAAK,UAAU,UAAUoC,OAAO;YAC5C,KAAK,MAAMU,OAAOV,MAAMW,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBE,KAAK3B,IAAI,IAAI1B,qBAAqBmD,IAAItD,MAAM,EAAEiC;gBAChD;YACF;QACF;QACA,IAAIW,MAAMpC,IAAI,KAAK,WAAW,YAAYoC,OAAO;YAC/CY,KAAK3B,IAAI,IAAI1B,qBAAqByC,MAAM5C,MAAM,EAAE,GAAGuC,UAAU,CAAC,CAAC;QACjE;QACA,IAAIK,MAAMpC,IAAI,KAAK,WAAW,YAAYoC,OAAO;YAC/CY,KAAK3B,IAAI,IAAI1B,qBAAqByC,MAAM5C,MAAM,EAAE,GAAGuC,UAAU,GAAG,CAAC;QACnE;QACA,IAAIK,MAAMpC,IAAI,KAAK,SAAS,YAAYoC,OAAO;YAC7CY,KAAK3B,IAAI,IAAI1B,qBAAqByC,MAAM5C,MAAM,EAAEiC;QAClD;QACA,IAAIW,MAAMpC,IAAI,KAAK,iBAAiB,YAAYoC,OAAO;YACrDY,KAAK3B,IAAI,IAAI1B,qBAAqByC,MAAM5C,MAAM,EAAEiC;QAClD;IACF;IAEA,OAAOuB;AACT;AASA;;;;CAIC,GACD,SAAS1B,wBAAwB9B,MAAe,EAAEyD,GAAuB;IACvE,MAAM7B,QAAyB,EAAE;IAEjC,KAAK,MAAMgB,SAAS5C,OAAQ;QAC1B,IAAI4C,MAAMpC,IAAI,KAAK,UAAU;YAC3B,MAAM+B,YAAY,UAAUK,SAASA,MAAMnC,IAAI,GAAGmC,MAAMnC,IAAI,GAAG;YAC/D,MAAMiD,WAAWD,IAAIxB,MAAM,GAAG,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEM,WAAW,GAAGA;YAC7D,MAAMoB,WAAWC,eAAehB;YAChC,MAAMiB,gBAAgBF,SAAStD,MAAM,CAAC,CAACyD,IAAML,IAAIhC,UAAU,CAACsC,GAAG,CAACD;YAEhE,MAAME,OAAgC;gBACpCjC,OAAO0B,IAAI1B,KAAK;gBAChBC,WAAWyB,IAAIzB,SAAS;gBACxBiC,WAAWP;gBACXQ,oBAAoBL;YACtB;YACA,MAAMf,UAAU,AAACF,MAAqCE,OAAO;YAC7D,IAAI,OAAOA,YAAY,UAAUkB,KAAKlB,OAAO,GAAGA;YAChDlB,MAAMC,IAAI,CAACmC;YACX;QACF;QAEA,IAAIpB,MAAMpC,IAAI,KAAK,UAAU,UAAUoC,OAAO;YAC5C,KAAK,MAAMU,OAAOV,MAAMW,IAAI,CAAE;gBAC5B,IAAI,CAAE,CAAA,YAAYD,GAAE,GAAI;gBACxB,MAAMa,UAAU,UAAUb,OAAOA,IAAI7C,IAAI,GAAG6C,IAAI7C,IAAI,GAAG;gBACvD,MAAM2D,YAAYD,UACdV,IAAIxB,MAAM,GACR,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEkC,SAAS,GAC1BA,UACFV,IAAIxB,MAAM;gBACdL,MAAMC,IAAI,IAAIC,wBAAwBwB,IAAItD,MAAM,EAAE;oBAAE,GAAGyD,GAAG;oBAAExB,QAAQmC;gBAAU;YAChF;YACA;QACF;QACA,IAAIxB,MAAMpC,IAAI,KAAK,SAAS,YAAYoC,OAAO;YAC7ChB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM5C,MAAM,EAAEyD;YACpD;QACF;QACA,IAAIb,MAAMpC,IAAI,KAAK,iBAAiB,YAAYoC,OAAO;YACrDhB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM5C,MAAM,EAAEyD;YACpD;QACF;QACA,IAAIb,MAAMpC,IAAI,KAAK,WAAW,YAAYoC,SAAS,UAAUA,SAASA,MAAMnC,IAAI,EAAE;YAChF,MAAM4D,YAAYZ,IAAIxB,MAAM,GAAG,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEW,MAAMnC,IAAI,EAAE,GAAGmC,MAAMnC,IAAI;YACzEmB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM5C,MAAM,EAAE;gBAAE,GAAGyD,GAAG;gBAAExB,QAAQoC;YAAU;YAChF;QACF;QACA,IAAIzB,MAAMpC,IAAI,KAAK,WAAW,YAAYoC,SAAS,UAAUA,SAASA,MAAMnC,IAAI,EAAE;YAChF,MAAM4D,YAAYZ,IAAIxB,MAAM,GAAG,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEW,MAAMnC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAGmC,MAAMnC,IAAI,CAAC,EAAE,CAAC;YAClFmB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM5C,MAAM,EAAE;gBAAE,GAAGyD,GAAG;gBAAExB,QAAQoC;YAAU;YAChF;QACF;IACF;IAEA,OAAOzC;AACT;AAEA;;;;CAIC,GACD,SAASgC,eAAehB,KAAiC;IACvD,MAAMtC,IAAIsC;IAEV,IACEG,MAAMC,OAAO,CAAC1C,EAAEe,MAAM,KACtBf,EAAEe,MAAM,CAACiD,MAAM,GAAG,KAClB,OAAOhE,EAAEe,MAAM,CAAC,EAAE,KAAK,YACvBf,EAAEe,MAAM,CAAC,EAAE,EAAEP,MACb;QACA,OAAOR,EAAEe,MAAM,CAACX,GAAG,CAAC,CAACiB,IAAwBA,EAAEb,IAAI;IACrD;IAEA,IAAIiC,MAAMC,OAAO,CAAC1C,EAAEiE,eAAe,KAAKjE,EAAEiE,eAAe,CAACD,MAAM,GAAG,GAAG;QACpE,OAAOhE,EAAEiE,eAAe,CAAClE,MAAM,CAAC,CAACmE,MAAiB,OAAOA,QAAQ;IACnE;IAEA,OAAO,EAAE;AACX"}
|
package/dist/resources.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
title: 'Collection Schema',
|
|
28
28
|
description: 'JSON schema of all collections — fields, select options, and relationship targets.',
|
|
29
29
|
uri: 'collections://schema',
|
|
30
|
-
payload:
|
|
30
|
+
payload: Object.fromEntries(schemas)
|
|
31
31
|
}),
|
|
32
32
|
buildJsonResource({
|
|
33
33
|
name: 'relationshipGraph',
|
|
@@ -58,12 +58,5 @@ function buildJsonResource(args) {
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
-
function collectionSchemasToObject(schemas) {
|
|
62
|
-
const obj = {};
|
|
63
|
-
for (const [slug, schema] of schemas){
|
|
64
|
-
obj[slug] = schema;
|
|
65
|
-
}
|
|
66
|
-
return obj;
|
|
67
|
-
}
|
|
68
61
|
|
|
69
62
|
//# sourceMappingURL=resources.js.map
|
package/dist/resources.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resources.ts"],"sourcesContent":["import type {\n BlockCatalog,\n BlockNestingMap,\n CollectionSchema,\n RelationshipEdge,\n} from './types'\n\n/**\n * Generate MCP resources that expose the introspected schema as static JSON.\n *\n * Four resources:\n * - blocks://catalog — flat list of every block and its fields\n * - blocks://nesting — per-blocks-field map of which slugs each field accepts\n * - collections://schema — collection field metadata\n * - collections://relationships — collection relationship graph\n */\nexport function generateResources(\n schemas: Map<string, CollectionSchema>,\n catalog: BlockCatalog,\n nesting: BlockNestingMap,\n relationships: RelationshipEdge[],\n) {\n return [\n buildJsonResource({\n name: 'blockCatalog',\n title: 'Block Catalog',\n description:\n 'Flat list of every block type and its fields. Pair with the blockNesting resource to know where each block can be placed.',\n uri: 'blocks://catalog',\n payload: catalog,\n }),\n buildJsonResource({\n name: 'blockNesting',\n title: 'Block Nesting Map',\n description:\n 'For every blocks-typed field in the schema (in collections and inside other blocks), lists the block slugs that field accepts. Use this to compose nested layouts at any depth.',\n uri: 'blocks://nesting',\n payload: nesting,\n }),\n buildJsonResource({\n name: 'collectionSchema',\n title: 'Collection Schema',\n description:\n 'JSON schema of all collections — fields, select options, and relationship targets.',\n uri: 'collections://schema',\n payload:
|
|
1
|
+
{"version":3,"sources":["../src/resources.ts"],"sourcesContent":["import type {\n BlockCatalog,\n BlockNestingMap,\n CollectionSchema,\n RelationshipEdge,\n} from './types'\n\n/**\n * Generate MCP resources that expose the introspected schema as static JSON.\n *\n * Four resources:\n * - blocks://catalog — flat list of every block and its fields\n * - blocks://nesting — per-blocks-field map of which slugs each field accepts\n * - collections://schema — collection field metadata\n * - collections://relationships — collection relationship graph\n */\nexport function generateResources(\n schemas: Map<string, CollectionSchema>,\n catalog: BlockCatalog,\n nesting: BlockNestingMap,\n relationships: RelationshipEdge[],\n) {\n return [\n buildJsonResource({\n name: 'blockCatalog',\n title: 'Block Catalog',\n description:\n 'Flat list of every block type and its fields. Pair with the blockNesting resource to know where each block can be placed.',\n uri: 'blocks://catalog',\n payload: catalog,\n }),\n buildJsonResource({\n name: 'blockNesting',\n title: 'Block Nesting Map',\n description:\n 'For every blocks-typed field in the schema (in collections and inside other blocks), lists the block slugs that field accepts. Use this to compose nested layouts at any depth.',\n uri: 'blocks://nesting',\n payload: nesting,\n }),\n buildJsonResource({\n name: 'collectionSchema',\n title: 'Collection Schema',\n description:\n 'JSON schema of all collections — fields, select options, and relationship targets.',\n uri: 'collections://schema',\n payload: Object.fromEntries(schemas),\n }),\n buildJsonResource({\n name: 'relationshipGraph',\n title: 'Relationship Graph',\n description:\n 'JSON representation of the collection relationship graph — which collections link to which.',\n uri: 'collections://relationships',\n payload: relationships,\n }),\n ]\n}\n\nfunction buildJsonResource(args: {\n name: string\n title: string\n description: string\n uri: string\n payload: unknown\n}) {\n const json = JSON.stringify(args.payload, null, 2)\n return {\n name: args.name,\n title: args.title,\n description: args.description,\n uri: args.uri,\n mimeType: 'application/json',\n handler(uri: URL) {\n return {\n contents: [{ uri: uri.href, text: json }],\n }\n },\n }\n}\n\n"],"names":["generateResources","schemas","catalog","nesting","relationships","buildJsonResource","name","title","description","uri","payload","Object","fromEntries","args","json","JSON","stringify","mimeType","handler","contents","href","text"],"mappings":"AAOA;;;;;;;;CAQC,GACD,OAAO,SAASA,kBACdC,OAAsC,EACtCC,OAAqB,EACrBC,OAAwB,EACxBC,aAAiC;IAEjC,OAAO;QACLC,kBAAkB;YAChBC,MAAM;YACNC,OAAO;YACPC,aACE;YACFC,KAAK;YACLC,SAASR;QACX;QACAG,kBAAkB;YAChBC,MAAM;YACNC,OAAO;YACPC,aACE;YACFC,KAAK;YACLC,SAASP;QACX;QACAE,kBAAkB;YAChBC,MAAM;YACNC,OAAO;YACPC,aACE;YACFC,KAAK;YACLC,SAASC,OAAOC,WAAW,CAACX;QAC9B;QACAI,kBAAkB;YAChBC,MAAM;YACNC,OAAO;YACPC,aACE;YACFC,KAAK;YACLC,SAASN;QACX;KACD;AACH;AAEA,SAASC,kBAAkBQ,IAM1B;IACC,MAAMC,OAAOC,KAAKC,SAAS,CAACH,KAAKH,OAAO,EAAE,MAAM;IAChD,OAAO;QACLJ,MAAMO,KAAKP,IAAI;QACfC,OAAOM,KAAKN,KAAK;QACjBC,aAAaK,KAAKL,WAAW;QAC7BC,KAAKI,KAAKJ,GAAG;QACbQ,UAAU;QACVC,SAAQT,GAAQ;YACd,OAAO;gBACLU,UAAU;oBAAC;wBAAEV,KAAKA,IAAIW,IAAI;wBAAEC,MAAMP;oBAAK;iBAAE;YAC3C;QACF;IACF;AACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
export interface McpTextResponse {
|
|
3
|
+
content: Array<{
|
|
4
|
+
type: 'text';
|
|
5
|
+
text: string;
|
|
6
|
+
}>;
|
|
7
|
+
}
|
|
8
|
+
export declare const DRAFT_NOTE = " Document is in draft status \u2014 use publishDraft to make it live.";
|
|
9
|
+
export declare function textResponse(text: string): McpTextResponse;
|
|
10
|
+
export declare function jsonResponse(payload: unknown): McpTextResponse;
|
|
11
|
+
export declare function errorMessage(error: unknown): string;
|
|
12
|
+
export declare function stampMcpContext(req: PayloadRequest): void;
|
|
13
|
+
export declare function getDocDisplayName(doc: unknown, fallback: string): string;
|
|
14
|
+
export declare function requireDraftCollection(collection: string, draftCollections: Set<string>, noun?: string): McpTextResponse | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const DRAFT_NOTE = ' Document is in draft status — use publishDraft to make it live.';
|
|
2
|
+
export function textResponse(text) {
|
|
3
|
+
return {
|
|
4
|
+
content: [
|
|
5
|
+
{
|
|
6
|
+
type: 'text',
|
|
7
|
+
text
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function jsonResponse(payload) {
|
|
13
|
+
return textResponse(JSON.stringify(payload));
|
|
14
|
+
}
|
|
15
|
+
export function errorMessage(error) {
|
|
16
|
+
return error instanceof Error ? error.message : String(error);
|
|
17
|
+
}
|
|
18
|
+
export function stampMcpContext(req) {
|
|
19
|
+
req.context = {
|
|
20
|
+
...req.context,
|
|
21
|
+
source: 'mcp'
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function getDocDisplayName(doc, fallback) {
|
|
25
|
+
const d = doc;
|
|
26
|
+
return typeof d?.name === 'string' && d.name || typeof d?.title === 'string' && d.title || typeof d?.slug === 'string' && d.slug || fallback;
|
|
27
|
+
}
|
|
28
|
+
export function requireDraftCollection(collection, draftCollections, noun = 'drafts') {
|
|
29
|
+
if (draftCollections.has(collection)) return null;
|
|
30
|
+
return textResponse(`Error: Collection "${collection}" does not support ${noun}. ` + `Draft-enabled collections: ${[
|
|
31
|
+
...draftCollections
|
|
32
|
+
].join(', ') || 'none'}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=_helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/_helpers.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nexport interface McpTextResponse {\n content: Array<{ type: 'text'; text: string }>\n}\n\nexport const DRAFT_NOTE = ' Document is in draft status — use publishDraft to make it live.'\n\nexport function textResponse(text: string): McpTextResponse {\n return { content: [{ type: 'text', text }] }\n}\n\nexport function jsonResponse(payload: unknown): McpTextResponse {\n return textResponse(JSON.stringify(payload))\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nexport function stampMcpContext(req: PayloadRequest): void {\n req.context = { ...req.context, source: 'mcp' }\n}\n\nexport function getDocDisplayName(doc: unknown, fallback: string): string {\n const d = doc as Record<string, unknown> | null | undefined\n return (\n (typeof d?.name === 'string' && d.name) ||\n (typeof d?.title === 'string' && d.title) ||\n (typeof d?.slug === 'string' && d.slug) ||\n fallback\n )\n}\n\nexport function requireDraftCollection(\n collection: string,\n draftCollections: Set<string>,\n noun = 'drafts',\n): McpTextResponse | null {\n if (draftCollections.has(collection)) return null\n return textResponse(\n `Error: Collection \"${collection}\" does not support ${noun}. ` +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n )\n}\n"],"names":["DRAFT_NOTE","textResponse","text","content","type","jsonResponse","payload","JSON","stringify","errorMessage","error","Error","message","String","stampMcpContext","req","context","source","getDocDisplayName","doc","fallback","d","name","title","slug","requireDraftCollection","collection","draftCollections","noun","has","join"],"mappings":"AAMA,OAAO,MAAMA,aAAa,mEAAkE;AAE5F,OAAO,SAASC,aAAaC,IAAY;IACvC,OAAO;QAAEC,SAAS;YAAC;gBAAEC,MAAM;gBAAQF;YAAK;SAAE;IAAC;AAC7C;AAEA,OAAO,SAASG,aAAaC,OAAgB;IAC3C,OAAOL,aAAaM,KAAKC,SAAS,CAACF;AACrC;AAEA,OAAO,SAASG,aAAaC,KAAc;IACzC,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;AACzD;AAEA,OAAO,SAASI,gBAAgBC,GAAmB;IACjDA,IAAIC,OAAO,GAAG;QAAE,GAAGD,IAAIC,OAAO;QAAEC,QAAQ;IAAM;AAChD;AAEA,OAAO,SAASC,kBAAkBC,GAAY,EAAEC,QAAgB;IAC9D,MAAMC,IAAIF;IACV,OACE,AAAC,OAAOE,GAAGC,SAAS,YAAYD,EAAEC,IAAI,IACrC,OAAOD,GAAGE,UAAU,YAAYF,EAAEE,KAAK,IACvC,OAAOF,GAAGG,SAAS,YAAYH,EAAEG,IAAI,IACtCJ;AAEJ;AAEA,OAAO,SAASK,uBACdC,UAAkB,EAClBC,gBAA6B,EAC7BC,OAAO,QAAQ;IAEf,IAAID,iBAAiBE,GAAG,CAACH,aAAa,OAAO;IAC7C,OAAOzB,aACL,CAAC,mBAAmB,EAAEyB,WAAW,mBAAmB,EAAEE,KAAK,EAAE,CAAC,GAC5D,CAAC,2BAA2B,EAAE;WAAID;KAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;AAEhF"}
|
|
@@ -2,9 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
import type { PayloadRequest } from 'payload';
|
|
3
3
|
import type { BlockCatalog, BlockNestingMap } from '../types';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* array through `updateDocument`.
|
|
5
|
+
* patchLayout — surgical wrapper that mutates a single document's blocks-typed
|
|
6
|
+
* field directly without round-tripping the entire array through updateDocument.
|
|
8
7
|
*
|
|
9
8
|
* Why this exists: prompting an LLM to "add a CTA at the bottom of the home
|
|
10
9
|
* page" via updateDocument forces it to send the whole layout array, which one
|
|
@@ -34,10 +33,5 @@ export declare function createPatchLayoutTool(catalog: BlockCatalog, nesting: Bl
|
|
|
34
33
|
blocks: Array<Record<string, unknown>>;
|
|
35
34
|
operation: "append" | "prepend" | "insertAt" | "replaceAt" | "full";
|
|
36
35
|
insertIndex?: number;
|
|
37
|
-
}, req: PayloadRequest, _extra: unknown) => Promise<
|
|
38
|
-
content: {
|
|
39
|
-
type: "text";
|
|
40
|
-
text: string;
|
|
41
|
-
}[];
|
|
42
|
-
}>;
|
|
36
|
+
}, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
43
37
|
};
|