Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

MCP Plugin

https://www.npmjs.com/package/@payloadcms/plugin-mcp

This plugin adds Model Context Protocol capabilities.

Core features

  • Adds a collection to your config where:
  • You can allow / disallow find, create, update, and delete operations for each collection
  • You can to allow / disallow capabilities in real time
  • You can define your own Prompts, Tools and Resources available over MCP

Installation

Install the plugin using any JavaScript package manager like pnpm, npm, or Yarn:

1
pnpm add @payloadcms/plugin-mcp

Basic Usage

In the plugins array of your Payload Config, call the plugin with options:

1
import { buildConfig } from 'payload'
2
import { mcpPlugin } from '@payloadcms/plugin-mcp'
3
4
const config = buildConfig({
5
collections: [
6
{
7
slug: 'posts',
8
fields: [],
9
},
10
],
11
plugins: [
12
mcpPlugin({
13
collections: {
14
posts: {
15
enabled: true,
16
},
17
},
18
}),
19
],
20
})
21
22
export default config

Options

Option

Type

Description

collections

object

An object of collection slugs to use for MCP capabilities.

collections[slug]

object

An object of collection slugs to use for MCP capabilities.

collections[slug].description

string

A description for the collection.

collections[slug].overrideResponse

function

A function that allows you to override the response from the operation tool call

collections[slug].enabled

object or boolean

Determines whether the model can find, create, update, and delete documents in the collection.

collections[slug].enabled.find

boolean

Whether to allow the model to find documents in the collection.

collections[slug].enabled.create

boolean

Whether to allow the model to create documents in the collection.

collections[slug].enabled.update

boolean

Whether to allow the model to update documents in the collection.

collections[slug].enabled.delete

boolean

Whether to allow the model to delete documents in the collection.

disabled

boolean

Disable the MCP plugin while keeping database schema consistent.

overrideApiKeyCollection

function

A function that allows you to override the automatically generated API Keys collection.

mcp

object

MCP options that allow you to customize the MCP server.

mcp.tools

array

An array of tools to add to the MCP server.

mcp.tools.name

string

The name of the tool.

mcp.tools.description

string

The description of the tool.

mcp.tools.handler

function

The handler function for the tool.

mcp.tools.parameters

object

The parameters for the tool (Zod schema).

mcp.prompts

array

An array of prompts to add to the MCP server.

mcp.prompts.name

string

The name of the prompt.

mcp.prompts.title

string

The title of the prompt (used by models to determine when to use it).

mcp.prompts.description

string

The description of the prompt.

mcp.prompts.handler

function

The handler function for the prompt.

mcp.prompts.argsSchema

object

The arguments schema for the prompt (Zod schema).

mcp.resources

array

An array of resources to add to the MCP server.

mcp.resources.name

string

The name of the resource.

mcp.resources.title

string

The title of the resource (used by models to determine when to use it).

mcp.resources.description

string

The description of the resource.

mcp.resources.handler

function

The handler function for the resource.

mcp.resources.uri

string or object

The URI of the resource (can be a string or ResourceTemplate for dynamic URIs).

mcp.resources.mimeType

string

The MIME type of the resource.

mcp.handlerOptions

object

The handler options for the MCP server.

mcp.handlerOptions.basePath

string

The base path for the MCP server (default: '/api').

mcp.handlerOptions.verboseLogs

boolean

Whether to log verbose logs to the console (default: false).

mcp.handlerOptions.maxDuration

number

The maximum duration for the MCP server requests (default: 60).

mcp.serverOptions

object

The server options for the MCP server.

mcp.serverOptions.serverInfo

object

The server info for the MCP server.

mcp.serverOptions.serverInfo.name

string

The name of the MCP server (default: 'Payload MCP Server').

mcp.serverOptions.serverInfo.version

string

The version of the MCP server (default: '1.0.0').

Connecting to MCP Clients

After installing and configuring the plugin, you can connect apps with MCP client capabilities to Payload.

Step 1: Create an API Key

  1. Start your Payload server
  2. Navigate to your admin panel at http://localhost:3000/admin
  3. Go to the MCP → API Keys collection
  4. Click Create New
  5. Allow or Disallow MCP traffic permissions for each collection (enable find, create, update, delete as needed)
  6. Click Create and copy the uniquely generated API key

Step 2: Configure Your MCP Client

MCP Clients can be configured to interact with your MCP server. These clients require some JSON configuration, or platform configuration in order to know how to reach your MCP server.

Our recommended approach to make your server available for most MCP clients is to use the mcp-remote package via npx.

Below are configuration examples for popular MCP clients.

VSCode

1
{
2
"mcp.servers": {
3
"Payload": {
4
"command": "npx",
5
"args": [
6
"-y",
7
"mcp-remote",
8
"http://127.0.0.1:3000/api/mcp",
9
"--header",
10
"Authorization: Bearer API-KEY-HERE"
11
]
12
}
13
}
14
}

Cursor

1
{
2
"mcpServers": {
3
"Payload": {
4
"command": "npx",
5
"args": [
6
"-y",
7
"mcp-remote",
8
"http://localhost:3000/api/mcp",
9
"--header",
10
"Authorization: Bearer API-KEY-HERE"
11
]
12
}
13
}
14
}

Other MCP Clients

For connections without using mcp-remote you can use this configuration format:

1
{
2
"mcpServers": {
3
"Payload": {
4
"type": "http",
5
"url": "http://localhost:3000/api/mcp",
6
"headers": {
7
"Authorization": "Bearer API-KEY-HERE"
8
}
9
}
10
}
11
}

Customizations

The plugin supports fully custom prompts, tools and resources that can be called or retrieved by MCP clients. After defining a custom method you can allow / disallow the feature from the admin panel by adjusting the API Key MCP Options checklist.

Prompts

Prompts allow models to generate structured messages for specific tasks. Each prompt defines a schema for arguments and returns formatted messages:

1
prompts: [
2
{
3
name: 'reviewContent',
4
title: 'Content Review Prompt',
5
description: 'Creates a prompt for reviewing content quality',
6
argsSchema: {
7
content: z.string().describe('The content to review'),
8
criteria: z.array(z.string()).describe('Review criteria'),
9
},
10
handler: ({ content, criteria }, req) => ({
11
messages: [
12
{
13
content: {
14
type: 'text',
15
text: `Please review this content based on the following criteria: ${criteria.join(', ')}\n\nContent: ${content}`,
16
},
17
role: 'user',
18
},
19
],
20
}),
21
},
22
]

Resources

Resources provide access to data or content that models can read. They can be static or dynamic with parameterized URIs:

1
resources: [
2
// Static resource
3
{
4
name: 'guidelines',
5
title: 'Content Guidelines',
6
description: 'Company content creation guidelines',
7
uri: 'guidelines://company',
8
mimeType: 'text/markdown',
9
handler: (uri, req) => ({
10
handler: (uri, req) => ({
11
contents: [
12
{
13
uri: uri.href,
14
text: '# Content Guidelines\n\n1. Keep it concise\n2. Use clear language',
15
},
16
],
17
}),
18
},
19
20
// Dynamic resource with template
21
{
22
name: 'userProfile',
23
title: 'User Profile',
24
description: 'Access user profile information',
25
uri: new ResourceTemplate('users://profile/{userId}', { list: undefined }),
26
mimeType: 'application/json',
27
handler: async (uri, { userId }, req) => {
28
// Fetch user data from your system
29
const userData = await getUserById(userId)
30
return {
31
contents: [
32
{
33
uri: uri.href,
34
text: JSON.stringify(userData, null, 2),
35
},
36
],
37
}
38
},
39
},
40
]

Tools

Tools allow you to extend MCP capabilities beyond basic CRUD operations. Use them when you need to perform complex queries, aggregations, or business logic that isn't covered by the standard collection operations.

1
tools: [
2
{
3
name: 'getPostScores',
4
description: 'Get useful scores about content in posts',
5
handler: async (args, req) => {
6
const { payload } = req
7
const stats = await payload.find({
8
collection: 'posts',
9
where: {
10
createdAt: {
11
greater_than: args.since,
12
},
13
},
14
req,
15
overrideAccess: false,
16
user: req.user,
17
})
18
19
return {
20
content: [
21
{
22
type: 'text',
23
text: `Found ${stats.totalDocs} posts created since ${args.since}`,
24
},
25
],
26
}
27
},
28
parameters: z.object({
29
since: z.string().describe('ISO date string for filtering posts'),
30
}).shape,
31
},
32
]

API Key access to MCP

Payload adds an API key collection that allows admins to manage MCP capabilities. Admins can:

  • Create user associated API keys for MCP clients
  • Allow or disallow endpoint traffic in real-time
  • Allow or disallow tools, resources, and prompts

You can customize the API Key collection using the overrideApiKeyCollection option:

1
mcpPlugin({
2
overrideApiKeyCollection: (collection) => {
3
// Add fields to the API Keys collection
4
collection.fields.push({
5
name: 'department',
6
type: 'select',
7
options: [
8
{ label: 'Development', value: 'dev' },
9
{ label: 'Marketing', value: 'marketing' },
10
],
11
})
12
13
// You can also add hooks
14
collection.hooks?.beforeRead?.push(({ doc, req }) => {
15
req.payload.logger.info('Before Read MCP hook!')
16
return doc
17
})
18
return collection
19
},
20
// ... other options
21
})

You can create an MCP access strategy using the overrideAuth option:

1
import { type MCPAccessSettings, mcpPlugin } from '@payloadcms/plugin-mcp'
2
3
// ... other config
4
5
mcpPlugin({
6
overrideAuth: (req, getDefaultMcpAccessSettings) => {
7
const { payload } = req
8
9
// This will return the default MCPAccessSettings
10
// getDefaultMcpAccessSettings()
11
12
payload.logger.info('Custom access Settings for all MCP traffic')
13
return {
14
posts: {
15
find: true,
16
},
17
products: {
18
find: true,
19
},
20
} as MCPAccessSettings
21
},
22
// ... other options
23
})

If you want the default MCPAccessSettings, you can use the addtional argument getDefaultMcpAccessSettings. This will use the Bearer token found in the headers on the req to return the MCPAccessSettings related to the user assigned to the API key.

Hooks

To understand or modify data returned by models at runtime use a collection Hook. Within a hook you can look up the API context. If the context is MCP that collection was triggered by the MCP Plugin. This does not apply to custom tools or resources that have their own context, and can make unrelated database calls.

In this example, Post titles are modified to include '(MCP Hook Override)' when they are read using MCP.

1
import type { CollectionConfig } from 'payload'
2
3
export const Posts: CollectionConfig = {
4
slug: 'posts',
5
fields: [
6
{
7
name: 'title',
8
type: 'text',
9
admin: {
10
description: 'The title of the post',
11
},
12
required: true,
13
},
14
15
// ... other fields
16
],
17
hooks: {
18
beforeRead: [
19
({ doc, req }) => {
20
if (req.payloadAPI === 'MCP') {
21
doc.title = `${doc.title} (MCP Hook Override)`
22
}
23
return doc
24
},
25
],
26
},
27
}

Performance

The description you choose to use for your collection greatly impacts the way a model will decide to use it.

The description in this example is more difficult for a model to understand it's purpose.

1
// Weak
2
const config = buildConfig({
3
// ...
4
plugins: [
5
mcpPlugin({
6
collections: {
7
posts: {
8
enabled: true,
9
description: 'My posts',
10
},
11
},
12
}),
13
],
14
})

The description in this example gives a model a stronger ability to know when to use this collection.

1
// Strong
2
const config = buildConfig({
3
// ...
4
plugins: [
5
mcpPlugin({
6
collections: {
7
posts: {
8
enabled: true,
9
description: 'Posts with content about science and nature',
10
},
11
},
12
}),
13
],
14
})
Next

Multi-Tenant Plugin