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

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 (240) hide show
  1. package/LICENSE.md +1 -1
  2. package/bin.js +7 -0
  3. package/dist/defaultAccess.d.ts +3 -0
  4. package/dist/defaultAccess.d.ts.map +1 -0
  5. package/dist/defaultAccess.js +3 -0
  6. package/dist/defaultAccess.js.map +1 -0
  7. package/dist/defineTool.d.ts +8 -8
  8. package/dist/defineTool.d.ts.map +1 -1
  9. package/dist/endpoint/access.d.ts +12 -5
  10. package/dist/endpoint/access.d.ts.map +1 -1
  11. package/dist/endpoint/access.js +75 -88
  12. package/dist/endpoint/access.js.map +1 -1
  13. package/dist/endpoint/index.d.ts +1 -1
  14. package/dist/endpoint/index.d.ts.map +1 -1
  15. package/dist/endpoint/index.js +68 -17
  16. package/dist/endpoint/index.js.map +1 -1
  17. package/dist/exports/client.d.ts +1 -1
  18. package/dist/exports/client.d.ts.map +1 -1
  19. package/dist/exports/client.js +1 -1
  20. package/dist/exports/client.js.map +1 -1
  21. package/dist/exports/internal.d.ts +2 -0
  22. package/dist/exports/internal.d.ts.map +1 -0
  23. package/dist/exports/internal.js +3 -0
  24. package/dist/exports/internal.js.map +1 -0
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +15 -20
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp/buildMcpServer.d.ts +5 -5
  30. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  31. package/dist/mcp/buildMcpServer.js +145 -80
  32. package/dist/mcp/buildMcpServer.js.map +1 -1
  33. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  34. package/dist/mcp/builtin/collections/authTools.js +122 -61
  35. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  36. package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
  37. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
  38. package/dist/mcp/builtin/collections/countTool.js +63 -0
  39. package/dist/mcp/builtin/collections/countTool.js.map +1 -0
  40. package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
  41. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
  42. package/dist/mcp/builtin/collections/countVersionsTool.js +60 -0
  43. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
  44. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  45. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  46. package/dist/mcp/builtin/collections/createTool.js +111 -68
  47. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  48. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  49. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  50. package/dist/mcp/builtin/collections/deleteTool.js +51 -61
  51. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  52. package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
  53. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
  54. package/dist/mcp/builtin/collections/duplicateTool.js +86 -0
  55. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
  56. package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
  57. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
  58. package/dist/mcp/builtin/collections/fileInput.js +123 -0
  59. package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
  60. package/dist/mcp/builtin/collections/fileInput.spec.js +86 -0
  61. package/dist/mcp/builtin/collections/fileInput.spec.js.map +1 -0
  62. package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
  63. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
  64. package/dist/mcp/builtin/collections/findDistinctTool.js +77 -0
  65. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
  66. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  67. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  68. package/dist/mcp/builtin/collections/findTool.js +55 -72
  69. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  70. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
  71. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
  72. package/dist/mcp/builtin/collections/findVersionByIDTool.js +74 -0
  73. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
  74. package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
  75. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
  76. package/dist/mcp/builtin/collections/findVersionsTool.js +84 -0
  77. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
  78. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  79. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  80. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +87 -0
  81. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  82. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
  83. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
  84. package/dist/mcp/builtin/collections/restoreVersionTool.js +69 -0
  85. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -0
  86. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  87. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  88. package/dist/mcp/builtin/collections/updateTool.js +104 -136
  89. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  90. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  91. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  92. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  93. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  94. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  95. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  96. package/dist/mcp/builtin/formatEntityError.js +55 -0
  97. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  98. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  99. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  100. package/dist/mcp/builtin/getConfigInfoTool.js +69 -0
  101. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  102. package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
  103. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
  104. package/dist/mcp/builtin/globals/countVersionsTool.js +60 -0
  105. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
  106. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  107. package/dist/mcp/builtin/globals/findTool.js +27 -36
  108. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  109. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
  110. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
  111. package/dist/mcp/builtin/globals/findVersionByIDTool.js +68 -0
  112. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
  113. package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
  114. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
  115. package/dist/mcp/builtin/globals/findVersionsTool.js +78 -0
  116. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
  117. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  118. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  119. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +65 -0
  120. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  121. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
  122. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
  123. package/dist/mcp/builtin/globals/restoreVersionTool.js +70 -0
  124. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
  125. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  126. package/dist/mcp/builtin/globals/updateTool.js +44 -62
  127. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  128. package/dist/mcp/builtinTools.d.ts +150 -19
  129. package/dist/mcp/builtinTools.d.ts.map +1 -1
  130. package/dist/mcp/builtinTools.js +125 -20
  131. package/dist/mcp/builtinTools.js.map +1 -1
  132. package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
  133. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  134. package/dist/mcp/sanitizeMCPConfig.js +124 -58
  135. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  136. package/dist/stdio.d.ts +3 -3
  137. package/dist/stdio.d.ts.map +1 -1
  138. package/dist/stdio.js +84 -46
  139. package/dist/stdio.js.map +1 -1
  140. package/dist/types.d.ts +114 -112
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/types.js +2 -4
  143. package/dist/types.js.map +1 -1
  144. package/dist/utils/camelCase.d.ts.map +1 -1
  145. package/dist/utils/getPluginConfig.d.ts +1 -1
  146. package/dist/utils/getPluginConfig.js +1 -1
  147. package/dist/utils/getPluginConfig.js.map +1 -1
  148. package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
  149. package/dist/utils/toStandardSchema.d.ts.map +1 -1
  150. package/package.json +11 -18
  151. package/src/defaultAccess.ts +3 -0
  152. package/src/endpoint/access.ts +80 -101
  153. package/src/endpoint/index.ts +58 -16
  154. package/src/exports/client.ts +2 -1
  155. package/src/exports/internal.ts +1 -0
  156. package/src/index.ts +12 -20
  157. package/src/mcp/buildMcpServer.ts +179 -114
  158. package/src/mcp/builtin/collections/authTools.ts +119 -75
  159. package/src/mcp/builtin/collections/countTool.ts +61 -0
  160. package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
  161. package/src/mcp/builtin/collections/createTool.ts +104 -88
  162. package/src/mcp/builtin/collections/deleteTool.ts +48 -74
  163. package/src/mcp/builtin/collections/duplicateTool.ts +83 -0
  164. package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
  165. package/src/mcp/builtin/collections/fileInput.ts +169 -0
  166. package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
  167. package/src/mcp/builtin/collections/findTool.ts +55 -108
  168. package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
  169. package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
  170. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +80 -0
  171. package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
  172. package/src/mcp/builtin/collections/updateTool.ts +101 -142
  173. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  174. package/src/mcp/builtin/formatEntityError.ts +77 -0
  175. package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
  176. package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
  177. package/src/mcp/builtin/globals/findTool.ts +27 -57
  178. package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
  179. package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
  180. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +63 -0
  181. package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
  182. package/src/mcp/builtin/globals/updateTool.ts +51 -79
  183. package/src/mcp/builtinTools.ts +118 -41
  184. package/src/mcp/sanitizeMCPConfig.ts +120 -78
  185. package/src/stdio.ts +79 -46
  186. package/src/types.ts +130 -117
  187. package/src/utils/getPluginConfig.ts +1 -1
  188. package/dist/@types/assets.d.js +0 -2
  189. package/dist/@types/assets.d.js.map +0 -1
  190. package/dist/collection/getAccessField.d.ts +0 -12
  191. package/dist/collection/getAccessField.d.ts.map +0 -1
  192. package/dist/collection/getAccessField.js +0 -57
  193. package/dist/collection/getAccessField.js.map +0 -1
  194. package/dist/collection/index.d.ts +0 -6
  195. package/dist/collection/index.d.ts.map +0 -1
  196. package/dist/collection/index.js +0 -60
  197. package/dist/collection/index.js.map +0 -1
  198. package/dist/components/AccessField/index.client.d.ts +0 -10
  199. package/dist/components/AccessField/index.client.d.ts.map +0 -1
  200. package/dist/components/AccessField/index.client.js +0 -305
  201. package/dist/components/AccessField/index.client.js.map +0 -1
  202. package/dist/components/AccessField/index.css +0 -93
  203. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  204. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  205. package/dist/utils/getVirtualFieldNames.js +0 -35
  206. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  207. package/dist/utils/localAPIDefaults.d.ts +0 -20
  208. package/dist/utils/localAPIDefaults.d.ts.map +0 -1
  209. package/dist/utils/localAPIDefaults.js +0 -19
  210. package/dist/utils/localAPIDefaults.js.map +0 -1
  211. package/dist/utils/schemaConversion/buildToolInput.d.ts +0 -29
  212. package/dist/utils/schemaConversion/buildToolInput.d.ts.map +0 -1
  213. package/dist/utils/schemaConversion/buildToolInput.js +0 -51
  214. package/dist/utils/schemaConversion/buildToolInput.js.map +0 -1
  215. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
  216. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
  217. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
  218. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
  219. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -15
  220. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  221. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -464
  222. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  223. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -158
  224. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  225. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  226. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  227. package/dist/utils/transformPointDataToPayload.js +0 -28
  228. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  229. package/src/@types/assets.d.ts +0 -3
  230. package/src/collection/getAccessField.ts +0 -64
  231. package/src/collection/index.ts +0 -64
  232. package/src/components/AccessField/index.client.tsx +0 -344
  233. package/src/components/AccessField/index.css +0 -93
  234. package/src/utils/getVirtualFieldNames.ts +0 -53
  235. package/src/utils/localAPIDefaults.ts +0 -22
  236. package/src/utils/schemaConversion/buildToolInput.ts +0 -68
  237. package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
  238. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -103
  239. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -529
  240. package/src/utils/transformPointDataToPayload.ts +0 -40
@@ -1,34 +1,29 @@
1
+ import type { PayloadRequest } from 'payload'
2
+
1
3
  import { McpServer, type ServerContext } from '@modelcontextprotocol/server'
2
- import { APIError, entityToStandaloneJSONSchema, type PayloadRequest } from 'payload'
4
+ import { APIError, strictObject, z } from 'payload'
3
5
 
4
6
  import type {
5
7
  AuthorizedMCP,
8
+ CollectionMCPItem,
9
+ GlobalMCPItem,
6
10
  JsonSchemaType,
7
11
  MCPResponseOverride,
8
12
  MCPToolResponse,
9
13
  SanitizedMCPPluginConfig,
14
+ ToolInputSchema,
10
15
  } from '../types.js'
11
16
 
12
- import { toCamelCase } from '../utils/camelCase.js'
13
17
  import { getLogger } from '../utils/getLogger.js'
14
- import {
15
- getCollectionVirtualFieldNames,
16
- getGlobalVirtualFieldNames,
17
- } from '../utils/getVirtualFieldNames.js'
18
- import { removeVirtualFieldsFromSchema } from '../utils/schemaConversion/removeVirtualFieldsFromSchema.js'
19
18
  import { toStandardSchema } from '../utils/toStandardSchema.js'
20
19
 
21
- /** `findPosts`, `updateSiteSettings` — auto-prefixed wire name for collection/global tools. */
22
- const wireName = (key: string, slug: string): string => {
23
- const camel = toCamelCase(slug)
24
- return `${key}${camel.charAt(0).toUpperCase()}${camel.slice(1)}`
25
- }
20
+ z.config(z.locales.en())
26
21
 
27
22
  /**
28
- * Transport-agnostic core: registers every authorized MCP item onto a fresh
29
- * `McpServer` and returns it. The caller is responsible for picking a transport
30
- * (`WebStandardStreamableHTTPServerTransport`, `StdioServerTransport`, …) and
31
- * calling `server.connect(transport)`.
23
+ * Serving-entry-agnostic core: registers every authorized MCP item onto a fresh
24
+ * `McpServer` and returns it. The HTTP and stdio entry point callers provide fresh
25
+ * instances from this builder while they own the transport and protocol-era
26
+ * decision.
32
27
  *
33
28
  * `req` is the request context handlers see. For HTTP it's the live
34
29
  * `PayloadRequest` derived from the incoming HTTP request; for stdio it's a
@@ -55,110 +50,126 @@ export const buildMcpServer = ({
55
50
  * Wrap a tool handler's response with the tool's `overrideResponse`, then
56
51
  * strip the internal `doc` field so it doesn't leak onto the wire.
57
52
  */
58
- const finalizeToolResponse = (
59
- response: MCPToolResponse,
60
- overrideResponse?: MCPResponseOverride,
61
- ): MCPToolResponse => {
62
- const overridden = overrideResponse?.(response, response.doc ?? {}, req) ?? response
53
+ const finalizeToolResponse = async ({
54
+ input,
55
+ overrideResponse,
56
+ response,
57
+ toolName,
58
+ }: {
59
+ input: unknown
60
+ overrideResponse?: MCPResponseOverride
61
+ response: MCPToolResponse
62
+ toolName: string
63
+ }): Promise<MCPToolResponse> => {
64
+ let overridden = overrideResponse?.(response, response.doc ?? {}, req) ?? response
65
+ for (const hook of pluginConfig.hooks?.afterToolCall ?? []) {
66
+ overridden = await hook({ input, req, response: overridden, toolName })
67
+ }
63
68
  const { doc: _doc, ...rest } = overridden
64
69
  return rest
65
70
  }
66
71
 
72
+ /**
73
+ * Runs a collection/global tool call:
74
+ * - reads `slug` from the input
75
+ * - runs access control: errors if `authorizedMCP.items` has no entry for this tool + slug
76
+ * - runs the tool handler and finalizes its response
77
+ */
78
+ const callEntityTool = async ({
79
+ input,
80
+ item,
81
+ serverContext,
82
+ }: {
83
+ input: unknown
84
+ item: CollectionMCPItem | GlobalMCPItem
85
+ serverContext: ServerContext
86
+ }): Promise<MCPToolResponse> => {
87
+ const entity = item.type === 'collectionTool' ? 'collection' : 'global'
88
+ const toolInput = (input ?? {}) as Record<string, unknown>
89
+ const slug = toolInput.slug as string | undefined
90
+
91
+ if (!slug) {
92
+ return {
93
+ content: [
94
+ {
95
+ type: 'text',
96
+ text: `Error: "${item.mcpName}" requires slug. Use getConfigInfo to inspect ${entity} slugs.`,
97
+ },
98
+ ],
99
+ isError: true,
100
+ }
101
+ }
102
+
103
+ const match = authorizedMCP.items.find(
104
+ (candidate): candidate is CollectionMCPItem | GlobalMCPItem =>
105
+ candidate.type === item.type &&
106
+ candidate.mcpName === item.mcpName &&
107
+ (candidate.type === 'collectionTool'
108
+ ? candidate.collectionSlug === slug
109
+ : candidate.type === 'globalTool' && candidate.globalSlug === slug),
110
+ )
111
+
112
+ if (!match) {
113
+ return {
114
+ content: [
115
+ {
116
+ type: 'text',
117
+ text: `Error: MCP access to "${item.mcpName}" is not enabled for ${entity} "${slug}"`,
118
+ },
119
+ ],
120
+ isError: true,
121
+ }
122
+ }
123
+
124
+ const handlerArgs = {
125
+ authorizedMCP,
126
+ input: toolInput,
127
+ req,
128
+ serverContext,
129
+ }
130
+ const response = await (match.type === 'collectionTool'
131
+ ? match.tool.handler({ ...handlerArgs, slug })
132
+ : match.tool.handler({ ...handlerArgs, slug }))
133
+
134
+ return finalizeToolResponse({
135
+ input: toolInput,
136
+ overrideResponse: match.tool.overrideResponse,
137
+ response,
138
+ toolName: match.mcpName,
139
+ })
140
+ }
141
+
67
142
  try {
143
+ const registeredEntityTools = new Set<string>()
144
+
68
145
  for (const item of authorizedMCP.items) {
69
146
  switch (item.type) {
70
- case 'collectionTool': {
71
- const tool = item.tool
72
- const name = wireName(item.key, item.collectionSlug)
73
- let inputSchema = tool.input
74
- if (typeof inputSchema === 'function') {
75
- const collection = req.payload.collections[item.collectionSlug]?.config
76
- if (!collection) {
77
- throw new APIError(
78
- `Collection schema not found for slug: ${item.collectionSlug}`,
79
- 500,
80
- )
81
- }
82
- const collectionSchema = removeVirtualFieldsFromSchema(
83
- entityToStandaloneJSONSchema({
84
- config: req.payload.config,
85
- defaultIDType: req.payload.db.defaultIDType,
86
- entity: collection,
87
- i18n: req.i18n,
88
- }) as unknown as JsonSchemaType,
89
- getCollectionVirtualFieldNames(req.payload.config, item.collectionSlug),
90
- )
91
- inputSchema = inputSchema({ collectionSchema })
92
- }
93
- server.registerTool(
94
- name,
95
- {
96
- description: tool.description,
97
- inputSchema: inputSchema ? toStandardSchema(inputSchema) : undefined,
98
- },
99
- async (input: unknown, ctx: ServerContext) =>
100
- finalizeToolResponse(
101
- await tool.handler({
102
- authorizedMCP,
103
- collectionSlug: item.collectionSlug,
104
- input: (input ?? {}) as Record<string, unknown>,
105
- req,
106
- serverContext: ctx,
107
- }),
108
- tool.overrideResponse,
109
- ),
110
- )
111
- logger.info(`✅ Tool: ${name} Registered.`)
112
- break
113
- }
147
+ case 'collectionTool':
114
148
  case 'globalTool': {
115
- const tool = item.tool
116
- const name = wireName(item.key, item.globalSlug)
117
- let inputSchema = tool.input
118
- if (typeof inputSchema === 'function') {
119
- const globalEntity = req.payload.config.globals.find(
120
- (globalConfig) => globalConfig.slug === item.globalSlug,
121
- )
122
- if (!globalEntity) {
123
- throw new APIError(`Global schema not found for slug: ${item.globalSlug}`, 500)
124
- }
125
- const globalSchema = removeVirtualFieldsFromSchema(
126
- entityToStandaloneJSONSchema({
127
- config: req.payload.config,
128
- defaultIDType: req.payload.db.defaultIDType,
129
- entity: globalEntity,
130
- i18n: req.i18n,
131
- }) as unknown as JsonSchemaType,
132
- getGlobalVirtualFieldNames(req.payload.config, item.globalSlug),
133
- )
134
-
135
- inputSchema = inputSchema({ globalSchema })
149
+ if (registeredEntityTools.has(item.mcpName)) {
150
+ break
136
151
  }
152
+ registeredEntityTools.add(item.mcpName)
153
+
154
+ const inputSchema = withSlugInput({ input: item.tool.input })
155
+
137
156
  server.registerTool(
138
- name,
157
+ item.mcpName,
139
158
  {
140
- description: tool.description,
141
- inputSchema: inputSchema ? toStandardSchema(inputSchema) : undefined,
159
+ annotations: item.tool.annotations,
160
+ description: item.tool.description,
161
+ inputSchema: toStandardSchema(inputSchema),
142
162
  },
143
163
  async (input: unknown, ctx: ServerContext) =>
144
- finalizeToolResponse(
145
- await tool.handler({
146
- authorizedMCP,
147
- globalSlug: item.globalSlug,
148
- input: (input ?? {}) as Record<string, unknown>,
149
- req,
150
- serverContext: ctx,
151
- }),
152
- tool.overrideResponse,
153
- ),
164
+ callEntityTool({ input, item, serverContext: ctx }),
154
165
  )
155
- logger.info(`✅ Tool: ${name} Registered.`)
166
+ logger.info(`✅ Tool: ${item.mcpName} Registered.`)
156
167
  break
157
168
  }
158
169
  case 'prompt': {
159
170
  const prompt = item.prompt
160
171
  server.registerPrompt(
161
- item.key,
172
+ item.mcpName,
162
173
  {
163
174
  argsSchema: prompt.argsSchema ? toStandardSchema(prompt.argsSchema) : undefined,
164
175
  description: prompt.description,
@@ -177,7 +188,7 @@ export const buildMcpServer = ({
177
188
  case 'resource': {
178
189
  const resource = item.resource
179
190
  server.registerResource(
180
- item.key,
191
+ item.mcpName,
181
192
  // @ts-expect-error - Overload type ambiguity (string OR ResourceTemplate is valid)
182
193
  resource.uri,
183
194
  {
@@ -200,23 +211,29 @@ export const buildMcpServer = ({
200
211
  case 'tool': {
201
212
  const tool = item.tool
202
213
  server.registerTool(
203
- item.key,
214
+ item.mcpName,
204
215
  {
216
+ annotations: tool.annotations,
205
217
  description: tool.description,
206
218
  inputSchema: tool.input ? toStandardSchema(tool.input) : undefined,
207
219
  },
208
- async (input: unknown, ctx: ServerContext) =>
209
- finalizeToolResponse(
210
- await tool.handler({
211
- authorizedMCP,
212
- input: (input ?? {}) as Record<string, unknown>,
213
- req,
214
- serverContext: ctx,
215
- }),
216
- tool.overrideResponse,
217
- ),
220
+ async (input: unknown, ctx: ServerContext) => {
221
+ const toolInput = (input ?? {}) as Record<string, unknown>
222
+ const response = await tool.handler({
223
+ authorizedMCP,
224
+ input: toolInput,
225
+ req,
226
+ serverContext: ctx,
227
+ })
228
+ return finalizeToolResponse({
229
+ input: toolInput,
230
+ overrideResponse: tool.overrideResponse,
231
+ response,
232
+ toolName: item.mcpName,
233
+ })
234
+ },
218
235
  )
219
- logger.info(`✅ Tool: ${item.key} Registered.`)
236
+ logger.info(`✅ Tool: ${item.mcpName} Registered.`)
220
237
  break
221
238
  }
222
239
  }
@@ -227,3 +244,51 @@ export const buildMcpServer = ({
227
244
 
228
245
  return server
229
246
  }
247
+
248
+ const withSlugInput = ({ input }: { input?: ToolInputSchema }): ToolInputSchema => {
249
+ const description = 'The target slug.'
250
+ const slugSchema = z.string().check(z.describe(description))
251
+
252
+ if (!input) {
253
+ return strictObject({ slug: slugSchema }) as unknown as ToolInputSchema
254
+ }
255
+
256
+ if (typeof input === 'object' && input !== null && '~standard' in input) {
257
+ const schema = input['~standard'].jsonSchema.input({ target: 'draft-2020-12' })
258
+ const properties = schema.properties as Record<string, unknown> | undefined
259
+
260
+ if (properties?.slug) {
261
+ return input
262
+ }
263
+
264
+ return addSlugToJSONSchema({ description, schema: schema as JsonSchemaType })
265
+ }
266
+
267
+ return addSlugToJSONSchema({ description, schema: input })
268
+ }
269
+
270
+ const addSlugToJSONSchema = ({
271
+ description,
272
+ schema,
273
+ }: {
274
+ description: string
275
+ schema: JsonSchemaType
276
+ }): JsonSchemaType => {
277
+ const objectSchema = schema as {
278
+ properties?: Record<string, JsonSchemaType>
279
+ required?: string[]
280
+ } & JsonSchemaType
281
+
282
+ return {
283
+ ...objectSchema,
284
+ type: 'object',
285
+ properties: {
286
+ ...objectSchema.properties,
287
+ slug: {
288
+ type: 'string',
289
+ description,
290
+ },
291
+ },
292
+ required: Array.from(new Set(['slug', ...(objectSchema.required ?? [])])),
293
+ }
294
+ }