@abinnovision/payloadcms-mcpx 2.0.0-beta.15 → 2.0.0-beta.16

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.
@@ -6,6 +6,7 @@ import { collectPublishBlockers } from "../write/publish-blockers.mjs";
6
6
  import { assertAcceptsFile, fileShape, fileWriteRefusal, uploadHandoff, uploadedFile } from "../upload/file.mjs";
7
7
  import { liveWriteSentence, localeOf, localeShape, patchOnlySlugs, slugEnum, slugsFor } from "./shared.mjs";
8
8
  import { readDraft, resolveEntity } from "./document.mjs";
9
+ import { documentLinks } from "./links.mjs";
9
10
  import { stripRowIds } from "../write/row-ids.mjs";
10
11
  import { z } from "zod";
11
12
  import { APIError } from "payload";
@@ -14,7 +15,7 @@ const uploadSentence = (scope) => {
14
15
  const slugs = patchOnlySlugs(scope);
15
16
  return slugs.length === 0 ? "" : ` Documents in ${slugs.join(", ")} are files and cannot be created here. Upload the file in the admin panel, then edit its fields with patchDocument.`;
16
17
  };
17
- const DESCRIPTION = (scope) => `Creates a document in a collection. Use it only when the document does not exist yet. Returns "id", "status", "updatedAt" and, if any, "publishBlockers". "publishBlockersUnavailable" means that check failed. "data" may leave required fields empty for patchDocument to fill later, except in a collection whose listCapabilities entry has "draftValidation" true or "drafts" false. A field describeSchema does not list is refused, and "id" is always assigned.
18
+ const DESCRIPTION = (scope) => `Creates a document in a collection. Use it only when the document does not exist yet. Returns "id", "status", "updatedAt" and, if any, "publishBlockers". "publishBlockersUnavailable" means that check failed. "data" may leave required fields empty for patchDocument to fill later, except in a collection whose listCapabilities entry has "draftValidation" true or "drafts" false. A field describeSchema does not list is refused, and "id" is always assigned. The response links the document with "adminUrl" and "previewUrl".
18
19
 
19
20
  ${liveWriteSentence(scope, "create")}${uploadSentence(scope)}`;
20
21
  /**
@@ -104,6 +105,11 @@ ${liveWriteSentence(scope, "create")}${uploadSentence(scope)}`;
104
105
  id: saved["id"],
105
106
  status: saved["_status"],
106
107
  updatedAt: saved["updatedAt"],
108
+ ...await documentLinks(scope.req, {
109
+ target,
110
+ doc: saved,
111
+ locale
112
+ }),
107
113
  ...validation.blockers.length > 0 ? { publishBlockers: validation.blockers } : {},
108
114
  ...validation.unavailable ? { publishBlockersUnavailable: true } : {}
109
115
  });
@@ -0,0 +1,52 @@
1
+ import { formatAdminURL } from "payload/shared";
2
+ //#region src/tools/links.ts
3
+ const livePreviewOf = (req, target) => {
4
+ if (target.config.admin.livePreview) return target.config.admin.livePreview;
5
+ const root = req.payload.config.admin.livePreview;
6
+ return (target.kind === "collection" ? root?.collections : root?.globals)?.includes(target.slug) ? root : void 0;
7
+ };
8
+ const previewOf = async (req, args) => {
9
+ const { target, doc, locale } = args;
10
+ const { preview } = target.config.admin;
11
+ if (preview) return await preview(doc, {
12
+ locale: locale ?? "",
13
+ req,
14
+ token: null
15
+ });
16
+ const url = livePreviewOf(req, target)?.url;
17
+ if (typeof url !== "function") return url;
18
+ const { localization } = req.payload.config;
19
+ const entry = localization ? localization.locales.find((item) => item.code === locale) : void 0;
20
+ return await url({
21
+ ...target.kind === "collection" ? { collectionConfig: target.config } : { globalConfig: target.config },
22
+ data: doc,
23
+ locale: entry ?? {
24
+ code: locale ?? "",
25
+ label: locale ?? ""
26
+ },
27
+ payload: req.payload,
28
+ req
29
+ });
30
+ };
31
+ /**
32
+ * The admin panel URL of a written document, and its preview URL where the
33
+ * entity configures one. Both sit at the origin of `serverURL` when set, else
34
+ * of the MCP request. A preview that fails is left out: the write has landed.
35
+ */ const documentLinks = async (req, args) => {
36
+ const { target, doc, locale } = args;
37
+ const { config } = req.payload;
38
+ const origin = config.serverURL || new URL(req.url ?? "").origin;
39
+ const path = target.kind === "collection" ? `/collections/${target.slug}/${String(doc["id"])}` : `/globals/${target.slug}`;
40
+ const adminUrl = formatAdminURL({
41
+ adminRoute: config.routes.admin,
42
+ path,
43
+ serverURL: origin
44
+ });
45
+ const preview = await previewOf(req, args).catch(() => void 0);
46
+ return {
47
+ adminUrl: locale ? `${adminUrl}?locale=${encodeURIComponent(locale)}` : adminUrl,
48
+ ...preview ? { previewUrl: new URL(preview, origin).toString() } : {}
49
+ };
50
+ };
51
+ //#endregion
52
+ export { documentLinks };
@@ -7,6 +7,7 @@ import { collectPublishBlockers } from "../write/publish-blockers.mjs";
7
7
  import { assertAcceptsFile, fileShape, fileSlugs, fileWriteRefusal, uploadHandoff, uploadedFile } from "../upload/file.mjs";
8
8
  import { entityShape, idShape, liveWriteSentence, localeOf, localeShape } from "./shared.mjs";
9
9
  import { identityOf, readDraft, resolveDocument, staleReadResult } from "./document.mjs";
10
+ import { documentLinks } from "./links.mjs";
10
11
  import { withTransaction } from "../write/transaction.mjs";
11
12
  import { applyPatchOperations, isElementPointer } from "../write/patch.mjs";
12
13
  import { buildWriteData } from "../write/write-data.mjs";
@@ -22,7 +23,7 @@ Read the document with getDocument right before patching. Append to a list with
22
23
 
23
24
  In a rich text field a pointer continues into the rich text state: "/content/root/children/2" is a node, "/content/root/children/2/tag" one of its properties and "/content/root/children/3/fields/tone" a field of a block node. getDocument "outline" lists each node's pointer and "version". Build nodes from describeSchema "nodeProperties". Check a position before writing to it, e.g. {"op":"test","path":"/content/root/children/2/type","value":"heading"}.
24
25
 
25
- The response carries "updatedAt" and, if any, "publishBlockers": what must be fixed before publishing. "publishBlockersUnavailable" means that check failed. The write stands either way. "notApplied" lists pointers whose value did not change or cannot be read back, for example where field access denies the update.`;
26
+ The response carries "updatedAt" and, if any, "publishBlockers": what must be fixed before publishing. "publishBlockersUnavailable" means that check failed. The write stands either way. "notApplied" lists pointers whose value did not change or cannot be read back, for example where field access denies the update. The response links the document with "adminUrl" and "previewUrl".`;
26
27
  const POINTER = z.string().regex(JSON_POINTER_PATTERN);
27
28
  const PATCHES_LIMIT = 500;
28
29
  /**
@@ -130,6 +131,11 @@ const notAppliedPointers = (patches, intended, saved) => patches.flatMap((operat
130
131
  ...identityOf(target, saved["id"]),
131
132
  status: saved["_status"],
132
133
  updatedAt: saved["updatedAt"],
134
+ ...await documentLinks(scope.req, {
135
+ target,
136
+ doc: saved,
137
+ locale
138
+ }),
133
139
  ...validation.blockers.length > 0 ? { publishBlockers: validation.blockers } : {},
134
140
  ...validation.unavailable ? { publishBlockersUnavailable: true } : {},
135
141
  ...notApplied.length > 0 ? { notApplied } : {}
@@ -2,6 +2,7 @@ import { defineMcpxTool } from "../define-tool.mjs";
2
2
  import { jsonResult } from "../result.mjs";
3
3
  import { entityShape, idShape, localeOf, localeShape } from "./shared.mjs";
4
4
  import { collectLocaleBlockers, identityOf, readDraft, resolveDocument, staleReadResult } from "./document.mjs";
5
+ import { documentLinks } from "./links.mjs";
5
6
  import { withPublishIntent } from "../write/publish-intent.mjs";
6
7
  import { withTransaction } from "../write/transaction.mjs";
7
8
  import { z } from "zod";
@@ -10,7 +11,7 @@ import { z } from "zod";
10
11
  * with drafts and the key has both the `write` and `publish` checkboxes.
11
12
  */ const publishDocument = defineMcpxTool({
12
13
  name: "publishDocument",
13
- description: `Makes the current draft of one document or global public, in every locale or only in "locale". A scoped publish keeps the other locales of localized fields as last published, and a never-published document goes live in full. A draft with publish blockers is refused, so call validateDocument first. Payload checks only the published locale, so "otherLocaleBlockers" lists what other locales still lack. There is no unpublish.`,
14
+ description: `Makes the current draft of one document or global public, in every locale or only in "locale". A scoped publish keeps the other locales of localized fields as last published, and a never-published document goes live in full. A draft with publish blockers is refused, so call validateDocument first. Payload checks only the published locale, so "otherLocaleBlockers" lists what other locales still lack. There is no unpublish. The response links the document with "adminUrl" and "previewUrl".`,
14
15
  annotations: {
15
16
  destructiveHint: true,
16
17
  openWorldHint: false
@@ -63,6 +64,11 @@ import { z } from "zod";
63
64
  ...identityOf(target, saved["id"]),
64
65
  status: saved["_status"],
65
66
  updatedAt: saved["updatedAt"],
67
+ ...await documentLinks(scope.req, {
68
+ target,
69
+ doc: saved,
70
+ locale
71
+ }),
66
72
  ...others.blockers.length > 0 ? { otherLocaleBlockers: others.blockers } : {},
67
73
  ...others.unavailable ? { otherLocaleBlockersUnavailable: true } : {}
68
74
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@abinnovision/payloadcms-mcpx",
4
- "version": "2.0.0-beta.15",
4
+ "version": "2.0.0-beta.16",
5
5
  "description": "Payload CMS plugin exposing a fixed, schema-aware MCP tool surface with draft-only writes and per-API-key capabilities.",
6
6
  "keywords": [
7
7
  "payload",