payload-mcp-toolkit 0.3.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +232 -150
- package/dist/__tests__/api-keys.test.js +292 -0
- package/dist/__tests__/api-keys.test.js.map +1 -0
- package/dist/__tests__/auth-strategy.test.js +681 -0
- package/dist/__tests__/auth-strategy.test.js.map +1 -0
- package/dist/__tests__/conflict-detection.test.js +69 -0
- package/dist/__tests__/conflict-detection.test.js.map +1 -0
- package/dist/__tests__/delete-document.test.js +70 -0
- package/dist/__tests__/delete-document.test.js.map +1 -0
- package/dist/__tests__/endpoint.test.js +143 -0
- package/dist/__tests__/endpoint.test.js.map +1 -0
- package/dist/__tests__/find-document.test.js +178 -0
- package/dist/__tests__/find-document.test.js.map +1 -0
- package/dist/__tests__/find-global.test.js +173 -0
- package/dist/__tests__/find-global.test.js.map +1 -0
- package/dist/__tests__/global-versions.test.js +183 -0
- package/dist/__tests__/global-versions.test.js.map +1 -0
- package/dist/__tests__/hash.test.js +58 -0
- package/dist/__tests__/hash.test.js.map +1 -0
- package/dist/__tests__/index-integration.test.js +191 -0
- package/dist/__tests__/index-integration.test.js.map +1 -0
- package/dist/__tests__/introspection.test.js +201 -1
- package/dist/__tests__/introspection.test.js.map +1 -1
- package/dist/__tests__/patch-global-layout.test.js +474 -0
- package/dist/__tests__/patch-global-layout.test.js.map +1 -0
- package/dist/__tests__/patch-layout.test.js +171 -0
- package/dist/__tests__/patch-layout.test.js.map +1 -0
- package/dist/__tests__/registry.test.js +795 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/resources.test.js +139 -0
- package/dist/__tests__/resources.test.js.map +1 -0
- package/dist/__tests__/update-global.test.js +157 -0
- package/dist/__tests__/update-global.test.js.map +1 -0
- package/dist/api-keys.d.ts +46 -0
- package/dist/api-keys.js +272 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/auth-strategy.d.ts +85 -0
- package/dist/auth-strategy.js +219 -0
- package/dist/auth-strategy.js.map +1 -0
- package/dist/components/CollectionScopesMatrix.d.ts +8 -0
- package/dist/components/CollectionScopesMatrix.js +32 -0
- package/dist/components/CollectionScopesMatrix.js.map +1 -0
- package/dist/components/GlobalScopesMatrix.d.ts +8 -0
- package/dist/components/GlobalScopesMatrix.js +28 -0
- package/dist/components/GlobalScopesMatrix.js.map +1 -0
- package/dist/components/ScopesTable.d.ts +19 -0
- package/dist/components/ScopesTable.js +285 -0
- package/dist/components/ScopesTable.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -0
- package/dist/conflict-detection.d.ts +13 -0
- package/dist/conflict-detection.js +41 -0
- package/dist/conflict-detection.js.map +1 -0
- package/dist/draft-workflow.d.ts +46 -47
- package/dist/draft-workflow.js +53 -130
- package/dist/draft-workflow.js.map +1 -1
- package/dist/endpoint.d.ts +35 -0
- package/dist/endpoint.js +105 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/hash.d.ts +21 -0
- package/dist/hash.js +36 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +9 -9
- package/dist/index.js +168 -68
- package/dist/index.js.map +1 -1
- package/dist/introspection.d.ts +17 -3
- package/dist/introspection.js +95 -36
- package/dist/introspection.js.map +1 -1
- package/dist/prompts.js +5 -5
- package/dist/prompts.js.map +1 -1
- package/dist/registry.d.ts +50 -0
- package/dist/registry.js +169 -0
- package/dist/registry.js.map +1 -0
- package/dist/resources.d.ts +5 -3
- package/dist/resources.js +23 -11
- package/dist/resources.js.map +1 -1
- package/dist/scope/audit-log.d.ts +18 -0
- package/dist/scope/audit-log.js +50 -0
- package/dist/scope/audit-log.js.map +1 -0
- package/dist/scope/policy.d.ts +73 -0
- package/dist/scope/policy.js +218 -0
- package/dist/scope/policy.js.map +1 -0
- package/dist/tools/_helpers.d.ts +28 -1
- package/dist/tools/_helpers.js +83 -0
- package/dist/tools/_helpers.js.map +1 -1
- package/dist/tools/_layout-helpers.d.ts +43 -0
- package/dist/tools/_layout-helpers.js +159 -0
- package/dist/tools/_layout-helpers.js.map +1 -0
- package/dist/tools/create-document.d.ts +36 -0
- package/dist/tools/create-document.js +83 -0
- package/dist/tools/create-document.js.map +1 -0
- package/dist/tools/delete-document.d.ts +25 -0
- package/dist/tools/delete-document.js +49 -0
- package/dist/tools/delete-document.js.map +1 -0
- package/dist/tools/find-document.d.ts +33 -0
- package/dist/tools/find-document.js +97 -0
- package/dist/tools/find-document.js.map +1 -0
- package/dist/tools/find-global.d.ts +26 -0
- package/dist/tools/find-global.js +122 -0
- package/dist/tools/find-global.js.map +1 -0
- package/dist/tools/global-versions.d.ts +39 -0
- package/dist/tools/global-versions.js +132 -0
- package/dist/tools/global-versions.js.map +1 -0
- package/dist/tools/patch-global-layout.d.ts +31 -0
- package/dist/tools/patch-global-layout.js +127 -0
- package/dist/tools/patch-global-layout.js.map +1 -0
- package/dist/tools/patch-layout.d.ts +5 -8
- package/dist/tools/patch-layout.js +18 -100
- package/dist/tools/patch-layout.js.map +1 -1
- package/dist/tools/publish-draft.d.ts +5 -4
- package/dist/tools/publish-draft.js +6 -1
- package/dist/tools/publish-draft.js.map +1 -1
- package/dist/tools/publish-global-draft.d.ts +20 -0
- package/dist/tools/publish-global-draft.js +50 -0
- package/dist/tools/publish-global-draft.js.map +1 -0
- package/dist/tools/resolve-reference.d.ts +5 -4
- package/dist/tools/resolve-reference.js +4 -0
- package/dist/tools/resolve-reference.js.map +1 -1
- package/dist/tools/safe-delete.d.ts +5 -5
- package/dist/tools/safe-delete.js +20 -15
- package/dist/tools/safe-delete.js.map +1 -1
- package/dist/tools/schedule-publish.d.ts +5 -5
- package/dist/tools/schedule-publish.js +23 -19
- package/dist/tools/schedule-publish.js.map +1 -1
- package/dist/tools/search-content.d.ts +5 -9
- package/dist/tools/search-content.js +16 -12
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/update-document.d.ts +5 -5
- package/dist/tools/update-document.js +10 -5
- package/dist/tools/update-document.js.map +1 -1
- package/dist/tools/update-global.d.ts +27 -0
- package/dist/tools/update-global.js +72 -0
- package/dist/tools/update-global.js.map +1 -0
- package/dist/tools/upload-media.d.ts +5 -4
- package/dist/tools/upload-media.js +6 -1
- package/dist/tools/upload-media.js.map +1 -1
- package/dist/tools/versions.d.ts +10 -9
- package/dist/tools/versions.js +15 -7
- package/dist/tools/versions.js.map +1 -1
- package/dist/types.d.ts +56 -3
- package/dist/types.js +13 -6
- package/dist/types.js.map +1 -1
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -1,150 +1,232 @@
|
|
|
1
|
-
# payload-mcp-toolkit
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
- `
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
- `
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
##
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
1
|
+
# payload-mcp-toolkit
|
|
2
|
+
|
|
3
|
+
> Standalone schema-aware MCP plugin for Payload CMS v3. Owns the `/api/mcp` endpoint, scoped API keys, draft workflow, and AI-friendly authoring tools so non-technical editors can manage content via AI chat.
|
|
4
|
+
|
|
5
|
+
`payload-mcp-toolkit` is a single, self-contained Payload v3 plugin. It introspects your Payload config at boot, registers schema-aware **prompts**, **resources**, and **tools** for any MCP-compatible client (Claude Desktop, Claude API, Continue, Cline), and exposes them over `POST /api/mcp` with bearer-token authentication on a built-in API-keys collection.
|
|
6
|
+
|
|
7
|
+
It is the standalone successor to the toolkit's earlier wrapper around `@payloadcms/plugin-mcp` — see [Upgrading from 0.3.x](#upgrading-from-03x) below.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add payload-mcp-toolkit
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer dependencies: `payload` ^3, `zod` ^3.
|
|
16
|
+
|
|
17
|
+
## Configure — zero config
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// payload.config.ts
|
|
21
|
+
import { mcpToolkitPlugin } from 'payload-mcp-toolkit'
|
|
22
|
+
|
|
23
|
+
export default buildConfig({
|
|
24
|
+
// ...your collections, blocks, globals
|
|
25
|
+
serverURL: process.env.SITE_URL, // used for absolute preview URLs + Host check
|
|
26
|
+
admin: { user: 'users' }, // your auth collection
|
|
27
|
+
plugins: [mcpToolkitPlugin()],
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That is the entire integration. The toolkit:
|
|
32
|
+
|
|
33
|
+
- Adds the `payload-mcp-api-keys` collection (admin UI: **MCP → API Keys**).
|
|
34
|
+
- Registers a bearer authentication strategy on your user collection.
|
|
35
|
+
- Mounts `POST /api/mcp` and `GET /api/mcp` (the latter returns 405 with a JSON-RPC error so probing clients see something useful).
|
|
36
|
+
- Builds tools / prompts / resources from your introspected schema.
|
|
37
|
+
|
|
38
|
+
Everything else is inferred:
|
|
39
|
+
|
|
40
|
+
- **Draft behavior** — collections with `versions.drafts` get `always-draft` semantics (clients flow through `publishDraft` / `patchLayout` / `updateDocument`); others publish immediately.
|
|
41
|
+
- **Preview URLs** — pulled from each collection's `admin.livePreview.url` (or `admin.preview` as a fallback). Falls back to a generic admin-panel hint when neither is set.
|
|
42
|
+
- **Block nesting** — recorded for every blocks-typed field anywhere in the schema; the AI composes layouts at any depth from that map.
|
|
43
|
+
- **User collection** — `admin.user`.
|
|
44
|
+
|
|
45
|
+
## API keys
|
|
46
|
+
|
|
47
|
+
Create one in admin (**MCP → API Keys → Create**). The plaintext key is shown once on creation; from then on only its `keyPrefix` (first 8 chars) is visible.
|
|
48
|
+
|
|
49
|
+
Authenticate every MCP request with:
|
|
50
|
+
|
|
51
|
+
```http
|
|
52
|
+
POST /api/mcp HTTP/1.1
|
|
53
|
+
Authorization: Bearer <plaintext-key>
|
|
54
|
+
Content-Type: application/json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Scopes
|
|
58
|
+
|
|
59
|
+
Configure each key's permissions through typed admin fields — no JSON to hand-edit.
|
|
60
|
+
|
|
61
|
+
| Field | Effect |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `preset` | Role preset: **Read-only**, **Editor** (read + create + update on collections; read-only on globals — see below), **Admin** (all actions on both), or **Custom** (use the override fields below). Required. Defaults to **Custom** so new keys deny everything until explicitly scoped. |
|
|
64
|
+
| `collectionScopes` | Array of `{ collection, actions[] }`. Only honoured when preset is **Custom**. Each row whitelists a collection and the actions (`read` / `create` / `update` / `delete`) allowed on it. An empty `actions[]` denies all actions on that collection. Listed collections are a *whitelist* — collections not in the list are denied. |
|
|
65
|
+
| `globalScopes` | Array of `{ global, actions[] }`. Only honoured when preset is **Custom** *and* the host config has at least one global. Globals only support `read` and `update` (no `create` / `delete` — they're singletons). Same whitelist semantics as `collectionScopes`. |
|
|
66
|
+
| `toolAllow` | Multi-select. Only honoured when preset is **Custom**. If set, only these tools are callable with this key. **Note:** `toolAllow` without an explicit `collectionScopes` / `globalScopes` map or preset is treated as a deny — see [Globals](#globals). |
|
|
67
|
+
| `toolDeny` | Multi-select. Always applied on top of any preset. Tools listed here are blocked regardless of preset / collection / global scopes. |
|
|
68
|
+
|
|
69
|
+
The collection and tool dropdowns are populated at plugin-init time from your live Payload config + the toolkit's registered tools. Adding a collection or custom tool requires a dev-server / app restart for it to surface in the dropdowns.
|
|
70
|
+
|
|
71
|
+
The same shape is editable programmatically via Payload's REST and GraphQL APIs against the `payload-mcp-api-keys` collection — useful for seeding keys from CI or scripted provisioning.
|
|
72
|
+
|
|
73
|
+
### Lifecycle fields
|
|
74
|
+
|
|
75
|
+
| Field | Effect |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `name`, `description` | Human-readable identifier in the admin list. |
|
|
78
|
+
| `expiresAt` | Authentication rejects keys past this date. |
|
|
79
|
+
| `revokedAt` | Authentication rejects keys when set. |
|
|
80
|
+
| `lastUsedAt` | Updated fire-and-forget on each successful auth. |
|
|
81
|
+
| `keyPrefix` | First 8 chars of the plaintext, for audit-log identification. |
|
|
82
|
+
|
|
83
|
+
## What the plugin adds
|
|
84
|
+
|
|
85
|
+
**Auto-generated prompts:**
|
|
86
|
+
|
|
87
|
+
- `contentModelOverview` — every collection, fields, and relationships.
|
|
88
|
+
- `blockCompositionGuide` — section/leaf hierarchy and nesting rules.
|
|
89
|
+
- `draftWorkflowGuide` — which collections need `publishDraft` to go live.
|
|
90
|
+
|
|
91
|
+
**Auto-generated resources:** `blocks://catalog`, `collections://schema`, `collections://relationships`.
|
|
92
|
+
|
|
93
|
+
**Tools (13, plus an auto-registered scheduler):**
|
|
94
|
+
|
|
95
|
+
*Authoring*
|
|
96
|
+
- `createDocument` — local-API based creation for any collection. JSON-string `data`. Defaults to `draft: true` on draft-enabled collections.
|
|
97
|
+
- `updateDocument` — local-API based update. Replaces the upstream plugin's `update<Resource>` tools, which crash on collections containing richText/upload/blocks fields.
|
|
98
|
+
- `patchLayout` — surgical append/prepend/insertAt/replaceAt against any blocks-typed field. Validates each block recursively against the introspected nesting map.
|
|
99
|
+
- `uploadMedia` — fetch a public HTTPS image, validate (SSRF-safe with a streaming size cap), create a Media doc.
|
|
100
|
+
|
|
101
|
+
*Discovery*
|
|
102
|
+
- `findDocument` — read documents by `id` or `where` filter, polymorphic across collections. Decorates draft responses with preview URLs when configured.
|
|
103
|
+
- `resolveReference` — search collections by name/title/slug for relationship IDs.
|
|
104
|
+
- `searchContent` — natural-language editor triage (status, recency, missing fields, free text).
|
|
105
|
+
|
|
106
|
+
*Lifecycle / safety*
|
|
107
|
+
- `publishDraft` — flip `_status` from draft to published.
|
|
108
|
+
- `schedulePublish` — auto-registered for collections with drafts AND a `publishedAt` date field. Stamps a future `publishedAt`; you wire up the actual flip via Payload Jobs Queue / cron / `beforeRead`.
|
|
109
|
+
- `listVersions` — recent saved versions of a draft document.
|
|
110
|
+
- `restoreVersion` — roll a document back to a saved version (creates a new version, so reversible).
|
|
111
|
+
- `safeDelete` — relationship-aware delete. Walks the relationship graph; refuses with a structured impact summary if the doc has inbound references. Override with `confirm: true`.
|
|
112
|
+
- `deleteDocument` — fast unsafe delete (no relationship walk). Use only when you know the doc has no inbound references; prefer `safeDelete` for general use.
|
|
113
|
+
|
|
114
|
+
*Globals* (registered when the host config has at least one global)
|
|
115
|
+
- `findGlobal` — read any global by slug. Stamps a preview URL on draft documents when `admin.livePreview` / `admin.preview` is configured.
|
|
116
|
+
- `updateGlobal` — partial-merge update; same prose JSON contract as `updateDocument`. Draft-enabled globals default to `'always-draft'`.
|
|
117
|
+
- `patchGlobalLayout` — surgical block-array edits on any blocks-typed field inside a global, at any nesting depth (e.g. `footer.sections`). Registered only when at least one global has a blocks field.
|
|
118
|
+
- `publishGlobalDraft`, `listGlobalVersions`, `restoreGlobalVersion` — registered only for globals with `versions: { drafts: true }`.
|
|
119
|
+
|
|
120
|
+
## Globals
|
|
121
|
+
|
|
122
|
+
Globals (site-wide singletons such as site settings, navigation, footer) are exposed alongside collections through the tools listed above and a `globals://schema` resource. The admin UI gains a second "Global scopes" matrix beneath "Collection scopes" under the Custom preset; rows are global slugs, columns are `Read` / `Update`.
|
|
123
|
+
|
|
124
|
+
### Why `editor` is read-only on globals
|
|
125
|
+
|
|
126
|
+
The `editor` preset grants read-only access to globals — only `admin` (or a Custom key with explicit `globalScopes`) can write them. Collections under `editor` continue to get `read + create + update`.
|
|
127
|
+
|
|
128
|
+
The asymmetry exists because globals broadcast site-wide on a single write: site name, footer links, social handles, banner text. A typo in a global is visible on every page that consumes it, with no per-document containment to roll back. Editor-tier keys are typically given to AI agents acting on imperfect natural-language instructions, and `"fix the site title"` going wrong is a one-shot vandalism path against the whole site. If you need editor-tier keys to update specific globals, use the Custom preset with a `globalScopes` entry naming the global slug.
|
|
129
|
+
|
|
130
|
+
## Optional configuration
|
|
131
|
+
|
|
132
|
+
Every option is an escape hatch — pass only what you need:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
mcpToolkitPlugin({
|
|
136
|
+
auth: {
|
|
137
|
+
allowedOrigins: ['https://app.example.com'], // origin allow-list for the /api/mcp Origin/Host check; browser preflight not yet handled — see Known limitations
|
|
138
|
+
},
|
|
139
|
+
apiKeyCollection: {
|
|
140
|
+
slug: 'mcp-keys', // default 'payload-mcp-api-keys'
|
|
141
|
+
userCollection: 'admins', // default admin.user
|
|
142
|
+
},
|
|
143
|
+
preview: {
|
|
144
|
+
siteUrl: 'https://staging.example.com',
|
|
145
|
+
disabled: false,
|
|
146
|
+
},
|
|
147
|
+
draftBehavior: {
|
|
148
|
+
posts: 'always-publish', // publish immediately on update
|
|
149
|
+
},
|
|
150
|
+
userCollection: 'admins',
|
|
151
|
+
exclude: {
|
|
152
|
+
collections: ['internal-bookkeeping'],
|
|
153
|
+
globals: ['secret-config'],
|
|
154
|
+
},
|
|
155
|
+
mediaUpload: { maxFileSize: 25 * 1024 * 1024, collectionSlug: 'images' },
|
|
156
|
+
domainPrompts: [
|
|
157
|
+
{ name: 'siteVocabulary', title: 'Site Vocabulary', description: 'Site-specific terms.', content: '...' },
|
|
158
|
+
],
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Option | Description |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `auth.allowedOrigins` | Origins permitted on the `Origin` header for the DNS-rebinding check. Empty / unset means server-to-server only. `*` is intentionally not honoured. **Note:** browser MCP clients are not yet fully supported — the endpoint does not emit CORS response headers or handle the `OPTIONS` preflight. See [Known limitations](#known-limitations). |
|
|
165
|
+
| `apiKeyCollection.slug` | API-keys collection slug. Defaults to `payload-mcp-api-keys` for zero-touch upgrade compatibility. |
|
|
166
|
+
| `apiKeyCollection.userCollection` | User collection that API keys link to. Defaults to `userCollection` / `admin.user`. |
|
|
167
|
+
| `preview.siteUrl` | Base URL for preview links. Defaults to `serverURL`, then `NEXT_PUBLIC_SERVER_URL`/`SITE_URL` env vars. |
|
|
168
|
+
| `preview.disabled` | Suppress preview URL injection on draft responses. |
|
|
169
|
+
| `draftBehavior` | Per-collection override of inferred behavior. |
|
|
170
|
+
| `userCollection` | Override `admin.user` for API key linkage. |
|
|
171
|
+
| `exclude.collections` / `exclude.globals` | Hide from MCP exposure. |
|
|
172
|
+
| `domainPrompts` | Site-specific vocabulary prompts. |
|
|
173
|
+
| `mediaUpload.maxFileSize` | Default 10MB. Enforced as a streaming cap, not a post-buffer check. |
|
|
174
|
+
| `mediaUpload.collectionSlug` | Default `'media'`. |
|
|
175
|
+
|
|
176
|
+
## Upgrading from 0.5
|
|
177
|
+
|
|
178
|
+
v0.6 adds globals support across the MCP surface. The changes most likely to surprise an upgrade:
|
|
179
|
+
|
|
180
|
+
- **`editor` preset is read-only on globals.** Editor-tier keys cannot `updateGlobal` or `patchGlobalLayout`. Use the `admin` preset or a Custom key with explicit `globalScopes` for editor-tier writes. See [Why `editor` is read-only on globals](#why-editor-is-read-only-on-globals) for the rationale.
|
|
181
|
+
- **Audit log field rename.** The per-tool audit field `collectionArg` is replaced by `targetSlug` + `targetKind` (`'collection' | 'global' | 'account' | undefined`). Operators with SIEM rules / dashboards filtering on `collectionArg` must update their queries. The old field is gone — there is no compatibility alias, because the original field misreported for global operations.
|
|
182
|
+
- **`tools.allow` without an explicit resource scope is now a deny.** Previously `tools: { allow: ['updateDocument'] }` with no `collections` map and no preset implicitly allowed `updateDocument` on every collection. The fix lands now and applies symmetrically across collections and globals. If your keys rely on the `tools.allow`-only shape (not a documented configuration), add an explicit `collections` / `globals` map or a `preset`.
|
|
183
|
+
- **Production deploys need a migration.** Run `pnpm payload migrate:create` after upgrading to capture the new `globalScopes` JSONB column on `payload-mcp-api-keys`. Local dev with `push: true` syncs on the next `pnpm dev`.
|
|
184
|
+
|
|
185
|
+
## Upgrading from 0.3.x
|
|
186
|
+
|
|
187
|
+
v0.3.x wrapped `@payloadcms/plugin-mcp`. v0.4 owns the small remaining surface (transport, auth, API-key collection, find/delete) directly. The migration is short.
|
|
188
|
+
|
|
189
|
+
1. **Remove the upstream plugin** from `plugins[]`:
|
|
190
|
+
```diff
|
|
191
|
+
- import { mcpPlugin } from '@payloadcms/plugin-mcp'
|
|
192
|
+
- // ...
|
|
193
|
+
- plugins: [mcpToolkitPlugin(), mcpPlugin({ ... })],
|
|
194
|
+
+ plugins: [mcpToolkitPlugin()],
|
|
195
|
+
```
|
|
196
|
+
2. **Drop the dependency** from `package.json`:
|
|
197
|
+
```bash
|
|
198
|
+
pnpm remove @payloadcms/plugin-mcp
|
|
199
|
+
```
|
|
200
|
+
3. **Existing API keys keep authenticating zero-touch.** The `payload-mcp-api-keys` slug, `apiKey` / `apiKeyIndex` columns, and HMAC formula are all preserved.
|
|
201
|
+
4. **Re-scope each key** — see the [API keys](#api-keys) section. Open each existing key in admin, pick a preset (or **Custom** with explicit collection / tool overrides), and save. Until you do, keys carry no scopes and authenticate at full access.
|
|
202
|
+
5. **Browser MCP clients are not yet fully supported.** Server-to-server callers (no `Origin` header — backend scripts, Claude Desktop's local connector) work as before and require no opt-in. Browser-based clients additionally need CORS response headers and `OPTIONS` preflight handling, which haven't landed yet — see [Known limitations](#known-limitations).
|
|
203
|
+
|
|
204
|
+
If you forget step 1, the plugin throws on boot with the same message — it refuses to register two MCP plugins racing for the `payload-mcp-api-keys` slug.
|
|
205
|
+
|
|
206
|
+
## Known limitations
|
|
207
|
+
|
|
208
|
+
- **Browser MCP clients are not yet fully supported.** The `/api/mcp` endpoint validates the `Origin` / `Host` headers (DNS-rebinding protection) and the `auth.allowedOrigins` option restricts which origins may call it, but the endpoint does not yet emit CORS response headers (`Access-Control-Allow-Origin` etc.) or handle the `OPTIONS` preflight request that browsers send before authenticated cross-origin POSTs. Server-to-server callers (backend scripts, Claude Desktop's local connector — no `Origin` header) are unaffected. Full browser-client support will land in a follow-up release once there is a concrete client to validate against; until then, treat `auth.allowedOrigins` as a server-side allow-list, not a browser opt-in.
|
|
209
|
+
|
|
210
|
+
## Development
|
|
211
|
+
|
|
212
|
+
This package follows the [official Payload 3 plugin template](https://github.com/payloadcms/payload/tree/main/templates/plugin) layout: source in `src/`, a fully-working Payload + Next.js app in `dev/`, source-export `package.json` so the dev harness consumes the plugin directly without a build step.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
pnpm install
|
|
216
|
+
cp dev/.env.example dev/.env
|
|
217
|
+
pnpm dev # boot dev/ Next.js + Payload at http://localhost:3000
|
|
218
|
+
pnpm test # vitest — runs the unit + integration suite
|
|
219
|
+
pnpm build # produce dist/ for npm publish
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The dev harness ships with a realistic CMS schema:
|
|
223
|
+
|
|
224
|
+
- `Pages` — block-based layout (FullWidth, TwoColumn, CtaBanner, HeadingOnly), drafts enabled.
|
|
225
|
+
- `Posts` — title/slug/excerpt/content/cover/category/authors/tags/SEO, drafts enabled.
|
|
226
|
+
- `Authors`, `Categories`, `Media`, `Users` — taxonomy + auth.
|
|
227
|
+
- `SiteSettings` — global with site name, logo, social, footer.
|
|
228
|
+
- 5 leaf blocks (Heading, RichText, Image, ButtonGroup, Quote) and 4 section blocks.
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|