MCP Plugin
This plugin adds Model Context Protocol capabilities to Payload.
Core features
- Streamable HTTP transport at
/api/mcpfor local and remote clients, using Payload auth when provided - Stable discovery and CRUD tools for configured collections and globals (
getConfigInfo,getCollectionSchema,findDocuments,createDocuments,updateDocument,deleteDocuments,getUploadInstructions,getGlobalSchema,findGlobal,updateGlobal) - filtered by Payload access and configurable with simple keys liketools: { delete: false } - Auth-enabled collections expose opt-in
login,auth,forgotPassword,resetPassword,unlock,verifytools - Define your own
tools,prompts, andresourceswith full TypeScript inference viadefineTool/defineCollectionTool/defineGlobalTool/definePrompt - Gate tools, prompts, and resources with code-level
accesscallbacks while Payload access control still protects the underlying documents - Observe or transform every completed tool response with
hooks.afterToolCall - Custom auth strategies via
overrideGetAuthorizedMCP
Installation
Quick Start
1. Add mcpPlugin to your Payload config:
2. Start Payload and point your MCP client at the HTTP endpoint:
This development URL skips access control. Outside development, remove overrideAccess=true and send Payload authorization. If your client does not support HTTP natively, see Connecting MCP clients.
That's it - every collection and global is now available through the generic MCP tools. For example, call getConfigInfo to see available slugs, inspect a collection with getCollectionSchema, then create one or more documents with createDocuments.
Uploading files
Upload collections include a getUploadInstructions tool. It keeps the file itself out of MCP tool arguments:
When supported by the storage adapter, the HTTP request uploads directly to the storage provider and bypasses Payload. Otherwise, it uploads to temporary storage through Payload. Named provider instructions tell the client to call an MCP tool with the returned name; if that tool is not installed, the upload is unsupported.
The program running the MCP client must be able to read the local file and send its bytes. A remote Payload server cannot read a path on the caller's computer, and the file should not be converted to base64 for createDocuments or updateDocument.
Configuring per-collection tools
To configure a collection - disable specific built-in tools, tighten a description, add a custom tool - use the collections map:
Auth-collection tools
Auth-enabled collections additionally support auth, login, forgotPassword, resetPassword, unlock, and verify tools. These are opt-in - set them to true (or to an override object) to expose them:
Like every MCP item, auth tools use default MCP access unless you provide an access callback. If you want an opt-in auth tool to be callable without an authenticated user, provide an override object with access: () => true.
Options
Option | Type | Description |
|---|---|---|
| | Map keyed by collection slug. See Per-collection options. |
| | Map keyed by global slug. See Per-global options. |
| | Cross-cutting custom tools, keyed by tool name. Values are |
| | Custom prompts, keyed by name. Values are |
| | Custom resources, keyed by name. Values are |
| | Skip MCP endpoint registration. |
| | Run cross-cutting hooks after tool handlers return. See MCP plugin hooks. |
| | Replace the default MCP authorization resolver. See Custom auth. |
| | Largest HTTP request body in bytes. Larger requests get a |
| | The MCP server name advertised to clients. Default |
| | The MCP server version. Default |
| | Raw options passed through to the underlying |
| | When |
Per-collection options
collections[slug] accepts:
Option | Type | Description |
|---|---|---|
| | Description shown for this collection in discovery responses. |
| | Map of tool name → configuration. See Configuring tools and Defining custom tools. |
| | Intercept the response of any built-in tool for this collection ( |
The tools map keys can be:
- A built-in key (
getCollectionSchema,getUploadInstructionson upload collections,find,create,update,delete, plus auth tool names on auth collections): set tofalseto disable, or to an override object{ access?, description?, overrideResponse? }. Auth tools also accepttrueto enable with no overrides. - A custom name: a value produced by
defineCollectionTool(...).
The create key controls createDocuments, which handles both single and multi-document requests.
getConfigInfo is always registered as a top-level tool. It returns the collection and global slugs visible to the current MCP client.
Per-global options
globals[slug] accepts the same shape, but the built-in keys are limited to getGlobalSchema, find, and update (globals are singletons; create / delete don't apply).
Creating documents
createDocuments accepts a documents array with at least one item. Each item has its own data and optional file. Options such as depth, draft, locale, and fallbackLocale apply to the whole request.
Documents are created one at a time through Payload's Local API. The operation is best-effort: a failed item does not roll back successful items. Results and errors include their original zero-based input index so failed items can be retried without creating successful items again. The tool has no fixed item limit, but normal request-size and execution-time limits still apply.
createDocuments and updateDocument return affected document IDs by default. Pass returning: true when the agent needs the complete documents. You can also pass select with returning: true to limit which fields come back.
Uploading files
MCP can add or replace files in upload-enabled collections.
- For a file that is already online, include its URL in your prompt:
Add https://example.com/logo.png to the media library with the alt text "Company logo". - If your MCP client supports attachments, attach a small local file and ask the agent to upload it.
The agent reads the collection's upload settings before acting. MIME type and file-size restrictions still apply, URL uploads respect pasteURL and its allow list, and local files are transferred as base64.
LLM instructions
The getCollectionSchema and getGlobalSchema tools return LLM instructions in structuredContent.instructions alongside schema, and in a text content block. Read them before creating or updating content.
These instructions come from your collection or global config and can include additional instructions saved in the Admin Panel. To read them, the caller needs permission to create or update documents in that collection, or to update that global. Saved instructions also require the caller to be logged in and have read permission for the collection or global. These permission checks are skipped when overrideAccess is enabled.
HTTP transport
HTTP is the recommended transport for both local development and deployed apps. During development, config changes follow Payload's normal dev-server reload lifecycle.
Once mcpPlugin is in your plugins array, POST /api/mcp accepts JSON-RPC 2.0 MCP requests. Send Payload authorization when you want MCP to run as a user (see Authorization); without it, MCP runs access control with no user.
Quick test:
Authorization
The HTTP endpoint uses Payload authorization. A user API key uses this header:
Send this value in the Authorization header.
When authorization is sent, MCP authenticates with Payload's normal auth system. The authenticated user is used for Payload access control, and MCP only advertises tools, prompts, and resources allowed for that request. If no authorization is sent, MCP runs access control with no user.
Creating an API key
- Start your Payload server and open the admin panel
- Open a user document in an auth-enabled collection with
auth.useAPIKey - Enable API key auth for that user, generate a key, then save and copy it
- Use that key as
Authorization: users API-Key <key>. Replaceuserswith that auth collection's slug.
overrideAccess
By default, MCP runs access control. Set overrideAccess explicitly when you want MCP to behave like a Payload local API call with overrideAccess: true. When true, MCP skips item access callbacks, built-in permission checks, and Payload access control inside built-in handlers.
For HTTP in development, add overrideAccess=true or overrideAccess=false to the MCP URL:
HTTP rejects the overrideAccess URL argument outside development.
Custom auth
Use overrideGetAuthorizedMCP to replace the default Payload authentication and MCP authorization flow. The custom resolver must:
- Authenticate the caller and assign the matching Payload user to
req.user. Assignnullwhen the caller is anonymous. - Return only the MCP
itemsthat caller may use. - Return the
overrideAccessvalue that handlers should pass to Payload's local API.
req.user is the single source of truth for the caller's identity. Payload access control, MCP access callbacks, and local API calls that receive req all use it.
This example allows every configured MCP item for any resolved user. Filter pluginConfig.items before returning when different users should see different items.
The handler receives:
req- the incomingPayloadRequestpluginConfig- the fully sanitized plugin config, including the registereditemsoverrideAccess- the requested Payload-style access override
Connecting MCP clients
Below are configuration examples for popular MCP clients. Replace users API-Key MCP-USER-API-KEY with the authorization value from Authorization. The format of these JSON files may change over time - check the client website for current schemas. The examples include authorization because default MCP access requires a user.
HTTP
Most clients support the streamable-HTTP transport directly. If yours doesn't, the mcp-remote package via npx is a reliable adapter.
VSCode
Cursor
Claude Code
Native HTTP (no mcp-remote)
Testing your MCP endpoint
The MCP Inspector is the fastest way to explore and call your server interactively:
Set the URL to http://127.0.0.1:3000/api/mcp and add Authorization: users API-Key MCP-USER-API-KEY as a header.
Defining custom tools, prompts, and resources
The plugin ships four helpers - defineTool, defineCollectionTool, defineGlobalTool, definePrompt - that give you full inference of the input schema in the handler's argument type. Each helper is a two-stage builder: pass the schema and metadata first, then chain .handler(fn).
Tool inputs and prompt arguments accept Standard Schema libraries such as Zod, or raw JSON Schema 2020-12.
Top-level tools
A "top-level" tool isn't scoped to a collection or global. Its wire name is exactly its key in the tools map.
Collection-scoped tools
A collection tool's wire name is exactly its key. Payload adds a slug string to the MCP input schema automatically; the handler receives the resolved slug. The field is intentionally not enum-restricted so newly-added collections can be used after HMR even if the MCP client cached the previous tool schema. Built-in collection tools still validate the requested slug against Payload access control on the server.
Global-scoped tools
defineGlobalTool mirrors defineCollectionTool. Payload adds a slug string to the MCP input schema automatically; the handler receives the resolved slug. The field is intentionally not enum-restricted so newly-added globals can be used after HMR even if the MCP client cached the previous tool schema. Built-in global tools still validate the requested slug against Payload access control on the server.
Access callbacks
Tools, prompts, and resources can include an access callback. Return false to hide that MCP primitive from the current request. Access callbacks receive req and, when Payload permissions are being enforced, permissions. Built-in collection and global tools accept the same callback in their override object. If no access callback is defined, MCP uses Payload's default behavior and requires req.user.
Built-in collection and global tools include Payload permission checks in their default access callback. If you provide an access callback for a built-in tool, it replaces that default.
Prompts
Resources
Resources can be static (string URI) or dynamic (ResourceTemplate for parameterized URIs):
Request identity and AuthorizedMCP
The authenticated user is available as req.user. Tool handlers also receive an authorizedMCP object describing the MCP authorization result:
When calling Payload's local API from a tool handler, pass req and authorizedMCP.overrideAccess. The local API reads the user from req.user:
Built-in CRUD tools already use this pattern. They run as req.user unless overrideAccess is true.
Prompt and resource handlers receive req, but not authorizedMCP.
Controlling returned fields
createDocuments and updateDocument return IDs by default to keep mutation responses small. Set returning: true to return complete documents. When you only need some fields, pass select together with returning: true.
The built-in findDocuments, findGlobal, and updateGlobal tools continue to accept select directly.
select follows Payload's Select API syntax - set a field to true to include it:
Without select, the full document is returned. On collections with rich text or deeply nested relationships this can exhaust a model's context budget quickly.
Modifying responses
Use overrideResponse to intercept what is sent back to the model after any built-in operation on a collection or global. This is the primary place to sanitize sensitive data:
For finer-grained control, you can set overrideResponse on an individual built-in tool override or on a custom tool. Resolution order: per-tool > collection/global > the built-in's default.
MCP plugin hooks
Use hooks.afterToolCall for behavior that should run after every collection, global, or top-level tool handler returns. Hooks run in array order, and each hook must return the response that the next hook receives:
Each hook receives:
Argument | Description |
|---|---|
| The parsed input passed to the tool. |
| The current Payload request. |
| The response returned by the previous response override or |
| The registered MCP tool name, such as |
Payload collection hooks
To modify documents at runtime use a collection Hook. Inside the hook you can detect MCP traffic via req.payloadAPI === 'MCP':
payloadAPI === 'MCP' is set on the request that the built-in collection / global tools issue. Custom tools that make their own local-API calls control their own req.payloadAPI value.
Localization
When your Payload config has localization enabled, built-in collection/global read and write tools include locale and fallbackLocale parameters - no extra plugin configuration is required.
Parameter | Description |
|---|---|
| Retrieve or write data in a specific locale (e.g. |
| The locale to use when the requested locale has no translation for a field. |
Example - find posts in Spanish with English as fallback:
Virtual fields
Virtual fields (computed, read-only fields) are automatically excluded from getCollectionSchema and getGlobalSchema. They cannot be set by a model, so including them in the schema would be misleading and add noise. They are still present in find responses.
Performance
There are several levers for reducing the number of tokens consumed per MCP request. Token efficiency matters because large responses can exhaust a model's context budget, slow down responses, and increase cost.
Write strong descriptions
The description you provide for a collection or global helps the model choose the right slug after inspecting the available schemas. A vague description leads to missed or incorrect tool calls; a precise description gets the right document target on the first try.
Use select to return only the fields you need
By default, the full document is returned for every operation. On collections with many fields, rich text, or deeply nested relationships this can be very large. Pass select to limit what comes back - see Reducing token usage with select.
Sanitize responses with overrideResponse
If a collection has fields that are irrelevant or sensitive for the model's task, strip them before the response leaves the server. Fewer fields returned means fewer tokens consumed on every call - see Modifying responses.
Only enable the operations you need
If a model only needs to read data, disable create, update, and delete.
stdio transport (with limitations)
For clients that can only spawn local processes, the package also ships a payload-mcp bin. It loads your Payload config, boots a separate Payload instance, and serves MCP over stdin/stdout.
PAYLOAD_MCP_AUTHORIZATION is the same value used for the HTTP Authorization header. If you omit it, MCP runs access control without a user. During development, you can instead set NODE_ENV=development and PAYLOAD_MCP_OVERRIDE_ACCESS=true to skip access control.
You do not need to add mcpPlugin to your Payload config to use the bin. Add it when you want to configure tools, descriptions, prompts, or resources. The bin uses tsx to load TypeScript configs; pass --disable-transpile when loading an already compiled JavaScript config.
Locating the config
The bin derives the project root from the installed package and resolves payload.config.ts from there. In hoisted monorepos or other unusual layouts, set PAYLOAD_CONFIG_PATH explicitly:
Drawbacks
- Payload config, MCP registrations, request context, and authorization are initialized once. Changes require restarting the server and reconnecting the client; some clients may continue showing old discovery results until the client itself is restarted.
- Credentials and permissions are evaluated only at startup. User, role, or access changes do not take effect until restart, while HTTP reevaluates them for every request.
- Each client starts a separate Payload process and database connection instead of reusing the running app.
- stdout is reserved for MCP JSON-RPC messages. Config, plugin, or dependency logs written to stdout can break the connection.
Was this page helpful?