payload-mcp-toolkit 0.7.4 → 0.7.5

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 CHANGED
@@ -99,7 +99,7 @@ The same shape is editable programmatically via Payload's REST and GraphQL APIs
99
99
  - `uploadMedia` — fetch a public HTTPS image, validate (SSRF-safe with a streaming size cap), create a Media doc.
100
100
 
101
101
  *Discovery*
102
- - `findDocument` — read documents by `id` or `where` filter, polymorphic across collections. Decorates draft responses with preview URLs when configured.
102
+ - `findDocument` — read documents by `documentId` or `where` filter, polymorphic across collections. Decorates draft responses with preview URLs when configured.
103
103
  - `resolveReference` — search collections by name/title/slug for relationship IDs.
104
104
  - `searchContent` — natural-language editor triage (status, recency, missing fields, free text).
105
105
 
@@ -60,6 +60,14 @@ const MEDIA_SLUG = 'media';
60
60
  stampMcpContext(req);
61
61
  const isDraftCollection = draftCollections.has(collection);
62
62
  const asDraft = draft ?? isDraftCollection;
63
+ // Payload's `draft: false` writes a published version but leaves the main
64
+ // row's `_status` at its field default ('draft'), so the admin list shows
65
+ // "draft" even though routes serve the published doc. Set `_status`
66
+ // explicitly to match intent (mirrors publishDraft); respect a caller
67
+ // value if one was passed in `data`.
68
+ if (isDraftCollection && data._status === undefined) {
69
+ data._status = asDraft ? 'draft' : 'published';
70
+ }
63
71
  try {
64
72
  const doc = await req.payload.create({
65
73
  collection: collection,
@@ -1 +1 @@
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"}
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 // Payload's `draft: false` writes a published version but leaves the main\r\n // row's `_status` at its field default ('draft'), so the admin list shows\r\n // \"draft\" even though routes serve the published doc. Set `_status`\r\n // explicitly to match intent (mirrors publishDraft); respect a caller\r\n // value if one was passed in `data`.\r\n if (isDraftCollection && data._status === undefined) {\r\n data._status = asDraft ? 'draft' : 'published'\r\n }\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","_status","undefined","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,0EAA0E;YAC1E,0EAA0E;YAC1E,oEAAoE;YACpE,sEAAsE;YACtE,qCAAqC;YACrC,IAAIA,qBAAqBf,KAAKiB,OAAO,KAAKC,WAAW;gBACnDlB,KAAKiB,OAAO,GAAGD,UAAU,UAAU;YACrC;YAEA,IAAI;gBACF,MAAMG,MAAM,MAAMb,IAAIc,OAAO,CAACC,MAAM,CAAC;oBACnCxB,YAAYA;oBACZG,MAAMA;oBACNC,OAAOe;oBACPV;oBACAgB,gBAAgB;oBAChBC,MAAMjB,IAAIiB,IAAI;gBAChB;gBAEA,MAAMC,cAAclD,kBAAkB6C,KAAKM,OAAO,AAACN,IAAyBO,EAAE,IAAI;gBAClF,MAAMC,QAAQF,OAAO,AAACN,IAAyBO,EAAE,IAAI;gBACrD,MAAME,YAAYb,qBAAqBC,UAAU5C,aAAa;gBAE9D,OAAOI,aACL,CAAC,SAAS,EAAEgD,YAAY,KAAK,EAAE3B,WAAW,MAAM,EAAE8B,MAAM,EAAE,EAAEC,WAAW;YAE3E,EAAE,OAAOC,OAAO;gBACd,OAAOrD,aACL,CAAC,2BAA2B,EAAEqB,WAAW,EAAE,EAAExB,aAAawD,QAAQ;YAEtE;QACF;IACF;AACF"}
@@ -19,7 +19,7 @@ export declare function createDeleteDocumentTool(collectionSchemas: Map<string,
19
19
  description: string;
20
20
  parameters: {
21
21
  collection: z.ZodString;
22
- id: z.ZodString;
22
+ documentId: z.ZodString;
23
23
  };
24
24
  handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
25
25
  };
@@ -21,10 +21,10 @@ import { errorMessage, getDocDisplayName, stampMcpContext, textResponse } from '
21
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
22
  parameters: {
23
23
  collection: z.string().describe(`Collection slug. One of: ${deletableSlugs.join(', ')}`),
24
- id: z.string().describe('Document ID to delete.')
24
+ documentId: z.string().describe('Document ID to delete.')
25
25
  },
26
26
  handler: async (args, req, _extra)=>{
27
- const { collection, id } = args;
27
+ const { collection, documentId } = args;
28
28
  if (!collectionSchemas.has(collection)) {
29
29
  return textResponse(`Error: Unknown collection "${collection}". Valid: ${deletableSlugs.join(', ')}`);
30
30
  }
@@ -32,15 +32,15 @@ import { errorMessage, getDocDisplayName, stampMcpContext, textResponse } from '
32
32
  try {
33
33
  const doc = await req.payload.delete({
34
34
  collection: collection,
35
- id,
35
+ id: documentId,
36
36
  req,
37
37
  overrideAccess: false,
38
38
  user: req.user
39
39
  });
40
- const displayName = getDocDisplayName(doc, id);
41
- return textResponse(`Deleted "${displayName}" from ${collection} (ID: ${id}).`);
40
+ const displayName = getDocDisplayName(doc, documentId);
41
+ return textResponse(`Deleted "${displayName}" from ${collection} (ID: ${documentId}).`);
42
42
  } catch (err) {
43
- return textResponse(`Error deleting ${id} from ${collection}: ${errorMessage(err)}`);
43
+ return textResponse(`Error deleting ${documentId} from ${collection}: ${errorMessage(err)}`);
44
44
  }
45
45
  }
46
46
  };
@@ -1 +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"}
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 documentId: 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 documentId: 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, documentId } = 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: documentId,\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n\r\n const displayName = getDocDisplayName(doc, documentId)\r\n return textResponse(\r\n `Deleted \"${displayName}\" from ${collection} (ID: ${documentId}).`,\r\n )\r\n } catch (err) {\r\n return textResponse(\r\n `Error deleting ${documentId} 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","documentId","handler","args","req","_extra","has","doc","payload","delete","id","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,YAAYnB,EAAEiB,MAAM,GAAGC,QAAQ,CAAC;QAClC;QACAE,SAAS,OAAOC,MAA+BC,KAAqBC;YAClE,MAAM,EAAEP,UAAU,EAAEG,UAAU,EAAE,GAAGE;YAEnC,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;oBACZY,IAAIT;oBACJG;oBACAO,gBAAgB;oBAChBC,MAAMR,IAAIQ,IAAI;gBAChB;gBAEA,MAAMC,cAAc7B,kBAAkBuB,KAAKN;gBAC3C,OAAOf,aACL,CAAC,SAAS,EAAE2B,YAAY,OAAO,EAAEf,WAAW,MAAM,EAAEG,WAAW,EAAE,CAAC;YAEtE,EAAE,OAAOa,KAAK;gBACZ,OAAO5B,aACL,CAAC,eAAe,EAAEe,WAAW,MAAM,EAAEH,WAAW,EAAE,EAAEf,aAAa+B,MAAM;YAE3E;QACF;IACF;AACF"}
@@ -8,8 +8,8 @@ import type { CollectionSchema } from '../types';
8
8
  * `createDocument` / `updateDocument`.
9
9
  *
10
10
  * Two modes:
11
- * - `id` set: `payload.findByID` (single doc)
12
- * - `id` unset: `payload.find` with optional JSON-string `where`
11
+ * - `documentId` set: `payload.findByID` (single doc)
12
+ * - `documentId` unset: `payload.find` with optional JSON-string `where`
13
13
  *
14
14
  * Draft-enabled collections get preview URLs appended to draft documents
15
15
  * via `decorateDraftResponse`.
@@ -23,7 +23,7 @@ export declare function createFindDocumentTool(collectionSchemas: Map<string, Co
23
23
  description: string;
24
24
  parameters: {
25
25
  collection: z.ZodString;
26
- id: z.ZodOptional<z.ZodString>;
26
+ documentId: z.ZodOptional<z.ZodString>;
27
27
  where: z.ZodOptional<z.ZodString>;
28
28
  limit: z.ZodOptional<z.ZodNumber>;
29
29
  depth: z.ZodOptional<z.ZodNumber>;
@@ -7,8 +7,8 @@ import { decorateDraftResponse, errorMessage, jsonResponse, stampMcpContext, tex
7
7
  * `createDocument` / `updateDocument`.
8
8
  *
9
9
  * Two modes:
10
- * - `id` set: `payload.findByID` (single doc)
11
- * - `id` unset: `payload.find` with optional JSON-string `where`
10
+ * - `documentId` set: `payload.findByID` (single doc)
11
+ * - `documentId` unset: `payload.find` with optional JSON-string `where`
12
12
  *
13
13
  * Draft-enabled collections get preview URLs appended to draft documents
14
14
  * via `decorateDraftResponse`.
@@ -23,28 +23,28 @@ import { decorateDraftResponse, errorMessage, jsonResponse, stampMcpContext, tex
23
23
  kind: 'collection',
24
24
  action: 'read'
25
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'),
26
+ description: 'Read documents from any collection. Pass `documentId` for a single document, or omit `documentId` 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
27
  parameters: {
28
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.'),
29
+ documentId: 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 `documentId` is set.'),
31
31
  limit: z.number().int().min(1).max(100).optional().describe('Max results when listing. Default 25.'),
32
32
  depth: z.number().int().min(0).max(3).optional().describe('Relationship population depth. Default 1.'),
33
33
  draft: z.boolean().optional().describe('When true, returns draft versions of draft-enabled collections. Default false (published only).')
34
34
  },
35
35
  handler: async (rawArgs, req, _extra)=>{
36
36
  const args = rawArgs;
37
- const { collection, id, where, limit, depth, draft } = args;
37
+ const { collection, documentId, where, limit, depth, draft } = args;
38
38
  if (!collectionSchemas.has(collection)) {
39
39
  return textResponse(`Error: Unknown collection "${collection}". Valid: ${findableSlugs.join(', ')}`);
40
40
  }
41
41
  stampMcpContext(req);
42
42
  const collectionConfig = collectionsBySlug.get(collection);
43
43
  try {
44
- if (id) {
44
+ if (documentId) {
45
45
  const doc = await req.payload.findByID({
46
46
  collection: collection,
47
- id,
47
+ id: documentId,
48
48
  depth: depth ?? 1,
49
49
  draft: draft ?? false,
50
50
  req,
@@ -1 +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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"}
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 documentId?: 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 * - `documentId` set: `payload.findByID` (single doc)\r\n * - `documentId` 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 `documentId` for a single document, or omit `documentId` 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 documentId: z\r\n .string()\r\n .optional()\r\n .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 `documentId` 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, documentId, 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 (documentId) {\r\n const doc = await req.payload.findByID({\r\n collection: collection as never,\r\n id: documentId,\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","documentId","optional","where","limit","number","int","min","max","depth","draft","boolean","handler","rawArgs","req","_extra","args","collectionConfig","get","doc","payload","findByID","id","overrideAccess","user","base","parsedWhere","trim","length","JSON","parse","err","result","find","pagination","totalDocs","docs","decorated","_status"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payload-mcp-toolkit",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Standalone schema-aware MCP plugin for Payload CMS v3 — owns the /api/mcp endpoint, scoped API keys, draft workflow, and AI-friendly tools so non-technical editors can manage content via AI chat.",
5
5
  "license": "MIT",
6
6
  "author": "jon8800",