@abinnovision/payloadcms-mcpx 1.1.0-beta.5 → 2.0.0-beta.7

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 (69) hide show
  1. package/README.md +141 -448
  2. package/dist/{client → admin}/capability-matrix.d.mts +3 -4
  3. package/dist/admin/capability-matrix.mjs +354 -0
  4. package/dist/admin/capability-toggles.mjs +73 -0
  5. package/dist/{client → admin}/setup-guide.d.mts +1 -1
  6. package/dist/{client → admin}/setup-guide.mjs +3 -10
  7. package/dist/api-keys/capability-matrix.d.mts +6 -6
  8. package/dist/api-keys/capability-matrix.mjs +15 -85
  9. package/dist/api-keys/fields.mjs +15 -24
  10. package/dist/auth/resolve.mjs +23 -5
  11. package/dist/{tools/names.mjs → builtin-tool-names.mjs} +2 -1
  12. package/dist/capabilities.mjs +34 -42
  13. package/dist/define-tool.d.mts +19 -0
  14. package/dist/{types.mjs → define-tool.mjs} +1 -1
  15. package/dist/endpoint/errors.mjs +5 -6
  16. package/dist/endpoint/handler.mjs +49 -19
  17. package/dist/endpoint/server.mjs +65 -19
  18. package/dist/entity.d.mts +9 -0
  19. package/dist/guards.mjs +5 -0
  20. package/dist/i18n.mjs +8 -8
  21. package/dist/index.d.mts +5 -2
  22. package/dist/index.mjs +3 -2
  23. package/dist/options.mjs +51 -43
  24. package/dist/plugin.mjs +0 -1
  25. package/dist/request.d.mts +2 -3
  26. package/dist/request.mjs +2 -3
  27. package/dist/result.d.mts +2 -3
  28. package/dist/result.mjs +3 -3
  29. package/dist/schema/admin-hidden.mjs +91 -0
  30. package/dist/schema/describe.mjs +17 -33
  31. package/dist/schema/errors.mjs +8 -0
  32. package/dist/schema/index.mjs +5 -2
  33. package/dist/schema/lexical-pointer.mjs +18 -20
  34. package/dist/schema/lexical.mjs +39 -92
  35. package/dist/schema/outline.mjs +5 -15
  36. package/dist/schema/path.mjs +22 -0
  37. package/dist/schema/pointer.mjs +18 -25
  38. package/dist/schema/shape.mjs +25 -71
  39. package/dist/schema/walk.mjs +35 -45
  40. package/dist/tools/builtin.mjs +6 -4
  41. package/dist/tools/create-document.mjs +26 -23
  42. package/dist/tools/describe-schema.mjs +38 -34
  43. package/dist/tools/document.mjs +84 -0
  44. package/dist/tools/find-documents.mjs +32 -19
  45. package/dist/tools/find-versions.mjs +74 -0
  46. package/dist/tools/get-document.mjs +80 -49
  47. package/dist/tools/list-capabilities.mjs +25 -16
  48. package/dist/tools/patch-document.mjs +74 -50
  49. package/dist/tools/publish-document.mjs +17 -28
  50. package/dist/tools/query-paths.mjs +55 -0
  51. package/dist/tools/read-request.d.mts +10 -0
  52. package/dist/tools/read-request.mjs +43 -0
  53. package/dist/tools/shared.mjs +65 -96
  54. package/dist/tools/validate-document.mjs +16 -29
  55. package/dist/tools/versions.mjs +127 -0
  56. package/dist/types.d.mts +55 -55
  57. package/dist/write/draft-guard.mjs +17 -37
  58. package/dist/write/patch.mjs +27 -183
  59. package/dist/write/publish-blockers.mjs +7 -14
  60. package/dist/write/publish-intent.mjs +4 -4
  61. package/dist/write/row-ids.mjs +44 -0
  62. package/dist/write/transaction.mjs +5 -8
  63. package/dist/write/write-data.mjs +64 -0
  64. package/package.json +4 -4
  65. package/dist/client/capability-matrix.mjs +0 -349
  66. package/dist/endpoint/index.mjs +0 -4
  67. package/dist/tools/target.mjs +0 -45
  68. /package/dist/{client → admin}/index.d.mts +0 -0
  69. /package/dist/{client → admin}/index.mjs +0 -0
@@ -1,349 +0,0 @@
1
- "use client";
2
- import { CAPABILITIES_FIELD } from "../capabilities.mjs";
3
- import { CAPABILITY_OPERATIONS, buildToggleActions, capabilityPaths, cellPath, columnState, rowState, toolPath, toolsState } from "../api-keys/capability-matrix.mjs";
4
- import { jsx, jsxs } from "react/jsx-runtime";
5
- import { CheckboxInput, FieldDescription, FieldLabel, useForm, useFormFields } from "@payloadcms/ui";
6
- import { useCallback, useMemo } from "react";
7
- //#region src/client/capability-matrix.tsx
8
- const NOT_EXPOSED = "The plugin config does not expose this.";
9
- const BASE_CLASS = "mcpx-capabilities";
10
- const SHEET = `
11
- .${BASE_CLASS} tbody tr:hover { background: var(--theme-elevation-50); }
12
- .${BASE_CLASS}__toggle:hover { text-decoration: underline; }
13
- .${BASE_CLASS} section:last-of-type { margin-bottom: 0; }
14
- `;
15
- const CELL_PADDING = "calc(var(--base) * 0.6)";
16
- const EDGE_PADDING = "calc(var(--base) * 0.8)";
17
- /**
18
- * Payload's own theme variables, so the matrix follows the admin's light and
19
- * dark themes without shipping a stylesheet consumers would have to transpile.
20
- * The header block borrows the admin table's treatment: muted, raised a step
21
- * off the page and closed with a rule.
22
- *
23
- * The only literal left is the small-caps section heading, which has no
24
- * counterpart in the admin to borrow from.
25
- */ const styles = {
26
- section: { marginBottom: "var(--base)" },
27
- table: {
28
- tableLayout: "fixed",
29
- width: "auto",
30
- borderCollapse: "separate",
31
- borderSpacing: 0,
32
- textAlign: "left"
33
- },
34
- head: { background: "var(--theme-elevation-50)" },
35
- /** The section name, which heads the label column instead of a stray title. */ headTitle: {
36
- padding: `${CELL_PADDING} ${CELL_PADDING} ${CELL_PADDING} ${EDGE_PADDING}`,
37
- verticalAlign: "middle",
38
- fontSize: "0.8em",
39
- letterSpacing: "0.08em",
40
- textTransform: "uppercase",
41
- color: "var(--theme-elevation-400)",
42
- fontWeight: "normal"
43
- },
44
- headOperation: {
45
- padding: CELL_PADDING,
46
- verticalAlign: "middle",
47
- color: "var(--theme-elevation-400)",
48
- fontWeight: "normal",
49
- textAlign: "center",
50
- whiteSpace: "nowrap"
51
- },
52
- headLabel: {
53
- padding: `${CELL_PADDING} ${CELL_PADDING} ${CELL_PADDING} ${EDGE_PADDING}`,
54
- verticalAlign: "middle",
55
- color: "var(--theme-elevation-500)",
56
- fontWeight: "normal"
57
- },
58
- /** Closes the header block, across every column rather than the first. */ rule: { borderBottom: "1px solid var(--theme-border-color)" },
59
- topStart: { borderStartStartRadius: "var(--style-radius-s)" },
60
- topEnd: { borderStartEndRadius: "var(--style-radius-s)" },
61
- rowHeader: {
62
- padding: `${CELL_PADDING} ${CELL_PADDING} ${CELL_PADDING} ${EDGE_PADDING}`,
63
- verticalAlign: "middle",
64
- fontWeight: "normal",
65
- wordBreak: "break-word"
66
- },
67
- rowButton: {
68
- padding: 0,
69
- border: 0,
70
- background: "none",
71
- color: "inherit",
72
- font: "inherit",
73
- cursor: "pointer",
74
- textAlign: "start",
75
- textDecoration: "none"
76
- },
77
- hint: {
78
- display: "block",
79
- color: "var(--theme-elevation-400)"
80
- },
81
- cell: {
82
- padding: CELL_PADDING,
83
- verticalAlign: "middle",
84
- textAlign: "center"
85
- },
86
- lastCell: { paddingInlineEnd: EDGE_PADDING },
87
- dash: {
88
- color: "var(--theme-elevation-400)",
89
- cursor: "help"
90
- }
91
- };
92
- /**
93
- * Payload's own checkbox, which carries the admin's styling and the partial
94
- * state the column and row toggles need. `name` is what it renders as the
95
- * input's `title`, so it doubles as the control's accessible name.
96
- */ const Box = ({ label, onChange, readOnly, state }) => /*#__PURE__*/ jsx(CheckboxInput, {
97
- checked: state === "on",
98
- name: label,
99
- onToggle: onChange,
100
- partialChecked: state === "mixed",
101
- readOnly
102
- });
103
- /** Both in `--base` multiples, so the columns keep Payload's rhythm. */ const LABEL_WIDTH = "calc(var(--base) * 9)";
104
- const OPERATION_WIDTH = "calc(var(--base) * 3.5)";
105
- /** Fixed widths, so every table lines its columns up with the ones above it. */ const Columns = ({ count }) => /*#__PURE__*/ jsxs("colgroup", { children: [/*#__PURE__*/ jsx("col", { style: { width: LABEL_WIDTH } }), Array.from({ length: count }, (_, index) => /*#__PURE__*/ jsx("col", { style: { width: OPERATION_WIDTH } }, index))] });
106
- /** The rightmost cell carries the table's outer padding, as Payload's does. */ const cellStyle = (isLast) => isLast ? {
107
- ...styles.cell,
108
- ...styles.lastCell
109
- } : styles.cell;
110
- const Dash = () => /*#__PURE__*/ jsx("span", {
111
- "aria-label": NOT_EXPOSED,
112
- style: styles.dash,
113
- title: NOT_EXPOSED,
114
- children: "—"
115
- });
116
- /**
117
- * What an API key may do, as one table per namespace: a row per entity, a
118
- * column per operation, and a bulk toggle in each column header. The nested
119
- * checkbox fields this replaces still back every cell, so the stored document
120
- * is unchanged; only the rendering is.
121
- */ const McpxCapabilityMatrix = ({ field, matrix, path = CAPABILITIES_FIELD, readOnly = false, withinTab = false }) => {
122
- const { dispatchFields, setModified } = useForm();
123
- const paths = useMemo(() => capabilityPaths(matrix, path), [matrix, path]);
124
- const encoded = useFormFields(([fields]) => paths.map((cell) => fields[cell]?.value === true ? "1" : "0").join(""));
125
- const values = useMemo(() => Object.fromEntries(paths.map((cell, index) => [cell, encoded[index] === "1"])), [paths, encoded]);
126
- const toggle = useCallback((intent) => {
127
- const actions = buildToggleActions(matrix, path, values, intent);
128
- if (actions.length === 0) return;
129
- for (const action of actions) dispatchFields(action);
130
- setModified(true);
131
- }, [
132
- matrix,
133
- path,
134
- values,
135
- dispatchFields,
136
- setModified
137
- ]);
138
- const namespaces = [{
139
- id: "collections",
140
- title: "Collections"
141
- }, {
142
- id: "globals",
143
- title: "Globals"
144
- }].filter((namespace) => matrix[namespace.id].length > 0);
145
- const toolState = toolsState(matrix, path, values);
146
- return /*#__PURE__*/ jsxs("div", {
147
- className: [
148
- "field-type",
149
- "group-field",
150
- "group-field--top-level",
151
- withinTab && "group-field--within-tab",
152
- BASE_CLASS
153
- ].filter(Boolean).join(" "),
154
- children: [/*#__PURE__*/ jsx("style", { children: SHEET }), /*#__PURE__*/ jsxs("div", {
155
- className: "group-field__wrap",
156
- children: [
157
- /*#__PURE__*/ jsx("div", {
158
- className: "group-field__header",
159
- children: /*#__PURE__*/ jsxs("header", { children: [/*#__PURE__*/ jsx("h3", {
160
- className: "group-field__title",
161
- children: /*#__PURE__*/ jsx(FieldLabel, {
162
- as: "span",
163
- label: "Capabilities"
164
- })
165
- }), typeof field?.admin?.description === "string" ? /*#__PURE__*/ jsx(FieldDescription, {
166
- description: field.admin.description,
167
- path
168
- }) : null] })
169
- }),
170
- namespaces.map((namespace) => {
171
- const rows = matrix[namespace.id];
172
- const columns = CAPABILITY_OPERATIONS.filter((operation) => rows.some((row) => row[operation.id]));
173
- return /*#__PURE__*/ jsx("section", {
174
- style: styles.section,
175
- children: /*#__PURE__*/ jsxs("table", {
176
- style: styles.table,
177
- children: [
178
- /*#__PURE__*/ jsx(Columns, { count: columns.length }),
179
- /*#__PURE__*/ jsxs("thead", {
180
- style: styles.head,
181
- children: [/*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
182
- scope: "col",
183
- style: {
184
- ...styles.headTitle,
185
- ...styles.topStart
186
- },
187
- children: namespace.title
188
- }), columns.map((operation, column) => /*#__PURE__*/ jsx("th", {
189
- scope: "col",
190
- style: column === columns.length - 1 ? {
191
- ...styles.headOperation,
192
- ...styles.topEnd
193
- } : styles.headOperation,
194
- title: operation.description,
195
- children: operation.label
196
- }, operation.id))] }), /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
197
- scope: "row",
198
- style: {
199
- ...styles.headLabel,
200
- ...styles.rule
201
- },
202
- children: "All"
203
- }), columns.map((operation, column) => {
204
- const state = columnState(matrix, path, namespace.id, operation.id, values);
205
- return /*#__PURE__*/ jsx("td", {
206
- style: {
207
- ...cellStyle(column === columns.length - 1),
208
- ...styles.rule
209
- },
210
- children: /*#__PURE__*/ jsx(Box, {
211
- label: `${operation.label} every ${namespace.title.toLowerCase()} entry`,
212
- onChange: () => {
213
- toggle({
214
- kind: "column",
215
- namespace: namespace.id,
216
- operation: operation.id,
217
- value: state !== "on"
218
- });
219
- },
220
- readOnly,
221
- state
222
- })
223
- }, operation.id);
224
- })] })]
225
- }),
226
- /*#__PURE__*/ jsx("tbody", { children: rows.map((row) => {
227
- const state = rowState(path, namespace.id, row, values);
228
- return /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsxs("th", {
229
- scope: "row",
230
- style: styles.rowHeader,
231
- children: [/*#__PURE__*/ jsx("button", {
232
- className: `${BASE_CLASS}__toggle`,
233
- disabled: readOnly,
234
- onClick: () => {
235
- toggle({
236
- kind: "row",
237
- namespace: namespace.id,
238
- fieldName: row.fieldName,
239
- value: state !== "on"
240
- });
241
- },
242
- style: styles.rowButton,
243
- title: `Toggle every capability for ${row.label}`,
244
- type: "button",
245
- children: row.label
246
- }), row.hint ? /*#__PURE__*/ jsx("span", {
247
- style: styles.hint,
248
- children: row.hint
249
- }) : null]
250
- }), columns.map((operation, column) => /*#__PURE__*/ jsx("td", {
251
- style: cellStyle(column === columns.length - 1),
252
- children: row[operation.id] ? /*#__PURE__*/ jsx(Box, {
253
- label: `${operation.label} ${row.label}`,
254
- onChange: () => {
255
- toggle({
256
- kind: "cell",
257
- namespace: namespace.id,
258
- fieldName: row.fieldName,
259
- operation: operation.id,
260
- value: values[cellPath(path, namespace.id, row.fieldName, operation.id)] !== true
261
- });
262
- },
263
- readOnly,
264
- state: values[cellPath(path, namespace.id, row.fieldName, operation.id)] === true ? "on" : "off"
265
- }) : /*#__PURE__*/ jsx(Dash, {})
266
- }, operation.id))] }, row.fieldName);
267
- }) })
268
- ]
269
- })
270
- }, namespace.id);
271
- }),
272
- matrix.tools.length > 0 ? /*#__PURE__*/ jsx("section", {
273
- style: styles.section,
274
- children: /*#__PURE__*/ jsxs("table", {
275
- style: styles.table,
276
- children: [
277
- /*#__PURE__*/ jsx(Columns, { count: 1 }),
278
- /*#__PURE__*/ jsxs("thead", {
279
- style: styles.head,
280
- children: [/*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
281
- scope: "col",
282
- style: {
283
- ...styles.headTitle,
284
- ...styles.topStart
285
- },
286
- children: "Tools"
287
- }), /*#__PURE__*/ jsx("th", {
288
- scope: "col",
289
- style: {
290
- ...styles.headOperation,
291
- ...styles.topEnd
292
- },
293
- children: "Enabled"
294
- })] }), /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
295
- scope: "row",
296
- style: {
297
- ...styles.headLabel,
298
- ...styles.rule
299
- },
300
- children: "All"
301
- }), /*#__PURE__*/ jsx("td", {
302
- style: {
303
- ...cellStyle(true),
304
- ...styles.rule
305
- },
306
- children: /*#__PURE__*/ jsx(Box, {
307
- label: "Enable every tool",
308
- onChange: () => {
309
- toggle({
310
- kind: "tools",
311
- value: toolState !== "on"
312
- });
313
- },
314
- readOnly,
315
- state: toolState
316
- })
317
- })] })]
318
- }),
319
- /*#__PURE__*/ jsx("tbody", { children: matrix.tools.map((tool) => /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsxs("th", {
320
- scope: "row",
321
- style: styles.rowHeader,
322
- children: [tool.name, /*#__PURE__*/ jsx("span", {
323
- style: styles.hint,
324
- children: tool.description
325
- })]
326
- }), /*#__PURE__*/ jsx("td", {
327
- style: cellStyle(true),
328
- children: /*#__PURE__*/ jsx(Box, {
329
- label: `Enable ${tool.name}`,
330
- onChange: () => {
331
- toggle({
332
- kind: "tool",
333
- name: tool.name,
334
- value: values[toolPath(path, tool.name)] !== true
335
- });
336
- },
337
- readOnly,
338
- state: values[toolPath(path, tool.name)] === true ? "on" : "off"
339
- })
340
- })] }, tool.name)) })
341
- ]
342
- })
343
- }) : null
344
- ]
345
- })]
346
- });
347
- };
348
- //#endregion
349
- export { McpxCapabilityMatrix };
@@ -1,4 +0,0 @@
1
- import { jsonRpcError, toToolError } from "./errors.mjs";
2
- import { createMcpServer, isToolEnabled, toolDescription, toolInputSchema } from "./server.mjs";
3
- import { createMcpxHandler, methodNotAllowed } from "./handler.mjs";
4
- export { createMcpServer, createMcpxHandler, isToolEnabled, jsonRpcError, methodNotAllowed, toToolError, toolDescription, toolInputSchema };
@@ -1,45 +0,0 @@
1
- import { slugsFor } from "./shared.mjs";
2
- import { APIError, Forbidden } from "payload";
3
- //#region src/tools/target.ts
4
- const refOf = (target) => ({
5
- kind: target.kind,
6
- slug: target.slug
7
- });
8
- /**
9
- * A raw input shape leaves no top-level `.refine` to express "exactly one of
10
- * collection and global", so the rule is enforced here, with a message naming
11
- * the offending arguments.
12
- */ const resolveTarget = (scope, args, operation) => {
13
- const { collection, global } = args;
14
- const allowedSlugs = slugsFor(scope, operation);
15
- if (collection !== void 0 && global !== void 0) throw new APIError("Pass either \"collection\" or \"global\", not both.", 400);
16
- if (collection === void 0 && global === void 0) throw new APIError("One of \"collection\" or \"global\" is required. Call listCapabilities to see which slugs are available.", 400);
17
- if (collection !== void 0) {
18
- const found = scope.req.payload.collections[collection];
19
- if (!allowedSlugs.collections.includes(collection) || !found) throw new Forbidden(scope.req.t);
20
- return {
21
- kind: "collection",
22
- slug: collection,
23
- config: found.config
24
- };
25
- }
26
- const slug = global;
27
- const found = scope.req.payload.globals.config.find((candidate) => candidate.slug === slug);
28
- if (!allowedSlugs.globals.includes(slug) || !found) throw new Forbidden(scope.req.t);
29
- return {
30
- kind: "global",
31
- slug,
32
- config: found
33
- };
34
- };
35
- /**
36
- * Checks `id` against the resolved target. A collection document needs one; a
37
- * global is a singleton and must not carry one. The schema cannot express the
38
- * dependency, so it is stated here and in every affected tool description.
39
- */ const requireIdFor = (target, id) => {
40
- if (target.kind === "collection" && id === void 0) throw new APIError(`"id" is required when "collection" is "${target.slug}".`, 400);
41
- if (target.kind === "global" && id !== void 0) throw new APIError(`"id" must be omitted when "global" is "${target.slug}"; a global is a singleton.`, 400);
42
- return target.kind === "collection" ? id : void 0;
43
- };
44
- //#endregion
45
- export { refOf, requireIdFor, resolveTarget };
File without changes
File without changes