From b70ff4dff8ff708b52dcb433e15c371a6292f697 Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Tue, 25 Aug 2026 12:27:01 -0400 Subject: [PATCH] feat(mcp): enable Payload MCP plugin Register mcpPlugin on the Payload config. Auth now also accepts payload-mcp-api-keys, so the generated types carry the new collection and the user union widens accordingly. Follow-up commits guard user auth paths against these API-key sessions. Also apply incidental prettier formatting to generatePlainText(). --- package.json | 1 + src/payload-types.ts | 94 ++++++++++++++++++++++++++++++++++++++----- src/payload.config.ts | 8 ++-- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 8957102..724edb8 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@payloadcms/email-nodemailer": "3.88.0", "@payloadcms/next": "3.88.0", "@payloadcms/payload-cloud": "3.88.0", + "@payloadcms/plugin-mcp": "^3.88.0", "@payloadcms/richtext-lexical": "3.88.0", "@payloadcms/ui": "3.88.0", "@radix-ui/react-avatar": "^1.2.6", diff --git a/src/payload-types.ts b/src/payload-types.ts index 6591f7a..cde3290 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -64,6 +64,7 @@ export type SupportedTimezones = export interface Config { auth: { users: UserAuthOperations; + 'payload-mcp-api-keys': PayloadMcpApiKeyAuthOperations; }; blocks: {}; collections: { @@ -112,6 +113,7 @@ export interface Config { 'arma-sync-events': ArmaSyncEvent; 'arma-commands': ArmaCommand; 'plugin-ai-instructions': PluginAiInstruction; + 'payload-mcp-api-keys': PayloadMcpApiKey; 'payload-kv': PayloadKv; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; @@ -171,6 +173,7 @@ export interface Config { 'arma-sync-events': ArmaSyncEventsSelect | ArmaSyncEventsSelect; 'arma-commands': ArmaCommandsSelect | ArmaCommandsSelect; 'plugin-ai-instructions': PluginAiInstructionsSelect | PluginAiInstructionsSelect; + 'payload-mcp-api-keys': PayloadMcpApiKeysSelect | PayloadMcpApiKeysSelect; 'payload-kv': PayloadKvSelect | PayloadKvSelect; 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; @@ -192,7 +195,7 @@ export interface Config { widgets: { collections: CollectionsWidget; }; - user: User; + user: User | PayloadMcpApiKey; jobs: { tasks: unknown; workflows: unknown; @@ -214,6 +217,24 @@ export interface UserAuthOperations { username: string; }; } +export interface PayloadMcpApiKeyAuthOperations { + forgotPassword: { + email: string; + password: string; + }; + login: { + email: string; + password: string; + }; + registerFirstUser: { + email: string; + password: string; + }; + unlock: { + email: string; + password: string; + }; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "media". @@ -2872,6 +2893,33 @@ export interface PluginAiInstruction { updatedAt: string; createdAt: string; } +/** + * API keys control which collections, resources, tools, and prompts MCP clients can access + * + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "payload-mcp-api-keys". + */ +export interface PayloadMcpApiKey { + id: number; + /** + * The user that the API key is associated with. + */ + user: number | User; + /** + * A useful label for the API key. + */ + label?: string | null; + /** + * The purpose of the API key. + */ + description?: string | null; + updatedAt: string; + createdAt: string; + enableAPIKey?: boolean | null; + apiKey?: string | null; + apiKeyIndex?: string | null; + collection: 'payload-mcp-api-keys'; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-kv". @@ -3075,12 +3123,21 @@ export interface PayloadLockedDocument { | ({ relationTo: 'plugin-ai-instructions'; value: number | PluginAiInstruction; + } | null) + | ({ + relationTo: 'payload-mcp-api-keys'; + value: number | PayloadMcpApiKey; } | null); globalSlug?: string | null; - user: { - relationTo: 'users'; - value: number | User; - }; + user: + | { + relationTo: 'users'; + value: number | User; + } + | { + relationTo: 'payload-mcp-api-keys'; + value: number | PayloadMcpApiKey; + }; updatedAt: string; createdAt: string; } @@ -3090,10 +3147,15 @@ export interface PayloadLockedDocument { */ export interface PayloadPreference { id: number; - user: { - relationTo: 'users'; - value: number | User; - }; + user: + | { + relationTo: 'users'; + value: number | User; + } + | { + relationTo: 'payload-mcp-api-keys'; + value: number | PayloadMcpApiKey; + }; key?: string | null; value?: | { @@ -4364,6 +4426,20 @@ export interface PluginAiInstructionsSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "payload-mcp-api-keys_select". + */ +export interface PayloadMcpApiKeysSelect { + user?: T; + label?: T; + description?: T; + updatedAt?: T; + createdAt?: T; + enableAPIKey?: T; + apiKey?: T; + apiKeyIndex?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-kv_select". diff --git a/src/payload.config.ts b/src/payload.config.ts index 8761e72..2ec5c3b 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -56,6 +56,7 @@ import { nodemailerAdapter } from "@payloadcms/email-nodemailer"; import nodemailer from "nodemailer"; import { Shims } from "@/collections/Shims"; import { payloadAiPlugin, PayloadAiPluginLexicalEditorFeature } from "@ai-stack/payloadcms"; +import { mcpPlugin } from "@payloadcms/plugin-mcp"; import { createOpenAI } from "@ai-sdk/openai"; import { createTextStreamResponse, jsonSchema, streamObject } from "ai"; @@ -104,11 +105,7 @@ function plainTextToLexicalJson(text: string): Record { }; } -async function generatePlainText( - prompt: string, - system: string, - model: string, -): Promise { +async function generatePlainText(prompt: string, system: string, model: string): Promise { const baseUrl = process.env.UNSLOTH_URL || "https://uns.onyxsimple.com/v1"; const response = await fetch(`${baseUrl}/chat/completions`, { method: "POST", @@ -288,6 +285,7 @@ export default buildConfig({ plugins: [ // payloadCloudPlugin(), // storage-adapter-placeholder + mcpPlugin({}), payloadAiPlugin({ collections: { [GameHardResources.slug]: true,