@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 +1 -1
- package/dist/admin/capability-matrix.d.mts +2 -1
- package/dist/admin/confirmations.d.mts +2 -1
- package/dist/admin/setup-guide.d.mts +2 -1
- package/dist/api-keys/capability-matrix.d.mts +2 -1
- package/dist/endpoint/handler.mjs +12 -10
- package/dist/local.d.mts +33 -0
- package/dist/local.mjs +51 -0
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.mjs +11 -1
- package/dist/types.d.mts +6 -2
- package/package.json +7 -3
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,
|
|
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
|
|
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
|
|
38
|
-
|
|
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 };
|
package/dist/local.d.mts
ADDED
|
@@ -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
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
86
|
+
"tsdown": "^0.23.1",
|
|
83
87
|
"typescript": "^6.0.3",
|
|
84
88
|
"unplugin-swc": "^2.0.0",
|
|
85
89
|
"vitest": "^5.0.3"
|