@payloadcms/plugin-mcp 4.0.0-internal.a0ef1b8 → 4.0.0-internal.a2d053c

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 (259) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/defaultAccess.d.ts +3 -0
  3. package/dist/defaultAccess.d.ts.map +1 -0
  4. package/dist/defaultAccess.js +3 -0
  5. package/dist/defaultAccess.js.map +1 -0
  6. package/dist/defineTool.d.ts +8 -8
  7. package/dist/defineTool.d.ts.map +1 -1
  8. package/dist/endpoint/access.d.ts +12 -5
  9. package/dist/endpoint/access.d.ts.map +1 -1
  10. package/dist/endpoint/access.js +95 -88
  11. package/dist/endpoint/access.js.map +1 -1
  12. package/dist/endpoint/index.d.ts +1 -1
  13. package/dist/endpoint/index.d.ts.map +1 -1
  14. package/dist/endpoint/index.js +109 -16
  15. package/dist/endpoint/index.js.map +1 -1
  16. package/dist/endpoint/oauth.d.ts +39 -0
  17. package/dist/endpoint/oauth.d.ts.map +1 -0
  18. package/dist/endpoint/oauth.js +94 -0
  19. package/dist/endpoint/oauth.js.map +1 -0
  20. package/dist/endpoint/oauthLoginToken.d.ts +21 -0
  21. package/dist/endpoint/oauthLoginToken.d.ts.map +1 -0
  22. package/dist/endpoint/oauthLoginToken.js +79 -0
  23. package/dist/endpoint/oauthLoginToken.js.map +1 -0
  24. package/dist/endpoint/oauthServer.d.ts +12 -0
  25. package/dist/endpoint/oauthServer.d.ts.map +1 -0
  26. package/dist/endpoint/oauthServer.js +362 -0
  27. package/dist/endpoint/oauthServer.js.map +1 -0
  28. package/dist/exports/client.d.ts +1 -1
  29. package/dist/exports/client.d.ts.map +1 -1
  30. package/dist/exports/client.js +1 -1
  31. package/dist/exports/client.js.map +1 -1
  32. package/dist/exports/internal.d.ts +2 -0
  33. package/dist/exports/internal.d.ts.map +1 -0
  34. package/dist/exports/internal.js +3 -0
  35. package/dist/exports/internal.js.map +1 -0
  36. package/dist/index.d.ts +12 -4
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +30 -22
  39. package/dist/index.js.map +1 -1
  40. package/dist/mcp/buildMcpServer.d.ts +6 -6
  41. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  42. package/dist/mcp/buildMcpServer.js +146 -81
  43. package/dist/mcp/buildMcpServer.js.map +1 -1
  44. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  45. package/dist/mcp/builtin/collections/authTools.js +123 -61
  46. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  47. package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
  48. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
  49. package/dist/mcp/builtin/collections/countTool.js +63 -0
  50. package/dist/mcp/builtin/collections/countTool.js.map +1 -0
  51. package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
  52. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
  53. package/dist/mcp/builtin/collections/countVersionsTool.js +60 -0
  54. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
  55. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  56. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  57. package/dist/mcp/builtin/collections/createTool.js +114 -68
  58. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  59. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  60. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  61. package/dist/mcp/builtin/collections/deleteTool.js +55 -61
  62. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  63. package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
  64. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
  65. package/dist/mcp/builtin/collections/duplicateTool.js +90 -0
  66. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
  67. package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
  68. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
  69. package/dist/mcp/builtin/collections/fileInput.js +88 -0
  70. package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
  71. package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
  72. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
  73. package/dist/mcp/builtin/collections/findDistinctTool.js +77 -0
  74. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
  75. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  76. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  77. package/dist/mcp/builtin/collections/findTool.js +62 -75
  78. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  79. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
  80. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
  81. package/dist/mcp/builtin/collections/findVersionByIDTool.js +74 -0
  82. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
  83. package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
  84. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
  85. package/dist/mcp/builtin/collections/findVersionsTool.js +84 -0
  86. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
  87. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  88. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  89. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +103 -0
  90. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  91. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
  92. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
  93. package/dist/mcp/builtin/collections/restoreVersionTool.js +69 -0
  94. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -0
  95. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  96. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  97. package/dist/mcp/builtin/collections/updateTool.js +118 -137
  98. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  99. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  100. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  101. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  102. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  103. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  104. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  105. package/dist/mcp/builtin/formatEntityError.js +55 -0
  106. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  107. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  108. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  109. package/dist/mcp/builtin/getConfigInfoTool.js +69 -0
  110. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  111. package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
  112. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
  113. package/dist/mcp/builtin/globals/countVersionsTool.js +60 -0
  114. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
  115. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  116. package/dist/mcp/builtin/globals/findTool.js +27 -36
  117. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  118. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
  119. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
  120. package/dist/mcp/builtin/globals/findVersionByIDTool.js +68 -0
  121. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
  122. package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
  123. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
  124. package/dist/mcp/builtin/globals/findVersionsTool.js +78 -0
  125. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
  126. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  127. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  128. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +81 -0
  129. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  130. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
  131. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
  132. package/dist/mcp/builtin/globals/restoreVersionTool.js +70 -0
  133. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
  134. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  135. package/dist/mcp/builtin/globals/updateTool.js +44 -62
  136. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  137. package/dist/mcp/builtinTools.d.ts +150 -19
  138. package/dist/mcp/builtinTools.d.ts.map +1 -1
  139. package/dist/mcp/builtinTools.js +125 -20
  140. package/dist/mcp/builtinTools.js.map +1 -1
  141. package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
  142. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  143. package/dist/mcp/sanitizeMCPConfig.js +127 -58
  144. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  145. package/dist/stdio.d.ts +3 -3
  146. package/dist/stdio.d.ts.map +1 -1
  147. package/dist/stdio.js +86 -47
  148. package/dist/stdio.js.map +1 -1
  149. package/dist/types.d.ts +205 -114
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/types.js +2 -4
  152. package/dist/types.js.map +1 -1
  153. package/dist/utils/camelCase.d.ts.map +1 -1
  154. package/dist/utils/getPluginConfig.d.ts +1 -1
  155. package/dist/utils/getPluginConfig.js +1 -1
  156. package/dist/utils/getPluginConfig.js.map +1 -1
  157. package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
  158. package/dist/utils/seal.d.ts +18 -0
  159. package/dist/utils/seal.d.ts.map +1 -0
  160. package/dist/utils/seal.js +27 -0
  161. package/dist/utils/seal.js.map +1 -0
  162. package/dist/utils/toStandardSchema.d.ts.map +1 -1
  163. package/dist/utils/toStandardSchema.js.map +1 -1
  164. package/package.json +27 -37
  165. package/src/defaultAccess.ts +3 -0
  166. package/src/endpoint/access.ts +99 -101
  167. package/src/endpoint/index.ts +95 -15
  168. package/src/endpoint/oauth.ts +92 -0
  169. package/src/endpoint/oauthLoginToken.ts +91 -0
  170. package/src/endpoint/oauthServer.ts +396 -0
  171. package/src/exports/client.ts +2 -1
  172. package/src/exports/internal.ts +1 -0
  173. package/src/index.ts +52 -25
  174. package/src/mcp/buildMcpServer.ts +181 -116
  175. package/src/mcp/builtin/collections/authTools.ts +124 -77
  176. package/src/mcp/builtin/collections/countTool.ts +61 -0
  177. package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
  178. package/src/mcp/builtin/collections/createTool.ts +115 -88
  179. package/src/mcp/builtin/collections/deleteTool.ts +49 -75
  180. package/src/mcp/builtin/collections/duplicateTool.ts +84 -0
  181. package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
  182. package/src/mcp/builtin/collections/fileInput.ts +127 -0
  183. package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
  184. package/src/mcp/builtin/collections/findTool.ts +58 -111
  185. package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
  186. package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
  187. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +91 -0
  188. package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
  189. package/src/mcp/builtin/collections/updateTool.ts +106 -143
  190. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  191. package/src/mcp/builtin/formatEntityError.ts +77 -0
  192. package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
  193. package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
  194. package/src/mcp/builtin/globals/findTool.ts +28 -58
  195. package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
  196. package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
  197. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +73 -0
  198. package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
  199. package/src/mcp/builtin/globals/updateTool.ts +52 -80
  200. package/src/mcp/builtinTools.ts +118 -41
  201. package/src/mcp/sanitizeMCPConfig.ts +121 -78
  202. package/src/stdio.ts +80 -47
  203. package/src/types.ts +223 -119
  204. package/src/utils/getPluginConfig.ts +1 -1
  205. package/src/utils/seal.ts +46 -0
  206. package/src/utils/toStandardSchema.ts +1 -1
  207. package/dist/@types/assets.d.js +0 -2
  208. package/dist/@types/assets.d.js.map +0 -1
  209. package/dist/collection/getAccessField.d.ts +0 -12
  210. package/dist/collection/getAccessField.d.ts.map +0 -1
  211. package/dist/collection/getAccessField.js +0 -57
  212. package/dist/collection/getAccessField.js.map +0 -1
  213. package/dist/collection/index.d.ts +0 -6
  214. package/dist/collection/index.d.ts.map +0 -1
  215. package/dist/collection/index.js +0 -60
  216. package/dist/collection/index.js.map +0 -1
  217. package/dist/components/AccessField/index.client.d.ts +0 -10
  218. package/dist/components/AccessField/index.client.d.ts.map +0 -1
  219. package/dist/components/AccessField/index.client.js +0 -305
  220. package/dist/components/AccessField/index.client.js.map +0 -1
  221. package/dist/components/AccessField/index.css +0 -93
  222. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  223. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  224. package/dist/utils/getVirtualFieldNames.js +0 -35
  225. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  226. package/dist/utils/localAPIDefaults.d.ts +0 -20
  227. package/dist/utils/localAPIDefaults.d.ts.map +0 -1
  228. package/dist/utils/localAPIDefaults.js +0 -19
  229. package/dist/utils/localAPIDefaults.js.map +0 -1
  230. package/dist/utils/schemaConversion/buildToolInput.d.ts +0 -29
  231. package/dist/utils/schemaConversion/buildToolInput.d.ts.map +0 -1
  232. package/dist/utils/schemaConversion/buildToolInput.js +0 -51
  233. package/dist/utils/schemaConversion/buildToolInput.js.map +0 -1
  234. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
  235. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
  236. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
  237. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
  238. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -15
  239. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  240. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -464
  241. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  242. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -158
  243. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  244. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  245. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  246. package/dist/utils/transformPointDataToPayload.js +0 -28
  247. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  248. package/src/@types/assets.d.ts +0 -3
  249. package/src/collection/getAccessField.ts +0 -64
  250. package/src/collection/index.ts +0 -64
  251. package/src/components/AccessField/index.client.tsx +0 -344
  252. package/src/components/AccessField/index.css +0 -93
  253. package/src/utils/getVirtualFieldNames.ts +0 -53
  254. package/src/utils/localAPIDefaults.ts +0 -22
  255. package/src/utils/schemaConversion/buildToolInput.ts +0 -68
  256. package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
  257. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -103
  258. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -529
  259. package/src/utils/transformPointDataToPayload.ts +0 -40
@@ -1,10 +1,10 @@
1
- import { COLLECTION_AUTH_BUILTIN_ENTRIES, COLLECTION_AUTH_BUILTINS, COLLECTION_BUILTIN_ENTRIES, COLLECTION_BUILTINS, GLOBAL_BUILTIN_ENTRIES, GLOBAL_BUILTINS } from './builtinTools.js';
1
+ import { defaultAccess } from '../defaultAccess.js';
2
+ import { COLLECTION_AUTH_BUILTIN_ENTRIES, COLLECTION_AUTH_BUILTINS, COLLECTION_BUILTIN_ENTRIES, COLLECTION_BUILTINS, GLOBAL_BUILTIN_ENTRIES, GLOBAL_BUILTINS, TOOL_BUILTIN_ENTRIES, TOOL_BUILTINS } from './builtinTools.js';
2
3
  /**
3
4
  * Converts the user-configured `MCPPluginConfig` into a `SanitizedMCPPluginConfig`:
4
5
  * - Flattens `tools` / `prompts` / `resources` / per-collection / per-global
5
6
  * tool maps into a single `items` array.
6
7
  * - Applies built-in tools for collections and globals, respecting opt-out user overrides.
7
- * - Applies the `userCollection` default
8
8
  *
9
9
  * Called once during plugin init. After that, `plugins['@payloadcms/plugin-mcp']
10
10
  * ?.options` holds the sanitized result
@@ -22,83 +22,143 @@ import { COLLECTION_AUTH_BUILTIN_ENTRIES, COLLECTION_AUTH_BUILTINS, COLLECTION_B
22
22
  pluginConfig
23
23
  }));
24
24
  }
25
- for (const [key, tool] of Object.entries(pluginConfig.tools ?? {})){
25
+ for (const [configKey, { mcpName, tool }] of TOOL_BUILTIN_ENTRIES){
26
26
  items.push({
27
27
  type: 'tool',
28
- key,
29
- label: key,
30
- tool
28
+ configKey,
29
+ label: tool.annotations?.title ?? configKey,
30
+ mcpName,
31
+ tool: {
32
+ ...tool,
33
+ access: tool.access ?? defaultAccess
34
+ }
35
+ });
36
+ }
37
+ for (const [configKey, tool] of Object.entries(pluginConfig.tools ?? {})){
38
+ if (configKey in TOOL_BUILTINS) {
39
+ continue;
40
+ }
41
+ items.push({
42
+ type: 'tool',
43
+ configKey,
44
+ label: tool.annotations?.title ?? configKey,
45
+ mcpName: configKey,
46
+ tool: {
47
+ ...tool,
48
+ access: tool.access ?? defaultAccess
49
+ }
31
50
  });
32
51
  }
33
- for (const [key, prompt] of Object.entries(pluginConfig.prompts ?? {})){
52
+ for (const [configKey, prompt] of Object.entries(pluginConfig.prompts ?? {})){
34
53
  items.push({
35
54
  type: 'prompt',
36
- key,
37
- label: prompt.title ?? key,
38
- prompt
55
+ configKey,
56
+ label: prompt.title ?? configKey,
57
+ mcpName: configKey,
58
+ prompt: {
59
+ ...prompt,
60
+ access: prompt.access ?? defaultAccess
61
+ }
39
62
  });
40
63
  }
41
- for (const [key, resource] of Object.entries(pluginConfig.resources ?? {})){
64
+ for (const [configKey, resource] of Object.entries(pluginConfig.resources ?? {})){
42
65
  items.push({
43
66
  type: 'resource',
44
- key,
45
- label: resource.title ?? key,
46
- resource
67
+ configKey,
68
+ label: resource.title ?? configKey,
69
+ mcpName: configKey,
70
+ resource: {
71
+ ...resource,
72
+ access: resource.access ?? defaultAccess
73
+ }
47
74
  });
48
75
  }
49
- // Mirror Payload's own admin.user detection (sanitize.ts) since plugins run first.
50
- const firstCollectionWithAuth = config.collections.find(({ auth })=>Boolean(auth));
51
76
  return {
52
77
  disabled: pluginConfig.disabled,
78
+ hooks: pluginConfig.hooks,
53
79
  items,
54
80
  mcp: pluginConfig.mcp,
55
- overrideApiKeyCollection: pluginConfig.overrideApiKeyCollection,
56
- overrideAuth: pluginConfig.overrideAuth,
57
- userCollection: pluginConfig.userCollection ?? config.admin?.user ?? firstCollectionWithAuth?.slug ?? 'users'
81
+ oauth: pluginConfig.oauth === false ? undefined : pluginConfig.oauth ?? {
82
+ type: 'builtin'
83
+ },
84
+ overrideGetAuthorizedMCP: pluginConfig.overrideGetAuthorizedMCP
58
85
  };
59
86
  };
60
87
  const sanitizeCollectionConfig = ({ collection, pluginConfig })=>{
61
- if (collection.slug === 'payload-mcp-api-keys') {
62
- return [];
63
- }
64
88
  const slug = collection.slug;
65
89
  const collectionPluginConfig = pluginConfig.collections?.[slug];
66
90
  const items = [];
67
- for (const [toolKey, tool] of COLLECTION_BUILTIN_ENTRIES){
91
+ /**
92
+ * Payload disables duplicate for auth collections by default unless the
93
+ * collection explicitly opts back in with `disableDuplicate: false`.
94
+ */ const isDuplicateDisabled = collection.disableDuplicate === true || Boolean(collection.auth) && collection.disableDuplicate !== false;
95
+ for (const [toolKey, { mcpName, requiresDuplicateEnabled, requiresUpload, requiresVersions, tool }] of COLLECTION_BUILTIN_ENTRIES){
96
+ if (requiresVersions && !collection.versions) {
97
+ continue;
98
+ }
99
+ if (requiresDuplicateEnabled && isDuplicateDisabled) {
100
+ continue;
101
+ }
102
+ if (requiresUpload && !collection.upload) {
103
+ continue;
104
+ }
68
105
  const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey];
69
106
  if (matchedConfigEntry === false) {
70
107
  continue;
71
108
  }
109
+ const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined;
110
+ const annotations = {
111
+ ...tool.annotations,
112
+ ...override?.annotations
113
+ };
72
114
  items.push({
73
115
  type: 'collectionTool',
74
116
  collectionSlug: slug,
75
- key: toolKey,
76
- label: capitalize(toolKey),
77
- tool: overrideBuiltinTool(tool, matchedConfigEntry, collectionPluginConfig)
117
+ configKey: toolKey,
118
+ label: annotations.title ?? toolKey,
119
+ mcpName,
120
+ tool: {
121
+ ...tool,
122
+ access: override?.access ?? tool.access ?? defaultAccess,
123
+ annotations,
124
+ description: override?.description ?? tool.description,
125
+ overrideResponse: override?.overrideResponse ?? collectionPluginConfig?.overrideResponse ?? tool.overrideResponse
126
+ }
78
127
  });
79
128
  }
80
129
  if (collection.auth) {
81
- for (const [authToolKey, { label, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES){
130
+ for (const [authToolKey, { mcpName, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES){
82
131
  const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey];
83
132
  if (!matchedConfigEntry) {
84
133
  continue;
85
134
  }
86
135
  // `true` means "enable, no override"; only the object form carries fields.
87
136
  const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined;
137
+ const annotations = {
138
+ ...tool.annotations,
139
+ ...override?.annotations
140
+ };
88
141
  items.push({
89
142
  type: 'collectionTool',
90
143
  collectionSlug: slug,
91
- key: authToolKey,
92
- label,
93
- tool: overrideBuiltinTool(tool, override, collectionPluginConfig)
144
+ configKey: authToolKey,
145
+ label: annotations.title ?? authToolKey,
146
+ mcpName,
147
+ tool: {
148
+ ...tool,
149
+ access: override?.access ?? tool.access ?? defaultAccess,
150
+ annotations,
151
+ description: override?.description ?? tool.description,
152
+ overrideResponse: override?.overrideResponse ?? collectionPluginConfig?.overrideResponse ?? tool.overrideResponse
153
+ }
94
154
  });
95
155
  }
96
156
  }
97
157
  // Cast: builtin keys are filtered out below, so the remaining values are
98
158
  // always custom tools (`CollectionTool`) or undefined
99
159
  const customEntries = Object.entries(collectionPluginConfig?.tools ?? {});
100
- for (const [key, customTool] of customEntries){
101
- if (key in COLLECTION_BUILTINS || key in COLLECTION_AUTH_BUILTINS) {
160
+ for (const [configKey, customTool] of customEntries){
161
+ if (configKey in COLLECTION_BUILTINS || configKey in COLLECTION_AUTH_BUILTINS) {
102
162
  continue;
103
163
  }
104
164
  if (!customTool) {
@@ -107,9 +167,13 @@ const sanitizeCollectionConfig = ({ collection, pluginConfig })=>{
107
167
  items.push({
108
168
  type: 'collectionTool',
109
169
  collectionSlug: slug,
110
- key,
111
- label: key,
112
- tool: customTool
170
+ configKey,
171
+ label: customTool.annotations?.title ?? configKey,
172
+ mcpName: configKey,
173
+ tool: {
174
+ ...customTool,
175
+ access: customTool.access ?? defaultAccess
176
+ }
113
177
  });
114
178
  }
115
179
  return items;
@@ -118,22 +182,37 @@ const sanitizeGlobalConfig = ({ global, pluginConfig })=>{
118
182
  const slug = global.slug;
119
183
  const globalPluginConfig = pluginConfig.globals?.[slug];
120
184
  const items = [];
121
- for (const [toolKey, baseTool] of GLOBAL_BUILTIN_ENTRIES){
185
+ for (const [toolKey, { mcpName, requiresVersions, tool }] of GLOBAL_BUILTIN_ENTRIES){
186
+ if (requiresVersions && !global.versions) {
187
+ continue;
188
+ }
122
189
  const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey];
123
190
  if (matchedConfigEntry === false) {
124
191
  continue;
125
192
  }
193
+ const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined;
194
+ const annotations = {
195
+ ...tool.annotations,
196
+ ...override?.annotations
197
+ };
126
198
  items.push({
127
199
  type: 'globalTool',
200
+ configKey: toolKey,
128
201
  globalSlug: slug,
129
- key: toolKey,
130
- label: capitalize(toolKey),
131
- tool: overrideBuiltinTool(baseTool, matchedConfigEntry, globalPluginConfig)
202
+ label: annotations.title ?? toolKey,
203
+ mcpName,
204
+ tool: {
205
+ ...tool,
206
+ access: override?.access ?? tool.access ?? defaultAccess,
207
+ annotations,
208
+ description: override?.description ?? tool.description,
209
+ overrideResponse: override?.overrideResponse ?? globalPluginConfig?.overrideResponse ?? tool.overrideResponse
210
+ }
132
211
  });
133
212
  }
134
213
  const customEntries = Object.entries(globalPluginConfig?.tools ?? {});
135
- for (const [key, customTool] of customEntries){
136
- if (key in GLOBAL_BUILTINS) {
214
+ for (const [configKey, customTool] of customEntries){
215
+ if (configKey in GLOBAL_BUILTINS) {
137
216
  continue;
138
217
  }
139
218
  if (!customTool) {
@@ -141,27 +220,17 @@ const sanitizeGlobalConfig = ({ global, pluginConfig })=>{
141
220
  }
142
221
  items.push({
143
222
  type: 'globalTool',
223
+ configKey,
144
224
  globalSlug: slug,
145
- key,
146
- label: key,
147
- tool: customTool
225
+ label: customTool.annotations?.title ?? configKey,
226
+ mcpName: configKey,
227
+ tool: {
228
+ ...customTool,
229
+ access: customTool.access ?? defaultAccess
230
+ }
148
231
  });
149
232
  }
150
233
  return items;
151
234
  };
152
- const capitalize = (s)=>s.charAt(0).toUpperCase() + s.slice(1);
153
- /**
154
- * Spread the static built-in tool and apply consumer-side overrides.
155
- * Precedence: per-tool override > scope-level (collection/global) override >
156
- * the static tool's defaults. `toolEntry` is whatever the user put under
157
- * `tools: { find: ... }` — could be `true`, `false`, or an override object —
158
- * so it's narrowed internally.
159
- */ const overrideBuiltinTool = (tool, toolOverride, entityPluginConfig)=>{
160
- return {
161
- ...tool,
162
- description: toolOverride?.description ?? entityPluginConfig?.description ?? tool.description,
163
- overrideResponse: toolOverride?.overrideResponse ?? entityPluginConfig?.overrideResponse ?? tool.overrideResponse
164
- };
165
- };
166
235
 
167
236
  //# sourceMappingURL=sanitizeMCPConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/sanitizeMCPConfig.ts"],"sourcesContent":["import type {\n CollectionConfig,\n Config,\n GlobalConfig,\n SanitizedCollectionConfig,\n SanitizedConfig,\n SanitizedGlobalConfig,\n} from 'payload'\n\nimport type {\n CollectionTool,\n GlobalTool,\n MCPBuiltInToolOverride,\n MCPItem,\n MCPPluginCollectionConfig,\n MCPPluginConfig,\n MCPPluginGlobalConfig,\n SanitizedMCPPluginConfig,\n} from '../types.js'\n\nimport {\n COLLECTION_AUTH_BUILTIN_ENTRIES,\n COLLECTION_AUTH_BUILTINS,\n COLLECTION_BUILTIN_ENTRIES,\n COLLECTION_BUILTINS,\n GLOBAL_BUILTIN_ENTRIES,\n GLOBAL_BUILTINS,\n} from './builtinTools.js'\n\n/**\n * Converts the user-configured `MCPPluginConfig` into a `SanitizedMCPPluginConfig`:\n * - Flattens `tools` / `prompts` / `resources` / per-collection / per-global\n * tool maps into a single `items` array.\n * - Applies built-in tools for collections and globals, respecting opt-out user overrides.\n * - Applies the `userCollection` default\n *\n * Called once during plugin init. After that, `plugins['@payloadcms/plugin-mcp']\n * ?.options` holds the sanitized result\n */\nexport const sanitizeMCPConfig = ({\n config,\n pluginConfig,\n}: {\n config: Config | SanitizedConfig\n pluginConfig: MCPPluginConfig\n}): SanitizedMCPPluginConfig => {\n const items: MCPItem[] = []\n\n for (const collection of config.collections ?? []) {\n items.push(...sanitizeCollectionConfig({ collection, pluginConfig }))\n }\n\n for (const global of config.globals ?? []) {\n items.push(...sanitizeGlobalConfig({ global, pluginConfig }))\n }\n\n for (const [key, tool] of Object.entries(pluginConfig.tools ?? {})) {\n items.push({\n type: 'tool',\n key,\n label: key,\n tool,\n })\n }\n\n for (const [key, prompt] of Object.entries(pluginConfig.prompts ?? {})) {\n items.push({\n type: 'prompt',\n key,\n label: prompt.title ?? key,\n prompt,\n })\n }\n\n for (const [key, resource] of Object.entries(pluginConfig.resources ?? {})) {\n items.push({\n type: 'resource',\n key,\n label: resource.title ?? key,\n resource,\n })\n }\n\n // Mirror Payload's own admin.user detection (sanitize.ts) since plugins run first.\n const firstCollectionWithAuth = config.collections!.find(({ auth }) => Boolean(auth))\n\n return {\n disabled: pluginConfig.disabled,\n items,\n mcp: pluginConfig.mcp,\n overrideApiKeyCollection: pluginConfig.overrideApiKeyCollection,\n overrideAuth: pluginConfig.overrideAuth,\n userCollection:\n pluginConfig.userCollection ?? config.admin?.user ?? firstCollectionWithAuth?.slug ?? 'users',\n }\n}\n\nconst sanitizeCollectionConfig = ({\n collection,\n pluginConfig,\n}: {\n collection: CollectionConfig | SanitizedCollectionConfig\n pluginConfig: MCPPluginConfig\n}): MCPItem[] => {\n if (collection.slug === 'payload-mcp-api-keys') {\n return []\n }\n const slug = collection.slug\n const collectionPluginConfig = pluginConfig.collections?.[slug]\n const items: MCPItem[] = []\n\n for (const [toolKey, tool] of COLLECTION_BUILTIN_ENTRIES) {\n const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n key: toolKey,\n label: capitalize(toolKey),\n tool: overrideBuiltinTool(tool, matchedConfigEntry, collectionPluginConfig),\n })\n }\n\n if (collection.auth) {\n for (const [authToolKey, { label, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES) {\n const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey]\n if (!matchedConfigEntry) {\n continue\n }\n // `true` means \"enable, no override\"; only the object form carries fields.\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n key: authToolKey,\n label,\n tool: overrideBuiltinTool(tool, override, collectionPluginConfig),\n })\n }\n }\n\n // Cast: builtin keys are filtered out below, so the remaining values are\n // always custom tools (`CollectionTool`) or undefined\n const customEntries = Object.entries(collectionPluginConfig?.tools ?? {}) as Array<\n [string, CollectionTool | undefined]\n >\n for (const [key, customTool] of customEntries) {\n if (key in COLLECTION_BUILTINS || key in COLLECTION_AUTH_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n key,\n label: key,\n tool: customTool,\n })\n }\n\n return items\n}\n\nconst sanitizeGlobalConfig = ({\n global,\n pluginConfig,\n}: {\n global: GlobalConfig | SanitizedGlobalConfig\n pluginConfig: MCPPluginConfig\n}): MCPItem[] => {\n const slug = global.slug\n const globalPluginConfig = pluginConfig.globals?.[slug]\n const items: MCPItem[] = []\n\n for (const [toolKey, baseTool] of GLOBAL_BUILTIN_ENTRIES) {\n const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n items.push({\n type: 'globalTool',\n globalSlug: slug,\n key: toolKey,\n label: capitalize(toolKey),\n tool: overrideBuiltinTool(baseTool, matchedConfigEntry, globalPluginConfig),\n })\n }\n\n const customEntries = Object.entries(globalPluginConfig?.tools ?? {}) as Array<\n [string, GlobalTool | undefined]\n >\n for (const [key, customTool] of customEntries) {\n if (key in GLOBAL_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'globalTool',\n globalSlug: slug,\n key,\n label: key,\n tool: customTool,\n })\n }\n\n return items\n}\n\nconst capitalize = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1)\n\n/**\n * Spread the static built-in tool and apply consumer-side overrides.\n * Precedence: per-tool override > scope-level (collection/global) override >\n * the static tool's defaults. `toolEntry` is whatever the user put under\n * `tools: { find: ... }` — could be `true`, `false`, or an override object —\n * so it's narrowed internally.\n */\nconst overrideBuiltinTool = <TTool extends CollectionTool | GlobalTool>(\n tool: TTool,\n toolOverride?: MCPBuiltInToolOverride,\n entityPluginConfig?: TTool extends CollectionTool\n ? MCPPluginCollectionConfig<any>\n : MCPPluginGlobalConfig,\n): TTool => {\n return {\n ...tool,\n description: toolOverride?.description ?? entityPluginConfig?.description ?? tool.description,\n overrideResponse:\n toolOverride?.overrideResponse ??\n entityPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n }\n}\n"],"names":["COLLECTION_AUTH_BUILTIN_ENTRIES","COLLECTION_AUTH_BUILTINS","COLLECTION_BUILTIN_ENTRIES","COLLECTION_BUILTINS","GLOBAL_BUILTIN_ENTRIES","GLOBAL_BUILTINS","sanitizeMCPConfig","config","pluginConfig","items","collection","collections","push","sanitizeCollectionConfig","global","globals","sanitizeGlobalConfig","key","tool","Object","entries","tools","type","label","prompt","prompts","title","resource","resources","firstCollectionWithAuth","find","auth","Boolean","disabled","mcp","overrideApiKeyCollection","overrideAuth","userCollection","admin","user","slug","collectionPluginConfig","toolKey","matchedConfigEntry","collectionSlug","capitalize","overrideBuiltinTool","authToolKey","override","undefined","customEntries","customTool","globalPluginConfig","baseTool","globalSlug","s","charAt","toUpperCase","slice","toolOverride","entityPluginConfig","description","overrideResponse"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/mcp/sanitizeMCPConfig.ts"],"sourcesContent":["import type {\n CollectionConfig,\n Config,\n GlobalConfig,\n SanitizedCollectionConfig,\n SanitizedConfig,\n SanitizedGlobalConfig,\n} from 'payload'\n\nimport type {\n CollectionMCPItem,\n CollectionTool,\n GlobalMCPItem,\n GlobalTool,\n MCPItem,\n MCPPluginConfig,\n SanitizedMCPPluginConfig,\n} from '../types.js'\n\nimport { defaultAccess } from '../defaultAccess.js'\nimport {\n COLLECTION_AUTH_BUILTIN_ENTRIES,\n COLLECTION_AUTH_BUILTINS,\n COLLECTION_BUILTIN_ENTRIES,\n COLLECTION_BUILTINS,\n GLOBAL_BUILTIN_ENTRIES,\n GLOBAL_BUILTINS,\n TOOL_BUILTIN_ENTRIES,\n TOOL_BUILTINS,\n} from './builtinTools.js'\n\n/**\n * Converts the user-configured `MCPPluginConfig` into a `SanitizedMCPPluginConfig`:\n * - Flattens `tools` / `prompts` / `resources` / per-collection / per-global\n * tool maps into a single `items` array.\n * - Applies built-in tools for collections and globals, respecting opt-out user overrides.\n *\n * Called once during plugin init. After that, `plugins['@payloadcms/plugin-mcp']\n * ?.options` holds the sanitized result\n */\nexport const sanitizeMCPConfig = ({\n config,\n pluginConfig,\n}: {\n config: Config | SanitizedConfig\n pluginConfig: MCPPluginConfig\n}): SanitizedMCPPluginConfig => {\n const items: MCPItem[] = []\n\n for (const collection of config.collections ?? []) {\n items.push(...sanitizeCollectionConfig({ collection, pluginConfig }))\n }\n\n for (const global of config.globals ?? []) {\n items.push(...sanitizeGlobalConfig({ global, pluginConfig }))\n }\n\n for (const [configKey, { mcpName, tool }] of TOOL_BUILTIN_ENTRIES) {\n items.push({\n type: 'tool',\n configKey,\n label: tool.annotations?.title ?? configKey,\n mcpName,\n tool: { ...tool, access: tool.access ?? defaultAccess },\n })\n }\n\n for (const [configKey, tool] of Object.entries(pluginConfig.tools ?? {})) {\n if (configKey in TOOL_BUILTINS) {\n continue\n }\n items.push({\n type: 'tool',\n configKey,\n label: tool.annotations?.title ?? configKey,\n mcpName: configKey,\n tool: { ...tool, access: tool.access ?? defaultAccess },\n })\n }\n\n for (const [configKey, prompt] of Object.entries(pluginConfig.prompts ?? {})) {\n items.push({\n type: 'prompt',\n configKey,\n label: prompt.title ?? configKey,\n mcpName: configKey,\n prompt: { ...prompt, access: prompt.access ?? defaultAccess },\n })\n }\n\n for (const [configKey, resource] of Object.entries(pluginConfig.resources ?? {})) {\n items.push({\n type: 'resource',\n configKey,\n label: resource.title ?? configKey,\n mcpName: configKey,\n resource: { ...resource, access: resource.access ?? defaultAccess },\n })\n }\n\n return {\n disabled: pluginConfig.disabled,\n hooks: pluginConfig.hooks,\n items,\n mcp: pluginConfig.mcp,\n oauth: pluginConfig.oauth === false ? undefined : (pluginConfig.oauth ?? { type: 'builtin' }),\n overrideGetAuthorizedMCP: pluginConfig.overrideGetAuthorizedMCP,\n }\n}\n\nconst sanitizeCollectionConfig = ({\n collection,\n pluginConfig,\n}: {\n collection: CollectionConfig | SanitizedCollectionConfig\n pluginConfig: MCPPluginConfig\n}): CollectionMCPItem[] => {\n const slug = collection.slug\n const collectionPluginConfig = pluginConfig.collections?.[slug]\n const items: CollectionMCPItem[] = []\n /**\n * Payload disables duplicate for auth collections by default unless the\n * collection explicitly opts back in with `disableDuplicate: false`.\n */\n const isDuplicateDisabled =\n collection.disableDuplicate === true ||\n (Boolean(collection.auth) && collection.disableDuplicate !== false)\n\n for (const [\n toolKey,\n { mcpName, requiresDuplicateEnabled, requiresUpload, requiresVersions, tool },\n ] of COLLECTION_BUILTIN_ENTRIES) {\n if (requiresVersions && !collection.versions) {\n continue\n }\n if (requiresDuplicateEnabled && isDuplicateDisabled) {\n continue\n }\n if (requiresUpload && !collection.upload) {\n continue\n }\n const matchedConfigEntry = collectionPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n const annotations = { ...tool.annotations, ...override?.annotations }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n configKey: toolKey,\n label: annotations.title ?? toolKey,\n mcpName,\n tool: {\n ...tool,\n access: override?.access ?? tool.access ?? defaultAccess,\n annotations,\n description: override?.description ?? tool.description,\n overrideResponse:\n override?.overrideResponse ??\n collectionPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n },\n })\n }\n\n if (collection.auth) {\n for (const [authToolKey, { mcpName, tool }] of COLLECTION_AUTH_BUILTIN_ENTRIES) {\n const matchedConfigEntry = collectionPluginConfig?.tools?.[authToolKey]\n if (!matchedConfigEntry) {\n continue\n }\n // `true` means \"enable, no override\"; only the object form carries fields.\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n const annotations = { ...tool.annotations, ...override?.annotations }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n configKey: authToolKey,\n label: annotations.title ?? authToolKey,\n mcpName,\n tool: {\n ...tool,\n access: override?.access ?? tool.access ?? defaultAccess,\n annotations,\n description: override?.description ?? tool.description,\n overrideResponse:\n override?.overrideResponse ??\n collectionPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n },\n })\n }\n }\n\n // Cast: builtin keys are filtered out below, so the remaining values are\n // always custom tools (`CollectionTool`) or undefined\n const customEntries = Object.entries(collectionPluginConfig?.tools ?? {}) as Array<\n [string, CollectionTool | undefined]\n >\n for (const [configKey, customTool] of customEntries) {\n if (configKey in COLLECTION_BUILTINS || configKey in COLLECTION_AUTH_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'collectionTool',\n collectionSlug: slug,\n configKey,\n label: customTool.annotations?.title ?? configKey,\n mcpName: configKey,\n tool: { ...customTool, access: customTool.access ?? defaultAccess },\n })\n }\n\n return items\n}\n\nconst sanitizeGlobalConfig = ({\n global,\n pluginConfig,\n}: {\n global: GlobalConfig | SanitizedGlobalConfig\n pluginConfig: MCPPluginConfig\n}): GlobalMCPItem[] => {\n const slug = global.slug\n const globalPluginConfig = pluginConfig.globals?.[slug]\n const items: GlobalMCPItem[] = []\n\n for (const [toolKey, { mcpName, requiresVersions, tool }] of GLOBAL_BUILTIN_ENTRIES) {\n if (requiresVersions && !global.versions) {\n continue\n }\n const matchedConfigEntry = globalPluginConfig?.tools?.[toolKey]\n if (matchedConfigEntry === false) {\n continue\n }\n const override = typeof matchedConfigEntry === 'object' ? matchedConfigEntry : undefined\n const annotations = { ...tool.annotations, ...override?.annotations }\n items.push({\n type: 'globalTool',\n configKey: toolKey,\n globalSlug: slug,\n label: annotations.title ?? toolKey,\n mcpName,\n tool: {\n ...tool,\n access: override?.access ?? tool.access ?? defaultAccess,\n annotations,\n description: override?.description ?? tool.description,\n overrideResponse:\n override?.overrideResponse ??\n globalPluginConfig?.overrideResponse ??\n tool.overrideResponse,\n },\n })\n }\n\n const customEntries = Object.entries(globalPluginConfig?.tools ?? {}) as Array<\n [string, GlobalTool | undefined]\n >\n for (const [configKey, customTool] of customEntries) {\n if (configKey in GLOBAL_BUILTINS) {\n continue\n }\n if (!customTool) {\n continue\n }\n items.push({\n type: 'globalTool',\n configKey,\n globalSlug: slug,\n label: customTool.annotations?.title ?? configKey,\n mcpName: configKey,\n tool: { ...customTool, access: customTool.access ?? defaultAccess },\n })\n }\n\n return items\n}\n"],"names":["defaultAccess","COLLECTION_AUTH_BUILTIN_ENTRIES","COLLECTION_AUTH_BUILTINS","COLLECTION_BUILTIN_ENTRIES","COLLECTION_BUILTINS","GLOBAL_BUILTIN_ENTRIES","GLOBAL_BUILTINS","TOOL_BUILTIN_ENTRIES","TOOL_BUILTINS","sanitizeMCPConfig","config","pluginConfig","items","collection","collections","push","sanitizeCollectionConfig","global","globals","sanitizeGlobalConfig","configKey","mcpName","tool","type","label","annotations","title","access","Object","entries","tools","prompt","prompts","resource","resources","disabled","hooks","mcp","oauth","undefined","overrideGetAuthorizedMCP","slug","collectionPluginConfig","isDuplicateDisabled","disableDuplicate","Boolean","auth","toolKey","requiresDuplicateEnabled","requiresUpload","requiresVersions","versions","upload","matchedConfigEntry","override","collectionSlug","description","overrideResponse","authToolKey","customEntries","customTool","globalPluginConfig","globalSlug"],"mappings":"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"}
package/dist/stdio.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Stdio adapter for the Payload MCP server.
2
+ * Starts Payload's MCP server over stdin and stdout.
3
3
  *
4
- * Do not use in production. There's no auth; whoever can spawn the process gets
5
- * full access to your local data.
4
+ * Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,
5
+ * `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.
6
6
  */
7
7
  export declare const runMcpStdio: () => Promise<void>;
8
8
  //# sourceMappingURL=stdio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CA4EhD,CAAA"}
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CA4GhD,CAAA"}
package/dist/stdio.js CHANGED
@@ -1,76 +1,115 @@
1
- /* eslint-disable no-console */ import { StdioServerTransport } from '@modelcontextprotocol/server';
1
+ /* eslint-disable no-console */ import { serveStdio } from '@modelcontextprotocol/server/stdio';
2
2
  import { fileURLToPath, pathToFileURL } from 'node:url';
3
- import { createLocalReq, getPayload } from 'payload';
4
- import { findConfig } from 'payload/node';
3
+ import { createPayloadRequest, getPayload } from 'payload';
4
+ import { findConfig, loadEnv } from 'payload/node';
5
+ import { getAuthorizedMCP } from './endpoint/access.js';
5
6
  import { buildMcpServer } from './mcp/buildMcpServer.js';
6
7
  import { sanitizeMCPConfig } from './mcp/sanitizeMCPConfig.js';
7
8
  import { getPluginConfig } from './utils/getPluginConfig.js';
8
9
  import { resolveProjectRoot } from './utils/resolveProjectRoot.js';
9
10
  /**
10
- * Stdio adapter for the Payload MCP server.
11
+ * Starts Payload's MCP server over stdin and stdout.
11
12
  *
12
- * Do not use in production. There's no auth; whoever can spawn the process gets
13
- * full access to your local data.
13
+ * Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,
14
+ * `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.
14
15
  */ export const runMcpStdio = async ()=>{
15
- /**
16
- * If MCP clients spawn stdio servers from an arbitrary working directory,
17
- * Payload's cwd-anchored `findConfig()` can't locate the project on
18
- * its own. This module always lives inside the project's `node_modules`, so
19
- * we derive the project root from its own path and `chdir` into it.
20
- *
21
- * An absolute `PAYLOAD_CONFIG_PATH` still overrides everything; unusual
22
- * layouts (e.g. a monorepo where the package is hoisted above the app) can
23
- * fall back to it. See the implementation of findConfig for details.
24
- */ const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url));
16
+ // MCP clients may start this command from another folder. Move to the Payload
17
+ // project so findConfig() can find the config.
18
+ const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url));
25
19
  if (projectRoot) {
26
20
  process.chdir(projectRoot);
27
21
  }
22
+ loadEnv();
28
23
  const configPath = findConfig();
29
24
  const configModule = await import(pathToFileURL(configPath).toString());
30
25
  const config = await (configModule.default ?? configModule);
26
+ /**
27
+ * stdout is only for MCP messages. The spec says the server must not write
28
+ * logs there and may write them to stderr instead.
29
+ *
30
+ * The 2.0 client library recovers from stray stdout logs, but that behavior is not
31
+ * guaranteed. Move Payload logs to stderr so Payload does not add invalid data
32
+ * to stdout. Keep options from configurable loggers. Replace pre-built loggers
33
+ * because their output cannot be redirected.
34
+ *
35
+ * @see https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#stdio
36
+ */ const loggerConfig = config;
37
+ if (loggerConfig.logger && typeof loggerConfig.logger === 'object' && 'options' in loggerConfig.logger) {
38
+ loggerConfig.logger.destination = process.stderr;
39
+ } else {
40
+ loggerConfig.logger = {
41
+ destination: process.stderr,
42
+ options: {}
43
+ };
44
+ }
31
45
  const payload = await getPayload({
32
- config
46
+ config,
47
+ cron: false,
48
+ disableOnInit: true
33
49
  });
34
- /**
35
- * If the user added `mcpPlugin({...})` to their `plugins` array, read the
36
- * sanitized config they registered. Otherwise fall back to the defaults. That way,
37
- * the mcp works on any project that has `@payloadcms/plugin-mcp` installed,
38
- * even if the plugin is not added to the plugins array.
39
- */ let pluginConfig;
50
+ let pluginConfig;
40
51
  try {
41
52
  pluginConfig = getPluginConfig({
42
53
  config: payload.config
43
54
  });
44
55
  } catch {
56
+ // The command also works when mcpPlugin() is not in the Payload config.
45
57
  pluginConfig = sanitizeMCPConfig({
46
58
  config: payload.config,
47
59
  pluginConfig: {}
48
60
  });
49
- const fakePluginFn = (config)=>config;
50
- fakePluginFn.slug = '@payloadcms/plugin-mcp';
51
- // @ts-expect-error
52
- fakePluginFn.sanitizedOptions = pluginConfig;
53
- (payload.config.plugins ??= []).push(fakePluginFn);
61
+ const fallbackPlugin = (config)=>config;
62
+ Object.assign(fallbackPlugin, {
63
+ slug: '@payloadcms/plugin-mcp',
64
+ sanitizedOptions: pluginConfig
65
+ });
66
+ payload.config.plugins ??= [];
67
+ payload.config.plugins.push(fallbackPlugin);
54
68
  }
55
- const authorizedMCP = {
56
- items: pluginConfig.items,
57
- overrideAccess: true,
58
- user: null
59
- };
60
- const req = await createLocalReq({}, payload);
69
+ const overrideAccessEnv = process.env.PAYLOAD_MCP_OVERRIDE_ACCESS;
70
+ let overrideAccess = false;
71
+ if (overrideAccessEnv === 'true') {
72
+ overrideAccess = true;
73
+ } else if (overrideAccessEnv && overrideAccessEnv !== 'false') {
74
+ throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS must be "true" or "false".');
75
+ }
76
+ if (overrideAccess && process.env.NODE_ENV !== 'development') {
77
+ throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS is only available in development.');
78
+ }
79
+ const headers = new Headers();
80
+ if (process.env.PAYLOAD_MCP_AUTHORIZATION) {
81
+ headers.set('Authorization', process.env.PAYLOAD_MCP_AUTHORIZATION);
82
+ }
83
+ const req = await createPayloadRequest({
84
+ payload,
85
+ req: {
86
+ headers
87
+ }
88
+ });
61
89
  req.payloadAPI = 'MCP';
62
- const server = buildMcpServer({
63
- authorizedMCP,
64
- pluginConfig,
90
+ const authorizedMCP = await getAuthorizedMCP({
91
+ overrideAccess,
65
92
  req
66
93
  });
67
- const transport = new StdioServerTransport();
68
- await server.connect(transport);
69
- // Process now lives until stdin closes. Wire graceful shutdown so DB
70
- // connections (and any other Payload-managed resources) get released.
71
- const shutdown = async (code = 0)=>{
94
+ const stdioServer = serveStdio(()=>buildMcpServer({
95
+ authorizedMCP,
96
+ pluginConfig,
97
+ req
98
+ }), {
99
+ onerror: (err)=>{
100
+ // MCP messages use stdout, so write SDK errors to stderr.
101
+ console.error('[payload-mcp] error serving MCP over stdio:', err);
102
+ }
103
+ });
104
+ // Close the MCP server and Payload when the client disconnects or the process stops.
105
+ let isShuttingDown = false;
106
+ const shutdown = async ()=>{
107
+ if (isShuttingDown) {
108
+ return;
109
+ }
110
+ isShuttingDown = true;
72
111
  try {
73
- await server.close();
112
+ await stdioServer.close();
74
113
  } catch (err) {
75
114
  console.error('[payload-mcp] error closing server:', err);
76
115
  }
@@ -79,11 +118,11 @@ import { resolveProjectRoot } from './utils/resolveProjectRoot.js';
79
118
  } catch (err) {
80
119
  console.error('[payload-mcp] error destroying payload:', err);
81
120
  }
82
- process.exit(code);
121
+ process.exit(0);
83
122
  };
84
- process.on('SIGINT', ()=>void shutdown(0));
85
- process.on('SIGTERM', ()=>void shutdown(0));
86
- process.stdin.on('close', ()=>void shutdown(0));
123
+ process.once('SIGINT', ()=>void shutdown());
124
+ process.once('SIGTERM', ()=>void shutdown());
125
+ process.stdin.once('close', ()=>void shutdown());
87
126
  };
88
127
 
89
128
  //# sourceMappingURL=stdio.js.map
package/dist/stdio.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stdio.ts"],"sourcesContent":["import type { MaybePromise, Plugin, SanitizedConfig } from 'payload'\n\n/* eslint-disable no-console */\nimport { StdioServerTransport } from '@modelcontextprotocol/server'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport { createLocalReq, getPayload } from 'payload'\nimport { findConfig } from 'payload/node'\n\nimport type { AuthorizedMCP, SanitizedMCPPluginConfig } from './types.js'\n\nimport { buildMcpServer } from './mcp/buildMcpServer.js'\nimport { sanitizeMCPConfig } from './mcp/sanitizeMCPConfig.js'\nimport { getPluginConfig } from './utils/getPluginConfig.js'\nimport { resolveProjectRoot } from './utils/resolveProjectRoot.js'\n\n/**\n * Stdio adapter for the Payload MCP server.\n *\n * Do not use in production. There's no auth; whoever can spawn the process gets\n * full access to your local data.\n */\nexport const runMcpStdio = async (): Promise<void> => {\n /**\n * If MCP clients spawn stdio servers from an arbitrary working directory,\n * Payload's cwd-anchored `findConfig()` can't locate the project on\n * its own. This module always lives inside the project's `node_modules`, so\n * we derive the project root from its own path and `chdir` into it.\n *\n * An absolute `PAYLOAD_CONFIG_PATH` still overrides everything; unusual\n * layouts (e.g. a monorepo where the package is hoisted above the app) can\n * fall back to it. See the implementation of findConfig for details.\n */\n const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url))\n if (projectRoot) {\n process.chdir(projectRoot)\n }\n\n const configPath = findConfig()\n const configModule = await import(pathToFileURL(configPath).toString())\n const config = (await (configModule.default ?? configModule)) as MaybePromise<SanitizedConfig>\n\n const payload = await getPayload({ config })\n\n /**\n * If the user added `mcpPlugin({...})` to their `plugins` array, read the\n * sanitized config they registered. Otherwise fall back to the defaults. That way,\n * the mcp works on any project that has `@payloadcms/plugin-mcp` installed,\n * even if the plugin is not added to the plugins array.\n */\n let pluginConfig: SanitizedMCPPluginConfig\n try {\n pluginConfig = getPluginConfig({ config: payload.config })\n } catch {\n pluginConfig = sanitizeMCPConfig({ config: payload.config, pluginConfig: {} })\n\n const fakePluginFn: Plugin = (config) => config\n fakePluginFn.slug = '@payloadcms/plugin-mcp'\n // @ts-expect-error\n fakePluginFn.sanitizedOptions = pluginConfig\n\n // Push to payload config, to ensure consecutive calls to `getPluginConfig()` work\n ;(payload.config.plugins ??= []).push(fakePluginFn)\n }\n\n const authorizedMCP: AuthorizedMCP = {\n items: pluginConfig.items,\n overrideAccess: true,\n user: null,\n }\n\n const req = await createLocalReq({}, payload)\n req.payloadAPI = 'MCP' as const\n\n const server = buildMcpServer({ authorizedMCP, pluginConfig, req })\n\n const transport = new StdioServerTransport()\n await server.connect(transport)\n\n // Process now lives until stdin closes. Wire graceful shutdown so DB\n // connections (and any other Payload-managed resources) get released.\n const shutdown = async (code = 0) => {\n try {\n await server.close()\n } catch (err) {\n console.error('[payload-mcp] error closing server:', err)\n }\n try {\n await payload.destroy()\n } catch (err) {\n console.error('[payload-mcp] error destroying payload:', err)\n }\n process.exit(code)\n }\n\n process.on('SIGINT', () => void shutdown(0))\n process.on('SIGTERM', () => void shutdown(0))\n process.stdin.on('close', () => void shutdown(0))\n}\n"],"names":["StdioServerTransport","fileURLToPath","pathToFileURL","createLocalReq","getPayload","findConfig","buildMcpServer","sanitizeMCPConfig","getPluginConfig","resolveProjectRoot","runMcpStdio","projectRoot","url","process","chdir","configPath","configModule","toString","config","default","payload","pluginConfig","fakePluginFn","slug","sanitizedOptions","plugins","push","authorizedMCP","items","overrideAccess","user","req","payloadAPI","server","transport","connect","shutdown","code","close","err","console","error","destroy","exit","on","stdin"],"mappings":"AAEA,6BAA6B,GAC7B,SAASA,oBAAoB,QAAQ,+BAA8B;AACnE,SAASC,aAAa,EAAEC,aAAa,QAAQ,WAAU;AACvD,SAASC,cAAc,EAAEC,UAAU,QAAQ,UAAS;AACpD,SAASC,UAAU,QAAQ,eAAc;AAIzC,SAASC,cAAc,QAAQ,0BAAyB;AACxD,SAASC,iBAAiB,QAAQ,6BAA4B;AAC9D,SAASC,eAAe,QAAQ,6BAA4B;AAC5D,SAASC,kBAAkB,QAAQ,gCAA+B;AAElE;;;;;CAKC,GACD,OAAO,MAAMC,cAAc;IACzB;;;;;;;;;GASC,GACD,MAAMC,cAAcF,mBAAmBR,cAAc,YAAYW,GAAG;IACpE,IAAID,aAAa;QACfE,QAAQC,KAAK,CAACH;IAChB;IAEA,MAAMI,aAAaV;IACnB,MAAMW,eAAe,MAAM,MAAM,CAACd,cAAca,YAAYE,QAAQ;IACpE,MAAMC,SAAU,MAAOF,CAAAA,aAAaG,OAAO,IAAIH,YAAW;IAE1D,MAAMI,UAAU,MAAMhB,WAAW;QAAEc;IAAO;IAE1C;;;;;GAKC,GACD,IAAIG;IACJ,IAAI;QACFA,eAAeb,gBAAgB;YAAEU,QAAQE,QAAQF,MAAM;QAAC;IAC1D,EAAE,OAAM;QACNG,eAAed,kBAAkB;YAAEW,QAAQE,QAAQF,MAAM;YAAEG,cAAc,CAAC;QAAE;QAE5E,MAAMC,eAAuB,CAACJ,SAAWA;QACzCI,aAAaC,IAAI,GAAG;QACpB,mBAAmB;QACnBD,aAAaE,gBAAgB,GAAGH;QAG9BD,CAAAA,QAAQF,MAAM,CAACO,OAAO,KAAK,EAAE,AAAD,EAAGC,IAAI,CAACJ;IACxC;IAEA,MAAMK,gBAA+B;QACnCC,OAAOP,aAAaO,KAAK;QACzBC,gBAAgB;QAChBC,MAAM;IACR;IAEA,MAAMC,MAAM,MAAM5B,eAAe,CAAC,GAAGiB;IACrCW,IAAIC,UAAU,GAAG;IAEjB,MAAMC,SAAS3B,eAAe;QAAEqB;QAAeN;QAAcU;IAAI;IAEjE,MAAMG,YAAY,IAAIlC;IACtB,MAAMiC,OAAOE,OAAO,CAACD;IAErB,qEAAqE;IACrE,sEAAsE;IACtE,MAAME,WAAW,OAAOC,OAAO,CAAC;QAC9B,IAAI;YACF,MAAMJ,OAAOK,KAAK;QACpB,EAAE,OAAOC,KAAK;YACZC,QAAQC,KAAK,CAAC,uCAAuCF;QACvD;QACA,IAAI;YACF,MAAMnB,QAAQsB,OAAO;QACvB,EAAE,OAAOH,KAAK;YACZC,QAAQC,KAAK,CAAC,2CAA2CF;QAC3D;QACA1B,QAAQ8B,IAAI,CAACN;IACf;IAEAxB,QAAQ+B,EAAE,CAAC,UAAU,IAAM,KAAKR,SAAS;IACzCvB,QAAQ+B,EAAE,CAAC,WAAW,IAAM,KAAKR,SAAS;IAC1CvB,QAAQgC,KAAK,CAACD,EAAE,CAAC,SAAS,IAAM,KAAKR,SAAS;AAChD,EAAC"}
1
+ {"version":3,"sources":["../src/stdio.ts"],"sourcesContent":["import type { Config, Plugin, SanitizedConfig } from 'payload'\n\n/* eslint-disable no-console */\nimport { serveStdio } from '@modelcontextprotocol/server/stdio'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport { createPayloadRequest, getPayload } from 'payload'\nimport { findConfig, loadEnv } from 'payload/node'\n\nimport type { SanitizedMCPPluginConfig } from './types.js'\n\nimport { getAuthorizedMCP } from './endpoint/access.js'\nimport { buildMcpServer } from './mcp/buildMcpServer.js'\nimport { sanitizeMCPConfig } from './mcp/sanitizeMCPConfig.js'\nimport { getPluginConfig } from './utils/getPluginConfig.js'\nimport { resolveProjectRoot } from './utils/resolveProjectRoot.js'\n\n/**\n * Starts Payload's MCP server over stdin and stdout.\n *\n * Set `PAYLOAD_MCP_AUTHORIZATION` to authenticate. In development,\n * `PAYLOAD_MCP_OVERRIDE_ACCESS=true` skips access checks.\n */\nexport const runMcpStdio = async (): Promise<void> => {\n // MCP clients may start this command from another folder. Move to the Payload\n // project so findConfig() can find the config.\n const projectRoot = resolveProjectRoot(fileURLToPath(import.meta.url))\n if (projectRoot) {\n process.chdir(projectRoot)\n }\n\n loadEnv()\n const configPath = findConfig()\n const configModule = await import(pathToFileURL(configPath).toString())\n const config = (await (configModule.default ?? configModule)) as SanitizedConfig\n\n /**\n * stdout is only for MCP messages. The spec says the server must not write\n * logs there and may write them to stderr instead.\n *\n * The 2.0 client library recovers from stray stdout logs, but that behavior is not\n * guaranteed. Move Payload logs to stderr so Payload does not add invalid data\n * to stdout. Keep options from configurable loggers. Replace pre-built loggers\n * because their output cannot be redirected.\n *\n * @see https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#stdio\n */\n const loggerConfig: { logger?: Config['logger'] } = config\n if (\n loggerConfig.logger &&\n typeof loggerConfig.logger === 'object' &&\n 'options' in loggerConfig.logger\n ) {\n loggerConfig.logger.destination = process.stderr\n } else {\n loggerConfig.logger = { destination: process.stderr, options: {} }\n }\n\n const payload = await getPayload({ config, cron: false, disableOnInit: true })\n\n let pluginConfig: SanitizedMCPPluginConfig\n try {\n pluginConfig = getPluginConfig({ config: payload.config })\n } catch {\n // The command also works when mcpPlugin() is not in the Payload config.\n pluginConfig = sanitizeMCPConfig({ config: payload.config, pluginConfig: {} })\n\n const fallbackPlugin: Plugin = (config) => config\n Object.assign(fallbackPlugin, {\n slug: '@payloadcms/plugin-mcp',\n sanitizedOptions: pluginConfig,\n })\n payload.config.plugins ??= []\n payload.config.plugins.push(fallbackPlugin)\n }\n\n const overrideAccessEnv = process.env.PAYLOAD_MCP_OVERRIDE_ACCESS\n let overrideAccess = false\n\n if (overrideAccessEnv === 'true') {\n overrideAccess = true\n } else if (overrideAccessEnv && overrideAccessEnv !== 'false') {\n throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS must be \"true\" or \"false\".')\n }\n\n if (overrideAccess && process.env.NODE_ENV !== 'development') {\n throw new Error('PAYLOAD_MCP_OVERRIDE_ACCESS is only available in development.')\n }\n\n const headers = new Headers()\n if (process.env.PAYLOAD_MCP_AUTHORIZATION) {\n headers.set('Authorization', process.env.PAYLOAD_MCP_AUTHORIZATION)\n }\n\n const req = await createPayloadRequest({ payload, req: { headers } })\n req.payloadAPI = 'MCP' as const\n const authorizedMCP = await getAuthorizedMCP({ overrideAccess, req })\n\n const stdioServer = serveStdio(() => buildMcpServer({ authorizedMCP, pluginConfig, req }), {\n onerror: (err) => {\n // MCP messages use stdout, so write SDK errors to stderr.\n console.error('[payload-mcp] error serving MCP over stdio:', err)\n },\n })\n\n // Close the MCP server and Payload when the client disconnects or the process stops.\n let isShuttingDown = false\n const shutdown = async () => {\n if (isShuttingDown) {\n return\n }\n isShuttingDown = true\n\n try {\n await stdioServer.close()\n } catch (err) {\n console.error('[payload-mcp] error closing server:', err)\n }\n\n try {\n await payload.destroy()\n } catch (err) {\n console.error('[payload-mcp] error destroying payload:', err)\n }\n\n process.exit(0)\n }\n\n process.once('SIGINT', () => void shutdown())\n process.once('SIGTERM', () => void shutdown())\n process.stdin.once('close', () => void shutdown())\n}\n"],"names":["serveStdio","fileURLToPath","pathToFileURL","createPayloadRequest","getPayload","findConfig","loadEnv","getAuthorizedMCP","buildMcpServer","sanitizeMCPConfig","getPluginConfig","resolveProjectRoot","runMcpStdio","projectRoot","url","process","chdir","configPath","configModule","toString","config","default","loggerConfig","logger","destination","stderr","options","payload","cron","disableOnInit","pluginConfig","fallbackPlugin","Object","assign","slug","sanitizedOptions","plugins","push","overrideAccessEnv","env","PAYLOAD_MCP_OVERRIDE_ACCESS","overrideAccess","Error","NODE_ENV","headers","Headers","PAYLOAD_MCP_AUTHORIZATION","set","req","payloadAPI","authorizedMCP","stdioServer","onerror","err","console","error","isShuttingDown","shutdown","close","destroy","exit","once","stdin"],"mappings":"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"}