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/email-nodemailer": "3.88.0",
"@payloadcms/next": "3.88.0", "@payloadcms/next": "3.88.0",
"@payloadcms/payload-cloud": "3.88.0", "@payloadcms/payload-cloud": "3.88.0",
"@payloadcms/plugin-mcp": "^3.88.0",
"@payloadcms/richtext-lexical": "3.88.0", "@payloadcms/richtext-lexical": "3.88.0",
"@payloadcms/ui": "3.88.0", "@payloadcms/ui": "3.88.0",
"@radix-ui/react-avatar": "^1.2.6", "@radix-ui/react-avatar": "^1.2.6",

View file

@ -64,6 +64,7 @@ export type SupportedTimezones =
export interface Config { export interface Config {
auth: { auth: {
users: UserAuthOperations; users: UserAuthOperations;
'payload-mcp-api-keys': PayloadMcpApiKeyAuthOperations;
}; };
blocks: {}; blocks: {};
collections: { collections: {
@ -112,6 +113,7 @@ export interface Config {
'arma-sync-events': ArmaSyncEvent; 'arma-sync-events': ArmaSyncEvent;
'arma-commands': ArmaCommand; 'arma-commands': ArmaCommand;
'plugin-ai-instructions': PluginAiInstruction; 'plugin-ai-instructions': PluginAiInstruction;
'payload-mcp-api-keys': PayloadMcpApiKey;
'payload-kv': PayloadKv; 'payload-kv': PayloadKv;
'payload-locked-documents': PayloadLockedDocument; 'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference; 'payload-preferences': PayloadPreference;
@ -171,6 +173,7 @@ export interface Config {
'arma-sync-events': ArmaSyncEventsSelect<false> | ArmaSyncEventsSelect<true>; 'arma-sync-events': ArmaSyncEventsSelect<false> | ArmaSyncEventsSelect<true>;
'arma-commands': ArmaCommandsSelect<false> | ArmaCommandsSelect<true>; 'arma-commands': ArmaCommandsSelect<false> | ArmaCommandsSelect<true>;
'plugin-ai-instructions': PluginAiInstructionsSelect<false> | PluginAiInstructionsSelect<true>; 'plugin-ai-instructions': PluginAiInstructionsSelect<false> | PluginAiInstructionsSelect<true>;
'payload-mcp-api-keys': PayloadMcpApiKeysSelect<false> | PayloadMcpApiKeysSelect<true>;
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>; 'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>; 'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>; 'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
@ -192,7 +195,7 @@ export interface Config {
widgets: { widgets: {
collections: CollectionsWidget; collections: CollectionsWidget;
}; };
user: User; user: User | PayloadMcpApiKey;
jobs: { jobs: {
tasks: unknown; tasks: unknown;
workflows: unknown; workflows: unknown;
@ -214,6 +217,24 @@ export interface UserAuthOperations {
username: string; 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 * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media". * via the `definition` "media".
@ -2872,6 +2893,33 @@ export interface PluginAiInstruction {
updatedAt: string; updatedAt: string;
createdAt: 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 * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-kv". * via the `definition` "payload-kv".
@ -3075,12 +3123,21 @@ export interface PayloadLockedDocument {
| ({ | ({
relationTo: 'plugin-ai-instructions'; relationTo: 'plugin-ai-instructions';
value: number | PluginAiInstruction; value: number | PluginAiInstruction;
} | null)
| ({
relationTo: 'payload-mcp-api-keys';
value: number | PayloadMcpApiKey;
} | null); } | null);
globalSlug?: string | null; globalSlug?: string | null;
user: { user:
relationTo: 'users'; | {
value: number | User; relationTo: 'users';
}; value: number | User;
}
| {
relationTo: 'payload-mcp-api-keys';
value: number | PayloadMcpApiKey;
};
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
@ -3090,10 +3147,15 @@ export interface PayloadLockedDocument {
*/ */
export interface PayloadPreference { export interface PayloadPreference {
id: number; id: number;
user: { user:
relationTo: 'users'; | {
value: number | User; relationTo: 'users';
}; value: number | User;
}
| {
relationTo: 'payload-mcp-api-keys';
value: number | PayloadMcpApiKey;
};
key?: string | null; key?: string | null;
value?: value?:
| { | {
@ -4364,6 +4426,20 @@ export interface PluginAiInstructionsSelect<T extends boolean = true> {
updatedAt?: T; updatedAt?: T;
createdAt?: 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 * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-kv_select". * via the `definition` "payload-kv_select".

View file

@ -56,6 +56,7 @@ import { nodemailerAdapter } from "@payloadcms/email-nodemailer";
import nodemailer from "nodemailer"; import nodemailer from "nodemailer";
import { Shims } from "@/collections/Shims"; import { Shims } from "@/collections/Shims";
import { payloadAiPlugin, PayloadAiPluginLexicalEditorFeature } from "@ai-stack/payloadcms"; import { payloadAiPlugin, PayloadAiPluginLexicalEditorFeature } from "@ai-stack/payloadcms";
import { mcpPlugin } from "@payloadcms/plugin-mcp";
import { createOpenAI } from "@ai-sdk/openai"; import { createOpenAI } from "@ai-sdk/openai";
import { createTextStreamResponse, jsonSchema, streamObject } from "ai"; import { createTextStreamResponse, jsonSchema, streamObject } from "ai";
@ -104,11 +105,7 @@ function plainTextToLexicalJson(text: string): Record<string, unknown> {
}; };
} }
async function generatePlainText( async function generatePlainText(prompt: string, system: string, model: string): Promise<string> {
prompt: string,
system: string,
model: string,
): Promise<string> {
const baseUrl = process.env.UNSLOTH_URL || "https://uns.onyxsimple.com/v1"; const baseUrl = process.env.UNSLOTH_URL || "https://uns.onyxsimple.com/v1";
const response = await fetch(`${baseUrl}/chat/completions`, { const response = await fetch(`${baseUrl}/chat/completions`, {
method: "POST", method: "POST",
@ -288,6 +285,7 @@ export default buildConfig({
plugins: [ plugins: [
// payloadCloudPlugin(), // payloadCloudPlugin(),
// storage-adapter-placeholder // storage-adapter-placeholder
mcpPlugin({}),
payloadAiPlugin({ payloadAiPlugin({
collections: { collections: {
[GameHardResources.slug]: true, [GameHardResources.slug]: true,