payload-mcp-toolkit 0.3.4 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -151
- package/dist/api-keys.d.ts +46 -0
- package/dist/api-keys.js +308 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/auth-strategy.d.ts +96 -0
- package/dist/auth-strategy.js +261 -0
- package/dist/auth-strategy.js.map +1 -0
- package/dist/components/CollectionScopesMatrix.d.ts +8 -0
- package/dist/components/CollectionScopesMatrix.js +32 -0
- package/dist/components/CollectionScopesMatrix.js.map +1 -0
- package/dist/components/GlobalScopesMatrix.d.ts +8 -0
- package/dist/components/GlobalScopesMatrix.js +28 -0
- package/dist/components/GlobalScopesMatrix.js.map +1 -0
- package/dist/components/ScopesTable.d.ts +19 -0
- package/dist/components/ScopesTable.js +285 -0
- package/dist/components/ScopesTable.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -0
- package/dist/conflict-detection.d.ts +13 -0
- package/dist/conflict-detection.js +41 -0
- package/dist/conflict-detection.js.map +1 -0
- package/dist/draft-workflow.d.ts +46 -48
- package/dist/draft-workflow.js +53 -135
- package/dist/draft-workflow.js.map +1 -1
- package/dist/endpoint.d.ts +35 -0
- package/dist/endpoint.js +105 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/hash.d.ts +21 -0
- package/dist/hash.js +36 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +9 -9
- package/dist/index.js +167 -69
- package/dist/index.js.map +1 -1
- package/dist/introspection.d.ts +17 -3
- package/dist/introspection.js +95 -36
- package/dist/introspection.js.map +1 -1
- package/dist/prompts.js +5 -5
- package/dist/prompts.js.map +1 -1
- package/dist/registry.d.ts +50 -0
- package/dist/registry.js +169 -0
- package/dist/registry.js.map +1 -0
- package/dist/resources.d.ts +5 -3
- package/dist/resources.js +23 -11
- package/dist/resources.js.map +1 -1
- package/dist/scope/audit-log.d.ts +18 -0
- package/dist/scope/audit-log.js +50 -0
- package/dist/scope/audit-log.js.map +1 -0
- package/dist/scope/policy.d.ts +73 -0
- package/dist/scope/policy.js +218 -0
- package/dist/scope/policy.js.map +1 -0
- package/dist/tools/_helpers.d.ts +62 -1
- package/dist/tools/_helpers.js +181 -0
- package/dist/tools/_helpers.js.map +1 -1
- package/dist/tools/_layout-helpers.d.ts +43 -0
- package/dist/tools/_layout-helpers.js +159 -0
- package/dist/tools/_layout-helpers.js.map +1 -0
- package/dist/tools/create-document.d.ts +5 -5
- package/dist/tools/create-document.js +25 -21
- package/dist/tools/create-document.js.map +1 -1
- package/dist/tools/delete-document.d.ts +25 -0
- package/dist/tools/delete-document.js +49 -0
- package/dist/tools/delete-document.js.map +1 -0
- package/dist/tools/find-document.d.ts +33 -0
- package/dist/tools/find-document.js +97 -0
- package/dist/tools/find-document.js.map +1 -0
- package/dist/tools/find-global.d.ts +26 -0
- package/dist/tools/find-global.js +122 -0
- package/dist/tools/find-global.js.map +1 -0
- package/dist/tools/global-versions.d.ts +39 -0
- package/dist/tools/global-versions.js +132 -0
- package/dist/tools/global-versions.js.map +1 -0
- package/dist/tools/patch-global-layout.d.ts +31 -0
- package/dist/tools/patch-global-layout.js +127 -0
- package/dist/tools/patch-global-layout.js.map +1 -0
- package/dist/tools/patch-layout.d.ts +5 -8
- package/dist/tools/patch-layout.js +18 -100
- package/dist/tools/patch-layout.js.map +1 -1
- package/dist/tools/publish-draft.d.ts +5 -4
- package/dist/tools/publish-draft.js +39 -2
- package/dist/tools/publish-draft.js.map +1 -1
- package/dist/tools/publish-global-draft.d.ts +20 -0
- package/dist/tools/publish-global-draft.js +79 -0
- package/dist/tools/publish-global-draft.js.map +1 -0
- package/dist/tools/resolve-reference.d.ts +5 -4
- package/dist/tools/resolve-reference.js +4 -0
- package/dist/tools/resolve-reference.js.map +1 -1
- package/dist/tools/safe-delete.d.ts +5 -5
- package/dist/tools/safe-delete.js +20 -15
- package/dist/tools/safe-delete.js.map +1 -1
- package/dist/tools/schedule-publish.d.ts +5 -5
- package/dist/tools/schedule-publish.js +23 -19
- package/dist/tools/schedule-publish.js.map +1 -1
- package/dist/tools/search-content.d.ts +5 -9
- package/dist/tools/search-content.js +16 -12
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/update-document.d.ts +5 -5
- package/dist/tools/update-document.js +10 -5
- package/dist/tools/update-document.js.map +1 -1
- package/dist/tools/update-global.d.ts +27 -0
- package/dist/tools/update-global.js +72 -0
- package/dist/tools/update-global.js.map +1 -0
- package/dist/tools/upload-media.d.ts +5 -4
- package/dist/tools/upload-media.js +6 -1
- package/dist/tools/upload-media.js.map +1 -1
- package/dist/tools/versions.d.ts +10 -9
- package/dist/tools/versions.js +15 -7
- package/dist/tools/versions.js.map +1 -1
- package/dist/types.d.ts +56 -3
- package/dist/types.js +13 -6
- package/dist/types.js.map +1 -1
- package/package.json +39 -18
- package/dist/__tests__/introspection.test.js +0 -459
- package/dist/__tests__/introspection.test.js.map +0 -1
- package/dist/__tests__/url-validator.test.js +0 -326
- package/dist/__tests__/url-validator.test.js.map +0 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared internals for patchLayout (collections) and patchGlobalLayout
|
|
3
|
+
* (globals). Both tools differ only in their fetch/write pairs and their
|
|
4
|
+
* nesting-map filter — every other piece of logic (block validation,
|
|
5
|
+
* operation application, dotted-path navigation, error shape) is identical
|
|
6
|
+
* and lives here. Keeping them separate as tools is intentional from the
|
|
7
|
+
* LLM's perspective; keeping the code single-sourced prevents the kind of
|
|
8
|
+
* drift that originally let only one side learn about dotted paths.
|
|
9
|
+
*/
|
|
10
|
+
export declare function errorResponse(message: string, extra?: Record<string, unknown>): import("./_helpers").McpTextResponse;
|
|
11
|
+
/**
|
|
12
|
+
* Recursively validate a block array against an allow list, descending into
|
|
13
|
+
* each block's own `blocks`-typed fields when present. A value is treated
|
|
14
|
+
* as a nested blocks field whenever it is an array of objects that each
|
|
15
|
+
* carry a `blockType` discriminator; the nesting map decides which slugs
|
|
16
|
+
* are admissible at that position.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateBlockList(blocks: Array<Record<string, unknown>>, allowedSlugs: string[], pathLabel: string, allBlockSlugs: Set<string>, nestingByBlockField: Map<string, string[]>, errors: string[]): void;
|
|
19
|
+
/**
|
|
20
|
+
* Apply a list operation against an existing array of blocks.
|
|
21
|
+
* `full` always replaces; the rest preserve the existing array.
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyOperation(newBlocks: Record<string, unknown>[], operation: 'full' | 'append' | 'prepend' | 'insertAt' | 'replaceAt', insertIndex: number | undefined, existingLayout: Record<string, unknown>[] | undefined): Record<string, unknown>[];
|
|
24
|
+
/** Walk a dotted path on an object and return the value at the leaf, or `[]`
|
|
25
|
+
* if any segment is missing or not an object. Used to pull the current
|
|
26
|
+
* blocks array out of a fetched document/global. */
|
|
27
|
+
export declare function readPath(obj: Record<string, unknown> | undefined, path: string): Record<string, unknown>[];
|
|
28
|
+
/**
|
|
29
|
+
* Write `value` at a dotted `path`, returning a new object whose top-level
|
|
30
|
+
* segment is a merge of `base`'s existing siblings with the patched leaf.
|
|
31
|
+
*
|
|
32
|
+
* Load-bearing for the sibling-wipe fix in patchGlobalLayout: Payload's
|
|
33
|
+
* `updateGlobal` only merges at the top level. So if the global is
|
|
34
|
+
* `{sections: {layout: [...], copyright: 'foo'}}` and we patch
|
|
35
|
+
* `sections.layout`, naively writing `{sections: {layout: [...]}}` makes
|
|
36
|
+
* Payload overwrite the entire `sections` group and `copyright` silently
|
|
37
|
+
* vanishes. By accepting `base` (the existing document) we can splice the
|
|
38
|
+
* new layout into a copy of every parent group along the dotted path,
|
|
39
|
+
* preserving siblings at every depth. For a flat (non-dotted) path this
|
|
40
|
+
* still produces `{[path]: value}` exactly as before — Payload then merges
|
|
41
|
+
* other top-level fields normally — so existing callers see no change.
|
|
42
|
+
*/
|
|
43
|
+
export declare function writePath(base: Record<string, unknown> | undefined, path: string, value: unknown): Record<string, unknown>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { jsonResponse } from './_helpers';
|
|
2
|
+
/**
|
|
3
|
+
* Shared internals for patchLayout (collections) and patchGlobalLayout
|
|
4
|
+
* (globals). Both tools differ only in their fetch/write pairs and their
|
|
5
|
+
* nesting-map filter — every other piece of logic (block validation,
|
|
6
|
+
* operation application, dotted-path navigation, error shape) is identical
|
|
7
|
+
* and lives here. Keeping them separate as tools is intentional from the
|
|
8
|
+
* LLM's perspective; keeping the code single-sourced prevents the kind of
|
|
9
|
+
* drift that originally let only one side learn about dotted paths.
|
|
10
|
+
*/ export function errorResponse(message, extra) {
|
|
11
|
+
return jsonResponse({
|
|
12
|
+
success: false,
|
|
13
|
+
error: message,
|
|
14
|
+
...extra ?? {}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Recursively validate a block array against an allow list, descending into
|
|
19
|
+
* each block's own `blocks`-typed fields when present. A value is treated
|
|
20
|
+
* as a nested blocks field whenever it is an array of objects that each
|
|
21
|
+
* carry a `blockType` discriminator; the nesting map decides which slugs
|
|
22
|
+
* are admissible at that position.
|
|
23
|
+
*/ export function validateBlockList(blocks, allowedSlugs, pathLabel, allBlockSlugs, nestingByBlockField, errors) {
|
|
24
|
+
for(let i = 0; i < blocks.length; i++){
|
|
25
|
+
const block = blocks[i];
|
|
26
|
+
const here = `${pathLabel}[${i}]`;
|
|
27
|
+
if (!block || typeof block !== 'object') {
|
|
28
|
+
errors.push(`${here}: not an object`);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const slug = block.blockType;
|
|
32
|
+
if (typeof slug !== 'string' || !slug) {
|
|
33
|
+
errors.push(`${here}: missing string \`blockType\``);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!allBlockSlugs.has(slug)) {
|
|
37
|
+
errors.push(`${here}: unknown blockType "${slug}". Known: ${[
|
|
38
|
+
...allBlockSlugs
|
|
39
|
+
].join(', ')}`);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!allowedSlugs.includes(slug)) {
|
|
43
|
+
errors.push(`${here}: blockType "${slug}" not allowed here. Allowed at this position: ${allowedSlugs.join(', ') || '(none)'}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
for (const [fieldName, value] of Object.entries(block)){
|
|
47
|
+
if (!Array.isArray(value)) continue;
|
|
48
|
+
if (value.length === 0) continue;
|
|
49
|
+
if (!value.every((v)=>v && typeof v === 'object' && 'blockType' in v)) continue;
|
|
50
|
+
const nextKey = `${slug}:${fieldName}`;
|
|
51
|
+
const nextAllowed = nestingByBlockField.get(nextKey);
|
|
52
|
+
if (!nextAllowed) {
|
|
53
|
+
errors.push(`${here}.${fieldName}: block "${slug}" has no blocks field named "${fieldName}" in the schema`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
validateBlockList(value, nextAllowed, `${here}.${fieldName}`, allBlockSlugs, nestingByBlockField, errors);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Apply a list operation against an existing array of blocks.
|
|
62
|
+
* `full` always replaces; the rest preserve the existing array.
|
|
63
|
+
*/ export function applyOperation(newBlocks, operation, insertIndex, existingLayout) {
|
|
64
|
+
if (operation === 'full' || !existingLayout) {
|
|
65
|
+
return newBlocks;
|
|
66
|
+
}
|
|
67
|
+
const existing = [
|
|
68
|
+
...existingLayout
|
|
69
|
+
];
|
|
70
|
+
if (operation === 'append') return [
|
|
71
|
+
...existing,
|
|
72
|
+
...newBlocks
|
|
73
|
+
];
|
|
74
|
+
if (operation === 'prepend') return [
|
|
75
|
+
...newBlocks,
|
|
76
|
+
...existing
|
|
77
|
+
];
|
|
78
|
+
if (operation === 'insertAt') {
|
|
79
|
+
if (insertIndex === undefined || insertIndex < 0 || insertIndex > existing.length) {
|
|
80
|
+
return [
|
|
81
|
+
...existing,
|
|
82
|
+
...newBlocks
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
existing.splice(insertIndex, 0, ...newBlocks);
|
|
86
|
+
return existing;
|
|
87
|
+
}
|
|
88
|
+
if (operation === 'replaceAt') {
|
|
89
|
+
if (insertIndex === undefined || insertIndex < 0 || insertIndex >= existing.length) {
|
|
90
|
+
return existing;
|
|
91
|
+
}
|
|
92
|
+
existing.splice(insertIndex, newBlocks.length, ...newBlocks);
|
|
93
|
+
return existing;
|
|
94
|
+
}
|
|
95
|
+
return newBlocks;
|
|
96
|
+
}
|
|
97
|
+
/** Walk a dotted path on an object and return the value at the leaf, or `[]`
|
|
98
|
+
* if any segment is missing or not an object. Used to pull the current
|
|
99
|
+
* blocks array out of a fetched document/global. */ export function readPath(obj, path) {
|
|
100
|
+
if (!obj) return [];
|
|
101
|
+
const parts = path.split('.');
|
|
102
|
+
let cur = obj;
|
|
103
|
+
for (const part of parts){
|
|
104
|
+
if (cur && typeof cur === 'object' && !Array.isArray(cur)) {
|
|
105
|
+
cur = cur[part];
|
|
106
|
+
} else {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return Array.isArray(cur) ? cur : [];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Write `value` at a dotted `path`, returning a new object whose top-level
|
|
114
|
+
* segment is a merge of `base`'s existing siblings with the patched leaf.
|
|
115
|
+
*
|
|
116
|
+
* Load-bearing for the sibling-wipe fix in patchGlobalLayout: Payload's
|
|
117
|
+
* `updateGlobal` only merges at the top level. So if the global is
|
|
118
|
+
* `{sections: {layout: [...], copyright: 'foo'}}` and we patch
|
|
119
|
+
* `sections.layout`, naively writing `{sections: {layout: [...]}}` makes
|
|
120
|
+
* Payload overwrite the entire `sections` group and `copyright` silently
|
|
121
|
+
* vanishes. By accepting `base` (the existing document) we can splice the
|
|
122
|
+
* new layout into a copy of every parent group along the dotted path,
|
|
123
|
+
* preserving siblings at every depth. For a flat (non-dotted) path this
|
|
124
|
+
* still produces `{[path]: value}` exactly as before — Payload then merges
|
|
125
|
+
* other top-level fields normally — so existing callers see no change.
|
|
126
|
+
*/ export function writePath(base, path, value) {
|
|
127
|
+
const parts = path.split('.');
|
|
128
|
+
// Flat path: behave like the original `{[path]: value}` — Payload's
|
|
129
|
+
// top-level merge handles all sibling preservation.
|
|
130
|
+
if (parts.length === 1) {
|
|
131
|
+
return {
|
|
132
|
+
[parts[0]]: value
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Dotted path: clone the top-level segment from `base` (so siblings of
|
|
136
|
+
// every intermediate group survive) and walk down, copying each level on
|
|
137
|
+
// the way so we never mutate the caller's `base`.
|
|
138
|
+
const rootKey = parts[0];
|
|
139
|
+
const baseRoot = base?.[rootKey];
|
|
140
|
+
const rootClone = baseRoot && typeof baseRoot === 'object' && !Array.isArray(baseRoot) ? {
|
|
141
|
+
...baseRoot
|
|
142
|
+
} : {};
|
|
143
|
+
let cur = rootClone;
|
|
144
|
+
for(let i = 1; i < parts.length - 1; i++){
|
|
145
|
+
const key = parts[i];
|
|
146
|
+
const next = cur[key];
|
|
147
|
+
const cloned = next && typeof next === 'object' && !Array.isArray(next) ? {
|
|
148
|
+
...next
|
|
149
|
+
} : {};
|
|
150
|
+
cur[key] = cloned;
|
|
151
|
+
cur = cloned;
|
|
152
|
+
}
|
|
153
|
+
cur[parts[parts.length - 1]] = value;
|
|
154
|
+
return {
|
|
155
|
+
[rootKey]: rootClone
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//# sourceMappingURL=_layout-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/_layout-helpers.ts"],"sourcesContent":["import { jsonResponse } from './_helpers'\r\n\r\n/**\r\n * Shared internals for patchLayout (collections) and patchGlobalLayout\r\n * (globals). Both tools differ only in their fetch/write pairs and their\r\n * nesting-map filter — every other piece of logic (block validation,\r\n * operation application, dotted-path navigation, error shape) is identical\r\n * and lives here. Keeping them separate as tools is intentional from the\r\n * LLM's perspective; keeping the code single-sourced prevents the kind of\r\n * drift that originally let only one side learn about dotted paths.\r\n */\r\n\r\nexport function errorResponse(message: string, extra?: Record<string, unknown>) {\r\n return jsonResponse({ success: false, error: message, ...(extra ?? {}) })\r\n}\r\n\r\n/**\r\n * Recursively validate a block array against an allow list, descending into\r\n * each block's own `blocks`-typed fields when present. A value is treated\r\n * as a nested blocks field whenever it is an array of objects that each\r\n * carry a `blockType` discriminator; the nesting map decides which slugs\r\n * are admissible at that position.\r\n */\r\nexport function validateBlockList(\r\n blocks: Array<Record<string, unknown>>,\r\n allowedSlugs: string[],\r\n pathLabel: string,\r\n allBlockSlugs: Set<string>,\r\n nestingByBlockField: Map<string, string[]>,\r\n errors: string[],\r\n) {\r\n for (let i = 0; i < blocks.length; i++) {\r\n const block = blocks[i]\r\n const here = `${pathLabel}[${i}]`\r\n\r\n if (!block || typeof block !== 'object') {\r\n errors.push(`${here}: not an object`)\r\n continue\r\n }\r\n\r\n const slug = block.blockType\r\n if (typeof slug !== 'string' || !slug) {\r\n errors.push(`${here}: missing string \\`blockType\\``)\r\n continue\r\n }\r\n\r\n if (!allBlockSlugs.has(slug)) {\r\n errors.push(`${here}: unknown blockType \"${slug}\". Known: ${[...allBlockSlugs].join(', ')}`)\r\n continue\r\n }\r\n\r\n if (!allowedSlugs.includes(slug)) {\r\n errors.push(\r\n `${here}: blockType \"${slug}\" not allowed here. Allowed at this position: ${allowedSlugs.join(', ') || '(none)'}`,\r\n )\r\n continue\r\n }\r\n\r\n for (const [fieldName, value] of Object.entries(block)) {\r\n if (!Array.isArray(value)) continue\r\n if (value.length === 0) continue\r\n if (!value.every((v) => v && typeof v === 'object' && 'blockType' in v)) continue\r\n\r\n const nextKey = `${slug}:${fieldName}`\r\n const nextAllowed = nestingByBlockField.get(nextKey)\r\n if (!nextAllowed) {\r\n errors.push(\r\n `${here}.${fieldName}: block \"${slug}\" has no blocks field named \"${fieldName}\" in the schema`,\r\n )\r\n continue\r\n }\r\n\r\n validateBlockList(\r\n value as Array<Record<string, unknown>>,\r\n nextAllowed,\r\n `${here}.${fieldName}`,\r\n allBlockSlugs,\r\n nestingByBlockField,\r\n errors,\r\n )\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Apply a list operation against an existing array of blocks.\r\n * `full` always replaces; the rest preserve the existing array.\r\n */\r\nexport function applyOperation(\r\n newBlocks: Record<string, unknown>[],\r\n operation: 'full' | 'append' | 'prepend' | 'insertAt' | 'replaceAt',\r\n insertIndex: number | undefined,\r\n existingLayout: Record<string, unknown>[] | undefined,\r\n): Record<string, unknown>[] {\r\n if (operation === 'full' || !existingLayout) {\r\n return newBlocks\r\n }\r\n\r\n const existing = [...existingLayout]\r\n\r\n if (operation === 'append') return [...existing, ...newBlocks]\r\n if (operation === 'prepend') return [...newBlocks, ...existing]\r\n\r\n if (operation === 'insertAt') {\r\n if (insertIndex === undefined || insertIndex < 0 || insertIndex > existing.length) {\r\n return [...existing, ...newBlocks]\r\n }\r\n existing.splice(insertIndex, 0, ...newBlocks)\r\n return existing\r\n }\r\n\r\n if (operation === 'replaceAt') {\r\n if (insertIndex === undefined || insertIndex < 0 || insertIndex >= existing.length) {\r\n return existing\r\n }\r\n existing.splice(insertIndex, newBlocks.length, ...newBlocks)\r\n return existing\r\n }\r\n\r\n return newBlocks\r\n}\r\n\r\n/** Walk a dotted path on an object and return the value at the leaf, or `[]`\r\n * if any segment is missing or not an object. Used to pull the current\r\n * blocks array out of a fetched document/global. */\r\nexport function readPath(\r\n obj: Record<string, unknown> | undefined,\r\n path: string,\r\n): Record<string, unknown>[] {\r\n if (!obj) return []\r\n const parts = path.split('.')\r\n let cur: unknown = obj\r\n for (const part of parts) {\r\n if (cur && typeof cur === 'object' && !Array.isArray(cur)) {\r\n cur = (cur as Record<string, unknown>)[part]\r\n } else {\r\n return []\r\n }\r\n }\r\n return Array.isArray(cur) ? (cur as Record<string, unknown>[]) : []\r\n}\r\n\r\n/**\r\n * Write `value` at a dotted `path`, returning a new object whose top-level\r\n * segment is a merge of `base`'s existing siblings with the patched leaf.\r\n *\r\n * Load-bearing for the sibling-wipe fix in patchGlobalLayout: Payload's\r\n * `updateGlobal` only merges at the top level. So if the global is\r\n * `{sections: {layout: [...], copyright: 'foo'}}` and we patch\r\n * `sections.layout`, naively writing `{sections: {layout: [...]}}` makes\r\n * Payload overwrite the entire `sections` group and `copyright` silently\r\n * vanishes. By accepting `base` (the existing document) we can splice the\r\n * new layout into a copy of every parent group along the dotted path,\r\n * preserving siblings at every depth. For a flat (non-dotted) path this\r\n * still produces `{[path]: value}` exactly as before — Payload then merges\r\n * other top-level fields normally — so existing callers see no change.\r\n */\r\nexport function writePath(\r\n base: Record<string, unknown> | undefined,\r\n path: string,\r\n value: unknown,\r\n): Record<string, unknown> {\r\n const parts = path.split('.')\r\n\r\n // Flat path: behave like the original `{[path]: value}` — Payload's\r\n // top-level merge handles all sibling preservation.\r\n if (parts.length === 1) {\r\n return { [parts[0]]: value }\r\n }\r\n\r\n // Dotted path: clone the top-level segment from `base` (so siblings of\r\n // every intermediate group survive) and walk down, copying each level on\r\n // the way so we never mutate the caller's `base`.\r\n const rootKey = parts[0]\r\n const baseRoot = base?.[rootKey]\r\n const rootClone: Record<string, unknown> =\r\n baseRoot && typeof baseRoot === 'object' && !Array.isArray(baseRoot)\r\n ? { ...(baseRoot as Record<string, unknown>) }\r\n : {}\r\n\r\n let cur = rootClone\r\n for (let i = 1; i < parts.length - 1; i++) {\r\n const key = parts[i]\r\n const next = cur[key]\r\n const cloned: Record<string, unknown> =\r\n next && typeof next === 'object' && !Array.isArray(next)\r\n ? { ...(next as Record<string, unknown>) }\r\n : {}\r\n cur[key] = cloned\r\n cur = cloned\r\n }\r\n cur[parts[parts.length - 1]] = value\r\n\r\n return { [rootKey]: rootClone }\r\n}\r\n"],"names":["jsonResponse","errorResponse","message","extra","success","error","validateBlockList","blocks","allowedSlugs","pathLabel","allBlockSlugs","nestingByBlockField","errors","i","length","block","here","push","slug","blockType","has","join","includes","fieldName","value","Object","entries","Array","isArray","every","v","nextKey","nextAllowed","get","applyOperation","newBlocks","operation","insertIndex","existingLayout","existing","undefined","splice","readPath","obj","path","parts","split","cur","part","writePath","base","rootKey","baseRoot","rootClone","key","next","cloned"],"mappings":"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"}
|
|
@@ -22,15 +22,15 @@ import type { CollectionSchema } from '../types';
|
|
|
22
22
|
*/
|
|
23
23
|
export declare function createCreateDocumentTool(collectionSchemas: Map<string, CollectionSchema>, draftCollections: Set<string>): {
|
|
24
24
|
name: string;
|
|
25
|
+
routing: {
|
|
26
|
+
readonly kind: "collection";
|
|
27
|
+
readonly action: "create";
|
|
28
|
+
};
|
|
25
29
|
description: string;
|
|
26
30
|
parameters: {
|
|
27
31
|
collection: z.ZodString;
|
|
28
32
|
data: z.ZodString;
|
|
29
33
|
draft: z.ZodOptional<z.ZodBoolean>;
|
|
30
34
|
};
|
|
31
|
-
handler: (args:
|
|
32
|
-
collection: string;
|
|
33
|
-
data: string;
|
|
34
|
-
draft?: boolean;
|
|
35
|
-
}, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
35
|
+
handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
36
36
|
};
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { DRAFT_NOTE, errorMessage, getDocDisplayName, stampMcpContext, textResponse } from './_helpers';
|
|
3
3
|
const MEDIA_SLUG = 'media';
|
|
4
|
-
/**
|
|
5
|
-
* Custom replacement for the official plugin's `create<Resource>` tools.
|
|
6
|
-
*
|
|
7
|
-
* The official plugin builds its create-tool input schema by spreading
|
|
8
|
-
* `convertCollectionSchemaToZod(schema).shape`, then validates the request
|
|
9
|
-
* with `additionalProperties: false`. For any collection whose JSON schema
|
|
10
|
-
* trips `json-schema-to-zod` (richText, upload, blocks, relationship arrays
|
|
11
|
-
* — i.e. virtually every real-world content collection), the converter
|
|
12
|
-
* falls back to `z.record(z.any())`, `.shape` is undefined, and the spread
|
|
13
|
-
* silently produces a metadata-only schema. The MCP SDK then strips every
|
|
14
|
-
* content field before it reaches `payload.create()`, so creates end up
|
|
15
|
-
* with empty `data` and fail required-field validation.
|
|
16
|
-
*
|
|
17
|
-
* `createDocument` sidesteps the whole pipeline: take a JSON `data` string,
|
|
18
|
-
* call `payload.create()` directly via the local API.
|
|
19
|
-
*
|
|
20
|
-
* Defaults to `draft: true` for draft-enabled collections so newly created
|
|
21
|
-
* documents land in the same draft-first workflow used by `updateDocument`.
|
|
4
|
+
/**
|
|
5
|
+
* Custom replacement for the official plugin's `create<Resource>` tools.
|
|
6
|
+
*
|
|
7
|
+
* The official plugin builds its create-tool input schema by spreading
|
|
8
|
+
* `convertCollectionSchemaToZod(schema).shape`, then validates the request
|
|
9
|
+
* with `additionalProperties: false`. For any collection whose JSON schema
|
|
10
|
+
* trips `json-schema-to-zod` (richText, upload, blocks, relationship arrays
|
|
11
|
+
* — i.e. virtually every real-world content collection), the converter
|
|
12
|
+
* falls back to `z.record(z.any())`, `.shape` is undefined, and the spread
|
|
13
|
+
* silently produces a metadata-only schema. The MCP SDK then strips every
|
|
14
|
+
* content field before it reaches `payload.create()`, so creates end up
|
|
15
|
+
* with empty `data` and fail required-field validation.
|
|
16
|
+
*
|
|
17
|
+
* `createDocument` sidesteps the whole pipeline: take a JSON `data` string,
|
|
18
|
+
* call `payload.create()` directly via the local API.
|
|
19
|
+
*
|
|
20
|
+
* Defaults to `draft: true` for draft-enabled collections so newly created
|
|
21
|
+
* documents land in the same draft-first workflow used by `updateDocument`.
|
|
22
22
|
*/ export function createCreateDocumentTool(collectionSchemas, draftCollections) {
|
|
23
23
|
const creatableSlugs = [];
|
|
24
24
|
const descriptionLines = [];
|
|
@@ -30,6 +30,10 @@ const MEDIA_SLUG = 'media';
|
|
|
30
30
|
const collectionDescriptions = descriptionLines.join('\n');
|
|
31
31
|
return {
|
|
32
32
|
name: 'createDocument',
|
|
33
|
+
routing: {
|
|
34
|
+
kind: 'collection',
|
|
35
|
+
action: 'create'
|
|
36
|
+
},
|
|
33
37
|
description: 'Create a new document in any collection. Pass the field values as a JSON string in `data`. ' + 'For draft-enabled collections, the document is created as a draft by default — use publishDraft to make it live, ' + 'or pass `draft: false` to publish immediately. ' + 'For relationship fields, pass the related document ID (use resolveReference to find IDs). ' + 'For upload fields, pass the media document ID (use uploadMedia to create one first).\n\n' + 'Available collections and their fields:\n' + collectionDescriptions,
|
|
34
38
|
parameters: {
|
|
35
39
|
collection: z.string().describe(`The collection slug. One of: ${creatableSlugs.join(', ')}`),
|
|
@@ -37,10 +41,10 @@ const MEDIA_SLUG = 'media';
|
|
|
37
41
|
draft: z.boolean().optional().describe('Override draft status. Defaults to `true` for draft-enabled collections, `false` otherwise. ' + 'Set explicitly to `false` on a draft-enabled collection to publish immediately.')
|
|
38
42
|
},
|
|
39
43
|
handler: async (args, req, _extra)=>{
|
|
40
|
-
const { collection } = args;
|
|
44
|
+
const { collection, data: rawData, draft } = args;
|
|
41
45
|
let data;
|
|
42
46
|
try {
|
|
43
|
-
data = JSON.parse(
|
|
47
|
+
data = JSON.parse(rawData);
|
|
44
48
|
} catch {
|
|
45
49
|
return textResponse('Error: "data" must be a valid JSON string. Example: \'{"name": "Aria", "slug": "aria"}\'');
|
|
46
50
|
}
|
|
@@ -55,7 +59,7 @@ const MEDIA_SLUG = 'media';
|
|
|
55
59
|
}
|
|
56
60
|
stampMcpContext(req);
|
|
57
61
|
const isDraftCollection = draftCollections.has(collection);
|
|
58
|
-
const asDraft =
|
|
62
|
+
const asDraft = draft ?? isDraftCollection;
|
|
59
63
|
try {
|
|
60
64
|
const doc = await req.payload.create({
|
|
61
65
|
collection: collection,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tools/create-document.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { PayloadRequest } from 'payload'\nimport type { CollectionSchema } from '../types'\nimport {\n DRAFT_NOTE,\n errorMessage,\n getDocDisplayName,\n stampMcpContext,\n textResponse,\n} from './_helpers'\n\nconst MEDIA_SLUG = 'media'\n\n/**\n * Custom replacement for the official plugin's `create<Resource>` tools.\n *\n * The official plugin builds its create-tool input schema by spreading\n * `convertCollectionSchemaToZod(schema).shape`, then validates the request\n * with `additionalProperties: false`. For any collection whose JSON schema\n * trips `json-schema-to-zod` (richText, upload, blocks, relationship arrays\n * — i.e. virtually every real-world content collection), the converter\n * falls back to `z.record(z.any())`, `.shape` is undefined, and the spread\n * silently produces a metadata-only schema. The MCP SDK then strips every\n * content field before it reaches `payload.create()`, so creates end up\n * with empty `data` and fail required-field validation.\n *\n * `createDocument` sidesteps the whole pipeline: take a JSON `data` string,\n * call `payload.create()` directly via the local API.\n *\n * Defaults to `draft: true` for draft-enabled collections so newly created\n * documents land in the same draft-first workflow used by `updateDocument`.\n */\nexport function createCreateDocumentTool(\n collectionSchemas: Map<string, CollectionSchema>,\n draftCollections: Set<string>,\n) {\n const creatableSlugs: string[] = []\n const descriptionLines: string[] = []\n for (const [slug, schema] of collectionSchemas) {\n if (slug === MEDIA_SLUG) continue\n creatableSlugs.push(slug)\n descriptionLines.push(` - \"${slug}\": ${schema.fields.map((f) => f.name).join(', ')}`)\n }\n const collectionDescriptions = descriptionLines.join('\\n')\n\n return {\n name: 'createDocument',\n description:\n 'Create a new document in any collection. Pass the field values as a JSON string in `data`. ' +\n 'For draft-enabled collections, the document is created as a draft by default — use publishDraft to make it live, ' +\n 'or pass `draft: false` to publish immediately. ' +\n 'For relationship fields, pass the related document ID (use resolveReference to find IDs). ' +\n 'For upload fields, pass the media document ID (use uploadMedia to create one first).\\n\\n' +\n 'Available collections and their fields:\\n' +\n collectionDescriptions,\n parameters: {\n collection: z\n .string()\n .describe(`The collection slug. One of: ${creatableSlugs.join(', ')}`),\n data: z\n .string()\n .describe(\n 'JSON string of field names to values for the new document. ' +\n 'Examples: \\'{\"name\": \"Aria\", \"slug\": \"aria\"}\\', ' +\n '\\'{\"title\": \"First-Time Clients\", \"heroTitle\": \"Welcome\", \"slug\": \"first-time-clients\"}\\'',\n ),\n draft: z\n .boolean()\n .optional()\n .describe(\n 'Override draft status. Defaults to `true` for draft-enabled collections, `false` otherwise. ' +\n 'Set explicitly to `false` on a draft-enabled collection to publish immediately.',\n ),\n },\n handler: async (\n args:
|
|
1
|
+
{"version":3,"sources":["../../src/tools/create-document.ts"],"sourcesContent":["import { z } from 'zod'\r\nimport type { PayloadRequest } from 'payload'\r\nimport type { CollectionSchema } from '../types'\r\nimport {\r\n DRAFT_NOTE,\r\n errorMessage,\r\n getDocDisplayName,\r\n stampMcpContext,\r\n textResponse,\r\n} from './_helpers'\r\n\r\nconst MEDIA_SLUG = 'media'\r\n\r\n/**\r\n * Custom replacement for the official plugin's `create<Resource>` tools.\r\n *\r\n * The official plugin builds its create-tool input schema by spreading\r\n * `convertCollectionSchemaToZod(schema).shape`, then validates the request\r\n * with `additionalProperties: false`. For any collection whose JSON schema\r\n * trips `json-schema-to-zod` (richText, upload, blocks, relationship arrays\r\n * — i.e. virtually every real-world content collection), the converter\r\n * falls back to `z.record(z.any())`, `.shape` is undefined, and the spread\r\n * silently produces a metadata-only schema. The MCP SDK then strips every\r\n * content field before it reaches `payload.create()`, so creates end up\r\n * with empty `data` and fail required-field validation.\r\n *\r\n * `createDocument` sidesteps the whole pipeline: take a JSON `data` string,\r\n * call `payload.create()` directly via the local API.\r\n *\r\n * Defaults to `draft: true` for draft-enabled collections so newly created\r\n * documents land in the same draft-first workflow used by `updateDocument`.\r\n */\r\nexport function createCreateDocumentTool(\r\n collectionSchemas: Map<string, CollectionSchema>,\r\n draftCollections: Set<string>,\r\n) {\r\n const creatableSlugs: string[] = []\r\n const descriptionLines: string[] = []\r\n for (const [slug, schema] of collectionSchemas) {\r\n if (slug === MEDIA_SLUG) continue\r\n creatableSlugs.push(slug)\r\n descriptionLines.push(` - \"${slug}\": ${schema.fields.map((f) => f.name).join(', ')}`)\r\n }\r\n const collectionDescriptions = descriptionLines.join('\\n')\r\n\r\n return {\r\n name: 'createDocument',\r\n routing: { kind: 'collection', action: 'create' } as const,\r\n description:\r\n 'Create a new document in any collection. Pass the field values as a JSON string in `data`. ' +\r\n 'For draft-enabled collections, the document is created as a draft by default — use publishDraft to make it live, ' +\r\n 'or pass `draft: false` to publish immediately. ' +\r\n 'For relationship fields, pass the related document ID (use resolveReference to find IDs). ' +\r\n 'For upload fields, pass the media document ID (use uploadMedia to create one first).\\n\\n' +\r\n 'Available collections and their fields:\\n' +\r\n collectionDescriptions,\r\n parameters: {\r\n collection: z\r\n .string()\r\n .describe(`The collection slug. One of: ${creatableSlugs.join(', ')}`),\r\n data: z\r\n .string()\r\n .describe(\r\n 'JSON string of field names to values for the new document. ' +\r\n 'Examples: \\'{\"name\": \"Aria\", \"slug\": \"aria\"}\\', ' +\r\n '\\'{\"title\": \"First-Time Clients\", \"heroTitle\": \"Welcome\", \"slug\": \"first-time-clients\"}\\'',\r\n ),\r\n draft: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n 'Override draft status. Defaults to `true` for draft-enabled collections, `false` otherwise. ' +\r\n 'Set explicitly to `false` on a draft-enabled collection to publish immediately.',\r\n ),\r\n },\r\n handler: async (\r\n args: Record<string, unknown>,\r\n req: PayloadRequest,\r\n _extra: unknown,\r\n ) => {\r\n const { collection, data: rawData, draft } = args as {\r\n collection: string\r\n data: string\r\n draft?: boolean\r\n }\r\n\r\n let data: Record<string, unknown>\r\n try {\r\n data = JSON.parse(rawData)\r\n } catch {\r\n return textResponse(\r\n 'Error: \"data\" must be a valid JSON string. Example: \\'{\"name\": \"Aria\", \"slug\": \"aria\"}\\'',\r\n )\r\n }\r\n\r\n if (!collectionSchemas.has(collection)) {\r\n return textResponse(\r\n `Error: Unknown collection \"${collection}\". Available: ${creatableSlugs.join(', ')}`,\r\n )\r\n }\r\n\r\n if (collection === MEDIA_SLUG) {\r\n return textResponse('Error: Use the uploadMedia tool to create media files.')\r\n }\r\n\r\n if (!data || Object.keys(data).length === 0) {\r\n return textResponse(\r\n 'Error: No fields provided in \"data\". Pass an object with field names and values for the new document.',\r\n )\r\n }\r\n\r\n stampMcpContext(req)\r\n\r\n const isDraftCollection = draftCollections.has(collection)\r\n const asDraft = draft ?? isDraftCollection\r\n\r\n try {\r\n const doc = await req.payload.create({\r\n collection: collection as any,\r\n data: data as any,\r\n draft: asDraft,\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n\r\n const displayName = getDocDisplayName(doc, String((doc as { id?: unknown }).id ?? ''))\r\n const newId = String((doc as { id?: unknown }).id ?? '')\r\n const draftNote = isDraftCollection && asDraft ? DRAFT_NOTE : ''\r\n\r\n return textResponse(\r\n `Created \"${displayName}\" in ${collection} (ID: ${newId}).${draftNote}`,\r\n )\r\n } catch (error) {\r\n return textResponse(\r\n `Error creating document in ${collection}: ${errorMessage(error)}`,\r\n )\r\n }\r\n },\r\n }\r\n}\r\n"],"names":["z","DRAFT_NOTE","errorMessage","getDocDisplayName","stampMcpContext","textResponse","MEDIA_SLUG","createCreateDocumentTool","collectionSchemas","draftCollections","creatableSlugs","descriptionLines","slug","schema","push","fields","map","f","name","join","collectionDescriptions","routing","kind","action","description","parameters","collection","string","describe","data","draft","boolean","optional","handler","args","req","_extra","rawData","JSON","parse","has","Object","keys","length","isDraftCollection","asDraft","doc","payload","create","overrideAccess","user","displayName","String","id","newId","draftNote","error"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAK;AAGvB,SACEC,UAAU,EACVC,YAAY,EACZC,iBAAiB,EACjBC,eAAe,EACfC,YAAY,QACP,aAAY;AAEnB,MAAMC,aAAa;AAEnB;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,yBACdC,iBAAgD,EAChDC,gBAA6B;IAE7B,MAAMC,iBAA2B,EAAE;IACnC,MAAMC,mBAA6B,EAAE;IACrC,KAAK,MAAM,CAACC,MAAMC,OAAO,IAAIL,kBAAmB;QAC9C,IAAII,SAASN,YAAY;QACzBI,eAAeI,IAAI,CAACF;QACpBD,iBAAiBG,IAAI,CAAC,CAAC,KAAK,EAAEF,KAAK,GAAG,EAAEC,OAAOE,MAAM,CAACC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI,EAAEC,IAAI,CAAC,OAAO;IACvF;IACA,MAAMC,yBAAyBT,iBAAiBQ,IAAI,CAAC;IAErD,OAAO;QACLD,MAAM;QACNG,SAAS;YAAEC,MAAM;YAAcC,QAAQ;QAAS;QAChDC,aACE,gGACA,sHACA,oDACA,+FACA,6FACA,8CACAJ;QACFK,YAAY;YACVC,YAAY1B,EACT2B,MAAM,GACNC,QAAQ,CAAC,CAAC,6BAA6B,EAAElB,eAAeS,IAAI,CAAC,OAAO;YACvEU,MAAM7B,EACH2B,MAAM,GACNC,QAAQ,CACP,gEACE,qDACA;YAENE,OAAO9B,EACJ+B,OAAO,GACPC,QAAQ,GACRJ,QAAQ,CACP,iGACE;QAER;QACAK,SAAS,OACPC,MACAC,KACAC;YAEA,MAAM,EAAEV,UAAU,EAAEG,MAAMQ,OAAO,EAAEP,KAAK,EAAE,GAAGI;YAM7C,IAAIL;YACJ,IAAI;gBACFA,OAAOS,KAAKC,KAAK,CAACF;YACpB,EAAE,OAAM;gBACN,OAAOhC,aACL;YAEJ;YAEA,IAAI,CAACG,kBAAkBgC,GAAG,CAACd,aAAa;gBACtC,OAAOrB,aACL,CAAC,2BAA2B,EAAEqB,WAAW,cAAc,EAAEhB,eAAeS,IAAI,CAAC,OAAO;YAExF;YAEA,IAAIO,eAAepB,YAAY;gBAC7B,OAAOD,aAAa;YACtB;YAEA,IAAI,CAACwB,QAAQY,OAAOC,IAAI,CAACb,MAAMc,MAAM,KAAK,GAAG;gBAC3C,OAAOtC,aACL;YAEJ;YAEAD,gBAAgB+B;YAEhB,MAAMS,oBAAoBnC,iBAAiB+B,GAAG,CAACd;YAC/C,MAAMmB,UAAUf,SAASc;YAEzB,IAAI;gBACF,MAAME,MAAM,MAAMX,IAAIY,OAAO,CAACC,MAAM,CAAC;oBACnCtB,YAAYA;oBACZG,MAAMA;oBACNC,OAAOe;oBACPV;oBACAc,gBAAgB;oBAChBC,MAAMf,IAAIe,IAAI;gBAChB;gBAEA,MAAMC,cAAchD,kBAAkB2C,KAAKM,OAAO,AAACN,IAAyBO,EAAE,IAAI;gBAClF,MAAMC,QAAQF,OAAO,AAACN,IAAyBO,EAAE,IAAI;gBACrD,MAAME,YAAYX,qBAAqBC,UAAU5C,aAAa;gBAE9D,OAAOI,aACL,CAAC,SAAS,EAAE8C,YAAY,KAAK,EAAEzB,WAAW,MAAM,EAAE4B,MAAM,EAAE,EAAEC,WAAW;YAE3E,EAAE,OAAOC,OAAO;gBACd,OAAOnD,aACL,CAAC,2BAA2B,EAAEqB,WAAW,EAAE,EAAExB,aAAasD,QAAQ;YAEtE;QACF;IACF;AACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PayloadRequest } from 'payload';
|
|
3
|
+
import type { CollectionSchema } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Polymorphic, fast unsafe-delete tool. Mirrors `safeDelete`'s args but skips
|
|
6
|
+
* the relationship-walk — use this when the caller knows the doc has no
|
|
7
|
+
* inbound references, or when relationship breakage is acceptable.
|
|
8
|
+
*
|
|
9
|
+
* `safeDelete` remains the recommended default; this exists for surgical
|
|
10
|
+
* deletes inside scripts and AI workflows where a relationship walk would
|
|
11
|
+
* be wasteful.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createDeleteDocumentTool(collectionSchemas: Map<string, CollectionSchema>): {
|
|
14
|
+
name: string;
|
|
15
|
+
routing: {
|
|
16
|
+
readonly kind: "collection";
|
|
17
|
+
readonly action: "delete";
|
|
18
|
+
};
|
|
19
|
+
description: string;
|
|
20
|
+
parameters: {
|
|
21
|
+
collection: z.ZodString;
|
|
22
|
+
id: z.ZodString;
|
|
23
|
+
};
|
|
24
|
+
handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
25
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { errorMessage, getDocDisplayName, stampMcpContext, textResponse } from './_helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Polymorphic, fast unsafe-delete tool. Mirrors `safeDelete`'s args but skips
|
|
5
|
+
* the relationship-walk — use this when the caller knows the doc has no
|
|
6
|
+
* inbound references, or when relationship breakage is acceptable.
|
|
7
|
+
*
|
|
8
|
+
* `safeDelete` remains the recommended default; this exists for surgical
|
|
9
|
+
* deletes inside scripts and AI workflows where a relationship walk would
|
|
10
|
+
* be wasteful.
|
|
11
|
+
*/ export function createDeleteDocumentTool(collectionSchemas) {
|
|
12
|
+
const deletableSlugs = [
|
|
13
|
+
...collectionSchemas.keys()
|
|
14
|
+
];
|
|
15
|
+
return {
|
|
16
|
+
name: 'deleteDocument',
|
|
17
|
+
routing: {
|
|
18
|
+
kind: 'collection',
|
|
19
|
+
action: 'delete'
|
|
20
|
+
},
|
|
21
|
+
description: 'Delete a document by ID. Skips the inbound-relationship safety check that `safeDelete` performs — use only when you know the document has no inbound references, or when broken relationships are acceptable. Prefer `safeDelete` for general use.\n\n' + `Collections: ${deletableSlugs.join(', ')}`,
|
|
22
|
+
parameters: {
|
|
23
|
+
collection: z.string().describe(`Collection slug. One of: ${deletableSlugs.join(', ')}`),
|
|
24
|
+
id: z.string().describe('Document ID to delete.')
|
|
25
|
+
},
|
|
26
|
+
handler: async (args, req, _extra)=>{
|
|
27
|
+
const { collection, id } = args;
|
|
28
|
+
if (!collectionSchemas.has(collection)) {
|
|
29
|
+
return textResponse(`Error: Unknown collection "${collection}". Valid: ${deletableSlugs.join(', ')}`);
|
|
30
|
+
}
|
|
31
|
+
stampMcpContext(req);
|
|
32
|
+
try {
|
|
33
|
+
const doc = await req.payload.delete({
|
|
34
|
+
collection: collection,
|
|
35
|
+
id,
|
|
36
|
+
req,
|
|
37
|
+
overrideAccess: false,
|
|
38
|
+
user: req.user
|
|
39
|
+
});
|
|
40
|
+
const displayName = getDocDisplayName(doc, id);
|
|
41
|
+
return textResponse(`Deleted "${displayName}" from ${collection} (ID: ${id}).`);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return textResponse(`Error deleting ${id} from ${collection}: ${errorMessage(err)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=delete-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/delete-document.ts"],"sourcesContent":["import { z } from 'zod'\r\nimport type { PayloadRequest } from 'payload'\r\nimport type { CollectionSchema } from '../types'\r\nimport {\r\n errorMessage,\r\n getDocDisplayName,\r\n stampMcpContext,\r\n textResponse,\r\n} from './_helpers'\r\n\r\ninterface DeleteDocumentArgs {\r\n collection: string\r\n id: string\r\n}\r\n\r\n/**\r\n * Polymorphic, fast unsafe-delete tool. Mirrors `safeDelete`'s args but skips\r\n * the relationship-walk — use this when the caller knows the doc has no\r\n * inbound references, or when relationship breakage is acceptable.\r\n *\r\n * `safeDelete` remains the recommended default; this exists for surgical\r\n * deletes inside scripts and AI workflows where a relationship walk would\r\n * be wasteful.\r\n */\r\nexport function createDeleteDocumentTool(collectionSchemas: Map<string, CollectionSchema>) {\r\n const deletableSlugs = [...collectionSchemas.keys()]\r\n\r\n return {\r\n name: 'deleteDocument',\r\n routing: { kind: 'collection', action: 'delete' } as const,\r\n description:\r\n 'Delete a document by ID. Skips the inbound-relationship safety check that `safeDelete` performs — use only when you know the document has no inbound references, or when broken relationships are acceptable. Prefer `safeDelete` for general use.\\n\\n' +\r\n `Collections: ${deletableSlugs.join(', ')}`,\r\n parameters: {\r\n collection: z.string().describe(`Collection slug. One of: ${deletableSlugs.join(', ')}`),\r\n id: z.string().describe('Document ID to delete.'),\r\n },\r\n handler: async (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => {\r\n const { collection, id } = args as unknown as DeleteDocumentArgs\r\n\r\n if (!collectionSchemas.has(collection)) {\r\n return textResponse(\r\n `Error: Unknown collection \"${collection}\". Valid: ${deletableSlugs.join(', ')}`,\r\n )\r\n }\r\n\r\n stampMcpContext(req)\r\n\r\n try {\r\n const doc = await req.payload.delete({\r\n collection: collection as never,\r\n id,\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n\r\n const displayName = getDocDisplayName(doc, id)\r\n return textResponse(\r\n `Deleted \"${displayName}\" from ${collection} (ID: ${id}).`,\r\n )\r\n } catch (err) {\r\n return textResponse(\r\n `Error deleting ${id} from ${collection}: ${errorMessage(err)}`,\r\n )\r\n }\r\n },\r\n }\r\n}\r\n"],"names":["z","errorMessage","getDocDisplayName","stampMcpContext","textResponse","createDeleteDocumentTool","collectionSchemas","deletableSlugs","keys","name","routing","kind","action","description","join","parameters","collection","string","describe","id","handler","args","req","_extra","has","doc","payload","delete","overrideAccess","user","displayName","err"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAK;AAGvB,SACEC,YAAY,EACZC,iBAAiB,EACjBC,eAAe,EACfC,YAAY,QACP,aAAY;AAOnB;;;;;;;;CAQC,GACD,OAAO,SAASC,yBAAyBC,iBAAgD;IACvF,MAAMC,iBAAiB;WAAID,kBAAkBE,IAAI;KAAG;IAEpD,OAAO;QACLC,MAAM;QACNC,SAAS;YAAEC,MAAM;YAAcC,QAAQ;QAAS;QAChDC,aACE,2PACA,CAAC,aAAa,EAAEN,eAAeO,IAAI,CAAC,OAAO;QAC7CC,YAAY;YACVC,YAAYhB,EAAEiB,MAAM,GAAGC,QAAQ,CAAC,CAAC,yBAAyB,EAAEX,eAAeO,IAAI,CAAC,OAAO;YACvFK,IAAInB,EAAEiB,MAAM,GAAGC,QAAQ,CAAC;QAC1B;QACAE,SAAS,OAAOC,MAA+BC,KAAqBC;YAClE,MAAM,EAAEP,UAAU,EAAEG,EAAE,EAAE,GAAGE;YAE3B,IAAI,CAACf,kBAAkBkB,GAAG,CAACR,aAAa;gBACtC,OAAOZ,aACL,CAAC,2BAA2B,EAAEY,WAAW,UAAU,EAAET,eAAeO,IAAI,CAAC,OAAO;YAEpF;YAEAX,gBAAgBmB;YAEhB,IAAI;gBACF,MAAMG,MAAM,MAAMH,IAAII,OAAO,CAACC,MAAM,CAAC;oBACnCX,YAAYA;oBACZG;oBACAG;oBACAM,gBAAgB;oBAChBC,MAAMP,IAAIO,IAAI;gBAChB;gBAEA,MAAMC,cAAc5B,kBAAkBuB,KAAKN;gBAC3C,OAAOf,aACL,CAAC,SAAS,EAAE0B,YAAY,OAAO,EAAEd,WAAW,MAAM,EAAEG,GAAG,EAAE,CAAC;YAE9D,EAAE,OAAOY,KAAK;gBACZ,OAAO3B,aACL,CAAC,eAAe,EAAEe,GAAG,MAAM,EAAEH,WAAW,EAAE,EAAEf,aAAa8B,MAAM;YAEnE;QACF;IACF;AACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { CollectionConfig, PayloadRequest } from 'payload';
|
|
3
|
+
import type { CollectionSchema } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Polymorphic replacement for the upstream plugin's per-collection
|
|
6
|
+
* `find<Resource>` tools. Takes the collection as an arg rather than
|
|
7
|
+
* generating one tool per collection — same authoring shape as
|
|
8
|
+
* `createDocument` / `updateDocument`.
|
|
9
|
+
*
|
|
10
|
+
* Two modes:
|
|
11
|
+
* - `id` set: `payload.findByID` (single doc)
|
|
12
|
+
* - `id` unset: `payload.find` with optional JSON-string `where`
|
|
13
|
+
*
|
|
14
|
+
* Draft-enabled collections get preview URLs appended to draft documents
|
|
15
|
+
* via `decorateDraftResponse`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createFindDocumentTool(collectionSchemas: Map<string, CollectionSchema>, draftCollections: Set<string>, collectionsBySlug: Map<string, CollectionConfig>, previewSiteUrl: string | undefined, previewDisabled?: boolean): {
|
|
18
|
+
name: string;
|
|
19
|
+
routing: {
|
|
20
|
+
readonly kind: "collection";
|
|
21
|
+
readonly action: "read";
|
|
22
|
+
};
|
|
23
|
+
description: string;
|
|
24
|
+
parameters: {
|
|
25
|
+
collection: z.ZodString;
|
|
26
|
+
id: z.ZodOptional<z.ZodString>;
|
|
27
|
+
where: z.ZodOptional<z.ZodString>;
|
|
28
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
depth: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
draft: z.ZodOptional<z.ZodBoolean>;
|
|
31
|
+
};
|
|
32
|
+
handler: (rawArgs: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
33
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { decorateDraftResponse, errorMessage, jsonResponse, stampMcpContext, textResponse } from './_helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Polymorphic replacement for the upstream plugin's per-collection
|
|
5
|
+
* `find<Resource>` tools. Takes the collection as an arg rather than
|
|
6
|
+
* generating one tool per collection — same authoring shape as
|
|
7
|
+
* `createDocument` / `updateDocument`.
|
|
8
|
+
*
|
|
9
|
+
* Two modes:
|
|
10
|
+
* - `id` set: `payload.findByID` (single doc)
|
|
11
|
+
* - `id` unset: `payload.find` with optional JSON-string `where`
|
|
12
|
+
*
|
|
13
|
+
* Draft-enabled collections get preview URLs appended to draft documents
|
|
14
|
+
* via `decorateDraftResponse`.
|
|
15
|
+
*/ export function createFindDocumentTool(collectionSchemas, draftCollections, collectionsBySlug, previewSiteUrl, previewDisabled = false) {
|
|
16
|
+
const findableSlugs = [
|
|
17
|
+
...collectionSchemas.keys()
|
|
18
|
+
];
|
|
19
|
+
const descriptionLines = findableSlugs.map((slug)=>` - "${slug}"${draftCollections.has(slug) ? ' (draft-enabled)' : ''}`);
|
|
20
|
+
return {
|
|
21
|
+
name: 'findDocument',
|
|
22
|
+
routing: {
|
|
23
|
+
kind: 'collection',
|
|
24
|
+
action: 'read'
|
|
25
|
+
},
|
|
26
|
+
description: 'Read documents from any collection. Pass `id` for a single document, or omit `id` and pass a Payload `where` filter as a JSON string for a list. ' + 'Draft-enabled collections include a preview URL on draft documents when available.\n\n' + 'Available collections:\n' + descriptionLines.join('\n'),
|
|
27
|
+
parameters: {
|
|
28
|
+
collection: z.string().describe(`The collection slug. One of: ${findableSlugs.join(', ')}`),
|
|
29
|
+
id: z.string().optional().describe('Document ID. When set, returns a single document.'),
|
|
30
|
+
where: z.string().optional().describe('JSON-encoded Payload `where` clause. Examples: \'{"status":{"equals":"published"}}\', ' + '\'{"slug":{"equals":"hello-world"}}\'. Ignored if `id` is set.'),
|
|
31
|
+
limit: z.number().int().min(1).max(100).optional().describe('Max results when listing. Default 25.'),
|
|
32
|
+
depth: z.number().int().min(0).max(3).optional().describe('Relationship population depth. Default 1.'),
|
|
33
|
+
draft: z.boolean().optional().describe('When true, returns draft versions of draft-enabled collections. Default false (published only).')
|
|
34
|
+
},
|
|
35
|
+
handler: async (rawArgs, req, _extra)=>{
|
|
36
|
+
const args = rawArgs;
|
|
37
|
+
const { collection, id, where, limit, depth, draft } = args;
|
|
38
|
+
if (!collectionSchemas.has(collection)) {
|
|
39
|
+
return textResponse(`Error: Unknown collection "${collection}". Valid: ${findableSlugs.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
stampMcpContext(req);
|
|
42
|
+
const collectionConfig = collectionsBySlug.get(collection);
|
|
43
|
+
try {
|
|
44
|
+
if (id) {
|
|
45
|
+
const doc = await req.payload.findByID({
|
|
46
|
+
collection: collection,
|
|
47
|
+
id,
|
|
48
|
+
depth: depth ?? 1,
|
|
49
|
+
draft: draft ?? false,
|
|
50
|
+
req,
|
|
51
|
+
overrideAccess: false,
|
|
52
|
+
user: req.user
|
|
53
|
+
});
|
|
54
|
+
const base = jsonResponse(doc);
|
|
55
|
+
if (previewDisabled) return base;
|
|
56
|
+
return await decorateDraftResponse(base, doc, collectionConfig, req, previewSiteUrl);
|
|
57
|
+
}
|
|
58
|
+
let parsedWhere;
|
|
59
|
+
if (where && where.trim().length > 0) {
|
|
60
|
+
try {
|
|
61
|
+
parsedWhere = JSON.parse(where);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return textResponse(`Error: \`where\` must be a valid JSON string. ${errorMessage(err)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const result = await req.payload.find({
|
|
67
|
+
collection: collection,
|
|
68
|
+
where: parsedWhere,
|
|
69
|
+
depth: depth ?? 1,
|
|
70
|
+
limit: limit ?? 25,
|
|
71
|
+
draft: draft ?? false,
|
|
72
|
+
req,
|
|
73
|
+
overrideAccess: false,
|
|
74
|
+
user: req.user,
|
|
75
|
+
pagination: false
|
|
76
|
+
});
|
|
77
|
+
const base = jsonResponse({
|
|
78
|
+
totalDocs: result.totalDocs ?? result.docs.length,
|
|
79
|
+
docs: result.docs
|
|
80
|
+
});
|
|
81
|
+
if (previewDisabled || !collectionConfig || !draftCollections.has(collection)) return base;
|
|
82
|
+
// Decorate any draft docs in the page with preview URLs.
|
|
83
|
+
let decorated = base;
|
|
84
|
+
for (const doc of result.docs){
|
|
85
|
+
if (doc._status === 'draft') {
|
|
86
|
+
decorated = await decorateDraftResponse(decorated, doc, collectionConfig, req, previewSiteUrl);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return decorated;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
return textResponse(`Error reading from ${collection}: ${errorMessage(err)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//# sourceMappingURL=find-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/find-document.ts"],"sourcesContent":["import { z } from 'zod'\r\nimport type { CollectionConfig, PayloadRequest } from 'payload'\r\nimport type { CollectionSchema } from '../types'\r\nimport {\r\n decorateDraftResponse,\r\n errorMessage,\r\n jsonResponse,\r\n stampMcpContext,\r\n textResponse,\r\n} from './_helpers'\r\n\r\ninterface FindDocumentArgs {\r\n collection: string\r\n id?: string\r\n where?: string\r\n limit?: number\r\n depth?: number\r\n draft?: boolean\r\n}\r\n\r\n/**\r\n * Polymorphic replacement for the upstream plugin's per-collection\r\n * `find<Resource>` tools. Takes the collection as an arg rather than\r\n * generating one tool per collection — same authoring shape as\r\n * `createDocument` / `updateDocument`.\r\n *\r\n * Two modes:\r\n * - `id` set: `payload.findByID` (single doc)\r\n * - `id` unset: `payload.find` with optional JSON-string `where`\r\n *\r\n * Draft-enabled collections get preview URLs appended to draft documents\r\n * via `decorateDraftResponse`.\r\n */\r\nexport function createFindDocumentTool(\r\n collectionSchemas: Map<string, CollectionSchema>,\r\n draftCollections: Set<string>,\r\n collectionsBySlug: Map<string, CollectionConfig>,\r\n previewSiteUrl: string | undefined,\r\n previewDisabled = false,\r\n) {\r\n const findableSlugs = [...collectionSchemas.keys()]\r\n const descriptionLines = findableSlugs.map(\r\n (slug) =>\r\n ` - \"${slug}\"${draftCollections.has(slug) ? ' (draft-enabled)' : ''}`,\r\n )\r\n\r\n return {\r\n name: 'findDocument',\r\n routing: { kind: 'collection', action: 'read' } as const,\r\n description:\r\n 'Read documents from any collection. Pass `id` for a single document, or omit `id` and pass a Payload `where` filter as a JSON string for a list. ' +\r\n 'Draft-enabled collections include a preview URL on draft documents when available.\\n\\n' +\r\n 'Available collections:\\n' +\r\n descriptionLines.join('\\n'),\r\n parameters: {\r\n collection: z\r\n .string()\r\n .describe(`The collection slug. One of: ${findableSlugs.join(', ')}`),\r\n id: z.string().optional().describe('Document ID. When set, returns a single document.'),\r\n where: z\r\n .string()\r\n .optional()\r\n .describe(\r\n 'JSON-encoded Payload `where` clause. Examples: \\'{\"status\":{\"equals\":\"published\"}}\\', ' +\r\n '\\'{\"slug\":{\"equals\":\"hello-world\"}}\\'. Ignored if `id` is set.',\r\n ),\r\n limit: z\r\n .number()\r\n .int()\r\n .min(1)\r\n .max(100)\r\n .optional()\r\n .describe('Max results when listing. Default 25.'),\r\n depth: z\r\n .number()\r\n .int()\r\n .min(0)\r\n .max(3)\r\n .optional()\r\n .describe('Relationship population depth. Default 1.'),\r\n draft: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n 'When true, returns draft versions of draft-enabled collections. Default false (published only).',\r\n ),\r\n },\r\n handler: async (\r\n rawArgs: Record<string, unknown>,\r\n req: PayloadRequest,\r\n _extra: unknown,\r\n ) => {\r\n const args = rawArgs as unknown as FindDocumentArgs\r\n const { collection, id, where, limit, depth, draft } = args\r\n\r\n if (!collectionSchemas.has(collection)) {\r\n return textResponse(\r\n `Error: Unknown collection \"${collection}\". Valid: ${findableSlugs.join(', ')}`,\r\n )\r\n }\r\n\r\n stampMcpContext(req)\r\n const collectionConfig = collectionsBySlug.get(collection)\r\n\r\n try {\r\n if (id) {\r\n const doc = await req.payload.findByID({\r\n collection: collection as never,\r\n id,\r\n depth: depth ?? 1,\r\n draft: draft ?? false,\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n const base = jsonResponse(doc)\r\n if (previewDisabled) return base\r\n return await decorateDraftResponse(\r\n base,\r\n doc as Record<string, unknown>,\r\n collectionConfig,\r\n req,\r\n previewSiteUrl,\r\n )\r\n }\r\n\r\n let parsedWhere: unknown\r\n if (where && where.trim().length > 0) {\r\n try {\r\n parsedWhere = JSON.parse(where)\r\n } catch (err) {\r\n return textResponse(\r\n `Error: \\`where\\` must be a valid JSON string. ${errorMessage(err)}`,\r\n )\r\n }\r\n }\r\n\r\n const result = await req.payload.find({\r\n collection: collection as never,\r\n where: parsedWhere as never,\r\n depth: depth ?? 1,\r\n limit: limit ?? 25,\r\n draft: draft ?? false,\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n pagination: false,\r\n })\r\n\r\n const base = jsonResponse({\r\n totalDocs: (result as { totalDocs?: number }).totalDocs ?? result.docs.length,\r\n docs: result.docs,\r\n })\r\n\r\n if (previewDisabled || !collectionConfig || !draftCollections.has(collection)) return base\r\n\r\n // Decorate any draft docs in the page with preview URLs.\r\n let decorated = base\r\n for (const doc of result.docs as Array<Record<string, unknown>>) {\r\n if (doc._status === 'draft') {\r\n decorated = await decorateDraftResponse(\r\n decorated,\r\n doc,\r\n collectionConfig,\r\n req,\r\n previewSiteUrl,\r\n )\r\n }\r\n }\r\n return decorated\r\n } catch (err) {\r\n return textResponse(\r\n `Error reading from ${collection}: ${errorMessage(err)}`,\r\n )\r\n }\r\n },\r\n }\r\n}\r\n"],"names":["z","decorateDraftResponse","errorMessage","jsonResponse","stampMcpContext","textResponse","createFindDocumentTool","collectionSchemas","draftCollections","collectionsBySlug","previewSiteUrl","previewDisabled","findableSlugs","keys","descriptionLines","map","slug","has","name","routing","kind","action","description","join","parameters","collection","string","describe","id","optional","where","limit","number","int","min","max","depth","draft","boolean","handler","rawArgs","req","_extra","args","collectionConfig","get","doc","payload","findByID","overrideAccess","user","base","parsedWhere","trim","length","JSON","parse","err","result","find","pagination","totalDocs","docs","decorated","_status"],"mappings":"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"}
|