@abinnovision/payloadcms-mcpx 2.1.0-beta.2 → 2.2.0-beta.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.0](https://github.com/abinnovision/payloadcms-commons/compare/payloadcms-mcpx-v2.0.0...payloadcms-mcpx-v2.1.0) (2026-10-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * **mcpx:** expose payload folders read-only by default ([#129](https://github.com/abinnovision/payloadcms-commons/issues/129)) ([1b2dd55](https://github.com/abinnovision/payloadcms-commons/commit/1b2dd55305fb84991f9a585006a3a33687bb22a7))
9
+ * **mcpx:** rename grants to leases and share the expiry check ([#127](https://github.com/abinnovision/payloadcms-commons/issues/127)) ([7170bd5](https://github.com/abinnovision/payloadcms-commons/commit/7170bd5d592226c6bb7f356fb17ec1b19a59e608))
10
+
3
11
  ## [2.0.0](https://github.com/abinnovision/payloadcms-commons/compare/payloadcms-mcpx-v1.0.0...payloadcms-mcpx-v2.0.0) (2026-10-08)
4
12
 
5
13
 
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.
@@ -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/options.mjs CHANGED
@@ -194,7 +194,7 @@ const normalizeLimits = (limits) => {
194
194
  auth: options.auth,
195
195
  serverInfo: {
196
196
  name: options.serverInfo?.name ?? "payloadcms-mcpx",
197
- version: options.serverInfo?.version ?? "2.0.0"
197
+ version: options.serverInfo?.version ?? "2.1.0"
198
198
  },
199
199
  diagnostics: options.diagnostics ?? true
200
200
  };
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,10 @@ 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
381
  //#endregion
package/dist/version.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  //#region src/version.ts
2
2
  /**
3
3
  * Package version injected at build time; sources under Vitest report "dev".
4
- */ const MCPX_VERSION = "2.0.0";
4
+ */ const MCPX_VERSION = "2.1.0";
5
5
  const MCPX_REPOSITORY_URL = "https://github.com/abinnovision/payloadcms-commons/tree/main/packages/mcpx";
6
6
  const MCPX_ISSUES_URL = "https://github.com/abinnovision/payloadcms-commons/issues";
7
7
  //#endregion
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-beta.2",
4
+ "version": "2.2.0-beta.1",
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": [