@payloadcms/plugin-mcp 4.0.0-internal.9496957 → 4.0.0-internal.970009e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/bin.js +0 -7
  2. package/dist/endpoint/access.d.ts.map +1 -1
  3. package/dist/endpoint/access.js +15 -3
  4. package/dist/endpoint/access.js.map +1 -1
  5. package/dist/endpoint/index.d.ts.map +1 -1
  6. package/dist/endpoint/index.js +46 -4
  7. package/dist/endpoint/index.js.map +1 -1
  8. package/dist/endpoint/oauth.d.ts +39 -0
  9. package/dist/endpoint/oauth.d.ts.map +1 -0
  10. package/dist/endpoint/oauth.js +92 -0
  11. package/dist/endpoint/oauth.js.map +1 -0
  12. package/dist/endpoint/oauthLoginToken.d.ts +20 -0
  13. package/dist/endpoint/oauthLoginToken.d.ts.map +1 -0
  14. package/dist/endpoint/oauthLoginToken.js +158 -0
  15. package/dist/endpoint/oauthLoginToken.js.map +1 -0
  16. package/dist/endpoint/oauthServer.d.ts +12 -0
  17. package/dist/endpoint/oauthServer.d.ts.map +1 -0
  18. package/dist/endpoint/oauthServer.js +377 -0
  19. package/dist/endpoint/oauthServer.js.map +1 -0
  20. package/dist/index.d.ts +10 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +15 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/mcp/buildMcpServer.d.ts +1 -1
  25. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  26. package/dist/mcp/buildMcpServer.js +3 -3
  27. package/dist/mcp/buildMcpServer.js.map +1 -1
  28. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  29. package/dist/mcp/builtin/collections/authTools.js +4 -3
  30. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  31. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  32. package/dist/mcp/builtin/collections/createTool.js +12 -6
  33. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  34. package/dist/mcp/builtin/collections/deleteTool.js +6 -2
  35. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  36. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -1
  37. package/dist/mcp/builtin/collections/duplicateTool.js +6 -2
  38. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -1
  39. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -1
  40. package/dist/mcp/builtin/collections/fileInput.js +19 -54
  41. package/dist/mcp/builtin/collections/fileInput.js.map +1 -1
  42. package/dist/mcp/builtin/collections/findTool.js +8 -4
  43. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  44. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -1
  45. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +19 -3
  46. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -1
  47. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -1
  48. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  49. package/dist/mcp/builtin/collections/updateTool.js +21 -8
  50. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  51. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -1
  52. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  53. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -1
  54. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +19 -3
  55. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -1
  56. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  57. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  58. package/dist/mcp/sanitizeMCPConfig.js +3 -0
  59. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  60. package/dist/stdio.js +4 -3
  61. package/dist/stdio.js.map +1 -1
  62. package/dist/types.d.ts +91 -3
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils/toStandardSchema.js.map +1 -1
  66. package/package.json +23 -26
  67. package/src/endpoint/access.ts +12 -3
  68. package/src/endpoint/index.ts +41 -3
  69. package/src/endpoint/oauth.ts +91 -0
  70. package/src/endpoint/oauthLoginToken.ts +210 -0
  71. package/src/endpoint/oauthServer.ts +428 -0
  72. package/src/index.ts +40 -5
  73. package/src/mcp/buildMcpServer.ts +5 -5
  74. package/src/mcp/builtin/collections/authTools.ts +7 -4
  75. package/src/mcp/builtin/collections/createTool.ts +26 -6
  76. package/src/mcp/builtin/collections/deleteTool.ts +3 -3
  77. package/src/mcp/builtin/collections/duplicateTool.ts +3 -2
  78. package/src/mcp/builtin/collections/fileInput.ts +17 -59
  79. package/src/mcp/builtin/collections/findTool.ts +4 -4
  80. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +12 -1
  81. package/src/mcp/builtin/collections/restoreVersionTool.ts +1 -1
  82. package/src/mcp/builtin/collections/updateTool.ts +10 -6
  83. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +1 -1
  84. package/src/mcp/builtin/globals/findTool.ts +1 -1
  85. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +11 -1
  86. package/src/mcp/builtin/globals/updateTool.ts +1 -1
  87. package/src/mcp/sanitizeMCPConfig.ts +1 -0
  88. package/src/stdio.ts +2 -2
  89. package/src/types.ts +93 -3
  90. package/src/utils/toStandardSchema.ts +1 -1
  91. package/dist/mcp/builtin/collections/fileInput.spec.js +0 -86
  92. package/dist/mcp/builtin/collections/fileInput.spec.js.map +0 -1
package/src/types.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type {
2
2
  CallToolResult,
3
3
  ContentBlock,
4
+ Implementation,
4
5
  JsonSchemaType,
5
- McpServer,
6
+ McpServerOptions,
6
7
  ResourceTemplate,
7
8
  ServerContext,
8
9
  StandardSchemaWithJSON,
@@ -279,9 +280,24 @@ export type MCPPluginConfig = {
279
280
  afterToolCall?: MCPAfterToolCallHook[]
280
281
  }
281
282
  mcp?: {
283
+ /**
284
+ * Largest HTTP request body, in bytes, that the MCP endpoint accepts. Larger requests
285
+ * are answered with `413`. Base64 file uploads count toward this limit.
286
+ *
287
+ * @default 4194304 (4 MiB, the MCP SDK default)
288
+ */
289
+ maxRequestBodySize?: number
282
290
  serverOptions?: MCPServerOptions
283
291
  verboseLogs?: boolean
284
292
  }
293
+ /**
294
+ * How MCP clients sign in. MCP requests without an authenticated user get a `401` that points
295
+ * to the protected resource metadata (RFC 9728). Set to `false` to only accept API keys and
296
+ * anonymous requests.
297
+ *
298
+ * @default { type: 'builtin' }
299
+ */
300
+ oauth?: false | MCPOAuthConfig
285
301
  /**
286
302
  * Replace the default MCP authorization resolver.
287
303
  *
@@ -299,13 +315,87 @@ export type MCPPluginConfig = {
299
315
  tools?: MCPTopLevelToolsMap
300
316
  }
301
317
 
318
+ export type MCPOAuthConfig = MCPBuiltinOAuthConfig | MCPExternalOAuthConfig
319
+
320
+ /**
321
+ * This Payload app is the authorization server. Users sign in to the admin panel and allow the
322
+ * MCP client, which then gets an access token for them.
323
+ */
324
+ export type MCPBuiltinOAuthConfig = {
325
+ /**
326
+ * Creates the tokens the MCP client gets when the user clicks Allow. `req` is the user's own
327
+ * browser request, so `req.user` is the user who allowed it. Pass `authenticate` too if
328
+ * Payload's auth strategies don't accept the access token.
329
+ *
330
+ * @default a Payload login token for that user, with a refresh token when the collection uses sessions
331
+ */
332
+ issueToken?: (args: { req: PayloadRequest }) => MaybePromise<MCPOAuthTokens>
333
+ /**
334
+ * Trades a refresh token from `issueToken`, or from an earlier refresh, for new tokens. Return
335
+ * `null` when it's no longer valid, and the client sends the user through sign-in again. `req`
336
+ * is the MCP client's request, without a user. Only used together with `issueToken`.
337
+ *
338
+ * OAuth requires noticing copied refresh tokens: return a new refresh token every time, and
339
+ * revoke the tokens when an already used refresh token comes back.
340
+ */
341
+ refreshToken?: (args: {
342
+ refreshToken: string
343
+ req: PayloadRequest
344
+ }) => MaybePromise<MCPOAuthTokens | null>
345
+ type: 'builtin'
346
+ } & MCPOAuthSharedConfig
347
+
348
+ export type MCPOAuthTokens = {
349
+ accessToken: string
350
+ /** Seconds until the access token expires. */
351
+ expiresIn?: number
352
+ /** Lets the client get new tokens without the user, through `refreshToken`. */
353
+ refreshToken?: string
354
+ }
355
+
356
+ /** Another service, e.g. your identity provider, is the authorization server. */
357
+ export type MCPExternalOAuthConfig = {
358
+ /**
359
+ * The authorization servers that issue access tokens for MCP clients. Clients require each one
360
+ * to exactly match the `issuer` in that server's metadata, so pass a function when the exact
361
+ * value is only known at runtime, e.g. from the server's OpenID configuration or per host.
362
+ */
363
+ authorizationServers: ((args: { req: PayloadRequest }) => MaybePromise<string[]>) | string[]
364
+ /**
365
+ * Scopes MCP clients should request, e.g. `['openid', 'email', 'profile']`. Without them,
366
+ * clients request none, which some authorization servers reject or answer with a token that
367
+ * doesn't identify the user.
368
+ */
369
+ scopes?: string[]
370
+ type: 'external'
371
+ } & MCPOAuthSharedConfig
372
+
373
+ type MCPOAuthSharedConfig = {
374
+ /**
375
+ * Called on MCP requests with a bearer token that Payload's auth strategies didn't accept.
376
+ * Return the Payload user, including its `collection`, or `null` to answer with the OAuth challenge.
377
+ */
378
+ authenticate?: (args: {
379
+ req: PayloadRequest
380
+ token: string
381
+ }) => MaybePromise<PayloadRequest['user']>
382
+ /**
383
+ * The URL clients call to reach the MCP endpoint.
384
+ *
385
+ * @default the absolute URL of `/api/mcp`, based on `serverURL` or the request URL
386
+ */
387
+ resource?: string
388
+ }
389
+
302
390
  export type SanitizedMCPPluginConfig = {
303
391
  items: MCPItem[]
392
+ /** `undefined` when OAuth is turned off. */
393
+ oauth?: MCPOAuthConfig
304
394
  } & Pick<MCPPluginConfig, 'disabled' | 'hooks' | 'mcp' | 'overrideGetAuthorizedMCP'>
305
395
 
306
396
  export type MCPServerOptions = {
307
- options?: ConstructorParameters<typeof McpServer>[1]
308
- serverInfo?: Partial<ConstructorParameters<typeof McpServer>[0]>
397
+ options?: McpServerOptions
398
+ serverInfo?: Partial<Implementation>
309
399
  }
310
400
 
311
401
  /**
@@ -5,5 +5,5 @@ import type { ToolInputSchema } from '../types.js'
5
5
  /** Normalize a tool/prompt input to a Standard Schema. Raw JSON Schema gets wrapped; Standard Schema instances pass through. */
6
6
  export const toStandardSchema = (schema: ToolInputSchema): StandardSchemaWithJSON =>
7
7
  typeof schema === 'object' && schema !== null && '~standard' in schema
8
- ? (schema as StandardSchemaWithJSON)
8
+ ? schema
9
9
  : fromJsonSchema(schema)
@@ -1,86 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { fileInputSchema, resolveFile } from './fileInput.js';
3
- describe('MCP file input', ()=>{
4
- it('should reject malformed MIME types', ()=>{
5
- const result = fileInputSchema.safeParse({
6
- data: 'aGVsbG8=',
7
- mimeType: 'text/plain\r\nx-test: value',
8
- name: 'hello.txt',
9
- source: 'base64'
10
- });
11
- expect(result.success).toBe(false);
12
- });
13
- it('should accept a prepared upload', ()=>{
14
- const result = fileInputSchema.safeParse({
15
- file: {
16
- filename: 'image.png',
17
- mimeType: 'image/png',
18
- size: 123,
19
- uploadReference: {
20
- uploadId: 'upload-id'
21
- }
22
- },
23
- source: 'uploadReference'
24
- });
25
- expect(result.success).toBe(true);
26
- });
27
- it('should reject oversized base64 files', async ()=>{
28
- const req = createRequest({
29
- maxFileSize: 4
30
- });
31
- await expect(resolveFile({
32
- slug: 'media',
33
- input: {
34
- data: Buffer.from('hello').toString('base64'),
35
- mimeType: 'text/plain',
36
- name: 'hello.txt',
37
- source: 'base64'
38
- },
39
- req
40
- })).rejects.toThrow('File exceeds the 4 byte upload limit.');
41
- });
42
- it('should reject URLs outside the collection allowlist', async ()=>{
43
- const req = createRequest({
44
- allowList: [
45
- {
46
- hostname: 'assets.example.com',
47
- protocol: 'https'
48
- }
49
- ]
50
- });
51
- await expect(resolveFile({
52
- slug: 'media',
53
- input: {
54
- source: 'externalURL',
55
- url: 'https://example.com/image.png'
56
- },
57
- req
58
- })).rejects.toThrow('The provided file URL is not allowed.');
59
- });
60
- });
61
- function createRequest({ allowList, maxFileSize }) {
62
- return {
63
- payload: {
64
- collections: {
65
- media: {
66
- config: {
67
- upload: allowList ? {
68
- pasteURL: {
69
- allowList
70
- }
71
- } : {}
72
- }
73
- }
74
- },
75
- config: {
76
- upload: {
77
- limits: {
78
- fileSize: maxFileSize
79
- }
80
- }
81
- }
82
- }
83
- };
84
- }
85
-
86
- //# sourceMappingURL=fileInput.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/mcp/builtin/collections/fileInput.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { fileInputSchema, resolveFile } from './fileInput.js'\n\ndescribe('MCP file input', () => {\n it('should reject malformed MIME types', () => {\n const result = fileInputSchema.safeParse({\n data: 'aGVsbG8=',\n mimeType: 'text/plain\\r\\nx-test: value',\n name: 'hello.txt',\n source: 'base64',\n })\n\n expect(result.success).toBe(false)\n })\n\n it('should accept a prepared upload', () => {\n const result = fileInputSchema.safeParse({\n file: {\n filename: 'image.png',\n mimeType: 'image/png',\n size: 123,\n uploadReference: { uploadId: 'upload-id' },\n },\n source: 'uploadReference',\n })\n\n expect(result.success).toBe(true)\n })\n\n it('should reject oversized base64 files', async () => {\n const req = createRequest({ maxFileSize: 4 })\n\n await expect(\n resolveFile({\n slug: 'media',\n input: {\n data: Buffer.from('hello').toString('base64'),\n mimeType: 'text/plain',\n name: 'hello.txt',\n source: 'base64',\n },\n req,\n }),\n ).rejects.toThrow('File exceeds the 4 byte upload limit.')\n })\n\n it('should reject URLs outside the collection allowlist', async () => {\n const req = createRequest({\n allowList: [{ hostname: 'assets.example.com', protocol: 'https' }],\n })\n\n await expect(\n resolveFile({\n slug: 'media',\n input: {\n source: 'externalURL',\n url: 'https://example.com/image.png',\n },\n req,\n }),\n ).rejects.toThrow('The provided file URL is not allowed.')\n })\n})\n\nfunction createRequest({\n allowList,\n maxFileSize,\n}: {\n allowList?: Array<{ hostname: string; protocol: 'http' | 'https' }>\n maxFileSize?: number\n}): PayloadRequest {\n return {\n payload: {\n collections: {\n media: {\n config: {\n upload: allowList ? { pasteURL: { allowList } } : {},\n },\n },\n },\n config: {\n upload: {\n limits: {\n fileSize: maxFileSize,\n },\n },\n },\n },\n } as unknown as PayloadRequest\n}\n"],"names":["describe","expect","it","fileInputSchema","resolveFile","result","safeParse","data","mimeType","name","source","success","toBe","file","filename","size","uploadReference","uploadId","req","createRequest","maxFileSize","slug","input","Buffer","from","toString","rejects","toThrow","allowList","hostname","protocol","url","payload","collections","media","config","upload","pasteURL","limits","fileSize"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,eAAe,EAAEC,WAAW,QAAQ,iBAAgB;AAE7DJ,SAAS,kBAAkB;IACzBE,GAAG,sCAAsC;QACvC,MAAMG,SAASF,gBAAgBG,SAAS,CAAC;YACvCC,MAAM;YACNC,UAAU;YACVC,MAAM;YACNC,QAAQ;QACV;QAEAT,OAAOI,OAAOM,OAAO,EAAEC,IAAI,CAAC;IAC9B;IAEAV,GAAG,mCAAmC;QACpC,MAAMG,SAASF,gBAAgBG,SAAS,CAAC;YACvCO,MAAM;gBACJC,UAAU;gBACVN,UAAU;gBACVO,MAAM;gBACNC,iBAAiB;oBAAEC,UAAU;gBAAY;YAC3C;YACAP,QAAQ;QACV;QAEAT,OAAOI,OAAOM,OAAO,EAAEC,IAAI,CAAC;IAC9B;IAEAV,GAAG,wCAAwC;QACzC,MAAMgB,MAAMC,cAAc;YAAEC,aAAa;QAAE;QAE3C,MAAMnB,OACJG,YAAY;YACViB,MAAM;YACNC,OAAO;gBACLf,MAAMgB,OAAOC,IAAI,CAAC,SAASC,QAAQ,CAAC;gBACpCjB,UAAU;gBACVC,MAAM;gBACNC,QAAQ;YACV;YACAQ;QACF,IACAQ,OAAO,CAACC,OAAO,CAAC;IACpB;IAEAzB,GAAG,uDAAuD;QACxD,MAAMgB,MAAMC,cAAc;YACxBS,WAAW;gBAAC;oBAAEC,UAAU;oBAAsBC,UAAU;gBAAQ;aAAE;QACpE;QAEA,MAAM7B,OACJG,YAAY;YACViB,MAAM;YACNC,OAAO;gBACLZ,QAAQ;gBACRqB,KAAK;YACP;YACAb;QACF,IACAQ,OAAO,CAACC,OAAO,CAAC;IACpB;AACF;AAEA,SAASR,cAAc,EACrBS,SAAS,EACTR,WAAW,EAIZ;IACC,OAAO;QACLY,SAAS;YACPC,aAAa;gBACXC,OAAO;oBACLC,QAAQ;wBACNC,QAAQR,YAAY;4BAAES,UAAU;gCAAET;4BAAU;wBAAE,IAAI,CAAC;oBACrD;gBACF;YACF;YACAO,QAAQ;gBACNC,QAAQ;oBACNE,QAAQ;wBACNC,UAAUnB;oBACZ;gBACF;YACF;QACF;IACF;AACF"}