@payloadcms/plugin-mcp 0.0.1-alpha.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/LICENSE.md +22 -0
- package/README.md +7 -0
- package/dist/collections/createApiKeysCollection.d.ts +7 -0
- package/dist/collections/createApiKeysCollection.d.ts.map +1 -0
- package/dist/collections/createApiKeysCollection.js +315 -0
- package/dist/collections/createApiKeysCollection.js.map +1 -0
- package/dist/endpoints/mcp.d.ts +4 -0
- package/dist/endpoints/mcp.d.ts.map +1 -0
- package/dist/endpoints/mcp.js +44 -0
- package/dist/endpoints/mcp.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/createRequest.d.ts +3 -0
- package/dist/mcp/createRequest.d.ts.map +1 -0
- package/dist/mcp/createRequest.js +14 -0
- package/dist/mcp/createRequest.js.map +1 -0
- package/dist/mcp/getMcpHandler.d.ts +4 -0
- package/dist/mcp/getMcpHandler.d.ts.map +1 -0
- package/dist/mcp/getMcpHandler.js +179 -0
- package/dist/mcp/getMcpHandler.js.map +1 -0
- package/dist/mcp/helpers/config.d.ts +30 -0
- package/dist/mcp/helpers/config.d.ts.map +1 -0
- package/dist/mcp/helpers/config.js +217 -0
- package/dist/mcp/helpers/config.js.map +1 -0
- package/dist/mcp/helpers/conversion.d.ts +2 -0
- package/dist/mcp/helpers/conversion.d.ts.map +1 -0
- package/dist/mcp/helpers/conversion.js +5 -0
- package/dist/mcp/helpers/conversion.js.map +1 -0
- package/dist/mcp/helpers/fields.d.ts +38 -0
- package/dist/mcp/helpers/fields.d.ts.map +1 -0
- package/dist/mcp/helpers/fields.js +96 -0
- package/dist/mcp/helpers/fields.js.map +1 -0
- package/dist/mcp/helpers/fileValidation.d.ts +69 -0
- package/dist/mcp/helpers/fileValidation.d.ts.map +1 -0
- package/dist/mcp/helpers/fileValidation.js +305 -0
- package/dist/mcp/helpers/fileValidation.js.map +1 -0
- package/dist/mcp/helpers/validation.d.ts +9 -0
- package/dist/mcp/helpers/validation.d.ts.map +1 -0
- package/dist/mcp/helpers/validation.js +22 -0
- package/dist/mcp/helpers/validation.js.map +1 -0
- package/dist/mcp/registerTool.d.ts +6 -0
- package/dist/mcp/registerTool.d.ts.map +1 -0
- package/dist/mcp/registerTool.js +18 -0
- package/dist/mcp/registerTool.js.map +1 -0
- package/dist/mcp/tools/auth/auth.d.ts +4 -0
- package/dist/mcp/tools/auth/auth.d.ts.map +1 -0
- package/dist/mcp/tools/auth/auth.js +54 -0
- package/dist/mcp/tools/auth/auth.js.map +1 -0
- package/dist/mcp/tools/auth/forgotPassword.d.ts +4 -0
- package/dist/mcp/tools/auth/forgotPassword.d.ts.map +1 -0
- package/dist/mcp/tools/auth/forgotPassword.js +45 -0
- package/dist/mcp/tools/auth/forgotPassword.js.map +1 -0
- package/dist/mcp/tools/auth/login.d.ts +4 -0
- package/dist/mcp/tools/auth/login.d.ts.map +1 -0
- package/dist/mcp/tools/auth/login.js +48 -0
- package/dist/mcp/tools/auth/login.js.map +1 -0
- package/dist/mcp/tools/auth/resetPassword.d.ts +4 -0
- package/dist/mcp/tools/auth/resetPassword.d.ts.map +1 -0
- package/dist/mcp/tools/auth/resetPassword.js +46 -0
- package/dist/mcp/tools/auth/resetPassword.js.map +1 -0
- package/dist/mcp/tools/auth/unlock.d.ts +4 -0
- package/dist/mcp/tools/auth/unlock.d.ts.map +1 -0
- package/dist/mcp/tools/auth/unlock.js +45 -0
- package/dist/mcp/tools/auth/unlock.js.map +1 -0
- package/dist/mcp/tools/auth/verify.d.ts +4 -0
- package/dist/mcp/tools/auth/verify.d.ts.map +1 -0
- package/dist/mcp/tools/auth/verify.js +42 -0
- package/dist/mcp/tools/auth/verify.js.map +1 -0
- package/dist/mcp/tools/collection/create.d.ts +10 -0
- package/dist/mcp/tools/collection/create.d.ts.map +1 -0
- package/dist/mcp/tools/collection/create.js +159 -0
- package/dist/mcp/tools/collection/create.js.map +1 -0
- package/dist/mcp/tools/collection/delete.d.ts +10 -0
- package/dist/mcp/tools/collection/delete.d.ts.map +1 -0
- package/dist/mcp/tools/collection/delete.js +162 -0
- package/dist/mcp/tools/collection/delete.js.map +1 -0
- package/dist/mcp/tools/collection/find.d.ts +10 -0
- package/dist/mcp/tools/collection/find.d.ts.map +1 -0
- package/dist/mcp/tools/collection/find.js +162 -0
- package/dist/mcp/tools/collection/find.js.map +1 -0
- package/dist/mcp/tools/collection/update.d.ts +10 -0
- package/dist/mcp/tools/collection/update.d.ts.map +1 -0
- package/dist/mcp/tools/collection/update.js +206 -0
- package/dist/mcp/tools/collection/update.js.map +1 -0
- package/dist/mcp/tools/config/find.d.ts +10 -0
- package/dist/mcp/tools/config/find.d.ts.map +1 -0
- package/dist/mcp/tools/config/find.js +94 -0
- package/dist/mcp/tools/config/find.js.map +1 -0
- package/dist/mcp/tools/config/update.d.ts +10 -0
- package/dist/mcp/tools/config/update.d.ts.map +1 -0
- package/dist/mcp/tools/config/update.js +212 -0
- package/dist/mcp/tools/config/update.js.map +1 -0
- package/dist/mcp/tools/job/create.d.ts +10 -0
- package/dist/mcp/tools/job/create.d.ts.map +1 -0
- package/dist/mcp/tools/job/create.js +293 -0
- package/dist/mcp/tools/job/create.js.map +1 -0
- package/dist/mcp/tools/job/run.d.ts +10 -0
- package/dist/mcp/tools/job/run.d.ts.map +1 -0
- package/dist/mcp/tools/job/run.js +147 -0
- package/dist/mcp/tools/job/run.js.map +1 -0
- package/dist/mcp/tools/job/update.d.ts +11 -0
- package/dist/mcp/tools/job/update.d.ts.map +1 -0
- package/dist/mcp/tools/job/update.js +211 -0
- package/dist/mcp/tools/job/update.js.map +1 -0
- package/dist/mcp/tools/resource/create.d.ts +6 -0
- package/dist/mcp/tools/resource/create.d.ts.map +1 -0
- package/dist/mcp/tools/resource/create.js +75 -0
- package/dist/mcp/tools/resource/create.js.map +1 -0
- package/dist/mcp/tools/resource/delete.d.ts +5 -0
- package/dist/mcp/tools/resource/delete.d.ts.map +1 -0
- package/dist/mcp/tools/resource/delete.js +140 -0
- package/dist/mcp/tools/resource/delete.js.map +1 -0
- package/dist/mcp/tools/resource/find.d.ts +5 -0
- package/dist/mcp/tools/resource/find.d.ts.map +1 -0
- package/dist/mcp/tools/resource/find.js +119 -0
- package/dist/mcp/tools/resource/find.js.map +1 -0
- package/dist/mcp/tools/resource/update.d.ts +6 -0
- package/dist/mcp/tools/resource/update.d.ts.map +1 -0
- package/dist/mcp/tools/resource/update.js +201 -0
- package/dist/mcp/tools/resource/update.js.map +1 -0
- package/dist/mcp/tools/schemas.d.ts +374 -0
- package/dist/mcp/tools/schemas.d.ts.map +1 -0
- package/dist/mcp/tools/schemas.js +201 -0
- package/dist/mcp/tools/schemas.js.map +1 -0
- package/dist/types.d.ts +379 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/camelCase.d.ts +9 -0
- package/dist/utils/camelCase.d.ts.map +1 -0
- package/dist/utils/camelCase.js +11 -0
- package/dist/utils/camelCase.js.map +1 -0
- package/dist/utils/convertCollectionSchemaToZod.d.ts +3 -0
- package/dist/utils/convertCollectionSchemaToZod.d.ts.map +1 -0
- package/dist/utils/convertCollectionSchemaToZod.js +30 -0
- package/dist/utils/convertCollectionSchemaToZod.js.map +1 -0
- package/package.json +64 -0
- package/src/collections/createApiKeysCollection.ts +393 -0
- package/src/endpoints/mcp.ts +60 -0
- package/src/index.ts +86 -0
- package/src/mcp/createRequest.ts +13 -0
- package/src/mcp/getMcpHandler.ts +433 -0
- package/src/mcp/helpers/config.ts +326 -0
- package/src/mcp/helpers/conversion.ts +3 -0
- package/src/mcp/helpers/fields.ts +158 -0
- package/src/mcp/helpers/fileValidation.ts +417 -0
- package/src/mcp/helpers/validation.ts +32 -0
- package/src/mcp/registerTool.ts +22 -0
- package/src/mcp/tools/auth/auth.ts +69 -0
- package/src/mcp/tools/auth/forgotPassword.ts +68 -0
- package/src/mcp/tools/auth/login.ts +70 -0
- package/src/mcp/tools/auth/resetPassword.ts +59 -0
- package/src/mcp/tools/auth/unlock.ts +62 -0
- package/src/mcp/tools/auth/verify.ts +55 -0
- package/src/mcp/tools/collection/create.ts +236 -0
- package/src/mcp/tools/collection/delete.ts +227 -0
- package/src/mcp/tools/collection/find.ts +222 -0
- package/src/mcp/tools/collection/update.ts +288 -0
- package/src/mcp/tools/config/find.ts +126 -0
- package/src/mcp/tools/config/update.ts +282 -0
- package/src/mcp/tools/job/create.ts +420 -0
- package/src/mcp/tools/job/run.ts +189 -0
- package/src/mcp/tools/job/update.ts +319 -0
- package/src/mcp/tools/resource/create.ts +121 -0
- package/src/mcp/tools/resource/delete.ts +210 -0
- package/src/mcp/tools/resource/find.ts +194 -0
- package/src/mcp/tools/resource/update.ts +314 -0
- package/src/mcp/tools/schemas.ts +373 -0
- package/src/types.ts +405 -0
- package/src/utils/camelCase.ts +12 -0
- package/src/utils/convertCollectionSchemaToZod.ts +35 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import type { PayloadRequest } from 'payload'
|
|
3
|
+
|
|
4
|
+
import { toolSchemas } from '../schemas.js'
|
|
5
|
+
|
|
6
|
+
export const resetPasswordTool = (server: McpServer, req: PayloadRequest, verboseLogs: boolean) => {
|
|
7
|
+
const tool = async (collection: string, token: string, password: string) => {
|
|
8
|
+
const payload = req.payload
|
|
9
|
+
|
|
10
|
+
if (verboseLogs) {
|
|
11
|
+
payload.logger.info(`[payload-mcp] Resetting password for user in collection: ${collection}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const result = await payload.resetPassword({
|
|
16
|
+
collection,
|
|
17
|
+
data: {
|
|
18
|
+
password,
|
|
19
|
+
token,
|
|
20
|
+
},
|
|
21
|
+
overrideAccess: true,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (verboseLogs) {
|
|
25
|
+
payload.logger.info('[payload-mcp] Password reset completed successfully')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text' as const,
|
|
32
|
+
text: `# Password Reset Successful\n\n**Collection:** ${collection}\n**Token:** ${token}\n\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\``,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
38
|
+
payload.logger.error(`[payload-mcp] Error resetting password: ${errorMessage}`)
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: 'text' as const,
|
|
44
|
+
text: `❌ **Error resetting password**: ${errorMessage}`,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
server.tool(
|
|
52
|
+
'resetPassword',
|
|
53
|
+
toolSchemas.resetPassword.description,
|
|
54
|
+
toolSchemas.resetPassword.parameters.shape,
|
|
55
|
+
async ({ collection, password, token }) => {
|
|
56
|
+
return await tool(collection, token, password)
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import type { PayloadRequest } from 'payload'
|
|
3
|
+
|
|
4
|
+
import { toolSchemas } from '../schemas.js'
|
|
5
|
+
|
|
6
|
+
export const unlockTool = (server: McpServer, req: PayloadRequest, verboseLogs: boolean) => {
|
|
7
|
+
const tool = async (collection: string, email: string) => {
|
|
8
|
+
const payload = req.payload
|
|
9
|
+
|
|
10
|
+
if (verboseLogs) {
|
|
11
|
+
payload.logger.info(
|
|
12
|
+
`[payload-mcp] Unlocking user account for user: ${email} in collection: ${collection}`,
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const result = await payload.unlock({
|
|
18
|
+
collection,
|
|
19
|
+
data: {
|
|
20
|
+
email,
|
|
21
|
+
},
|
|
22
|
+
overrideAccess: true,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
if (verboseLogs) {
|
|
26
|
+
payload.logger.info(`[payload-mcp] User account unlocked successfully for user: ${email}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: 'text' as const,
|
|
33
|
+
text: `# User Account Unlocked\n\n**User:** ${email}\n**Collection:** ${collection}\n\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\``,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
39
|
+
payload.logger.error(
|
|
40
|
+
`[payload-mcp] Error unlocking user account for user ${email}: ${errorMessage}`,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: 'text' as const,
|
|
47
|
+
text: `❌ **Error unlocking user account for user "${email}"**: ${errorMessage}`,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
server.tool(
|
|
55
|
+
'unlock',
|
|
56
|
+
toolSchemas.unlock.description,
|
|
57
|
+
toolSchemas.unlock.parameters.shape,
|
|
58
|
+
async ({ collection, email }) => {
|
|
59
|
+
return await tool(collection, email)
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import type { PayloadRequest } from 'payload'
|
|
3
|
+
|
|
4
|
+
import { toolSchemas } from '../schemas.js'
|
|
5
|
+
|
|
6
|
+
export const verifyTool = (server: McpServer, req: PayloadRequest, verboseLogs: boolean) => {
|
|
7
|
+
const tool = async (collection: string, token: string) => {
|
|
8
|
+
const payload = req.payload
|
|
9
|
+
|
|
10
|
+
if (verboseLogs) {
|
|
11
|
+
payload.logger.info(`[payload-mcp] Verifying user account for collection: ${collection}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const result = await payload.verifyEmail({
|
|
16
|
+
collection,
|
|
17
|
+
token,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (verboseLogs) {
|
|
21
|
+
payload.logger.info('[payload-mcp] Email verification completed successfully')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text' as const,
|
|
28
|
+
text: `# Email Verification Successful\n\n**Collection:** ${collection}\n**Token:** ${token}\n**Result:** ${result ? 'Success' : 'Failed'}`,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
34
|
+
payload.logger.error(`[payload-mcp] Error verifying email: ${errorMessage}`)
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text' as const,
|
|
40
|
+
text: `❌ **Error verifying email**: ${errorMessage}`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
server.tool(
|
|
48
|
+
'verify',
|
|
49
|
+
toolSchemas.verify.description,
|
|
50
|
+
toolSchemas.verify.parameters.shape,
|
|
51
|
+
async ({ collection, token }) => {
|
|
52
|
+
return await tool(collection, token)
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import type { PayloadRequest } from 'payload'
|
|
3
|
+
|
|
4
|
+
import { writeFileSync } from 'fs'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
|
|
7
|
+
import { validateCollectionFile } from '../../helpers/fileValidation.js'
|
|
8
|
+
import { toolSchemas } from '../schemas.js'
|
|
9
|
+
|
|
10
|
+
export const createCollection = async (
|
|
11
|
+
req: PayloadRequest,
|
|
12
|
+
verboseLogs: boolean,
|
|
13
|
+
collectionsDirPath: string,
|
|
14
|
+
configFilePath: string,
|
|
15
|
+
collectionName: string,
|
|
16
|
+
collectionDescription: string | undefined,
|
|
17
|
+
fields: any[],
|
|
18
|
+
hasUpload: boolean | undefined,
|
|
19
|
+
) => {
|
|
20
|
+
const payload = req.payload
|
|
21
|
+
if (verboseLogs) {
|
|
22
|
+
payload.logger.info(
|
|
23
|
+
`[payload-mcp] Creating collection: ${collectionName} with ${fields.length} fields`,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const capitalizedName = collectionName.charAt(0).toUpperCase() + collectionName.slice(1)
|
|
28
|
+
const slug = collectionName
|
|
29
|
+
.replace(/([A-Z])/g, '-$1')
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/^-/, '')
|
|
32
|
+
|
|
33
|
+
if (verboseLogs) {
|
|
34
|
+
payload.logger.info(`[payload-mcp] Generated slug: ${slug} for collection: ${collectionName}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Generate TypeScript field definitions more systematically
|
|
38
|
+
const generateFieldDefinition = (field: any) => {
|
|
39
|
+
const fieldConfig = []
|
|
40
|
+
fieldConfig.push(` {`)
|
|
41
|
+
fieldConfig.push(` name: '${field.name}',`)
|
|
42
|
+
fieldConfig.push(` type: '${field.type}',`)
|
|
43
|
+
|
|
44
|
+
if (field.required) {
|
|
45
|
+
fieldConfig.push(` required: true,`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (field.description) {
|
|
49
|
+
fieldConfig.push(` admin: {`)
|
|
50
|
+
fieldConfig.push(` description: '${field.description}',`)
|
|
51
|
+
fieldConfig.push(` },`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (field.options && field.type === 'select') {
|
|
55
|
+
fieldConfig.push(` options: [`)
|
|
56
|
+
field.options.forEach((option: { label: string; value: string }) => {
|
|
57
|
+
fieldConfig.push(` { label: '${option.label}', value: '${option.value}' },`)
|
|
58
|
+
})
|
|
59
|
+
fieldConfig.push(` ],`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fieldConfig.push(` },`)
|
|
63
|
+
return fieldConfig.join('\n')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const fieldDefinitions = fields.map(generateFieldDefinition).join('\n')
|
|
67
|
+
|
|
68
|
+
// Generate collection file content
|
|
69
|
+
const collectionContent = `import type { CollectionConfig } from 'payload'
|
|
70
|
+
|
|
71
|
+
export const ${capitalizedName}: CollectionConfig = {
|
|
72
|
+
slug: '${slug}',${
|
|
73
|
+
collectionDescription
|
|
74
|
+
? `
|
|
75
|
+
admin: {
|
|
76
|
+
description: '${collectionDescription}',
|
|
77
|
+
},`
|
|
78
|
+
: ''
|
|
79
|
+
}${
|
|
80
|
+
hasUpload
|
|
81
|
+
? `
|
|
82
|
+
upload: true,`
|
|
83
|
+
: ''
|
|
84
|
+
}
|
|
85
|
+
fields: [
|
|
86
|
+
${fieldDefinitions}
|
|
87
|
+
],
|
|
88
|
+
}
|
|
89
|
+
`
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Validate the collection name and path
|
|
93
|
+
const fileName = `${capitalizedName}.ts`
|
|
94
|
+
const filePath = join(collectionsDirPath, fileName)
|
|
95
|
+
|
|
96
|
+
// Security check: ensure we're working with the collections directory
|
|
97
|
+
if (!filePath.startsWith(collectionsDirPath)) {
|
|
98
|
+
payload.logger.error(`[payload-mcp] Invalid collection path attempted: ${filePath}`)
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: 'text' as const,
|
|
103
|
+
text: '❌ **Error**: Invalid collection path',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check if file already exists
|
|
110
|
+
try {
|
|
111
|
+
const fs = await import('fs')
|
|
112
|
+
if (fs.existsSync(filePath)) {
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: 'text' as const,
|
|
117
|
+
text: `❌ **Error**: Collection file already exists: ${fileName}`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (_ignore) {
|
|
123
|
+
// File doesn't exist, which is what we want
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Write the collection file
|
|
127
|
+
writeFileSync(filePath, collectionContent, 'utf8')
|
|
128
|
+
if (verboseLogs) {
|
|
129
|
+
payload.logger.info(`[payload-mcp] Successfully created collection file: ${filePath}`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Validate the generated file
|
|
133
|
+
const validationResult = await validateCollectionFile(fileName)
|
|
134
|
+
if (validationResult.error) {
|
|
135
|
+
return {
|
|
136
|
+
content: [
|
|
137
|
+
{
|
|
138
|
+
type: 'text' as const,
|
|
139
|
+
text: `❌ **Error**: Generated collection has validation issues:\n\n${validationResult.error}`,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: 'text' as const,
|
|
149
|
+
text: `✅ **Collection created successfully!**
|
|
150
|
+
**File**: \`${fileName}\`
|
|
151
|
+
**Collection Config:**
|
|
152
|
+
\`\`\`typescript
|
|
153
|
+
${collectionContent}
|
|
154
|
+
\`\`\``,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
const errorMessage = (error as Error).message
|
|
160
|
+
payload.logger.error(`[payload-mcp] Error creating collection: ${errorMessage}`)
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
content: [
|
|
164
|
+
{
|
|
165
|
+
type: 'text' as const,
|
|
166
|
+
text: `❌ **Error creating collection**: ${errorMessage}`,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const createCollectionTool = (
|
|
174
|
+
server: McpServer,
|
|
175
|
+
req: PayloadRequest,
|
|
176
|
+
verboseLogs: boolean,
|
|
177
|
+
collectionsDirPath: string,
|
|
178
|
+
configFilePath: string,
|
|
179
|
+
) => {
|
|
180
|
+
const tool = async (
|
|
181
|
+
collectionName: string,
|
|
182
|
+
collectionDescription?: string,
|
|
183
|
+
fields: any[] = [],
|
|
184
|
+
hasUpload?: boolean,
|
|
185
|
+
) => {
|
|
186
|
+
const payload = req.payload
|
|
187
|
+
|
|
188
|
+
if (verboseLogs) {
|
|
189
|
+
payload.logger.info(
|
|
190
|
+
`[payload-mcp] Creating collection: ${collectionName}, fields: ${fields.length}, upload: ${hasUpload}`,
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const result = await createCollection(
|
|
196
|
+
req,
|
|
197
|
+
verboseLogs,
|
|
198
|
+
collectionsDirPath,
|
|
199
|
+
configFilePath,
|
|
200
|
+
collectionName,
|
|
201
|
+
collectionDescription,
|
|
202
|
+
fields,
|
|
203
|
+
hasUpload,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if (verboseLogs) {
|
|
207
|
+
payload.logger.info(`[payload-mcp] Collection creation completed for: ${collectionName}`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result
|
|
211
|
+
} catch (error) {
|
|
212
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
213
|
+
payload.logger.error(
|
|
214
|
+
`[payload-mcp] Error creating collection ${collectionName}: ${errorMessage}`,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
content: [
|
|
219
|
+
{
|
|
220
|
+
type: 'text' as const,
|
|
221
|
+
text: `Error creating collection "${collectionName}": ${errorMessage}`,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
server.tool(
|
|
229
|
+
'createCollection',
|
|
230
|
+
toolSchemas.createCollection.description,
|
|
231
|
+
toolSchemas.createCollection.parameters.shape,
|
|
232
|
+
async ({ collectionDescription, collectionName, fields, hasUpload }) => {
|
|
233
|
+
return await tool(collectionName, collectionDescription, fields, hasUpload)
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import type { PayloadRequest } from 'payload'
|
|
3
|
+
|
|
4
|
+
import { readFileSync, unlinkSync, writeFileSync } from 'fs'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
|
|
7
|
+
import { toolSchemas } from '../schemas.js'
|
|
8
|
+
|
|
9
|
+
// Helper function for removing collection from config
|
|
10
|
+
const removeCollectionFromConfig = (configContent: string, collectionName: string): string => {
|
|
11
|
+
// Simple implementation - find and remove the collection import and reference
|
|
12
|
+
let updatedContent = configContent
|
|
13
|
+
|
|
14
|
+
// Remove import statement
|
|
15
|
+
const importRegex = new RegExp(
|
|
16
|
+
`import\\s*{\\s*${collectionName}\\s*}\\s*from\\s*['"]\\./collections/${collectionName}['"];?\\s*\\n?`,
|
|
17
|
+
'g',
|
|
18
|
+
)
|
|
19
|
+
updatedContent = updatedContent.replace(importRegex, '')
|
|
20
|
+
|
|
21
|
+
// Remove from collections array
|
|
22
|
+
const collectionsRegex = new RegExp(`\\s*${collectionName},?\\s*`, 'g')
|
|
23
|
+
updatedContent = updatedContent.replace(collectionsRegex, '')
|
|
24
|
+
|
|
25
|
+
return updatedContent
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const deleteCollection = (
|
|
29
|
+
req: PayloadRequest,
|
|
30
|
+
verboseLogs: boolean,
|
|
31
|
+
collectionsDirPath: string,
|
|
32
|
+
configFilePath: string,
|
|
33
|
+
collectionName: string,
|
|
34
|
+
confirmDeletion: boolean,
|
|
35
|
+
updateConfig: boolean,
|
|
36
|
+
) => {
|
|
37
|
+
const payload = req.payload
|
|
38
|
+
|
|
39
|
+
if (verboseLogs) {
|
|
40
|
+
payload.logger.info(`[payload-mcp] Attempting to delete collection: ${collectionName}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!confirmDeletion) {
|
|
44
|
+
payload.logger.warn(`[payload-mcp] Deletion cancelled for collection: ${collectionName}`)
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text' as const,
|
|
49
|
+
text: `❌ **Deletion cancelled**. Set confirmDeletion to true to proceed with deleting collection "${collectionName}".`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const capitalizedName = collectionName.charAt(0).toUpperCase() + collectionName.slice(1)
|
|
56
|
+
const collectionFilePath = join(collectionsDirPath, `${capitalizedName}.ts`)
|
|
57
|
+
|
|
58
|
+
// Security check: ensure we're working with the collections directory
|
|
59
|
+
if (!collectionFilePath.startsWith(collectionsDirPath)) {
|
|
60
|
+
payload.logger.error(`[payload-mcp] Invalid collection path attempted: ${collectionFilePath}`)
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: 'text' as const,
|
|
65
|
+
text: '❌ **Error**: Invalid collection path',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
// Check if collection file exists
|
|
73
|
+
let fileExists = false
|
|
74
|
+
try {
|
|
75
|
+
readFileSync(collectionFilePath, 'utf8')
|
|
76
|
+
fileExists = true
|
|
77
|
+
} catch {
|
|
78
|
+
payload.logger.warn(`[payload-mcp] Collection file does not exist: ${collectionFilePath}`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Read current config if we need to update it
|
|
82
|
+
let configContent = ''
|
|
83
|
+
let configExists = false
|
|
84
|
+
if (updateConfig) {
|
|
85
|
+
try {
|
|
86
|
+
configContent = readFileSync(configFilePath, 'utf8')
|
|
87
|
+
configExists = true
|
|
88
|
+
} catch {
|
|
89
|
+
payload.logger.warn(`[payload-mcp] Config file does not exist: ${configFilePath}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let responseText = ''
|
|
94
|
+
let operationsPerformed = 0
|
|
95
|
+
|
|
96
|
+
// Delete the collection file
|
|
97
|
+
if (fileExists) {
|
|
98
|
+
try {
|
|
99
|
+
unlinkSync(collectionFilePath)
|
|
100
|
+
if (verboseLogs) {
|
|
101
|
+
payload.logger.info(
|
|
102
|
+
`[payload-mcp] Successfully deleted collection file: ${collectionFilePath}`,
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
responseText += `✅ Deleted collection file: \`${capitalizedName}.ts\`\n`
|
|
106
|
+
operationsPerformed++
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const errorMessage = (error as Error).message
|
|
109
|
+
payload.logger.error(`[payload-mcp] Error deleting collection file: ${errorMessage}`)
|
|
110
|
+
responseText += `❌ Error deleting collection file: ${errorMessage}\n`
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
responseText += `⚠️ Collection file not found: \`${capitalizedName}.ts\`\n`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Update the config file if requested and it exists
|
|
117
|
+
if (updateConfig && configExists) {
|
|
118
|
+
try {
|
|
119
|
+
const updatedConfigContent = removeCollectionFromConfig(configContent, capitalizedName)
|
|
120
|
+
writeFileSync(configFilePath, updatedConfigContent, 'utf8')
|
|
121
|
+
if (verboseLogs) {
|
|
122
|
+
payload.logger.info(`[payload-mcp] Successfully updated config file: ${configFilePath}`)
|
|
123
|
+
}
|
|
124
|
+
responseText += `✅ Updated payload.config.ts to remove collection reference\n`
|
|
125
|
+
operationsPerformed++
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const errorMessage = (error as Error).message
|
|
128
|
+
payload.logger.error(`[payload-mcp] Error updating config file: ${errorMessage}`)
|
|
129
|
+
responseText += `❌ Error updating config file: ${errorMessage}\n`
|
|
130
|
+
}
|
|
131
|
+
} else if (updateConfig && !configExists) {
|
|
132
|
+
responseText += `⚠️ Config file not found: payload.config.ts\n`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Summary
|
|
136
|
+
if (operationsPerformed > 0) {
|
|
137
|
+
responseText += `\n✅ **Collection deletion completed!**`
|
|
138
|
+
} else {
|
|
139
|
+
responseText += `\n⚠️ **No operations performed**
|
|
140
|
+
|
|
141
|
+
The collection file may not have existed or there were errors during deletion.`
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
content: [
|
|
146
|
+
{
|
|
147
|
+
type: 'text' as const,
|
|
148
|
+
text: responseText,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
const errorMessage = (error as Error).message
|
|
154
|
+
payload.logger.error(`[payload-mcp] Error during collection deletion: ${errorMessage}`)
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: 'text' as const,
|
|
159
|
+
text: `❌ **Error during collection deletion**: ${errorMessage}`,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const deleteCollectionTool = (
|
|
167
|
+
server: McpServer,
|
|
168
|
+
req: PayloadRequest,
|
|
169
|
+
verboseLogs: boolean,
|
|
170
|
+
collectionsDirPath: string,
|
|
171
|
+
configFilePath: string,
|
|
172
|
+
) => {
|
|
173
|
+
const tool = (
|
|
174
|
+
collectionName: string,
|
|
175
|
+
confirmDeletion: boolean,
|
|
176
|
+
updateConfig: boolean = false,
|
|
177
|
+
) => {
|
|
178
|
+
const payload = req.payload
|
|
179
|
+
|
|
180
|
+
if (verboseLogs) {
|
|
181
|
+
payload.logger.info(
|
|
182
|
+
`[payload-mcp] Deleting collection: ${collectionName}, confirmDeletion: ${confirmDeletion}, updateConfig: ${updateConfig}`,
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const result = deleteCollection(
|
|
188
|
+
req,
|
|
189
|
+
verboseLogs,
|
|
190
|
+
collectionsDirPath,
|
|
191
|
+
configFilePath,
|
|
192
|
+
collectionName,
|
|
193
|
+
confirmDeletion,
|
|
194
|
+
updateConfig,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if (verboseLogs) {
|
|
198
|
+
payload.logger.info(`[payload-mcp] Collection deletion completed for: ${collectionName}`)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return result
|
|
202
|
+
} catch (error) {
|
|
203
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
204
|
+
payload.logger.error(
|
|
205
|
+
`[payload-mcp] Error deleting collection ${collectionName}: ${errorMessage}`,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
content: [
|
|
210
|
+
{
|
|
211
|
+
type: 'text' as const,
|
|
212
|
+
text: `Error deleting collection "${collectionName}": ${errorMessage}`,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
server.tool(
|
|
220
|
+
'deleteCollection',
|
|
221
|
+
toolSchemas.deleteCollection.description,
|
|
222
|
+
toolSchemas.deleteCollection.parameters.shape,
|
|
223
|
+
({ collectionName, confirmDeletion, updateConfig }) => {
|
|
224
|
+
return tool(collectionName, confirmDeletion, updateConfig)
|
|
225
|
+
},
|
|
226
|
+
)
|
|
227
|
+
}
|