@abinnovision/payloadcms-mcpx 2.0.0-beta.20 → 2.0.0-beta.22
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 +2 -2
- package/dist/admin/setup-guide.d.mts +3 -3
- package/dist/admin/setup-guide.mjs +49 -32
- package/dist/api-keys/collection.mjs +7 -2
- package/dist/api-keys/fields.mjs +4 -12
- package/dist/types.d.mts +3 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ claude mcp add --transport http payload http://localhost:3000/api/mcpx \
|
|
|
82
82
|
|
|
83
83
|
Claude Desktop and the MCP Inspector are covered in
|
|
84
84
|
[`docs/integration.md`](./docs/integration.md#connecting-other-clients). Saved keys also have a
|
|
85
|
-
"Connect a client"
|
|
85
|
+
"Connect a client" button that opens ready-to-paste snippets.
|
|
86
86
|
|
|
87
87
|
## Options
|
|
88
88
|
|
|
@@ -96,7 +96,7 @@ Claude Desktop and the MCP Inspector are covered in
|
|
|
96
96
|
| `globals` | `{}` | Globals to expose, with `read`, `write` and `publish`. |
|
|
97
97
|
| `userCollection` | `config.admin.user`, then `users` | Auth collection whose users the keys act as. |
|
|
98
98
|
| `apiKeys.slug` | `mcpx-api-keys` | Slug of the key collection. |
|
|
99
|
-
| `apiKeys.setupGuide` | `true` | Add the "Connect a client"
|
|
99
|
+
| `apiKeys.setupGuide` | `true` | Add the "Connect a client" button to saved keys. |
|
|
100
100
|
| `apiKeys.overrideCollection` | none | Function that receives the key collection and returns it. |
|
|
101
101
|
| `endpoint.path` | `/mcpx` | Endpoint path below the API route. |
|
|
102
102
|
| `limits.maxLimit` | `25` | Highest `limit` a client may pass to a list tool. |
|
|
@@ -7,9 +7,9 @@ interface McpxSetupGuideProps {
|
|
|
7
7
|
endpointPath: string;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Per-key connection instructions on the API key edit view
|
|
11
|
-
*
|
|
12
|
-
* a client.
|
|
10
|
+
* Per-key connection instructions on the API key edit view, opened from a
|
|
11
|
+
* button next to the document controls. Renders nothing until the document is
|
|
12
|
+
* saved, because before that there is no key to hand to a client.
|
|
13
13
|
*/
|
|
14
14
|
export declare const McpxSetupGuide: React.FC<McpxSetupGuideProps>;
|
|
15
15
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { endpointUrl } from "./endpoint-url.mjs";
|
|
3
3
|
import { buildSetupGuide } from "../api-keys/setup-guide.mjs";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
import { CopyToClipboard, useConfig, useDocumentInfo, useFormFields } from "@payloadcms/ui";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Banner, Button, CopyToClipboard, Drawer, formatDrawerSlug, useConfig, useDocumentInfo, useEditDepth, useFormFields, useModal } from "@payloadcms/ui";
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
7
|
//#region src/admin/setup-guide.tsx
|
|
8
8
|
const asString = (value) => typeof value === "string" ? value : void 0;
|
|
@@ -14,7 +14,6 @@ const useOrigin = (serverUrl) => {
|
|
|
14
14
|
return origin;
|
|
15
15
|
};
|
|
16
16
|
const styles = {
|
|
17
|
-
lead: { marginBottom: "calc(var(--base) * 0.75)" },
|
|
18
17
|
section: { marginBottom: "calc(var(--base) * 0.75)" },
|
|
19
18
|
sectionHeader: {
|
|
20
19
|
display: "flex",
|
|
@@ -37,45 +36,63 @@ const styles = {
|
|
|
37
36
|
wordBreak: "break-all"
|
|
38
37
|
}
|
|
39
38
|
};
|
|
40
|
-
|
|
41
|
-
* Per-key connection instructions on the API key edit view. Renders nothing
|
|
42
|
-
* until the document is saved, because before that there is no key to hand to
|
|
43
|
-
* a client.
|
|
44
|
-
*/ const McpxSetupGuide = ({ endpointPath }) => {
|
|
45
|
-
const { id } = useDocumentInfo();
|
|
39
|
+
const SetupGuideContent = ({ endpointPath }) => {
|
|
46
40
|
const { config } = useConfig();
|
|
47
41
|
const apiKey = useFormFields(([fields]) => fields["apiKey"]?.value);
|
|
48
42
|
const label = useFormFields(([fields]) => fields["label"]?.value);
|
|
49
43
|
const origin = useOrigin(config.serverURL);
|
|
50
|
-
if (id === void 0) return null;
|
|
51
44
|
const sections = buildSetupGuide({
|
|
52
45
|
endpointUrl: endpointUrl(origin, config.routes.api, endpointPath),
|
|
53
46
|
apiKey: asString(apiKey),
|
|
54
47
|
label: asString(label)
|
|
55
48
|
});
|
|
56
|
-
return /*#__PURE__*/ jsxs(
|
|
57
|
-
|
|
58
|
-
children:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/*#__PURE__*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
section.description
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
49
|
+
return /*#__PURE__*/ jsxs(Fragment, { children: [/*#__PURE__*/ jsx(Banner, {
|
|
50
|
+
type: "warning",
|
|
51
|
+
children: "Every snippet below contains this key in full. Treat it like a password."
|
|
52
|
+
}), sections.map((section) => /*#__PURE__*/ jsxs("section", {
|
|
53
|
+
style: styles.section,
|
|
54
|
+
children: [
|
|
55
|
+
/*#__PURE__*/ jsxs("div", {
|
|
56
|
+
style: styles.sectionHeader,
|
|
57
|
+
children: [/*#__PURE__*/ jsx("strong", { children: section.title }), /*#__PURE__*/ jsx(CopyToClipboard, { value: section.snippet })]
|
|
58
|
+
}),
|
|
59
|
+
section.description ? /*#__PURE__*/ jsx("p", {
|
|
60
|
+
style: styles.description,
|
|
61
|
+
children: section.description
|
|
62
|
+
}) : null,
|
|
63
|
+
/*#__PURE__*/ jsx("pre", {
|
|
64
|
+
style: styles.snippet,
|
|
65
|
+
children: section.snippet
|
|
66
|
+
})
|
|
67
|
+
]
|
|
68
|
+
}, section.id))] });
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Per-key connection instructions on the API key edit view, opened from a
|
|
72
|
+
* button next to the document controls. Renders nothing until the document is
|
|
73
|
+
* saved, because before that there is no key to hand to a client.
|
|
74
|
+
*/ const McpxSetupGuide = ({ endpointPath }) => {
|
|
75
|
+
const { id } = useDocumentInfo();
|
|
76
|
+
const { openModal } = useModal();
|
|
77
|
+
const drawerSlug = formatDrawerSlug({
|
|
78
|
+
slug: "mcpx-connect",
|
|
79
|
+
depth: useEditDepth()
|
|
78
80
|
});
|
|
81
|
+
if (id === void 0) return null;
|
|
82
|
+
return /*#__PURE__*/ jsxs(Fragment, { children: [/*#__PURE__*/ jsx(Button, {
|
|
83
|
+
buttonStyle: "secondary",
|
|
84
|
+
margin: false,
|
|
85
|
+
onClick: () => {
|
|
86
|
+
openModal(drawerSlug);
|
|
87
|
+
},
|
|
88
|
+
size: "medium",
|
|
89
|
+
type: "button",
|
|
90
|
+
children: "Connect a client"
|
|
91
|
+
}), /*#__PURE__*/ jsx(Drawer, {
|
|
92
|
+
slug: drawerSlug,
|
|
93
|
+
title: "Connect a client",
|
|
94
|
+
children: /*#__PURE__*/ jsx(SetupGuideContent, { endpointPath })
|
|
95
|
+
})] });
|
|
79
96
|
};
|
|
80
97
|
//#endregion
|
|
81
98
|
export { McpxSetupGuide };
|
|
@@ -28,7 +28,12 @@ import { generateApiKey, hashApiKey } from "./key.mjs";
|
|
|
28
28
|
admin: {
|
|
29
29
|
group: "MCP",
|
|
30
30
|
useAsTitle: "label",
|
|
31
|
-
description: "Keys for MCP clients. Each key acts as its user and may only do what its capabilities allow."
|
|
31
|
+
description: "Keys for MCP clients. Each key acts as its user and may only do what its capabilities allow.",
|
|
32
|
+
...options.setupGuide && { components: { edit: { beforeDocumentControls: [{
|
|
33
|
+
path: "@abinnovision/payloadcms-mcpx/admin",
|
|
34
|
+
exportName: "McpxSetupGuide",
|
|
35
|
+
clientProps: { endpointPath: options.endpointPath }
|
|
36
|
+
}] } } }
|
|
32
37
|
},
|
|
33
38
|
access: {
|
|
34
39
|
create: isUser,
|
|
@@ -45,7 +50,7 @@ import { generateApiKey, hashApiKey } from "./key.mjs";
|
|
|
45
50
|
access: SERVER_ONLY,
|
|
46
51
|
defaultValue: ({ req }) => isUser({ req }) ? req.user?.id : void 0,
|
|
47
52
|
admin: { description: "The user this key acts as." }
|
|
48
|
-
}, ...createKeyFields()], createCapabilityFields(options)
|
|
53
|
+
}, ...createKeyFields()], createCapabilityFields(options))]
|
|
49
54
|
};
|
|
50
55
|
};
|
|
51
56
|
//#endregion
|
package/dist/api-keys/fields.mjs
CHANGED
|
@@ -96,13 +96,10 @@ const adminField = (name, exportName, options) => ({
|
|
|
96
96
|
* modified.
|
|
97
97
|
*/ const createConfirmationFields = (options) => options.confirmations ? [adminField("confirmations", "McpxConfirmations", options)] : [];
|
|
98
98
|
/**
|
|
99
|
-
* Wraps the key fields
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
*
|
|
104
|
-
* The guide tab only shows on update: on create there is no key to hand out.
|
|
105
|
-
*/ const withKeyTabs = (keyFields, capabilityFields, options) => {
|
|
99
|
+
* Wraps the key fields and the capabilities in unnamed tabs, so wide tables
|
|
100
|
+
* get the full form width. Named tabs would nest the data and move
|
|
101
|
+
* `capabilities` off the document root, which capability resolution reads.
|
|
102
|
+
*/ const withKeyTabs = (keyFields, capabilityFields) => {
|
|
106
103
|
const tabs = [{
|
|
107
104
|
label: "Key",
|
|
108
105
|
fields: keyFields
|
|
@@ -111,11 +108,6 @@ const adminField = (name, exportName, options) => ({
|
|
|
111
108
|
label: "Capabilities",
|
|
112
109
|
fields: capabilityFields
|
|
113
110
|
});
|
|
114
|
-
if (options.setupGuide) tabs.push({
|
|
115
|
-
label: "Connect a client",
|
|
116
|
-
admin: { condition: (_data, _siblingData, { operation }) => operation === "update" },
|
|
117
|
-
fields: [adminField("setupGuide", "McpxSetupGuide", options)]
|
|
118
|
-
});
|
|
119
111
|
return [{
|
|
120
112
|
type: "tabs",
|
|
121
113
|
tabs
|
package/dist/types.d.mts
CHANGED
|
@@ -284,8 +284,9 @@ export type McpxPluginOptions = {
|
|
|
284
284
|
*/
|
|
285
285
|
slug?: string;
|
|
286
286
|
/**
|
|
287
|
-
* Add a "Connect a client"
|
|
288
|
-
* client config. Default `true`. The snippets contain
|
|
287
|
+
* Add a "Connect a client" button to saved keys, opening a drawer with
|
|
288
|
+
* ready-to-paste MCP client config. Default `true`. The snippets contain
|
|
289
|
+
* the key in full.
|
|
289
290
|
*/
|
|
290
291
|
setupGuide?: boolean;
|
|
291
292
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@abinnovision/payloadcms-mcpx",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.22",
|
|
5
5
|
"description": "Payload CMS plugin exposing a fixed, schema-aware MCP tool surface with draft-only writes and per-API-key capabilities.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"payload",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"prettier": "@abinnovision/prettier-config",
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
61
61
|
"rfc6902": "^5.3.0",
|
|
62
62
|
"zod": "^4.6.5"
|
|
63
63
|
},
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@types/node": "^26.6.3",
|
|
74
74
|
"@types/react": "^19.3.0",
|
|
75
75
|
"@vitest/coverage-v8": "^5.0.3",
|
|
76
|
-
"eslint": "^10.
|
|
76
|
+
"eslint": "^10.12.0",
|
|
77
77
|
"payload": "3.90.2",
|
|
78
78
|
"prettier": "^3.9.9",
|
|
79
79
|
"publint": "^0.3.25",
|