payload-mcp-toolkit 0.2.0 → 0.3.1

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.
Files changed (61) hide show
  1. package/README.md +150 -133
  2. package/dist/__tests__/introspection.test.js +141 -46
  3. package/dist/__tests__/introspection.test.js.map +1 -1
  4. package/dist/draft-workflow.d.ts +24 -19
  5. package/dist/draft-workflow.js +89 -42
  6. package/dist/draft-workflow.js.map +1 -1
  7. package/dist/index.d.ts +10 -15
  8. package/dist/index.js +36 -76
  9. package/dist/index.js.map +1 -1
  10. package/dist/introspection.d.ts +18 -7
  11. package/dist/introspection.js +113 -84
  12. package/dist/introspection.js.map +1 -1
  13. package/dist/prompts.d.ts +4 -4
  14. package/dist/prompts.js +47 -38
  15. package/dist/prompts.js.map +1 -1
  16. package/dist/resources.d.ts +9 -4
  17. package/dist/resources.js +43 -58
  18. package/dist/resources.js.map +1 -1
  19. package/dist/tools/_helpers.d.ts +14 -0
  20. package/dist/tools/_helpers.js +35 -0
  21. package/dist/tools/_helpers.js.map +1 -0
  22. package/dist/tools/patch-layout.d.ts +15 -85
  23. package/dist/tools/patch-layout.js +142 -69
  24. package/dist/tools/patch-layout.js.map +1 -1
  25. package/dist/tools/publish-draft.d.ts +1 -11
  26. package/dist/tools/publish-draft.js +8 -39
  27. package/dist/tools/publish-draft.js.map +1 -1
  28. package/dist/tools/resolve-reference.d.ts +1 -12
  29. package/dist/tools/resolve-reference.js +45 -85
  30. package/dist/tools/resolve-reference.js.map +1 -1
  31. package/dist/tools/safe-delete.d.ts +8 -13
  32. package/dist/tools/safe-delete.js +68 -100
  33. package/dist/tools/safe-delete.js.map +1 -1
  34. package/dist/tools/schedule-publish.d.ts +11 -21
  35. package/dist/tools/schedule-publish.js +18 -61
  36. package/dist/tools/schedule-publish.js.map +1 -1
  37. package/dist/tools/search-content.d.ts +1 -6
  38. package/dist/tools/search-content.js +52 -64
  39. package/dist/tools/search-content.js.map +1 -1
  40. package/dist/tools/update-document.d.ts +4 -14
  41. package/dist/tools/update-document.js +23 -72
  42. package/dist/tools/update-document.js.map +1 -1
  43. package/dist/tools/upload-media.d.ts +1 -10
  44. package/dist/tools/upload-media.js +11 -54
  45. package/dist/tools/upload-media.js.map +1 -1
  46. package/dist/tools/versions.d.ts +7 -20
  47. package/dist/tools/versions.js +25 -82
  48. package/dist/tools/versions.js.map +1 -1
  49. package/dist/types.d.ts +82 -53
  50. package/dist/types.js +6 -1
  51. package/dist/types.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/rate-limiter.d.ts +0 -25
  54. package/dist/rate-limiter.js +0 -51
  55. package/dist/rate-limiter.js.map +0 -1
  56. package/dist/tools/compose-helpers.d.ts +0 -117
  57. package/dist/tools/compose-helpers.js +0 -236
  58. package/dist/tools/compose-helpers.js.map +0 -1
  59. package/dist/tools/compose-layout.d.ts +0 -139
  60. package/dist/tools/compose-layout.js +0 -61
  61. package/dist/tools/compose-layout.js.map +0 -1
@@ -1,5 +1,4 @@
1
1
  import type { CollectionConfig, PayloadRequest } from 'payload';
2
- import type { DraftBehavior } from './types';
3
2
  /** MCP response shape used by overrideResponse */
4
3
  interface McpResponse {
5
4
  content: Array<{
@@ -16,42 +15,48 @@ interface McpCollectionConfig {
16
15
  find: boolean;
17
16
  update: boolean;
18
17
  };
19
- overrideResponse?: (response: McpResponse, doc: Record<string, unknown>, req: PayloadRequest) => McpResponse;
18
+ overrideResponse?: (response: McpResponse, doc: Record<string, unknown>, req: PayloadRequest) => McpResponse | Promise<McpResponse>;
20
19
  }
21
20
  interface GenerateOptions {
22
- /** Base site URL for preview links */
23
- siteUrl: string;
24
- /** Preview authentication secret */
25
- previewSecret: string;
26
21
  /**
27
- * Per-collection URL path prefix used when constructing preview URLs.
28
- * Defaults to `/{slug}` for collections not in the map.
22
+ * Optional absolute base URL prepended to relative preview paths returned
23
+ * by the collection's own preview URL function. Resolved upstream from
24
+ * (in order): `options.preview.siteUrl`, `incomingConfig.serverURL`,
25
+ * `process.env.NEXT_PUBLIC_SERVER_URL`, `process.env.SITE_URL`. May be
26
+ * undefined — relative-path returns will then be skipped.
29
27
  */
30
- previewPaths?: Record<string, string>;
28
+ siteUrl?: string;
31
29
  /** Per-collection draft behavior overrides */
32
- draftBehavior?: Record<string, DraftBehavior>;
30
+ draftBehavior?: Record<string, 'always-draft' | 'always-publish'>;
33
31
  /** Collection slugs to exclude from MCP */
34
32
  excludeCollections?: string[];
33
+ /** Disable preview URL injection entirely */
34
+ previewDisabled?: boolean;
35
35
  }
36
36
  /**
37
- * Determines the draft behavior for a collection based on its config and user overrides.
37
+ * Determines the draft behavior for a collection.
38
38
  *
39
- * - If the collection has no `versions.drafts`: always 'publish' regardless of override
40
- * - If the user specified an override: use that override
41
- * - Default: 'always-draft' for draft-enabled collections, 'publish' for others
39
+ * - No drafts configured 'publish' (raw update allowed; no draft concept)
40
+ * - Drafts configured + override given use override
41
+ * - Drafts configured + no override → 'always-draft' (raw update locked)
42
42
  */
43
43
  export declare function getDraftBehavior(collection: CollectionConfig, options?: {
44
- draftBehavior?: Record<string, DraftBehavior>;
44
+ draftBehavior?: Record<string, 'always-draft' | 'always-publish'>;
45
45
  }): 'always-draft' | 'always-publish' | 'publish';
46
46
  /**
47
47
  * Generates the mcpCollections config object for the official mcpPlugin.
48
48
  *
49
- * For each collection:
49
+ * For each non-excluded collection:
50
50
  * - Determines enabled CRUD operations based on draft behavior
51
- * - For 'always-draft' collections: disables raw `update` to force clients through publishDraft tool
52
- * - Generates `overrideResponse` that appends preview URLs for draft documents
51
+ * - For 'always-draft' collections: disables raw `update` to force clients
52
+ * through publishDraft / patchLayout / updateDocument (which preserve
53
+ * draft semantics)
54
+ * - For draft collections: attaches an `overrideResponse` that appends a
55
+ * preview URL — sourced from the collection's own livePreview/preview
56
+ * function — to draft documents. Falls back to a generic admin-panel
57
+ * message when no preview function is configured.
53
58
  *
54
- * @returns A record of collection slug to MCP collection config, plus the set of draft collection slugs
59
+ * @returns Map of slug MCP collection config, plus the set of draft slugs
55
60
  */
56
61
  export declare function generateMcpCollectionConfigs(collections: CollectionConfig[], options: GenerateOptions): {
57
62
  mcpCollections: Record<string, McpCollectionConfig>;
@@ -1,40 +1,81 @@
1
- /**
2
- * Determines the draft behavior for a collection based on its config and user overrides.
3
- *
4
- * - If the collection has no `versions.drafts`: always 'publish' regardless of override
5
- * - If the user specified an override: use that override
6
- * - Default: 'always-draft' for draft-enabled collections, 'publish' for others
1
+ import { hasCollectionDrafts } from './introspection';
2
+ /**
3
+ * Determines the draft behavior for a collection.
4
+ *
5
+ * - No drafts configured 'publish' (raw update allowed; no draft concept)
6
+ * - Drafts configured + override given use override
7
+ * - Drafts configured + no override → 'always-draft' (raw update locked)
7
8
  */ export function getDraftBehavior(collection, options) {
8
- const hasDrafts = typeof collection.versions === 'object' && collection.versions !== null && 'drafts' in collection.versions && Boolean(collection.versions.drafts);
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';
13
13
  }
14
- /**
15
- * Builds a preview URL for a draft document.
16
- *
17
- * Path is `${siteUrl}/next/preview?...`. Path prefix per collection comes
18
- * from `previewPaths`, defaulting to `/{collectionSlug}` when not configured.
19
- */ function buildPreviewUrl(doc, collectionSlug, siteUrl, previewSecret, previewPaths) {
20
- const slug = doc.slug || '';
21
- const prefix = previewPaths?.[collectionSlug] ?? `/${collectionSlug}`;
22
- const path = `${prefix}/${slug}`;
23
- const params = new URLSearchParams({
24
- slug,
25
- collection: collectionSlug,
26
- path,
27
- previewSecret
28
- });
29
- return `${siteUrl}/next/preview?${params.toString()}`;
14
+ /**
15
+ * Build a preview URL for a draft document by delegating to the collection's
16
+ * own configured preview URL function. Tries `admin.livePreview.url` first
17
+ * (the modern API), then `admin.preview` (the older `GeneratePreviewURL`).
18
+ *
19
+ * If neither is configured, or the function returns null/undefined/empty,
20
+ * returns null and the override response will skip preview injection.
21
+ */ async function resolvePreviewUrl(collection, doc, req, siteUrl) {
22
+ const admin = collection.admin ?? {};
23
+ const locale = req.locale ?? 'en';
24
+ let raw;
25
+ const livePreviewUrl = admin.livePreview?.url;
26
+ if (typeof livePreviewUrl === 'function') {
27
+ try {
28
+ raw = await livePreviewUrl({
29
+ data: doc,
30
+ locale: {
31
+ code: locale,
32
+ label: locale
33
+ },
34
+ req,
35
+ payload: req.payload,
36
+ collectionConfig: collection
37
+ });
38
+ } catch {
39
+ raw = null;
40
+ }
41
+ } else if (typeof livePreviewUrl === 'string') {
42
+ raw = livePreviewUrl;
43
+ }
44
+ if (!raw && typeof admin.preview === 'function') {
45
+ try {
46
+ raw = await admin.preview(doc, {
47
+ locale,
48
+ req,
49
+ token: null
50
+ });
51
+ } catch {
52
+ raw = null;
53
+ }
54
+ }
55
+ if (!raw || typeof raw !== 'string') return null;
56
+ if (raw.startsWith('http://') || raw.startsWith('https://')) {
57
+ return raw;
58
+ }
59
+ if (!siteUrl) return null;
60
+ const base = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl;
61
+ const path = raw.startsWith('/') ? raw : `/${raw}`;
62
+ return `${base}${path}`;
30
63
  }
31
- /**
32
- * Creates an overrideResponse function for a draft-enabled collection.
33
- * When a document has `_status === 'draft'`, appends a preview URL to the response.
34
- */ function createOverrideResponse(collectionSlug, siteUrl, previewSecret, previewPaths) {
35
- return (response, doc)=>{
64
+ function createOverrideResponse(collection, siteUrl) {
65
+ return async (response, doc, req)=>{
36
66
  if (doc._status !== 'draft') return response;
37
- const previewUrl = buildPreviewUrl(doc, collectionSlug, siteUrl, previewSecret, previewPaths);
67
+ const previewUrl = await resolvePreviewUrl(collection, doc, req, siteUrl);
68
+ if (!previewUrl) {
69
+ return {
70
+ content: [
71
+ ...response.content,
72
+ {
73
+ type: 'text',
74
+ text: '\n📋 This document is a draft. Use the admin panel to preview it.'
75
+ }
76
+ ]
77
+ };
78
+ }
38
79
  return {
39
80
  content: [
40
81
  ...response.content,
@@ -46,25 +87,31 @@
46
87
  };
47
88
  };
48
89
  }
49
- /**
50
- * Generates the mcpCollections config object for the official mcpPlugin.
51
- *
52
- * For each collection:
53
- * - Determines enabled CRUD operations based on draft behavior
54
- * - For 'always-draft' collections: disables raw `update` to force clients through publishDraft tool
55
- * - Generates `overrideResponse` that appends preview URLs for draft documents
56
- *
57
- * @returns A record of collection slug to MCP collection config, plus the set of draft collection slugs
90
+ /**
91
+ * Generates the mcpCollections config object for the official mcpPlugin.
92
+ *
93
+ * For each non-excluded collection:
94
+ * - Determines enabled CRUD operations based on draft behavior
95
+ * - For 'always-draft' collections: disables raw `update` to force clients
96
+ * through publishDraft / patchLayout / updateDocument (which preserve
97
+ * draft semantics)
98
+ * - For draft collections: attaches an `overrideResponse` that appends a
99
+ * preview URL — sourced from the collection's own livePreview/preview
100
+ * function — to draft documents. Falls back to a generic admin-panel
101
+ * message when no preview function is configured.
102
+ *
103
+ * @returns Map of slug → MCP collection config, plus the set of draft slugs
58
104
  */ export function generateMcpCollectionConfigs(collections, options) {
59
105
  const mcpCollections = {};
60
106
  const draftCollections = new Set();
61
107
  const excludeSlugs = new Set([
62
- 'users',
63
108
  'payload-mcp-api-keys',
64
109
  ...options.excludeCollections ?? []
65
110
  ]);
66
111
  for (const collection of collections){
67
112
  if (excludeSlugs.has(collection.slug)) continue;
113
+ // Auth-enabled collections are users — never expose them via MCP.
114
+ if (collection.auth) continue;
68
115
  const behavior = getDraftBehavior(collection, options);
69
116
  if (behavior !== 'publish') {
70
117
  draftCollections.add(collection.slug);
@@ -79,8 +126,8 @@
79
126
  description: `Manage ${collection.slug} content`,
80
127
  enabled
81
128
  };
82
- if (draftCollections.has(collection.slug)) {
83
- config.overrideResponse = createOverrideResponse(collection.slug, options.siteUrl, options.previewSecret, options.previewPaths);
129
+ if (draftCollections.has(collection.slug) && !options.previewDisabled) {
130
+ config.overrideResponse = createOverrideResponse(collection, options.siteUrl);
84
131
  }
85
132
  mcpCollections[collection.slug] = config;
86
133
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/draft-workflow.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest } from 'payload'\nimport type { DraftBehavior } from './types'\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\n}\n\ninterface GenerateOptions {\n /** Base site URL for preview links */\n siteUrl: string\n /** Preview authentication secret */\n previewSecret: string\n /**\n * Per-collection URL path prefix used when constructing preview URLs.\n * Defaults to `/{slug}` for collections not in the map.\n */\n previewPaths?: Record<string, string>\n /** Per-collection draft behavior overrides */\n draftBehavior?: Record<string, DraftBehavior>\n /** Collection slugs to exclude from MCP */\n excludeCollections?: string[]\n}\n\n/**\n * Determines the draft behavior for a collection based on its config and user overrides.\n *\n * - If the collection has no `versions.drafts`: always 'publish' regardless of override\n * - If the user specified an override: use that override\n * - Default: 'always-draft' for draft-enabled collections, 'publish' for others\n */\nexport function getDraftBehavior(\n collection: CollectionConfig,\n options?: { draftBehavior?: Record<string, DraftBehavior> },\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 * Builds a preview URL for a draft document.\n *\n * Path is `${siteUrl}/next/preview?...`. Path prefix per collection comes\n * from `previewPaths`, defaulting to `/{collectionSlug}` when not configured.\n */\nfunction buildPreviewUrl(\n doc: Record<string, unknown>,\n collectionSlug: string,\n siteUrl: string,\n previewSecret: string,\n previewPaths?: Record<string, string>,\n): string {\n const slug = (doc.slug as string) || ''\n const prefix = previewPaths?.[collectionSlug] ?? `/${collectionSlug}`\n const path = `${prefix}/${slug}`\n\n const params = new URLSearchParams({\n slug,\n collection: collectionSlug,\n path,\n previewSecret,\n })\n\n return `${siteUrl}/next/preview?${params.toString()}`\n}\n\n/**\n * Creates an overrideResponse function for a draft-enabled collection.\n * When a document has `_status === 'draft'`, appends a preview URL to the response.\n */\nfunction createOverrideResponse(\n collectionSlug: string,\n siteUrl: string,\n previewSecret: string,\n previewPaths?: Record<string, string>,\n): McpCollectionConfig['overrideResponse'] {\n return (response: McpResponse, doc: Record<string, unknown>): McpResponse => {\n if (doc._status !== 'draft') return response\n\n const previewUrl = buildPreviewUrl(doc, collectionSlug, siteUrl, previewSecret, previewPaths)\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 collection:\n * - Determines enabled CRUD operations based on draft behavior\n * - For 'always-draft' collections: disables raw `update` to force clients through publishDraft tool\n * - Generates `overrideResponse` that appends preview URLs for draft documents\n *\n * @returns A record of collection slug to MCP collection config, plus the set of draft collection 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 'users',\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 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)) {\n config.overrideResponse = createOverrideResponse(\n collection.slug,\n options.siteUrl,\n options.previewSecret,\n options.previewPaths,\n )\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","buildPreviewUrl","doc","collectionSlug","siteUrl","previewSecret","previewPaths","prefix","path","params","URLSearchParams","toString","createOverrideResponse","response","_status","previewUrl","content","type","text","generateMcpCollectionConfigs","collections","mcpCollections","draftCollections","Set","excludeSlugs","excludeCollections","has","behavior","add","enabled","find","create","update","delete","config","description","overrideResponse"],"mappings":"AAwCA;;;;;;CAMC,GACD,OAAO,SAASA,iBACdC,UAA4B,EAC5BC,OAA2D;IAE3D,MAAMC,YACJ,OAAOF,WAAWG,QAAQ,KAAK,YAC/BH,WAAWG,QAAQ,KAAK,QACxB,YAAYH,WAAWG,QAAQ,IAC/BC,QAAQJ,WAAWG,QAAQ,CAACE,MAAM;IAEpC,IAAI,CAACH,WAAW,OAAO;IAEvB,MAAMI,WAAWL,SAASM,eAAe,CAACP,WAAWQ,IAAI,CAAC;IAC1D,IAAIF,UAAU,OAAOA;IAErB,OAAO;AACT;AAEA;;;;;CAKC,GACD,SAASG,gBACPC,GAA4B,EAC5BC,cAAsB,EACtBC,OAAe,EACfC,aAAqB,EACrBC,YAAqC;IAErC,MAAMN,OAAO,AAACE,IAAIF,IAAI,IAAe;IACrC,MAAMO,SAASD,cAAc,CAACH,eAAe,IAAI,CAAC,CAAC,EAAEA,gBAAgB;IACrE,MAAMK,OAAO,GAAGD,OAAO,CAAC,EAAEP,MAAM;IAEhC,MAAMS,SAAS,IAAIC,gBAAgB;QACjCV;QACAR,YAAYW;QACZK;QACAH;IACF;IAEA,OAAO,GAAGD,QAAQ,cAAc,EAAEK,OAAOE,QAAQ,IAAI;AACvD;AAEA;;;CAGC,GACD,SAASC,uBACPT,cAAsB,EACtBC,OAAe,EACfC,aAAqB,EACrBC,YAAqC;IAErC,OAAO,CAACO,UAAuBX;QAC7B,IAAIA,IAAIY,OAAO,KAAK,SAAS,OAAOD;QAEpC,MAAME,aAAad,gBAAgBC,KAAKC,gBAAgBC,SAASC,eAAeC;QAEhF,OAAO;YACLU,SAAS;mBACJH,SAASG,OAAO;gBACnB;oBACEC,MAAM;oBACNC,MAAM,CAAC,gDAAgD,EAAEH,YAAY;gBACvE;aACD;QACH;IACF;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASI,6BACdC,WAA+B,EAC/B3B,OAAwB;IAKxB,MAAM4B,iBAAsD,CAAC;IAC7D,MAAMC,mBAAmB,IAAIC;IAE7B,MAAMC,eAAe,IAAID,IAAI;QAC3B;QACA;WACI9B,QAAQgC,kBAAkB,IAAI,EAAE;KACrC;IAED,KAAK,MAAMjC,cAAc4B,YAAa;QACpC,IAAII,aAAaE,GAAG,CAAClC,WAAWQ,IAAI,GAAG;QAEvC,MAAM2B,WAAWpC,iBAAiBC,YAAYC;QAE9C,IAAIkC,aAAa,WAAW;YAC1BL,iBAAiBM,GAAG,CAACpC,WAAWQ,IAAI;QACtC;QAEA,MAAM6B,UAAU;YACdC,MAAM;YACNC,QAAQ;YACRC,QAAQL,aAAa;YACrBM,QAAQ;QACV;QAEA,MAAMC,SAA8B;YAClCC,aAAa,CAAC,OAAO,EAAE3C,WAAWQ,IAAI,CAAC,QAAQ,CAAC;YAChD6B;QACF;QAEA,IAAIP,iBAAiBI,GAAG,CAAClC,WAAWQ,IAAI,GAAG;YACzCkC,OAAOE,gBAAgB,GAAGxB,uBACxBpB,WAAWQ,IAAI,EACfP,QAAQW,OAAO,EACfX,QAAQY,aAAa,EACrBZ,QAAQa,YAAY;QAExB;QAEAe,cAAc,CAAC7B,WAAWQ,IAAI,CAAC,GAAGkC;IACpC;IAEA,OAAO;QAAEb;QAAgBC;IAAiB;AAC5C"}
1
+ {"version":3,"sources":["../src/draft-workflow.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest } from 'payload'\r\nimport { hasCollectionDrafts } from './introspection'\r\n\r\n/** MCP response shape used by overrideResponse */\r\ninterface McpResponse {\r\n content: Array<{ text: string; type: string }>\r\n}\r\n\r\n/** Per-collection MCP config with enabled operations and optional overrideResponse */\r\ninterface McpCollectionConfig {\r\n description: string\r\n enabled: {\r\n create: boolean\r\n delete: boolean\r\n find: boolean\r\n update: boolean\r\n }\r\n overrideResponse?: (\r\n response: McpResponse,\r\n doc: Record<string, unknown>,\r\n req: PayloadRequest,\r\n ) => McpResponse | Promise<McpResponse>\r\n}\r\n\r\ninterface GenerateOptions {\r\n /**\r\n * Optional absolute base URL prepended to relative preview paths returned\r\n * by the collection's own preview URL function. Resolved upstream from\r\n * (in order): `options.preview.siteUrl`, `incomingConfig.serverURL`,\r\n * `process.env.NEXT_PUBLIC_SERVER_URL`, `process.env.SITE_URL`. May be\r\n * undefined — relative-path returns will then be skipped.\r\n */\r\n siteUrl?: string\r\n /** Per-collection draft behavior overrides */\r\n draftBehavior?: Record<string, 'always-draft' | 'always-publish'>\r\n /** Collection slugs to exclude from MCP */\r\n excludeCollections?: string[]\r\n /** Disable preview URL injection entirely */\r\n previewDisabled?: boolean\r\n}\r\n\r\n/**\r\n * Determines the draft behavior for a collection.\r\n *\r\n * - No drafts configured → 'publish' (raw update allowed; no draft concept)\r\n * - Drafts configured + override given → use override\r\n * - Drafts configured + no override → 'always-draft' (raw update locked)\r\n */\r\nexport function getDraftBehavior(\r\n collection: CollectionConfig,\r\n options?: { draftBehavior?: Record<string, 'always-draft' | 'always-publish'> },\r\n): 'always-draft' | 'always-publish' | 'publish' {\r\n if (!hasCollectionDrafts(collection)) return 'publish'\r\n\r\n const override = options?.draftBehavior?.[collection.slug]\r\n if (override) return override\r\n\r\n return 'always-draft'\r\n}\r\n\r\n/**\r\n * Build a preview URL for a draft document by delegating to the collection's\r\n * own configured preview URL function. Tries `admin.livePreview.url` first\r\n * (the modern API), then `admin.preview` (the older `GeneratePreviewURL`).\r\n *\r\n * If neither is configured, or the function returns null/undefined/empty,\r\n * returns null and the override response will skip preview injection.\r\n */\r\nasync function resolvePreviewUrl(\r\n collection: CollectionConfig,\r\n doc: Record<string, unknown>,\r\n req: PayloadRequest,\r\n siteUrl: string | undefined,\r\n): Promise<string | null> {\r\n const admin = (collection.admin ?? {}) as Record<string, any>\r\n const locale = (req as any).locale ?? 'en'\r\n\r\n let raw: string | null | undefined\r\n\r\n const livePreviewUrl = admin.livePreview?.url\r\n if (typeof livePreviewUrl === 'function') {\r\n try {\r\n raw = await livePreviewUrl({\r\n data: doc,\r\n locale: { code: locale, label: locale },\r\n req,\r\n payload: req.payload,\r\n collectionConfig: collection as any,\r\n })\r\n } catch {\r\n raw = null\r\n }\r\n } else if (typeof livePreviewUrl === 'string') {\r\n raw = livePreviewUrl\r\n }\r\n\r\n if (!raw && typeof admin.preview === 'function') {\r\n try {\r\n raw = await admin.preview(doc, {\r\n locale,\r\n req,\r\n token: null,\r\n })\r\n } catch {\r\n raw = null\r\n }\r\n }\r\n\r\n if (!raw || typeof raw !== 'string') return null\r\n\r\n if (raw.startsWith('http://') || raw.startsWith('https://')) {\r\n return raw\r\n }\r\n\r\n if (!siteUrl) return null\r\n\r\n const base = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl\r\n const path = raw.startsWith('/') ? raw : `/${raw}`\r\n return `${base}${path}`\r\n}\r\n\r\nfunction createOverrideResponse(\r\n collection: CollectionConfig,\r\n siteUrl: string | undefined,\r\n): McpCollectionConfig['overrideResponse'] {\r\n return async (response, doc, req): Promise<McpResponse> => {\r\n if (doc._status !== 'draft') return response\r\n\r\n const previewUrl = await resolvePreviewUrl(collection, doc, req, siteUrl)\r\n if (!previewUrl) {\r\n return {\r\n content: [\r\n ...response.content,\r\n {\r\n type: 'text',\r\n text: '\\n📋 This document is a draft. Use the admin panel to preview it.',\r\n },\r\n ],\r\n }\r\n }\r\n\r\n return {\r\n content: [\r\n ...response.content,\r\n {\r\n type: 'text',\r\n text: `\\n📋 This document is a draft. Preview it here: ${previewUrl}`,\r\n },\r\n ],\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Generates the mcpCollections config object for the official mcpPlugin.\r\n *\r\n * For each non-excluded collection:\r\n * - Determines enabled CRUD operations based on draft behavior\r\n * - For 'always-draft' collections: disables raw `update` to force clients\r\n * through publishDraft / patchLayout / updateDocument (which preserve\r\n * draft semantics)\r\n * - For draft collections: attaches an `overrideResponse` that appends a\r\n * preview URL — sourced from the collection's own livePreview/preview\r\n * function — to draft documents. Falls back to a generic admin-panel\r\n * message when no preview function is configured.\r\n *\r\n * @returns Map of slug → MCP collection config, plus the set of draft slugs\r\n */\r\nexport function generateMcpCollectionConfigs(\r\n collections: CollectionConfig[],\r\n options: GenerateOptions,\r\n): {\r\n mcpCollections: Record<string, McpCollectionConfig>\r\n draftCollections: Set<string>\r\n} {\r\n const mcpCollections: Record<string, McpCollectionConfig> = {}\r\n const draftCollections = new Set<string>()\r\n\r\n const excludeSlugs = new Set([\r\n 'payload-mcp-api-keys',\r\n ...(options.excludeCollections ?? []),\r\n ])\r\n\r\n for (const collection of collections) {\r\n if (excludeSlugs.has(collection.slug)) continue\r\n\r\n // Auth-enabled collections are users — never expose them via MCP.\r\n if ((collection as any).auth) continue\r\n\r\n const behavior = getDraftBehavior(collection, options)\r\n\r\n if (behavior !== 'publish') {\r\n draftCollections.add(collection.slug)\r\n }\r\n\r\n const enabled = {\r\n find: true,\r\n create: true,\r\n update: behavior !== 'always-draft',\r\n delete: true,\r\n }\r\n\r\n const config: McpCollectionConfig = {\r\n description: `Manage ${collection.slug} content`,\r\n enabled,\r\n }\r\n\r\n if (draftCollections.has(collection.slug) && !options.previewDisabled) {\r\n config.overrideResponse = createOverrideResponse(collection, options.siteUrl)\r\n }\r\n\r\n mcpCollections[collection.slug] = config\r\n }\r\n\r\n return { mcpCollections, draftCollections }\r\n}\r\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":"AACA,SAASA,mBAAmB,QAAQ,kBAAiB;AAwCrD;;;;;;CAMC,GACD,OAAO,SAASC,iBACdC,UAA4B,EAC5BC,OAA+E;IAE/E,IAAI,CAACH,oBAAoBE,aAAa,OAAO;IAE7C,MAAME,WAAWD,SAASE,eAAe,CAACH,WAAWI,IAAI,CAAC;IAC1D,IAAIF,UAAU,OAAOA;IAErB,OAAO;AACT;AAEA;;;;;;;CAOC,GACD,eAAeG,kBACbL,UAA4B,EAC5BM,GAA4B,EAC5BC,GAAmB,EACnBC,OAA2B;IAE3B,MAAMC,QAAST,WAAWS,KAAK,IAAI,CAAC;IACpC,MAAMC,SAAS,AAACH,IAAYG,MAAM,IAAI;IAEtC,IAAIC;IAEJ,MAAMC,iBAAiBH,MAAMI,WAAW,EAAEC;IAC1C,IAAI,OAAOF,mBAAmB,YAAY;QACxC,IAAI;YACFD,MAAM,MAAMC,eAAe;gBACzBG,MAAMT;gBACNI,QAAQ;oBAAEM,MAAMN;oBAAQO,OAAOP;gBAAO;gBACtCH;gBACAW,SAASX,IAAIW,OAAO;gBACpBC,kBAAkBnB;YACpB;QACF,EAAE,OAAM;YACNW,MAAM;QACR;IACF,OAAO,IAAI,OAAOC,mBAAmB,UAAU;QAC7CD,MAAMC;IACR;IAEA,IAAI,CAACD,OAAO,OAAOF,MAAMW,OAAO,KAAK,YAAY;QAC/C,IAAI;YACFT,MAAM,MAAMF,MAAMW,OAAO,CAACd,KAAK;gBAC7BI;gBACAH;gBACAc,OAAO;YACT;QACF,EAAE,OAAM;YACNV,MAAM;QACR;IACF;IAEA,IAAI,CAACA,OAAO,OAAOA,QAAQ,UAAU,OAAO;IAE5C,IAAIA,IAAIW,UAAU,CAAC,cAAcX,IAAIW,UAAU,CAAC,aAAa;QAC3D,OAAOX;IACT;IAEA,IAAI,CAACH,SAAS,OAAO;IAErB,MAAMe,OAAOf,QAAQgB,QAAQ,CAAC,OAAOhB,QAAQiB,KAAK,CAAC,GAAG,CAAC,KAAKjB;IAC5D,MAAMkB,OAAOf,IAAIW,UAAU,CAAC,OAAOX,MAAM,CAAC,CAAC,EAAEA,KAAK;IAClD,OAAO,GAAGY,OAAOG,MAAM;AACzB;AAEA,SAASC,uBACP3B,UAA4B,EAC5BQ,OAA2B;IAE3B,OAAO,OAAOoB,UAAUtB,KAAKC;QAC3B,IAAID,IAAIuB,OAAO,KAAK,SAAS,OAAOD;QAEpC,MAAME,aAAa,MAAMzB,kBAAkBL,YAAYM,KAAKC,KAAKC;QACjE,IAAI,CAACsB,YAAY;YACf,OAAO;gBACLC,SAAS;uBACJH,SAASG,OAAO;oBACnB;wBACEC,MAAM;wBACNC,MAAM;oBACR;iBACD;YACH;QACF;QAEA,OAAO;YACLF,SAAS;mBACJH,SAASG,OAAO;gBACnB;oBACEC,MAAM;oBACNC,MAAM,CAAC,gDAAgD,EAAEH,YAAY;gBACvE;aACD;QACH;IACF;AACF;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASI,6BACdC,WAA+B,EAC/BlC,OAAwB;IAKxB,MAAMmC,iBAAsD,CAAC;IAC7D,MAAMC,mBAAmB,IAAIC;IAE7B,MAAMC,eAAe,IAAID,IAAI;QAC3B;WACIrC,QAAQuC,kBAAkB,IAAI,EAAE;KACrC;IAED,KAAK,MAAMxC,cAAcmC,YAAa;QACpC,IAAII,aAAaE,GAAG,CAACzC,WAAWI,IAAI,GAAG;QAEvC,kEAAkE;QAClE,IAAI,AAACJ,WAAmB0C,IAAI,EAAE;QAE9B,MAAMC,WAAW5C,iBAAiBC,YAAYC;QAE9C,IAAI0C,aAAa,WAAW;YAC1BN,iBAAiBO,GAAG,CAAC5C,WAAWI,IAAI;QACtC;QAEA,MAAMyC,UAAU;YACdC,MAAM;YACNC,QAAQ;YACRC,QAAQL,aAAa;YACrBM,QAAQ;QACV;QAEA,MAAMC,SAA8B;YAClCC,aAAa,CAAC,OAAO,EAAEnD,WAAWI,IAAI,CAAC,QAAQ,CAAC;YAChDyC;QACF;QAEA,IAAIR,iBAAiBI,GAAG,CAACzC,WAAWI,IAAI,KAAK,CAACH,QAAQmD,eAAe,EAAE;YACrEF,OAAOG,gBAAgB,GAAG1B,uBAAuB3B,YAAYC,QAAQO,OAAO;QAC9E;QAEA4B,cAAc,CAACpC,WAAWI,IAAI,CAAC,GAAG8C;IACpC;IAEA,OAAO;QAAEd;QAAgBC;IAAiB;AAC5C"}
package/dist/index.d.ts CHANGED
@@ -3,22 +3,17 @@ import type { ContentToolkitOptions } from './types';
3
3
  /**
4
4
  * Payload MCP Toolkit
5
5
  *
6
- * A wrapper plugin that introspects the Payload config and generates
7
- * domain-aware MCP tools, prompts, and resources for AI content management.
6
+ * Layered on top of the official @payloadcms/plugin-mcp. The toolkit
7
+ * introspects your Payload config and registers schema-aware prompts,
8
+ * resources, and tools so AI clients can drive the CMS without
9
+ * hand-built plumbing.
8
10
  *
9
- * Usage in payload.config.ts:
11
+ * Zero-config usage:
10
12
  * ```ts
11
- * import { contentToolkitPlugin } from 'payload-mcp-toolkit'
12
- *
13
- * plugins: [
14
- * contentToolkitPlugin({
15
- * siteUrl: process.env.SITE_URL!,
16
- * previewSecret: process.env.PREVIEW_SECRET!,
17
- * previewPaths: { posts: '/blog', pages: '' },
18
- * draftBehavior: { pages: 'always-draft' },
19
- * }),
20
- * ]
13
+ * plugins: [contentToolkitPlugin()]
21
14
  * ```
15
+ *
16
+ * Every option below is an optional escape hatch — see ContentToolkitOptions.
22
17
  */
23
- export declare function contentToolkitPlugin(options: ContentToolkitOptions): Plugin;
24
- export type { ContentToolkitOptions, DomainPrompt, DraftBehavior, CollectionSchema, BlockCatalog, SectionBlockSchema, LeafBlockSchema, RelationshipEdge, FieldSchema, } from './types';
18
+ export declare function contentToolkitPlugin(options?: ContentToolkitOptions): Plugin;
19
+ export type { ContentToolkitOptions, DomainPrompt, CollectionSchema, BlockCatalog, BlockSchema, BlockNestingMap, BlockNestingEdge, RelationshipEdge, FieldSchema, } from './types';
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { mcpPlugin } from '@payloadcms/plugin-mcp';
2
- import { introspectCollections, introspectBlocks, buildRelationshipGraph } from './introspection';
2
+ import { introspectCollections, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph } from './introspection';
3
3
  import { generatePrompts } from './prompts';
4
4
  import { generateResources } from './resources';
5
5
  import { generateMcpCollectionConfigs } from './draft-workflow';
6
- import { createComposeLayoutTool } from './tools/compose-layout';
7
6
  import { createPatchLayoutTool } from './tools/patch-layout';
8
7
  import { createPublishDraftTool } from './tools/publish-draft';
9
8
  import { createResolveReferenceTool } from './tools/resolve-reference';
@@ -13,83 +12,47 @@ import { createSearchContentTool } from './tools/search-content';
13
12
  import { createUpdateDocumentTool } from './tools/update-document';
14
13
  import { createUploadMediaTool } from './tools/upload-media';
15
14
  import { createListVersionsTool, createRestoreVersionTool } from './tools/versions';
16
- /**
17
- * Payload MCP Toolkit
18
- *
19
- * A wrapper plugin that introspects the Payload config and generates
20
- * domain-aware MCP tools, prompts, and resources for AI content management.
21
- *
22
- * Usage in payload.config.ts:
23
- * ```ts
24
- * import { contentToolkitPlugin } from 'payload-mcp-toolkit'
25
- *
26
- * plugins: [
27
- * contentToolkitPlugin({
28
- * siteUrl: process.env.SITE_URL!,
29
- * previewSecret: process.env.PREVIEW_SECRET!,
30
- * previewPaths: { posts: '/blog', pages: '' },
31
- * draftBehavior: { pages: 'always-draft' },
32
- * }),
33
- * ]
34
- * ```
35
- */ export function contentToolkitPlugin(options) {
15
+ /**
16
+ * Payload MCP Toolkit
17
+ *
18
+ * Layered on top of the official @payloadcms/plugin-mcp. The toolkit
19
+ * introspects your Payload config and registers schema-aware prompts,
20
+ * resources, and tools so AI clients can drive the CMS without
21
+ * hand-built plumbing.
22
+ *
23
+ * Zero-config usage:
24
+ * ```ts
25
+ * plugins: [contentToolkitPlugin()]
26
+ * ```
27
+ *
28
+ * Every option below is an optional escape hatch — see ContentToolkitOptions.
29
+ */ export function contentToolkitPlugin(options = {}) {
36
30
  return (incomingConfig)=>{
37
31
  const collections = incomingConfig.collections ?? [];
38
32
  const allBlocks = incomingConfig.blocks ?? [];
39
- // Separate section blocks from leaf blocks.
40
- //
41
- // Preferred: `options.sectionBlockSlugs` — explicit, unambiguous.
42
- // Fallback heuristic: blocks containing a nested `blocks`-type field are
43
- // sections, all others are leaves. The heuristic mis-classifies "fixed"
44
- // sections (no nested blocks but their own standalone fields), so prefer
45
- // the explicit option whenever the schema has any fixed sections.
46
- const sectionBlocks = [];
47
- const leafBlocks = [];
48
- if (options.sectionBlockSlugs && options.sectionBlockSlugs.length > 0) {
49
- const sectionSlugs = new Set(options.sectionBlockSlugs);
50
- for (const block of allBlocks){
51
- if (sectionSlugs.has(block.slug)) {
52
- sectionBlocks.push(block);
53
- } else {
54
- leafBlocks.push(block);
55
- }
56
- }
57
- } else {
58
- for (const block of allBlocks){
59
- const hasBlocksField = block.fields.some((f)=>f.type === 'blocks' || f.type === 'tabs' && 'tabs' in f && f.tabs.some((tab)=>'fields' in tab && tab.fields.some((tf)=>tf.type === 'blocks')));
60
- if (hasBlocksField) {
61
- sectionBlocks.push(block);
62
- } else {
63
- leafBlocks.push(block);
64
- }
65
- }
66
- }
67
- // 1. Schema Introspection
68
33
  const collectionSchemas = introspectCollections(collections);
69
- const blockCatalog = introspectBlocks(sectionBlocks, leafBlocks);
34
+ const blockCatalog = introspectBlocks(allBlocks);
35
+ const blockNesting = buildBlockNestingMap(collections, allBlocks);
70
36
  const relationships = buildRelationshipGraph(collectionSchemas);
71
- // 2. Generate Prompts
72
- const prompts = generatePrompts(collectionSchemas, blockCatalog, relationships, options.domainPrompts);
73
- // 3. Generate Resources
74
- const resources = generateResources(collectionSchemas, blockCatalog, relationships);
75
- // 4. Generate Tools
37
+ // Preview siteUrl resolves: explicit option → Payload serverURL → env vars.
38
+ // May be undefined; relative-path preview URLs are skipped in that case.
39
+ const previewSiteUrl = options.preview?.siteUrl ?? incomingConfig.serverURL ?? process.env.NEXT_PUBLIC_SERVER_URL ?? process.env.SITE_URL;
40
+ const prompts = generatePrompts(collectionSchemas, blockCatalog, blockNesting, relationships, options.domainPrompts);
41
+ const resources = generateResources(collectionSchemas, blockCatalog, blockNesting, relationships);
42
+ const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {
43
+ siteUrl: previewSiteUrl,
44
+ draftBehavior: options.draftBehavior,
45
+ excludeCollections: options.exclude?.collections,
46
+ previewDisabled: options.preview?.disabled
47
+ });
76
48
  const searchableCollections = new Map();
77
49
  for (const [slug, schema] of collectionSchemas){
78
50
  if (schema.searchableFields.length > 0) {
79
51
  searchableCollections.set(slug, schema.searchableFields);
80
52
  }
81
53
  }
82
- // 5. Generate MCP Collection Configs with draft workflow
83
- const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {
84
- siteUrl: options.siteUrl,
85
- previewSecret: options.previewSecret,
86
- previewPaths: options.previewPaths,
87
- draftBehavior: options.draftBehavior,
88
- excludeCollections: options.excludeCollections
89
- });
90
54
  const tools = [
91
- createComposeLayoutTool(blockCatalog),
92
- createPatchLayoutTool(blockCatalog, draftCollections),
55
+ createPatchLayoutTool(blockCatalog, blockNesting, draftCollections),
93
56
  createPublishDraftTool(draftCollections),
94
57
  createResolveReferenceTool(searchableCollections),
95
58
  createSafeDeleteTool(relationships),
@@ -102,12 +65,10 @@ import { createListVersionsTool, createRestoreVersionTool } from './tools/versio
102
65
  createListVersionsTool(draftCollections),
103
66
  createRestoreVersionTool(draftCollections)
104
67
  ];
105
- // schedulePublish only registers when at least one draft collection has a `publishedAt` date field
106
68
  const schedulePublish = createSchedulePublishTool(collectionSchemas, draftCollections);
107
69
  if (schedulePublish) tools.push(schedulePublish);
108
- // Build MCP global configs
109
70
  const mcpGlobals = {};
110
- const excludeGlobalSlugs = new Set(options.excludeGlobals ?? []);
71
+ const excludeGlobalSlugs = new Set(options.exclude?.globals ?? []);
111
72
  for (const global of incomingConfig.globals ?? []){
112
73
  if (excludeGlobalSlugs.has(global.slug)) continue;
113
74
  mcpGlobals[global.slug] = {
@@ -115,20 +76,19 @@ import { createListVersionsTool, createRestoreVersionTool } from './tools/versio
115
76
  description: `Manage ${global.slug} global settings`
116
77
  };
117
78
  }
118
- // Apply the official MCP plugin with our generated config
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`.
119
83
  const withMcp = mcpPlugin({
120
84
  collections: mcpCollections,
121
85
  globals: mcpGlobals,
86
+ userCollection: options.userCollection,
122
87
  mcp: {
123
88
  tools: tools,
124
89
  prompts: prompts,
125
90
  resources: resources
126
91
  },
127
- // Set req.user from the API key's linked user so custom tools
128
- // can use overrideAccess: false and relationship field validation
129
- // has a valid user context for access control checks.
130
- // Safe: getDefault() throws inside the official plugin if the API key
131
- // has no linked user, so settings.user is guaranteed to exist here.
132
92
  overrideAuth: async (req, getDefault)=>{
133
93
  const settings = await getDefault();
134
94
  req.user = settings.user;
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 { introspectCollections, introspectBlocks, buildRelationshipGraph } from './introspection'\nimport { generatePrompts } from './prompts'\nimport { generateResources } from './resources'\nimport { generateMcpCollectionConfigs } from './draft-workflow'\nimport { createComposeLayoutTool } from './tools/compose-layout'\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 * A wrapper plugin that introspects the Payload config and generates\n * domain-aware MCP tools, prompts, and resources for AI content management.\n *\n * Usage in payload.config.ts:\n * ```ts\n * import { contentToolkitPlugin } from 'payload-mcp-toolkit'\n *\n * plugins: [\n * contentToolkitPlugin({\n * siteUrl: process.env.SITE_URL!,\n * previewSecret: process.env.PREVIEW_SECRET!,\n * previewPaths: { posts: '/blog', pages: '' },\n * draftBehavior: { pages: 'always-draft' },\n * }),\n * ]\n * ```\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 // Separate section blocks from leaf blocks.\n //\n // Preferred: `options.sectionBlockSlugs` — explicit, unambiguous.\n // Fallback heuristic: blocks containing a nested `blocks`-type field are\n // sections, all others are leaves. The heuristic mis-classifies \"fixed\"\n // sections (no nested blocks but their own standalone fields), so prefer\n // the explicit option whenever the schema has any fixed sections.\n const sectionBlocks: Block[] = []\n const leafBlocks: Block[] = []\n\n if (options.sectionBlockSlugs && options.sectionBlockSlugs.length > 0) {\n const sectionSlugs = new Set(options.sectionBlockSlugs)\n for (const block of allBlocks) {\n if (sectionSlugs.has(block.slug)) {\n sectionBlocks.push(block)\n } else {\n leafBlocks.push(block)\n }\n }\n } else {\n for (const block of allBlocks) {\n const hasBlocksField = block.fields.some(\n (f) => f.type === 'blocks' ||\n (f.type === 'tabs' && 'tabs' in f && f.tabs.some(\n (tab: any) => 'fields' in tab && tab.fields.some((tf: any) => tf.type === 'blocks')\n ))\n )\n if (hasBlocksField) {\n sectionBlocks.push(block)\n } else {\n leafBlocks.push(block)\n }\n }\n }\n\n // 1. Schema Introspection\n const collectionSchemas = introspectCollections(collections)\n const blockCatalog = introspectBlocks(sectionBlocks, leafBlocks)\n const relationships = buildRelationshipGraph(collectionSchemas)\n\n // 2. Generate Prompts\n const prompts = generatePrompts(\n collectionSchemas,\n blockCatalog,\n relationships,\n options.domainPrompts,\n )\n\n // 3. Generate Resources\n const resources = generateResources(collectionSchemas, blockCatalog, relationships)\n\n // 4. Generate Tools\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 // 5. Generate MCP Collection Configs with draft workflow\n const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {\n siteUrl: options.siteUrl,\n previewSecret: options.previewSecret,\n previewPaths: options.previewPaths,\n draftBehavior: options.draftBehavior,\n excludeCollections: options.excludeCollections,\n })\n\n const tools: any[] = [\n createComposeLayoutTool(blockCatalog),\n createPatchLayoutTool(blockCatalog, 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 // schedulePublish only registers when at least one draft collection has a `publishedAt` date field\n const schedulePublish = createSchedulePublishTool(collectionSchemas, draftCollections)\n if (schedulePublish) tools.push(schedulePublish)\n\n // Build MCP global configs\n const mcpGlobals: Record<string, { enabled: boolean; description?: string }> = {}\n const excludeGlobalSlugs = new Set(options.excludeGlobals ?? [])\n\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 // Apply the official MCP plugin with our generated config\n const withMcp = mcpPlugin({\n collections: mcpCollections as any,\n globals: mcpGlobals as any,\n mcp: {\n tools: tools as any[],\n prompts: prompts as any[],\n resources: resources as any[],\n },\n // Set req.user from the API key's linked user so custom tools\n // can use overrideAccess: false and relationship field validation\n // has a valid user context for access control checks.\n // Safe: getDefault() throws inside the official plugin if the API key\n // has no linked user, so settings.user is guaranteed to exist here.\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 DraftBehavior,\n CollectionSchema,\n BlockCatalog,\n SectionBlockSchema,\n LeafBlockSchema,\n RelationshipEdge,\n FieldSchema,\n} from './types'\n"],"names":["mcpPlugin","introspectCollections","introspectBlocks","buildRelationshipGraph","generatePrompts","generateResources","generateMcpCollectionConfigs","createComposeLayoutTool","createPatchLayoutTool","createPublishDraftTool","createResolveReferenceTool","createSafeDeleteTool","createSchedulePublishTool","createSearchContentTool","createUpdateDocumentTool","createUploadMediaTool","createListVersionsTool","createRestoreVersionTool","contentToolkitPlugin","options","incomingConfig","collections","allBlocks","blocks","sectionBlocks","leafBlocks","sectionBlockSlugs","length","sectionSlugs","Set","block","has","slug","push","hasBlocksField","fields","some","f","type","tabs","tab","tf","collectionSchemas","blockCatalog","relationships","prompts","domainPrompts","resources","searchableCollections","Map","schema","searchableFields","set","mcpCollections","draftCollections","siteUrl","previewSecret","previewPaths","draftBehavior","excludeCollections","tools","maxFileSize","mediaUpload","collectionSlug","schedulePublish","mcpGlobals","excludeGlobalSlugs","excludeGlobals","global","globals","enabled","description","withMcp","mcp","overrideAuth","req","getDefault","settings","user"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Block, CollectionConfig, Config, Plugin } from 'payload'\r\nimport { mcpPlugin } from '@payloadcms/plugin-mcp'\r\nimport type { ContentToolkitOptions } from './types'\r\nimport {\r\n introspectCollections,\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 { generateMcpCollectionConfigs } from './draft-workflow'\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 * Payload MCP Toolkit\r\n *\r\n * Layered on top of the official @payloadcms/plugin-mcp. The toolkit\r\n * introspects your Payload config and registers schema-aware prompts,\r\n * resources, and tools so AI clients can drive the CMS without\r\n * hand-built plumbing.\r\n *\r\n * Zero-config usage:\r\n * ```ts\r\n * plugins: [contentToolkitPlugin()]\r\n * ```\r\n *\r\n * Every option below is an optional escape hatch — see ContentToolkitOptions.\r\n */\r\nexport function contentToolkitPlugin(options: ContentToolkitOptions = {}): Plugin {\r\n return (incomingConfig: Config): Config => {\r\n const collections = (incomingConfig.collections ?? []) as CollectionConfig[]\r\n const allBlocks = (incomingConfig.blocks ?? []) as Block[]\r\n\r\n const collectionSchemas = introspectCollections(collections)\r\n const blockCatalog = introspectBlocks(allBlocks)\r\n const blockNesting = buildBlockNestingMap(collections, allBlocks)\r\n const relationships = buildRelationshipGraph(collectionSchemas)\r\n\r\n // Preview siteUrl resolves: explicit option → Payload serverURL → env vars.\r\n // May be undefined; relative-path preview URLs are skipped in that case.\r\n const previewSiteUrl =\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\r\n const prompts = generatePrompts(\r\n collectionSchemas,\r\n blockCatalog,\r\n blockNesting,\r\n relationships,\r\n options.domainPrompts,\r\n )\r\n const resources = generateResources(\r\n collectionSchemas,\r\n blockCatalog,\r\n blockNesting,\r\n relationships,\r\n )\r\n\r\n const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {\r\n siteUrl: previewSiteUrl,\r\n draftBehavior: options.draftBehavior,\r\n excludeCollections: options.exclude?.collections,\r\n previewDisabled: options.preview?.disabled,\r\n })\r\n\r\n const searchableCollections = new Map<string, string[]>()\r\n for (const [slug, schema] of collectionSchemas) {\r\n if (schema.searchableFields.length > 0) {\r\n searchableCollections.set(slug, schema.searchableFields)\r\n }\r\n }\r\n\r\n const tools: any[] = [\r\n createPatchLayoutTool(blockCatalog, blockNesting, draftCollections),\r\n createPublishDraftTool(draftCollections),\r\n createResolveReferenceTool(searchableCollections),\r\n createSafeDeleteTool(relationships),\r\n createSearchContentTool(collectionSchemas),\r\n createUpdateDocumentTool(collectionSchemas, 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(collectionSchemas, draftCollections)\r\n if (schedulePublish) tools.push(schedulePublish)\r\n\r\n const mcpGlobals: Record<string, { enabled: boolean; description?: string }> = {}\r\n const excludeGlobalSlugs = new Set(options.exclude?.globals ?? [])\r\n for (const global of (incomingConfig.globals ?? []) as Array<{ slug: string }>) {\r\n if (excludeGlobalSlugs.has(global.slug)) continue\r\n mcpGlobals[global.slug] = {\r\n enabled: true,\r\n description: `Manage ${global.slug} global settings`,\r\n }\r\n }\r\n\r\n // overrideAuth rebinds req.user from the API key's linked user so our\r\n // custom tools' `overrideAccess: false` checks run against the right\r\n // identity. userCollection passthrough lets the official plugin fall\r\n // back to `incomingConfig.admin.user`.\r\n const withMcp = mcpPlugin({\r\n collections: mcpCollections as any,\r\n globals: mcpGlobals as any,\r\n userCollection: options.userCollection as any,\r\n mcp: {\r\n tools: tools as any[],\r\n prompts: prompts as any[],\r\n resources: resources as any[],\r\n },\r\n overrideAuth: async (req, getDefault) => {\r\n const settings = await getDefault()\r\n req.user = (settings as any).user\r\n return settings\r\n },\r\n })\r\n\r\n return withMcp(incomingConfig)\r\n }\r\n}\r\n\r\nexport type {\r\n ContentToolkitOptions,\r\n DomainPrompt,\r\n CollectionSchema,\r\n BlockCatalog,\r\n BlockSchema,\r\n BlockNestingMap,\r\n BlockNestingEdge,\r\n RelationshipEdge,\r\n FieldSchema,\r\n} from './types'\r\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":"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"}
@@ -1,5 +1,9 @@
1
1
  import type { Block, CollectionConfig } from 'payload';
2
- import type { BlockCatalog, CollectionSchema, RelationshipEdge } from './types';
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
  */
@@ -9,14 +13,21 @@ export declare function introspectCollection(collection: CollectionConfig): Coll
9
13
  */
10
14
  export declare function introspectCollections(collections: CollectionConfig[]): Map<string, CollectionSchema>;
11
15
  /**
12
- * Introspect block configs into a block catalog with section/leaf hierarchy and nesting rules.
16
+ * Build a flat catalog of every block in the schema. Whether a block can
17
+ * nest other blocks is represented separately in the BlockNestingMap, not
18
+ * as a section/leaf classification — the AI reads both and composes
19
+ * arbitrarily-nested layouts from there.
20
+ */
21
+ export declare function introspectBlocks(blocks: Block[]): BlockCatalog;
22
+ /**
23
+ * Walk every collection and every block, recording each `blocks`-typed
24
+ * field's owner, dotted path, and accepted slugs.
13
25
  *
14
- * NOTE: In the Payload plugin context, blockReferences may or may not be resolved
15
- * depending on when introspection runs. This function handles both cases:
16
- * - If field.blocks contains resolved block objects, reads slugs from them
17
- * - If field.blocks is empty and field.blockReferences exists, uses those slugs directly
26
+ * The AI uses this to compose layouts at any depth: it looks up which
27
+ * slugs the relevant field accepts, picks one, then if that block has
28
+ * its own `blocks` fields it recurses against the same map.
18
29
  */
19
- export declare function introspectBlocks(sectionBlocks: Block[], leafBlocks: Block[]): BlockCatalog;
30
+ export declare function buildBlockNestingMap(collections: CollectionConfig[], blocks: Block[]): BlockNestingMap;
20
31
  /**
21
32
  * Build a relationship graph from introspected collection schemas.
22
33
  */