@payloadcms/plugin-mcp 4.0.0-canary.31 → 4.0.0-canary.33
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileInput.d.ts","sourceRoot":"","sources":["../../../../src/mcp/builtin/collections/fileInput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"fileInput.d.ts","sourceRoot":"","sources":["../../../../src/mcp/builtin/collections/fileInput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEnE,OAAO,EAAY,CAAC,EAAE,MAAM,SAAS,CAAA;AAwBrC,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;8BAoBzB,CAAA;AAEH,KAAK,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEhD,wBAAsB,WAAW,CAAC,EAChC,IAAI,EACJ,KAAK,EACL,GAAG,GACJ,EAAE;IACD,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,GAAG,EAAE,cAAc,CAAA;IACnB,IAAI,EAAE,cAAc,CAAA;CACrB,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CA2C5B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APIError, z } from 'payload';
|
|
2
|
-
import {
|
|
2
|
+
import { externalURLInputSchema, getFileFromUploadInstructions, resolveURLUploadInput } from 'payload/internal';
|
|
3
3
|
import { sanitizeFilename } from 'payload/shared';
|
|
4
4
|
const mimeTypeSchema = z.string().check(z.regex(/^[!#$%&'*+.^`|~\w-]+\/[!#$%&'*+.^`|~\w-]+$/, 'MIME type must use the type/subtype format'));
|
|
5
5
|
const uploadFileSchema = z.strictObject({
|
|
@@ -15,11 +15,7 @@ export const fileInputSchema = z.discriminatedUnion('source', [
|
|
|
15
15
|
mimeType: mimeTypeSchema.check(z.describe('The file MIME type, for example image/png.')),
|
|
16
16
|
source: z.literal('base64')
|
|
17
17
|
}),
|
|
18
|
-
|
|
19
|
-
name: z.optional(z.string().check(z.minLength(1))).check(z.describe('File name override.')),
|
|
20
|
-
source: z.literal('externalURL'),
|
|
21
|
-
url: z.url().check(z.describe('The http or https URL to download.'))
|
|
22
|
-
}),
|
|
18
|
+
externalURLInputSchema,
|
|
23
19
|
z.strictObject({
|
|
24
20
|
file: uploadFileSchema.check(z.describe('getUploadInstructions file field post-upload.')),
|
|
25
21
|
source: z.literal('uploadReference')
|
|
@@ -43,51 +39,28 @@ export async function resolveFile({ slug, input, req }) {
|
|
|
43
39
|
throw error;
|
|
44
40
|
}
|
|
45
41
|
}
|
|
42
|
+
if (input.source === 'externalURL') {
|
|
43
|
+
return resolveURLUploadInput({
|
|
44
|
+
slug,
|
|
45
|
+
input,
|
|
46
|
+
req
|
|
47
|
+
});
|
|
48
|
+
}
|
|
46
49
|
const uploadConfig = req.payload.collections[slug]?.config.upload;
|
|
47
50
|
if (!uploadConfig) {
|
|
48
51
|
throw new APIError(`Collection "${slug}" does not support file uploads.`, 400);
|
|
49
52
|
}
|
|
50
53
|
const maxFileSize = req.payload.config.upload.limits?.fileSize;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
size: data.length
|
|
62
|
-
};
|
|
63
|
-
} else {
|
|
64
|
-
if (uploadConfig.pasteURL === false) {
|
|
65
|
-
throw new APIError(`Uploading files from URLs is disabled for collection "${slug}".`, 400);
|
|
66
|
-
}
|
|
67
|
-
const url = new URL(input.url);
|
|
68
|
-
if (![
|
|
69
|
-
'http:',
|
|
70
|
-
'https:'
|
|
71
|
-
].includes(url.protocol)) {
|
|
72
|
-
throw new APIError('File URLs must use http or https.', 400);
|
|
73
|
-
}
|
|
74
|
-
if (typeof uploadConfig.pasteURL === 'object' && !isURLAllowed(input.url, uploadConfig.pasteURL.allowList)) {
|
|
75
|
-
throw new APIError('The provided file URL is not allowed.', 400);
|
|
76
|
-
}
|
|
77
|
-
file = await getExternalFile({
|
|
78
|
-
data: {
|
|
79
|
-
filename: sanitizeFilename(input.name || getURLFilename(url)),
|
|
80
|
-
url: input.url
|
|
81
|
-
},
|
|
82
|
-
req,
|
|
83
|
-
uploadConfig: {
|
|
84
|
-
...uploadConfig,
|
|
85
|
-
externalFileHeaderFilter: uploadConfig.externalFileHeaderFilter ?? (()=>({}))
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
file.mimetype = file.mimetype?.split(';')[0] || 'application/octet-stream';
|
|
89
|
-
file.size = file.data.length;
|
|
90
|
-
}
|
|
54
|
+
const data = decodeBase64({
|
|
55
|
+
maxFileSize,
|
|
56
|
+
value: input.data
|
|
57
|
+
});
|
|
58
|
+
const file = {
|
|
59
|
+
name: sanitizeFilename(input.name),
|
|
60
|
+
data,
|
|
61
|
+
mimetype: input.mimeType,
|
|
62
|
+
size: data.length
|
|
63
|
+
};
|
|
91
64
|
if (maxFileSize !== undefined && Number.isFinite(maxFileSize) && file.size > maxFileSize) {
|
|
92
65
|
throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400);
|
|
93
66
|
}
|
|
@@ -111,13 +84,5 @@ function decodeBase64({ maxFileSize, value }) {
|
|
|
111
84
|
}
|
|
112
85
|
return data;
|
|
113
86
|
}
|
|
114
|
-
function getURLFilename(url) {
|
|
115
|
-
const pathSegment = url.pathname.split('/').pop() || 'upload';
|
|
116
|
-
try {
|
|
117
|
-
return decodeURIComponent(pathSegment);
|
|
118
|
-
} catch {
|
|
119
|
-
return pathSegment;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
87
|
|
|
123
88
|
//# sourceMappingURL=fileInput.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/mcp/builtin/collections/fileInput.ts"],"sourcesContent":["import type { CollectionSlug, File,
|
|
1
|
+
{"version":3,"sources":["../../../../src/mcp/builtin/collections/fileInput.ts"],"sourcesContent":["import type { CollectionSlug, File, PayloadRequest } from 'payload'\n\nimport { APIError, z } from 'payload'\nimport {\n externalURLInputSchema,\n getFileFromUploadInstructions,\n resolveURLUploadInput,\n} from 'payload/internal'\nimport { sanitizeFilename } from 'payload/shared'\n\nconst mimeTypeSchema = z\n .string()\n .check(\n z.regex(\n /^[!#$%&'*+.^`|~\\w-]+\\/[!#$%&'*+.^`|~\\w-]+$/,\n 'MIME type must use the type/subtype format',\n ),\n )\n\nconst uploadFileSchema = z.strictObject({\n filename: z.string(),\n mimeType: z.string(),\n size: z.int().check(z.nonnegative()),\n uploadReference: z.record(z.string(), z.unknown()),\n})\n\nexport const fileInputSchema = z\n .discriminatedUnion('source', [\n z.strictObject({\n name: z.string().check(z.minLength(1), z.describe('The file name, including its extension.')),\n data: z\n .string()\n .check(z.describe('The base64-encoded file bytes, without a data URL prefix.')),\n mimeType: mimeTypeSchema.check(z.describe('The file MIME type, for example image/png.')),\n source: z.literal('base64'),\n }),\n externalURLInputSchema,\n z.strictObject({\n file: uploadFileSchema.check(z.describe('getUploadInstructions file field post-upload.')),\n source: z.literal('uploadReference'),\n }),\n ])\n .check(\n z.describe(\n 'A file for an upload collection. Prefer uploadReference after its upload succeeds; use base64 only for small local files or externalURL for an online file.',\n ),\n )\n\ntype FileInput = z.infer<typeof fileInputSchema>\n\nexport async function resolveFile({\n slug,\n input,\n req,\n}: {\n input?: FileInput\n req: PayloadRequest\n slug: CollectionSlug\n}): Promise<File | undefined> {\n if (!input) {\n return undefined\n }\n\n if (input.source === 'uploadReference') {\n try {\n return await getFileFromUploadInstructions({ collectionSlug: slug, file: input.file, req })\n } catch (error) {\n if (error instanceof Error && error.message === 'Staged upload was not found.') {\n throw new APIError(\n 'Staged upload not found. Complete the upload action first, or use base64 for small local files.',\n 400,\n )\n }\n throw error\n }\n }\n\n if (input.source === 'externalURL') {\n return resolveURLUploadInput({ slug, input, req })\n }\n\n const uploadConfig = req.payload.collections[slug]?.config.upload\n\n if (!uploadConfig) {\n throw new APIError(`Collection \"${slug}\" does not support file uploads.`, 400)\n }\n\n const maxFileSize = req.payload.config.upload.limits?.fileSize\n const data = decodeBase64({ maxFileSize, value: input.data })\n const file: File = {\n name: sanitizeFilename(input.name),\n data,\n mimetype: input.mimeType,\n size: data.length,\n }\n\n if (maxFileSize !== undefined && Number.isFinite(maxFileSize) && file.size > maxFileSize) {\n throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400)\n }\n\n return file\n}\n\nfunction decodeBase64({ maxFileSize, value }: { maxFileSize?: number; value: string }): Buffer {\n const normalized = value.replace(/\\s/g, '')\n\n if (!/^[a-z0-9+/]*={0,2}$/i.test(normalized) || normalized.length % 4 === 1) {\n throw new APIError('File data must be valid base64.', 400)\n }\n\n if (maxFileSize !== undefined && Number.isFinite(maxFileSize)) {\n const paddingLength = normalized.endsWith('==') ? 2 : normalized.endsWith('=') ? 1 : 0\n const decodedSize = Math.floor((normalized.length * 3) / 4) - paddingLength\n\n if (decodedSize > maxFileSize) {\n throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400)\n }\n }\n\n const data = Buffer.from(normalized, 'base64')\n\n if (data.toString('base64').replace(/=+$/, '') !== normalized.replace(/=+$/, '')) {\n throw new APIError('File data must be valid base64.', 400)\n }\n\n return data\n}\n"],"names":["APIError","z","externalURLInputSchema","getFileFromUploadInstructions","resolveURLUploadInput","sanitizeFilename","mimeTypeSchema","string","check","regex","uploadFileSchema","strictObject","filename","mimeType","size","int","nonnegative","uploadReference","record","unknown","fileInputSchema","discriminatedUnion","name","minLength","describe","data","source","literal","file","resolveFile","slug","input","req","undefined","collectionSlug","error","Error","message","uploadConfig","payload","collections","config","upload","maxFileSize","limits","fileSize","decodeBase64","value","mimetype","length","Number","isFinite","normalized","replace","test","paddingLength","endsWith","decodedSize","Math","floor","Buffer","from","toString"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/plugin-mcp",
|
|
3
|
-
"version": "4.0.0-canary.
|
|
3
|
+
"version": "4.0.0-canary.33",
|
|
4
4
|
"description": "MCP (Model Context Protocol) capabilities with Payload",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"plugin",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@payloadcms/eslint-config": "3.28.0",
|
|
63
|
-
"payload": "4.0.0-canary.
|
|
63
|
+
"payload": "4.0.0-canary.33"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"payload": "4.0.0-canary.
|
|
66
|
+
"payload": "4.0.0-canary.33"
|
|
67
67
|
},
|
|
68
68
|
"//deps_notes": {
|
|
69
69
|
"tsx": "Required by the `bin.js` stdio mcp. Keep the version pinned to whatever `payload` ships to avoid duplicate installs."
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type { CollectionSlug, File,
|
|
1
|
+
import type { CollectionSlug, File, PayloadRequest } from 'payload'
|
|
2
2
|
|
|
3
3
|
import { APIError, z } from 'payload'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
externalURLInputSchema,
|
|
6
|
+
getFileFromUploadInstructions,
|
|
7
|
+
resolveURLUploadInput,
|
|
8
|
+
} from 'payload/internal'
|
|
5
9
|
import { sanitizeFilename } from 'payload/shared'
|
|
6
10
|
|
|
7
11
|
const mimeTypeSchema = z
|
|
@@ -30,11 +34,7 @@ export const fileInputSchema = z
|
|
|
30
34
|
mimeType: mimeTypeSchema.check(z.describe('The file MIME type, for example image/png.')),
|
|
31
35
|
source: z.literal('base64'),
|
|
32
36
|
}),
|
|
33
|
-
|
|
34
|
-
name: z.optional(z.string().check(z.minLength(1))).check(z.describe('File name override.')),
|
|
35
|
-
source: z.literal('externalURL'),
|
|
36
|
-
url: z.url().check(z.describe('The http or https URL to download.')),
|
|
37
|
-
}),
|
|
37
|
+
externalURLInputSchema,
|
|
38
38
|
z.strictObject({
|
|
39
39
|
file: uploadFileSchema.check(z.describe('getUploadInstructions file field post-upload.')),
|
|
40
40
|
source: z.literal('uploadReference'),
|
|
@@ -75,6 +75,10 @@ export async function resolveFile({
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
if (input.source === 'externalURL') {
|
|
79
|
+
return resolveURLUploadInput({ slug, input, req })
|
|
80
|
+
}
|
|
81
|
+
|
|
78
82
|
const uploadConfig = req.payload.collections[slug]?.config.upload
|
|
79
83
|
|
|
80
84
|
if (!uploadConfig) {
|
|
@@ -82,48 +86,12 @@ export async function resolveFile({
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
const maxFileSize = req.payload.config.upload.limits?.fileSize
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
name: sanitizeFilename(input.name),
|
|
92
|
-
data,
|
|
93
|
-
mimetype: input.mimeType,
|
|
94
|
-
size: data.length,
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
if (uploadConfig.pasteURL === false) {
|
|
98
|
-
throw new APIError(`Uploading files from URLs is disabled for collection "${slug}".`, 400)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const url = new URL(input.url)
|
|
102
|
-
|
|
103
|
-
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
104
|
-
throw new APIError('File URLs must use http or https.', 400)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
typeof uploadConfig.pasteURL === 'object' &&
|
|
109
|
-
!isURLAllowed(input.url, uploadConfig.pasteURL.allowList)
|
|
110
|
-
) {
|
|
111
|
-
throw new APIError('The provided file URL is not allowed.', 400)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
file = await getExternalFile({
|
|
115
|
-
data: {
|
|
116
|
-
filename: sanitizeFilename(input.name || getURLFilename(url)),
|
|
117
|
-
url: input.url,
|
|
118
|
-
} as FileData,
|
|
119
|
-
req,
|
|
120
|
-
uploadConfig: {
|
|
121
|
-
...uploadConfig,
|
|
122
|
-
externalFileHeaderFilter: uploadConfig.externalFileHeaderFilter ?? (() => ({})),
|
|
123
|
-
},
|
|
124
|
-
})
|
|
125
|
-
file.mimetype = file.mimetype?.split(';')[0] || 'application/octet-stream'
|
|
126
|
-
file.size = file.data.length
|
|
89
|
+
const data = decodeBase64({ maxFileSize, value: input.data })
|
|
90
|
+
const file: File = {
|
|
91
|
+
name: sanitizeFilename(input.name),
|
|
92
|
+
data,
|
|
93
|
+
mimetype: input.mimeType,
|
|
94
|
+
size: data.length,
|
|
127
95
|
}
|
|
128
96
|
|
|
129
97
|
if (maxFileSize !== undefined && Number.isFinite(maxFileSize) && file.size > maxFileSize) {
|
|
@@ -157,13 +125,3 @@ function decodeBase64({ maxFileSize, value }: { maxFileSize?: number; value: str
|
|
|
157
125
|
|
|
158
126
|
return data
|
|
159
127
|
}
|
|
160
|
-
|
|
161
|
-
function getURLFilename(url: URL): string {
|
|
162
|
-
const pathSegment = url.pathname.split('/').pop() || 'upload'
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
return decodeURIComponent(pathSegment)
|
|
166
|
-
} catch {
|
|
167
|
-
return pathSegment
|
|
168
|
-
}
|
|
169
|
-
}
|