@payloadcms/plugin-mcp 3.87.1 → 3.89.0-internal.4b0e9b6

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 (50) hide show
  1. package/dist/collections/createApiKeysCollection.d.ts.map +1 -1
  2. package/dist/collections/createApiKeysCollection.js +25 -0
  3. package/dist/collections/createApiKeysCollection.js.map +1 -1
  4. package/dist/endpoints/mcp.d.ts +1 -1
  5. package/dist/endpoints/mcp.d.ts.map +1 -1
  6. package/dist/endpoints/mcp.js +14 -61
  7. package/dist/endpoints/mcp.js.map +1 -1
  8. package/dist/endpoints/resolveAccessSettings.d.ts +10 -0
  9. package/dist/endpoints/resolveAccessSettings.d.ts.map +1 -0
  10. package/dist/endpoints/resolveAccessSettings.js +44 -0
  11. package/dist/endpoints/resolveAccessSettings.js.map +1 -0
  12. package/dist/endpoints/withRestoredWebGlobals.d.ts +5 -0
  13. package/dist/endpoints/withRestoredWebGlobals.d.ts.map +1 -0
  14. package/dist/endpoints/withRestoredWebGlobals.js +23 -0
  15. package/dist/endpoints/withRestoredWebGlobals.js.map +1 -0
  16. package/dist/mcp/getMcpHandler.d.ts +1 -1
  17. package/dist/mcp/getMcpHandler.d.ts.map +1 -1
  18. package/dist/mcp/getMcpHandler.js +23 -23
  19. package/dist/mcp/getMcpHandler.js.map +1 -1
  20. package/dist/mcp/tools/auth/login.d.ts.map +1 -1
  21. package/dist/mcp/tools/auth/login.js +4 -5
  22. package/dist/mcp/tools/auth/login.js.map +1 -1
  23. package/dist/mcp/tools/global/find.d.ts.map +1 -1
  24. package/dist/mcp/tools/global/find.js +2 -0
  25. package/dist/mcp/tools/global/find.js.map +1 -1
  26. package/dist/mcp/tools/global/update.d.ts.map +1 -1
  27. package/dist/mcp/tools/global/update.js +2 -0
  28. package/dist/mcp/tools/global/update.js.map +1 -1
  29. package/dist/mcp/tools/job/run.d.ts.map +1 -1
  30. package/dist/mcp/tools/job/run.js +2 -0
  31. package/dist/mcp/tools/job/run.js.map +1 -1
  32. package/dist/mcp/tools/resource/update.d.ts.map +1 -1
  33. package/dist/mcp/tools/resource/update.js +3 -17
  34. package/dist/mcp/tools/resource/update.js.map +1 -1
  35. package/dist/mcp/tools/schemas.d.ts +0 -12
  36. package/dist/mcp/tools/schemas.d.ts.map +1 -1
  37. package/dist/mcp/tools/schemas.js +1 -5
  38. package/dist/mcp/tools/schemas.js.map +1 -1
  39. package/package.json +3 -3
  40. package/src/collections/createApiKeysCollection.ts +27 -1
  41. package/src/endpoints/mcp.ts +12 -77
  42. package/src/endpoints/resolveAccessSettings.ts +74 -0
  43. package/src/endpoints/withRestoredWebGlobals.ts +19 -0
  44. package/src/mcp/getMcpHandler.ts +32 -31
  45. package/src/mcp/tools/auth/login.ts +4 -12
  46. package/src/mcp/tools/global/find.ts +2 -0
  47. package/src/mcp/tools/global/update.ts +2 -0
  48. package/src/mcp/tools/job/run.ts +2 -0
  49. package/src/mcp/tools/resource/update.ts +0 -16
  50. package/src/mcp/tools/schemas.ts +0 -16
@@ -1 +1 @@
1
- {"version":3,"file":"createApiKeysCollection.d.ts","sourceRoot":"","sources":["../../src/collections/createApiKeysCollection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,SAAS,CAAA;AAE/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAKlD,eAAO,MAAM,uBAAuB,gBACrB,eAAe,CAAC,aAAa,CAAC,WAClC,eAAe,CAAC,SAAS,CAAC,eACtB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,iCACtC,WAAW,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,6BACzD,eAAe,KAC7B,gBAuWF,CAAA"}
1
+ {"version":3,"file":"createApiKeysCollection.d.ts","sourceRoot":"","sources":["../../src/collections/createApiKeysCollection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAyC,MAAM,SAAS,CAAA;AAEtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAKlD,eAAO,MAAM,uBAAuB,gBACrB,eAAe,CAAC,aAAa,CAAC,WAClC,eAAe,CAAC,SAAS,CAAC,eACtB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,iCACtC,WAAW,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,6BACzD,eAAe,KAC7B,gBAiYF,CAAA"}
@@ -38,8 +38,24 @@ export const createAPIKeysCollection = (collections, globals, customTools = [],
38
38
  };
39
39
  }) || [];
40
40
  const userCollection = pluginOptions.userCollection;
41
+ const isAuthenticated = ({ req })=>Boolean(req.user && req.user.collection === userCollection);
42
+ const restrictToOwnKeys = ({ req })=>req.user && req.user.collection === userCollection ? {
43
+ user: {
44
+ equals: req.user.id
45
+ }
46
+ } : false;
41
47
  return {
42
48
  slug: 'payload-mcp-api-keys',
49
+ access: {
50
+ // Fail-safe default: any authenticated user may manage only their OWN keys.
51
+ // Cross-user key issuance is disabled by default (see the `user` field below)
52
+ // and must be granted explicitly via `overrideApiKeyCollection`.
53
+ create: isAuthenticated,
54
+ delete: restrictToOwnKeys,
55
+ read: restrictToOwnKeys,
56
+ unlock: restrictToOwnKeys,
57
+ update: restrictToOwnKeys
58
+ },
43
59
  admin: {
44
60
  description: 'API keys control which collections, resources, tools, and prompts MCP clients can access',
45
61
  group: 'MCP',
@@ -53,9 +69,18 @@ export const createAPIKeysCollection = (collections, globals, customTools = [],
53
69
  {
54
70
  name: 'user',
55
71
  type: 'relationship',
72
+ access: {
73
+ // Bind new keys to the creating user and prevent re-binding on update.
74
+ // A key acts as its associated user, so allowing arbitrary assignment lets
75
+ // any key manager impersonate anyone. Grant these via `overrideApiKeyCollection`
76
+ // to let trusted administrators issue keys on behalf of other users.
77
+ create: ()=>false,
78
+ update: ()=>false
79
+ },
56
80
  admin: {
57
81
  description: 'The user that the API key is associated with.'
58
82
  },
83
+ defaultValue: ({ req })=>req.user && req.user.collection === userCollection ? req.user.id : undefined,
59
84
  relationTo: userCollection,
60
85
  required: true
61
86
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/createApiKeysCollection.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug } from 'payload'\n\nimport type { MCPPluginConfig } from '../types.js'\n\nimport { toCamelCase } from '../utils/camelCase.js'\nimport { createApiKeyFields } from '../utils/createApiKeyFields.js'\n\nexport const createAPIKeysCollection = (\n collections: MCPPluginConfig['collections'],\n globals: MCPPluginConfig['globals'],\n customTools: Array<{ description: string; name: string }> = [],\n experimentalTools: NonNullable<MCPPluginConfig['experimental']>['tools'] = {},\n pluginOptions: MCPPluginConfig,\n): CollectionConfig => {\n const customToolsFields = customTools.map((tool) => {\n const camelCasedName = toCamelCase(tool.name)\n return {\n name: camelCasedName,\n type: 'checkbox' as const,\n admin: {\n description: tool.description,\n },\n defaultValue: true,\n label: camelCasedName,\n }\n })\n\n const customResourceFields =\n pluginOptions.mcp?.resources?.map((resource) => {\n const camelCasedName = toCamelCase(resource.name)\n return {\n name: camelCasedName,\n type: 'checkbox' as const,\n admin: {\n description: resource.description,\n },\n defaultValue: true,\n label: camelCasedName,\n }\n }) || []\n\n const customPromptFields =\n pluginOptions.mcp?.prompts?.map((prompt) => {\n const camelCasedName = toCamelCase(prompt.name)\n return {\n name: camelCasedName,\n type: 'checkbox' as const,\n admin: {\n description: prompt.description,\n },\n defaultValue: true,\n label: camelCasedName,\n }\n }) || []\n\n const userCollection = pluginOptions.userCollection\n\n return {\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: {\n description: 'The user that the API key is associated with.',\n },\n relationTo: userCollection as CollectionSlug,\n required: true,\n },\n {\n name: 'label',\n type: 'text',\n admin: {\n description: 'A useful label for the API key.',\n },\n },\n {\n name: 'description',\n type: 'text',\n admin: {\n description: 'The purpose of the API key.',\n },\n },\n\n ...createApiKeyFields({\n config: collections,\n configType: 'collection',\n }),\n\n ...createApiKeyFields({\n config: globals,\n configType: 'global',\n }),\n\n ...(customTools.length > 0\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to tools',\n position: 'sidebar' as const,\n },\n fields: [\n {\n name: 'payload-mcp-tool',\n type: 'group' as const,\n fields: customToolsFields,\n label: false as const,\n },\n ],\n label: 'Tools',\n },\n ]\n : []),\n\n ...(pluginOptions.mcp?.resources && pluginOptions.mcp?.resources.length > 0\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to resources',\n position: 'sidebar' as const,\n },\n fields: [\n {\n name: 'payload-mcp-resource',\n type: 'group' as const,\n fields: customResourceFields,\n label: false as const,\n },\n ],\n label: 'Resources',\n },\n ]\n : []),\n\n ...(pluginOptions.mcp?.prompts && pluginOptions.mcp?.prompts.length > 0\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to prompts',\n position: 'sidebar' as const,\n },\n fields: [\n {\n name: 'payload-mcp-prompt',\n type: 'group' as const,\n fields: customPromptFields,\n label: false as const,\n },\n ],\n label: 'Prompts',\n },\n ]\n : []),\n\n // Experimental Tools\n ...(process.env.NODE_ENV === 'development' &&\n (experimentalTools?.collections?.enabled ||\n experimentalTools?.jobs?.enabled ||\n experimentalTools?.config?.enabled ||\n experimentalTools?.auth?.enabled)\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to experimental tools',\n position: 'sidebar' as const,\n },\n fields: [\n ...(experimentalTools?.collections?.enabled\n ? [\n {\n name: 'collections',\n type: 'group' as const,\n fields: [\n {\n name: 'find',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to find and list Payload collections with optional content and document counts.',\n },\n defaultValue: false,\n },\n {\n name: 'create',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to create new Payload collections with specified fields and configuration.',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to update existing Payload collections with new fields, modifications, or configuration changes.',\n },\n defaultValue: false,\n },\n {\n name: 'delete',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to delete Payload collections and optionally update the configuration.',\n },\n defaultValue: false,\n },\n ],\n },\n ]\n : []),\n ...(experimentalTools?.jobs?.enabled\n ? [\n {\n name: 'jobs',\n type: 'group' as const,\n fields: [\n {\n name: 'create',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to create new Payload jobs (tasks and workflows) with custom schemas and configuration.',\n },\n defaultValue: false,\n },\n {\n name: 'run',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to execute Payload jobs with custom input data and queue options.',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to update existing Payload jobs with new schemas, configuration, or handler code.',\n },\n defaultValue: false,\n },\n ],\n },\n ]\n : []),\n ...(experimentalTools?.config?.enabled\n ? [\n {\n name: 'config',\n type: 'group' as const,\n fields: [\n {\n name: 'find',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to read and display a Payload configuration file.',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to update a Payload configuration file with various modifications.',\n },\n defaultValue: false,\n },\n ],\n },\n ]\n : []),\n ...(experimentalTools?.auth?.enabled\n ? [\n {\n name: 'auth',\n type: 'group' as const,\n fields: [\n {\n name: 'auth',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to check authentication status for a user by setting custom headers. (e.g. {\"Authorization\": \"Bearer <token>\"})',\n },\n defaultValue: false,\n label: 'Check Auth Status',\n },\n {\n name: 'login',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to authenticate a user with email and password.',\n },\n defaultValue: false,\n label: 'User Login',\n },\n {\n name: 'verify',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to verify a user email with a verification token.',\n },\n defaultValue: false,\n label: 'Email Verification',\n },\n {\n name: 'resetPassword',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to reset a user password with a reset token.',\n },\n defaultValue: false,\n label: 'Reset Password',\n },\n {\n name: 'forgotPassword',\n type: 'checkbox' as const,\n admin: {\n description: 'Allow LLMs to send a password reset email to a user.',\n },\n defaultValue: false,\n label: 'Forgot Password',\n },\n {\n name: 'unlock',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to unlock a user account that has been locked due to failed login attempts.',\n },\n defaultValue: false,\n label: 'Unlock Account',\n },\n ],\n },\n ]\n : []),\n ],\n label: 'Experimental Tools',\n },\n ]\n : []),\n ],\n labels: {\n plural: 'API Keys',\n singular: 'API Key',\n },\n }\n}\n"],"names":["toCamelCase","createApiKeyFields","createAPIKeysCollection","collections","globals","customTools","experimentalTools","pluginOptions","customToolsFields","map","tool","camelCasedName","name","type","admin","description","defaultValue","label","customResourceFields","mcp","resources","resource","customPromptFields","prompts","prompt","userCollection","slug","group","useAsTitle","auth","disableLocalStrategy","useAPIKey","fields","relationTo","required","config","configType","length","position","process","env","NODE_ENV","enabled","jobs","labels","plural","singular"],"mappings":"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NC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;oCACA;wCACEJ,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;iCACD;4BACH;yBACD,GACD,EAAE;2BACFV,mBAAmBqC,MAAMD,UACzB;4BACE;gCACE9B,MAAM;gCACNC,MAAM;gCACNmB,QAAQ;oCACN;wCACEpB,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;oCACA;wCACEJ,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;oCACA;wCACEJ,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;iCACD;4BACH;yBACD,GACD,EAAE;2BACFV,mBAAmB6B,QAAQO,UAC3B;4BACE;gCACE9B,MAAM;gCACNC,MAAM;gCACNmB,QAAQ;oCACN;wCACEpB,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;oCACA;wCACEJ,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;iCACD;4BACH;yBACD,GACD,EAAE;2BACFV,mBAAmBuB,MAAMa,UACzB;4BACE;gCACE9B,MAAM;gCACNC,MAAM;gCACNmB,QAAQ;oCACN;wCACEpB,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aAAa;wCACf;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;iCACD;4BACH;yBACD,GACD,EAAE;qBACP;oBACDA,OAAO;gBACT;aACD,GACD,EAAE;SACP;QACD2B,QAAQ;YACNC,QAAQ;YACRC,UAAU;QACZ;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/collections/createApiKeysCollection.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, PayloadRequest, Where } from 'payload'\n\nimport type { MCPPluginConfig } from '../types.js'\n\nimport { toCamelCase } from '../utils/camelCase.js'\nimport { createApiKeyFields } from '../utils/createApiKeyFields.js'\n\nexport const createAPIKeysCollection = (\n collections: MCPPluginConfig['collections'],\n globals: MCPPluginConfig['globals'],\n customTools: Array<{ description: string; name: string }> = [],\n experimentalTools: NonNullable<MCPPluginConfig['experimental']>['tools'] = {},\n pluginOptions: MCPPluginConfig,\n): CollectionConfig => {\n const customToolsFields = customTools.map((tool) => {\n const camelCasedName = toCamelCase(tool.name)\n return {\n name: camelCasedName,\n type: 'checkbox' as const,\n admin: {\n description: tool.description,\n },\n defaultValue: true,\n label: camelCasedName,\n }\n })\n\n const customResourceFields =\n pluginOptions.mcp?.resources?.map((resource) => {\n const camelCasedName = toCamelCase(resource.name)\n return {\n name: camelCasedName,\n type: 'checkbox' as const,\n admin: {\n description: resource.description,\n },\n defaultValue: true,\n label: camelCasedName,\n }\n }) || []\n\n const customPromptFields =\n pluginOptions.mcp?.prompts?.map((prompt) => {\n const camelCasedName = toCamelCase(prompt.name)\n return {\n name: camelCasedName,\n type: 'checkbox' as const,\n admin: {\n description: prompt.description,\n },\n defaultValue: true,\n label: camelCasedName,\n }\n }) || []\n\n const userCollection = pluginOptions.userCollection\n\n const isAuthenticated = ({ req }: { req: PayloadRequest }): boolean =>\n Boolean(req.user && req.user.collection === userCollection)\n\n const restrictToOwnKeys = ({ req }: { req: PayloadRequest }): boolean | Where =>\n req.user && req.user.collection === userCollection ? { user: { equals: req.user.id } } : false\n\n return {\n slug: 'payload-mcp-api-keys',\n access: {\n // Fail-safe default: any authenticated user may manage only their OWN keys.\n // Cross-user key issuance is disabled by default (see the `user` field below)\n // and must be granted explicitly via `overrideApiKeyCollection`.\n create: isAuthenticated,\n delete: restrictToOwnKeys,\n read: restrictToOwnKeys,\n unlock: restrictToOwnKeys,\n update: restrictToOwnKeys,\n },\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 access: {\n // Bind new keys to the creating user and prevent re-binding on update.\n // A key acts as its associated user, so allowing arbitrary assignment lets\n // any key manager impersonate anyone. Grant these via `overrideApiKeyCollection`\n // to let trusted administrators issue keys on behalf of other users.\n create: () => false,\n update: () => false,\n },\n admin: {\n description: 'The user that the API key is associated with.',\n },\n defaultValue: ({ req }: { req: PayloadRequest }) =>\n req.user && req.user.collection === userCollection ? req.user.id : undefined,\n relationTo: userCollection as CollectionSlug,\n required: true,\n },\n {\n name: 'label',\n type: 'text',\n admin: {\n description: 'A useful label for the API key.',\n },\n },\n {\n name: 'description',\n type: 'text',\n admin: {\n description: 'The purpose of the API key.',\n },\n },\n\n ...createApiKeyFields({\n config: collections,\n configType: 'collection',\n }),\n\n ...createApiKeyFields({\n config: globals,\n configType: 'global',\n }),\n\n ...(customTools.length > 0\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to tools',\n position: 'sidebar' as const,\n },\n fields: [\n {\n name: 'payload-mcp-tool',\n type: 'group' as const,\n fields: customToolsFields,\n label: false as const,\n },\n ],\n label: 'Tools',\n },\n ]\n : []),\n\n ...(pluginOptions.mcp?.resources && pluginOptions.mcp?.resources.length > 0\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to resources',\n position: 'sidebar' as const,\n },\n fields: [\n {\n name: 'payload-mcp-resource',\n type: 'group' as const,\n fields: customResourceFields,\n label: false as const,\n },\n ],\n label: 'Resources',\n },\n ]\n : []),\n\n ...(pluginOptions.mcp?.prompts && pluginOptions.mcp?.prompts.length > 0\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to prompts',\n position: 'sidebar' as const,\n },\n fields: [\n {\n name: 'payload-mcp-prompt',\n type: 'group' as const,\n fields: customPromptFields,\n label: false as const,\n },\n ],\n label: 'Prompts',\n },\n ]\n : []),\n\n // Experimental Tools\n ...(process.env.NODE_ENV === 'development' &&\n (experimentalTools?.collections?.enabled ||\n experimentalTools?.jobs?.enabled ||\n experimentalTools?.config?.enabled ||\n experimentalTools?.auth?.enabled)\n ? [\n {\n type: 'collapsible' as const,\n admin: {\n description: 'Manage client access to experimental tools',\n position: 'sidebar' as const,\n },\n fields: [\n ...(experimentalTools?.collections?.enabled\n ? [\n {\n name: 'collections',\n type: 'group' as const,\n fields: [\n {\n name: 'find',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to find and list Payload collections with optional content and document counts.',\n },\n defaultValue: false,\n },\n {\n name: 'create',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to create new Payload collections with specified fields and configuration.',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to update existing Payload collections with new fields, modifications, or configuration changes.',\n },\n defaultValue: false,\n },\n {\n name: 'delete',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to delete Payload collections and optionally update the configuration.',\n },\n defaultValue: false,\n },\n ],\n },\n ]\n : []),\n ...(experimentalTools?.jobs?.enabled\n ? [\n {\n name: 'jobs',\n type: 'group' as const,\n fields: [\n {\n name: 'create',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to create new Payload jobs (tasks and workflows) with custom schemas and configuration.',\n },\n defaultValue: false,\n },\n {\n name: 'run',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to execute Payload jobs with custom input data and queue options.',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to update existing Payload jobs with new schemas, configuration, or handler code.',\n },\n defaultValue: false,\n },\n ],\n },\n ]\n : []),\n ...(experimentalTools?.config?.enabled\n ? [\n {\n name: 'config',\n type: 'group' as const,\n fields: [\n {\n name: 'find',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to read and display a Payload configuration file.',\n },\n defaultValue: false,\n },\n {\n name: 'update',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to update a Payload configuration file with various modifications.',\n },\n defaultValue: false,\n },\n ],\n },\n ]\n : []),\n ...(experimentalTools?.auth?.enabled\n ? [\n {\n name: 'auth',\n type: 'group' as const,\n fields: [\n {\n name: 'auth',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to check authentication status for a user by setting custom headers. (e.g. {\"Authorization\": \"Bearer <token>\"})',\n },\n defaultValue: false,\n label: 'Check Auth Status',\n },\n {\n name: 'login',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to authenticate a user with email and password.',\n },\n defaultValue: false,\n label: 'User Login',\n },\n {\n name: 'verify',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to verify a user email with a verification token.',\n },\n defaultValue: false,\n label: 'Email Verification',\n },\n {\n name: 'resetPassword',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to reset a user password with a reset token.',\n },\n defaultValue: false,\n label: 'Reset Password',\n },\n {\n name: 'forgotPassword',\n type: 'checkbox' as const,\n admin: {\n description: 'Allow LLMs to send a password reset email to a user.',\n },\n defaultValue: false,\n label: 'Forgot Password',\n },\n {\n name: 'unlock',\n type: 'checkbox' as const,\n admin: {\n description:\n 'Allow LLMs to unlock a user account that has been locked due to failed login attempts.',\n },\n defaultValue: false,\n label: 'Unlock Account',\n },\n ],\n },\n ]\n : []),\n ],\n label: 'Experimental Tools',\n },\n ]\n : []),\n ],\n labels: {\n plural: 'API Keys',\n singular: 'API Key',\n },\n }\n}\n"],"names":["toCamelCase","createApiKeyFields","createAPIKeysCollection","collections","globals","customTools","experimentalTools","pluginOptions","customToolsFields","map","tool","camelCasedName","name","type","admin","description","defaultValue","label","customResourceFields","mcp","resources","resource","customPromptFields","prompts","prompt","userCollection","isAuthenticated","req","Boolean","user","collection","restrictToOwnKeys","equals","id","slug","access","create","delete","read","unlock","update","group","useAsTitle","auth","disableLocalStrategy","useAPIKey","fields","undefined","relationTo","required","config","configType","length","position","process","env","NODE_ENV","enabled","jobs","labels","plural","singular"],"mappings":"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NC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;oCACA;wCACEJ,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;iCACD;4BACH;yBACD,GACD,EAAE;2BACFV,mBAAmBqC,MAAMc,UACzB;4BACE;gCACE7C,MAAM;gCACNC,MAAM;gCACNiC,QAAQ;oCACN;wCACElC,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aAAa;wCACf;wCACAC,cAAc;wCACdC,OAAO;oCACT;oCACA;wCACEL,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;wCACdC,OAAO;oCACT;iCACD;4BACH;yBACD,GACD,EAAE;qBACP;oBACDA,OAAO;gBACT;aACD,GACD,EAAE;SACP;QACD0C,QAAQ;YACNC,QAAQ;YACRC,UAAU;QACZ;IACF;AACF,EAAC"}
@@ -1,4 +1,4 @@
1
- import { type PayloadHandler } from 'payload';
1
+ import type { PayloadHandler } from 'payload';
2
2
  import type { MCPPluginConfig } from '../types.js';
3
3
  export declare const initializeMCPHandler: (pluginOptions: MCPPluginConfig) => PayloadHandler;
4
4
  //# sourceMappingURL=mcp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/endpoints/mcp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAiD,MAAM,SAAS,CAAA;AAE5F,OAAO,KAAK,EAAqB,eAAe,EAAE,MAAM,aAAa,CAAA;AAKrE,eAAO,MAAM,oBAAoB,kBAAmB,eAAe,mBAkFlE,CAAA"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/endpoints/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAOlD,eAAO,MAAM,oBAAoB,kBAAmB,eAAe,mBAgBlE,CAAA"}
@@ -1,69 +1,22 @@
1
- import crypto from 'crypto';
2
- import { UnauthorizedError } from 'payload';
3
1
  import { createRequestFromPayloadRequest } from '../mcp/createRequest.js';
4
- import { getMCPHandler } from '../mcp/getMcpHandler.js';
2
+ import { getMcpHandler } from '../mcp/getMcpHandler.js';
3
+ import { resolveAccessSettings } from './resolveAccessSettings.js';
4
+ import { withRestoredWebGlobals } from './withRestoredWebGlobals.js';
5
5
  export const initializeMCPHandler = (pluginOptions)=>{
6
6
  const mcpHandler = async (req)=>{
7
- const { payload } = req;
8
- const MCPOptions = pluginOptions.mcp || {};
9
- const MCPHandlerOptions = MCPOptions.handlerOptions || {};
10
- const useVerboseLogs = MCPHandlerOptions.verboseLogs ?? false;
7
+ const useVerboseLogs = pluginOptions.mcp?.handlerOptions?.verboseLogs ?? false;
11
8
  req.payloadAPI = 'MCP';
12
- const getDefaultMcpAccessSettings = async (overrideApiKey)=>{
13
- const apiKey = overrideApiKey ?? req.headers.get('Authorization')?.startsWith('Bearer ') ? req.headers.get('Authorization')?.replace('Bearer ', '').trim() : null;
14
- if (apiKey === null) {
15
- throw new UnauthorizedError();
16
- }
17
- const sha256APIKeyIndex = crypto.createHmac('sha256', payload.secret).update(apiKey || '').digest('hex');
18
- const where = {
19
- apiKeyIndex: {
20
- equals: sha256APIKeyIndex
21
- }
22
- };
23
- const { docs } = await payload.find({
24
- collection: 'payload-mcp-api-keys',
25
- depth: 1,
26
- limit: 1,
27
- pagination: false,
28
- where
29
- });
30
- if (docs.length === 0) {
31
- throw new UnauthorizedError();
32
- }
33
- if (useVerboseLogs) {
34
- payload.logger.info('[payload-mcp] API Key is valid');
35
- }
36
- const user = docs[0]?.user;
37
- user.collection = pluginOptions.userCollection;
38
- user._strategy = 'mcp-api-key';
39
- return docs[0];
40
- };
41
- const mcpAccessSettings = pluginOptions.overrideAuth ? await pluginOptions.overrideAuth(req, getDefaultMcpAccessSettings) : await getDefaultMcpAccessSettings();
42
- // @modelcontextprotocol/sdk's StreamableHTTPServerTransport uses @hono/node-server's
43
- // getRequestListener, which replaces global.Request and global.Response with Hono
44
- // custom classes. Unfortunately, we cannot pass overrideGlobalObjects: false because the option is
45
- // consumed inside the SDK transport and is not exposed to callers.
46
- // Save originals here and restore after the handler resolves so that Next.js
47
- // instanceof Response checks on subsequent route handlers keep working.
48
- const globals = globalThis;
49
- const originalResponse = globals['Response'];
50
- const originalRequest = globals['Request'];
51
- const handler = getMCPHandler(pluginOptions, mcpAccessSettings, req);
52
- const request = createRequestFromPayloadRequest(req);
53
- try {
9
+ const mcpAccessSettings = await resolveAccessSettings({
10
+ pluginOptions,
11
+ req,
12
+ useVerboseLogs
13
+ });
14
+ req.user = mcpAccessSettings.user;
15
+ return await withRestoredWebGlobals(async ()=>{
16
+ const handler = getMcpHandler(pluginOptions, mcpAccessSettings, req);
17
+ const request = createRequestFromPayloadRequest(req);
54
18
  return await handler(request);
55
- } finally{
56
- if (globals['Response'] !== originalResponse) {
57
- Object.defineProperty(globalThis, 'Response', {
58
- value: originalResponse
59
- });
60
- }
61
- if (globals['Request'] !== originalRequest) {
62
- Object.defineProperty(globalThis, 'Request', {
63
- value: originalRequest
64
- });
65
- }
66
- }
19
+ });
67
20
  };
68
21
  return mcpHandler;
69
22
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/mcp.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { type PayloadHandler, type TypedUser, UnauthorizedError, type Where } from 'payload'\n\nimport type { MCPAccessSettings, MCPPluginConfig } from '../types.js'\n\nimport { createRequestFromPayloadRequest } from '../mcp/createRequest.js'\nimport { getMCPHandler } from '../mcp/getMcpHandler.js'\n\nexport const initializeMCPHandler = (pluginOptions: MCPPluginConfig) => {\n const mcpHandler: PayloadHandler = async (req) => {\n const { payload } = req\n const MCPOptions = pluginOptions.mcp || {}\n const MCPHandlerOptions = MCPOptions.handlerOptions || {}\n const useVerboseLogs = MCPHandlerOptions.verboseLogs ?? false\n\n req.payloadAPI = 'MCP' as const\n\n const getDefaultMcpAccessSettings = async (overrideApiKey?: null | string) => {\n const apiKey =\n (overrideApiKey ?? req.headers.get('Authorization')?.startsWith('Bearer '))\n ? req.headers.get('Authorization')?.replace('Bearer ', '').trim()\n : null\n\n if (apiKey === null) {\n throw new UnauthorizedError()\n }\n\n const sha256APIKeyIndex = crypto\n .createHmac('sha256', payload.secret)\n .update(apiKey || '')\n .digest('hex')\n\n const where: Where = {\n apiKeyIndex: {\n equals: sha256APIKeyIndex,\n },\n }\n\n const { docs } = await payload.find({\n collection: 'payload-mcp-api-keys',\n depth: 1,\n limit: 1,\n pagination: false,\n where,\n })\n\n if (docs.length === 0) {\n throw new UnauthorizedError()\n }\n\n if (useVerboseLogs) {\n payload.logger.info('[payload-mcp] API Key is valid')\n }\n\n const user = docs[0]?.user as TypedUser\n user.collection = pluginOptions.userCollection as string\n user._strategy = 'mcp-api-key' as const\n\n return docs[0] as unknown as MCPAccessSettings\n }\n\n const mcpAccessSettings = pluginOptions.overrideAuth\n ? await pluginOptions.overrideAuth(req, getDefaultMcpAccessSettings)\n : await getDefaultMcpAccessSettings()\n\n // @modelcontextprotocol/sdk's StreamableHTTPServerTransport uses @hono/node-server's\n // getRequestListener, which replaces global.Request and global.Response with Hono\n // custom classes. Unfortunately, we cannot pass overrideGlobalObjects: false because the option is\n // consumed inside the SDK transport and is not exposed to callers.\n // Save originals here and restore after the handler resolves so that Next.js\n // instanceof Response checks on subsequent route handlers keep working.\n const globals = globalThis as Record<string, unknown>\n const originalResponse = globals['Response']\n const originalRequest = globals['Request']\n\n const handler = getMCPHandler(pluginOptions, mcpAccessSettings, req)\n const request = createRequestFromPayloadRequest(req)\n\n try {\n return await handler(request)\n } finally {\n if (globals['Response'] !== originalResponse) {\n Object.defineProperty(globalThis, 'Response', { value: originalResponse })\n }\n if (globals['Request'] !== originalRequest) {\n Object.defineProperty(globalThis, 'Request', { value: originalRequest })\n }\n }\n }\n return mcpHandler\n}\n"],"names":["crypto","UnauthorizedError","createRequestFromPayloadRequest","getMCPHandler","initializeMCPHandler","pluginOptions","mcpHandler","req","payload","MCPOptions","mcp","MCPHandlerOptions","handlerOptions","useVerboseLogs","verboseLogs","payloadAPI","getDefaultMcpAccessSettings","overrideApiKey","apiKey","headers","get","startsWith","replace","trim","sha256APIKeyIndex","createHmac","secret","update","digest","where","apiKeyIndex","equals","docs","find","collection","depth","limit","pagination","length","logger","info","user","userCollection","_strategy","mcpAccessSettings","overrideAuth","globals","globalThis","originalResponse","originalRequest","handler","request","Object","defineProperty","value"],"mappings":"AAAA,OAAOA,YAAY,SAAQ;AAC3B,SAA8CC,iBAAiB,QAAoB,UAAS;AAI5F,SAASC,+BAA+B,QAAQ,0BAAyB;AACzE,SAASC,aAAa,QAAQ,0BAAyB;AAEvD,OAAO,MAAMC,uBAAuB,CAACC;IACnC,MAAMC,aAA6B,OAAOC;QACxC,MAAM,EAAEC,OAAO,EAAE,GAAGD;QACpB,MAAME,aAAaJ,cAAcK,GAAG,IAAI,CAAC;QACzC,MAAMC,oBAAoBF,WAAWG,cAAc,IAAI,CAAC;QACxD,MAAMC,iBAAiBF,kBAAkBG,WAAW,IAAI;QAExDP,IAAIQ,UAAU,GAAG;QAEjB,MAAMC,8BAA8B,OAAOC;YACzC,MAAMC,SACJ,AAACD,kBAAkBV,IAAIY,OAAO,CAACC,GAAG,CAAC,kBAAkBC,WAAW,aAC5Dd,IAAIY,OAAO,CAACC,GAAG,CAAC,kBAAkBE,QAAQ,WAAW,IAAIC,SACzD;YAEN,IAAIL,WAAW,MAAM;gBACnB,MAAM,IAAIjB;YACZ;YAEA,MAAMuB,oBAAoBxB,OACvByB,UAAU,CAAC,UAAUjB,QAAQkB,MAAM,EACnCC,MAAM,CAACT,UAAU,IACjBU,MAAM,CAAC;YAEV,MAAMC,QAAe;gBACnBC,aAAa;oBACXC,QAAQP;gBACV;YACF;YAEA,MAAM,EAAEQ,IAAI,EAAE,GAAG,MAAMxB,QAAQyB,IAAI,CAAC;gBAClCC,YAAY;gBACZC,OAAO;gBACPC,OAAO;gBACPC,YAAY;gBACZR;YACF;YAEA,IAAIG,KAAKM,MAAM,KAAK,GAAG;gBACrB,MAAM,IAAIrC;YACZ;YAEA,IAAIY,gBAAgB;gBAClBL,QAAQ+B,MAAM,CAACC,IAAI,CAAC;YACtB;YAEA,MAAMC,OAAOT,IAAI,CAAC,EAAE,EAAES;YACtBA,KAAKP,UAAU,GAAG7B,cAAcqC,cAAc;YAC9CD,KAAKE,SAAS,GAAG;YAEjB,OAAOX,IAAI,CAAC,EAAE;QAChB;QAEA,MAAMY,oBAAoBvC,cAAcwC,YAAY,GAChD,MAAMxC,cAAcwC,YAAY,CAACtC,KAAKS,+BACtC,MAAMA;QAEV,qFAAqF;QACrF,kFAAkF;QAClF,mGAAmG;QACnG,mEAAmE;QACnE,6EAA6E;QAC7E,wEAAwE;QACxE,MAAM8B,UAAUC;QAChB,MAAMC,mBAAmBF,OAAO,CAAC,WAAW;QAC5C,MAAMG,kBAAkBH,OAAO,CAAC,UAAU;QAE1C,MAAMI,UAAU/C,cAAcE,eAAeuC,mBAAmBrC;QAChE,MAAM4C,UAAUjD,gCAAgCK;QAEhD,IAAI;YACF,OAAO,MAAM2C,QAAQC;QACvB,SAAU;YACR,IAAIL,OAAO,CAAC,WAAW,KAAKE,kBAAkB;gBAC5CI,OAAOC,cAAc,CAACN,YAAY,YAAY;oBAAEO,OAAON;gBAAiB;YAC1E;YACA,IAAIF,OAAO,CAAC,UAAU,KAAKG,iBAAiB;gBAC1CG,OAAOC,cAAc,CAACN,YAAY,WAAW;oBAAEO,OAAOL;gBAAgB;YACxE;QACF;IACF;IACA,OAAO3C;AACT,EAAC"}
1
+ {"version":3,"sources":["../../src/endpoints/mcp.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\nimport type { MCPPluginConfig } from '../types.js'\n\nimport { createRequestFromPayloadRequest } from '../mcp/createRequest.js'\nimport { getMcpHandler } from '../mcp/getMcpHandler.js'\nimport { resolveAccessSettings } from './resolveAccessSettings.js'\nimport { withRestoredWebGlobals } from './withRestoredWebGlobals.js'\n\nexport const initializeMCPHandler = (pluginOptions: MCPPluginConfig) => {\n const mcpHandler: PayloadHandler = async (req) => {\n const useVerboseLogs = pluginOptions.mcp?.handlerOptions?.verboseLogs ?? false\n\n req.payloadAPI = 'MCP' as const\n\n const mcpAccessSettings = await resolveAccessSettings({ pluginOptions, req, useVerboseLogs })\n req.user = mcpAccessSettings.user\n\n return await withRestoredWebGlobals(async () => {\n const handler = getMcpHandler(pluginOptions, mcpAccessSettings, req)\n const request = createRequestFromPayloadRequest(req)\n return await handler(request)\n })\n }\n return mcpHandler\n}\n"],"names":["createRequestFromPayloadRequest","getMcpHandler","resolveAccessSettings","withRestoredWebGlobals","initializeMCPHandler","pluginOptions","mcpHandler","req","useVerboseLogs","mcp","handlerOptions","verboseLogs","payloadAPI","mcpAccessSettings","user","handler","request"],"mappings":"AAIA,SAASA,+BAA+B,QAAQ,0BAAyB;AACzE,SAASC,aAAa,QAAQ,0BAAyB;AACvD,SAASC,qBAAqB,QAAQ,6BAA4B;AAClE,SAASC,sBAAsB,QAAQ,8BAA6B;AAEpE,OAAO,MAAMC,uBAAuB,CAACC;IACnC,MAAMC,aAA6B,OAAOC;QACxC,MAAMC,iBAAiBH,cAAcI,GAAG,EAAEC,gBAAgBC,eAAe;QAEzEJ,IAAIK,UAAU,GAAG;QAEjB,MAAMC,oBAAoB,MAAMX,sBAAsB;YAAEG;YAAeE;YAAKC;QAAe;QAC3FD,IAAIO,IAAI,GAAGD,kBAAkBC,IAAI;QAEjC,OAAO,MAAMX,uBAAuB;YAClC,MAAMY,UAAUd,cAAcI,eAAeQ,mBAAmBN;YAChE,MAAMS,UAAUhB,gCAAgCO;YAChD,OAAO,MAAMQ,QAAQC;QACvB;IACF;IACA,OAAOV;AACT,EAAC"}
@@ -0,0 +1,10 @@
1
+ import { type PayloadRequest } from 'payload';
2
+ import type { MCPAccessSettings, MCPPluginConfig } from '../types.js';
3
+ type ResolveAccessSettingsArgs = {
4
+ pluginOptions: MCPPluginConfig;
5
+ req: PayloadRequest;
6
+ useVerboseLogs: boolean;
7
+ };
8
+ export declare const resolveAccessSettings: (args: ResolveAccessSettingsArgs) => Promise<MCPAccessSettings>;
9
+ export {};
10
+ //# sourceMappingURL=resolveAccessSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveAccessSettings.d.ts","sourceRoot":"","sources":["../../src/endpoints/resolveAccessSettings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAiD,MAAM,SAAS,CAAA;AAE5F,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAErE,KAAK,yBAAyB,GAAG;IAC/B,aAAa,EAAE,eAAe,CAAA;IAC9B,GAAG,EAAE,cAAc,CAAA;IACnB,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAwDD,eAAO,MAAM,qBAAqB,SAC1B,yBAAyB,KAC9B,OAAO,CAAC,iBAAiB,CAM3B,CAAA"}
@@ -0,0 +1,44 @@
1
+ import crypto from 'crypto';
2
+ import { UnauthorizedError } from 'payload';
3
+ const createDefaultAccessSettingsResolver = ({ pluginOptions, req, useVerboseLogs })=>{
4
+ return async (overrideApiKey)=>{
5
+ const authorization = req.headers.get('Authorization');
6
+ const apiKey = overrideApiKey ?? (authorization?.startsWith('Bearer ') ? authorization.replace('Bearer ', '').trim() : null);
7
+ if (apiKey === null) {
8
+ throw new UnauthorizedError();
9
+ }
10
+ const sha256APIKeyIndex = crypto.createHmac('sha256', req.payload.secret).update(apiKey || '').digest('hex');
11
+ const where = {
12
+ apiKeyIndex: {
13
+ equals: sha256APIKeyIndex
14
+ }
15
+ };
16
+ const { docs } = await req.payload.find({
17
+ collection: 'payload-mcp-api-keys',
18
+ depth: 1,
19
+ limit: 1,
20
+ pagination: false,
21
+ where
22
+ });
23
+ const apiKeyDoc = docs[0];
24
+ if (!apiKeyDoc) {
25
+ throw new UnauthorizedError();
26
+ }
27
+ if (useVerboseLogs) {
28
+ req.payload.logger.info('[payload-mcp] API Key is valid');
29
+ }
30
+ const user = apiKeyDoc.user;
31
+ if (!user || typeof user !== 'object' || !('id' in user)) {
32
+ throw new UnauthorizedError();
33
+ }
34
+ user.collection = pluginOptions.userCollection;
35
+ user._strategy = 'mcp-api-key';
36
+ return apiKeyDoc;
37
+ };
38
+ };
39
+ export const resolveAccessSettings = async (args)=>{
40
+ const getDefaultMcpAccessSettings = createDefaultAccessSettingsResolver(args);
41
+ return args.pluginOptions.overrideAuth ? await args.pluginOptions.overrideAuth(args.req, getDefaultMcpAccessSettings) : await getDefaultMcpAccessSettings();
42
+ };
43
+
44
+ //# sourceMappingURL=resolveAccessSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/resolveAccessSettings.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { type PayloadRequest, type TypedUser, UnauthorizedError, type Where } from 'payload'\n\nimport type { MCPAccessSettings, MCPPluginConfig } from '../types.js'\n\ntype ResolveAccessSettingsArgs = {\n pluginOptions: MCPPluginConfig\n req: PayloadRequest\n useVerboseLogs: boolean\n}\n\nconst createDefaultAccessSettingsResolver = ({\n pluginOptions,\n req,\n useVerboseLogs,\n}: ResolveAccessSettingsArgs) => {\n return async (overrideApiKey?: null | string): Promise<MCPAccessSettings> => {\n const authorization = req.headers.get('Authorization')\n const apiKey =\n overrideApiKey ??\n (authorization?.startsWith('Bearer ') ? authorization.replace('Bearer ', '').trim() : null)\n\n if (apiKey === null) {\n throw new UnauthorizedError()\n }\n\n const sha256APIKeyIndex = crypto\n .createHmac('sha256', req.payload.secret)\n .update(apiKey || '')\n .digest('hex')\n const where: Where = {\n apiKeyIndex: {\n equals: sha256APIKeyIndex,\n },\n }\n const { docs } = await req.payload.find({\n collection: 'payload-mcp-api-keys',\n depth: 1,\n limit: 1,\n pagination: false,\n where,\n })\n const apiKeyDoc = docs[0]\n\n if (!apiKeyDoc) {\n throw new UnauthorizedError()\n }\n\n if (useVerboseLogs) {\n req.payload.logger.info('[payload-mcp] API Key is valid')\n }\n\n const user = apiKeyDoc.user as TypedUser\n\n if (!user || typeof user !== 'object' || !('id' in user)) {\n throw new UnauthorizedError()\n }\n\n user.collection = pluginOptions.userCollection as string\n user._strategy = 'mcp-api-key' as const\n\n return apiKeyDoc as unknown as MCPAccessSettings\n }\n}\n\nexport const resolveAccessSettings = async (\n args: ResolveAccessSettingsArgs,\n): Promise<MCPAccessSettings> => {\n const getDefaultMcpAccessSettings = createDefaultAccessSettingsResolver(args)\n\n return args.pluginOptions.overrideAuth\n ? await args.pluginOptions.overrideAuth(args.req, getDefaultMcpAccessSettings)\n : await getDefaultMcpAccessSettings()\n}\n"],"names":["crypto","UnauthorizedError","createDefaultAccessSettingsResolver","pluginOptions","req","useVerboseLogs","overrideApiKey","authorization","headers","get","apiKey","startsWith","replace","trim","sha256APIKeyIndex","createHmac","payload","secret","update","digest","where","apiKeyIndex","equals","docs","find","collection","depth","limit","pagination","apiKeyDoc","logger","info","user","userCollection","_strategy","resolveAccessSettings","args","getDefaultMcpAccessSettings","overrideAuth"],"mappings":"AAAA,OAAOA,YAAY,SAAQ;AAC3B,SAA8CC,iBAAiB,QAAoB,UAAS;AAU5F,MAAMC,sCAAsC,CAAC,EAC3CC,aAAa,EACbC,GAAG,EACHC,cAAc,EACY;IAC1B,OAAO,OAAOC;QACZ,MAAMC,gBAAgBH,IAAII,OAAO,CAACC,GAAG,CAAC;QACtC,MAAMC,SACJJ,kBACCC,CAAAA,eAAeI,WAAW,aAAaJ,cAAcK,OAAO,CAAC,WAAW,IAAIC,IAAI,KAAK,IAAG;QAE3F,IAAIH,WAAW,MAAM;YACnB,MAAM,IAAIT;QACZ;QAEA,MAAMa,oBAAoBd,OACvBe,UAAU,CAAC,UAAUX,IAAIY,OAAO,CAACC,MAAM,EACvCC,MAAM,CAACR,UAAU,IACjBS,MAAM,CAAC;QACV,MAAMC,QAAe;YACnBC,aAAa;gBACXC,QAAQR;YACV;QACF;QACA,MAAM,EAAES,IAAI,EAAE,GAAG,MAAMnB,IAAIY,OAAO,CAACQ,IAAI,CAAC;YACtCC,YAAY;YACZC,OAAO;YACPC,OAAO;YACPC,YAAY;YACZR;QACF;QACA,MAAMS,YAAYN,IAAI,CAAC,EAAE;QAEzB,IAAI,CAACM,WAAW;YACd,MAAM,IAAI5B;QACZ;QAEA,IAAII,gBAAgB;YAClBD,IAAIY,OAAO,CAACc,MAAM,CAACC,IAAI,CAAC;QAC1B;QAEA,MAAMC,OAAOH,UAAUG,IAAI;QAE3B,IAAI,CAACA,QAAQ,OAAOA,SAAS,YAAY,CAAE,CAAA,QAAQA,IAAG,GAAI;YACxD,MAAM,IAAI/B;QACZ;QAEA+B,KAAKP,UAAU,GAAGtB,cAAc8B,cAAc;QAC9CD,KAAKE,SAAS,GAAG;QAEjB,OAAOL;IACT;AACF;AAEA,OAAO,MAAMM,wBAAwB,OACnCC;IAEA,MAAMC,8BAA8BnC,oCAAoCkC;IAExE,OAAOA,KAAKjC,aAAa,CAACmC,YAAY,GAClC,MAAMF,KAAKjC,aAAa,CAACmC,YAAY,CAACF,KAAKhC,GAAG,EAAEiC,+BAChD,MAAMA;AACZ,EAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Restores globals replaced by @hono/node-server while handling an MCP request.
3
+ */
4
+ export declare const withRestoredWebGlobals: <T>(handler: () => Promise<T>) => Promise<T>;
5
+ //# sourceMappingURL=withRestoredWebGlobals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withRestoredWebGlobals.d.ts","sourceRoot":"","sources":["../../src/endpoints/withRestoredWebGlobals.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAAU,CAAC,WAAW,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAepF,CAAA"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Restores globals replaced by @hono/node-server while handling an MCP request.
3
+ */ export const withRestoredWebGlobals = async (handler)=>{
4
+ const globals = globalThis;
5
+ const originalRequest = globals['Request'];
6
+ const originalResponse = globals['Response'];
7
+ try {
8
+ return await handler();
9
+ } finally{
10
+ if (globals['Response'] !== originalResponse) {
11
+ Object.defineProperty(globalThis, 'Response', {
12
+ value: originalResponse
13
+ });
14
+ }
15
+ if (globals['Request'] !== originalRequest) {
16
+ Object.defineProperty(globalThis, 'Request', {
17
+ value: originalRequest
18
+ });
19
+ }
20
+ }
21
+ };
22
+
23
+ //# sourceMappingURL=withRestoredWebGlobals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/withRestoredWebGlobals.ts"],"sourcesContent":["/**\n * Restores globals replaced by @hono/node-server while handling an MCP request.\n */\nexport const withRestoredWebGlobals = async <T>(handler: () => Promise<T>): Promise<T> => {\n const globals = globalThis as Record<string, unknown>\n const originalRequest = globals['Request']\n const originalResponse = globals['Response']\n\n try {\n return await handler()\n } finally {\n if (globals['Response'] !== originalResponse) {\n Object.defineProperty(globalThis, 'Response', { value: originalResponse })\n }\n if (globals['Request'] !== originalRequest) {\n Object.defineProperty(globalThis, 'Request', { value: originalRequest })\n }\n }\n}\n"],"names":["withRestoredWebGlobals","handler","globals","globalThis","originalRequest","originalResponse","Object","defineProperty","value"],"mappings":"AAAA;;CAEC,GACD,OAAO,MAAMA,yBAAyB,OAAUC;IAC9C,MAAMC,UAAUC;IAChB,MAAMC,kBAAkBF,OAAO,CAAC,UAAU;IAC1C,MAAMG,mBAAmBH,OAAO,CAAC,WAAW;IAE5C,IAAI;QACF,OAAO,MAAMD;IACf,SAAU;QACR,IAAIC,OAAO,CAAC,WAAW,KAAKG,kBAAkB;YAC5CC,OAAOC,cAAc,CAACJ,YAAY,YAAY;gBAAEK,OAAOH;YAAiB;QAC1E;QACA,IAAIH,OAAO,CAAC,UAAU,KAAKE,iBAAiB;YAC1CE,OAAOC,cAAc,CAACJ,YAAY,WAAW;gBAAEK,OAAOJ;YAAgB;QACxE;IACF;AACF,EAAC"}
@@ -1,4 +1,4 @@
1
1
  import { type PayloadRequest } from 'payload';
2
2
  import type { MCPAccessSettings, MCPPluginConfig } from '../types.js';
3
- export declare const getMCPHandler: (pluginOptions: MCPPluginConfig, mcpAccessSettings: MCPAccessSettings, req: PayloadRequest) => (request: Request) => Promise<Response>;
3
+ export declare const getMcpHandler: (pluginOptions: MCPPluginConfig, mcpAccessSettings: MCPAccessSettings, req: PayloadRequest) => (request: Request) => Promise<Response>;
4
4
  //# sourceMappingURL=getMcpHandler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getMcpHandler.d.ts","sourceRoot":"","sources":["../../src/mcp/getMcpHandler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgC,KAAK,cAAc,EAAkB,MAAM,SAAS,CAAA;AAE3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAuCrE,eAAO,MAAM,aAAa,kBACT,eAAe,qBACX,iBAAiB,OAC/B,cAAc,4CAgfpB,CAAA"}
1
+ {"version":3,"file":"getMcpHandler.d.ts","sourceRoot":"","sources":["../../src/mcp/getMcpHandler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAE3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAuCrE,eAAO,MAAM,aAAa,kBACT,eAAe,qBACX,iBAAiB,OAC/B,cAAc,4CAifpB,CAAA"}
@@ -31,7 +31,7 @@ import { updateConfigTool } from './tools/config/update.js';
31
31
  import { createJobTool } from './tools/job/create.js';
32
32
  import { runJobTool } from './tools/job/run.js';
33
33
  import { updateJobTool } from './tools/job/update.js';
34
- export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
34
+ export const getMcpHandler = (pluginOptions, mcpAccessSettings, req)=>{
35
35
  const { payload } = req;
36
36
  const configSchema = configToJSONSchema(payload.config, payload.db.defaultIDType, req.i18n, {
37
37
  forceInlineBlocks: true
@@ -50,21 +50,21 @@ export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
50
50
  // User
51
51
  const user = mcpAccessSettings.user;
52
52
  // MCP Server and Handler Options
53
- const MCPOptions = pluginOptions.mcp || {};
54
- const customMCPTools = MCPOptions.tools || [];
55
- const customMCPPrompts = MCPOptions.prompts || [];
56
- const customMCPResources = MCPOptions.resources || [];
57
- const MCPHandlerOptions = MCPOptions.handlerOptions || {};
58
- const serverOptions = MCPOptions.serverOptions || {};
59
- const useVerboseLogs = MCPHandlerOptions.verboseLogs ?? false;
53
+ const mcpOptions = pluginOptions.mcp || {};
54
+ const customTools = mcpOptions.tools || [];
55
+ const customPrompts = mcpOptions.prompts || [];
56
+ const customResources = mcpOptions.resources || [];
57
+ const mcpHandlerOptions = mcpOptions.handlerOptions || {};
58
+ const serverOptions = mcpOptions.serverOptions || {};
59
+ const useVerboseLogs = mcpHandlerOptions.verboseLogs ?? false;
60
60
  // Experimental MCP Tool Requirements
61
61
  const isDevelopment = process.env.NODE_ENV === 'development';
62
- const experimentalTools = pluginOptions?.experimental?.tools || {};
62
+ const experimentalTools = pluginOptions.experimental?.tools || {};
63
63
  const collectionsPluginConfig = pluginOptions.collections || {};
64
64
  const globalsPluginConfig = pluginOptions.globals || {};
65
- const collectionsDirPath = experimentalTools && experimentalTools.collections?.collectionsDirPath ? experimentalTools.collections.collectionsDirPath : join(process.cwd(), 'src/collections');
66
- const configFilePath = experimentalTools && experimentalTools.config?.configFilePath ? experimentalTools.config.configFilePath : join(process.cwd(), 'src/payload.config.ts');
67
- const jobsDirPath = experimentalTools && experimentalTools.jobs?.jobsDirPath ? experimentalTools.jobs.jobsDirPath : join(process.cwd(), 'src/jobs');
65
+ const collectionsDirPath = experimentalTools.collections?.collectionsDirPath || join(process.cwd(), 'src/collections');
66
+ const configFilePath = experimentalTools.config?.configFilePath || join(process.cwd(), 'src/payload.config.ts');
67
+ const jobsDirPath = experimentalTools.jobs?.jobsDirPath || join(process.cwd(), 'src/jobs');
68
68
  try {
69
69
  return createMcpHandler((server)=>{
70
70
  // Get enabled collections
@@ -117,7 +117,7 @@ export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
117
117
  }
118
118
  });
119
119
  // Custom tools
120
- customMCPTools.forEach((tool)=>{
120
+ customTools.forEach((tool)=>{
121
121
  const camelCasedToolName = toCamelCase(tool.name);
122
122
  const isToolEnabled = mcpAccessSettings['payload-mcp-tool']?.[camelCasedToolName] ?? false;
123
123
  registerTool(isToolEnabled, tool.name, ()=>server.registerTool(tool.name, {
@@ -126,7 +126,7 @@ export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
126
126
  }, payloadToolHandler(tool.handler)), payload, useVerboseLogs);
127
127
  });
128
128
  // Custom prompts
129
- customMCPPrompts.forEach((prompt)=>{
129
+ customPrompts.forEach((prompt)=>{
130
130
  const camelCasedPromptName = toCamelCase(prompt.name);
131
131
  const isPromptEnabled = mcpAccessSettings['payload-mcp-prompt']?.[camelCasedPromptName] ?? false;
132
132
  if (isPromptEnabled) {
@@ -143,7 +143,7 @@ export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
143
143
  }
144
144
  });
145
145
  // Custom resources
146
- customMCPResources.forEach((resource)=>{
146
+ customResources.forEach((resource)=>{
147
147
  const camelCasedResourceName = toCamelCase(resource.name);
148
148
  const isResourceEnabled = mcpAccessSettings['payload-mcp-resource']?.[camelCasedResourceName] ?? false;
149
149
  if (isResourceEnabled) {
@@ -200,13 +200,13 @@ export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
200
200
  if (mcpAccessSettings.auth?.verify && experimentalTools.auth?.enabled && isDevelopment) {
201
201
  registerTool(mcpAccessSettings.auth.verify, 'Verify', ()=>verifyTool(server, req, useVerboseLogs), payload, useVerboseLogs);
202
202
  }
203
- if (mcpAccessSettings.auth?.resetPassword && experimentalTools.auth?.enabled) {
203
+ if (mcpAccessSettings.auth?.resetPassword && experimentalTools.auth?.enabled && isDevelopment) {
204
204
  registerTool(mcpAccessSettings.auth.resetPassword, 'Reset Password', ()=>resetPasswordTool(server, req, useVerboseLogs), payload, useVerboseLogs);
205
205
  }
206
- if (mcpAccessSettings.auth?.forgotPassword && experimentalTools.auth?.enabled) {
206
+ if (mcpAccessSettings.auth?.forgotPassword && experimentalTools.auth?.enabled && isDevelopment) {
207
207
  registerTool(mcpAccessSettings.auth.forgotPassword, 'Forgot Password', ()=>forgotPasswordTool(server, req, useVerboseLogs), payload, useVerboseLogs);
208
208
  }
209
- if (mcpAccessSettings.auth?.unlock && experimentalTools.auth?.enabled) {
209
+ if (mcpAccessSettings.auth?.unlock && experimentalTools.auth?.enabled && isDevelopment) {
210
210
  registerTool(mcpAccessSettings.auth.unlock, 'Unlock', ()=>unlockTool(server, req, useVerboseLogs), payload, useVerboseLogs);
211
211
  }
212
212
  if (useVerboseLogs) {
@@ -216,11 +216,11 @@ export const getMCPHandler = (pluginOptions, mcpAccessSettings, req)=>{
216
216
  instructions: serverOptions.instructions,
217
217
  serverInfo: serverOptions.serverInfo
218
218
  }, {
219
- basePath: MCPHandlerOptions.basePath || payload.config.routes?.api || '/api',
220
- disableSse: MCPHandlerOptions.disableSse ?? true,
221
- maxDuration: MCPHandlerOptions.maxDuration || 60,
222
- onEvent: MCPHandlerOptions.onEvent,
223
- redisUrl: MCPHandlerOptions.redisUrl,
219
+ basePath: mcpHandlerOptions.basePath || payload.config.routes?.api || '/api',
220
+ disableSse: mcpHandlerOptions.disableSse ?? true,
221
+ maxDuration: mcpHandlerOptions.maxDuration || 60,
222
+ onEvent: mcpHandlerOptions.onEvent,
223
+ redisUrl: mcpHandlerOptions.redisUrl,
224
224
  verboseLogs: useVerboseLogs
225
225
  });
226
226
  } catch (error) {