@payloadcms/plugin-mcp 4.0.0-internal.c2b57ce → 4.0.0-internal.c6494b5

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 (241) 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 +75 -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 +98 -18
  15. package/dist/endpoint/index.js.map +1 -1
  16. package/dist/exports/client.d.ts +1 -1
  17. package/dist/exports/client.d.ts.map +1 -1
  18. package/dist/exports/client.js +1 -1
  19. package/dist/exports/client.js.map +1 -1
  20. package/dist/exports/internal.d.ts +2 -0
  21. package/dist/exports/internal.d.ts.map +1 -0
  22. package/dist/exports/internal.js +3 -0
  23. package/dist/exports/internal.js.map +1 -0
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +15 -20
  27. package/dist/index.js.map +1 -1
  28. package/dist/mcp/buildMcpServer.d.ts +6 -6
  29. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  30. package/dist/mcp/buildMcpServer.js +146 -74
  31. package/dist/mcp/buildMcpServer.js.map +1 -1
  32. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  33. package/dist/mcp/builtin/collections/authTools.js +123 -61
  34. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  35. package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
  36. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
  37. package/dist/mcp/builtin/collections/countTool.js +63 -0
  38. package/dist/mcp/builtin/collections/countTool.js.map +1 -0
  39. package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
  40. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
  41. package/dist/mcp/builtin/collections/countVersionsTool.js +60 -0
  42. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
  43. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  44. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  45. package/dist/mcp/builtin/collections/createTool.js +114 -64
  46. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  47. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  48. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  49. package/dist/mcp/builtin/collections/deleteTool.js +55 -61
  50. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  51. package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
  52. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
  53. package/dist/mcp/builtin/collections/duplicateTool.js +90 -0
  54. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
  55. package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
  56. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
  57. package/dist/mcp/builtin/collections/fileInput.js +88 -0
  58. package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
  59. package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
  60. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
  61. package/dist/mcp/builtin/collections/findDistinctTool.js +77 -0
  62. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
  63. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  64. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  65. package/dist/mcp/builtin/collections/findTool.js +61 -74
  66. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  67. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
  68. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
  69. package/dist/mcp/builtin/collections/findVersionByIDTool.js +74 -0
  70. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
  71. package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
  72. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
  73. package/dist/mcp/builtin/collections/findVersionsTool.js +84 -0
  74. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
  75. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  76. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  77. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +103 -0
  78. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  79. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
  80. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
  81. package/dist/mcp/builtin/collections/restoreVersionTool.js +69 -0
  82. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -0
  83. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  84. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  85. package/dist/mcp/builtin/collections/updateTool.js +118 -133
  86. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  87. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  88. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  89. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  90. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  91. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  92. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  93. package/dist/mcp/builtin/formatEntityError.js +55 -0
  94. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  95. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  96. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  97. package/dist/mcp/builtin/getConfigInfoTool.js +69 -0
  98. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  99. package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
  100. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
  101. package/dist/mcp/builtin/globals/countVersionsTool.js +60 -0
  102. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
  103. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  104. package/dist/mcp/builtin/globals/findTool.js +27 -36
  105. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  106. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
  107. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
  108. package/dist/mcp/builtin/globals/findVersionByIDTool.js +68 -0
  109. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
  110. package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
  111. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
  112. package/dist/mcp/builtin/globals/findVersionsTool.js +78 -0
  113. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
  114. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  115. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  116. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +81 -0
  117. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  118. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
  119. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
  120. package/dist/mcp/builtin/globals/restoreVersionTool.js +70 -0
  121. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
  122. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  123. package/dist/mcp/builtin/globals/updateTool.js +44 -58
  124. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  125. package/dist/mcp/builtinTools.d.ts +150 -19
  126. package/dist/mcp/builtinTools.d.ts.map +1 -1
  127. package/dist/mcp/builtinTools.js +125 -20
  128. package/dist/mcp/builtinTools.js.map +1 -1
  129. package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
  130. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  131. package/dist/mcp/sanitizeMCPConfig.js +124 -58
  132. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  133. package/dist/stdio.d.ts +3 -3
  134. package/dist/stdio.d.ts.map +1 -1
  135. package/dist/stdio.js +86 -47
  136. package/dist/stdio.js.map +1 -1
  137. package/dist/types.d.ts +123 -114
  138. package/dist/types.d.ts.map +1 -1
  139. package/dist/types.js +2 -4
  140. package/dist/types.js.map +1 -1
  141. package/dist/utils/camelCase.d.ts.map +1 -1
  142. package/dist/utils/getPluginConfig.d.ts +1 -1
  143. package/dist/utils/getPluginConfig.js +1 -1
  144. package/dist/utils/getPluginConfig.js.map +1 -1
  145. package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
  146. package/dist/utils/toStandardSchema.d.ts.map +1 -1
  147. package/package.json +14 -21
  148. package/src/defaultAccess.ts +3 -0
  149. package/src/endpoint/access.ts +80 -101
  150. package/src/endpoint/index.ts +88 -16
  151. package/src/exports/client.ts +2 -1
  152. package/src/exports/internal.ts +1 -0
  153. package/src/index.ts +12 -20
  154. package/src/mcp/buildMcpServer.ts +180 -110
  155. package/src/mcp/builtin/collections/authTools.ts +122 -75
  156. package/src/mcp/builtin/collections/countTool.ts +61 -0
  157. package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
  158. package/src/mcp/builtin/collections/createTool.ts +117 -88
  159. package/src/mcp/builtin/collections/deleteTool.ts +48 -74
  160. package/src/mcp/builtin/collections/duplicateTool.ts +84 -0
  161. package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
  162. package/src/mcp/builtin/collections/fileInput.ts +127 -0
  163. package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
  164. package/src/mcp/builtin/collections/findTool.ts +57 -110
  165. package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
  166. package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
  167. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +91 -0
  168. package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
  169. package/src/mcp/builtin/collections/updateTool.ts +106 -138
  170. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  171. package/src/mcp/builtin/formatEntityError.ts +77 -0
  172. package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
  173. package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
  174. package/src/mcp/builtin/globals/findTool.ts +27 -57
  175. package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
  176. package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
  177. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +73 -0
  178. package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
  179. package/src/mcp/builtin/globals/updateTool.ts +51 -77
  180. package/src/mcp/builtinTools.ts +118 -41
  181. package/src/mcp/sanitizeMCPConfig.ts +120 -78
  182. package/src/stdio.ts +80 -47
  183. package/src/types.ts +141 -120
  184. package/src/utils/getPluginConfig.ts +1 -1
  185. package/dist/collection/getAccessField.d.ts +0 -12
  186. package/dist/collection/getAccessField.d.ts.map +0 -1
  187. package/dist/collection/getAccessField.js +0 -57
  188. package/dist/collection/getAccessField.js.map +0 -1
  189. package/dist/collection/index.d.ts +0 -6
  190. package/dist/collection/index.d.ts.map +0 -1
  191. package/dist/collection/index.js +0 -59
  192. package/dist/collection/index.js.map +0 -1
  193. package/dist/components/AccessField/index.client.d.ts +0 -10
  194. package/dist/components/AccessField/index.client.d.ts.map +0 -1
  195. package/dist/components/AccessField/index.client.js +0 -305
  196. package/dist/components/AccessField/index.client.js.map +0 -1
  197. package/dist/components/AccessField/index.css +0 -93
  198. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  199. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  200. package/dist/utils/getVirtualFieldNames.js +0 -35
  201. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  202. package/dist/utils/localAPIDefaults.d.ts +0 -20
  203. package/dist/utils/localAPIDefaults.d.ts.map +0 -1
  204. package/dist/utils/localAPIDefaults.js +0 -19
  205. package/dist/utils/localAPIDefaults.js.map +0 -1
  206. package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts +0 -7
  207. package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts.map +0 -1
  208. package/dist/utils/schemaConversion/prepareCollectionSchema.js +0 -37
  209. package/dist/utils/schemaConversion/prepareCollectionSchema.js.map +0 -1
  210. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
  211. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
  212. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
  213. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
  214. package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts +0 -13
  215. package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts.map +0 -1
  216. package/dist/utils/schemaConversion/sanitizeJsonSchema.js +0 -56
  217. package/dist/utils/schemaConversion/sanitizeJsonSchema.js.map +0 -1
  218. package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts +0 -20
  219. package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts.map +0 -1
  220. package/dist/utils/schemaConversion/simplifyRelationshipFields.js +0 -56
  221. package/dist/utils/schemaConversion/simplifyRelationshipFields.js.map +0 -1
  222. package/dist/utils/schemaConversion/transformPointFields.d.ts +0 -3
  223. package/dist/utils/schemaConversion/transformPointFields.d.ts.map +0 -1
  224. package/dist/utils/schemaConversion/transformPointFields.js +0 -57
  225. package/dist/utils/schemaConversion/transformPointFields.js.map +0 -1
  226. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  227. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  228. package/dist/utils/transformPointDataToPayload.js +0 -28
  229. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  230. package/src/collection/getAccessField.ts +0 -64
  231. package/src/collection/index.ts +0 -63
  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/prepareCollectionSchema.ts +0 -39
  237. package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
  238. package/src/utils/schemaConversion/sanitizeJsonSchema.ts +0 -62
  239. package/src/utils/schemaConversion/simplifyRelationshipFields.ts +0 -70
  240. package/src/utils/schemaConversion/transformPointFields.ts +0 -56
  241. package/src/utils/transformPointDataToPayload.ts +0 -40
@@ -1,38 +1,31 @@
1
+ import type { PayloadRequest } from 'payload'
2
+
1
3
  import { McpServer, type ServerContext } from '@modelcontextprotocol/server'
2
- import { APIError, configToJSONSchema, 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
- }
26
-
27
20
  /**
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)`.
21
+ * Serving-entry-agnostic core: registers every authorized MCP item onto a fresh
22
+ * `McpServer` and returns it. The HTTP and stdio entry point callers provide fresh
23
+ * instances from this builder while they own the transport and protocol-era
24
+ * decision.
32
25
  *
33
26
  * `req` is the request context handlers see. For HTTP it's the live
34
27
  * `PayloadRequest` derived from the incoming HTTP request; for stdio it's a
35
- * synthesized one built via `createLocalReq`.
28
+ * synthesized one built via `createPayloadRequest`.
36
29
  */
37
30
  export const buildMcpServer = ({
38
31
  authorizedMCP,
@@ -43,6 +36,8 @@ export const buildMcpServer = ({
43
36
  pluginConfig: SanitizedMCPPluginConfig
44
37
  req: PayloadRequest
45
38
  }): McpServer => {
39
+ z.config(z.locales.en())
40
+
46
41
  const serverOptions = pluginConfig.mcp?.serverOptions || {}
47
42
  const server = new McpServer(
48
43
  { name: 'Payload MCP Server', version: '1.0.0', ...serverOptions.serverInfo },
@@ -55,105 +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
 
67
- const configSchema = configToJSONSchema(
68
- req.payload.config,
69
- req.payload.db.defaultIDType,
70
- req.i18n,
71
- { forceInlineBlocks: true },
72
- ) as JsonSchemaType
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
+ }
73
141
 
74
142
  try {
143
+ const registeredEntityTools = new Set<string>()
144
+
75
145
  for (const item of authorizedMCP.items) {
76
146
  switch (item.type) {
77
- case 'collectionTool': {
78
- const tool = item.tool
79
- const name = wireName(item.key, item.collectionSlug)
80
- let inputSchema = tool.input
81
- if (typeof inputSchema === 'function') {
82
- const raw = configSchema.definitions?.[item.collectionSlug]
83
- if (!raw) {
84
- throw new APIError(
85
- `Collection schema not found for slug: ${item.collectionSlug}`,
86
- 500,
87
- )
88
- }
89
- const collectionSchema = removeVirtualFieldsFromSchema(
90
- JSON.parse(JSON.stringify(raw)) as JsonSchemaType,
91
- getCollectionVirtualFieldNames(req.payload.config, item.collectionSlug),
92
- )
93
- inputSchema = inputSchema({ collectionSchema })
94
- }
95
- server.registerTool(
96
- name,
97
- {
98
- description: tool.description,
99
- inputSchema: inputSchema ? toStandardSchema(inputSchema) : undefined,
100
- },
101
- async (input: unknown, ctx: ServerContext) =>
102
- finalizeToolResponse(
103
- await tool.handler({
104
- authorizedMCP,
105
- collectionSlug: item.collectionSlug,
106
- input: (input ?? {}) as Record<string, unknown>,
107
- req,
108
- serverContext: ctx,
109
- }),
110
- tool.overrideResponse,
111
- ),
112
- )
113
- logger.info(`✅ Tool: ${name} Registered.`)
114
- break
115
- }
147
+ case 'collectionTool':
116
148
  case 'globalTool': {
117
- const tool = item.tool
118
- const name = wireName(item.key, item.globalSlug)
119
- let inputSchema = tool.input
120
- if (typeof inputSchema === 'function') {
121
- const raw = configSchema.definitions?.[item.globalSlug]
122
- if (!raw) {
123
- throw new APIError(`Global schema not found for slug: ${item.globalSlug}`, 500)
124
- }
125
- const globalSchema = removeVirtualFieldsFromSchema(
126
- JSON.parse(JSON.stringify(raw)) as JsonSchemaType,
127
- getGlobalVirtualFieldNames(req.payload.config, item.globalSlug),
128
- )
129
-
130
- inputSchema = inputSchema({ globalSchema })
149
+ if (registeredEntityTools.has(item.mcpName)) {
150
+ break
131
151
  }
152
+ registeredEntityTools.add(item.mcpName)
153
+
154
+ const inputSchema = withSlugInput({ input: item.tool.input })
155
+
132
156
  server.registerTool(
133
- name,
157
+ item.mcpName,
134
158
  {
135
- description: tool.description,
136
- inputSchema: inputSchema ? toStandardSchema(inputSchema) : undefined,
159
+ annotations: item.tool.annotations,
160
+ description: item.tool.description,
161
+ inputSchema: toStandardSchema(inputSchema),
137
162
  },
138
163
  async (input: unknown, ctx: ServerContext) =>
139
- finalizeToolResponse(
140
- await tool.handler({
141
- authorizedMCP,
142
- globalSlug: item.globalSlug,
143
- input: (input ?? {}) as Record<string, unknown>,
144
- req,
145
- serverContext: ctx,
146
- }),
147
- tool.overrideResponse,
148
- ),
164
+ callEntityTool({ input, item, serverContext: ctx }),
149
165
  )
150
- logger.info(`✅ Tool: ${name} Registered.`)
166
+ logger.info(`✅ Tool: ${item.mcpName} Registered.`)
151
167
  break
152
168
  }
153
169
  case 'prompt': {
154
170
  const prompt = item.prompt
155
171
  server.registerPrompt(
156
- item.key,
172
+ item.mcpName,
157
173
  {
158
174
  argsSchema: prompt.argsSchema ? toStandardSchema(prompt.argsSchema) : undefined,
159
175
  description: prompt.description,
@@ -172,7 +188,7 @@ export const buildMcpServer = ({
172
188
  case 'resource': {
173
189
  const resource = item.resource
174
190
  server.registerResource(
175
- item.key,
191
+ item.mcpName,
176
192
  // @ts-expect-error - Overload type ambiguity (string OR ResourceTemplate is valid)
177
193
  resource.uri,
178
194
  {
@@ -195,23 +211,29 @@ export const buildMcpServer = ({
195
211
  case 'tool': {
196
212
  const tool = item.tool
197
213
  server.registerTool(
198
- item.key,
214
+ item.mcpName,
199
215
  {
216
+ annotations: tool.annotations,
200
217
  description: tool.description,
201
218
  inputSchema: tool.input ? toStandardSchema(tool.input) : undefined,
202
219
  },
203
- async (input: unknown, ctx: ServerContext) =>
204
- finalizeToolResponse(
205
- await tool.handler({
206
- authorizedMCP,
207
- input: (input ?? {}) as Record<string, unknown>,
208
- req,
209
- serverContext: ctx,
210
- }),
211
- tool.overrideResponse,
212
- ),
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
+ },
213
235
  )
214
- logger.info(`✅ Tool: ${item.key} Registered.`)
236
+ logger.info(`✅ Tool: ${item.mcpName} Registered.`)
215
237
  break
216
238
  }
217
239
  }
@@ -222,3 +244,51 @@ export const buildMcpServer = ({
222
244
 
223
245
  return server
224
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
+ }