@payloadcms/plugin-mcp 4.0.0-canary.0 → 4.0.0-canary.1

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 (122) hide show
  1. package/dist/collection/getAccessField.js +1 -1
  2. package/dist/collection/getAccessField.js.map +1 -1
  3. package/dist/collection/index.d.ts.map +1 -1
  4. package/dist/collection/index.js +2 -1
  5. package/dist/collection/index.js.map +1 -1
  6. package/dist/components/AccessField/index.client.d.ts.map +1 -1
  7. package/dist/components/AccessField/index.client.js +30 -30
  8. package/dist/components/AccessField/index.client.js.map +1 -1
  9. package/dist/endpoint/access.js +5 -5
  10. package/dist/endpoint/access.js.map +1 -1
  11. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  12. package/dist/mcp/buildMcpServer.js +100 -64
  13. package/dist/mcp/buildMcpServer.js.map +1 -1
  14. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  15. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  16. package/dist/mcp/builtin/collections/createTool.js +28 -21
  17. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  18. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  19. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  20. package/dist/mcp/builtin/collections/deleteTool.js +5 -20
  21. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  22. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  23. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  24. package/dist/mcp/builtin/collections/findTool.js +6 -21
  25. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  26. package/dist/mcp/builtin/collections/formatCollectionError.d.ts +9 -0
  27. package/dist/mcp/builtin/collections/formatCollectionError.d.ts.map +1 -0
  28. package/dist/mcp/builtin/collections/formatCollectionError.js +60 -0
  29. package/dist/mcp/builtin/collections/formatCollectionError.js.map +1 -0
  30. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  31. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  32. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +35 -0
  33. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  34. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  35. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  36. package/dist/mcp/builtin/collections/updateTool.js +74 -62
  37. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  38. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  39. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  40. package/dist/mcp/builtin/getConfigInfoTool.js +49 -0
  41. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  42. package/dist/mcp/builtin/globals/findTool.js +1 -1
  43. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  44. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  45. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  46. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +35 -0
  47. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  48. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  49. package/dist/mcp/builtin/globals/updateTool.js +21 -19
  50. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  51. package/dist/mcp/builtin/validateEntityData.d.ts +14 -0
  52. package/dist/mcp/builtin/validateEntityData.d.ts.map +1 -0
  53. package/dist/mcp/builtin/validateEntityData.js +82 -0
  54. package/dist/mcp/builtin/validateEntityData.js.map +1 -0
  55. package/dist/mcp/builtinTools.d.ts +84 -16
  56. package/dist/mcp/builtinTools.d.ts.map +1 -1
  57. package/dist/mcp/builtinTools.js +54 -11
  58. package/dist/mcp/builtinTools.js.map +1 -1
  59. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  60. package/dist/mcp/sanitizeMCPConfig.js +61 -40
  61. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  62. package/dist/types.d.ts +16 -27
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils/schemaConversion/getEntityInputSchema.d.ts +11 -0
  66. package/dist/utils/schemaConversion/getEntityInputSchema.d.ts.map +1 -0
  67. package/dist/utils/schemaConversion/getEntityInputSchema.js +34 -0
  68. package/dist/utils/schemaConversion/getEntityInputSchema.js.map +1 -0
  69. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +15 -0
  70. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +1 -0
  71. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +464 -0
  72. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +1 -0
  73. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +158 -0
  74. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +1 -0
  75. package/dist/utils/whereSchema.d.ts +9 -0
  76. package/dist/utils/whereSchema.d.ts.map +1 -0
  77. package/dist/utils/whereSchema.js +13 -0
  78. package/dist/utils/whereSchema.js.map +1 -0
  79. package/package.json +5 -5
  80. package/src/collection/getAccessField.ts +1 -1
  81. package/src/collection/index.ts +1 -0
  82. package/src/components/AccessField/index.client.tsx +34 -31
  83. package/src/endpoint/access.ts +5 -5
  84. package/src/mcp/buildMcpServer.ts +123 -90
  85. package/src/mcp/builtin/collections/createTool.ts +46 -50
  86. package/src/mcp/builtin/collections/deleteTool.ts +9 -16
  87. package/src/mcp/builtin/collections/findTool.ts +7 -17
  88. package/src/mcp/builtin/collections/formatCollectionError.ts +84 -0
  89. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +28 -0
  90. package/src/mcp/builtin/collections/updateTool.ts +97 -91
  91. package/src/mcp/builtin/getConfigInfoTool.ts +44 -0
  92. package/src/mcp/builtin/globals/findTool.ts +1 -1
  93. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +28 -0
  94. package/src/mcp/builtin/globals/updateTool.ts +40 -43
  95. package/src/mcp/builtin/validateEntityData.ts +132 -0
  96. package/src/mcp/builtinTools.ts +52 -38
  97. package/src/mcp/sanitizeMCPConfig.ts +78 -57
  98. package/src/types.ts +24 -29
  99. package/src/utils/schemaConversion/getEntityInputSchema.ts +78 -0
  100. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +103 -0
  101. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +529 -0
  102. package/src/utils/whereSchema.ts +24 -0
  103. package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts +0 -7
  104. package/dist/utils/schemaConversion/prepareCollectionSchema.d.ts.map +0 -1
  105. package/dist/utils/schemaConversion/prepareCollectionSchema.js +0 -37
  106. package/dist/utils/schemaConversion/prepareCollectionSchema.js.map +0 -1
  107. package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts +0 -13
  108. package/dist/utils/schemaConversion/sanitizeJsonSchema.d.ts.map +0 -1
  109. package/dist/utils/schemaConversion/sanitizeJsonSchema.js +0 -56
  110. package/dist/utils/schemaConversion/sanitizeJsonSchema.js.map +0 -1
  111. package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts +0 -20
  112. package/dist/utils/schemaConversion/simplifyRelationshipFields.d.ts.map +0 -1
  113. package/dist/utils/schemaConversion/simplifyRelationshipFields.js +0 -56
  114. package/dist/utils/schemaConversion/simplifyRelationshipFields.js.map +0 -1
  115. package/dist/utils/schemaConversion/transformPointFields.d.ts +0 -3
  116. package/dist/utils/schemaConversion/transformPointFields.d.ts.map +0 -1
  117. package/dist/utils/schemaConversion/transformPointFields.js +0 -57
  118. package/dist/utils/schemaConversion/transformPointFields.js.map +0 -1
  119. package/src/utils/schemaConversion/prepareCollectionSchema.ts +0 -39
  120. package/src/utils/schemaConversion/sanitizeJsonSchema.ts +0 -62
  121. package/src/utils/schemaConversion/simplifyRelationshipFields.ts +0 -70
  122. package/src/utils/schemaConversion/transformPointFields.ts +0 -56
@@ -39,8 +39,8 @@
39
39
  globalSlug: item.globalSlug
40
40
  } : {},
41
41
  type: item.type,
42
+ configKey: item.configKey,
42
43
  description: itemDescription(item),
43
- key: item.key,
44
44
  label: item.label
45
45
  }))
46
46
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collection/getAccessField.ts"],"sourcesContent":["import type { JSONField } from 'payload'\n\nimport type { ClientMCPPluginConfig, MCPItem, SanitizedMCPPluginConfig } from '../types.js'\n\n/**\n * Returns the API key collection's `access` field — a JSON field whose value\n * is the `MCPAPIKeysDocAccessTree` the endpoint consults. The actual UI is\n * a custom client component (`AccessField`) which renders a permissions\n * matrix; the field's stored shape is just the runtime access tree.\n */\nexport const getAccessField = ({\n pluginConfig,\n}: {\n pluginConfig: SanitizedMCPPluginConfig\n}): JSONField => {\n const clientPluginConfig = sanitizeClientPluginConfig(pluginConfig)\n\n return {\n name: 'access',\n type: 'json',\n admin: {\n components: {\n Field: {\n clientProps: { pluginConfig: clientPluginConfig },\n path: '@payloadcms/plugin-mcp/client#AccessField',\n },\n },\n // TODO: needs i18n once design is finalized\n description: 'Access for this API key — uncheck to revoke individual tools.',\n position: 'sidebar',\n },\n defaultValue: {},\n // TODO: needs i18n once design is finalized\n label: 'Access',\n }\n}\n\n/**\n * Strips the non-serializable parts of `SanitizedMCPPluginConfig` (handlers,\n * input/argsSchema functions, etc.) so the result is safe to thread to a\n * client component via `clientProps`.\n */\nconst sanitizeClientPluginConfig = (\n pluginConfig: SanitizedMCPPluginConfig,\n): ClientMCPPluginConfig => ({\n items: pluginConfig.items.map((item) => ({\n ...(item.type === 'collectionTool' ? { collectionSlug: item.collectionSlug } : {}),\n ...(item.type === 'globalTool' ? { globalSlug: item.globalSlug } : {}),\n type: item.type,\n description: itemDescription(item),\n key: item.key,\n label: item.label,\n })),\n})\n\nconst itemDescription = (item: MCPItem): string => {\n if (item.type === 'prompt') {\n return item.prompt.description\n }\n if (item.type === 'resource') {\n return item.resource.description\n }\n return item.tool.description\n}\n"],"names":["getAccessField","pluginConfig","clientPluginConfig","sanitizeClientPluginConfig","name","type","admin","components","Field","clientProps","path","description","position","defaultValue","label","items","map","item","collectionSlug","globalSlug","itemDescription","key","prompt","resource","tool"],"mappings":"AAIA;;;;;CAKC,GACD,OAAO,MAAMA,iBAAiB,CAAC,EAC7BC,YAAY,EAGb;IACC,MAAMC,qBAAqBC,2BAA2BF;IAEtD,OAAO;QACLG,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,YAAY;gBACVC,OAAO;oBACLC,aAAa;wBAAER,cAAcC;oBAAmB;oBAChDQ,MAAM;gBACR;YACF;YACA,4CAA4C;YAC5CC,aAAa;YACbC,UAAU;QACZ;QACAC,cAAc,CAAC;QACf,4CAA4C;QAC5CC,OAAO;IACT;AACF,EAAC;AAED;;;;CAIC,GACD,MAAMX,6BAA6B,CACjCF,eAC2B,CAAA;QAC3Bc,OAAOd,aAAac,KAAK,CAACC,GAAG,CAAC,CAACC,OAAU,CAAA;gBACvC,GAAIA,KAAKZ,IAAI,KAAK,mBAAmB;oBAAEa,gBAAgBD,KAAKC,cAAc;gBAAC,IAAI,CAAC,CAAC;gBACjF,GAAID,KAAKZ,IAAI,KAAK,eAAe;oBAAEc,YAAYF,KAAKE,UAAU;gBAAC,IAAI,CAAC,CAAC;gBACrEd,MAAMY,KAAKZ,IAAI;gBACfM,aAAaS,gBAAgBH;gBAC7BI,KAAKJ,KAAKI,GAAG;gBACbP,OAAOG,KAAKH,KAAK;YACnB,CAAA;IACF,CAAA;AAEA,MAAMM,kBAAkB,CAACH;IACvB,IAAIA,KAAKZ,IAAI,KAAK,UAAU;QAC1B,OAAOY,KAAKK,MAAM,CAACX,WAAW;IAChC;IACA,IAAIM,KAAKZ,IAAI,KAAK,YAAY;QAC5B,OAAOY,KAAKM,QAAQ,CAACZ,WAAW;IAClC;IACA,OAAOM,KAAKO,IAAI,CAACb,WAAW;AAC9B"}
1
+ {"version":3,"sources":["../../src/collection/getAccessField.ts"],"sourcesContent":["import type { JSONField } from 'payload'\n\nimport type { ClientMCPPluginConfig, MCPItem, SanitizedMCPPluginConfig } from '../types.js'\n\n/**\n * Returns the API key collection's `access` field — a JSON field whose value\n * is the `MCPAPIKeysDocAccessTree` the endpoint consults. The actual UI is\n * a custom client component (`AccessField`) which renders a permissions\n * matrix; the field's stored shape is just the runtime access tree.\n */\nexport const getAccessField = ({\n pluginConfig,\n}: {\n pluginConfig: SanitizedMCPPluginConfig\n}): JSONField => {\n const clientPluginConfig = sanitizeClientPluginConfig(pluginConfig)\n\n return {\n name: 'access',\n type: 'json',\n admin: {\n components: {\n Field: {\n clientProps: { pluginConfig: clientPluginConfig },\n path: '@payloadcms/plugin-mcp/client#AccessField',\n },\n },\n // TODO: needs i18n once design is finalized\n description: 'Access for this API key — uncheck to revoke individual tools.',\n position: 'sidebar',\n },\n defaultValue: {},\n // TODO: needs i18n once design is finalized\n label: 'Access',\n }\n}\n\n/**\n * Strips the non-serializable parts of `SanitizedMCPPluginConfig` (handlers,\n * input/argsSchema functions, etc.) so the result is safe to thread to a\n * client component via `clientProps`.\n */\nconst sanitizeClientPluginConfig = (\n pluginConfig: SanitizedMCPPluginConfig,\n): ClientMCPPluginConfig => ({\n items: pluginConfig.items.map((item) => ({\n ...(item.type === 'collectionTool' ? { collectionSlug: item.collectionSlug } : {}),\n ...(item.type === 'globalTool' ? { globalSlug: item.globalSlug } : {}),\n type: item.type,\n configKey: item.configKey,\n description: itemDescription(item),\n label: item.label,\n })),\n})\n\nconst itemDescription = (item: MCPItem): string => {\n if (item.type === 'prompt') {\n return item.prompt.description\n }\n if (item.type === 'resource') {\n return item.resource.description\n }\n return item.tool.description\n}\n"],"names":["getAccessField","pluginConfig","clientPluginConfig","sanitizeClientPluginConfig","name","type","admin","components","Field","clientProps","path","description","position","defaultValue","label","items","map","item","collectionSlug","globalSlug","configKey","itemDescription","prompt","resource","tool"],"mappings":"AAIA;;;;;CAKC,GACD,OAAO,MAAMA,iBAAiB,CAAC,EAC7BC,YAAY,EAGb;IACC,MAAMC,qBAAqBC,2BAA2BF;IAEtD,OAAO;QACLG,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,YAAY;gBACVC,OAAO;oBACLC,aAAa;wBAAER,cAAcC;oBAAmB;oBAChDQ,MAAM;gBACR;YACF;YACA,4CAA4C;YAC5CC,aAAa;YACbC,UAAU;QACZ;QACAC,cAAc,CAAC;QACf,4CAA4C;QAC5CC,OAAO;IACT;AACF,EAAC;AAED;;;;CAIC,GACD,MAAMX,6BAA6B,CACjCF,eAC2B,CAAA;QAC3Bc,OAAOd,aAAac,KAAK,CAACC,GAAG,CAAC,CAACC,OAAU,CAAA;gBACvC,GAAIA,KAAKZ,IAAI,KAAK,mBAAmB;oBAAEa,gBAAgBD,KAAKC,cAAc;gBAAC,IAAI,CAAC,CAAC;gBACjF,GAAID,KAAKZ,IAAI,KAAK,eAAe;oBAAEc,YAAYF,KAAKE,UAAU;gBAAC,IAAI,CAAC,CAAC;gBACrEd,MAAMY,KAAKZ,IAAI;gBACfe,WAAWH,KAAKG,SAAS;gBACzBT,aAAaU,gBAAgBJ;gBAC7BH,OAAOG,KAAKH,KAAK;YACnB,CAAA;IACF,CAAA;AAEA,MAAMO,kBAAkB,CAACJ;IACvB,IAAIA,KAAKZ,IAAI,KAAK,UAAU;QAC1B,OAAOY,KAAKK,MAAM,CAACX,WAAW;IAChC;IACA,IAAIM,KAAKZ,IAAI,KAAK,YAAY;QAC5B,OAAOY,KAAKM,QAAQ,CAACZ,WAAW;IAClC;IACA,OAAOM,KAAKO,IAAI,CAACb,WAAW;AAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collection/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAI3D,eAAO,MAAM,oBAAoB,GAAI,mBAElC;IACD,YAAY,EAAE,wBAAwB,CAAA;CACvC,KAAG,gBAoDH,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collection/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAI3D,eAAO,MAAM,oBAAoB,GAAI,mBAElC;IACD,YAAY,EAAE,wBAAwB,CAAA;CACvC,KAAG,gBAqDH,CAAA"}
@@ -51,7 +51,8 @@ export const getAPIKeysCollection = ({ pluginConfig })=>{
51
51
  labels: {
52
52
  plural: 'API Keys',
53
53
  singular: 'API Key'
54
- }
54
+ },
55
+ versions: false
55
56
  };
56
57
  return pluginConfig.overrideApiKeyCollection ? pluginConfig.overrideApiKeyCollection(collection) : collection;
57
58
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collection/index.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedMCPPluginConfig } from '../types.js'\n\nimport { getAccessField } from './getAccessField.js'\n\nexport const getAPIKeysCollection = ({\n pluginConfig,\n}: {\n pluginConfig: SanitizedMCPPluginConfig\n}): CollectionConfig => {\n const collection: CollectionConfig = {\n slug: 'payload-mcp-api-keys',\n admin: {\n description:\n 'API keys control which collections, resources, tools, and prompts MCP clients can access',\n group: 'MCP',\n useAsTitle: 'label',\n },\n auth: {\n disableLocalStrategy: true,\n useAPIKey: true,\n },\n fields: [\n {\n name: 'user',\n type: 'relationship',\n admin: { description: 'The user that the API key is associated with.' },\n relationTo: pluginConfig.userCollection,\n required: true,\n },\n {\n name: 'label',\n type: 'text',\n admin: { description: 'A useful label for the API key.' },\n },\n {\n name: 'description',\n type: 'text',\n admin: { description: 'The purpose of the API key.' },\n },\n {\n name: 'overrideAccess',\n type: 'checkbox',\n admin: {\n description:\n 'When checked, this key bypasses Payload access control on every operation it performs. Leave unchecked unless you have a specific reason.',\n },\n defaultValue: false,\n label: 'Override access control',\n },\n getAccessField({ pluginConfig }),\n ],\n labels: {\n plural: 'API Keys',\n singular: 'API Key',\n },\n }\n\n return pluginConfig.overrideApiKeyCollection\n ? pluginConfig.overrideApiKeyCollection(collection)\n : collection\n}\n"],"names":["getAccessField","getAPIKeysCollection","pluginConfig","collection","slug","admin","description","group","useAsTitle","auth","disableLocalStrategy","useAPIKey","fields","name","type","relationTo","userCollection","required","defaultValue","label","labels","plural","singular","overrideApiKeyCollection"],"mappings":"AAIA,SAASA,cAAc,QAAQ,sBAAqB;AAEpD,OAAO,MAAMC,uBAAuB,CAAC,EACnCC,YAAY,EAGb;IACC,MAAMC,aAA+B;QACnCC,MAAM;QACNC,OAAO;YACLC,aACE;YACFC,OAAO;YACPC,YAAY;QACd;QACAC,MAAM;YACJC,sBAAsB;YACtBC,WAAW;QACb;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBAAEC,aAAa;gBAAgD;gBACtES,YAAYb,aAAac,cAAc;gBACvCC,UAAU;YACZ;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBAAEC,aAAa;gBAAkC;YAC1D;YACA;gBACEO,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBAAEC,aAAa;gBAA8B;YACtD;YACA;gBACEO,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBACLC,aACE;gBACJ;gBACAY,cAAc;gBACdC,OAAO;YACT;YACAnB,eAAe;gBAAEE;YAAa;SAC/B;QACDkB,QAAQ;YACNC,QAAQ;YACRC,UAAU;QACZ;IACF;IAEA,OAAOpB,aAAaqB,wBAAwB,GACxCrB,aAAaqB,wBAAwB,CAACpB,cACtCA;AACN,EAAC"}
1
+ {"version":3,"sources":["../../src/collection/index.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedMCPPluginConfig } from '../types.js'\n\nimport { getAccessField } from './getAccessField.js'\n\nexport const getAPIKeysCollection = ({\n pluginConfig,\n}: {\n pluginConfig: SanitizedMCPPluginConfig\n}): CollectionConfig => {\n const collection: CollectionConfig = {\n slug: 'payload-mcp-api-keys',\n admin: {\n description:\n 'API keys control which collections, resources, tools, and prompts MCP clients can access',\n group: 'MCP',\n useAsTitle: 'label',\n },\n auth: {\n disableLocalStrategy: true,\n useAPIKey: true,\n },\n fields: [\n {\n name: 'user',\n type: 'relationship',\n admin: { description: 'The user that the API key is associated with.' },\n relationTo: pluginConfig.userCollection,\n required: true,\n },\n {\n name: 'label',\n type: 'text',\n admin: { description: 'A useful label for the API key.' },\n },\n {\n name: 'description',\n type: 'text',\n admin: { description: 'The purpose of the API key.' },\n },\n {\n name: 'overrideAccess',\n type: 'checkbox',\n admin: {\n description:\n 'When checked, this key bypasses Payload access control on every operation it performs. Leave unchecked unless you have a specific reason.',\n },\n defaultValue: false,\n label: 'Override access control',\n },\n getAccessField({ pluginConfig }),\n ],\n labels: {\n plural: 'API Keys',\n singular: 'API Key',\n },\n versions: false,\n }\n\n return pluginConfig.overrideApiKeyCollection\n ? pluginConfig.overrideApiKeyCollection(collection)\n : collection\n}\n"],"names":["getAccessField","getAPIKeysCollection","pluginConfig","collection","slug","admin","description","group","useAsTitle","auth","disableLocalStrategy","useAPIKey","fields","name","type","relationTo","userCollection","required","defaultValue","label","labels","plural","singular","versions","overrideApiKeyCollection"],"mappings":"AAIA,SAASA,cAAc,QAAQ,sBAAqB;AAEpD,OAAO,MAAMC,uBAAuB,CAAC,EACnCC,YAAY,EAGb;IACC,MAAMC,aAA+B;QACnCC,MAAM;QACNC,OAAO;YACLC,aACE;YACFC,OAAO;YACPC,YAAY;QACd;QACAC,MAAM;YACJC,sBAAsB;YACtBC,WAAW;QACb;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBAAEC,aAAa;gBAAgD;gBACtES,YAAYb,aAAac,cAAc;gBACvCC,UAAU;YACZ;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBAAEC,aAAa;gBAAkC;YAC1D;YACA;gBACEO,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBAAEC,aAAa;gBAA8B;YACtD;YACA;gBACEO,MAAM;gBACNC,MAAM;gBACNT,OAAO;oBACLC,aACE;gBACJ;gBACAY,cAAc;gBACdC,OAAO;YACT;YACAnB,eAAe;gBAAEE;YAAa;SAC/B;QACDkB,QAAQ;YACNC,QAAQ;YACRC,UAAU;QACZ;QACAC,UAAU;IACZ;IAEA,OAAOrB,aAAasB,wBAAwB,GACxCtB,aAAasB,wBAAwB,CAACrB,cACtCA;AACN,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/components/AccessField/index.client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAGnD,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,qBAAqB,EAA2B,MAAM,gBAAgB,CAAA;AAEpF,OAAO,aAAa,CAAA;AAQpB,KAAK,KAAK,GAAG;IACX,YAAY,EAAE,qBAAqB,CAAA;CACpC,GAAG,oBAAoB,CAAA;AAoBxB,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA0QvC,CAAA"}
1
+ {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/components/AccessField/index.client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAGnD,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,qBAAqB,EAA2B,MAAM,gBAAgB,CAAA;AAEpF,OAAO,aAAa,CAAA;AAQpB,KAAK,KAAK,GAAG;IACX,YAAY,EAAE,qBAAqB,CAAA;CACpC,GAAG,oBAAoB,CAAA;AAoBxB,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA6QvC,CAAA"}
@@ -48,23 +48,23 @@ export const AccessField = ({ path, pluginConfig })=>{
48
48
  break;
49
49
  }
50
50
  }
51
- const isScopedAllowed = (scope, slug, key)=>access[scope]?.[slug]?.[key] !== false;
52
- const isFlatAllowed = (scope, key)=>access[scope]?.[key] !== false;
53
- const toggleScoped = (scope, slug, key, allow)=>{
51
+ const isScopedAllowed = (scope, slug, configKey)=>access[scope]?.[slug]?.[configKey] !== false;
52
+ const isFlatAllowed = (scope, configKey)=>access[scope]?.[configKey] !== false;
53
+ const toggleScoped = (scope, slug, configKey, allow)=>{
54
54
  if (allow) {
55
- const slugBucket = without(access[scope]?.[slug], key);
55
+ const slugBucket = without(access[scope]?.[slug], configKey);
56
56
  const scopeBucket = slugBucket ? setKey(access[scope], slug, slugBucket) : without(access[scope], slug);
57
57
  setValue(scopeBucket ? setKey(access, scope, scopeBucket) : without(access, scope) ?? {});
58
58
  } else {
59
- setValue(setKey(access, scope, setKey(access[scope], slug, setKey(access[scope]?.[slug], key, false))));
59
+ setValue(setKey(access, scope, setKey(access[scope], slug, setKey(access[scope]?.[slug], configKey, false))));
60
60
  }
61
61
  };
62
- const toggleFlat = (scope, key, allow)=>{
62
+ const toggleFlat = (scope, configKey, allow)=>{
63
63
  if (allow) {
64
- const bucket = without(access[scope], key);
64
+ const bucket = without(access[scope], configKey);
65
65
  setValue(bucket ? setKey(access, scope, bucket) : without(access, scope) ?? {});
66
66
  } else {
67
- setValue(setKey(access, scope, setKey(access[scope], key, false)));
67
+ setValue(setKey(access, scope, setKey(access[scope], configKey, false)));
68
68
  }
69
69
  };
70
70
  const setAllScoped = (scope, slug, leaves, allow)=>{
@@ -74,7 +74,7 @@ export const AccessField = ({ path, pluginConfig })=>{
74
74
  } else {
75
75
  const slugBucket = leaves.reduce((acc, leaf)=>({
76
76
  ...acc,
77
- [leaf.key]: false
77
+ [leaf.configKey]: false
78
78
  }), {});
79
79
  setValue(setKey(access, scope, setKey(access[scope], slug, slugBucket)));
80
80
  }
@@ -85,7 +85,7 @@ export const AccessField = ({ path, pluginConfig })=>{
85
85
  } else {
86
86
  const bucket = leaves.reduce((acc, leaf)=>({
87
87
  ...acc,
88
- [leaf.key]: false
88
+ [leaf.configKey]: false
89
89
  }), {});
90
90
  setValue(setKey(access, scope, bucket));
91
91
  }
@@ -125,13 +125,13 @@ export const AccessField = ({ path, pluginConfig })=>{
125
125
  className: `${baseClass}__list`,
126
126
  children: leaves.map((leaf)=>/*#__PURE__*/ _jsx("li", {
127
127
  children: /*#__PURE__*/ _jsx(CheckboxInput, {
128
- checked: isScopedAllowed('collections', slug, leaf.key),
129
- id: `${path}.collections.${slug}.${leaf.key}`,
128
+ checked: isScopedAllowed('collections', slug, leaf.configKey),
129
+ id: `${path}.collections.${slug}.${leaf.configKey}`,
130
130
  label: leaf.label,
131
- onToggle: (e)=>toggleScoped('collections', slug, leaf.key, e.target.checked),
131
+ onToggle: (e)=>toggleScoped('collections', slug, leaf.configKey, e.target.checked),
132
132
  tooltip: leaf.description
133
133
  })
134
- }, leaf.key))
134
+ }, leaf.configKey))
135
135
  })
136
136
  }, `collection-${slug}`);
137
137
  })
@@ -167,13 +167,13 @@ export const AccessField = ({ path, pluginConfig })=>{
167
167
  className: `${baseClass}__list`,
168
168
  children: leaves.map((leaf)=>/*#__PURE__*/ _jsx("li", {
169
169
  children: /*#__PURE__*/ _jsx(CheckboxInput, {
170
- checked: isScopedAllowed('globals', slug, leaf.key),
171
- id: `${path}.globals.${slug}.${leaf.key}`,
170
+ checked: isScopedAllowed('globals', slug, leaf.configKey),
171
+ id: `${path}.globals.${slug}.${leaf.configKey}`,
172
172
  label: leaf.label,
173
- onToggle: (e)=>toggleScoped('globals', slug, leaf.key, e.target.checked),
173
+ onToggle: (e)=>toggleScoped('globals', slug, leaf.configKey, e.target.checked),
174
174
  tooltip: leaf.description
175
175
  })
176
- }, leaf.key))
176
+ }, leaf.configKey))
177
177
  })
178
178
  }, `global-${slug}`);
179
179
  })
@@ -207,13 +207,13 @@ export const AccessField = ({ path, pluginConfig })=>{
207
207
  className: `${baseClass}__list`,
208
208
  children: tools.map((leaf)=>/*#__PURE__*/ _jsx("li", {
209
209
  children: /*#__PURE__*/ _jsx(CheckboxInput, {
210
- checked: isFlatAllowed('tools', leaf.key),
211
- id: `${path}.tools.${leaf.key}`,
210
+ checked: isFlatAllowed('tools', leaf.configKey),
211
+ id: `${path}.tools.${leaf.configKey}`,
212
212
  label: leaf.label,
213
- onToggle: (e)=>toggleFlat('tools', leaf.key, e.target.checked),
213
+ onToggle: (e)=>toggleFlat('tools', leaf.configKey, e.target.checked),
214
214
  tooltip: leaf.description
215
215
  })
216
- }, leaf.key))
216
+ }, leaf.configKey))
217
217
  })
218
218
  }),
219
219
  prompts.length > 0 && /*#__PURE__*/ _jsx(Collapsible, {
@@ -230,13 +230,13 @@ export const AccessField = ({ path, pluginConfig })=>{
230
230
  className: `${baseClass}__list`,
231
231
  children: prompts.map((leaf)=>/*#__PURE__*/ _jsx("li", {
232
232
  children: /*#__PURE__*/ _jsx(CheckboxInput, {
233
- checked: isFlatAllowed('prompts', leaf.key),
234
- id: `${path}.prompts.${leaf.key}`,
233
+ checked: isFlatAllowed('prompts', leaf.configKey),
234
+ id: `${path}.prompts.${leaf.configKey}`,
235
235
  label: leaf.label,
236
- onToggle: (e)=>toggleFlat('prompts', leaf.key, e.target.checked),
236
+ onToggle: (e)=>toggleFlat('prompts', leaf.configKey, e.target.checked),
237
237
  tooltip: leaf.description
238
238
  })
239
- }, leaf.key))
239
+ }, leaf.configKey))
240
240
  })
241
241
  }),
242
242
  resources.length > 0 && /*#__PURE__*/ _jsx(Collapsible, {
@@ -253,13 +253,13 @@ export const AccessField = ({ path, pluginConfig })=>{
253
253
  className: `${baseClass}__list`,
254
254
  children: resources.map((leaf)=>/*#__PURE__*/ _jsx("li", {
255
255
  children: /*#__PURE__*/ _jsx(CheckboxInput, {
256
- checked: isFlatAllowed('resources', leaf.key),
257
- id: `${path}.resources.${leaf.key}`,
256
+ checked: isFlatAllowed('resources', leaf.configKey),
257
+ id: `${path}.resources.${leaf.configKey}`,
258
258
  label: leaf.label,
259
- onToggle: (e)=>toggleFlat('resources', leaf.key, e.target.checked),
259
+ onToggle: (e)=>toggleFlat('resources', leaf.configKey, e.target.checked),
260
260
  tooltip: leaf.description
261
261
  })
262
- }, leaf.key))
262
+ }, leaf.configKey))
263
263
  })
264
264
  })
265
265
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/AccessField/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { JSONFieldClientProps } from 'payload'\n\nimport { CheckboxInput, Collapsible, useField } from '@payloadcms/ui'\nimport React from 'react'\n\nimport type { ClientMCPPluginConfig, MCPAPIKeysDocAccessTree } from '../../types.js'\n\nimport './index.css'\n\nconst baseClass = 'mcp-access-field'\n\ntype ClientItem = ClientMCPPluginConfig['items'][number]\ntype ScopeKey = 'collections' | 'globals'\ntype FlatKey = 'prompts' | 'resources' | 'tools'\n\ntype Props = {\n pluginConfig: ClientMCPPluginConfig\n} & JSONFieldClientProps\n\n/** Drop a key from an object and return a new object — or `undefined` if it'd be empty. */\nconst without = <T extends Record<string, unknown>>(\n obj: T | undefined,\n key: string,\n): T | undefined => {\n if (!obj || !(key in obj)) {\n return obj\n }\n const { [key]: _omitted, ...rest } = obj\n return Object.keys(rest).length === 0 ? undefined : (rest as T)\n}\n\nconst setKey = <T extends Record<string, unknown>>(\n obj: T | undefined,\n key: string,\n value: unknown,\n): T => ({ ...(obj ?? {}), [key]: value }) as T\n\nexport const AccessField: React.FC<Props> = ({ path, pluginConfig }) => {\n const { setValue, value } = useField<MCPAPIKeysDocAccessTree>({ path })\n const access = value ?? {}\n\n // Bucket items for rendering. (Bucketing is cheap and runs once per render;\n // memoizing would mean managing inputs/refs for marginal benefit.)\n const collectionsBySlug: Record<string, ClientItem[]> = {}\n const globalsBySlug: Record<string, ClientItem[]> = {}\n const tools: ClientItem[] = []\n const prompts: ClientItem[] = []\n const resources: ClientItem[] = []\n for (const item of pluginConfig.items) {\n switch (item.type) {\n case 'collectionTool':\n ;(collectionsBySlug[item.collectionSlug!] ??= []).push(item)\n break\n case 'globalTool':\n ;(globalsBySlug[item.globalSlug!] ??= []).push(item)\n break\n case 'prompt':\n prompts.push(item)\n break\n case 'resource':\n resources.push(item)\n break\n case 'tool':\n tools.push(item)\n break\n }\n }\n\n const isScopedAllowed = (scope: ScopeKey, slug: string, key: string): boolean =>\n access[scope]?.[slug]?.[key] !== false\n\n const isFlatAllowed = (scope: FlatKey, key: string): boolean => access[scope]?.[key] !== false\n\n const toggleScoped = (scope: ScopeKey, slug: string, key: string, allow: boolean) => {\n if (allow) {\n const slugBucket = without(access[scope]?.[slug], key)\n const scopeBucket = slugBucket\n ? setKey(access[scope], slug, slugBucket)\n : without(access[scope], slug)\n setValue(scopeBucket ? setKey(access, scope, scopeBucket) : (without(access, scope) ?? {}))\n } else {\n setValue(\n setKey(\n access,\n scope,\n setKey(access[scope], slug, setKey(access[scope]?.[slug], key, false)),\n ),\n )\n }\n }\n\n const toggleFlat = (scope: FlatKey, key: string, allow: boolean) => {\n if (allow) {\n const bucket = without(access[scope], key)\n setValue(bucket ? setKey(access, scope, bucket) : (without(access, scope) ?? {}))\n } else {\n setValue(setKey(access, scope, setKey(access[scope], key, false)))\n }\n }\n\n const setAllScoped = (scope: ScopeKey, slug: string, leaves: ClientItem[], allow: boolean) => {\n if (allow) {\n const scopeBucket = without(access[scope], slug)\n setValue(scopeBucket ? setKey(access, scope, scopeBucket) : (without(access, scope) ?? {}))\n } else {\n const slugBucket = leaves.reduce<Record<string, boolean>>(\n (acc, leaf) => ({ ...acc, [leaf.key]: false }),\n {},\n )\n setValue(setKey(access, scope, setKey(access[scope], slug, slugBucket)))\n }\n }\n\n const setAllFlat = (scope: FlatKey, leaves: ClientItem[], allow: boolean) => {\n if (allow) {\n setValue(without(access, scope) ?? {})\n } else {\n const bucket = leaves.reduce<Record<string, boolean>>(\n (acc, leaf) => ({ ...acc, [leaf.key]: false }),\n {},\n )\n setValue(setKey(access, scope, bucket))\n }\n }\n\n const collectionSlugs = Object.keys(collectionsBySlug)\n const globalSlugs = Object.keys(globalsBySlug)\n\n return (\n <div className={baseClass}>\n {collectionSlugs.length > 0 && (\n <section className={`${baseClass}__section`}>\n <header className={`${baseClass}__section-header`}>\n {/* TODO: needs i18n once design is finalized */}\n <h4>Collection-level permissions</h4>\n {/* TODO: needs i18n once design is finalized */}\n <p>Allow MCP clients to perform the following actions within these collections:</p>\n </header>\n {collectionSlugs.map((slug) => {\n const leaves = collectionsBySlug[slug]!\n return (\n <Collapsible\n actions={\n <GroupActions\n onSetAll={(allow) => setAllScoped('collections', slug, leaves, allow)}\n />\n }\n className={`${baseClass}__group`}\n header={<span className={`${baseClass}__group-label`}>{titleCase(slug)}</span>}\n initCollapsed\n key={`collection-${slug}`}\n >\n <ul className={`${baseClass}__list`}>\n {leaves.map((leaf) => (\n <li key={leaf.key}>\n <CheckboxInput\n checked={isScopedAllowed('collections', slug, leaf.key)}\n id={`${path}.collections.${slug}.${leaf.key}`}\n label={leaf.label}\n onToggle={(e) =>\n toggleScoped('collections', slug, leaf.key, e.target.checked)\n }\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )\n })}\n </section>\n )}\n\n {globalSlugs.length > 0 && (\n <section className={`${baseClass}__section`}>\n <header className={`${baseClass}__section-header`}>\n {/* TODO: needs i18n once design is finalized */}\n <h4>Global-level permissions</h4>\n {/* TODO: needs i18n once design is finalized */}\n <p>Allow MCP clients to perform the following actions on these globals:</p>\n </header>\n {globalSlugs.map((slug) => {\n const leaves = globalsBySlug[slug]!\n return (\n <Collapsible\n actions={\n <GroupActions\n onSetAll={(allow) => setAllScoped('globals', slug, leaves, allow)}\n />\n }\n className={`${baseClass}__group`}\n header={<span className={`${baseClass}__group-label`}>{titleCase(slug)}</span>}\n initCollapsed\n key={`global-${slug}`}\n >\n <ul className={`${baseClass}__list`}>\n {leaves.map((leaf) => (\n <li key={leaf.key}>\n <CheckboxInput\n checked={isScopedAllowed('globals', slug, leaf.key)}\n id={`${path}.globals.${slug}.${leaf.key}`}\n label={leaf.label}\n onToggle={(e) => toggleScoped('globals', slug, leaf.key, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )\n })}\n </section>\n )}\n\n {(tools.length > 0 || prompts.length > 0 || resources.length > 0) && (\n <section className={`${baseClass}__section`}>\n <header className={`${baseClass}__section-header`}>\n {/* TODO: needs i18n once design is finalized */}\n <h4>Project-level permissions</h4>\n {/* TODO: needs i18n once design is finalized */}\n <p>Cross-cutting tools, prompts, and resources not scoped to a single collection.</p>\n </header>\n {tools.length > 0 && (\n <Collapsible\n actions={<GroupActions onSetAll={(allow) => setAllFlat('tools', tools, allow)} />}\n className={`${baseClass}__group`}\n header={\n /* TODO: needs i18n once design is finalized */\n <span className={`${baseClass}__group-label`}>Tools</span>\n }\n initCollapsed\n >\n <ul className={`${baseClass}__list`}>\n {tools.map((leaf) => (\n <li key={leaf.key}>\n <CheckboxInput\n checked={isFlatAllowed('tools', leaf.key)}\n id={`${path}.tools.${leaf.key}`}\n label={leaf.label}\n onToggle={(e) => toggleFlat('tools', leaf.key, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )}\n {prompts.length > 0 && (\n <Collapsible\n actions={<GroupActions onSetAll={(allow) => setAllFlat('prompts', prompts, allow)} />}\n className={`${baseClass}__group`}\n header={\n /* TODO: needs i18n once design is finalized */\n <span className={`${baseClass}__group-label`}>Prompts</span>\n }\n initCollapsed\n >\n <ul className={`${baseClass}__list`}>\n {prompts.map((leaf) => (\n <li key={leaf.key}>\n <CheckboxInput\n checked={isFlatAllowed('prompts', leaf.key)}\n id={`${path}.prompts.${leaf.key}`}\n label={leaf.label}\n onToggle={(e) => toggleFlat('prompts', leaf.key, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )}\n {resources.length > 0 && (\n <Collapsible\n actions={\n <GroupActions onSetAll={(allow) => setAllFlat('resources', resources, allow)} />\n }\n className={`${baseClass}__group`}\n header={\n /* TODO: needs i18n once design is finalized */\n <span className={`${baseClass}__group-label`}>Resources</span>\n }\n initCollapsed\n >\n <ul className={`${baseClass}__list`}>\n {resources.map((leaf) => (\n <li key={leaf.key}>\n <CheckboxInput\n checked={isFlatAllowed('resources', leaf.key)}\n id={`${path}.resources.${leaf.key}`}\n label={leaf.label}\n onToggle={(e) => toggleFlat('resources', leaf.key, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )}\n </section>\n )}\n </div>\n )\n}\n\nconst GroupActions: React.FC<{ onSetAll: (allow: boolean) => void }> = ({ onSetAll }) => (\n // TODO: button labels + aria-labels need i18n once design is finalized\n <div className={`${baseClass}__group-actions`}>\n <button\n aria-label=\"Select all\"\n className={`${baseClass}__action`}\n onClick={(e) => {\n e.stopPropagation()\n onSetAll(true)\n }}\n title=\"Select all\"\n type=\"button\"\n >\n all\n </button>\n <span aria-hidden className={`${baseClass}__action-sep`}>\n /\n </span>\n <button\n aria-label=\"Clear all\"\n className={`${baseClass}__action`}\n onClick={(e) => {\n e.stopPropagation()\n onSetAll(false)\n }}\n title=\"Clear all\"\n type=\"button\"\n >\n none\n </button>\n </div>\n)\n\nconst titleCase = (slug: string): string =>\n slug.replace(/(^|[-_])(.)/g, (_, sep: string, ch: string) =>\n sep ? ` ${ch.toUpperCase()}` : ch.toUpperCase(),\n )\n"],"names":["CheckboxInput","Collapsible","useField","React","baseClass","without","obj","key","_omitted","rest","Object","keys","length","undefined","setKey","value","AccessField","path","pluginConfig","setValue","access","collectionsBySlug","globalsBySlug","tools","prompts","resources","item","items","type","collectionSlug","push","globalSlug","isScopedAllowed","scope","slug","isFlatAllowed","toggleScoped","allow","slugBucket","scopeBucket","toggleFlat","bucket","setAllScoped","leaves","reduce","acc","leaf","setAllFlat","collectionSlugs","globalSlugs","div","className","section","header","h4","p","map","actions","GroupActions","onSetAll","span","titleCase","initCollapsed","ul","li","checked","id","label","onToggle","e","target","tooltip","description","button","aria-label","onClick","stopPropagation","title","aria-hidden","replace","_","sep","ch","toUpperCase"],"mappings":"AAAA;;AAIA,SAASA,aAAa,EAAEC,WAAW,EAAEC,QAAQ,QAAQ,iBAAgB;AACrE,OAAOC,WAAW,QAAO;AAIzB,OAAO,cAAa;AAEpB,MAAMC,YAAY;AAUlB,yFAAyF,GACzF,MAAMC,UAAU,CACdC,KACAC;IAEA,IAAI,CAACD,OAAO,CAAEC,CAAAA,OAAOD,GAAE,GAAI;QACzB,OAAOA;IACT;IACA,MAAM,EAAE,CAACC,IAAI,EAAEC,QAAQ,EAAE,GAAGC,MAAM,GAAGH;IACrC,OAAOI,OAAOC,IAAI,CAACF,MAAMG,MAAM,KAAK,IAAIC,YAAaJ;AACvD;AAEA,MAAMK,SAAS,CACbR,KACAC,KACAQ,QACO,CAAA;QAAE,GAAIT,OAAO,CAAC,CAAC;QAAG,CAACC,IAAI,EAAEQ;IAAM,CAAA;AAExC,OAAO,MAAMC,cAA+B,CAAC,EAAEC,IAAI,EAAEC,YAAY,EAAE;IACjE,MAAM,EAAEC,QAAQ,EAAEJ,KAAK,EAAE,GAAGb,SAAkC;QAAEe;IAAK;IACrE,MAAMG,SAASL,SAAS,CAAC;IAEzB,4EAA4E;IAC5E,mEAAmE;IACnE,MAAMM,oBAAkD,CAAC;IACzD,MAAMC,gBAA8C,CAAC;IACrD,MAAMC,QAAsB,EAAE;IAC9B,MAAMC,UAAwB,EAAE;IAChC,MAAMC,YAA0B,EAAE;IAClC,KAAK,MAAMC,QAAQR,aAAaS,KAAK,CAAE;QACrC,OAAQD,KAAKE,IAAI;YACf,KAAK;;gBACDP,CAAAA,iBAAiB,CAACK,KAAKG,cAAc,CAAE,KAAK,EAAE,AAAD,EAAGC,IAAI,CAACJ;gBACvD;YACF,KAAK;;gBACDJ,CAAAA,aAAa,CAACI,KAAKK,UAAU,CAAE,KAAK,EAAE,AAAD,EAAGD,IAAI,CAACJ;gBAC/C;YACF,KAAK;gBACHF,QAAQM,IAAI,CAACJ;gBACb;YACF,KAAK;gBACHD,UAAUK,IAAI,CAACJ;gBACf;YACF,KAAK;gBACHH,MAAMO,IAAI,CAACJ;gBACX;QACJ;IACF;IAEA,MAAMM,kBAAkB,CAACC,OAAiBC,MAAc3B,MACtDa,MAAM,CAACa,MAAM,EAAE,CAACC,KAAK,EAAE,CAAC3B,IAAI,KAAK;IAEnC,MAAM4B,gBAAgB,CAACF,OAAgB1B,MAAyBa,MAAM,CAACa,MAAM,EAAE,CAAC1B,IAAI,KAAK;IAEzF,MAAM6B,eAAe,CAACH,OAAiBC,MAAc3B,KAAa8B;QAChE,IAAIA,OAAO;YACT,MAAMC,aAAajC,QAAQe,MAAM,CAACa,MAAM,EAAE,CAACC,KAAK,EAAE3B;YAClD,MAAMgC,cAAcD,aAChBxB,OAAOM,MAAM,CAACa,MAAM,EAAEC,MAAMI,cAC5BjC,QAAQe,MAAM,CAACa,MAAM,EAAEC;YAC3Bf,SAASoB,cAAczB,OAAOM,QAAQa,OAAOM,eAAgBlC,QAAQe,QAAQa,UAAU,CAAC;QAC1F,OAAO;YACLd,SACEL,OACEM,QACAa,OACAnB,OAAOM,MAAM,CAACa,MAAM,EAAEC,MAAMpB,OAAOM,MAAM,CAACa,MAAM,EAAE,CAACC,KAAK,EAAE3B,KAAK;QAGrE;IACF;IAEA,MAAMiC,aAAa,CAACP,OAAgB1B,KAAa8B;QAC/C,IAAIA,OAAO;YACT,MAAMI,SAASpC,QAAQe,MAAM,CAACa,MAAM,EAAE1B;YACtCY,SAASsB,SAAS3B,OAAOM,QAAQa,OAAOQ,UAAWpC,QAAQe,QAAQa,UAAU,CAAC;QAChF,OAAO;YACLd,SAASL,OAAOM,QAAQa,OAAOnB,OAAOM,MAAM,CAACa,MAAM,EAAE1B,KAAK;QAC5D;IACF;IAEA,MAAMmC,eAAe,CAACT,OAAiBC,MAAcS,QAAsBN;QACzE,IAAIA,OAAO;YACT,MAAME,cAAclC,QAAQe,MAAM,CAACa,MAAM,EAAEC;YAC3Cf,SAASoB,cAAczB,OAAOM,QAAQa,OAAOM,eAAgBlC,QAAQe,QAAQa,UAAU,CAAC;QAC1F,OAAO;YACL,MAAMK,aAAaK,OAAOC,MAAM,CAC9B,CAACC,KAAKC,OAAU,CAAA;oBAAE,GAAGD,GAAG;oBAAE,CAACC,KAAKvC,GAAG,CAAC,EAAE;gBAAM,CAAA,GAC5C,CAAC;YAEHY,SAASL,OAAOM,QAAQa,OAAOnB,OAAOM,MAAM,CAACa,MAAM,EAAEC,MAAMI;QAC7D;IACF;IAEA,MAAMS,aAAa,CAACd,OAAgBU,QAAsBN;QACxD,IAAIA,OAAO;YACTlB,SAASd,QAAQe,QAAQa,UAAU,CAAC;QACtC,OAAO;YACL,MAAMQ,SAASE,OAAOC,MAAM,CAC1B,CAACC,KAAKC,OAAU,CAAA;oBAAE,GAAGD,GAAG;oBAAE,CAACC,KAAKvC,GAAG,CAAC,EAAE;gBAAM,CAAA,GAC5C,CAAC;YAEHY,SAASL,OAAOM,QAAQa,OAAOQ;QACjC;IACF;IAEA,MAAMO,kBAAkBtC,OAAOC,IAAI,CAACU;IACpC,MAAM4B,cAAcvC,OAAOC,IAAI,CAACW;IAEhC,qBACE,MAAC4B;QAAIC,WAAW/C;;YACb4C,gBAAgBpC,MAAM,GAAG,mBACxB,MAACwC;gBAAQD,WAAW,GAAG/C,UAAU,SAAS,CAAC;;kCACzC,MAACiD;wBAAOF,WAAW,GAAG/C,UAAU,gBAAgB,CAAC;;0CAE/C,KAACkD;0CAAG;;0CAEJ,KAACC;0CAAE;;;;oBAEJP,gBAAgBQ,GAAG,CAAC,CAACtB;wBACpB,MAAMS,SAAStB,iBAAiB,CAACa,KAAK;wBACtC,qBACE,KAACjC;4BACCwD,uBACE,KAACC;gCACCC,UAAU,CAACtB,QAAUK,aAAa,eAAeR,MAAMS,QAAQN;;4BAGnEc,WAAW,GAAG/C,UAAU,OAAO,CAAC;4BAChCiD,sBAAQ,KAACO;gCAAKT,WAAW,GAAG/C,UAAU,aAAa,CAAC;0CAAGyD,UAAU3B;;4BACjE4B,aAAa;sCAGb,cAAA,KAACC;gCAAGZ,WAAW,GAAG/C,UAAU,MAAM,CAAC;0CAChCuC,OAAOa,GAAG,CAAC,CAACV,qBACX,KAACkB;kDACC,cAAA,KAAChE;4CACCiE,SAASjC,gBAAgB,eAAeE,MAAMY,KAAKvC,GAAG;4CACtD2D,IAAI,GAAGjD,KAAK,aAAa,EAAEiB,KAAK,CAAC,EAAEY,KAAKvC,GAAG,EAAE;4CAC7C4D,OAAOrB,KAAKqB,KAAK;4CACjBC,UAAU,CAACC,IACTjC,aAAa,eAAeF,MAAMY,KAAKvC,GAAG,EAAE8D,EAAEC,MAAM,CAACL,OAAO;4CAE9DM,SAASzB,KAAK0B,WAAW;;uCARpB1B,KAAKvC,GAAG;;2BAJhB,CAAC,WAAW,EAAE2B,MAAM;oBAmB/B;;;YAIHe,YAAYrC,MAAM,GAAG,mBACpB,MAACwC;gBAAQD,WAAW,GAAG/C,UAAU,SAAS,CAAC;;kCACzC,MAACiD;wBAAOF,WAAW,GAAG/C,UAAU,gBAAgB,CAAC;;0CAE/C,KAACkD;0CAAG;;0CAEJ,KAACC;0CAAE;;;;oBAEJN,YAAYO,GAAG,CAAC,CAACtB;wBAChB,MAAMS,SAASrB,aAAa,CAACY,KAAK;wBAClC,qBACE,KAACjC;4BACCwD,uBACE,KAACC;gCACCC,UAAU,CAACtB,QAAUK,aAAa,WAAWR,MAAMS,QAAQN;;4BAG/Dc,WAAW,GAAG/C,UAAU,OAAO,CAAC;4BAChCiD,sBAAQ,KAACO;gCAAKT,WAAW,GAAG/C,UAAU,aAAa,CAAC;0CAAGyD,UAAU3B;;4BACjE4B,aAAa;sCAGb,cAAA,KAACC;gCAAGZ,WAAW,GAAG/C,UAAU,MAAM,CAAC;0CAChCuC,OAAOa,GAAG,CAAC,CAACV,qBACX,KAACkB;kDACC,cAAA,KAAChE;4CACCiE,SAASjC,gBAAgB,WAAWE,MAAMY,KAAKvC,GAAG;4CAClD2D,IAAI,GAAGjD,KAAK,SAAS,EAAEiB,KAAK,CAAC,EAAEY,KAAKvC,GAAG,EAAE;4CACzC4D,OAAOrB,KAAKqB,KAAK;4CACjBC,UAAU,CAACC,IAAMjC,aAAa,WAAWF,MAAMY,KAAKvC,GAAG,EAAE8D,EAAEC,MAAM,CAACL,OAAO;4CACzEM,SAASzB,KAAK0B,WAAW;;uCANpB1B,KAAKvC,GAAG;;2BAJhB,CAAC,OAAO,EAAE2B,MAAM;oBAiB3B;;;YAIFX,CAAAA,MAAMX,MAAM,GAAG,KAAKY,QAAQZ,MAAM,GAAG,KAAKa,UAAUb,MAAM,GAAG,CAAA,mBAC7D,MAACwC;gBAAQD,WAAW,GAAG/C,UAAU,SAAS,CAAC;;kCACzC,MAACiD;wBAAOF,WAAW,GAAG/C,UAAU,gBAAgB,CAAC;;0CAE/C,KAACkD;0CAAG;;0CAEJ,KAACC;0CAAE;;;;oBAEJhC,MAAMX,MAAM,GAAG,mBACd,KAACX;wBACCwD,uBAAS,KAACC;4BAAaC,UAAU,CAACtB,QAAUU,WAAW,SAASxB,OAAOc;;wBACvEc,WAAW,GAAG/C,UAAU,OAAO,CAAC;wBAChCiD,QACE,6CAA6C,iBAC7C,KAACO;4BAAKT,WAAW,GAAG/C,UAAU,aAAa,CAAC;sCAAE;;wBAEhD0D,aAAa;kCAEb,cAAA,KAACC;4BAAGZ,WAAW,GAAG/C,UAAU,MAAM,CAAC;sCAChCmB,MAAMiC,GAAG,CAAC,CAACV,qBACV,KAACkB;8CACC,cAAA,KAAChE;wCACCiE,SAAS9B,cAAc,SAASW,KAAKvC,GAAG;wCACxC2D,IAAI,GAAGjD,KAAK,OAAO,EAAE6B,KAAKvC,GAAG,EAAE;wCAC/B4D,OAAOrB,KAAKqB,KAAK;wCACjBC,UAAU,CAACC,IAAM7B,WAAW,SAASM,KAAKvC,GAAG,EAAE8D,EAAEC,MAAM,CAACL,OAAO;wCAC/DM,SAASzB,KAAK0B,WAAW;;mCANpB1B,KAAKvC,GAAG;;;oBAaxBiB,QAAQZ,MAAM,GAAG,mBAChB,KAACX;wBACCwD,uBAAS,KAACC;4BAAaC,UAAU,CAACtB,QAAUU,WAAW,WAAWvB,SAASa;;wBAC3Ec,WAAW,GAAG/C,UAAU,OAAO,CAAC;wBAChCiD,QACE,6CAA6C,iBAC7C,KAACO;4BAAKT,WAAW,GAAG/C,UAAU,aAAa,CAAC;sCAAE;;wBAEhD0D,aAAa;kCAEb,cAAA,KAACC;4BAAGZ,WAAW,GAAG/C,UAAU,MAAM,CAAC;sCAChCoB,QAAQgC,GAAG,CAAC,CAACV,qBACZ,KAACkB;8CACC,cAAA,KAAChE;wCACCiE,SAAS9B,cAAc,WAAWW,KAAKvC,GAAG;wCAC1C2D,IAAI,GAAGjD,KAAK,SAAS,EAAE6B,KAAKvC,GAAG,EAAE;wCACjC4D,OAAOrB,KAAKqB,KAAK;wCACjBC,UAAU,CAACC,IAAM7B,WAAW,WAAWM,KAAKvC,GAAG,EAAE8D,EAAEC,MAAM,CAACL,OAAO;wCACjEM,SAASzB,KAAK0B,WAAW;;mCANpB1B,KAAKvC,GAAG;;;oBAaxBkB,UAAUb,MAAM,GAAG,mBAClB,KAACX;wBACCwD,uBACE,KAACC;4BAAaC,UAAU,CAACtB,QAAUU,WAAW,aAAatB,WAAWY;;wBAExEc,WAAW,GAAG/C,UAAU,OAAO,CAAC;wBAChCiD,QACE,6CAA6C,iBAC7C,KAACO;4BAAKT,WAAW,GAAG/C,UAAU,aAAa,CAAC;sCAAE;;wBAEhD0D,aAAa;kCAEb,cAAA,KAACC;4BAAGZ,WAAW,GAAG/C,UAAU,MAAM,CAAC;sCAChCqB,UAAU+B,GAAG,CAAC,CAACV,qBACd,KAACkB;8CACC,cAAA,KAAChE;wCACCiE,SAAS9B,cAAc,aAAaW,KAAKvC,GAAG;wCAC5C2D,IAAI,GAAGjD,KAAK,WAAW,EAAE6B,KAAKvC,GAAG,EAAE;wCACnC4D,OAAOrB,KAAKqB,KAAK;wCACjBC,UAAU,CAACC,IAAM7B,WAAW,aAAaM,KAAKvC,GAAG,EAAE8D,EAAEC,MAAM,CAACL,OAAO;wCACnEM,SAASzB,KAAK0B,WAAW;;mCANpB1B,KAAKvC,GAAG;;;;;;;AAiBnC,EAAC;AAED,MAAMmD,eAAiE,CAAC,EAAEC,QAAQ,EAAE,GAClF,uEAAuE;kBACvE,MAACT;QAAIC,WAAW,GAAG/C,UAAU,eAAe,CAAC;;0BAC3C,KAACqE;gBACCC,cAAW;gBACXvB,WAAW,GAAG/C,UAAU,QAAQ,CAAC;gBACjCuE,SAAS,CAACN;oBACRA,EAAEO,eAAe;oBACjBjB,SAAS;gBACX;gBACAkB,OAAM;gBACNjD,MAAK;0BACN;;0BAGD,KAACgC;gBAAKkB,aAAW;gBAAC3B,WAAW,GAAG/C,UAAU,YAAY,CAAC;0BAAE;;0BAGzD,KAACqE;gBACCC,cAAW;gBACXvB,WAAW,GAAG/C,UAAU,QAAQ,CAAC;gBACjCuE,SAAS,CAACN;oBACRA,EAAEO,eAAe;oBACjBjB,SAAS;gBACX;gBACAkB,OAAM;gBACNjD,MAAK;0BACN;;;;AAML,MAAMiC,YAAY,CAAC3B,OACjBA,KAAK6C,OAAO,CAAC,gBAAgB,CAACC,GAAGC,KAAaC,KAC5CD,MAAM,CAAC,CAAC,EAAEC,GAAGC,WAAW,IAAI,GAAGD,GAAGC,WAAW"}
1
+ {"version":3,"sources":["../../../src/components/AccessField/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { JSONFieldClientProps } from 'payload'\n\nimport { CheckboxInput, Collapsible, useField } from '@payloadcms/ui'\nimport React from 'react'\n\nimport type { ClientMCPPluginConfig, MCPAPIKeysDocAccessTree } from '../../types.js'\n\nimport './index.css'\n\nconst baseClass = 'mcp-access-field'\n\ntype ClientItem = ClientMCPPluginConfig['items'][number]\ntype ScopeKey = 'collections' | 'globals'\ntype FlatKey = 'prompts' | 'resources' | 'tools'\n\ntype Props = {\n pluginConfig: ClientMCPPluginConfig\n} & JSONFieldClientProps\n\n/** Drop a key from an object and return a new object — or `undefined` if it'd be empty. */\nconst without = <T extends Record<string, unknown>>(\n obj: T | undefined,\n key: string,\n): T | undefined => {\n if (!obj || !(key in obj)) {\n return obj\n }\n const { [key]: _omitted, ...rest } = obj\n return Object.keys(rest).length === 0 ? undefined : (rest as T)\n}\n\nconst setKey = <T extends Record<string, unknown>>(\n obj: T | undefined,\n key: string,\n value: unknown,\n): T => ({ ...(obj ?? {}), [key]: value }) as T\n\nexport const AccessField: React.FC<Props> = ({ path, pluginConfig }) => {\n const { setValue, value } = useField<MCPAPIKeysDocAccessTree>({ path })\n const access = value ?? {}\n\n // Bucket items for rendering. (Bucketing is cheap and runs once per render;\n // memoizing would mean managing inputs/refs for marginal benefit.)\n const collectionsBySlug: Record<string, ClientItem[]> = {}\n const globalsBySlug: Record<string, ClientItem[]> = {}\n const tools: ClientItem[] = []\n const prompts: ClientItem[] = []\n const resources: ClientItem[] = []\n for (const item of pluginConfig.items) {\n switch (item.type) {\n case 'collectionTool':\n ;(collectionsBySlug[item.collectionSlug!] ??= []).push(item)\n break\n case 'globalTool':\n ;(globalsBySlug[item.globalSlug!] ??= []).push(item)\n break\n case 'prompt':\n prompts.push(item)\n break\n case 'resource':\n resources.push(item)\n break\n case 'tool':\n tools.push(item)\n break\n }\n }\n\n const isScopedAllowed = (scope: ScopeKey, slug: string, configKey: string): boolean =>\n access[scope]?.[slug]?.[configKey] !== false\n\n const isFlatAllowed = (scope: FlatKey, configKey: string): boolean =>\n access[scope]?.[configKey] !== false\n\n const toggleScoped = (scope: ScopeKey, slug: string, configKey: string, allow: boolean) => {\n if (allow) {\n const slugBucket = without(access[scope]?.[slug], configKey)\n const scopeBucket = slugBucket\n ? setKey(access[scope], slug, slugBucket)\n : without(access[scope], slug)\n setValue(scopeBucket ? setKey(access, scope, scopeBucket) : (without(access, scope) ?? {}))\n } else {\n setValue(\n setKey(\n access,\n scope,\n setKey(access[scope], slug, setKey(access[scope]?.[slug], configKey, false)),\n ),\n )\n }\n }\n\n const toggleFlat = (scope: FlatKey, configKey: string, allow: boolean) => {\n if (allow) {\n const bucket = without(access[scope], configKey)\n setValue(bucket ? setKey(access, scope, bucket) : (without(access, scope) ?? {}))\n } else {\n setValue(setKey(access, scope, setKey(access[scope], configKey, false)))\n }\n }\n\n const setAllScoped = (scope: ScopeKey, slug: string, leaves: ClientItem[], allow: boolean) => {\n if (allow) {\n const scopeBucket = without(access[scope], slug)\n setValue(scopeBucket ? setKey(access, scope, scopeBucket) : (without(access, scope) ?? {}))\n } else {\n const slugBucket = leaves.reduce<Record<string, boolean>>(\n (acc, leaf) => ({ ...acc, [leaf.configKey]: false }),\n {},\n )\n setValue(setKey(access, scope, setKey(access[scope], slug, slugBucket)))\n }\n }\n\n const setAllFlat = (scope: FlatKey, leaves: ClientItem[], allow: boolean) => {\n if (allow) {\n setValue(without(access, scope) ?? {})\n } else {\n const bucket = leaves.reduce<Record<string, boolean>>(\n (acc, leaf) => ({ ...acc, [leaf.configKey]: false }),\n {},\n )\n setValue(setKey(access, scope, bucket))\n }\n }\n\n const collectionSlugs = Object.keys(collectionsBySlug)\n const globalSlugs = Object.keys(globalsBySlug)\n\n return (\n <div className={baseClass}>\n {collectionSlugs.length > 0 && (\n <section className={`${baseClass}__section`}>\n <header className={`${baseClass}__section-header`}>\n {/* TODO: needs i18n once design is finalized */}\n <h4>Collection-level permissions</h4>\n {/* TODO: needs i18n once design is finalized */}\n <p>Allow MCP clients to perform the following actions within these collections:</p>\n </header>\n {collectionSlugs.map((slug) => {\n const leaves = collectionsBySlug[slug]!\n return (\n <Collapsible\n actions={\n <GroupActions\n onSetAll={(allow) => setAllScoped('collections', slug, leaves, allow)}\n />\n }\n className={`${baseClass}__group`}\n header={<span className={`${baseClass}__group-label`}>{titleCase(slug)}</span>}\n initCollapsed\n key={`collection-${slug}`}\n >\n <ul className={`${baseClass}__list`}>\n {leaves.map((leaf) => (\n <li key={leaf.configKey}>\n <CheckboxInput\n checked={isScopedAllowed('collections', slug, leaf.configKey)}\n id={`${path}.collections.${slug}.${leaf.configKey}`}\n label={leaf.label}\n onToggle={(e) =>\n toggleScoped('collections', slug, leaf.configKey, e.target.checked)\n }\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )\n })}\n </section>\n )}\n\n {globalSlugs.length > 0 && (\n <section className={`${baseClass}__section`}>\n <header className={`${baseClass}__section-header`}>\n {/* TODO: needs i18n once design is finalized */}\n <h4>Global-level permissions</h4>\n {/* TODO: needs i18n once design is finalized */}\n <p>Allow MCP clients to perform the following actions on these globals:</p>\n </header>\n {globalSlugs.map((slug) => {\n const leaves = globalsBySlug[slug]!\n return (\n <Collapsible\n actions={\n <GroupActions\n onSetAll={(allow) => setAllScoped('globals', slug, leaves, allow)}\n />\n }\n className={`${baseClass}__group`}\n header={<span className={`${baseClass}__group-label`}>{titleCase(slug)}</span>}\n initCollapsed\n key={`global-${slug}`}\n >\n <ul className={`${baseClass}__list`}>\n {leaves.map((leaf) => (\n <li key={leaf.configKey}>\n <CheckboxInput\n checked={isScopedAllowed('globals', slug, leaf.configKey)}\n id={`${path}.globals.${slug}.${leaf.configKey}`}\n label={leaf.label}\n onToggle={(e) =>\n toggleScoped('globals', slug, leaf.configKey, e.target.checked)\n }\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )\n })}\n </section>\n )}\n\n {(tools.length > 0 || prompts.length > 0 || resources.length > 0) && (\n <section className={`${baseClass}__section`}>\n <header className={`${baseClass}__section-header`}>\n {/* TODO: needs i18n once design is finalized */}\n <h4>Project-level permissions</h4>\n {/* TODO: needs i18n once design is finalized */}\n <p>Cross-cutting tools, prompts, and resources not scoped to a single collection.</p>\n </header>\n {tools.length > 0 && (\n <Collapsible\n actions={<GroupActions onSetAll={(allow) => setAllFlat('tools', tools, allow)} />}\n className={`${baseClass}__group`}\n header={\n /* TODO: needs i18n once design is finalized */\n <span className={`${baseClass}__group-label`}>Tools</span>\n }\n initCollapsed\n >\n <ul className={`${baseClass}__list`}>\n {tools.map((leaf) => (\n <li key={leaf.configKey}>\n <CheckboxInput\n checked={isFlatAllowed('tools', leaf.configKey)}\n id={`${path}.tools.${leaf.configKey}`}\n label={leaf.label}\n onToggle={(e) => toggleFlat('tools', leaf.configKey, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )}\n {prompts.length > 0 && (\n <Collapsible\n actions={<GroupActions onSetAll={(allow) => setAllFlat('prompts', prompts, allow)} />}\n className={`${baseClass}__group`}\n header={\n /* TODO: needs i18n once design is finalized */\n <span className={`${baseClass}__group-label`}>Prompts</span>\n }\n initCollapsed\n >\n <ul className={`${baseClass}__list`}>\n {prompts.map((leaf) => (\n <li key={leaf.configKey}>\n <CheckboxInput\n checked={isFlatAllowed('prompts', leaf.configKey)}\n id={`${path}.prompts.${leaf.configKey}`}\n label={leaf.label}\n onToggle={(e) => toggleFlat('prompts', leaf.configKey, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )}\n {resources.length > 0 && (\n <Collapsible\n actions={\n <GroupActions onSetAll={(allow) => setAllFlat('resources', resources, allow)} />\n }\n className={`${baseClass}__group`}\n header={\n /* TODO: needs i18n once design is finalized */\n <span className={`${baseClass}__group-label`}>Resources</span>\n }\n initCollapsed\n >\n <ul className={`${baseClass}__list`}>\n {resources.map((leaf) => (\n <li key={leaf.configKey}>\n <CheckboxInput\n checked={isFlatAllowed('resources', leaf.configKey)}\n id={`${path}.resources.${leaf.configKey}`}\n label={leaf.label}\n onToggle={(e) => toggleFlat('resources', leaf.configKey, e.target.checked)}\n tooltip={leaf.description}\n />\n </li>\n ))}\n </ul>\n </Collapsible>\n )}\n </section>\n )}\n </div>\n )\n}\n\nconst GroupActions: React.FC<{ onSetAll: (allow: boolean) => void }> = ({ onSetAll }) => (\n // TODO: button labels + aria-labels need i18n once design is finalized\n <div className={`${baseClass}__group-actions`}>\n <button\n aria-label=\"Select all\"\n className={`${baseClass}__action`}\n onClick={(e) => {\n e.stopPropagation()\n onSetAll(true)\n }}\n title=\"Select all\"\n type=\"button\"\n >\n all\n </button>\n <span aria-hidden className={`${baseClass}__action-sep`}>\n /\n </span>\n <button\n aria-label=\"Clear all\"\n className={`${baseClass}__action`}\n onClick={(e) => {\n e.stopPropagation()\n onSetAll(false)\n }}\n title=\"Clear all\"\n type=\"button\"\n >\n none\n </button>\n </div>\n)\n\nconst titleCase = (slug: string): string =>\n slug.replace(/(^|[-_])(.)/g, (_, sep: string, ch: string) =>\n sep ? ` ${ch.toUpperCase()}` : ch.toUpperCase(),\n )\n"],"names":["CheckboxInput","Collapsible","useField","React","baseClass","without","obj","key","_omitted","rest","Object","keys","length","undefined","setKey","value","AccessField","path","pluginConfig","setValue","access","collectionsBySlug","globalsBySlug","tools","prompts","resources","item","items","type","collectionSlug","push","globalSlug","isScopedAllowed","scope","slug","configKey","isFlatAllowed","toggleScoped","allow","slugBucket","scopeBucket","toggleFlat","bucket","setAllScoped","leaves","reduce","acc","leaf","setAllFlat","collectionSlugs","globalSlugs","div","className","section","header","h4","p","map","actions","GroupActions","onSetAll","span","titleCase","initCollapsed","ul","li","checked","id","label","onToggle","e","target","tooltip","description","button","aria-label","onClick","stopPropagation","title","aria-hidden","replace","_","sep","ch","toUpperCase"],"mappings":"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"}
@@ -18,15 +18,15 @@ import { getPluginConfig } from '../utils/getPluginConfig.js';
18
18
  items: pluginConfig.items.filter((item)=>{
19
19
  switch(item.type){
20
20
  case 'collectionTool':
21
- return apiKeyDoc.access.collections?.[item.collectionSlug]?.[item.key] !== false;
21
+ return apiKeyDoc.access.collections?.[item.collectionSlug]?.[item.configKey] !== false;
22
22
  case 'globalTool':
23
- return apiKeyDoc.access.globals?.[item.globalSlug]?.[item.key] !== false;
23
+ return apiKeyDoc.access.globals?.[item.globalSlug]?.[item.configKey] !== false;
24
24
  case 'prompt':
25
- return apiKeyDoc.access.prompts?.[item.key] !== false;
25
+ return apiKeyDoc.access.prompts?.[item.configKey] !== false;
26
26
  case 'resource':
27
- return apiKeyDoc.access.resources?.[item.key] !== false;
27
+ return apiKeyDoc.access.resources?.[item.configKey] !== false;
28
28
  case 'tool':
29
- return apiKeyDoc.access.tools?.[item.key] !== false;
29
+ return apiKeyDoc.access.tools?.[item.configKey] !== false;
30
30
  }
31
31
  }),
32
32
  overrideAccess: typeof apiKeyDoc.overrideAccess === 'boolean' ? apiKeyDoc.overrideAccess : false,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoint/access.ts"],"sourcesContent":["import type { DefaultDocumentIDType, PayloadRequest, TypedUser } from 'payload'\n\nimport crypto from 'crypto'\nimport { UnauthorizedError } from 'payload'\n\nimport type { AuthorizedMCP, MCPAPIKeysDoc } from '../types.js'\n\nimport { getLogger } from '../utils/getLogger.js'\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\n\n/**\n * Resolves the API key (or dev-mode session) and returns the items the caller\n * may use. Denied items are dropped from the array.\n */\nexport const getAuthorizedMCP: (args: { req: PayloadRequest }) => Promise<AuthorizedMCP> = async ({\n req,\n}) => {\n const logger = getLogger({ payload: req.payload })\n const pluginConfig = getPluginConfig({ config: req.payload.config })\n\n const authHeader = req.headers.get('Authorization')\n const hasBearerToken = authHeader?.startsWith('Bearer ')\n\n const buildAuthorized = (apiKeyDoc: MCPAPIKeysDoc): AuthorizedMCP => ({\n items: pluginConfig.items.filter((item) => {\n switch (item.type) {\n case 'collectionTool':\n return apiKeyDoc.access.collections?.[item.collectionSlug]?.[item.key] !== false\n case 'globalTool':\n return apiKeyDoc.access.globals?.[item.globalSlug]?.[item.key] !== false\n case 'prompt':\n return apiKeyDoc.access.prompts?.[item.key] !== false\n case 'resource':\n return apiKeyDoc.access.resources?.[item.key] !== false\n case 'tool':\n return apiKeyDoc.access.tools?.[item.key] !== false\n }\n }),\n overrideAccess:\n typeof apiKeyDoc.overrideAccess === 'boolean' ? apiKeyDoc.overrideAccess : false,\n user: apiKeyDoc.user,\n })\n\n if (pluginConfig.overrideAuth) {\n return await pluginConfig.overrideAuth({\n getAPIKeyDoc: (overrideApiKey) => getAPIKeyDoc({ logger, overrideApiKey, pluginConfig, req }),\n getAuthorizedMCP: ({ apiKeyDoc }) => buildAuthorized(apiKeyDoc),\n pluginConfig,\n req,\n })\n }\n\n if (process.env.NODE_ENV === 'development' && !hasBearerToken) {\n logger.info('Dev mode: skipping API key check, using session user')\n return buildAuthorized({\n id: -1,\n access: {},\n overrideAccess: true,\n user: req.user ?? null,\n })\n }\n\n return buildAuthorized(await getAPIKeyDoc({ logger, pluginConfig, req }))\n}\n\nconst getAPIKeyDoc = async ({\n logger,\n overrideApiKey,\n pluginConfig,\n req,\n}: {\n logger: ReturnType<typeof getLogger>\n overrideApiKey?: string\n pluginConfig: ReturnType<typeof getPluginConfig>\n req: PayloadRequest\n}): Promise<MCPAPIKeysDoc> => {\n const authHeader = req.headers.get('Authorization')\n const hasBearerToken = authHeader?.startsWith('Bearer ')\n\n const apiKey =\n overrideApiKey ?? (hasBearerToken ? authHeader?.replace('Bearer ', '').trim() || null : null)\n\n if (!apiKey) {\n throw new UnauthorizedError()\n }\n\n const sha256APIKeyIndex = crypto\n .createHmac('sha256', req.payload.secret)\n .update(apiKey)\n .digest('hex')\n\n const doc = await req.payload.db.findOne<MCPAPIKeysDoc>({\n collection: 'payload-mcp-api-keys',\n req,\n where: {\n apiKeyIndex: { equals: sha256APIKeyIndex },\n },\n })\n\n if (!doc || !doc.user) {\n throw new UnauthorizedError()\n }\n\n logger.info('API Key is valid')\n\n const userRef = doc.user\n const userID =\n typeof userRef === 'object' && userRef !== null && 'id' in userRef\n ? userRef.id\n : (userRef as unknown as DefaultDocumentIDType)\n\n const user = (await req.payload.findByID({\n id: userID,\n collection: pluginConfig.userCollection,\n depth: 0,\n disableErrors: true,\n req,\n })) as null | TypedUser\n\n if (!user) {\n throw new UnauthorizedError()\n }\n\n return {\n ...doc,\n user: {\n ...user,\n _strategy: 'mcp-api-key' as const,\n collection: pluginConfig.userCollection,\n },\n }\n}\n"],"names":["crypto","UnauthorizedError","getLogger","getPluginConfig","getAuthorizedMCP","req","logger","payload","pluginConfig","config","authHeader","headers","get","hasBearerToken","startsWith","buildAuthorized","apiKeyDoc","items","filter","item","type","access","collections","collectionSlug","key","globals","globalSlug","prompts","resources","tools","overrideAccess","user","overrideAuth","getAPIKeyDoc","overrideApiKey","process","env","NODE_ENV","info","id","apiKey","replace","trim","sha256APIKeyIndex","createHmac","secret","update","digest","doc","db","findOne","collection","where","apiKeyIndex","equals","userRef","userID","findByID","userCollection","depth","disableErrors","_strategy"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoint/access.ts"],"sourcesContent":["import type { DefaultDocumentIDType, PayloadRequest, TypedUser } from 'payload'\n\nimport crypto from 'crypto'\nimport { UnauthorizedError } from 'payload'\n\nimport type { AuthorizedMCP, MCPAPIKeysDoc } from '../types.js'\n\nimport { getLogger } from '../utils/getLogger.js'\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\n\n/**\n * Resolves the API key (or dev-mode session) and returns the items the caller\n * may use. Denied items are dropped from the array.\n */\nexport const getAuthorizedMCP: (args: { req: PayloadRequest }) => Promise<AuthorizedMCP> = async ({\n req,\n}) => {\n const logger = getLogger({ payload: req.payload })\n const pluginConfig = getPluginConfig({ config: req.payload.config })\n\n const authHeader = req.headers.get('Authorization')\n const hasBearerToken = authHeader?.startsWith('Bearer ')\n\n const buildAuthorized = (apiKeyDoc: MCPAPIKeysDoc): AuthorizedMCP => ({\n items: pluginConfig.items.filter((item) => {\n switch (item.type) {\n case 'collectionTool':\n return apiKeyDoc.access.collections?.[item.collectionSlug]?.[item.configKey] !== false\n case 'globalTool':\n return apiKeyDoc.access.globals?.[item.globalSlug]?.[item.configKey] !== false\n case 'prompt':\n return apiKeyDoc.access.prompts?.[item.configKey] !== false\n case 'resource':\n return apiKeyDoc.access.resources?.[item.configKey] !== false\n case 'tool':\n return apiKeyDoc.access.tools?.[item.configKey] !== false\n }\n }),\n overrideAccess:\n typeof apiKeyDoc.overrideAccess === 'boolean' ? apiKeyDoc.overrideAccess : false,\n user: apiKeyDoc.user,\n })\n\n if (pluginConfig.overrideAuth) {\n return await pluginConfig.overrideAuth({\n getAPIKeyDoc: (overrideApiKey) => getAPIKeyDoc({ logger, overrideApiKey, pluginConfig, req }),\n getAuthorizedMCP: ({ apiKeyDoc }) => buildAuthorized(apiKeyDoc),\n pluginConfig,\n req,\n })\n }\n\n if (process.env.NODE_ENV === 'development' && !hasBearerToken) {\n logger.info('Dev mode: skipping API key check, using session user')\n return buildAuthorized({\n id: -1,\n access: {},\n overrideAccess: true,\n user: req.user ?? null,\n })\n }\n\n return buildAuthorized(await getAPIKeyDoc({ logger, pluginConfig, req }))\n}\n\nconst getAPIKeyDoc = async ({\n logger,\n overrideApiKey,\n pluginConfig,\n req,\n}: {\n logger: ReturnType<typeof getLogger>\n overrideApiKey?: string\n pluginConfig: ReturnType<typeof getPluginConfig>\n req: PayloadRequest\n}): Promise<MCPAPIKeysDoc> => {\n const authHeader = req.headers.get('Authorization')\n const hasBearerToken = authHeader?.startsWith('Bearer ')\n\n const apiKey =\n overrideApiKey ?? (hasBearerToken ? authHeader?.replace('Bearer ', '').trim() || null : null)\n\n if (!apiKey) {\n throw new UnauthorizedError()\n }\n\n const sha256APIKeyIndex = crypto\n .createHmac('sha256', req.payload.secret)\n .update(apiKey)\n .digest('hex')\n\n const doc = await req.payload.db.findOne<MCPAPIKeysDoc>({\n collection: 'payload-mcp-api-keys',\n req,\n where: {\n apiKeyIndex: { equals: sha256APIKeyIndex },\n },\n })\n\n if (!doc || !doc.user) {\n throw new UnauthorizedError()\n }\n\n logger.info('API Key is valid')\n\n const userRef = doc.user\n const userID =\n typeof userRef === 'object' && userRef !== null && 'id' in userRef\n ? userRef.id\n : (userRef as unknown as DefaultDocumentIDType)\n\n const user = (await req.payload.findByID({\n id: userID,\n collection: pluginConfig.userCollection,\n depth: 0,\n disableErrors: true,\n req,\n })) as null | TypedUser\n\n if (!user) {\n throw new UnauthorizedError()\n }\n\n return {\n ...doc,\n user: {\n ...user,\n _strategy: 'mcp-api-key' as const,\n collection: pluginConfig.userCollection,\n },\n }\n}\n"],"names":["crypto","UnauthorizedError","getLogger","getPluginConfig","getAuthorizedMCP","req","logger","payload","pluginConfig","config","authHeader","headers","get","hasBearerToken","startsWith","buildAuthorized","apiKeyDoc","items","filter","item","type","access","collections","collectionSlug","configKey","globals","globalSlug","prompts","resources","tools","overrideAccess","user","overrideAuth","getAPIKeyDoc","overrideApiKey","process","env","NODE_ENV","info","id","apiKey","replace","trim","sha256APIKeyIndex","createHmac","secret","update","digest","doc","db","findOne","collection","where","apiKeyIndex","equals","userRef","userID","findByID","userCollection","depth","disableErrors","_strategy"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"buildMcpServer.d.ts","sourceRoot":"","sources":["../../src/mcp/buildMcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,8BAA8B,CAAA;AAC5E,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAE3E,OAAO,KAAK,EACV,aAAa,EAIb,wBAAwB,EACzB,MAAM,aAAa,CAAA;AAiBpB;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,uCAI5B;IACD,aAAa,EAAE,aAAa,CAAA;IAC5B,YAAY,EAAE,wBAAwB,CAAA;IACtC,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,SAmLH,CAAA"}
1
+ {"version":3,"file":"buildMcpServer.d.ts","sourceRoot":"","sources":["../../src/mcp/buildMcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,8BAA8B,CAAA;AAC5E,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAGvD,OAAO,KAAK,EACV,aAAa,EAMb,wBAAwB,EAEzB,MAAM,aAAa,CAAA;AAKpB;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,uCAI5B;IACD,aAAa,EAAE,aAAa,CAAA;IAC5B,YAAY,EAAE,wBAAwB,CAAA;IACtC,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,SAuLH,CAAA"}
@@ -1,14 +1,8 @@
1
1
  import { McpServer } from '@modelcontextprotocol/server';
2
- import { APIError, configToJSONSchema } from 'payload';
3
- import { toCamelCase } from '../utils/camelCase.js';
2
+ import { APIError } from 'payload';
3
+ import { z } from 'zod';
4
4
  import { getLogger } from '../utils/getLogger.js';
5
- import { getCollectionVirtualFieldNames, getGlobalVirtualFieldNames } from '../utils/getVirtualFieldNames.js';
6
- import { removeVirtualFieldsFromSchema } from '../utils/schemaConversion/removeVirtualFieldsFromSchema.js';
7
5
  import { toStandardSchema } from '../utils/toStandardSchema.js';
8
- /** `findPosts`, `updateSiteSettings` — auto-prefixed wire name for collection/global tools. */ const wireName = (key, slug)=>{
9
- const camel = toCamelCase(slug);
10
- return `${key}${camel.charAt(0).toUpperCase()}${camel.slice(1)}`;
11
- };
12
6
  /**
13
7
  * Transport-agnostic core: registers every authorized MCP item onto a fresh
14
8
  * `McpServer` and returns it. The caller is responsible for picking a transport
@@ -36,72 +30,84 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
36
30
  const { doc: _doc, ...rest } = overridden;
37
31
  return rest;
38
32
  };
39
- const configSchema = configToJSONSchema(req.payload.config, req.payload.db.defaultIDType, req.i18n, {
40
- forceInlineBlocks: true
41
- });
33
+ /**
34
+ * Runs a collection/global tool call:
35
+ * - reads `collectionSlug` / `globalSlug` from the input
36
+ * - runs access control: errors if `authorizedMCP.items` has no entry for this tool + slug
37
+ * - runs the tool handler and finalizes its response
38
+ */ const callEntityTool = async ({ input, item, serverContext })=>{
39
+ const entity = item.type === 'collectionTool' ? 'collection' : 'global';
40
+ const slugKey = item.type === 'collectionTool' ? 'collectionSlug' : 'globalSlug';
41
+ const toolInput = input ?? {};
42
+ const slug = toolInput[slugKey];
43
+ if (!slug) {
44
+ return {
45
+ content: [
46
+ {
47
+ type: 'text',
48
+ text: `Error: "${item.mcpName}" requires ${slugKey}. Use getConfigInfo to inspect ${entity} slugs.`
49
+ }
50
+ ],
51
+ isError: true
52
+ };
53
+ }
54
+ const match = authorizedMCP.items.find((candidate)=>candidate.type === item.type && candidate.mcpName === item.mcpName && (candidate.type === 'collectionTool' ? candidate.collectionSlug === slug : candidate.type === 'globalTool' && candidate.globalSlug === slug));
55
+ if (!match) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: `Error: MCP access to "${item.mcpName}" is not enabled for ${entity} "${slug}"`
61
+ }
62
+ ],
63
+ isError: true
64
+ };
65
+ }
66
+ const handlerArgs = {
67
+ authorizedMCP,
68
+ input: toolInput,
69
+ req,
70
+ serverContext
71
+ };
72
+ const response = await (match.type === 'collectionTool' ? match.tool.handler({
73
+ ...handlerArgs,
74
+ collectionSlug: slug
75
+ }) : match.tool.handler({
76
+ ...handlerArgs,
77
+ globalSlug: slug
78
+ }));
79
+ return finalizeToolResponse(response, match.tool.overrideResponse);
80
+ };
42
81
  try {
82
+ const registeredEntityTools = new Set();
43
83
  for (const item of authorizedMCP.items){
44
84
  switch(item.type){
45
85
  case 'collectionTool':
46
- {
47
- const tool = item.tool;
48
- const name = wireName(item.key, item.collectionSlug);
49
- let inputSchema = tool.input;
50
- if (typeof inputSchema === 'function') {
51
- const raw = configSchema.$defs?.[item.collectionSlug];
52
- if (!raw) {
53
- throw new APIError(`Collection schema not found for slug: ${item.collectionSlug}`, 500);
54
- }
55
- const collectionSchema = removeVirtualFieldsFromSchema(JSON.parse(JSON.stringify(raw)), getCollectionVirtualFieldNames(req.payload.config, item.collectionSlug));
56
- inputSchema = inputSchema({
57
- collectionSchema
58
- });
59
- }
60
- server.registerTool(name, {
61
- description: tool.description,
62
- inputSchema: inputSchema ? toStandardSchema(inputSchema) : undefined
63
- }, async (input, ctx)=>finalizeToolResponse(await tool.handler({
64
- authorizedMCP,
65
- collectionSlug: item.collectionSlug,
66
- input: input ?? {},
67
- req,
68
- serverContext: ctx
69
- }), tool.overrideResponse));
70
- logger.info(`✅ Tool: ${name} Registered.`);
71
- break;
72
- }
73
86
  case 'globalTool':
74
87
  {
75
- const tool = item.tool;
76
- const name = wireName(item.key, item.globalSlug);
77
- let inputSchema = tool.input;
78
- if (typeof inputSchema === 'function') {
79
- const raw = configSchema.$defs?.[item.globalSlug];
80
- if (!raw) {
81
- throw new APIError(`Global schema not found for slug: ${item.globalSlug}`, 500);
82
- }
83
- const globalSchema = removeVirtualFieldsFromSchema(JSON.parse(JSON.stringify(raw)), getGlobalVirtualFieldNames(req.payload.config, item.globalSlug));
84
- inputSchema = inputSchema({
85
- globalSchema
86
- });
88
+ if (registeredEntityTools.has(item.mcpName)) {
89
+ break;
87
90
  }
88
- server.registerTool(name, {
89
- description: tool.description,
90
- inputSchema: inputSchema ? toStandardSchema(inputSchema) : undefined
91
- }, async (input, ctx)=>finalizeToolResponse(await tool.handler({
92
- authorizedMCP,
93
- globalSlug: item.globalSlug,
94
- input: input ?? {},
95
- req,
91
+ registeredEntityTools.add(item.mcpName);
92
+ const inputSchema = withSlugInput({
93
+ name: item.type === 'collectionTool' ? 'collectionSlug' : 'globalSlug',
94
+ input: item.tool.input
95
+ });
96
+ server.registerTool(item.mcpName, {
97
+ description: item.tool.description,
98
+ inputSchema: toStandardSchema(inputSchema)
99
+ }, async (input, ctx)=>callEntityTool({
100
+ input,
101
+ item,
96
102
  serverContext: ctx
97
- }), tool.overrideResponse));
98
- logger.info(`✅ Tool: ${name} Registered.`);
103
+ }));
104
+ logger.info(`✅ Tool: ${item.mcpName} Registered.`);
99
105
  break;
100
106
  }
101
107
  case 'prompt':
102
108
  {
103
109
  const prompt = item.prompt;
104
- server.registerPrompt(item.key, {
110
+ server.registerPrompt(item.mcpName, {
105
111
  argsSchema: prompt.argsSchema ? toStandardSchema(prompt.argsSchema) : undefined,
106
112
  description: prompt.description,
107
113
  title: prompt.title
@@ -116,7 +122,7 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
116
122
  case 'resource':
117
123
  {
118
124
  const resource = item.resource;
119
- server.registerResource(item.key, // @ts-expect-error - Overload type ambiguity (string OR ResourceTemplate is valid)
125
+ server.registerResource(item.mcpName, // @ts-expect-error - Overload type ambiguity (string OR ResourceTemplate is valid)
120
126
  resource.uri, {
121
127
  description: resource.description,
122
128
  mimeType: resource.mimeType,
@@ -140,7 +146,7 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
140
146
  case 'tool':
141
147
  {
142
148
  const tool = item.tool;
143
- server.registerTool(item.key, {
149
+ server.registerTool(item.mcpName, {
144
150
  description: tool.description,
145
151
  inputSchema: tool.input ? toStandardSchema(tool.input) : undefined
146
152
  }, async (input, ctx)=>finalizeToolResponse(await tool.handler({
@@ -149,7 +155,7 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
149
155
  req,
150
156
  serverContext: ctx
151
157
  }), tool.overrideResponse));
152
- logger.info(`✅ Tool: ${item.key} Registered.`);
158
+ logger.info(`✅ Tool: ${item.mcpName} Registered.`);
153
159
  break;
154
160
  }
155
161
  }
@@ -159,5 +165,35 @@ import { toStandardSchema } from '../utils/toStandardSchema.js';
159
165
  }
160
166
  return server;
161
167
  };
168
+ const withSlugInput = ({ name, input })=>{
169
+ const description = name === 'collectionSlug' ? 'The collection slug' : 'The global slug';
170
+ const slugSchema = z.string().describe(description);
171
+ if (!input) {
172
+ return z.object({
173
+ [name]: slugSchema
174
+ });
175
+ }
176
+ if (input instanceof z.ZodObject) {
177
+ return input.extend({
178
+ [name]: slugSchema
179
+ });
180
+ }
181
+ const schema = input;
182
+ return {
183
+ ...schema,
184
+ type: 'object',
185
+ properties: {
186
+ ...schema.properties,
187
+ [name]: {
188
+ type: 'string',
189
+ description
190
+ }
191
+ },
192
+ required: Array.from(new Set([
193
+ name,
194
+ ...schema.required ?? []
195
+ ]))
196
+ };
197
+ };
162
198
 
163
199
  //# sourceMappingURL=buildMcpServer.js.map