@plitzi/sdk-mcp 0.38.7 → 0.38.8

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/README.md +9 -9
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.js +2 -3
  5. package/dist/modules/mcp/helpers/guide.js +37 -21
  6. package/dist/modules/mcp/index.d.ts +2 -2
  7. package/dist/modules/mcp/server.d.ts +4 -4
  8. package/dist/modules/mcp/server.js +10 -5
  9. package/dist/modules/mcp/tests/cheapMistakes.test.d.ts +1 -0
  10. package/dist/modules/mcp/tests/helpers.d.ts +9 -2
  11. package/dist/modules/mcp/tests/intents.test.d.ts +1 -0
  12. package/dist/modules/mcp/tests/look.test.d.ts +1 -0
  13. package/dist/modules/mcp/tools/apply/index.d.ts +64 -50
  14. package/dist/modules/mcp/tools/apply/index.js +70 -20
  15. package/dist/modules/mcp/tools/describeOperation.d.ts +11 -0
  16. package/dist/modules/mcp/tools/describeOperation.js +40 -0
  17. package/dist/modules/mcp/tools/describeOperation.test.d.ts +1 -0
  18. package/dist/modules/mcp/tools/index.d.ts +1 -6
  19. package/dist/modules/mcp/tools/index.js +12 -9
  20. package/dist/modules/mcp/tools/intents.d.ts +5 -0
  21. package/dist/modules/mcp/tools/intents.js +201 -0
  22. package/dist/modules/mcp/tools/look.d.ts +17 -0
  23. package/dist/modules/mcp/tools/look.js +32 -0
  24. package/dist/modules/mcp/tools/operations/closed.d.ts +7 -0
  25. package/dist/modules/mcp/tools/operations/closed.js +29 -0
  26. package/dist/modules/mcp/tools/operations/index.d.ts +229 -180
  27. package/dist/modules/mcp/tools/operations/index.js +72 -51
  28. package/dist/modules/mcp/tools/operations/schema/bindings/upsertBinding.d.ts +1 -1
  29. package/dist/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.d.ts +1 -1
  30. package/dist/modules/mcp/tools/operations/schema/operations.d.ts +2 -2
  31. package/dist/modules/mcp/tools/operations/schema/shared.d.ts +2 -2
  32. package/dist/modules/mcp/tools/operations/schema/shared.js +7 -6
  33. package/dist/modules/mcp/tools/operations/schemaIds.js +4 -4
  34. package/dist/modules/mcp/tools/render.d.ts +40 -40
  35. package/dist/modules/mcp/tools/render.js +5 -1
  36. package/dist/modules/mcp/tools/shared/draftBatch.d.ts +3 -2
  37. package/dist/modules/mcp/tools/shared/draftBatch.js +16 -5
  38. package/dist/modules/mcp/tools/shared/effects.d.ts +2 -0
  39. package/dist/modules/mcp/tools/shared/effects.js +155 -0
  40. package/dist/modules/mcp/tools/shared/effects.test.d.ts +1 -0
  41. package/dist/modules/mcp/tools/shared/look.d.ts +32 -0
  42. package/dist/modules/mcp/tools/shared/look.js +128 -0
  43. package/dist/modules/mcp/tools/shared/normalizeOperations.d.ts +6 -0
  44. package/dist/modules/mcp/tools/shared/normalizeOperations.js +37 -0
  45. package/dist/modules/mcp/tools/shared/repeats.d.ts +13 -0
  46. package/dist/modules/mcp/tools/shared/repeats.js +38 -0
  47. package/dist/modules/mcp/tools/shared/tool.d.ts +9 -6
  48. package/dist/modules/mcp/tools/shared/tool.js +2 -1
  49. package/dist/modules/mcp/tools/shared/validator/context.d.ts +0 -1
  50. package/dist/modules/mcp/tools/shared/validator/elements.js +0 -1
  51. package/dist/modules/mcp/tools/shared/validator/index.js +25 -2
  52. package/dist/modules/mcp/types/screenshotTypes.d.ts +3 -3
  53. package/dist/modules/mcp/types/toolTypes.d.ts +2 -4
  54. package/dist/options.d.ts +4 -5
  55. package/dist/preview/createPreview.js +9 -18
  56. package/package.json +7 -7
  57. package/skills/plitzi-render/SKILL.md +8 -184
  58. package/skills/plitzi-render/reference/good-call.md +88 -0
  59. package/skills/plitzi-render/reference/interactive.md +64 -0
  60. package/skills/plitzi-render/reference/looks.md +29 -0
  61. package/dist/modules/mcp/tools/preview.d.ts +0 -1423
  62. package/dist/modules/mcp/tools/preview.js +0 -37
  63. package/dist/modules/mcp/tools/screenshot.d.ts +0 -1708
  64. package/dist/modules/mcp/tools/screenshot.js +0 -127
  65. package/dist/modules/mcp/tools/validate.d.ts +0 -1699
  66. package/dist/modules/mcp/tools/validate.js +0 -32
@@ -0,0 +1,201 @@
1
+ import { findElementByRef } from "../helpers/space.js";
2
+ import { scalar } from "./operations/schema/shared.js";
3
+ import { defineTool } from "./shared/tool.js";
4
+ import { apply } from "./apply/index.js";
5
+ import { closest } from "@plitzi/sdk-authoring";
6
+ import { z } from "zod";
7
+ //#region src/modules/mcp/tools/intents.ts
8
+ /** The element a ref names, with the page or layout it is on — or the nearest ref there is. */
9
+ var locate = (ctx, ref) => {
10
+ const element = findElementByRef(ctx.space.schema, ref);
11
+ if (element) return {
12
+ element,
13
+ root: element.definition.rootId,
14
+ classes: element.definition.styleSelectors.base.split(/\s+/).filter(Boolean)
15
+ };
16
+ const nearest = closest(ref, Object.keys(ctx.space.schema.flat));
17
+ return {
18
+ done: false,
19
+ errors: [{
20
+ path: "ref",
21
+ message: `There is no element "${ref}"${nearest ? ` — did you mean "${nearest}"?` : ""}`,
22
+ hint: "plitzi_search finds an element by its words, type or class"
23
+ }]
24
+ };
25
+ };
26
+ /** The attributes of an element a binding computes, with the source each reads. */
27
+ var boundOf = (element) => new Map((element.definition.bindings?.attributes ?? []).map((binding) => [binding.to, binding.source]));
28
+ /**
29
+ * The batch applied, answered with what it did. `notes` are what the agent believed that was already so — a class it
30
+ * wears, an attribute it did not have — said beside the effects, never dropped because nothing came of them.
31
+ */
32
+ var run = async (ctx, operations, page, notes = []) => {
33
+ const result = await apply({
34
+ operations,
35
+ environment: ctx.env
36
+ }, ctx.space, ctx.persisters);
37
+ const warnings = [...notes, ...result.warnings ?? []];
38
+ if (!result.applied) return {
39
+ done: false,
40
+ errors: result.errors ?? (result.conflict ? [result.conflict] : []),
41
+ ...warnings.length > 0 ? { warnings } : {}
42
+ };
43
+ return {
44
+ done: true,
45
+ saved: result.persisted !== false,
46
+ effects: result.effects ?? [],
47
+ ...warnings.length > 0 ? { warnings } : {},
48
+ next: `plitzi_look { pageRef: "${page}" } to see it`
49
+ };
50
+ };
51
+ var setAttributesTool = defineTool({
52
+ name: "plitzi_set_attributes",
53
+ title: "Set an element’s attributes",
54
+ description: "Set or remove attributes of one element — its words (`content`), a link’s `href`, an image’s `src` and `alt`, a field’s `label`… — by its ref. plitzi://explain/{type} lists what a type takes.",
55
+ inputShape: {
56
+ ref: z.string().describe("The element, by its ref"),
57
+ set: z.record(z.string(), scalar).optional().describe("Attributes to write: { \"content\": \"Start free\" }"),
58
+ unset: z.array(z.string()).optional().describe("Attributes to remove")
59
+ },
60
+ access: "write",
61
+ run: async (input, ctx) => {
62
+ const at = locate(ctx, input.ref);
63
+ if ("done" in at) return at;
64
+ const set = input.set ?? {};
65
+ const unset = input.unset ?? [];
66
+ const bound = boundOf(at.element);
67
+ const covered = [...Object.keys(set), ...unset].filter((key) => bound.has(key));
68
+ if (covered.length > 0) return {
69
+ done: false,
70
+ errors: covered.map((key) => ({
71
+ path: key,
72
+ message: `${key} of ${input.ref} is bound to ${String(bound.get(key))}: the page shows what the binding computes, not a value written here`,
73
+ hint: "plitzi_bind_attribute changes what it reads; to write a value instead, plitzi_apply deleteBinding first"
74
+ }))
75
+ };
76
+ const { attributes } = at.element;
77
+ const notes = [...Object.entries(set).filter(([key, value]) => attributes[key] === value).map(([key, value]) => `${key} was already ${JSON.stringify(value)}`), ...unset.filter((key) => !(key in attributes)).map((key) => `${key} was not set`)];
78
+ const props = {
79
+ ...set,
80
+ ...Object.fromEntries(unset.map((key) => [key, null]))
81
+ };
82
+ return run(ctx, [{
83
+ type: "patchElement",
84
+ pageRef: at.root,
85
+ ref: input.ref,
86
+ props
87
+ }], at.root, notes);
88
+ }
89
+ });
90
+ var setClassesTool = defineTool({
91
+ name: "plitzi_set_classes",
92
+ title: "Add or remove an element’s classes",
93
+ description: "Add classes to one element, or take them off; the ones it wears and you do not name stay. A class is a definition of the space (plitzi://definitions/{env}).",
94
+ inputShape: {
95
+ ref: z.string().describe("The element, by its ref"),
96
+ add: z.array(z.string()).optional().describe("Classes it should also wear, by name"),
97
+ remove: z.array(z.string()).optional().describe("Classes it should no longer wear, by name")
98
+ },
99
+ access: "write",
100
+ run: async (input, ctx) => {
101
+ const at = locate(ctx, input.ref);
102
+ if ("done" in at) return at;
103
+ const absent = (input.remove ?? []).filter((name) => !at.classes.includes(name));
104
+ if (absent.length > 0) return {
105
+ done: false,
106
+ errors: absent.map((name) => {
107
+ const nearest = closest(name, at.classes);
108
+ return {
109
+ path: "remove",
110
+ message: `${input.ref} does not wear "${name}"${nearest ? ` — did you mean "${nearest}"?` : ""}; it wears ${at.classes.join(", ") || "no class"}`
111
+ };
112
+ })
113
+ };
114
+ const notes = (input.add ?? []).filter((name) => at.classes.includes(name)).map((name) => `${input.ref} already wore ${name}`);
115
+ const removed = new Set(input.remove ?? []);
116
+ const base = [...at.classes.filter((name) => !removed.has(name)), ...(input.add ?? []).filter((name) => !at.classes.includes(name))];
117
+ return run(ctx, [{
118
+ type: "patchElement",
119
+ pageRef: at.root,
120
+ ref: input.ref,
121
+ style: { base }
122
+ }], at.root, notes);
123
+ }
124
+ });
125
+ var bindAttributeTool = defineTool({
126
+ name: "plitzi_bind_attribute",
127
+ title: "Bind an element to data",
128
+ description: "Feed one attribute of an element from a data source — a list’s `items` from `apiContainer_products.data`, a heading’s `content` from a field. Sources: plitzi://data-sources/{env}.",
129
+ inputShape: {
130
+ ref: z.string().describe("The element, by its ref"),
131
+ to: z.string().describe("The attribute it feeds: \"items\", \"content\"…"),
132
+ source: z.string().describe("The source path, spelled in full: \"apiContainer_products.data\"")
133
+ },
134
+ access: "write",
135
+ run: async (input, ctx) => {
136
+ const at = locate(ctx, input.ref);
137
+ if ("done" in at) return at;
138
+ const was = boundOf(at.element).get(input.to);
139
+ return run(ctx, [{
140
+ type: "upsertBinding",
141
+ pageRef: at.root,
142
+ ref: input.ref,
143
+ category: "attributes",
144
+ binding: {
145
+ to: input.to,
146
+ source: input.source
147
+ }
148
+ }], at.root, was === void 0 ? [] : [`${input.ref}.${input.to} was bound to ${was}: replaced`]);
149
+ }
150
+ });
151
+ var placeComponentTool = defineTool({
152
+ name: "plitzi_place_component",
153
+ title: "Place a component",
154
+ description: "Place an instance of one of the space’s components inside an element, handing it its props. plitzi://schema/{env}/components lists them and what each takes.",
155
+ inputShape: {
156
+ component: z.string().describe("The component, by its ref"),
157
+ ref: z.string().describe("A ref for the new instance"),
158
+ into: z.string().describe("The element it goes inside, by its ref"),
159
+ props: z.record(z.string(), scalar).optional().describe("What it hands the component: { \"title\": \"Lamp\" }")
160
+ },
161
+ access: "write",
162
+ run: async (input, ctx) => {
163
+ const at = locate(ctx, input.into);
164
+ if ("done" in at) return at;
165
+ return run(ctx, [{
166
+ type: "upsertElement",
167
+ pageRef: at.root,
168
+ parentRef: input.into,
169
+ element: {
170
+ ref: input.ref,
171
+ type: "reference",
172
+ props: {
173
+ referenceType: "component",
174
+ referenceId: input.component,
175
+ ...input.props
176
+ }
177
+ }
178
+ }], at.root);
179
+ }
180
+ });
181
+ var addPageTool = defineTool({
182
+ name: "plitzi_add_page",
183
+ title: "Add a page",
184
+ description: "A new page at a slug, inside a layout when the space has one for it (plitzi://schema/{env}/pages).",
185
+ inputShape: {
186
+ ref: z.string().describe("Its ref: \"pricing\""),
187
+ slug: z.string().describe("Its path, relative and without a leading slash: \"pricing\", \"posts/:postId\""),
188
+ label: z.string().optional().describe("Its name in the builder"),
189
+ layout: z.string().optional().describe("The layout it renders inside, by its ref")
190
+ },
191
+ access: "write",
192
+ run: async (input, ctx) => run(ctx, [{
193
+ type: "upsertPage",
194
+ ref: input.ref,
195
+ slug: input.slug,
196
+ ...input.label ? { label: input.label } : {},
197
+ ...input.layout ? { layout: input.layout } : {}
198
+ }], input.ref)
199
+ });
200
+ //#endregion
201
+ export { addPageTool, bindAttributeTool, placeComponentTool, setAttributesTool, setClassesTool };
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ export declare const lookShape: {
3
+ pageRef: z.ZodOptional<z.ZodString>;
4
+ view: z.ZodOptional<z.ZodEnum<{
5
+ image: "image";
6
+ accessibility: "accessibility";
7
+ html: "html";
8
+ both: "both";
9
+ }>>;
10
+ viewport: z.ZodOptional<z.ZodEnum<{
11
+ desktop: "desktop";
12
+ mobile: "mobile";
13
+ both: "both";
14
+ }>>;
15
+ fullPage: z.ZodOptional<z.ZodBoolean>;
16
+ };
17
+ export declare const lookTool: import('.').ToolDef;
@@ -0,0 +1,32 @@
1
+ import { fullPage, lookAt, pageRef, viewport } from "./shared/look.js";
2
+ import { defineTool, imageResult } from "./shared/tool.js";
3
+ import { z } from "zod";
4
+ //#region src/modules/mcp/tools/look.ts
5
+ var lookShape = {
6
+ pageRef,
7
+ view: z.enum([
8
+ "accessibility",
9
+ "html",
10
+ "image",
11
+ "both"
12
+ ]).optional().describe("How to see it. \"accessibility\" (default): the page as a screen reader and a browser agent read it — an outline of roles and names, every control with no name listed; text, and cheap. \"html\": the markup. \"image\": a PNG, for what only a picture shows — overflow, misalignment. \"both\": image and outline."),
13
+ viewport,
14
+ fullPage
15
+ };
16
+ var lookTool = defineTool({
17
+ name: "plitzi_look",
18
+ title: "Look at a page",
19
+ description: "See a saved page: its accessibility outline (default, text), its HTML, or a PNG. To see a change before saving it, plitzi_apply with `dryRun` and `look` instead.",
20
+ inputShape: lookShape,
21
+ access: "read",
22
+ run: async (input, ctx) => {
23
+ const look = await lookAt(ctx, {
24
+ ...input,
25
+ view: input.view ?? "accessibility"
26
+ });
27
+ if ("refused" in look) return look.refused;
28
+ return look.images.length > 0 ? imageResult(look.images, look.meta) : look.meta;
29
+ }
30
+ });
31
+ //#endregion
32
+ export { lookShape, lookTool };
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ /** A part of an operation (`an element`), closed: its fields as `shape` declares them, and no other. */
3
+ export declare const closedObject: <Shape extends z.ZodRawShape>(owner: string, shape: Shape, listed: string) => z.ZodObject<{ -readonly [P in keyof Shape]: Shape[P]; }, z.core.$strict>;
4
+ /** An operation's type, as its schema states it. */
5
+ export declare const typeOf: (operation: z.ZodObject) => string;
6
+ /** An operation, closed: its fields as declared, its description kept. */
7
+ export declare const closed: <Shape extends z.ZodRawShape>(operation: z.ZodObject<Shape>) => z.ZodObject<{ -readonly [P in keyof Shape]: Shape[P]; }, z.core.$strict>;
@@ -0,0 +1,29 @@
1
+ import { closest } from "@plitzi/sdk-authoring";
2
+ import { z } from "zod";
3
+ //#region src/modules/mcp/tools/operations/closed.ts
4
+ /**
5
+ * Objects of the write vocabulary that refuse a field they do not have. One left out of a schema used to be dropped as
6
+ * the batch was read — `prop` for `props` applied nothing and answered success, the one mistake an agent cannot see.
7
+ * Refused instead, with the field meant and where every field is listed.
8
+ */
9
+ /** What a field `owner` does not have is answered with: the nearest one it does, and where every one is listed. */
10
+ var unknownFields = (owner, written, fields, listed) => {
11
+ return `${owner} has no field ${written.map((key) => {
12
+ const nearest = closest(key, fields);
13
+ return nearest ? `"${key}" (did you mean "${nearest}"?)` : `"${key}"`;
14
+ }).join(", ")}: ${listed} lists its fields`;
15
+ };
16
+ /** A part of an operation (`an element`), closed: its fields as `shape` declares them, and no other. */
17
+ var closedObject = (owner, shape, listed) => {
18
+ const fields = Object.keys(shape);
19
+ return z.strictObject(shape, { error: (issue) => issue.code === "unrecognized_keys" ? unknownFields(owner, issue.keys, fields, listed) : void 0 });
20
+ };
21
+ /** An operation's type, as its schema states it. */
22
+ var typeOf = (operation) => operation.shape.type instanceof z.ZodLiteral ? String(operation.shape.type.value) : "";
23
+ /** An operation, closed: its fields as declared, its description kept. */
24
+ var closed = (operation) => {
25
+ const type = typeOf(operation);
26
+ return closedObject(type, operation.shape, `plitzi_describe_operation { type: "${type}" }`).describe(operation.description ?? "");
27
+ };
28
+ //#endregion
29
+ export { closed, closedObject, typeOf };