@payloadcms/plugin-mcp 3.87.1 → 3.88.0
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.
- package/dist/collections/createApiKeysCollection.d.ts.map +1 -1
- package/dist/collections/createApiKeysCollection.js +25 -0
- package/dist/collections/createApiKeysCollection.js.map +1 -1
- package/dist/endpoints/mcp.d.ts +1 -1
- package/dist/endpoints/mcp.d.ts.map +1 -1
- package/dist/endpoints/mcp.js +14 -61
- package/dist/endpoints/mcp.js.map +1 -1
- package/dist/endpoints/resolveAccessSettings.d.ts +10 -0
- package/dist/endpoints/resolveAccessSettings.d.ts.map +1 -0
- package/dist/endpoints/resolveAccessSettings.js +44 -0
- package/dist/endpoints/resolveAccessSettings.js.map +1 -0
- package/dist/endpoints/withRestoredWebGlobals.d.ts +5 -0
- package/dist/endpoints/withRestoredWebGlobals.d.ts.map +1 -0
- package/dist/endpoints/withRestoredWebGlobals.js +23 -0
- package/dist/endpoints/withRestoredWebGlobals.js.map +1 -0
- package/dist/mcp/getMcpHandler.d.ts +1 -1
- package/dist/mcp/getMcpHandler.d.ts.map +1 -1
- package/dist/mcp/getMcpHandler.js +23 -23
- package/dist/mcp/getMcpHandler.js.map +1 -1
- package/dist/mcp/tools/auth/login.d.ts.map +1 -1
- package/dist/mcp/tools/auth/login.js +4 -5
- package/dist/mcp/tools/auth/login.js.map +1 -1
- package/dist/mcp/tools/global/find.d.ts.map +1 -1
- package/dist/mcp/tools/global/find.js +2 -0
- package/dist/mcp/tools/global/find.js.map +1 -1
- package/dist/mcp/tools/global/update.d.ts.map +1 -1
- package/dist/mcp/tools/global/update.js +2 -0
- package/dist/mcp/tools/global/update.js.map +1 -1
- package/dist/mcp/tools/job/run.d.ts.map +1 -1
- package/dist/mcp/tools/job/run.js +2 -0
- package/dist/mcp/tools/job/run.js.map +1 -1
- package/dist/mcp/tools/resource/update.d.ts.map +1 -1
- package/dist/mcp/tools/resource/update.js +3 -17
- package/dist/mcp/tools/resource/update.js.map +1 -1
- package/dist/mcp/tools/schemas.d.ts +0 -12
- package/dist/mcp/tools/schemas.d.ts.map +1 -1
- package/dist/mcp/tools/schemas.js +1 -5
- package/dist/mcp/tools/schemas.js.map +1 -1
- package/package.json +3 -3
- package/src/collections/createApiKeysCollection.ts +27 -1
- package/src/endpoints/mcp.ts +12 -77
- package/src/endpoints/resolveAccessSettings.ts +74 -0
- package/src/endpoints/withRestoredWebGlobals.ts +19 -0
- package/src/mcp/getMcpHandler.ts +32 -31
- package/src/mcp/tools/auth/login.ts +4 -12
- package/src/mcp/tools/global/find.ts +2 -0
- package/src/mcp/tools/global/update.ts +2 -0
- package/src/mcp/tools/job/run.ts +2 -0
- package/src/mcp/tools/resource/update.ts +0 -16
- 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,
|
|
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":"AAIA,SAASA,WAAW,QAAQ,wBAAuB;AACnD,SAASC,kBAAkB,QAAQ,iCAAgC;AAEnE,OAAO,MAAMC,0BAA0B,CACrCC,aACAC,SACAC,cAA4D,EAAE,EAC9DC,oBAA2E,CAAC,CAAC,EAC7EC;IAEA,MAAMC,oBAAoBH,YAAYI,GAAG,CAAC,CAACC;QACzC,MAAMC,iBAAiBX,YAAYU,KAAKE,IAAI;QAC5C,OAAO;YACLA,MAAMD;YACNE,MAAM;YACNC,OAAO;gBACLC,aAAaL,KAAKK,WAAW;YAC/B;YACAC,cAAc;YACdC,OAAON;QACT;IACF;IAEA,MAAMO,uBACJX,cAAcY,GAAG,EAAEC,WAAWX,IAAI,CAACY;QACjC,MAAMV,iBAAiBX,YAAYqB,SAAST,IAAI;QAChD,OAAO;YACLA,MAAMD;YACNE,MAAM;YACNC,OAAO;gBACLC,aAAaM,SAASN,WAAW;YACnC;YACAC,cAAc;YACdC,OAAON;QACT;IACF,MAAM,EAAE;IAEV,MAAMW,qBACJf,cAAcY,GAAG,EAAEI,SAASd,IAAI,CAACe;QAC/B,MAAMb,iBAAiBX,YAAYwB,OAAOZ,IAAI;QAC9C,OAAO;YACLA,MAAMD;YACNE,MAAM;YACNC,OAAO;gBACLC,aAAaS,OAAOT,WAAW;YACjC;YACAC,cAAc;YACdC,OAAON;QACT;IACF,MAAM,EAAE;IAEV,MAAMc,iBAAiBlB,cAAckB,cAAc;IAEnD,MAAMC,kBAAkB,CAAC,EAAEC,GAAG,EAA2B,GACvDC,QAAQD,IAAIE,IAAI,IAAIF,IAAIE,IAAI,CAACC,UAAU,KAAKL;IAE9C,MAAMM,oBAAoB,CAAC,EAAEJ,GAAG,EAA2B,GACzDA,IAAIE,IAAI,IAAIF,IAAIE,IAAI,CAACC,UAAU,KAAKL,iBAAiB;YAAEI,MAAM;gBAAEG,QAAQL,IAAIE,IAAI,CAACI,EAAE;YAAC;QAAE,IAAI;IAE3F,OAAO;QACLC,MAAM;QACNC,QAAQ;YACN,4EAA4E;YAC5E,8EAA8E;YAC9E,iEAAiE;YACjEC,QAAQV;YACRW,QAAQN;YACRO,MAAMP;YACNQ,QAAQR;YACRS,QAAQT;QACV;QACAjB,OAAO;YACLC,aACE;YACF0B,OAAO;YACPC,YAAY;QACd;QACAC,MAAM;YACJC,sBAAsB;YACtBC,WAAW;QACb;QACAC,QAAQ;YACN;gBACElC,MAAM;gBACNC,MAAM;gBACNsB,QAAQ;oBACN,uEAAuE;oBACvE,2EAA2E;oBAC3E,iFAAiF;oBACjF,qEAAqE;oBACrEC,QAAQ,IAAM;oBACdI,QAAQ,IAAM;gBAChB;gBACA1B,OAAO;oBACLC,aAAa;gBACf;gBACAC,cAAc,CAAC,EAAEW,GAAG,EAA2B,GAC7CA,IAAIE,IAAI,IAAIF,IAAIE,IAAI,CAACC,UAAU,KAAKL,iBAAiBE,IAAIE,IAAI,CAACI,EAAE,GAAGc;gBACrEC,YAAYvB;gBACZwB,UAAU;YACZ;YACA;gBACErC,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBACLC,aAAa;gBACf;YACF;YACA;gBACEH,MAAM;gBACNC,MAAM;gBACNC,OAAO;oBACLC,aAAa;gBACf;YACF;eAEGd,mBAAmB;gBACpBiD,QAAQ/C;gBACRgD,YAAY;YACd;eAEGlD,mBAAmB;gBACpBiD,QAAQ9C;gBACR+C,YAAY;YACd;eAEI9C,YAAY+C,MAAM,GAAG,IACrB;gBACE;oBACEvC,MAAM;oBACNC,OAAO;wBACLC,aAAa;wBACbsC,UAAU;oBACZ;oBACAP,QAAQ;wBACN;4BACElC,MAAM;4BACNC,MAAM;4BACNiC,QAAQtC;4BACRS,OAAO;wBACT;qBACD;oBACDA,OAAO;gBACT;aACD,GACD,EAAE;eAEFV,cAAcY,GAAG,EAAEC,aAAab,cAAcY,GAAG,EAAEC,UAAUgC,SAAS,IACtE;gBACE;oBACEvC,MAAM;oBACNC,OAAO;wBACLC,aAAa;wBACbsC,UAAU;oBACZ;oBACAP,QAAQ;wBACN;4BACElC,MAAM;4BACNC,MAAM;4BACNiC,QAAQ5B;4BACRD,OAAO;wBACT;qBACD;oBACDA,OAAO;gBACT;aACD,GACD,EAAE;eAEFV,cAAcY,GAAG,EAAEI,WAAWhB,cAAcY,GAAG,EAAEI,QAAQ6B,SAAS,IAClE;gBACE;oBACEvC,MAAM;oBACNC,OAAO;wBACLC,aAAa;wBACbsC,UAAU;oBACZ;oBACAP,QAAQ;wBACN;4BACElC,MAAM;4BACNC,MAAM;4BACNiC,QAAQxB;4BACRL,OAAO;wBACT;qBACD;oBACDA,OAAO;gBACT;aACD,GACD,EAAE;YAEN,qBAAqB;eACjBqC,QAAQC,GAAG,CAACC,QAAQ,KAAK,iBAC5BlD,CAAAA,mBAAmBH,aAAasD,WAC/BnD,mBAAmBoD,MAAMD,WACzBnD,mBAAmB4C,QAAQO,WAC3BnD,mBAAmBqC,MAAMc,OAAM,IAC7B;gBACE;oBACE5C,MAAM;oBACNC,OAAO;wBACLC,aAAa;wBACbsC,UAAU;oBACZ;oBACAP,QAAQ;2BACFxC,mBAAmBH,aAAasD,UAChC;4BACE;gCACE7C,MAAM;gCACNC,MAAM;gCACNiC,QAAQ;oCACN;wCACElC,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;oCACA;wCACEJ,MAAM;wCACNC,MAAM;wCACNC,OAAO;4CACLC,aACE;wCACJ;wCACAC,cAAc;oCAChB;iCACD;4BACH;yBACD,GACD,EAAE;2BACFV,mBAAmBoD,MAAMD,UACzB;4BACE;gCACE7C,MAAM;gCACNC,MAAM;gCACNiC,QAAQ;oCACN;wCACElC,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,mBAAmB4C,QAAQO,UAC3B;4BACE;gCACE7C,MAAM;gCACNC,MAAM;gCACNiC,QAAQ;oCACN;wCACElC,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,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"}
|
package/dist/endpoints/mcp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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":"
|
|
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"}
|
package/dist/endpoints/mcp.js
CHANGED
|
@@ -1,69 +1,22 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { UnauthorizedError } from 'payload';
|
|
3
1
|
import { createRequestFromPayloadRequest } from '../mcp/createRequest.js';
|
|
4
|
-
import {
|
|
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
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
}
|
|
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
|
|
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 @@
|
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const serverOptions =
|
|
59
|
-
const useVerboseLogs =
|
|
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
|
|
62
|
+
const experimentalTools = pluginOptions.experimental?.tools || {};
|
|
63
63
|
const collectionsPluginConfig = pluginOptions.collections || {};
|
|
64
64
|
const globalsPluginConfig = pluginOptions.globals || {};
|
|
65
|
-
const collectionsDirPath = experimentalTools
|
|
66
|
-
const configFilePath = experimentalTools
|
|
67
|
-
const jobsDirPath = experimentalTools
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
220
|
-
disableSse:
|
|
221
|
-
maxDuration:
|
|
222
|
-
onEvent:
|
|
223
|
-
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) {
|