@abinnovision/payloadcms-mcpx 1.0.0-beta.10 → 1.0.0-beta.11
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 +57 -4
- package/dist/endpoint/handler.mjs +8 -4
- package/dist/endpoint/result.d.mts +14 -0
- package/dist/endpoint/server.mjs +22 -21
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +3 -2
- package/dist/tools/create-document.mjs +4 -3
- package/dist/tools/describe-schema.mjs +4 -3
- package/dist/tools/find-documents.mjs +5 -4
- package/dist/tools/get-document.mjs +4 -3
- package/dist/tools/index.mjs +6 -3
- package/dist/tools/list-capabilities.mjs +7 -6
- package/dist/tools/patch-document.mjs +8 -6
- package/dist/tools/shared.mjs +15 -10
- package/dist/tools/target.d.mts +1 -1
- package/dist/tools/validate-document.mjs +4 -3
- package/dist/types.d.mts +95 -11
- package/dist/types.mjs +3 -4
- package/package.json +1 -1
- package/dist/options.d.mts +0 -2
- package/dist/tools/types.d.mts +0 -5
package/README.md
CHANGED
|
@@ -297,9 +297,62 @@ const publishQueue = defineMcpxTool({
|
|
|
297
297
|
|
|
298
298
|
Each custom tool gets its own checkbox on every API key, default off.
|
|
299
299
|
|
|
300
|
-
Custom
|
|
301
|
-
|
|
302
|
-
|
|
300
|
+
Custom tools take the same route as the builtins: one `McpxTool` shape, one
|
|
301
|
+
registration loop. Anything a builtin does, a custom tool can do.
|
|
302
|
+
|
|
303
|
+
`handler` receives `scope` alongside `args`, `req` and `extra`. The scope
|
|
304
|
+
carries what the key may touch (`readable`, `writable`, `readableGlobals`,
|
|
305
|
+
`writableGlobals`), the configured locales, the limits in force and the
|
|
306
|
+
exposed collections and globals. `req` is shorthand for `scope.req`.
|
|
307
|
+
|
|
308
|
+
`inputSchema` may be a function of that scope instead of a fixed shape, which
|
|
309
|
+
is how a tool narrows an enum to what the key may read:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { defineMcpxTool } from "@abinnovision/payloadcms-mcpx";
|
|
313
|
+
import { z } from "zod";
|
|
314
|
+
|
|
315
|
+
const whichCollection = defineMcpxTool({
|
|
316
|
+
name: "whichCollection",
|
|
317
|
+
description: "Echoes back one of the collections this key may read.",
|
|
318
|
+
isEnabled: (scope) =>
|
|
319
|
+
scope.capabilities.tools["whichCollection"] === true &&
|
|
320
|
+
scope.readable.length > 0,
|
|
321
|
+
inputSchema: (scope) => ({
|
|
322
|
+
collection: z.enum(scope.readable as [string, ...string[]]),
|
|
323
|
+
}),
|
|
324
|
+
handler: ({ args }) => ({
|
|
325
|
+
content: [{ type: "text", text: args.collection }],
|
|
326
|
+
}),
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
`defineMcpxTool` defines every tool, builtin ones included, and infers the
|
|
331
|
+
handler's arguments from the input schema either way: from a fixed shape, or
|
|
332
|
+
from the object literal a per-request shape returns. Above, `args` is
|
|
333
|
+
`{ collection: string }` without being told.
|
|
334
|
+
|
|
335
|
+
Inference reaches as far as the shape's static type. A helper returning
|
|
336
|
+
`z.ZodRawShape` erases that type and leaves `args` as
|
|
337
|
+
`Record<string, unknown>`, so the builtins' shape helpers declare the superset
|
|
338
|
+
they produce instead: which keys a helper emits depends on the key's scope,
|
|
339
|
+
and the declared type states what a handler must cope with across every scope.
|
|
340
|
+
Their arguments stay derived from their schema that way, and cannot drift from
|
|
341
|
+
it. If your own helpers erase, state the arguments as a type argument:
|
|
342
|
+
`defineMcpxTool<Args>({ ... })`.
|
|
343
|
+
|
|
344
|
+
`isEnabled` decides whether the tool is registered for this key at all: a tool
|
|
345
|
+
that is not enabled never appears in `tools/list`. It defaults to the tool's
|
|
346
|
+
own checkbox, which is what the builtins replace to derive their availability
|
|
347
|
+
from the key's collection and global capabilities. Defining it **replaces**
|
|
348
|
+
the checkbox check, so restate `scope.capabilities.tools[name]` when you still
|
|
349
|
+
want it, as above.
|
|
350
|
+
|
|
351
|
+
Every input schema is registered strictly, custom tools included: an unknown
|
|
352
|
+
argument is rejected by name rather than stripped before the handler runs.
|
|
353
|
+
|
|
354
|
+
`jsonResult` and `errorResult` are exported so a custom tool can return
|
|
355
|
+
results shaped like a builtin's.
|
|
303
356
|
|
|
304
357
|
## Options
|
|
305
358
|
|
|
@@ -320,7 +373,7 @@ Builtin tools reject them instead.
|
|
|
320
373
|
| `endpoint.path` | `/mcpx` | Endpoint path below the API route. |
|
|
321
374
|
| `limits.maxLimit` | `25` | Upper bound for `findDocuments.limit`. |
|
|
322
375
|
| `limits.maxDepth` | `1` | Upper bound for `depth` on reads. |
|
|
323
|
-
| `tools` | `[]` | Custom tools.
|
|
376
|
+
| `tools` | `[]` | Custom tools, defined the same way as the builtins. |
|
|
324
377
|
| `auth.resolve` | none | Replace or wrap the default key resolution. |
|
|
325
378
|
| `serverInfo` | package name and version | Reported to MCP clients. |
|
|
326
379
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { jsonRpcError } from "./result.mjs";
|
|
1
2
|
import { readableGlobalSlugs, readableSlugs, resolveCapabilities, writableGlobalSlugs, writableSlugs } from "../capabilities.mjs";
|
|
2
3
|
import { resolveApiKeyAuth } from "../auth/resolve.mjs";
|
|
3
|
-
import { jsonRpcError } from "./result.mjs";
|
|
4
4
|
import { createMcpServer } from "./server.mjs";
|
|
5
5
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
6
6
|
//#region src/endpoint/handler.ts
|
|
@@ -8,14 +8,18 @@ const buildScope = (req, options, capabilities) => {
|
|
|
8
8
|
const { localization } = req.payload.config;
|
|
9
9
|
return {
|
|
10
10
|
req,
|
|
11
|
-
options,
|
|
12
11
|
capabilities,
|
|
13
12
|
readable: readableSlugs(capabilities),
|
|
14
13
|
writable: writableSlugs(capabilities),
|
|
15
14
|
readableGlobals: readableGlobalSlugs(capabilities),
|
|
16
15
|
writableGlobals: writableGlobalSlugs(capabilities),
|
|
17
16
|
locales: localization ? localization.localeCodes : null,
|
|
18
|
-
defaultLocale: localization ? localization.defaultLocale : null
|
|
17
|
+
defaultLocale: localization ? localization.defaultLocale : null,
|
|
18
|
+
limits: options.limits,
|
|
19
|
+
exposure: {
|
|
20
|
+
collections: options.collections,
|
|
21
|
+
globals: options.globals
|
|
22
|
+
}
|
|
19
23
|
};
|
|
20
24
|
};
|
|
21
25
|
/**
|
|
@@ -68,7 +72,7 @@ const buildScope = (req, options, capabilities) => {
|
|
|
68
72
|
code: -32600,
|
|
69
73
|
message: "Invalid request: a JSON body is required."
|
|
70
74
|
});
|
|
71
|
-
const server = createMcpServer(buildScope(req, options, capabilities));
|
|
75
|
+
const server = createMcpServer(buildScope(req, options, capabilities), options);
|
|
72
76
|
const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
|
|
73
77
|
await server.connect(transport);
|
|
74
78
|
const headers = new Headers(req.headers);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "payload";
|
|
2
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
//#region src/endpoint/result.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A successful tool result carrying `value` as JSON text.
|
|
6
|
+
*/
|
|
7
|
+
declare const jsonResult: (value: unknown) => CallToolResult;
|
|
8
|
+
/**
|
|
9
|
+
* A failed tool result. `extras` travel alongside the message so the client
|
|
10
|
+
* can act on them (problems, validation errors, the current `updatedAt`).
|
|
11
|
+
*/
|
|
12
|
+
declare const errorResult: (message: string, extras?: Record<string, unknown>) => CallToolResult;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { errorResult, jsonResult };
|
package/dist/endpoint/server.mjs
CHANGED
|
@@ -4,16 +4,24 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
//#region src/endpoint/server.ts
|
|
6
6
|
/**
|
|
7
|
-
* Builds a
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
7
|
+
* Builds a tool's input schema as a strict object, so an unknown argument is
|
|
8
|
+
* rejected with its name instead of being silently stripped and the tool
|
|
9
|
+
* answering as if it had not been passed. A tool may build its shape from the
|
|
10
|
+
* scope to narrow enums to what the key may touch.
|
|
11
|
+
*/ const toolInputSchema = (tool, scope) => z.strictObject(typeof tool.inputSchema === "function" ? tool.inputSchema(scope) : tool.inputSchema ?? {});
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
* Whether the key may call the tool. A tool that does not decide for itself is
|
|
14
|
+
* gated by its own checkbox on the key, which is how the tools from
|
|
15
|
+
* `options.tools` work; the builtins derive it from the key's collection and
|
|
16
|
+
* global capabilities instead.
|
|
17
|
+
*/ const isToolEnabled = (tool, scope) => tool.isEnabled ? tool.isEnabled(scope) : scope.capabilities.tools[tool.name] === true;
|
|
18
|
+
/**
|
|
19
|
+
* Builds the MCP server for one request. Builtin and configured tools take the
|
|
20
|
+
* same route: each is registered against the key's capabilities, so
|
|
21
|
+
* `tools/list` shows exactly what the key may call and every `collection` enum
|
|
22
|
+
* is limited to what it may touch.
|
|
23
|
+
*/ const createMcpServer = (scope, options) => {
|
|
24
|
+
const { req } = scope;
|
|
17
25
|
const { logger } = req.payload;
|
|
18
26
|
const server = new McpServer({
|
|
19
27
|
name: options.serverInfo.name,
|
|
@@ -26,22 +34,15 @@ import { z } from "zod";
|
|
|
26
34
|
return toToolError(error, logger);
|
|
27
35
|
}
|
|
28
36
|
};
|
|
29
|
-
for (const tool of BUILTIN_TOOLS) {
|
|
30
|
-
if (!tool
|
|
31
|
-
server.registerTool(tool.name, {
|
|
32
|
-
description: tool.description,
|
|
33
|
-
inputSchema: builtinInputSchema(tool, scope),
|
|
34
|
-
annotations: tool.annotations
|
|
35
|
-
}, (args) => guarded(() => tool.handler(args, scope))());
|
|
36
|
-
}
|
|
37
|
-
for (const tool of options.tools) {
|
|
38
|
-
if (capabilities.tools[tool.name] !== true) continue;
|
|
37
|
+
for (const tool of [...BUILTIN_TOOLS, ...options.tools]) {
|
|
38
|
+
if (!isToolEnabled(tool, scope)) continue;
|
|
39
39
|
server.registerTool(tool.name, {
|
|
40
40
|
description: tool.description,
|
|
41
|
-
inputSchema: tool
|
|
41
|
+
inputSchema: toolInputSchema(tool, scope),
|
|
42
42
|
...tool.annotations ? { annotations: tool.annotations } : {}
|
|
43
43
|
}, (args, extra) => guarded(() => tool.handler({
|
|
44
44
|
args,
|
|
45
|
+
scope,
|
|
45
46
|
req,
|
|
46
47
|
extra
|
|
47
48
|
}))());
|
|
@@ -49,4 +50,4 @@ import { z } from "zod";
|
|
|
49
50
|
return server;
|
|
50
51
|
};
|
|
51
52
|
//#endregion
|
|
52
|
-
export {
|
|
53
|
+
export { createMcpServer, isToolEnabled, toolInputSchema };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { errorResult, jsonResult } from "./endpoint/result.mjs";
|
|
2
|
+
import { McpxAnyTool, McpxAuthResult, McpxCollectionCapabilities, McpxCollectionOptions, McpxExposedEntity, McpxGlobalOptions, McpxPluginOptions, McpxRequestContext, McpxResolvedCapabilities, McpxTool, McpxToolExtra, McpxToolScope, defineMcpxTool } from "./types.mjs";
|
|
2
3
|
import { mcpxPlugin } from "./plugin.mjs";
|
|
3
4
|
import { isMcpxRequest } from "./write/draft-guard.mjs";
|
|
4
5
|
import { PublishBlocker } from "./write/publish-blockers.mjs";
|
|
5
|
-
export { type McpxAuthResult, type McpxCollectionCapabilities, type McpxCollectionOptions, type McpxGlobalOptions, type McpxPluginOptions, type McpxRequestContext, type McpxResolvedCapabilities, type McpxTool, type McpxToolExtra, type PublishBlocker, defineMcpxTool, isMcpxRequest, mcpxPlugin };
|
|
6
|
+
export { type McpxAnyTool, type McpxAuthResult, type McpxCollectionCapabilities, type McpxCollectionOptions, type McpxExposedEntity, type McpxGlobalOptions, type McpxPluginOptions, type McpxRequestContext, type McpxResolvedCapabilities, type McpxTool, type McpxToolExtra, type McpxToolScope, type PublishBlocker, defineMcpxTool, errorResult, isMcpxRequest, jsonResult, mcpxPlugin };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { errorResult, jsonResult } from "./endpoint/result.mjs";
|
|
2
|
+
import { defineMcpxTool } from "./types.mjs";
|
|
1
3
|
import { isMcpxRequest } from "./write/draft-guard.mjs";
|
|
2
4
|
import { mcpxPlugin } from "./plugin.mjs";
|
|
3
|
-
|
|
4
|
-
export { defineMcpxTool, isMcpxRequest, mcpxPlugin };
|
|
5
|
+
export { defineMcpxTool, errorResult, isMcpxRequest, jsonResult, mcpxPlugin };
|
|
@@ -2,11 +2,12 @@ import { errorResult, jsonResult } from "../endpoint/result.mjs";
|
|
|
2
2
|
import { localeOf, localeShape, readTarget, slugEnum } from "./shared.mjs";
|
|
3
3
|
import { resolveTarget } from "./target.mjs";
|
|
4
4
|
import { validateWriteValue } from "../schema/shape.mjs";
|
|
5
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
5
6
|
import { stripRowIds } from "../write/patch.mjs";
|
|
6
7
|
import { collectPublishBlockers } from "../write/publish-blockers.mjs";
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
//#region src/tools/create-document.ts
|
|
9
|
-
const createDocument = {
|
|
10
|
+
const createDocument = defineMcpxTool({
|
|
10
11
|
name: "createDocument",
|
|
11
12
|
description: `Creates a new document as a draft from a minimal seed. Only the fields describeSchema lists may appear in "data"; unknown keys are refused with the valid siblings. The draft may be incomplete: the response lists "publishBlockers", which patchDocument can then work through. Use this when no document exists yet; prefer patching an existing draft otherwise.`,
|
|
12
13
|
annotations: {
|
|
@@ -24,7 +25,7 @@ const createDocument = {
|
|
|
24
25
|
}),
|
|
25
26
|
data: z.record(z.string(), z.unknown()).describe("Initial field values, as describeSchema lists them.")
|
|
26
27
|
}),
|
|
27
|
-
handler: async (args, scope) => {
|
|
28
|
+
handler: async ({ args, scope }) => {
|
|
28
29
|
const target = resolveTarget(scope, { collection: args.collection }, "write");
|
|
29
30
|
const { payload } = scope.req;
|
|
30
31
|
const locale = localeOf(scope, args.locale);
|
|
@@ -63,6 +64,6 @@ const createDocument = {
|
|
|
63
64
|
...publishBlockers.length > 0 ? { publishBlockers } : {}
|
|
64
65
|
});
|
|
65
66
|
}
|
|
66
|
-
};
|
|
67
|
+
});
|
|
67
68
|
//#endregion
|
|
68
69
|
export { createDocument };
|
|
@@ -2,10 +2,11 @@ import { translatorFor } from "../i18n.mjs";
|
|
|
2
2
|
import { jsonResult } from "../endpoint/result.mjs";
|
|
3
3
|
import { targetShape } from "./shared.mjs";
|
|
4
4
|
import { refOf, resolveTarget } from "./target.mjs";
|
|
5
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
5
6
|
import { nodeDescriber, reachableSchemaPaths } from "../schema/describe.mjs";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
//#region src/tools/describe-schema.ts
|
|
8
|
-
const describeSchema = {
|
|
9
|
+
const describeSchema = defineMcpxTool({
|
|
9
10
|
name: "describeSchema",
|
|
10
11
|
description: `Describes the writable shape of a document, one node at a time.
|
|
11
12
|
|
|
@@ -31,7 +32,7 @@ Fields Payload maintains (id, _status, createdAt, updatedAt) are never listed an
|
|
|
31
32
|
paths: z.array(z.string()).optional().describe("Schema paths to describe, e.g. \"/layout/sections/sectionWrapper\". Omit for the collection root."),
|
|
32
33
|
expand: z.boolean().optional().describe("Return every node reachable from the root in one response. Ignores paths.")
|
|
33
34
|
}),
|
|
34
|
-
handler: (args, scope) => {
|
|
35
|
+
handler: ({ args, scope }) => {
|
|
35
36
|
const ref = refOf(resolveTarget(scope, args, "read"));
|
|
36
37
|
const { config } = scope.req.payload;
|
|
37
38
|
const describeNode = nodeDescriber(translatorFor(scope.req.i18n));
|
|
@@ -49,6 +50,6 @@ Fields Payload maintains (id, _status, createdAt, updatedAt) are never listed an
|
|
|
49
50
|
if (expanded?.truncated) nodes.push({ error: `Result truncated after ${String(400)} nodes. Request explicit paths instead.` });
|
|
50
51
|
return Promise.resolve(jsonResult(nodes));
|
|
51
52
|
}
|
|
52
|
-
};
|
|
53
|
+
});
|
|
53
54
|
//#endregion
|
|
54
55
|
export { describeSchema };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsonResult } from "../endpoint/result.mjs";
|
|
2
2
|
import { depthShape, localeOf, localeShape, slugEnum } from "./shared.mjs";
|
|
3
3
|
import { resolveTarget } from "./target.mjs";
|
|
4
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
//#region src/tools/find-documents.ts
|
|
6
|
-
const findDocuments = {
|
|
7
|
+
const findDocuments = defineMcpxTool({
|
|
7
8
|
name: "findDocuments",
|
|
8
9
|
description: `Finds documents in a collection. "where" is a Payload query object, e.g. {"title":{"contains":"home"}} or {"and":[...]}; "select" picks fields, e.g. {"title":true}. Drafts are included by default so unpublished work is visible. Keep depth at 0 unless populated relationships are needed; ids are enough for writes.`,
|
|
9
10
|
annotations: {
|
|
@@ -15,7 +16,7 @@ const findDocuments = {
|
|
|
15
16
|
collection: slugEnum(scope.readable).describe("Collection to search."),
|
|
16
17
|
where: z.record(z.string(), z.unknown()).optional().describe("Payload where query."),
|
|
17
18
|
sort: z.string().optional().describe("Sort field, prefix with \"-\" for descending."),
|
|
18
|
-
limit: z.number().int().min(1).max(scope.
|
|
19
|
+
limit: z.number().int().min(1).max(scope.limits.maxLimit).optional().describe(`Documents per page. Default 10, at most ${String(scope.limits.maxLimit)}.`),
|
|
19
20
|
page: z.number().int().min(1).optional().describe("Page number, from 1."),
|
|
20
21
|
...depthShape(scope),
|
|
21
22
|
select: z.record(z.string(), z.unknown()).optional().describe("Fields to return, e.g. {\"title\":true}."),
|
|
@@ -25,7 +26,7 @@ const findDocuments = {
|
|
|
25
26
|
}),
|
|
26
27
|
draft: z.boolean().optional().describe("Include the latest drafts. Default true.")
|
|
27
28
|
}),
|
|
28
|
-
handler: async (args, scope) => {
|
|
29
|
+
handler: async ({ args, scope }) => {
|
|
29
30
|
resolveTarget(scope, { collection: args.collection }, "read");
|
|
30
31
|
const locale = localeOf(scope, args.locale);
|
|
31
32
|
const result = await scope.req.payload.find({
|
|
@@ -50,6 +51,6 @@ const findDocuments = {
|
|
|
50
51
|
hasNextPage: result.hasNextPage
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
|
-
};
|
|
54
|
+
});
|
|
54
55
|
//#endregion
|
|
55
56
|
export { findDocuments };
|
|
@@ -2,10 +2,11 @@ import { JSON_POINTER_PATTERN } from "../schema/walk.mjs";
|
|
|
2
2
|
import { errorResult, jsonResult } from "../endpoint/result.mjs";
|
|
3
3
|
import { depthShape, idShape, localeOf, localeShape, targetShape } from "./shared.mjs";
|
|
4
4
|
import { requireIdFor, resolveTarget } from "./target.mjs";
|
|
5
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { Pointer } from "rfc6902";
|
|
7
8
|
//#region src/tools/get-document.ts
|
|
8
|
-
const getDocument = {
|
|
9
|
+
const getDocument = defineMcpxTool({
|
|
9
10
|
name: "getDocument",
|
|
10
11
|
description: `Reads one document, or one subtree of it when "path" is given as a JSON pointer such as "/layout/sections/2". Returns the latest draft by default. Read before patching: the response carries "updatedAt" for expectedUpdatedAt and the indices pointers need.
|
|
11
12
|
|
|
@@ -29,7 +30,7 @@ Pass exactly one of "collection" and "global". "id" is required with "collection
|
|
|
29
30
|
}),
|
|
30
31
|
draft: z.boolean().optional().describe("Return the latest draft. Default true.")
|
|
31
32
|
}),
|
|
32
|
-
handler: async (args, scope) => {
|
|
33
|
+
handler: async ({ args, scope }) => {
|
|
33
34
|
const target = resolveTarget(scope, args, "read");
|
|
34
35
|
const id = requireIdFor(target, args.id);
|
|
35
36
|
const locale = localeOf(scope, args.locale);
|
|
@@ -63,6 +64,6 @@ Pass exactly one of "collection" and "global". "id" is required with "collection
|
|
|
63
64
|
value
|
|
64
65
|
});
|
|
65
66
|
}
|
|
66
|
-
};
|
|
67
|
+
});
|
|
67
68
|
//#endregion
|
|
68
69
|
export { getDocument };
|
package/dist/tools/index.mjs
CHANGED
|
@@ -7,9 +7,12 @@ import { patchDocument } from "./patch-document.mjs";
|
|
|
7
7
|
import { validateDocument } from "./validate-document.mjs";
|
|
8
8
|
//#region src/tools/index.ts
|
|
9
9
|
/**
|
|
10
|
-
* The builtin tools in registration order.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The builtin tools in registration order. They are ordinary {@link McpxTool}s
|
|
11
|
+
* that ship with the plugin and register through the same loop as the tools
|
|
12
|
+
* from `options.tools`; only their `isEnabled` differs, deriving from the
|
|
13
|
+
* key's collection and global capabilities rather than a checkbox of their
|
|
14
|
+
* own. The surface is fixed: adding a collection, block or field never
|
|
15
|
+
* changes it.
|
|
13
16
|
*/ const BUILTIN_TOOLS = [
|
|
14
17
|
listCapabilities,
|
|
15
18
|
describeSchema,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { translatorFor } from "../i18n.mjs";
|
|
2
2
|
import { jsonResult } from "../endpoint/result.mjs";
|
|
3
3
|
import { translateLabel } from "./shared.mjs";
|
|
4
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
4
5
|
import { hasDraftValidationEnabled } from "payload/shared";
|
|
5
6
|
//#region src/tools/list-capabilities.ts
|
|
6
|
-
const listCapabilities = {
|
|
7
|
+
const listCapabilities = defineMcpxTool({
|
|
7
8
|
name: "listCapabilities",
|
|
8
9
|
description: `Lists what this key may do: the collections and globals it can read or write, their draft behaviour and id type, the configured locales, the limits in force and the custom tools available. Call it first to orient; nothing here changes with the content model.
|
|
9
10
|
|
|
@@ -14,10 +15,10 @@ A global is a singleton: it has no id, is not listed by findDocuments and cannot
|
|
|
14
15
|
},
|
|
15
16
|
isEnabled: () => true,
|
|
16
17
|
inputSchema: () => ({}),
|
|
17
|
-
handler: (
|
|
18
|
+
handler: ({ scope }) => {
|
|
18
19
|
const { payload } = scope.req;
|
|
19
20
|
const translate = translatorFor(scope.req.i18n);
|
|
20
|
-
const collections = scope.
|
|
21
|
+
const collections = scope.exposure.collections.flatMap((entry) => {
|
|
21
22
|
const capability = scope.capabilities.collections[entry.slug];
|
|
22
23
|
const collection = payload.collections[entry.slug];
|
|
23
24
|
if (!capability || !collection || !(capability.read || capability.write)) return [];
|
|
@@ -37,7 +38,7 @@ A global is a singleton: it has no id, is not listed by findDocuments and cannot
|
|
|
37
38
|
idType: collection.customIDType ?? payload.db.defaultIDType
|
|
38
39
|
}];
|
|
39
40
|
});
|
|
40
|
-
const globals = scope.
|
|
41
|
+
const globals = scope.exposure.globals.flatMap((entry) => {
|
|
41
42
|
const capability = scope.capabilities.globals[entry.slug];
|
|
42
43
|
const config = payload.globals.config.find((candidate) => candidate.slug === entry.slug);
|
|
43
44
|
if (!capability || !config || !(capability.read || capability.write)) return [];
|
|
@@ -59,10 +60,10 @@ A global is a singleton: it has no id, is not listed by findDocuments and cannot
|
|
|
59
60
|
codes: scope.locales,
|
|
60
61
|
default: scope.defaultLocale
|
|
61
62
|
} : null,
|
|
62
|
-
limits: scope.
|
|
63
|
+
limits: scope.limits,
|
|
63
64
|
tools: Object.entries(scope.capabilities.tools).filter(([, enabled]) => enabled).map(([name]) => name)
|
|
64
65
|
}));
|
|
65
66
|
}
|
|
66
|
-
};
|
|
67
|
+
});
|
|
67
68
|
//#endregion
|
|
68
69
|
export { listCapabilities };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { errorResult, jsonResult } from "../endpoint/result.mjs";
|
|
2
2
|
import { idShape, localeOf, localeShape, readTarget, targetShape } from "./shared.mjs";
|
|
3
3
|
import { refOf, requireIdFor, resolveTarget } from "./target.mjs";
|
|
4
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
4
5
|
import { PATCH_OPERATION_SCHEMA, applyPatchToCopy, buildWriteData, findPatchProblems, isElementPointer } from "../write/patch.mjs";
|
|
5
6
|
import { collectPublishBlockers } from "../write/publish-blockers.mjs";
|
|
6
7
|
import { withTransaction } from "../write/transaction.mjs";
|
|
@@ -42,7 +43,7 @@ const isPlainObject = (value) => typeof value === "object" && value !== null &&
|
|
|
42
43
|
const actual = pointer.get(saved);
|
|
43
44
|
return survives(expected, actual) ? [] : [operation.path];
|
|
44
45
|
});
|
|
45
|
-
const patchDocument = {
|
|
46
|
+
const patchDocument = defineMcpxTool({
|
|
46
47
|
name: "patchDocument",
|
|
47
48
|
description: DESCRIPTION,
|
|
48
49
|
annotations: {
|
|
@@ -65,11 +66,12 @@ const patchDocument = {
|
|
|
65
66
|
patches: z.array(PATCH_OPERATION_SCHEMA).min(1).describe("Operations, applied in order."),
|
|
66
67
|
expectedUpdatedAt: z.string().optional().describe("The updatedAt read before patching. The write is refused if the document has changed since.")
|
|
67
68
|
}),
|
|
68
|
-
handler: async (args, scope) => {
|
|
69
|
+
handler: async ({ args, scope }) => {
|
|
69
70
|
const target = resolveTarget(scope, args, "write");
|
|
70
71
|
const id = requireIdFor(target, args.id);
|
|
71
72
|
const { payload } = scope.req;
|
|
72
73
|
const locale = localeOf(scope, args.locale);
|
|
74
|
+
const patches = args.patches;
|
|
73
75
|
return await withTransaction(scope.req, async () => {
|
|
74
76
|
const doc = await readTarget(scope, {
|
|
75
77
|
target,
|
|
@@ -79,11 +81,11 @@ const patchDocument = {
|
|
|
79
81
|
if (args.expectedUpdatedAt !== void 0 && !sameInstant(doc["updatedAt"], args.expectedUpdatedAt)) return errorResult("The document changed since you read it. Read it again and re-apply the patch.", { updatedAt: doc["updatedAt"] });
|
|
80
82
|
const problems = findPatchProblems(payload.config, {
|
|
81
83
|
doc,
|
|
82
|
-
patches
|
|
84
|
+
patches,
|
|
83
85
|
ref: refOf(target)
|
|
84
86
|
});
|
|
85
87
|
if (problems.length > 0) return errorResult("No operation was applied.", { problems });
|
|
86
|
-
const applied = applyPatchToCopy(doc,
|
|
88
|
+
const applied = applyPatchToCopy(doc, patches);
|
|
87
89
|
if ("problems" in applied) return errorResult("No operation was applied.", { problems: applied.problems });
|
|
88
90
|
const write = {
|
|
89
91
|
data: buildWriteData(payload.config, target.config, applied.next),
|
|
@@ -108,7 +110,7 @@ const patchDocument = {
|
|
|
108
110
|
locale,
|
|
109
111
|
privileged: true
|
|
110
112
|
});
|
|
111
|
-
const notApplied = notAppliedPointers(
|
|
113
|
+
const notApplied = notAppliedPointers(patches, applied.next, saved);
|
|
112
114
|
const publishBlockers = await collectPublishBlockers(scope.req, {
|
|
113
115
|
doc: saved,
|
|
114
116
|
entity: target
|
|
@@ -122,6 +124,6 @@ const patchDocument = {
|
|
|
122
124
|
});
|
|
123
125
|
});
|
|
124
126
|
}
|
|
125
|
-
};
|
|
127
|
+
});
|
|
126
128
|
//#endregion
|
|
127
129
|
export { patchDocument };
|
package/dist/tools/shared.mjs
CHANGED
|
@@ -4,6 +4,11 @@ import { z } from "zod";
|
|
|
4
4
|
//#region src/tools/shared.ts
|
|
5
5
|
const slugEnum = (slugs) => z.enum(slugs);
|
|
6
6
|
const idSchema = z.union([z.string(), z.number()]).describe("Document id.");
|
|
7
|
+
/**
|
|
8
|
+
* Widens one branch to the superset a handler sees. The widening itself is
|
|
9
|
+
* unchecked — the runtime shape really does vary — so `Branch` checks what it
|
|
10
|
+
* can around it.
|
|
11
|
+
*/ const widen = (branch) => branch;
|
|
7
12
|
const slugsFor = (scope, operation) => ({
|
|
8
13
|
collections: operation === "read" ? scope.readable : scope.writable,
|
|
9
14
|
globals: operation === "read" ? scope.readableGlobals : scope.writableGlobals
|
|
@@ -18,12 +23,12 @@ const slugsFor = (scope, operation) => ({
|
|
|
18
23
|
* argument optional, and the handler enforces the exclusivity there.
|
|
19
24
|
*/ const targetShape = (scope, operation, descriptions) => {
|
|
20
25
|
const { collections, globals } = slugsFor(scope, operation);
|
|
21
|
-
if (globals.length === 0) return { collection: slugEnum(collections).describe(descriptions.collection) };
|
|
22
|
-
if (collections.length === 0) return { global: slugEnum(globals).describe(descriptions.global) };
|
|
23
|
-
return {
|
|
26
|
+
if (globals.length === 0) return widen({ collection: slugEnum(collections).describe(descriptions.collection) });
|
|
27
|
+
if (collections.length === 0) return widen({ global: slugEnum(globals).describe(descriptions.global) });
|
|
28
|
+
return widen({
|
|
24
29
|
collection: slugEnum(collections).optional().describe(descriptions.collection),
|
|
25
30
|
global: slugEnum(globals).optional().describe(descriptions.global)
|
|
26
|
-
};
|
|
31
|
+
});
|
|
27
32
|
};
|
|
28
33
|
/**
|
|
29
34
|
* The `id` argument, which only a collection document has. Omitted when the key
|
|
@@ -31,18 +36,18 @@ const slugsFor = (scope, operation) => ({
|
|
|
31
36
|
* in between, where `requireIdFor` enforces the dependency.
|
|
32
37
|
*/ const idShape = (scope, operation) => {
|
|
33
38
|
const { collections, globals } = slugsFor(scope, operation);
|
|
34
|
-
if (collections.length === 0) return {};
|
|
35
|
-
if (globals.length === 0) return { id: idSchema };
|
|
36
|
-
return { id: idSchema.optional().describe("Document id. Required with \"collection\"; must be omitted with \"global\".") };
|
|
39
|
+
if (collections.length === 0) return widen({});
|
|
40
|
+
if (globals.length === 0) return widen({ id: idSchema });
|
|
41
|
+
return widen({ id: idSchema.optional().describe("Document id. Required with \"collection\"; must be omitted with \"global\".") });
|
|
37
42
|
};
|
|
38
43
|
/**
|
|
39
44
|
* The `locale` argument, present only when localization is configured.
|
|
40
45
|
*/ const localeShape = (scope, options) => {
|
|
41
|
-
if (!scope.locales) return {};
|
|
46
|
+
if (!scope.locales) return widen({});
|
|
42
47
|
const locale = z.enum(scope.locales);
|
|
43
|
-
return { locale: (options.required ? locale : locale.optional()).describe(options.description) };
|
|
48
|
+
return widen({ locale: (options.required ? locale : locale.optional()).describe(options.description) });
|
|
44
49
|
};
|
|
45
|
-
const depthShape = (scope) => ({ depth: z.number().int().min(0).max(scope.
|
|
50
|
+
const depthShape = (scope) => ({ depth: z.number().int().min(0).max(scope.limits.maxDepth).optional().describe(`Relationship population depth. Default 0, at most ${String(scope.limits.maxDepth)}.`) });
|
|
46
51
|
/**
|
|
47
52
|
* The locale to operate on: the explicit argument, else the request's, else
|
|
48
53
|
* the default. `undefined` when localization is off.
|
package/dist/tools/target.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsonResult } from "../endpoint/result.mjs";
|
|
2
2
|
import { idShape, localeOf, localeShape, readTarget, targetShape } from "./shared.mjs";
|
|
3
3
|
import { requireIdFor, resolveTarget } from "./target.mjs";
|
|
4
|
+
import { defineMcpxTool } from "../types.mjs";
|
|
4
5
|
import { collectPublishBlockers } from "../write/publish-blockers.mjs";
|
|
5
6
|
//#region src/tools/validate-document.ts
|
|
6
|
-
const validateDocument = {
|
|
7
|
+
const validateDocument = defineMcpxTool({
|
|
7
8
|
name: "validateDocument",
|
|
8
9
|
description: `Reports what still prevents a human from publishing the draft, without writing anything. The same list patchDocument returns after a write; use it to check work or to answer "is this ready".
|
|
9
10
|
|
|
@@ -24,7 +25,7 @@ Pass exactly one of "collection" and "global". "id" is required with "collection
|
|
|
24
25
|
description: "Locale to validate."
|
|
25
26
|
})
|
|
26
27
|
}),
|
|
27
|
-
handler: async (args, scope) => {
|
|
28
|
+
handler: async ({ args, scope }) => {
|
|
28
29
|
const target = resolveTarget(scope, args, "write");
|
|
29
30
|
const id = requireIdFor(target, args.id);
|
|
30
31
|
const locale = localeOf(scope, args.locale);
|
|
@@ -50,6 +51,6 @@ Pass exactly one of "collection" and "global". "id" is required with "collection
|
|
|
50
51
|
publishBlockers
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
|
-
};
|
|
54
|
+
});
|
|
54
55
|
//#endregion
|
|
55
56
|
export { validateDocument };
|
package/dist/types.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CollectionConfig, CollectionSlug, GlobalSlug, PayloadRequest, TypedUser } from "payload";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
3
|
import { CallToolResult, ServerNotification, ServerRequest, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
declare module "payload" {
|
|
7
7
|
interface RequestContext {
|
|
@@ -53,26 +53,110 @@ interface McpxGlobalOptions {
|
|
|
53
53
|
}
|
|
54
54
|
type McpxToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;
|
|
55
55
|
/**
|
|
56
|
-
* A
|
|
57
|
-
*
|
|
56
|
+
* A collection or global the plugin config exposes, before an API key's
|
|
57
|
+
* checkboxes narrow it further.
|
|
58
|
+
*/
|
|
59
|
+
interface McpxExposedEntity {
|
|
60
|
+
slug: string;
|
|
61
|
+
read: boolean;
|
|
62
|
+
write: boolean;
|
|
63
|
+
allowLiveWrites: boolean;
|
|
64
|
+
hasDrafts: boolean;
|
|
65
|
+
/** Name of the capability group on the key document. */
|
|
66
|
+
fieldName: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Everything a tool knows about the current request: the authenticated
|
|
70
|
+
* request, what this key may touch and the limits in force.
|
|
71
|
+
*/
|
|
72
|
+
interface McpxToolScope {
|
|
73
|
+
req: PayloadRequest;
|
|
74
|
+
capabilities: McpxResolvedCapabilities;
|
|
75
|
+
/** Collection slugs the key may read / write. */
|
|
76
|
+
readable: string[];
|
|
77
|
+
writable: string[];
|
|
78
|
+
/** Global slugs the key may read / write. */
|
|
79
|
+
readableGlobals: string[];
|
|
80
|
+
writableGlobals: string[];
|
|
81
|
+
/** Configured locale codes, or `null` when localization is off. */
|
|
82
|
+
locales: null | string[];
|
|
83
|
+
defaultLocale: null | string;
|
|
84
|
+
limits: {
|
|
85
|
+
maxLimit: number;
|
|
86
|
+
maxDepth: number;
|
|
87
|
+
};
|
|
88
|
+
/** What the plugin config exposes, before the key's checkboxes apply. */
|
|
89
|
+
exposure: {
|
|
90
|
+
collections: McpxExposedEntity[];
|
|
91
|
+
globals: McpxExposedEntity[];
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A tool. The builtins and any tool passed through `options.tools` use this
|
|
96
|
+
* same shape and register through the same loop. Every tool runs with
|
|
97
|
+
* `req.user` resolved from the key and `req.context.mcpx` set.
|
|
98
|
+
*
|
|
99
|
+
* `Args` only needs stating when `inputSchema` is built per request, which
|
|
100
|
+
* leaves no static shape to infer from; a tool with a fixed shape gets its
|
|
101
|
+
* argument type from that shape.
|
|
58
102
|
*/
|
|
59
|
-
interface McpxTool<Shape extends z.ZodRawShape = z.ZodRawShape
|
|
103
|
+
interface McpxTool<Shape extends z.ZodRawShape = z.ZodRawShape, Args = z.infer<z.ZodObject<Shape>>> {
|
|
60
104
|
/** camelCase, unique, not one of the builtin tool names. */
|
|
61
105
|
name: string;
|
|
62
106
|
description: string;
|
|
63
|
-
inputSchema?: Shape;
|
|
64
107
|
annotations?: ToolAnnotations;
|
|
108
|
+
/**
|
|
109
|
+
* Whether this key may call the tool; a tool that is not enabled never
|
|
110
|
+
* appears in `tools/list`. Defaults to the tool's own checkbox on the API
|
|
111
|
+
* key, which the builtins replace to derive availability from the key's
|
|
112
|
+
* collection and global capabilities. Defining it replaces that checkbox
|
|
113
|
+
* check rather than adding to it.
|
|
114
|
+
*/
|
|
115
|
+
isEnabled?: (scope: McpxToolScope) => boolean;
|
|
116
|
+
/**
|
|
117
|
+
* A fixed shape, or one built per request so enums can be narrowed to what
|
|
118
|
+
* the key may touch. Registered strictly either way: an unknown argument is
|
|
119
|
+
* rejected by name instead of being stripped and the tool answering as if
|
|
120
|
+
* it had not been passed.
|
|
121
|
+
*/
|
|
122
|
+
inputSchema?: Shape | ((scope: McpxToolScope) => z.ZodRawShape);
|
|
65
123
|
handler(ctx: {
|
|
66
|
-
args:
|
|
124
|
+
args: Args;
|
|
125
|
+
scope: McpxToolScope;
|
|
126
|
+
/** Shorthand for `scope.req`. */
|
|
67
127
|
req: PayloadRequest;
|
|
68
128
|
extra: McpxToolExtra;
|
|
69
129
|
}): CallToolResult | Promise<CallToolResult>;
|
|
70
130
|
}
|
|
71
131
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
132
|
+
* A tool with its argument type erased, which is how a registry holds tools of
|
|
133
|
+
* differing input shapes. Each tool validates its own arguments through its
|
|
134
|
+
* input schema.
|
|
135
|
+
*/
|
|
136
|
+
type McpxAnyTool = McpxTool<z.ZodRawShape, never>;
|
|
137
|
+
/**
|
|
138
|
+
* Defines a tool with a fixed input shape. The handler's arguments are
|
|
139
|
+
* inferred from that shape.
|
|
140
|
+
*/
|
|
141
|
+
declare function defineMcpxTool<Shape extends z.ZodRawShape>(tool: McpxTool<Shape> & {
|
|
142
|
+
inputSchema?: Shape;
|
|
143
|
+
}): McpxTool<Shape>;
|
|
144
|
+
/**
|
|
145
|
+
* Defines a tool whose input shape is built per request and returned as an
|
|
146
|
+
* object literal. The handler's arguments are inferred from that literal, so
|
|
147
|
+
* a scope-narrowed enum still types as the value it produces.
|
|
148
|
+
*/
|
|
149
|
+
declare function defineMcpxTool<Shape extends z.ZodRawShape>(tool: McpxTool<Shape> & {
|
|
150
|
+
inputSchema: (scope: McpxToolScope) => Shape;
|
|
151
|
+
}): McpxAnyTool;
|
|
152
|
+
/**
|
|
153
|
+
* Defines a tool whose input shape is assembled from helpers that erase to
|
|
154
|
+
* `z.ZodRawShape`, as the builtins do. Nothing is left to infer from, so the
|
|
155
|
+
* handler's arguments are stated instead: `defineMcpxTool<Args>({ ... })`.
|
|
74
156
|
*/
|
|
75
|
-
declare
|
|
157
|
+
declare function defineMcpxTool<Args>(tool: McpxTool<z.ZodRawShape, Args> & {
|
|
158
|
+
inputSchema: (scope: McpxToolScope) => z.ZodRawShape;
|
|
159
|
+
}): McpxAnyTool;
|
|
76
160
|
/**
|
|
77
161
|
* Outcome of resolving an API key. `user` must carry `collection`.
|
|
78
162
|
*/
|
|
@@ -110,7 +194,7 @@ type McpxPluginOptions = {
|
|
|
110
194
|
/** Upper bound for `depth` on reads. Default 1. */
|
|
111
195
|
maxDepth?: number;
|
|
112
196
|
};
|
|
113
|
-
tools?:
|
|
197
|
+
tools?: McpxAnyTool[];
|
|
114
198
|
auth?: {
|
|
115
199
|
/** Replace or wrap the default key resolution. Return `null` for 401. */
|
|
116
200
|
resolve?: (args: {
|
|
@@ -140,4 +224,4 @@ interface McpxRequestContext {
|
|
|
140
224
|
capabilities: McpxResolvedCapabilities;
|
|
141
225
|
}
|
|
142
226
|
//#endregion
|
|
143
|
-
export { McpxAuthResult, McpxCollectionCapabilities, McpxCollectionOptions, McpxGlobalOptions, McpxPluginOptions, McpxRequestContext, McpxResolvedCapabilities, McpxTool, McpxToolExtra, defineMcpxTool };
|
|
227
|
+
export { McpxAnyTool, McpxAuthResult, McpxCollectionCapabilities, McpxCollectionOptions, McpxExposedEntity, McpxGlobalOptions, McpxPluginOptions, McpxRequestContext, McpxResolvedCapabilities, McpxTool, McpxToolExtra, McpxToolScope, defineMcpxTool };
|
package/dist/types.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
//#region src/types.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/ const defineMcpxTool = (tool) => tool;
|
|
2
|
+
function defineMcpxTool(tool) {
|
|
3
|
+
return tool;
|
|
4
|
+
}
|
|
6
5
|
//#endregion
|
|
7
6
|
export { defineMcpxTool };
|
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": "1.0.0-beta.
|
|
4
|
+
"version": "1.0.0-beta.11",
|
|
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",
|
package/dist/options.d.mts
DELETED