@payloadcms/plugin-mcp 4.0.0-internal.314c594 → 4.0.0-internal.31ba7ee

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/bin.js +0 -7
  2. package/dist/endpoint/access.js +2 -2
  3. package/dist/endpoint/access.js.map +1 -1
  4. package/dist/mcp/buildMcpServer.d.ts +2 -2
  5. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  6. package/dist/mcp/buildMcpServer.js +36 -24
  7. package/dist/mcp/buildMcpServer.js.map +1 -1
  8. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  9. package/dist/mcp/builtin/collections/authTools.js +80 -56
  10. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  11. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -1
  12. package/dist/mcp/builtin/collections/countTool.js +9 -14
  13. package/dist/mcp/builtin/collections/countTool.js.map +1 -1
  14. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -1
  15. package/dist/mcp/builtin/collections/countVersionsTool.js +9 -13
  16. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -1
  17. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  18. package/dist/mcp/builtin/collections/createTool.js +48 -49
  19. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  20. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  21. package/dist/mcp/builtin/collections/deleteTool.js +44 -43
  22. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  23. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -1
  24. package/dist/mcp/builtin/collections/duplicateTool.js +31 -37
  25. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -1
  26. package/dist/mcp/builtin/collections/fileInput.d.ts +21 -21
  27. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -1
  28. package/dist/mcp/builtin/collections/fileInput.js +34 -70
  29. package/dist/mcp/builtin/collections/fileInput.js.map +1 -1
  30. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -1
  31. package/dist/mcp/builtin/collections/findDistinctTool.js +11 -26
  32. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -1
  33. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  34. package/dist/mcp/builtin/collections/findTool.js +27 -45
  35. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  36. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -1
  37. package/dist/mcp/builtin/collections/findVersionByIDTool.js +17 -26
  38. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -1
  39. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -1
  40. package/dist/mcp/builtin/collections/findVersionsTool.js +16 -30
  41. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -1
  42. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -1
  43. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +12 -12
  44. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -1
  45. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -1
  46. package/dist/mcp/builtin/collections/restoreVersionTool.js +14 -26
  47. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -1
  48. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  49. package/dist/mcp/builtin/collections/updateTool.js +55 -87
  50. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  51. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +11 -11
  52. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -1
  53. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  54. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  55. package/dist/mcp/builtin/formatEntityError.js +55 -0
  56. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  57. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -1
  58. package/dist/mcp/builtin/getConfigInfoTool.js +3 -2
  59. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -1
  60. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -1
  61. package/dist/mcp/builtin/globals/countVersionsTool.js +9 -13
  62. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -1
  63. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  64. package/dist/mcp/builtin/globals/findTool.js +10 -16
  65. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  66. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -1
  67. package/dist/mcp/builtin/globals/findVersionByIDTool.js +13 -24
  68. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -1
  69. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -1
  70. package/dist/mcp/builtin/globals/findVersionsTool.js +13 -29
  71. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -1
  72. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -1
  73. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +11 -11
  74. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -1
  75. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -1
  76. package/dist/mcp/builtin/globals/restoreVersionTool.js +15 -22
  77. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -1
  78. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  79. package/dist/mcp/builtin/globals/updateTool.js +27 -36
  80. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  81. package/dist/stdio.js +4 -3
  82. package/dist/stdio.js.map +1 -1
  83. package/dist/types.d.ts +4 -4
  84. package/dist/types.d.ts.map +1 -1
  85. package/dist/types.js.map +1 -1
  86. package/package.json +5 -7
  87. package/src/endpoint/access.ts +2 -3
  88. package/src/mcp/buildMcpServer.ts +40 -30
  89. package/src/mcp/builtin/collections/authTools.ts +80 -69
  90. package/src/mcp/builtin/collections/countTool.ts +9 -21
  91. package/src/mcp/builtin/collections/countVersionsTool.ts +9 -18
  92. package/src/mcp/builtin/collections/createTool.ts +73 -96
  93. package/src/mcp/builtin/collections/deleteTool.ts +36 -64
  94. package/src/mcp/builtin/collections/duplicateTool.ts +35 -85
  95. package/src/mcp/builtin/collections/fileInput.spec.ts +2 -2
  96. package/src/mcp/builtin/collections/fileInput.ts +44 -81
  97. package/src/mcp/builtin/collections/findDistinctTool.ts +11 -63
  98. package/src/mcp/builtin/collections/findTool.ts +23 -105
  99. package/src/mcp/builtin/collections/findVersionByIDTool.ts +15 -58
  100. package/src/mcp/builtin/collections/findVersionsTool.ts +14 -85
  101. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +11 -11
  102. package/src/mcp/builtin/collections/restoreVersionTool.ts +14 -56
  103. package/src/mcp/builtin/collections/updateTool.ts +55 -109
  104. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +12 -12
  105. package/src/mcp/builtin/formatEntityError.ts +77 -0
  106. package/src/mcp/builtin/getConfigInfoTool.ts +2 -1
  107. package/src/mcp/builtin/globals/countVersionsTool.ts +9 -17
  108. package/src/mcp/builtin/globals/findTool.ts +12 -51
  109. package/src/mcp/builtin/globals/findVersionByIDTool.ts +13 -52
  110. package/src/mcp/builtin/globals/findVersionsTool.ts +14 -92
  111. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +10 -10
  112. package/src/mcp/builtin/globals/restoreVersionTool.ts +14 -45
  113. package/src/mcp/builtin/globals/updateTool.ts +37 -60
  114. package/src/stdio.ts +2 -2
  115. package/src/types.ts +4 -4
  116. package/dist/mcp/builtin/collections/fileInput.spec.js +0 -86
  117. package/dist/mcp/builtin/collections/fileInput.spec.js.map +0 -1
  118. package/dist/mcp/builtin/collections/formatCollectionError.d.ts +0 -9
  119. package/dist/mcp/builtin/collections/formatCollectionError.d.ts.map +0 -1
  120. package/dist/mcp/builtin/collections/formatCollectionError.js +0 -60
  121. package/dist/mcp/builtin/collections/formatCollectionError.js.map +0 -1
  122. package/dist/mcp/builtin/validateEntityData.d.ts +0 -14
  123. package/dist/mcp/builtin/validateEntityData.d.ts.map +0 -1
  124. package/dist/mcp/builtin/validateEntityData.js +0 -82
  125. package/dist/mcp/builtin/validateEntityData.js.map +0 -1
  126. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  127. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  128. package/dist/utils/getVirtualFieldNames.js +0 -35
  129. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  130. package/dist/utils/schemaConversion/filterFieldsByAccess.d.ts +0 -28
  131. package/dist/utils/schemaConversion/filterFieldsByAccess.d.ts.map +0 -1
  132. package/dist/utils/schemaConversion/filterFieldsByAccess.js +0 -71
  133. package/dist/utils/schemaConversion/filterFieldsByAccess.js.map +0 -1
  134. package/dist/utils/schemaConversion/getEntityInputSchema.d.ts +0 -13
  135. package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +0 -1
  136. package/dist/utils/schemaConversion/getEntityInputSchema.js +0 -66
  137. package/dist/utils/schemaConversion/getEntityInputSchema.js.map +0 -1
  138. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -10
  139. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  140. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -384
  141. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  142. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -133
  143. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  144. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  145. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  146. package/dist/utils/transformPointDataToPayload.js +0 -28
  147. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  148. package/dist/utils/whereSchema.d.ts +0 -9
  149. package/dist/utils/whereSchema.d.ts.map +0 -1
  150. package/dist/utils/whereSchema.js +0 -13
  151. package/dist/utils/whereSchema.js.map +0 -1
  152. package/src/mcp/builtin/collections/formatCollectionError.ts +0 -84
  153. package/src/mcp/builtin/validateEntityData.ts +0 -132
  154. package/src/utils/getVirtualFieldNames.ts +0 -53
  155. package/src/utils/schemaConversion/filterFieldsByAccess.ts +0 -123
  156. package/src/utils/schemaConversion/getEntityInputSchema.ts +0 -96
  157. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -96
  158. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -432
  159. package/src/utils/transformPointDataToPayload.ts +0 -40
  160. package/src/utils/whereSchema.ts +0 -24
package/bin.js CHANGED
@@ -27,13 +27,6 @@ const start = async () => {
27
27
  return
28
28
  }
29
29
 
30
- // tsx 4.22.4's synchronous loader leaks its namespace query onto Node built-ins on Node 23.5+.
31
- // Force the stable asynchronous loader path, matching payload/bin.js.
32
- const nodeModule = await import('node:module')
33
- if (typeof nodeModule.default.registerHooks === 'function') {
34
- nodeModule.default.registerHooks = undefined
35
- }
36
-
37
30
  const { tsImport } = await import('tsx/esm/api')
38
31
  const { runMcpStdio } = await tsImport('./dist/stdio.js', baseURL)
39
32
  await runMcpStdio()
@@ -62,13 +62,13 @@ export const filterMCPItems = async ({ items, overrideAccess, req })=>{
62
62
  switch(item.type){
63
63
  case 'collectionTool':
64
64
  return !item.tool.access || await item.tool.access({
65
- collectionSlug: item.collectionSlug,
65
+ slug: item.collectionSlug,
66
66
  permissions,
67
67
  req
68
68
  });
69
69
  case 'globalTool':
70
70
  return !item.tool.access || await item.tool.access({
71
- globalSlug: item.globalSlug,
71
+ slug: item.globalSlug,
72
72
  permissions,
73
73
  req
74
74
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoint/access.ts"],"sourcesContent":["import type { PayloadRequest, SanitizedPermissions } from 'payload'\n\nimport { getAccessResults, UnauthorizedError } from 'payload'\n\nimport type { AuthorizedMCP, MCPItem } from '../types.js'\n\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\n\nexport type GetAuthorizedMCPArgs = {\n overrideAccess: boolean\n req: PayloadRequest\n}\n\n/**\n * Authenticates an MCP request and removes items the user cannot access.\n * `overrideAccess` skips Payload and MCP item access checks.\n */\nexport const getAuthorizedMCP: (args: GetAuthorizedMCPArgs) => Promise<AuthorizedMCP> = async ({\n overrideAccess,\n req,\n}) => {\n const pluginConfig = getPluginConfig({ config: req.payload.config })\n\n if (pluginConfig.overrideGetAuthorizedMCP) {\n return await pluginConfig.overrideGetAuthorizedMCP({\n overrideAccess,\n pluginConfig,\n req,\n })\n }\n\n if (req.headers) {\n const headers = new Headers(req.headers)\n const hasAuthorization = headers.has('Authorization')\n\n headers.set('DisableAutologin', 'true')\n req.user = (await req.payload.auth({ headers, req })).user\n\n if (hasAuthorization && !req.user) {\n throw new UnauthorizedError(req.t)\n }\n }\n\n return {\n items: await filterMCPItems({\n items: pluginConfig.items,\n overrideAccess,\n req,\n }),\n overrideAccess,\n }\n}\n\nexport const filterMCPItems = async ({\n items,\n overrideAccess,\n req,\n}: {\n items: MCPItem[]\n overrideAccess: boolean\n req: PayloadRequest\n}): Promise<MCPItem[]> => {\n // Match Payload core: overrideAccess bypasses access evaluation\n if (overrideAccess) {\n return items\n }\n\n const authorizedItems: MCPItem[] = []\n\n const permissions = await getAccessResults({ req })\n\n for (const item of items) {\n if (!(await checkItemAccess({ item, permissions, req }))) {\n continue\n }\n authorizedItems.push(item)\n }\n\n return authorizedItems\n}\n\n/**\n * Runs MCP item access callbacks\n */\nconst checkItemAccess = async ({\n item,\n permissions,\n req,\n}: {\n item: MCPItem\n permissions?: SanitizedPermissions\n req: PayloadRequest\n}): Promise<boolean> => {\n switch (item.type) {\n case 'collectionTool':\n return (\n !item.tool.access ||\n (await item.tool.access({ collectionSlug: item.collectionSlug, permissions, req }))\n )\n case 'globalTool':\n return (\n !item.tool.access ||\n (await item.tool.access({ globalSlug: item.globalSlug, permissions, req }))\n )\n case 'prompt':\n return !item.prompt.access || (await item.prompt.access({ permissions, req }))\n case 'resource':\n return !item.resource.access || (await item.resource.access({ permissions, req }))\n case 'tool':\n return !item.tool.access || (await item.tool.access({ permissions, req }))\n }\n}\n"],"names":["getAccessResults","UnauthorizedError","getPluginConfig","getAuthorizedMCP","overrideAccess","req","pluginConfig","config","payload","overrideGetAuthorizedMCP","headers","Headers","hasAuthorization","has","set","user","auth","t","items","filterMCPItems","authorizedItems","permissions","item","checkItemAccess","push","type","tool","access","collectionSlug","globalSlug","prompt","resource"],"mappings":"AAEA,SAASA,gBAAgB,EAAEC,iBAAiB,QAAQ,UAAS;AAI7D,SAASC,eAAe,QAAQ,8BAA6B;AAO7D;;;CAGC,GACD,OAAO,MAAMC,mBAA2E,OAAO,EAC7FC,cAAc,EACdC,GAAG,EACJ;IACC,MAAMC,eAAeJ,gBAAgB;QAAEK,QAAQF,IAAIG,OAAO,CAACD,MAAM;IAAC;IAElE,IAAID,aAAaG,wBAAwB,EAAE;QACzC,OAAO,MAAMH,aAAaG,wBAAwB,CAAC;YACjDL;YACAE;YACAD;QACF;IACF;IAEA,IAAIA,IAAIK,OAAO,EAAE;QACf,MAAMA,UAAU,IAAIC,QAAQN,IAAIK,OAAO;QACvC,MAAME,mBAAmBF,QAAQG,GAAG,CAAC;QAErCH,QAAQI,GAAG,CAAC,oBAAoB;QAChCT,IAAIU,IAAI,GAAG,AAAC,CAAA,MAAMV,IAAIG,OAAO,CAACQ,IAAI,CAAC;YAAEN;YAASL;QAAI,EAAC,EAAGU,IAAI;QAE1D,IAAIH,oBAAoB,CAACP,IAAIU,IAAI,EAAE;YACjC,MAAM,IAAId,kBAAkBI,IAAIY,CAAC;QACnC;IACF;IAEA,OAAO;QACLC,OAAO,MAAMC,eAAe;YAC1BD,OAAOZ,aAAaY,KAAK;YACzBd;YACAC;QACF;QACAD;IACF;AACF,EAAC;AAED,OAAO,MAAMe,iBAAiB,OAAO,EACnCD,KAAK,EACLd,cAAc,EACdC,GAAG,EAKJ;IACC,gEAAgE;IAChE,IAAID,gBAAgB;QAClB,OAAOc;IACT;IAEA,MAAME,kBAA6B,EAAE;IAErC,MAAMC,cAAc,MAAMrB,iBAAiB;QAAEK;IAAI;IAEjD,KAAK,MAAMiB,QAAQJ,MAAO;QACxB,IAAI,CAAE,MAAMK,gBAAgB;YAAED;YAAMD;YAAahB;QAAI,IAAK;YACxD;QACF;QACAe,gBAAgBI,IAAI,CAACF;IACvB;IAEA,OAAOF;AACT,EAAC;AAED;;CAEC,GACD,MAAMG,kBAAkB,OAAO,EAC7BD,IAAI,EACJD,WAAW,EACXhB,GAAG,EAKJ;IACC,OAAQiB,KAAKG,IAAI;QACf,KAAK;YACH,OACE,CAACH,KAAKI,IAAI,CAACC,MAAM,IAChB,MAAML,KAAKI,IAAI,CAACC,MAAM,CAAC;gBAAEC,gBAAgBN,KAAKM,cAAc;gBAAEP;gBAAahB;YAAI;QAEpF,KAAK;YACH,OACE,CAACiB,KAAKI,IAAI,CAACC,MAAM,IAChB,MAAML,KAAKI,IAAI,CAACC,MAAM,CAAC;gBAAEE,YAAYP,KAAKO,UAAU;gBAAER;gBAAahB;YAAI;QAE5E,KAAK;YACH,OAAO,CAACiB,KAAKQ,MAAM,CAACH,MAAM,IAAK,MAAML,KAAKQ,MAAM,CAACH,MAAM,CAAC;gBAAEN;gBAAahB;YAAI;QAC7E,KAAK;YACH,OAAO,CAACiB,KAAKS,QAAQ,CAACJ,MAAM,IAAK,MAAML,KAAKS,QAAQ,CAACJ,MAAM,CAAC;gBAAEN;gBAAahB;YAAI;QACjF,KAAK;YACH,OAAO,CAACiB,KAAKI,IAAI,CAACC,MAAM,IAAK,MAAML,KAAKI,IAAI,CAACC,MAAM,CAAC;gBAAEN;gBAAahB;YAAI;IAC3E;AACF"}
1
+ {"version":3,"sources":["../../src/endpoint/access.ts"],"sourcesContent":["import type { PayloadRequest, SanitizedPermissions } from 'payload'\n\nimport { getAccessResults, UnauthorizedError } from 'payload'\n\nimport type { AuthorizedMCP, MCPItem } from '../types.js'\n\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\n\nexport type GetAuthorizedMCPArgs = {\n overrideAccess: boolean\n req: PayloadRequest\n}\n\n/**\n * Authenticates an MCP request and removes items the user cannot access.\n * `overrideAccess` skips Payload and MCP item access checks.\n */\nexport const getAuthorizedMCP: (args: GetAuthorizedMCPArgs) => Promise<AuthorizedMCP> = async ({\n overrideAccess,\n req,\n}) => {\n const pluginConfig = getPluginConfig({ config: req.payload.config })\n\n if (pluginConfig.overrideGetAuthorizedMCP) {\n return await pluginConfig.overrideGetAuthorizedMCP({\n overrideAccess,\n pluginConfig,\n req,\n })\n }\n\n if (req.headers) {\n const headers = new Headers(req.headers)\n const hasAuthorization = headers.has('Authorization')\n\n headers.set('DisableAutologin', 'true')\n req.user = (await req.payload.auth({ headers, req })).user\n\n if (hasAuthorization && !req.user) {\n throw new UnauthorizedError(req.t)\n }\n }\n\n return {\n items: await filterMCPItems({\n items: pluginConfig.items,\n overrideAccess,\n req,\n }),\n overrideAccess,\n }\n}\n\nexport const filterMCPItems = async ({\n items,\n overrideAccess,\n req,\n}: {\n items: MCPItem[]\n overrideAccess: boolean\n req: PayloadRequest\n}): Promise<MCPItem[]> => {\n // Match Payload core: overrideAccess bypasses access evaluation\n if (overrideAccess) {\n return items\n }\n\n const authorizedItems: MCPItem[] = []\n\n const permissions = await getAccessResults({ req })\n\n for (const item of items) {\n if (!(await checkItemAccess({ item, permissions, req }))) {\n continue\n }\n authorizedItems.push(item)\n }\n\n return authorizedItems\n}\n\n/**\n * Runs MCP item access callbacks\n */\nconst checkItemAccess = async ({\n item,\n permissions,\n req,\n}: {\n item: MCPItem\n permissions?: SanitizedPermissions\n req: PayloadRequest\n}): Promise<boolean> => {\n switch (item.type) {\n case 'collectionTool':\n return (\n !item.tool.access ||\n (await item.tool.access({ slug: item.collectionSlug, permissions, req }))\n )\n case 'globalTool':\n return (\n !item.tool.access || (await item.tool.access({ slug: item.globalSlug, permissions, req }))\n )\n case 'prompt':\n return !item.prompt.access || (await item.prompt.access({ permissions, req }))\n case 'resource':\n return !item.resource.access || (await item.resource.access({ permissions, req }))\n case 'tool':\n return !item.tool.access || (await item.tool.access({ permissions, req }))\n }\n}\n"],"names":["getAccessResults","UnauthorizedError","getPluginConfig","getAuthorizedMCP","overrideAccess","req","pluginConfig","config","payload","overrideGetAuthorizedMCP","headers","Headers","hasAuthorization","has","set","user","auth","t","items","filterMCPItems","authorizedItems","permissions","item","checkItemAccess","push","type","tool","access","slug","collectionSlug","globalSlug","prompt","resource"],"mappings":"AAEA,SAASA,gBAAgB,EAAEC,iBAAiB,QAAQ,UAAS;AAI7D,SAASC,eAAe,QAAQ,8BAA6B;AAO7D;;;CAGC,GACD,OAAO,MAAMC,mBAA2E,OAAO,EAC7FC,cAAc,EACdC,GAAG,EACJ;IACC,MAAMC,eAAeJ,gBAAgB;QAAEK,QAAQF,IAAIG,OAAO,CAACD,MAAM;IAAC;IAElE,IAAID,aAAaG,wBAAwB,EAAE;QACzC,OAAO,MAAMH,aAAaG,wBAAwB,CAAC;YACjDL;YACAE;YACAD;QACF;IACF;IAEA,IAAIA,IAAIK,OAAO,EAAE;QACf,MAAMA,UAAU,IAAIC,QAAQN,IAAIK,OAAO;QACvC,MAAME,mBAAmBF,QAAQG,GAAG,CAAC;QAErCH,QAAQI,GAAG,CAAC,oBAAoB;QAChCT,IAAIU,IAAI,GAAG,AAAC,CAAA,MAAMV,IAAIG,OAAO,CAACQ,IAAI,CAAC;YAAEN;YAASL;QAAI,EAAC,EAAGU,IAAI;QAE1D,IAAIH,oBAAoB,CAACP,IAAIU,IAAI,EAAE;YACjC,MAAM,IAAId,kBAAkBI,IAAIY,CAAC;QACnC;IACF;IAEA,OAAO;QACLC,OAAO,MAAMC,eAAe;YAC1BD,OAAOZ,aAAaY,KAAK;YACzBd;YACAC;QACF;QACAD;IACF;AACF,EAAC;AAED,OAAO,MAAMe,iBAAiB,OAAO,EACnCD,KAAK,EACLd,cAAc,EACdC,GAAG,EAKJ;IACC,gEAAgE;IAChE,IAAID,gBAAgB;QAClB,OAAOc;IACT;IAEA,MAAME,kBAA6B,EAAE;IAErC,MAAMC,cAAc,MAAMrB,iBAAiB;QAAEK;IAAI;IAEjD,KAAK,MAAMiB,QAAQJ,MAAO;QACxB,IAAI,CAAE,MAAMK,gBAAgB;YAAED;YAAMD;YAAahB;QAAI,IAAK;YACxD;QACF;QACAe,gBAAgBI,IAAI,CAACF;IACvB;IAEA,OAAOF;AACT,EAAC;AAED;;CAEC,GACD,MAAMG,kBAAkB,OAAO,EAC7BD,IAAI,EACJD,WAAW,EACXhB,GAAG,EAKJ;IACC,OAAQiB,KAAKG,IAAI;QACf,KAAK;YACH,OACE,CAACH,KAAKI,IAAI,CAACC,MAAM,IAChB,MAAML,KAAKI,IAAI,CAACC,MAAM,CAAC;gBAAEC,MAAMN,KAAKO,cAAc;gBAAER;gBAAahB;YAAI;QAE1E,KAAK;YACH,OACE,CAACiB,KAAKI,IAAI,CAACC,MAAM,IAAK,MAAML,KAAKI,IAAI,CAACC,MAAM,CAAC;gBAAEC,MAAMN,KAAKQ,UAAU;gBAAET;gBAAahB;YAAI;QAE3F,KAAK;YACH,OAAO,CAACiB,KAAKS,MAAM,CAACJ,MAAM,IAAK,MAAML,KAAKS,MAAM,CAACJ,MAAM,CAAC;gBAAEN;gBAAahB;YAAI;QAC7E,KAAK;YACH,OAAO,CAACiB,KAAKU,QAAQ,CAACL,MAAM,IAAK,MAAML,KAAKU,QAAQ,CAACL,MAAM,CAAC;gBAAEN;gBAAahB;YAAI;QACjF,KAAK;YACH,OAAO,CAACiB,KAAKI,IAAI,CAACC,MAAM,IAAK,MAAML,KAAKI,IAAI,CAACC,MAAM,CAAC;gBAAEN;gBAAahB;YAAI;IAC3E;AACF"}
@@ -1,5 +1,5 @@
1
+ import type { PayloadRequest } from 'payload';
1
2
  import { McpServer } from '@modelcontextprotocol/server';
2
- import { type PayloadRequest } from 'payload';
3
3
  import type { AuthorizedMCP, SanitizedMCPPluginConfig } from '../types.js';
4
4
  /**
5
5
  * Serving-entry-agnostic core: registers every authorized MCP item onto a fresh
@@ -9,7 +9,7 @@ import type { AuthorizedMCP, SanitizedMCPPluginConfig } from '../types.js';
9
9
  *
10
10
  * `req` is the request context handlers see. For HTTP it's the live
11
11
  * `PayloadRequest` derived from the incoming HTTP request; for stdio it's a
12
- * synthesized one built via `createLocalReq`.
12
+ * synthesized one built via `createPayloadRequest`.
13
13
  */
14
14
  export declare const buildMcpServer: ({ authorizedMCP, pluginConfig, req, }: {
15
15
  authorizedMCP: AuthorizedMCP;
@@ -1 +1 @@
1
- {"version":3,"file":"buildMcpServer.d.ts","sourceRoot":"","sources":["../../src/mcp/buildMcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,8BAA8B,CAAA;AAC5E,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAGvD,OAAO,KAAK,EACV,aAAa,EAMb,wBAAwB,EAEzB,MAAM,aAAa,CAAA;AAKpB;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,0CAIxB;IACD,aAAa,EAAE,aAAa,CAAA;IAC5B,YAAY,EAAE,wBAAwB,CAAA;IACtC,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,SAkNH,CAAA"}
1
+ {"version":3,"file":"buildMcpServer.d.ts","sourceRoot":"","sources":["../../src/mcp/buildMcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,EAAE,SAAS,EAAsB,MAAM,8BAA8B,CAAA;AAG5E,OAAO,KAAK,EACV,aAAa,EAMb,wBAAwB,EAEzB,MAAM,aAAa,CAAA;AAKpB;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,0CAIxB;IACD,aAAa,EAAE,aAAa,CAAA;IAC5B,YAAY,EAAE,wBAAwB,CAAA;IACtC,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,SAgNH,CAAA"}
@@ -1,6 +1,5 @@
1
1
  import { McpServer } from '@modelcontextprotocol/server';
2
- import { APIError } from 'payload';
3
- import { z } from 'zod';
2
+ import { APIError, strictObject, z } from 'payload';
4
3
  import { getLogger } from '../utils/getLogger.js';
5
4
  import { toStandardSchema } from '../utils/toStandardSchema.js';
6
5
  /**
@@ -11,8 +10,9 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
11
10
  *
12
11
  * `req` is the request context handlers see. For HTTP it's the live
13
12
  * `PayloadRequest` derived from the incoming HTTP request; for stdio it's a
14
- * synthesized one built via `createLocalReq`.
13
+ * synthesized one built via `createPayloadRequest`.
15
14
  */ export const buildMcpServer = ({ authorizedMCP, pluginConfig, req })=>{
15
+ z.config(z.locales.en());
16
16
  const serverOptions = pluginConfig.mcp?.serverOptions || {};
17
17
  const server = new McpServer({
18
18
  name: 'Payload MCP Server',
@@ -40,20 +40,19 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
40
40
  };
41
41
  /**
42
42
  * Runs a collection/global tool call:
43
- * - reads `collectionSlug` / `globalSlug` from the input
43
+ * - reads `slug` from the input
44
44
  * - runs access control: errors if `authorizedMCP.items` has no entry for this tool + slug
45
45
  * - runs the tool handler and finalizes its response
46
46
  */ const callEntityTool = async ({ input, item, serverContext })=>{
47
47
  const entity = item.type === 'collectionTool' ? 'collection' : 'global';
48
- const slugKey = item.type === 'collectionTool' ? 'collectionSlug' : 'globalSlug';
49
48
  const toolInput = input ?? {};
50
- const slug = toolInput[slugKey];
49
+ const slug = toolInput.slug;
51
50
  if (!slug) {
52
51
  return {
53
52
  content: [
54
53
  {
55
54
  type: 'text',
56
- text: `Error: "${item.mcpName}" requires ${slugKey}. Use getConfigInfo to inspect ${entity} slugs.`
55
+ text: `Error: "${item.mcpName}" requires slug. Use getConfigInfo to inspect ${entity} slugs.`
57
56
  }
58
57
  ],
59
58
  isError: true
@@ -79,10 +78,10 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
79
78
  };
80
79
  const response = await (match.type === 'collectionTool' ? match.tool.handler({
81
80
  ...handlerArgs,
82
- collectionSlug: slug
81
+ slug
83
82
  }) : match.tool.handler({
84
83
  ...handlerArgs,
85
- globalSlug: slug
84
+ slug
86
85
  }));
87
86
  return finalizeToolResponse({
88
87
  input: toolInput,
@@ -103,7 +102,6 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
103
102
  }
104
103
  registeredEntityTools.add(item.mcpName);
105
104
  const inputSchema = withSlugInput({
106
- name: item.type === 'collectionTool' ? 'collectionSlug' : 'globalSlug',
107
105
  input: item.tool.input
108
106
  });
109
107
  server.registerTool(item.mcpName, {
@@ -189,33 +187,47 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
189
187
  }
190
188
  return server;
191
189
  };
192
- const withSlugInput = ({ name, input })=>{
193
- const description = name === 'collectionSlug' ? 'The collection slug' : 'The global slug';
194
- const slugSchema = z.string().describe(description);
190
+ const withSlugInput = ({ input })=>{
191
+ const description = 'The target slug.';
192
+ const slugSchema = z.string().check(z.describe(description));
195
193
  if (!input) {
196
- return z.object({
197
- [name]: slugSchema
194
+ return strictObject({
195
+ slug: slugSchema
198
196
  });
199
197
  }
200
- if (input instanceof z.ZodObject) {
201
- return input.extend({
202
- [name]: slugSchema
198
+ if (typeof input === 'object' && input !== null && '~standard' in input) {
199
+ const schema = input['~standard'].jsonSchema.input({
200
+ target: 'draft-2020-12'
201
+ });
202
+ const properties = schema.properties;
203
+ if (properties?.slug) {
204
+ return input;
205
+ }
206
+ return addSlugToJSONSchema({
207
+ description,
208
+ schema: schema
203
209
  });
204
210
  }
205
- const schema = input;
211
+ return addSlugToJSONSchema({
212
+ description,
213
+ schema: input
214
+ });
215
+ };
216
+ const addSlugToJSONSchema = ({ description, schema })=>{
217
+ const objectSchema = schema;
206
218
  return {
207
- ...schema,
219
+ ...objectSchema,
208
220
  type: 'object',
209
221
  properties: {
210
- ...schema.properties,
211
- [name]: {
222
+ ...objectSchema.properties,
223
+ slug: {
212
224
  type: 'string',
213
225
  description
214
226
  }
215
227
  },
216
228
  required: Array.from(new Set([
217
- name,
218
- ...schema.required ?? []
229
+ 'slug',
230
+ ...objectSchema.required ?? []
219
231
  ]))
220
232
  };
221
233
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/buildMcpServer.ts"],"sourcesContent":["import { McpServer, type ServerContext } from '@modelcontextprotocol/server'\nimport { APIError, type PayloadRequest } from 'payload'\nimport { z } from 'zod'\n\nimport type {\n AuthorizedMCP,\n CollectionMCPItem,\n GlobalMCPItem,\n JsonSchemaType,\n MCPResponseOverride,\n MCPToolResponse,\n SanitizedMCPPluginConfig,\n ToolInputSchema,\n} from '../types.js'\n\nimport { getLogger } from '../utils/getLogger.js'\nimport { toStandardSchema } from '../utils/toStandardSchema.js'\n\n/**\n * Serving-entry-agnostic core: registers every authorized MCP item onto a fresh\n * `McpServer` and returns it. The HTTP and stdio entry point callers provide fresh\n * instances from this builder while they own the transport and protocol-era\n * decision.\n *\n * `req` is the request context handlers see. For HTTP it's the live\n * `PayloadRequest` derived from the incoming HTTP request; for stdio it's a\n * synthesized one built via `createLocalReq`.\n */\nexport const buildMcpServer = ({\n authorizedMCP,\n pluginConfig,\n req,\n}: {\n authorizedMCP: AuthorizedMCP\n pluginConfig: SanitizedMCPPluginConfig\n req: PayloadRequest\n}): McpServer => {\n const serverOptions = pluginConfig.mcp?.serverOptions || {}\n const server = new McpServer(\n { name: 'Payload MCP Server', version: '1.0.0', ...serverOptions.serverInfo },\n serverOptions.options,\n )\n\n const logger = getLogger({ payload: req.payload })\n\n /**\n * Wrap a tool handler's response with the tool's `overrideResponse`, then\n * strip the internal `doc` field so it doesn't leak onto the wire.\n */\n const finalizeToolResponse = async ({\n input,\n overrideResponse,\n response,\n toolName,\n }: {\n input: unknown\n overrideResponse?: MCPResponseOverride\n response: MCPToolResponse\n toolName: string\n }): Promise<MCPToolResponse> => {\n let overridden = overrideResponse?.(response, response.doc ?? {}, req) ?? response\n for (const hook of pluginConfig.hooks?.afterToolCall ?? []) {\n overridden = await hook({ input, req, response: overridden, toolName })\n }\n const { doc: _doc, ...rest } = overridden\n return rest\n }\n\n /**\n * Runs a collection/global tool call:\n * - reads `collectionSlug` / `globalSlug` from the input\n * - runs access control: errors if `authorizedMCP.items` has no entry for this tool + slug\n * - runs the tool handler and finalizes its response\n */\n const callEntityTool = async ({\n input,\n item,\n serverContext,\n }: {\n input: unknown\n item: CollectionMCPItem | GlobalMCPItem\n serverContext: ServerContext\n }): Promise<MCPToolResponse> => {\n const entity = item.type === 'collectionTool' ? 'collection' : 'global'\n const slugKey = item.type === 'collectionTool' ? 'collectionSlug' : 'globalSlug'\n const toolInput = (input ?? {}) as Record<string, unknown>\n const slug = toolInput[slugKey] as string | undefined\n\n if (!slug) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: \"${item.mcpName}\" requires ${slugKey}. Use getConfigInfo to inspect ${entity} slugs.`,\n },\n ],\n isError: true,\n }\n }\n\n const match = authorizedMCP.items.find(\n (candidate): candidate is CollectionMCPItem | GlobalMCPItem =>\n candidate.type === item.type &&\n candidate.mcpName === item.mcpName &&\n (candidate.type === 'collectionTool'\n ? candidate.collectionSlug === slug\n : candidate.type === 'globalTool' && candidate.globalSlug === slug),\n )\n\n if (!match) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: MCP access to \"${item.mcpName}\" is not enabled for ${entity} \"${slug}\"`,\n },\n ],\n isError: true,\n }\n }\n\n const handlerArgs = {\n authorizedMCP,\n input: toolInput,\n req,\n serverContext,\n }\n const response = await (match.type === 'collectionTool'\n ? match.tool.handler({ ...handlerArgs, collectionSlug: slug })\n : match.tool.handler({ ...handlerArgs, globalSlug: slug }))\n\n return finalizeToolResponse({\n input: toolInput,\n overrideResponse: match.tool.overrideResponse,\n response,\n toolName: match.mcpName,\n })\n }\n\n try {\n const registeredEntityTools = new Set<string>()\n\n for (const item of authorizedMCP.items) {\n switch (item.type) {\n case 'collectionTool':\n case 'globalTool': {\n if (registeredEntityTools.has(item.mcpName)) {\n break\n }\n registeredEntityTools.add(item.mcpName)\n\n const inputSchema = withSlugInput({\n name: item.type === 'collectionTool' ? 'collectionSlug' : 'globalSlug',\n input: item.tool.input,\n })\n\n server.registerTool(\n item.mcpName,\n {\n annotations: item.tool.annotations,\n description: item.tool.description,\n inputSchema: toStandardSchema(inputSchema),\n },\n async (input: unknown, ctx: ServerContext) =>\n callEntityTool({ input, item, serverContext: ctx }),\n )\n logger.info(`✅ Tool: ${item.mcpName} Registered.`)\n break\n }\n case 'prompt': {\n const prompt = item.prompt\n server.registerPrompt(\n item.mcpName,\n {\n argsSchema: prompt.argsSchema ? toStandardSchema(prompt.argsSchema) : undefined,\n description: prompt.description,\n title: prompt.title,\n },\n async (input: unknown, ctx: ServerContext) =>\n prompt.handler({\n input: (input ?? {}) as Record<string, unknown>,\n req,\n serverContext: ctx,\n }),\n )\n logger.info(`✅ Prompt: ${prompt.title} Registered.`)\n break\n }\n case 'resource': {\n const resource = item.resource\n server.registerResource(\n item.mcpName,\n // @ts-expect-error - Overload type ambiguity (string OR ResourceTemplate is valid)\n resource.uri,\n {\n description: resource.description,\n mimeType: resource.mimeType,\n title: resource.title,\n },\n // Static URIs call (uri, ctx); ResourceTemplates call (uri, params, ctx).\n // The rest-params shape lets us collect either signature uniformly.\n async (...sdkArgs: unknown[]) => {\n const ctx = sdkArgs[sdkArgs.length - 1] as ServerContext\n const uri = sdkArgs[0] as URL\n const params = (sdkArgs.length > 2 ? sdkArgs[1] : {}) as Record<string, string>\n return resource.handler({ params, req, serverContext: ctx, uri })\n },\n )\n logger.info(`✅ Resource: ${resource.title} Registered.`)\n break\n }\n case 'tool': {\n const tool = item.tool\n server.registerTool(\n item.mcpName,\n {\n annotations: tool.annotations,\n description: tool.description,\n inputSchema: tool.input ? toStandardSchema(tool.input) : undefined,\n },\n async (input: unknown, ctx: ServerContext) => {\n const toolInput = (input ?? {}) as Record<string, unknown>\n const response = await tool.handler({\n authorizedMCP,\n input: toolInput,\n req,\n serverContext: ctx,\n })\n return finalizeToolResponse({\n input: toolInput,\n overrideResponse: tool.overrideResponse,\n response,\n toolName: item.mcpName,\n })\n },\n )\n logger.info(`✅ Tool: ${item.mcpName} Registered.`)\n break\n }\n }\n }\n } catch (error) {\n throw new APIError(`Error initializing MCP handler: ${String(error)}`, 500)\n }\n\n return server\n}\n\nconst withSlugInput = ({\n name,\n input,\n}: {\n input?: ToolInputSchema\n name: 'collectionSlug' | 'globalSlug'\n}): ToolInputSchema => {\n const description = name === 'collectionSlug' ? 'The collection slug' : 'The global slug'\n const slugSchema = z.string().describe(description)\n\n if (!input) {\n return z.object({ [name]: slugSchema })\n }\n\n if (input instanceof z.ZodObject) {\n return input.extend({ [name]: slugSchema })\n }\n\n const schema = input as {\n properties?: Record<string, JsonSchemaType>\n required?: string[]\n } & JsonSchemaType\n\n return {\n ...schema,\n type: 'object',\n properties: {\n ...schema.properties,\n [name]: {\n type: 'string',\n description,\n },\n },\n required: Array.from(new Set([name, ...(schema.required ?? [])])),\n }\n}\n"],"names":["McpServer","APIError","z","getLogger","toStandardSchema","buildMcpServer","authorizedMCP","pluginConfig","req","serverOptions","mcp","server","name","version","serverInfo","options","logger","payload","finalizeToolResponse","input","overrideResponse","response","toolName","overridden","doc","hook","hooks","afterToolCall","_doc","rest","callEntityTool","item","serverContext","entity","type","slugKey","toolInput","slug","content","text","mcpName","isError","match","items","find","candidate","collectionSlug","globalSlug","handlerArgs","tool","handler","registeredEntityTools","Set","has","add","inputSchema","withSlugInput","registerTool","annotations","description","ctx","info","prompt","registerPrompt","argsSchema","undefined","title","resource","registerResource","uri","mimeType","sdkArgs","length","params","error","String","slugSchema","string","describe","object","ZodObject","extend","schema","properties","required","Array","from"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/mcp/buildMcpServer.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { McpServer, type ServerContext } from '@modelcontextprotocol/server'\nimport { APIError, strictObject, z } from 'payload'\n\nimport type {\n AuthorizedMCP,\n CollectionMCPItem,\n GlobalMCPItem,\n JsonSchemaType,\n MCPResponseOverride,\n MCPToolResponse,\n SanitizedMCPPluginConfig,\n ToolInputSchema,\n} from '../types.js'\n\nimport { getLogger } from '../utils/getLogger.js'\nimport { toStandardSchema } from '../utils/toStandardSchema.js'\n\n/**\n * Serving-entry-agnostic core: registers every authorized MCP item onto a fresh\n * `McpServer` and returns it. The HTTP and stdio entry point callers provide fresh\n * instances from this builder while they own the transport and protocol-era\n * decision.\n *\n * `req` is the request context handlers see. For HTTP it's the live\n * `PayloadRequest` derived from the incoming HTTP request; for stdio it's a\n * synthesized one built via `createPayloadRequest`.\n */\nexport const buildMcpServer = ({\n authorizedMCP,\n pluginConfig,\n req,\n}: {\n authorizedMCP: AuthorizedMCP\n pluginConfig: SanitizedMCPPluginConfig\n req: PayloadRequest\n}): McpServer => {\n z.config(z.locales.en())\n\n const serverOptions = pluginConfig.mcp?.serverOptions || {}\n const server = new McpServer(\n { name: 'Payload MCP Server', version: '1.0.0', ...serverOptions.serverInfo },\n serverOptions.options,\n )\n\n const logger = getLogger({ payload: req.payload })\n\n /**\n * Wrap a tool handler's response with the tool's `overrideResponse`, then\n * strip the internal `doc` field so it doesn't leak onto the wire.\n */\n const finalizeToolResponse = async ({\n input,\n overrideResponse,\n response,\n toolName,\n }: {\n input: unknown\n overrideResponse?: MCPResponseOverride\n response: MCPToolResponse\n toolName: string\n }): Promise<MCPToolResponse> => {\n let overridden = overrideResponse?.(response, response.doc ?? {}, req) ?? response\n for (const hook of pluginConfig.hooks?.afterToolCall ?? []) {\n overridden = await hook({ input, req, response: overridden, toolName })\n }\n const { doc: _doc, ...rest } = overridden\n return rest\n }\n\n /**\n * Runs a collection/global tool call:\n * - reads `slug` from the input\n * - runs access control: errors if `authorizedMCP.items` has no entry for this tool + slug\n * - runs the tool handler and finalizes its response\n */\n const callEntityTool = async ({\n input,\n item,\n serverContext,\n }: {\n input: unknown\n item: CollectionMCPItem | GlobalMCPItem\n serverContext: ServerContext\n }): Promise<MCPToolResponse> => {\n const entity = item.type === 'collectionTool' ? 'collection' : 'global'\n const toolInput = (input ?? {}) as Record<string, unknown>\n const slug = toolInput.slug as string | undefined\n\n if (!slug) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: \"${item.mcpName}\" requires slug. Use getConfigInfo to inspect ${entity} slugs.`,\n },\n ],\n isError: true,\n }\n }\n\n const match = authorizedMCP.items.find(\n (candidate): candidate is CollectionMCPItem | GlobalMCPItem =>\n candidate.type === item.type &&\n candidate.mcpName === item.mcpName &&\n (candidate.type === 'collectionTool'\n ? candidate.collectionSlug === slug\n : candidate.type === 'globalTool' && candidate.globalSlug === slug),\n )\n\n if (!match) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: MCP access to \"${item.mcpName}\" is not enabled for ${entity} \"${slug}\"`,\n },\n ],\n isError: true,\n }\n }\n\n const handlerArgs = {\n authorizedMCP,\n input: toolInput,\n req,\n serverContext,\n }\n const response = await (match.type === 'collectionTool'\n ? match.tool.handler({ ...handlerArgs, slug })\n : match.tool.handler({ ...handlerArgs, slug }))\n\n return finalizeToolResponse({\n input: toolInput,\n overrideResponse: match.tool.overrideResponse,\n response,\n toolName: match.mcpName,\n })\n }\n\n try {\n const registeredEntityTools = new Set<string>()\n\n for (const item of authorizedMCP.items) {\n switch (item.type) {\n case 'collectionTool':\n case 'globalTool': {\n if (registeredEntityTools.has(item.mcpName)) {\n break\n }\n registeredEntityTools.add(item.mcpName)\n\n const inputSchema = withSlugInput({ input: item.tool.input })\n\n server.registerTool(\n item.mcpName,\n {\n annotations: item.tool.annotations,\n description: item.tool.description,\n inputSchema: toStandardSchema(inputSchema),\n },\n async (input: unknown, ctx: ServerContext) =>\n callEntityTool({ input, item, serverContext: ctx }),\n )\n logger.info(`✅ Tool: ${item.mcpName} Registered.`)\n break\n }\n case 'prompt': {\n const prompt = item.prompt\n server.registerPrompt(\n item.mcpName,\n {\n argsSchema: prompt.argsSchema ? toStandardSchema(prompt.argsSchema) : undefined,\n description: prompt.description,\n title: prompt.title,\n },\n async (input: unknown, ctx: ServerContext) =>\n prompt.handler({\n input: (input ?? {}) as Record<string, unknown>,\n req,\n serverContext: ctx,\n }),\n )\n logger.info(`✅ Prompt: ${prompt.title} Registered.`)\n break\n }\n case 'resource': {\n const resource = item.resource\n server.registerResource(\n item.mcpName,\n // @ts-expect-error - Overload type ambiguity (string OR ResourceTemplate is valid)\n resource.uri,\n {\n description: resource.description,\n mimeType: resource.mimeType,\n title: resource.title,\n },\n // Static URIs call (uri, ctx); ResourceTemplates call (uri, params, ctx).\n // The rest-params shape lets us collect either signature uniformly.\n async (...sdkArgs: unknown[]) => {\n const ctx = sdkArgs[sdkArgs.length - 1] as ServerContext\n const uri = sdkArgs[0] as URL\n const params = (sdkArgs.length > 2 ? sdkArgs[1] : {}) as Record<string, string>\n return resource.handler({ params, req, serverContext: ctx, uri })\n },\n )\n logger.info(`✅ Resource: ${resource.title} Registered.`)\n break\n }\n case 'tool': {\n const tool = item.tool\n server.registerTool(\n item.mcpName,\n {\n annotations: tool.annotations,\n description: tool.description,\n inputSchema: tool.input ? toStandardSchema(tool.input) : undefined,\n },\n async (input: unknown, ctx: ServerContext) => {\n const toolInput = (input ?? {}) as Record<string, unknown>\n const response = await tool.handler({\n authorizedMCP,\n input: toolInput,\n req,\n serverContext: ctx,\n })\n return finalizeToolResponse({\n input: toolInput,\n overrideResponse: tool.overrideResponse,\n response,\n toolName: item.mcpName,\n })\n },\n )\n logger.info(`✅ Tool: ${item.mcpName} Registered.`)\n break\n }\n }\n }\n } catch (error) {\n throw new APIError(`Error initializing MCP handler: ${String(error)}`, 500)\n }\n\n return server\n}\n\nconst withSlugInput = ({ input }: { input?: ToolInputSchema }): ToolInputSchema => {\n const description = 'The target slug.'\n const slugSchema = z.string().check(z.describe(description))\n\n if (!input) {\n return strictObject({ slug: slugSchema }) as unknown as ToolInputSchema\n }\n\n if (typeof input === 'object' && input !== null && '~standard' in input) {\n const schema = input['~standard'].jsonSchema.input({ target: 'draft-2020-12' })\n const properties = schema.properties as Record<string, unknown> | undefined\n\n if (properties?.slug) {\n return input\n }\n\n return addSlugToJSONSchema({ description, schema: schema as JsonSchemaType })\n }\n\n return addSlugToJSONSchema({ description, schema: input })\n}\n\nconst addSlugToJSONSchema = ({\n description,\n schema,\n}: {\n description: string\n schema: JsonSchemaType\n}): JsonSchemaType => {\n const objectSchema = schema as {\n properties?: Record<string, JsonSchemaType>\n required?: string[]\n } & JsonSchemaType\n\n return {\n ...objectSchema,\n type: 'object',\n properties: {\n ...objectSchema.properties,\n slug: {\n type: 'string',\n description,\n },\n },\n required: Array.from(new Set(['slug', ...(objectSchema.required ?? [])])),\n }\n}\n"],"names":["McpServer","APIError","strictObject","z","getLogger","toStandardSchema","buildMcpServer","authorizedMCP","pluginConfig","req","config","locales","en","serverOptions","mcp","server","name","version","serverInfo","options","logger","payload","finalizeToolResponse","input","overrideResponse","response","toolName","overridden","doc","hook","hooks","afterToolCall","_doc","rest","callEntityTool","item","serverContext","entity","type","toolInput","slug","content","text","mcpName","isError","match","items","find","candidate","collectionSlug","globalSlug","handlerArgs","tool","handler","registeredEntityTools","Set","has","add","inputSchema","withSlugInput","registerTool","annotations","description","ctx","info","prompt","registerPrompt","argsSchema","undefined","title","resource","registerResource","uri","mimeType","sdkArgs","length","params","error","String","slugSchema","string","check","describe","schema","jsonSchema","target","properties","addSlugToJSONSchema","objectSchema","required","Array","from"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"authTools.d.ts","sourceRoot":"","sources":["../../../../src/mcp/builtin/collections/authTools.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,kBAAkB,4CAqC7B,CAAA;AAEF,eAAO,MAAM,4BAA4B,4CAuCvC,CAAA;AAEF,eAAO,MAAM,mBAAmB,4CAiD9B,CAAA;AAEF,eAAO,MAAM,2BAA2B,4CAmCtC,CAAA;AAEF,eAAO,MAAM,oBAAoB,4CAgC/B,CAAA;AAEF,eAAO,MAAM,oBAAoB,4CAiC/B,CAAA"}
1
+ {"version":3,"file":"authTools.d.ts","sourceRoot":"","sources":["../../../../src/mcp/builtin/collections/authTools.ts"],"names":[],"mappings":"AAkCA,eAAO,MAAM,kBAAkB,4CA0D7B,CAAA;AAEF,eAAO,MAAM,4BAA4B,4CAmCvC,CAAA;AAEF,eAAO,MAAM,mBAAmB,4CAuC9B,CAAA;AAEF,eAAO,MAAM,2BAA2B,4CAmCtC,CAAA;AAEF,eAAO,MAAM,oBAAoB,4CAmC/B,CAAA;AAEF,eAAO,MAAM,oBAAoB,4CAiC/B,CAAA"}
@@ -1,4 +1,5 @@
1
- import { z } from 'zod';
1
+ import { createPayloadRequest, strictObject, z } from 'payload';
2
+ import { defaultAccess } from '../../../defaultAccess.js';
2
3
  import { defineCollectionTool } from '../../../defineTool.js';
3
4
  import { getLogger } from '../../../utils/getLogger.js';
4
5
  /**
@@ -10,13 +11,13 @@ import { getLogger } from '../../../utils/getLogger.js';
10
11
  * `auth` (check current token) doesn't actually depend on the collection at
11
12
  * runtime — `payload.auth({ headers })` is global — but it's grouped under the
12
13
  * users collection so all auth-shaped tools live in one place.
13
- */ const emailSchema = z.string().email().describe('The user email address');
14
- const wrapError = (name)=>({ collectionSlug, message })=>{
14
+ */ const emailSchema = z.email().check(z.describe('The user email address.'));
15
+ const wrapError = (name)=>({ slug, message })=>{
15
16
  return {
16
17
  content: [
17
18
  {
18
19
  type: 'text',
19
- text: `❌ **Error in ${name}** on ${collectionSlug}: ${message}`
20
+ text: `❌ **Error in ${name}** on ${slug}: ${message}`
20
21
  }
21
22
  ]
22
23
  };
@@ -30,10 +31,10 @@ export const authCollectionTool = defineCollectionTool({
30
31
  title: 'Check Auth Status'
31
32
  },
32
33
  description: 'Checks authentication status for the current user.',
33
- input: z.object({
34
- headers: z.record(z.string(), z.string()).describe('Optional custom headers to send with the authentication request').optional()
34
+ input: strictObject({
35
+ headers: z.optional(z.record(z.string(), z.string())).check(z.describe('Custom headers to send with the authentication request.'))
35
36
  })
36
- }).handler(async ({ collectionSlug, input, req })=>{
37
+ }).handler(async ({ slug, authorizedMCP, input, req })=>{
37
38
  const logger = getLogger({
38
39
  payload: req.payload
39
40
  });
@@ -42,9 +43,33 @@ export const authCollectionTool = defineCollectionTool({
42
43
  if (input.headers) {
43
44
  authHeaders = new Headers(input.headers);
44
45
  }
46
+ const authReq = await createPayloadRequest({
47
+ payload: req.payload,
48
+ req: {
49
+ headers: authHeaders
50
+ }
51
+ });
45
52
  const result = await req.payload.auth({
46
- headers: authHeaders
53
+ headers: authHeaders,
54
+ req: authReq
47
55
  });
56
+ if (result.user) {
57
+ const authenticatedUser = result.user;
58
+ // Auth strategies read users with trusted Local API access. Re-read the user with the MCP
59
+ // caller's access level before returning it to the client.
60
+ const user = await req.payload.findByID({
61
+ id: authenticatedUser.id,
62
+ collection: authenticatedUser.collection,
63
+ overrideAccess: authorizedMCP.overrideAccess,
64
+ req: authReq
65
+ });
66
+ result.user = {
67
+ ...user,
68
+ _sid: authenticatedUser._sid,
69
+ _strategy: authenticatedUser._strategy,
70
+ collection: authenticatedUser.collection
71
+ };
72
+ }
48
73
  return {
49
74
  content: [
50
75
  {
@@ -56,9 +81,9 @@ export const authCollectionTool = defineCollectionTool({
56
81
  };
57
82
  } catch (error) {
58
83
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
59
- logger.error(`Error in auth tool on ${collectionSlug}: ${errorMessage}`);
84
+ logger.error(`Error in auth tool on ${slug}: ${errorMessage}`);
60
85
  return wrapError('auth')({
61
- collectionSlug,
86
+ slug,
62
87
  message: errorMessage
63
88
  });
64
89
  }
@@ -72,38 +97,36 @@ export const forgotPasswordCollectionTool = defineCollectionTool({
72
97
  title: 'Forgot Password'
73
98
  },
74
99
  description: 'Sends a password reset email to a user.',
75
- input: z.object({
76
- disableEmail: z.boolean().describe('Whether to disable sending the email (for testing)').optional().default(false),
100
+ input: strictObject({
77
101
  email: emailSchema
78
102
  })
79
- }).handler(async ({ collectionSlug, input, req })=>{
103
+ }).handler(async ({ slug, input, req })=>{
80
104
  const logger = getLogger({
81
105
  payload: req.payload
82
106
  });
83
107
  try {
84
- const result = await req.payload.forgotPassword({
85
- collection: collectionSlug,
108
+ await req.payload.forgotPassword({
109
+ collection: slug,
86
110
  data: {
87
111
  email: input.email
88
112
  },
89
- disableEmail: input.disableEmail
113
+ disableEmail: false,
114
+ overrideAccess: false,
115
+ req
90
116
  });
91
117
  return {
92
118
  content: [
93
119
  {
94
120
  type: 'text',
95
- text: `# Forgot Password Email Sent\n\n**Collection:** ${collectionSlug}\n**Email:** ${input.email}\n\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``
121
+ text: 'If an account matches that email, password reset instructions have been sent.'
96
122
  }
97
- ],
98
- doc: {
99
- result
100
- }
123
+ ]
101
124
  };
102
125
  } catch (error) {
103
126
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
104
- logger.error(`Error in forgotPassword tool on ${collectionSlug}: ${errorMessage}`);
127
+ logger.error(`Error in forgotPassword tool on ${slug}: ${errorMessage}`);
105
128
  return wrapError('forgotPassword')({
106
- collectionSlug,
129
+ slug,
107
130
  message: errorMessage
108
131
  });
109
132
  }
@@ -117,40 +140,39 @@ export const loginCollectionTool = defineCollectionTool({
117
140
  title: 'User Login'
118
141
  },
119
142
  description: 'Authenticates a user with email and password.',
120
- input: z.object({
121
- depth: z.number().int().min(0).max(10).describe('Depth of population for relationships').optional().default(0),
143
+ input: strictObject({
144
+ depth: z._default(z.int().check(z.minimum(0), z.maximum(10)), 0).check(z.describe('Depth of population for relationships.')),
122
145
  email: emailSchema,
123
- password: z.string().describe('The user password'),
124
- showHiddenFields: z.boolean().describe('Whether to show hidden fields in the response').optional().default(false)
146
+ password: z.string().check(z.describe('The user password.'))
125
147
  })
126
- }).handler(async ({ collectionSlug, input, req })=>{
148
+ }).handler(async ({ slug, authorizedMCP, input, req })=>{
127
149
  const logger = getLogger({
128
150
  payload: req.payload
129
151
  });
130
152
  try {
131
153
  const result = await req.payload.login({
132
- collection: collectionSlug,
154
+ collection: slug,
133
155
  data: {
134
156
  email: input.email,
135
157
  password: input.password
136
158
  },
137
159
  depth: input.depth,
138
- showHiddenFields: input.showHiddenFields
160
+ overrideAccess: authorizedMCP.overrideAccess
139
161
  });
140
162
  return {
141
163
  content: [
142
164
  {
143
165
  type: 'text',
144
- text: `# Login Successful\n\n**User:** ${input.email}\n**Collection:** ${collectionSlug}\n\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``
166
+ text: `# Login Successful\n\n**User:** ${input.email}\n**Collection:** ${slug}\n\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``
145
167
  }
146
168
  ],
147
169
  doc: result
148
170
  };
149
171
  } catch (error) {
150
172
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
151
- logger.error(`Error in login tool on ${collectionSlug}: ${errorMessage}`);
173
+ logger.error(`Error in login tool on ${slug}: ${errorMessage}`);
152
174
  return wrapError('login')({
153
- collectionSlug,
175
+ slug,
154
176
  message: errorMessage
155
177
  });
156
178
  }
@@ -164,42 +186,43 @@ export const resetPasswordCollectionTool = defineCollectionTool({
164
186
  title: 'Reset Password'
165
187
  },
166
188
  description: 'Resets a user password with a reset token.',
167
- input: z.object({
168
- password: z.string().describe('The new password for the user'),
169
- token: z.string().describe('The password reset token sent to the user email')
189
+ input: strictObject({
190
+ password: z.string().check(z.describe('The new password for the user.')),
191
+ token: z.string().check(z.describe('The password reset token sent to the user email.'))
170
192
  })
171
- }).handler(async ({ collectionSlug, input, req })=>{
193
+ }).handler(async ({ slug, authorizedMCP, input, req })=>{
172
194
  const logger = getLogger({
173
195
  payload: req.payload
174
196
  });
175
197
  try {
176
198
  const result = await req.payload.resetPassword({
177
- collection: collectionSlug,
199
+ collection: slug,
178
200
  data: {
179
201
  password: input.password,
180
202
  token: input.token
181
203
  },
182
- overrideAccess: true
204
+ overrideAccess: authorizedMCP.overrideAccess
183
205
  });
184
206
  return {
185
207
  content: [
186
208
  {
187
209
  type: 'text',
188
- text: `# Password Reset Successful\n\n**Collection:** ${collectionSlug}\n\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``
210
+ text: `# Password Reset Successful\n\n**Collection:** ${slug}\n\n\`\`\`json\n${JSON.stringify(result)}\n\`\`\``
189
211
  }
190
212
  ],
191
213
  doc: result
192
214
  };
193
215
  } catch (error) {
194
216
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
195
- logger.error(`Error in resetPassword tool on ${collectionSlug}: ${errorMessage}`);
217
+ logger.error(`Error in resetPassword tool on ${slug}: ${errorMessage}`);
196
218
  return wrapError('resetPassword')({
197
- collectionSlug,
219
+ slug,
198
220
  message: errorMessage
199
221
  });
200
222
  }
201
223
  });
202
224
  export const unlockCollectionTool = defineCollectionTool({
225
+ access: (args)=>defaultAccess(args) && Boolean(args.permissions?.collections?.[args.slug]?.unlock),
203
226
  annotations: {
204
227
  destructiveHint: false,
205
228
  idempotentHint: true,
@@ -208,26 +231,27 @@ export const unlockCollectionTool = defineCollectionTool({
208
231
  title: 'Unlock Account'
209
232
  },
210
233
  description: 'Unlocks a user account that has been locked due to failed login attempts.',
211
- input: z.object({
234
+ input: strictObject({
212
235
  email: emailSchema
213
236
  })
214
- }).handler(async ({ collectionSlug, input, req })=>{
237
+ }).handler(async ({ slug, authorizedMCP, input, req })=>{
215
238
  const logger = getLogger({
216
239
  payload: req.payload
217
240
  });
218
241
  try {
219
242
  const result = await req.payload.unlock({
220
- collection: collectionSlug,
243
+ collection: slug,
221
244
  data: {
222
245
  email: input.email
223
246
  },
224
- overrideAccess: true
247
+ overrideAccess: authorizedMCP.overrideAccess,
248
+ req
225
249
  });
226
250
  return {
227
251
  content: [
228
252
  {
229
253
  type: 'text',
230
- text: `# Account Unlocked\n\n**Collection:** ${collectionSlug}\n**Email:** ${input.email}\n**Result:** ${result ? 'Success' : 'Failed'}`
254
+ text: `# Account Unlocked\n\n**Collection:** ${slug}\n**Email:** ${input.email}\n**Result:** ${result ? 'Success' : 'Failed'}`
231
255
  }
232
256
  ],
233
257
  doc: {
@@ -236,9 +260,9 @@ export const unlockCollectionTool = defineCollectionTool({
236
260
  };
237
261
  } catch (error) {
238
262
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
239
- logger.error(`Error in unlock tool on ${collectionSlug}: ${errorMessage}`);
263
+ logger.error(`Error in unlock tool on ${slug}: ${errorMessage}`);
240
264
  return wrapError('unlock')({
241
- collectionSlug,
265
+ slug,
242
266
  message: errorMessage
243
267
  });
244
268
  }
@@ -252,23 +276,23 @@ export const verifyCollectionTool = defineCollectionTool({
252
276
  title: 'Email Verification'
253
277
  },
254
278
  description: 'Verifies a user email with a verification token.',
255
- input: z.object({
256
- token: z.string().describe('The verification token sent to the user email')
279
+ input: strictObject({
280
+ token: z.string().check(z.describe('The verification token sent to the user email.'))
257
281
  })
258
- }).handler(async ({ collectionSlug, input, req })=>{
282
+ }).handler(async ({ slug, input, req })=>{
259
283
  const logger = getLogger({
260
284
  payload: req.payload
261
285
  });
262
286
  try {
263
287
  const result = await req.payload.verifyEmail({
264
- collection: collectionSlug,
288
+ collection: slug,
265
289
  token: input.token
266
290
  });
267
291
  return {
268
292
  content: [
269
293
  {
270
294
  type: 'text',
271
- text: `# Email Verification Successful\n\n**Collection:** ${collectionSlug}\n**Result:** ${result ? 'Success' : 'Failed'}`
295
+ text: `# Email Verification Successful\n\n**Collection:** ${slug}\n**Result:** ${result ? 'Success' : 'Failed'}`
272
296
  }
273
297
  ],
274
298
  doc: {
@@ -277,9 +301,9 @@ export const verifyCollectionTool = defineCollectionTool({
277
301
  };
278
302
  } catch (error) {
279
303
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
280
- logger.error(`Error in verify tool on ${collectionSlug}: ${errorMessage}`);
304
+ logger.error(`Error in verify tool on ${slug}: ${errorMessage}`);
281
305
  return wrapError('verify')({
282
- collectionSlug,
306
+ slug,
283
307
  message: errorMessage
284
308
  });
285
309
  }