1
0
Fork 0

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().
This commit is contained in:
Jason Fraley 2026-08-25 12:27:01 -04:00
parent 83790a629b
commit b70ff4dff8
3 changed files with 89 additions and 14 deletions

View file

@ -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",

View file

@ -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<false> | ArmaSyncEventsSelect<true>;
'arma-commands': ArmaCommandsSelect<false> | ArmaCommandsSelect<true>;
'plugin-ai-instructions': PluginAiInstructionsSelect<false> | PluginAiInstructionsSelect<true>;
'payload-mcp-api-keys': PayloadMcpApiKeysSelect<false> | PayloadMcpApiKeysSelect<true>;
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
@ -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<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-mcp-api-keys_select".
*/
export interface PayloadMcpApiKeysSelect<T extends boolean = true> {
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".

View file

@ -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<string, unknown> {
};
}
async function generatePlainText(
prompt: string,
system: string,
model: string,
): Promise<string> {
async function generatePlainText(prompt: string, system: string, model: string): Promise<string> {
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,