@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 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" tab with ready-to-paste snippets.
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" tab to saved keys. |
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. Renders nothing
11
- * until the document is saved, because before that there is no key to hand to
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("div", {
57
- className: "field-type",
58
- children: [/*#__PURE__*/ jsx("p", {
59
- style: styles.lead,
60
- children: "Every snippet below contains this key in full. Treat it like a password."
61
- }), sections.map((section) => /*#__PURE__*/ jsxs("section", {
62
- style: styles.section,
63
- children: [
64
- /*#__PURE__*/ jsxs("div", {
65
- style: styles.sectionHeader,
66
- children: [/*#__PURE__*/ jsx("strong", { children: section.title }), /*#__PURE__*/ jsx(CopyToClipboard, { value: section.snippet })]
67
- }),
68
- section.description ? /*#__PURE__*/ jsx("p", {
69
- style: styles.description,
70
- children: section.description
71
- }) : null,
72
- /*#__PURE__*/ jsx("pre", {
73
- style: styles.snippet,
74
- children: section.snippet
75
- })
76
- ]
77
- }, section.id))]
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), options)]
53
+ }, ...createKeyFields()], createCapabilityFields(options))]
49
54
  };
50
55
  };
51
56
  //#endregion
@@ -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, the capabilities and the setup guide in unnamed tabs,
100
- * so wide tables and snippets get the full form width. Named tabs would nest
101
- * the data and move `capabilities` off the document root, which capability
102
- * resolution reads.
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" tab to saved keys, holding ready-to-paste MCP
288
- * client config. Default `true`. The snippets contain the key in full.
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.20",
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.31.0",
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.11.0",
76
+ "eslint": "^10.12.0",
77
77
  "payload": "3.90.2",
78
78
  "prettier": "^3.9.9",
79
79
  "publint": "^0.3.25",