@abinnovision/payloadcms-mcpx 2.1.0 → 2.2.0-beta.16

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
@@ -201,7 +201,7 @@ Upgrading from 1.x: [Upgrading to 2.0](./docs/integration.md#upgrading-to-20).
201
201
  - [`docs/concepts.md`](./docs/concepts.md): tool arguments, schema paths and pointers, patching,
202
202
  rich text, globals, uploads, drafts and publishing, versions and diffs.
203
203
  - [`docs/integration.md`](./docs/integration.md): the endpoint, startup validation, API keys in the
204
- admin panel, and custom key resolution.
204
+ admin panel, custom key resolution, and the in-process client.
205
205
  - [`docs/custom-tools.md`](./docs/custom-tools.md): defining your own tools.
206
206
  - [`docs/security.md`](./docs/security.md): authentication, access, limits and known limitations.
207
207
  - [`docs/limitations.md`](./docs/limitations.md): what the plugin does not do.
@@ -32,4 +32,5 @@ interface McpxCapabilityMatrixProps {
32
32
  * unaffected.
33
33
  */
34
34
  export declare const McpxCapabilityMatrix: React.FC<McpxCapabilityMatrixProps>;
35
- //#endregion
35
+ //#endregion
36
+ export {};
@@ -12,4 +12,5 @@ interface McpxConfirmationsProps {
12
12
  * does not mark the key modified. Renders nothing while none is waiting.
13
13
  */
14
14
  export declare const McpxConfirmations: React.FC<McpxConfirmationsProps>;
15
- //#endregion
15
+ //#endregion
16
+ export {};
@@ -12,4 +12,5 @@ interface McpxSetupGuideProps {
12
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
- //#endregion
15
+ //#endregion
16
+ export {};
@@ -31,4 +31,5 @@ export interface CapabilityMatrix {
31
31
  globals: CapabilityRow[];
32
32
  tools: CapabilityTool[];
33
33
  }
34
- //#endregion
34
+ //#endregion
35
+ export {};
@@ -31,22 +31,24 @@ const BATCH_MESSAGES_LIMIT = 10;
31
31
  };
32
32
  };
33
33
  /**
34
- * Makes `req` act as the key: sets `req.user`, replacing any user Payload
34
+ * Makes `req` act as `user`: sets `req.user`, replacing any user Payload
35
35
  * resolved from cookies, and the `req.context.mcpx` stamp the draft guard
36
36
  * reads. Returns the scope the tools see.
37
- */ const authenticateAs = (req, options, auth) => {
38
- const capabilities = resolveCapabilities(options, auth.capabilities);
39
- req.user = auth.user;
37
+ */ const actAs = (req, options, user, mcpx) => {
38
+ req.user = user;
40
39
  req.context = {
41
40
  ...req.context,
42
- mcpx: {
43
- apiKeyId: auth.apiKeyId,
44
- capabilities
45
- }
41
+ mcpx
46
42
  };
47
- return buildScope(req, options, capabilities);
43
+ return buildScope(req, options, mcpx.capabilities);
48
44
  };
49
45
  /**
46
+ * Makes `req` act as the key, with the capabilities its checkboxes resolve to.
47
+ */ const authenticateAs = (req, options, auth) => actAs(req, options, auth.user, {
48
+ apiKeyId: auth.apiKeyId,
49
+ capabilities: resolveCapabilities(options, auth.capabilities)
50
+ });
51
+ /**
50
52
  * Answers GET and DELETE on the endpoint path. The server is stateless and
51
53
  * never streams, so only POST carries meaning.
52
54
  */ const methodNotAllowed = () => jsonRpcError({
@@ -117,4 +119,4 @@ const BATCH_MESSAGES_LIMIT = 10;
117
119
  }
118
120
  };
119
121
  //#endregion
120
- export { authenticateAs, buildScope, createMcpxHandler, methodNotAllowed };
122
+ export { actAs, authenticateAs, buildScope, createMcpxHandler, methodNotAllowed };
@@ -0,0 +1,33 @@
1
+ import { Payload, TypedUser } from "payload";
2
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
+ //#region src/local.d.ts
4
+ /**
5
+ * What one entity grants, keyed by operation. Deleting is not offered here.
6
+ */
7
+ export interface McpxLocalEntityCapabilities {
8
+ read?: boolean;
9
+ write?: boolean;
10
+ publish?: boolean;
11
+ }
12
+ /**
13
+ * What an in-process client may do, keyed by collection slug, global slug and
14
+ * tool name. The plugin config caps it the same way it caps an API key.
15
+ */
16
+ export interface McpxLocalCapabilities {
17
+ collections?: Record<string, McpxLocalEntityCapabilities>;
18
+ globals?: Record<string, McpxLocalEntityCapabilities>;
19
+ tools?: Record<string, boolean>;
20
+ }
21
+ /**
22
+ * An MCP client connected in process to the tools of the plugin installed on
23
+ * `payload`, acting as `user` with `capabilities`. The caller vouches for
24
+ * `user`: no API key is checked, and Payload's access control runs as that
25
+ * user. Without a key there is nothing to approve or upload against, so
26
+ * deletes, uploads and confirmations are unavailable. `close` also closes the
27
+ * server.
28
+ */
29
+ export declare const createMcpxClient: (payload: Payload, args: {
30
+ user: TypedUser;
31
+ capabilities: McpxLocalCapabilities;
32
+ }) => Promise<Client>;
33
+ //#endregion
package/dist/local.mjs ADDED
@@ -0,0 +1,51 @@
1
+ import { ownValue } from "./guards.mjs";
2
+ import { resolveCapabilities } from "./capabilities.mjs";
3
+ import { relationId } from "./auth/resolve.mjs";
4
+ import { MCPX_VERSION } from "./version.mjs";
5
+ import { createMcpServer } from "./endpoint/server.mjs";
6
+ import { actAs } from "./endpoint/handler.mjs";
7
+ import { optionsOf } from "./plugin.mjs";
8
+ import { createLocalReq } from "payload";
9
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
10
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
11
+ //#region src/local.ts
12
+ const storedGroup = (entities, granted) => Object.fromEntries(entities.map((entity) => {
13
+ const operations = ownValue(granted, entity.slug);
14
+ return [entity.fieldName, {
15
+ read: operations?.read,
16
+ write: operations?.write,
17
+ publish: operations?.publish
18
+ }];
19
+ }));
20
+ /**
21
+ * An MCP client connected in process to the tools of the plugin installed on
22
+ * `payload`, acting as `user` with `capabilities`. The caller vouches for
23
+ * `user`: no API key is checked, and Payload's access control runs as that
24
+ * user. Without a key there is nothing to approve or upload against, so
25
+ * deletes, uploads and confirmations are unavailable. `close` also closes the
26
+ * server.
27
+ */ const createMcpxClient = async (payload, args) => {
28
+ const options = optionsOf(payload);
29
+ const { user, capabilities } = args;
30
+ if (relationId(user) === void 0 || user.collection !== options.userCollection) throw new Error(`[payloadcms-mcpx] createMcpxClient needs a user of the "${options.userCollection}" collection with an id.`);
31
+ const req = await createLocalReq({ user }, payload);
32
+ const scope = actAs(req, options, user, { capabilities: resolveCapabilities(options, {
33
+ collections: storedGroup(options.collections, capabilities.collections),
34
+ globals: storedGroup(options.globals, capabilities.globals),
35
+ tools: capabilities.tools
36
+ }) });
37
+ const server = createMcpServer({
38
+ ...scope,
39
+ uploads: false
40
+ }, options);
41
+ const client = new Client({
42
+ name: "mcpx-local",
43
+ version: MCPX_VERSION
44
+ });
45
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
46
+ await server.connect(serverTransport);
47
+ await client.connect(clientTransport);
48
+ return client;
49
+ };
50
+ //#endregion
51
+ export { createMcpxClient };
package/dist/plugin.d.mts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { McpxPluginOptions } from "./types.mjs";
2
+ import "./options.mjs";
3
+ import { Payload } from "payload";
2
4
  //#region src/plugin.d.ts
3
5
  /**
4
6
  * Mounts the MCP, upload and download endpoints, and the confirmation
package/dist/plugin.mjs CHANGED
@@ -7,6 +7,15 @@ import { assertFolderCollection, normalizeOptions } from "./options.mjs";
7
7
  import { installDraftGuards, installGlobalDraftGuards } from "./write/draft-guard.mjs";
8
8
  import { definePlugin } from "payload";
9
9
  //#region src/plugin.ts
10
+ const initialized = /* @__PURE__ */ new WeakMap();
11
+ /**
12
+ * The normalized options of the plugin installed on `payload`. Throws where
13
+ * the plugin is not installed or Payload has not run `onInit`.
14
+ */ const optionsOf = (payload) => {
15
+ const options = initialized.get(payload.config);
16
+ if (options === void 0) throw new Error("[payloadcms-mcpx] The plugin is not installed on this Payload instance, or Payload has not been initialized.");
17
+ return options;
18
+ };
10
19
  /**
11
20
  * Mounts the MCP, upload and download endpoints, and the confirmation
12
21
  * endpoints where a collection exposes `delete`. Adds the API key collection
@@ -25,6 +34,7 @@ import { definePlugin } from "payload";
25
34
  globals: installGlobalDraftGuards(config.globals ?? []),
26
35
  onInit: async (payload) => {
27
36
  assertFolderCollection(payload.config, normalized);
37
+ initialized.set(payload.config, normalized);
28
38
  await config.onInit?.(payload);
29
39
  },
30
40
  endpoints: [
@@ -60,4 +70,4 @@ import { definePlugin } from "payload";
60
70
  }
61
71
  });
62
72
  //#endregion
63
- export { mcpxPlugin };
73
+ export { mcpxPlugin, optionsOf };
package/dist/types.d.mts CHANGED
@@ -372,7 +372,11 @@ export interface McpxResolvedCapabilities {
372
372
  * Stamped on `req.context.mcpx`; see {@link isMcpxRequest}.
373
373
  */
374
374
  export interface McpxRequestContext {
375
- apiKeyId: DocumentId;
375
+ /**
376
+ * `undefined` for a client from `createMcpxClient`, which acts without a key.
377
+ */
378
+ apiKeyId?: DocumentId;
376
379
  capabilities: McpxResolvedCapabilities;
377
380
  }
378
- //#endregion
381
+ //#endregion
382
+ export {};
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.1.0",
4
+ "version": "2.2.0-beta.16",
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",
@@ -30,6 +30,10 @@
30
30
  "./admin": {
31
31
  "types": "./dist/admin/index.d.mts",
32
32
  "default": "./dist/admin/index.mjs"
33
+ },
34
+ "./local": {
35
+ "types": "./dist/local.d.mts",
36
+ "default": "./dist/local.mjs"
33
37
  }
34
38
  },
35
39
  "files": [
@@ -75,11 +79,11 @@
75
79
  "@vitest/coverage-v8": "^5.0.3",
76
80
  "eslint": "^10.12.0",
77
81
  "payload": "3.90.2",
78
- "prettier": "^3.9.9",
82
+ "prettier": "^3.9.10",
79
83
  "publint": "^0.3.25",
80
84
  "react": "^19.3.0",
81
85
  "react-dom": "^19.3.0",
82
- "tsdown": "^0.23.0",
86
+ "tsdown": "^0.23.1",
83
87
  "typescript": "^6.0.3",
84
88
  "unplugin-swc": "^2.0.0",
85
89
  "vitest": "^5.0.3"