@plitzi/sdk-mcp 0.37.8 → 0.37.10

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 (65) hide show
  1. package/CHANGELOG.md +837 -0
  2. package/README.md +7 -2
  3. package/dist/modules/mcp/apps/render/view/streamProgress.ts +1 -3
  4. package/dist/modules/mcp/helpers/agentPrompt.js +7 -5
  5. package/dist/modules/mcp/helpers/components.d.ts +14 -0
  6. package/dist/modules/mcp/helpers/components.js +32 -0
  7. package/dist/modules/mcp/helpers/guide.js +170 -6
  8. package/dist/modules/mcp/helpers/index.d.ts +1 -0
  9. package/dist/modules/mcp/helpers/space.d.ts +5 -0
  10. package/dist/modules/mcp/helpers/space.js +2 -1
  11. package/dist/modules/mcp/helpers/uris.d.ts +3 -0
  12. package/dist/modules/mcp/helpers/uris.js +4 -1
  13. package/dist/modules/mcp/resources/explain.d.ts +19 -0
  14. package/dist/modules/mcp/resources/explain.js +22 -0
  15. package/dist/modules/mcp/resources/explain.test.d.ts +1 -0
  16. package/dist/modules/mcp/resources/primer.js +12 -1
  17. package/dist/modules/mcp/resources/register.js +10 -0
  18. package/dist/modules/mcp/resources/schema.js +17 -10
  19. package/dist/modules/mcp/server.js +1 -1
  20. package/dist/modules/mcp/tests/components.test.d.ts +1 -0
  21. package/dist/modules/mcp/tests/flags.test.d.ts +1 -0
  22. package/dist/modules/mcp/tools/apply/dispatch.js +30 -1
  23. package/dist/modules/mcp/tools/apply/index.d.ts +27 -0
  24. package/dist/modules/mcp/tools/apply/index.js +5 -3
  25. package/dist/modules/mcp/tools/operations/index.d.ts +108 -0
  26. package/dist/modules/mcp/tools/operations/index.js +4 -0
  27. package/dist/modules/mcp/tools/operations/schema/components/deleteComponent.d.ts +9 -0
  28. package/dist/modules/mcp/tools/operations/schema/components/deleteComponent.js +21 -0
  29. package/dist/modules/mcp/tools/operations/schema/components/index.d.ts +2 -0
  30. package/dist/modules/mcp/tools/operations/schema/components/upsertComponent.d.ts +14 -0
  31. package/dist/modules/mcp/tools/operations/schema/components/upsertComponent.js +147 -0
  32. package/dist/modules/mcp/tools/operations/schema/elements/patchElement.d.ts +2 -0
  33. package/dist/modules/mcp/tools/operations/schema/elements/patchElement.js +9 -2
  34. package/dist/modules/mcp/tools/operations/schema/elements/upsertElement.js +3 -0
  35. package/dist/modules/mcp/tools/operations/schema/flags/deleteFlag.d.ts +9 -0
  36. package/dist/modules/mcp/tools/operations/schema/flags/deleteFlag.js +21 -0
  37. package/dist/modules/mcp/tools/operations/schema/flags/upsertFlag.d.ts +15 -0
  38. package/dist/modules/mcp/tools/operations/schema/flags/upsertFlag.js +38 -0
  39. package/dist/modules/mcp/tools/operations/schema/index.d.ts +3 -0
  40. package/dist/modules/mcp/tools/operations/schema/operations.d.ts +31 -0
  41. package/dist/modules/mcp/tools/operations/schema/operations.js +9 -1
  42. package/dist/modules/mcp/tools/operations/schema/pages/upsertPage.d.ts +1 -0
  43. package/dist/modules/mcp/tools/operations/schema/pages/upsertPage.js +8 -2
  44. package/dist/modules/mcp/tools/operations/schema/settings/patchSettings.d.ts +1 -0
  45. package/dist/modules/mcp/tools/operations/schema/settings/patchSettings.js +28 -2
  46. package/dist/modules/mcp/tools/operations/schema/shared.d.ts +8 -0
  47. package/dist/modules/mcp/tools/operations/schema/shared.js +10 -2
  48. package/dist/modules/mcp/tools/operations/schema/translator.js +8 -0
  49. package/dist/modules/mcp/tools/operations/schema/write.js +7 -2
  50. package/dist/modules/mcp/tools/operations/schemaIds.js +3 -1
  51. package/dist/modules/mcp/tools/preview.d.ts +27 -0
  52. package/dist/modules/mcp/tools/render.d.ts +27 -0
  53. package/dist/modules/mcp/tools/screenshot.d.ts +27 -0
  54. package/dist/modules/mcp/tools/shared/draftBatch.d.ts +2 -0
  55. package/dist/modules/mcp/tools/shared/draftBatch.js +3 -1
  56. package/dist/modules/mcp/tools/shared/lintDraft.js +16 -4
  57. package/dist/modules/mcp/tools/shared/newSuggestions.d.ts +7 -0
  58. package/dist/modules/mcp/tools/shared/newSuggestions.js +20 -0
  59. package/dist/modules/mcp/tools/shared/validator/batch.js +1 -1
  60. package/dist/modules/mcp/tools/shared/validator/index.js +17 -9
  61. package/dist/modules/mcp/tools/validate.d.ts +27 -0
  62. package/dist/modules/mcp/tools/validate.js +8 -6
  63. package/dist/modules/mcp/types/aiSchema.d.ts +26 -1
  64. package/dist/modules/mcp/types/toolTypes.d.ts +4 -0
  65. package/package.json +9 -9
@@ -1,6 +1,8 @@
1
1
  import { findRootByRef, indexAddElement, resolveRef } from "../../../helpers/space.js";
2
2
  import { folderUri, foldersUri, layoutsUri, pageUri, pagesUri, schemaVarsUri, settingsUri } from "../../../helpers/uris.js";
3
3
  import { fail } from "../../../helpers/opResult.js";
4
+ import { flagGateOf } from "@plitzi/sdk-authoring";
5
+ import { documentIds } from "@plitzi/sdk-schema/helpers/components";
4
6
  import { elementIdConflict, isValidElementId } from "@plitzi/sdk-schema/helpers/elementId";
5
7
  import FlatMap from "@plitzi/sdk-schema/helpers/FlatMap";
6
8
  //#region src/modules/mcp/tools/operations/schema/write.ts
@@ -12,7 +14,8 @@ var collectInputRefs = (input) => [input.ref, ...(input.children ?? []).flatMap(
12
14
  * it. Both rules are sdk-schema's `elementIdConflict` — never restated here, so the MCP cannot drift from what
13
15
  * the builder and the schema validator enforce. Null when the name is usable. */
14
16
  var guardNewRef = (space, ref, field) => {
15
- const conflict = elementIdConflict(space.schema.flat, ref);
17
+ const elsewhere = documentIds(space.document ?? space.schema).has(ref) ? `"${ref}" is already used by another element here` : null;
18
+ const conflict = elementIdConflict(space.schema.flat, ref) ?? elsewhere;
16
19
  if (!conflict) return null;
17
20
  return isValidElementId(ref) ? fail(field, conflict, TAKEN_HINT) : fail(field, conflict, ID_REF_HINT);
18
21
  };
@@ -62,7 +65,9 @@ var createElement = (space, page, input, anchorId, drop) => {
62
65
  type: input.type,
63
66
  items: [],
64
67
  styleSelectors,
65
- ...input.runtime === void 0 ? {} : { runtime: input.runtime }
68
+ ...input.runtime === void 0 ? {} : { runtime: input.runtime },
69
+ ...input.flag === void 0 ? {} : { flag: flagGateOf(input.flag, `Element "${input.ref}"`) },
70
+ ...input.anchor === void 0 ? {} : { anchor: input.anchor }
66
71
  }
67
72
  };
68
73
  if (!new FlatMap({ flat: space.schema.flat }).addElement(el, anchorId, drop)) return false;
@@ -1,6 +1,6 @@
1
1
  import { actionAccess, actionField, actionLimits, actionNode, actionTriggerParams } from "./actions/document.js";
2
2
  import { connectorConnection, readEndpoint, writeEndpoint } from "./connectors/manifest.js";
3
- import { bindingInput, elementInput, initialStateInput, interactionNode, position, ruleGroup, styleRefs, styleVariantInput } from "./schema/shared.js";
3
+ import { bindingInput, elementAnchor, elementFlag, elementInput, initialStateInput, interactionNode, position, ruleGroup, styleRefs, styleVariantInput } from "./schema/shared.js";
4
4
  import { ancestors, ancestorsPatch, cssMap, cssPatchMap, definitionSlot, definitionSlotPatch, displayModeCss, displayModeCssPatch, themeValue } from "./style/shared.js";
5
5
  import { z } from "zod";
6
6
  //#region src/modules/mcp/tools/operations/schemaIds.ts
@@ -23,6 +23,8 @@ import { z } from "zod";
23
23
  * the co-worker's own tool-schema converter alike. */
24
24
  var SHARED_SCHEMAS = [
25
25
  [elementInput, "Element"],
26
+ [elementFlag, "ElementFlag"],
27
+ [elementAnchor, "ElementAnchor"],
26
28
  [ruleGroup, "RuleGroup"],
27
29
  [styleRefs, "StyleRefs"],
28
30
  [initialStateInput, "InitialState"],
@@ -50,6 +50,8 @@ export declare const previewShape: {
50
50
  client: "client";
51
51
  shared: "shared";
52
52
  }>>;
53
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
54
+ anchor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
53
55
  }, z.core.$strip>, z.ZodObject<{
54
56
  type: z.ZodLiteral<"deleteElement">;
55
57
  pageRef: z.ZodString;
@@ -74,6 +76,7 @@ export declare const previewShape: {
74
76
  layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
75
77
  layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
76
78
  enabled: z.ZodOptional<z.ZodBoolean>;
79
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
77
80
  }, z.core.$strip>, z.ZodObject<{
78
81
  type: z.ZodLiteral<"deletePage">;
79
82
  ref: z.ZodString;
@@ -118,6 +121,18 @@ export declare const previewShape: {
118
121
  }, z.core.$strip>, z.ZodObject<{
119
122
  type: z.ZodLiteral<"deleteVariable">;
120
123
  name: z.ZodString;
124
+ }, z.core.$strip>, z.ZodObject<{
125
+ type: z.ZodLiteral<"upsertFlag">;
126
+ name: z.ZodString;
127
+ description: z.ZodOptional<z.ZodString>;
128
+ value: z.ZodBoolean;
129
+ rules: z.ZodOptional<z.ZodArray<z.ZodObject<{
130
+ when: z.ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, z.core.$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>;
131
+ value: z.ZodBoolean;
132
+ }, z.core.$strip>>>;
133
+ }, z.core.$strip>, z.ZodObject<{
134
+ type: z.ZodLiteral<"deleteFlag">;
135
+ name: z.ZodString;
121
136
  }, z.core.$strip>, z.ZodObject<{
122
137
  type: z.ZodLiteral<"upsertBinding">;
123
138
  pageRef: z.ZodString;
@@ -221,6 +236,7 @@ export declare const previewShape: {
221
236
  }, z.core.$strip>, z.ZodObject<{
222
237
  type: z.ZodLiteral<"patchSettings">;
223
238
  customCss: z.ZodOptional<z.ZodString>;
239
+ notifications: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>;
224
240
  keepState: z.ZodOptional<z.ZodBoolean>;
225
241
  stateStorage: z.ZodOptional<z.ZodEnum<{
226
242
  localStorage: "localStorage";
@@ -265,6 +281,17 @@ export declare const previewShape: {
265
281
  }>>;
266
282
  presence: z.ZodOptional<z.ZodBoolean>;
267
283
  }, z.core.$strip>>>>;
284
+ }, z.core.$strip>, z.ZodObject<{
285
+ type: z.ZodLiteral<"upsertComponent">;
286
+ ref: z.ZodString;
287
+ label: z.ZodOptional<z.ZodString>;
288
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
289
+ slots: z.ZodOptional<z.ZodArray<z.ZodString>>;
290
+ fromRef: z.ZodOptional<z.ZodString>;
291
+ instanceRef: z.ZodOptional<z.ZodString>;
292
+ }, z.core.$strip>, z.ZodObject<{
293
+ type: z.ZodLiteral<"deleteComponent">;
294
+ ref: z.ZodString;
268
295
  }, z.core.$strip>, z.ZodObject<{
269
296
  desktop: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
270
297
  tablet: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
@@ -52,6 +52,8 @@ export declare const renderShape: {
52
52
  client: "client";
53
53
  shared: "shared";
54
54
  }>>;
55
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
+ anchor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
57
  }, z.core.$strip>, z.ZodObject<{
56
58
  type: z.ZodLiteral<"deleteElement">;
57
59
  pageRef: z.ZodString;
@@ -76,6 +78,7 @@ export declare const renderShape: {
76
78
  layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
77
79
  layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
78
80
  enabled: z.ZodOptional<z.ZodBoolean>;
81
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
82
  }, z.core.$strip>, z.ZodObject<{
80
83
  type: z.ZodLiteral<"deletePage">;
81
84
  ref: z.ZodString;
@@ -120,6 +123,18 @@ export declare const renderShape: {
120
123
  }, z.core.$strip>, z.ZodObject<{
121
124
  type: z.ZodLiteral<"deleteVariable">;
122
125
  name: z.ZodString;
126
+ }, z.core.$strip>, z.ZodObject<{
127
+ type: z.ZodLiteral<"upsertFlag">;
128
+ name: z.ZodString;
129
+ description: z.ZodOptional<z.ZodString>;
130
+ value: z.ZodBoolean;
131
+ rules: z.ZodOptional<z.ZodArray<z.ZodObject<{
132
+ when: z.ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, z.core.$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>;
133
+ value: z.ZodBoolean;
134
+ }, z.core.$strip>>>;
135
+ }, z.core.$strip>, z.ZodObject<{
136
+ type: z.ZodLiteral<"deleteFlag">;
137
+ name: z.ZodString;
123
138
  }, z.core.$strip>, z.ZodObject<{
124
139
  type: z.ZodLiteral<"upsertBinding">;
125
140
  pageRef: z.ZodString;
@@ -223,6 +238,7 @@ export declare const renderShape: {
223
238
  }, z.core.$strip>, z.ZodObject<{
224
239
  type: z.ZodLiteral<"patchSettings">;
225
240
  customCss: z.ZodOptional<z.ZodString>;
241
+ notifications: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>;
226
242
  keepState: z.ZodOptional<z.ZodBoolean>;
227
243
  stateStorage: z.ZodOptional<z.ZodEnum<{
228
244
  localStorage: "localStorage";
@@ -267,6 +283,17 @@ export declare const renderShape: {
267
283
  }>>;
268
284
  presence: z.ZodOptional<z.ZodBoolean>;
269
285
  }, z.core.$strip>>>>;
286
+ }, z.core.$strip>, z.ZodObject<{
287
+ type: z.ZodLiteral<"upsertComponent">;
288
+ ref: z.ZodString;
289
+ label: z.ZodOptional<z.ZodString>;
290
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
291
+ slots: z.ZodOptional<z.ZodArray<z.ZodString>>;
292
+ fromRef: z.ZodOptional<z.ZodString>;
293
+ instanceRef: z.ZodOptional<z.ZodString>;
294
+ }, z.core.$strip>, z.ZodObject<{
295
+ type: z.ZodLiteral<"deleteComponent">;
296
+ ref: z.ZodString;
270
297
  }, z.core.$strip>, z.ZodObject<{
271
298
  desktop: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
272
299
  tablet: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
@@ -50,6 +50,8 @@ export declare const screenshotShape: {
50
50
  client: "client";
51
51
  shared: "shared";
52
52
  }>>;
53
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
54
+ anchor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
53
55
  }, z.core.$strip>, z.ZodObject<{
54
56
  type: z.ZodLiteral<"deleteElement">;
55
57
  pageRef: z.ZodString;
@@ -74,6 +76,7 @@ export declare const screenshotShape: {
74
76
  layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
75
77
  layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
76
78
  enabled: z.ZodOptional<z.ZodBoolean>;
79
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
77
80
  }, z.core.$strip>, z.ZodObject<{
78
81
  type: z.ZodLiteral<"deletePage">;
79
82
  ref: z.ZodString;
@@ -118,6 +121,18 @@ export declare const screenshotShape: {
118
121
  }, z.core.$strip>, z.ZodObject<{
119
122
  type: z.ZodLiteral<"deleteVariable">;
120
123
  name: z.ZodString;
124
+ }, z.core.$strip>, z.ZodObject<{
125
+ type: z.ZodLiteral<"upsertFlag">;
126
+ name: z.ZodString;
127
+ description: z.ZodOptional<z.ZodString>;
128
+ value: z.ZodBoolean;
129
+ rules: z.ZodOptional<z.ZodArray<z.ZodObject<{
130
+ when: z.ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, z.core.$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>;
131
+ value: z.ZodBoolean;
132
+ }, z.core.$strip>>>;
133
+ }, z.core.$strip>, z.ZodObject<{
134
+ type: z.ZodLiteral<"deleteFlag">;
135
+ name: z.ZodString;
121
136
  }, z.core.$strip>, z.ZodObject<{
122
137
  type: z.ZodLiteral<"upsertBinding">;
123
138
  pageRef: z.ZodString;
@@ -221,6 +236,7 @@ export declare const screenshotShape: {
221
236
  }, z.core.$strip>, z.ZodObject<{
222
237
  type: z.ZodLiteral<"patchSettings">;
223
238
  customCss: z.ZodOptional<z.ZodString>;
239
+ notifications: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>;
224
240
  keepState: z.ZodOptional<z.ZodBoolean>;
225
241
  stateStorage: z.ZodOptional<z.ZodEnum<{
226
242
  localStorage: "localStorage";
@@ -265,6 +281,17 @@ export declare const screenshotShape: {
265
281
  }>>;
266
282
  presence: z.ZodOptional<z.ZodBoolean>;
267
283
  }, z.core.$strip>>>>;
284
+ }, z.core.$strip>, z.ZodObject<{
285
+ type: z.ZodLiteral<"upsertComponent">;
286
+ ref: z.ZodString;
287
+ label: z.ZodOptional<z.ZodString>;
288
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
289
+ slots: z.ZodOptional<z.ZodArray<z.ZodString>>;
290
+ fromRef: z.ZodOptional<z.ZodString>;
291
+ instanceRef: z.ZodOptional<z.ZodString>;
292
+ }, z.core.$strip>, z.ZodObject<{
293
+ type: z.ZodLiteral<"deleteComponent">;
294
+ ref: z.ZodString;
268
295
  }, z.core.$strip>, z.ZodObject<{
269
296
  desktop: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
270
297
  tablet: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
@@ -12,6 +12,8 @@ export type DraftResult = {
12
12
  draft: Space;
13
13
  outcome: MutationOutcome;
14
14
  warnings: string[];
15
+ /** Shorter ways to the same page this batch opened up. Never a reason to refuse it. */
16
+ suggestions: string[];
15
17
  };
16
18
  /**
17
19
  * A batch run on a copy of the space and read back — the one way every tool that takes operations handles them, so
@@ -1,6 +1,7 @@
1
1
  import { cloneSpace } from "../../helpers/space.js";
2
2
  import { expandOperations } from "./expandOperations.js";
3
3
  import { fixTouched, lintDraft } from "./lintDraft.js";
4
+ import { newSuggestions } from "./newSuggestions.js";
4
5
  import { validateOperations } from "./validator/index.js";
5
6
  import { applyOperations } from "../apply/dispatch.js";
6
7
  //#region src/modules/mcp/tools/shared/draftBatch.ts
@@ -51,7 +52,8 @@ var draftBatch = (space, env, operations, mode = "space") => {
51
52
  ops,
52
53
  draft,
53
54
  outcome,
54
- warnings
55
+ warnings,
56
+ suggestions: newSuggestions(prepared.space, draft)
55
57
  };
56
58
  };
57
59
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { findElementByRef, findPageByRef } from "../../helpers/space.js";
2
- import { elementSourceTypes, fixSpace, lintSpace } from "@plitzi/sdk-authoring";
2
+ import { authoringCodeEntry, elementSourceTypes, fixSpace, lintSpace } from "@plitzi/sdk-authoring";
3
3
  import { validateSchema } from "@plitzi/sdk-schema/helpers/schemaValidator";
4
4
  import { styleWithoutTag } from "@plitzi/sdk-schema/helpers/styleWithoutTag";
5
5
  //#region src/modules/mcp/tools/shared/lintDraft.ts
@@ -76,6 +76,15 @@ var readingOf = (space) => {
76
76
  ]
77
77
  };
78
78
  };
79
+ /**
80
+ * An issue as the agent reads it: led by its code, which is how it is filed — the agent has no skill page to look a
81
+ * code up in, so what to write instead comes with it, from the same table `authorSpace` raises it from.
82
+ */
83
+ var said = (issue) => `[${issue.code}] ${issue.message}`;
84
+ var fixOf = (issue) => {
85
+ const entry = authoringCodeEntry(issue.code);
86
+ return entry ? `Write instead: ${entry.fix}.` : "";
87
+ };
79
88
  var keyOf = (issue) => JSON.stringify([
80
89
  issue.code,
81
90
  issue.elementId ?? "",
@@ -123,11 +132,14 @@ var lintDraft = (draft, ops, before) => {
123
132
  const preExisting = before && (candidates.length > 0 || touchedWarnings.length > 0) ? preExistingIn(readingOf(before).all, all) : () => false;
124
133
  const toError = (issue) => ({
125
134
  path: issue.elementId ? `element "${issue.elementId}"` : "schema",
126
- message: preExisting(issue) ? `Pre-existing malformation in element "${issue.elementId ?? ""}": ${issue.message}` : issue.message,
127
- hint: preExisting(issue) ? "This issue already exists in the space (NOT caused by your change), but the save is blocked until you fix it too, in this same batch." : ""
135
+ message: preExisting(issue) ? `Pre-existing malformation in element "${issue.elementId ?? ""}": ${said(issue)}` : said(issue),
136
+ hint: [preExisting(issue) ? "This issue already exists in the space (NOT caused by your change), but the save is blocked until you fix it too, in this same batch." : "", fixOf(issue)].filter(Boolean).join(" ")
128
137
  });
129
138
  const errors = candidates.filter((issue) => onTouched(issue) || !preExisting(issue)).map(toError);
130
- const warnings = touchedWarnings.map((issue) => preExisting(issue) ? `Pre-existing issue in element "${issue.elementId ?? ""}": ${issue.message}` : issue.message);
139
+ const warnings = touchedWarnings.map((issue) => {
140
+ const line = [said(issue), fixOf(issue)].filter(Boolean).join(" ");
141
+ return preExisting(issue) ? `Pre-existing issue in element "${issue.elementId ?? ""}": ${line}` : line;
142
+ });
131
143
  for (const id of touched) {
132
144
  const tagless = styleWithoutTag(draft.schema.flat[id], draft.style);
133
145
  if (tagless) warnings.push(`element "${id}" ${tagless}`);
@@ -0,0 +1,7 @@
1
+ import { Space } from '../../helpers';
2
+ /**
3
+ * The shorter ways to the same page a batch opened up — a header the agent just copied onto a third page, a button it
4
+ * gave a text child — and none the space already had: those were said when they appeared, and saying them on every
5
+ * batch would bury the new ones. Not problems: nothing here blocks anything.
6
+ */
7
+ export declare const newSuggestions: (before: Space, after: Space) => string[];
@@ -0,0 +1,20 @@
1
+ import { suggestSpace } from "@plitzi/sdk-authoring";
2
+ //#region src/modules/mcp/tools/shared/newSuggestions.ts
3
+ var keyOf = ({ code, elementIds }) => `${code}|${[...elementIds].sort().join(",")}`;
4
+ /**
5
+ * The shorter ways to the same page a batch opened up — a header the agent just copied onto a third page, a button it
6
+ * gave a text child — and none the space already had: those were said when they appeared, and saying them on every
7
+ * batch would bury the new ones. Not problems: nothing here blocks anything.
8
+ */
9
+ var newSuggestions = (before, after) => {
10
+ const had = new Set(suggestSpace({
11
+ schema: before.schema,
12
+ style: before.style
13
+ }).map(keyOf));
14
+ return suggestSpace({
15
+ schema: after.schema,
16
+ style: after.style
17
+ }).filter((suggestion) => !had.has(keyOf(suggestion))).map(({ code, message, saves }) => `[${code}] ${message}${saves > 0 ? ` (saves ${String(saves)} element${saves === 1 ? "" : "s"})` : ""}`);
18
+ };
19
+ //#endregion
20
+ export { newSuggestions };
@@ -8,7 +8,7 @@ var batchDeclaredVars = (ops) => {
8
8
  };
9
9
  var batchDeclaredPages = (ops) => {
10
10
  const refs = /* @__PURE__ */ new Set();
11
- for (const op of ops) if (op.type === "upsertPage" || op.type === "upsertLayout") refs.add(op.ref);
11
+ for (const op of ops) if (op.type === "upsertPage" || op.type === "upsertLayout" || op.type === "upsertComponent") refs.add(op.ref);
12
12
  return refs;
13
13
  };
14
14
  var batchDeclaredFolders = (ops) => {
@@ -1,6 +1,7 @@
1
1
  import { observedDataSources, observedInteractionActions } from "../../../catalogs/observed.js";
2
2
  import { buildTypeRegistry } from "../../../catalogs/registry.js";
3
3
  import { elementTypeNames } from "../../../catalogs/index.js";
4
+ import { componentView, findComponentByRef } from "../../../helpers/components.js";
4
5
  import { findElementByRef, findFolderByRef, findPageByRef, findRootByRef, folderAncestorIds, getLayoutElements, getPageElements, pageFoldersOf, resolveRef, routeParamNames } from "../../../helpers/space.js";
5
6
  import { MAX_OPS } from "../../operations/index.js";
6
7
  import { batchDeclaredElements, batchDeclaredFolders, batchDeclaredPages, batchDeclaredVariants, batchDeclaredVars } from "./batch.js";
@@ -11,6 +12,7 @@ import { checkSlotCss } from "./css.js";
11
12
  import { checkRef } from "./refs.js";
12
13
  import { checkElementInput, checkRawMarkup, checkTypeProps, checkVariantApplication } from "./elements.js";
13
14
  import { checkInteractionNode } from "./interactions.js";
15
+ import { treeOf } from "@plitzi/sdk-schema/helpers/components";
14
16
  import { isValidElementId } from "@plitzi/sdk-schema/helpers/elementId";
15
17
  //#region src/modules/mcp/tools/shared/validator/index.ts
16
18
  var STYLE_CATEGORIES = [
@@ -50,8 +52,8 @@ var buildValidationCtx = (space, ops, mode = "space") => {
50
52
  knownTypes: /* @__PURE__ */ new Set([...Object.keys(registry.types), ...elementTypeNames]),
51
53
  typeProps: new Map(Object.entries(registry.types).map(([type, info]) => [type, new Set(Object.keys(info.props))])),
52
54
  typeMeta: buildTypeMeta(space.catalog),
53
- elementType: (ref) => (findElementByRef(space.schema, ref) ?? findPageByRef(space.schema, ref))?.definition.type,
54
- elementExists: (ref) => batchElements.has(ref) || Boolean(findElementByRef(space.schema, ref) ?? findPageByRef(space.schema, ref)),
55
+ elementType: (ref) => treeOf(space.schema, ref)?.flat[ref]?.definition.type,
56
+ elementExists: (ref) => batchElements.has(ref) || treeOf(space.schema, ref) !== void 0,
55
57
  schemaVars: /* @__PURE__ */ new Set([
56
58
  ...space.schema.variables.map((v) => v.name),
57
59
  ...routeParamNames(space.schema),
@@ -81,13 +83,19 @@ var validateOperations = (space, ops, mode = "space") => {
81
83
  });
82
84
  ops.forEach((op, i) => {
83
85
  const base = `operations[${i}]`;
86
+ const component = "pageRef" in op ? findComponentByRef(space.schema, op.pageRef) : void 0;
87
+ const scoped = component ? componentView(space, component).schema : space.schema;
84
88
  if ((op.type === "upsertElement" || op.type === "patchElement" || op.type === "deleteElement" || op.type === "moveElement" || op.type === "upsertBinding" || op.type === "patchBinding" || op.type === "deleteBinding" || op.type === "upsertInteractionFlow" || op.type === "patchInteractionNode" || op.type === "deleteInteraction") && op.pageRef) {
85
- if (!findRootByRef(space.schema, op.pageRef) && !batchPages.has(op.pageRef)) {
86
- const validRefs = [...getPageElements(space.schema).map((page) => page.id), ...getLayoutElements(space.schema).map((layout) => layout.id)];
89
+ if (!component && !findRootByRef(space.schema, op.pageRef) && !batchPages.has(op.pageRef)) {
90
+ const validRefs = [
91
+ ...getPageElements(space.schema).map((page) => page.id),
92
+ ...getLayoutElements(space.schema).map((layout) => layout.id),
93
+ ...Object.keys(space.schema.components)
94
+ ];
87
95
  ctx.errors.push({
88
96
  path: `${base}.pageRef`,
89
- message: `Page or layout "${op.pageRef}" does not exist`,
90
- hint: "Use an existing page or layout ref, or create the page with upsertPage earlier in the same batch",
97
+ message: `Page, layout or component "${op.pageRef}" does not exist`,
98
+ hint: "Use an existing page, layout or component ref, or create it earlier in the same batch",
91
99
  validValues: validRefs
92
100
  });
93
101
  }
@@ -100,8 +108,8 @@ var validateOperations = (space, ops, mode = "space") => {
100
108
  break;
101
109
  case "patchElement": {
102
110
  checkRef(op.ref, `${base}.ref`, ctx);
103
- const page = findRootByRef(space.schema, op.pageRef);
104
- const target = page ? resolveRef(space.schema, page, op.ref) : void 0;
111
+ const page = findRootByRef(scoped, component ? component.rootId : op.pageRef);
112
+ const target = page ? resolveRef(scoped, page, op.ref) : void 0;
105
113
  if (op.props && target && target.id !== page?.id) {
106
114
  checkRawMarkup(target.definition.type, op.props, base, ctx);
107
115
  checkTypeProps(target.definition.type, op.props, base, ctx);
@@ -230,7 +238,7 @@ var validateOperations = (space, ops, mode = "space") => {
230
238
  break;
231
239
  case "patchInteractionNode": {
232
240
  checkRef(op.ref, `${base}.ref`, ctx);
233
- const existing = findElementByRef(space.schema, op.ref)?.definition.interactions?.[op.nodeId];
241
+ const existing = findElementByRef(scoped, op.ref)?.definition.interactions?.[op.nodeId];
234
242
  if (existing) {
235
243
  const merged = {
236
244
  id: existing.id,
@@ -51,6 +51,8 @@ export declare const validateShape: {
51
51
  client: "client";
52
52
  shared: "shared";
53
53
  }>>;
54
+ flag: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
55
+ anchor: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
54
56
  }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
55
57
  type: import('zod').ZodLiteral<"deleteElement">;
56
58
  pageRef: import('zod').ZodString;
@@ -75,6 +77,7 @@ export declare const validateShape: {
75
77
  layout: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
76
78
  layoutContainer: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
77
79
  enabled: import('zod').ZodOptional<import('zod').ZodBoolean>;
80
+ flag: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
78
81
  }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
79
82
  type: import('zod').ZodLiteral<"deletePage">;
80
83
  ref: import('zod').ZodString;
@@ -119,6 +122,18 @@ export declare const validateShape: {
119
122
  }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
120
123
  type: import('zod').ZodLiteral<"deleteVariable">;
121
124
  name: import('zod').ZodString;
125
+ }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
126
+ type: import('zod').ZodLiteral<"upsertFlag">;
127
+ name: import('zod').ZodString;
128
+ description: import('zod').ZodOptional<import('zod').ZodString>;
129
+ value: import('zod').ZodBoolean;
130
+ rules: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodObject<{
131
+ when: import('zod').ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, import('zod/v4/core').$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>;
132
+ value: import('zod').ZodBoolean;
133
+ }, import('zod/v4/core').$strip>>>;
134
+ }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
135
+ type: import('zod').ZodLiteral<"deleteFlag">;
136
+ name: import('zod').ZodString;
122
137
  }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
123
138
  type: import('zod').ZodLiteral<"upsertBinding">;
124
139
  pageRef: import('zod').ZodString;
@@ -222,6 +237,7 @@ export declare const validateShape: {
222
237
  }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
223
238
  type: import('zod').ZodLiteral<"patchSettings">;
224
239
  customCss: import('zod').ZodOptional<import('zod').ZodString>;
240
+ notifications: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodNullable<import('zod').ZodString>>>;
225
241
  keepState: import('zod').ZodOptional<import('zod').ZodBoolean>;
226
242
  stateStorage: import('zod').ZodOptional<import('zod').ZodEnum<{
227
243
  localStorage: "localStorage";
@@ -266,6 +282,17 @@ export declare const validateShape: {
266
282
  }>>;
267
283
  presence: import('zod').ZodOptional<import('zod').ZodBoolean>;
268
284
  }, import('zod/v4/core').$strip>>>>;
285
+ }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
286
+ type: import('zod').ZodLiteral<"upsertComponent">;
287
+ ref: import('zod').ZodString;
288
+ label: import('zod').ZodOptional<import('zod').ZodString>;
289
+ props: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnknown>>;
290
+ slots: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodString>>;
291
+ fromRef: import('zod').ZodOptional<import('zod').ZodString>;
292
+ instanceRef: import('zod').ZodOptional<import('zod').ZodString>;
293
+ }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
294
+ type: import('zod').ZodLiteral<"deleteComponent">;
295
+ ref: import('zod').ZodString;
269
296
  }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
270
297
  desktop: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnion<readonly [import('zod').ZodString, import('zod').ZodNumber]>>>;
271
298
  tablet: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnion<readonly [import('zod').ZodString, import('zod').ZodNumber]>>>;
@@ -8,20 +8,22 @@ var validateShape = {
8
8
  };
9
9
  var validate = (input, space) => {
10
10
  const result = draftBatch(space, input.environment ?? "main", input.operations);
11
- return result.ok ? {
12
- valid: true,
13
- errors: [],
14
- warnings: result.warnings
15
- } : {
11
+ if (!result.ok) return {
16
12
  valid: false,
17
13
  errors: result.errors,
18
14
  warnings: result.warnings
19
15
  };
16
+ return {
17
+ valid: true,
18
+ errors: [],
19
+ warnings: result.warnings,
20
+ ...result.suggestions.length > 0 ? { suggestions: result.suggestions } : {}
21
+ };
20
22
  };
21
23
  var validateTool = defineTool({
22
24
  name: "plitzi_validate",
23
25
  title: "Validate",
24
- description: "Check a batch of operations without executing them. Returns teachable errors and warnings — including any PRE-EXISTING malformation in a resource the batch touches (which blocks a save until fixed).",
26
+ description: "Check a batch of operations without executing them. Returns teachable errors and warnings — including any PRE-EXISTING malformation in a resource the batch touches (which blocks a save until fixed) — and suggestions: shorter ways to the same page the batch opens up (a layout, a component, an element’s own content).",
25
27
  inputShape: validateShape,
26
28
  access: "read",
27
29
  run: (input, ctx) => validate(input, ctx.space)
@@ -1,4 +1,4 @@
1
- import { CssProps } from '@plitzi/sdk-authoring';
1
+ import { NotificationsSpec, CssProps } from '@plitzi/sdk-authoring';
2
2
  import { ElementRuntime, Environment, WhileRunning } from '@plitzi/sdk-shared';
3
3
  export type Env = Environment;
4
4
  /** CSS declarations for one selector state. Keys are kebab-case CSS properties (see plitzi://css-properties).
@@ -49,6 +49,26 @@ export interface AILayoutSummary {
49
49
  slots: string[];
50
50
  elementCount: number;
51
51
  }
52
+ /** A component of the space: a reusable subtree, placed on a page as an instance and edited as a root of its own. */
53
+ export interface AIComponentSummary {
54
+ ref: string;
55
+ label: string;
56
+ /** What an instance hands in, each an attribute of the instance and read inside as `{{ props.<name> }}`. */
57
+ props: Record<string, {
58
+ type: string;
59
+ description: string;
60
+ required?: boolean;
61
+ default?: unknown;
62
+ options?: string[];
63
+ }>;
64
+ /** Elements of its tree an instance fills with its children, each child naming the one it fills (`slot`). */
65
+ slots: string[];
66
+ /** The root of its tree: `pageRef` for every element op inside it is the component's ref. */
67
+ rootRef: string;
68
+ /** How many places render it. */
69
+ instances: number;
70
+ elementCount: number;
71
+ }
52
72
  /** A page folder in the sidebar tree. `ref` is the folder's id; pages reference it
53
73
  * by that id via their `folder`, and nested folders via `parentId`. */
54
74
  export interface AIFolder {
@@ -177,6 +197,8 @@ export interface AIElementDetail {
177
197
  /** Where this element renders, when it is not the default 'shared'. An apiContainer reading through a connector
178
198
  * is 'server'; one showing 'client' (or nothing) fetches from the browser instead and ignores its connector. */
179
199
  runtime?: ElementRuntime;
200
+ /** The feature flag it exists under — 'name' while on, '!name' while off — when it is gated at all. */
201
+ flag?: string;
180
202
  /** Which variant/visibility this element applies today (element.definition.initialState). */
181
203
  initialState?: AIInitialState;
182
204
  /** Data bindings on this element, grouped by category. */
@@ -235,7 +257,10 @@ export interface AISchemaVariable {
235
257
  /** Space-level settings: the arbitrary global CSS (`customCss`), state persistence, and the user/auth provider
236
258
  * configuration. Every field is optional — a patch changes only the keys it sends. */
237
259
  export interface AISettings {
260
+ /** The space's own global CSS — without the notifications' rule, which reads as `notifications`. */
238
261
  customCss?: string;
262
+ /** How the toasts an `addNotification` step shows look: colours and shape, any CSS value. */
263
+ notifications?: NotificationsSpec;
239
264
  /** Keep `runtime.state` — what `setState` writes — across reloads, filed under whoever is signed in. */
240
265
  keepState?: boolean;
241
266
  stateStorage?: 'localStorage' | 'sessionStorage';
@@ -45,6 +45,8 @@ export interface WriteResponse {
45
45
  changed: ChangedResource[];
46
46
  elements?: WriteElement[];
47
47
  warnings?: string[];
48
+ /** Shorter ways to the same page the batch opened up. Never a reason it was refused. */
49
+ suggestions?: string[];
48
50
  errors?: ValidationError[];
49
51
  conflict?: {
50
52
  message: string;
@@ -85,6 +87,8 @@ export interface ValidationResult {
85
87
  valid: boolean;
86
88
  errors: ValidationError[];
87
89
  warnings: string[];
90
+ /** Shorter ways to the same page the batch would open up — a layout, a component, an element's own content. */
91
+ suggestions?: string[];
88
92
  }
89
93
  export interface SearchInput {
90
94
  query: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-mcp",
3
- "version": "0.37.8",
3
+ "version": "0.37.10",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist",
@@ -30,14 +30,14 @@
30
30
  "dependencies": {
31
31
  "@modelcontextprotocol/ext-apps": "^1.7.5",
32
32
  "@modelcontextprotocol/sdk": "^1.30.1",
33
- "@plitzi/plitzi-sdk": "0.37.8",
34
- "@plitzi/sdk-authoring": "0.37.8",
35
- "@plitzi/sdk-elements": "0.37.8",
36
- "@plitzi/sdk-interactions": "0.37.8",
37
- "@plitzi/sdk-schema": "0.37.8",
38
- "@plitzi/sdk-server": "0.37.8",
39
- "@plitzi/sdk-shared": "0.37.8",
40
- "@plitzi/sdk-style": "0.37.8",
33
+ "@plitzi/plitzi-sdk": "0.37.10",
34
+ "@plitzi/sdk-authoring": "0.37.10",
35
+ "@plitzi/sdk-elements": "0.37.10",
36
+ "@plitzi/sdk-interactions": "0.37.10",
37
+ "@plitzi/sdk-schema": "0.37.10",
38
+ "@plitzi/sdk-server": "0.37.10",
39
+ "@plitzi/sdk-shared": "0.37.10",
40
+ "@plitzi/sdk-style": "0.37.10",
41
41
  "ejs": "^6.0.1",
42
42
  "esbuild": "^0.28.2",
43
43
  "zod": "^4.6.5"