@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
@@ -0,0 +1,147 @@
1
+ import { invalidateIndex } from "../../../../helpers/space.js";
2
+ import { componentsUri, pageUri } from "../../../../helpers/uris.js";
3
+ import { empty, fail } from "../../../../helpers/opResult.js";
4
+ import { ID_REF_HINT, guardNewRef } from "../write.js";
5
+ import { addComponent, componentNamed, propNameProblem, treeOf, updateComponent } from "@plitzi/sdk-schema/helpers/components";
6
+ import { descendants } from "@plitzi/sdk-schema/helpers/elementTree";
7
+ import { z } from "zod";
8
+ import { isValidElementId } from "@plitzi/sdk-schema/helpers/elementId";
9
+ //#region src/modules/mcp/tools/operations/schema/components/upsertComponent.ts
10
+ /**
11
+ * A prop as a component declares it. Checked when the op runs rather than spelled out in the op's schema: the op union
12
+ * rides on every request of every conversation, and the guide — read once — says what a prop is.
13
+ */
14
+ var componentProp = z.object({
15
+ type: z.enum([
16
+ "text",
17
+ "textarea",
18
+ "number",
19
+ "boolean",
20
+ "select",
21
+ "scalar",
22
+ "json"
23
+ ]),
24
+ description: z.string(),
25
+ required: z.boolean().optional(),
26
+ default: z.union([
27
+ z.string(),
28
+ z.number(),
29
+ z.boolean()
30
+ ]).optional(),
31
+ options: z.array(z.string()).optional(),
32
+ label: z.string().optional()
33
+ });
34
+ var upsertComponentOp = z.object({
35
+ type: z.literal("upsertComponent"),
36
+ ref: z.string(),
37
+ label: z.string().optional(),
38
+ props: z.record(z.string(), z.unknown()).optional().describe("{ name: { type, description, required?, default?, options? } }"),
39
+ slots: z.array(z.string()).optional(),
40
+ fromRef: z.string().optional().describe("New only: the element it is made of"),
41
+ instanceRef: z.string().optional().describe("With fromRef: the instance left in its place")
42
+ }).describe("Declare or update a component; edit inside it with pageRef: \"<ref>\". See \"Components\" in plitzi://guide.");
43
+ /** The declaration an op writes, with nothing it leaves out — an update keeps what it does not name. */
44
+ var declarationOf = (op, props) => ({
45
+ ...op.label === void 0 ? {} : { label: op.label },
46
+ ...props === void 0 ? {} : { props },
47
+ ...op.slots === void 0 ? {} : { slots: op.slots }
48
+ });
49
+ /** The props an op declares, each held to what a prop is — or why one is not, by name. */
50
+ var parseProps = (props) => {
51
+ if (props === void 0) return {};
52
+ const parsed = {};
53
+ for (const [name, value] of Object.entries(props)) {
54
+ const result = componentProp.safeParse(value);
55
+ if (!result.success) return { error: `prop "${name}" is not a prop: ${result.error.issues.map((issue) => `${issue.path.join(".") || "it"} ${issue.message}`).join("; ")}` };
56
+ parsed[name] = result.data;
57
+ }
58
+ return { props: parsed };
59
+ };
60
+ /** Why a declaration cannot stand, in words, or undefined: the same two rules `sdk-schema` holds every writer to. */
61
+ var declarationProblem = (declaration, flat) => {
62
+ for (const name of Object.keys(declaration.props ?? {})) {
63
+ const problem = propNameProblem(name);
64
+ if (problem) return problem;
65
+ }
66
+ const missing = (declaration.slots ?? []).find((slot) => !Object.hasOwn(flat, slot));
67
+ return missing ? `slot "${missing}" is not an element of the component` : void 0;
68
+ };
69
+ var upsertComponent = (space, env, op) => {
70
+ const { schema } = space;
71
+ const stale = [componentsUri(env), pageUri(env, op.ref)];
72
+ const props = parseProps(op.props);
73
+ if ("error" in props) return fail("props", props.error, "A prop is { type: \"text\" | \"textarea\" | \"number\" | \"boolean\" | \"select\" | \"scalar\" | \"json\", description, required?, default?, options? }");
74
+ const declaration = declarationOf(op, props.props);
75
+ const existing = componentNamed(schema, op.ref);
76
+ if (existing) {
77
+ if (op.fromRef) return fail("fromRef", `Component "${op.ref}" already exists`, "fromRef makes a NEW component; pick another ref");
78
+ const problem = declarationProblem({
79
+ ...existing,
80
+ ...declaration
81
+ }, existing.flat);
82
+ if (problem || !updateComponent(schema, op.ref, {
83
+ ...existing,
84
+ ...declaration
85
+ })) return fail("props", problem ?? `Component "${op.ref}" refused the declaration`, "Name props as {{ props.<name> }} reads them, and slots as elements of the tree");
86
+ return {
87
+ ...empty(),
88
+ updated: 1,
89
+ staleResources: stale
90
+ };
91
+ }
92
+ if (!isValidElementId(op.ref)) return fail("ref", `"${op.ref}" is not a name a component can have`, ID_REF_HINT);
93
+ if (op.fromRef) {
94
+ const tree = treeOf(schema, op.fromRef);
95
+ const element = tree?.flat[op.fromRef];
96
+ if (!tree || !element?.definition.parentId) return fail("fromRef", `"${op.fromRef}" is not an element inside a page, a layout or a component`, "A page or a layout is a root, not a subtree — name an element inside one");
97
+ if (!op.instanceRef) return fail("instanceRef", "fromRef needs the name of the instance that takes the element’s place", "e.g. instanceRef: \"hero-card\"");
98
+ const guard = guardNewRef(space, op.instanceRef, "instanceRef");
99
+ if (guard) return guard;
100
+ const component = {
101
+ ...declaration,
102
+ id: op.ref,
103
+ rootId: "",
104
+ flat: {}
105
+ };
106
+ const problem = declarationProblem(declaration, Object.fromEntries([op.fromRef, ...descendants(tree.flat, op.fromRef)].map((id) => [id, tree.flat[id]])));
107
+ if (problem || !addComponent(schema, component, {
108
+ elementId: op.fromRef,
109
+ instanceId: op.instanceRef
110
+ })) return fail("props", problem ?? `"${op.fromRef}" could not be made a component`, "Slots name elements inside fromRef");
111
+ invalidateIndex(schema);
112
+ return {
113
+ ...empty(),
114
+ created: 1,
115
+ staleResources: stale,
116
+ elementRefs: [op.instanceRef]
117
+ };
118
+ }
119
+ const rootRef = `${op.ref}-root`;
120
+ const guard = guardNewRef(space, rootRef, "ref");
121
+ if (guard) return guard;
122
+ const component = {
123
+ ...declaration,
124
+ id: op.ref,
125
+ rootId: rootRef,
126
+ flat: { [rootRef]: {
127
+ id: rootRef,
128
+ attributes: { subType: "div" },
129
+ definition: {
130
+ label: op.label ?? op.ref,
131
+ type: "container",
132
+ rootId: rootRef,
133
+ items: [],
134
+ styleSelectors: { base: "" }
135
+ }
136
+ } }
137
+ };
138
+ const problem = declarationProblem(declaration, component.flat);
139
+ if (problem || !addComponent(schema, component)) return fail("props", problem ?? `Component "${op.ref}" could not be declared`, "A new component has one element yet — its root; add the rest with upsertElement { pageRef: \"<ref>\" } first, then name slots");
140
+ return {
141
+ ...empty(),
142
+ created: 1,
143
+ staleResources: stale
144
+ };
145
+ };
146
+ //#endregion
147
+ export { upsertComponent, upsertComponentOp };
@@ -22,6 +22,8 @@ export declare const patchElementOp: z.ZodObject<{
22
22
  client: "client";
23
23
  shared: "shared";
24
24
  }>>;
25
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
26
+ anchor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
25
27
  }, z.core.$strip>;
26
28
  export type PatchElement = z.infer<typeof patchElementOp>;
27
29
  export declare const patchElement: (space: Space, env: Env, op: PatchElement) => OpResult;
@@ -1,8 +1,9 @@
1
1
  import { findRootByRef, invalidateIndex, resolveRef } from "../../../../helpers/space.js";
2
2
  import { pageUri } from "../../../../helpers/uris.js";
3
3
  import { empty, fail } from "../../../../helpers/opResult.js";
4
- import { elementRuntime, initialStateInput, styleRefs } from "../shared.js";
4
+ import { elementAnchor, elementFlag, elementRuntime, initialStateInput, styleRefs } from "../shared.js";
5
5
  import { guardNewRef, writeInitialState } from "../write.js";
6
+ import { flagGateOf } from "@plitzi/sdk-authoring";
6
7
  import { z } from "zod";
7
8
  import { repointIds } from "@plitzi/sdk-schema/helpers/elementId";
8
9
  //#region src/modules/mcp/tools/operations/schema/elements/patchElement.ts
@@ -16,7 +17,9 @@ var patchElementOp = z.object({
16
17
  props: z.record(z.string(), z.unknown()).optional().describe("Merged onto existing props: listed keys change, null unsets a key, others are preserved"),
17
18
  style: styleRefs.optional().describe("Merged onto existing style: base replaces base, listed slots replace slots"),
18
19
  initialState: initialStateInput.optional().describe("Merged onto existing initialState: styleVariant overlays per class/selector, visibility overrides"),
19
- runtime: elementRuntime.optional()
20
+ runtime: elementRuntime.optional(),
21
+ flag: elementFlag.nullable().optional(),
22
+ anchor: elementAnchor.nullable().optional().describe("null removes it")
20
23
  }).describe("Partially update an EXISTING element: only the fields you pass change (props/style/initialState are merged, not replaced). Never creates — fails if ref does not resolve. Use upsertElement to create or fully replace.");
21
24
  var patchElement = (space, env, op) => {
22
25
  const page = findRootByRef(space.schema, op.pageRef);
@@ -48,6 +51,10 @@ var patchElement = (space, env, op) => {
48
51
  }
49
52
  if (op.initialState !== void 0) writeInitialState(el, op.initialState, true);
50
53
  if (op.runtime !== void 0) el.definition.runtime = op.runtime;
54
+ if (op.flag === null) Reflect.deleteProperty(el.definition, "flag");
55
+ else if (op.flag !== void 0) el.definition.flag = flagGateOf(op.flag, `Element "${op.ref}"`);
56
+ if (op.anchor === null) Reflect.deleteProperty(el.definition, "anchor");
57
+ else if (op.anchor !== void 0) el.definition.anchor = op.anchor;
51
58
  return {
52
59
  ...empty(),
53
60
  updated: 1,
@@ -3,6 +3,7 @@ import { pageUri } from "../../../../helpers/uris.js";
3
3
  import { empty, fail } from "../../../../helpers/opResult.js";
4
4
  import { elementInput, position } from "../shared.js";
5
5
  import { DROP_POSITION, collectInputRefs, createElement, guardNewRef, writeInitialState } from "../write.js";
6
+ import { flagGateOf } from "@plitzi/sdk-authoring";
6
7
  import { z } from "zod";
7
8
  //#region src/modules/mcp/tools/operations/schema/elements/upsertElement.ts
8
9
  var upsertElementOp = z.object({
@@ -32,6 +33,8 @@ var upsertElement = (space, env, op) => {
32
33
  }
33
34
  if (op.element.initialState !== void 0) writeInitialState(existing, op.element.initialState, false);
34
35
  if (op.element.runtime !== void 0) existing.definition.runtime = op.element.runtime;
36
+ if (op.element.flag !== void 0) existing.definition.flag = flagGateOf(op.element.flag, `Element "${op.element.ref}"`);
37
+ if (op.element.anchor !== void 0) existing.definition.anchor = op.element.anchor;
35
38
  return {
36
39
  ...empty(),
37
40
  updated: 1,
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ import { Space, OpResult } from '../../../../helpers';
3
+ import { Env } from '../../../../types';
4
+ export declare const deleteFlagOp: z.ZodObject<{
5
+ type: z.ZodLiteral<"deleteFlag">;
6
+ name: z.ZodString;
7
+ }, z.core.$strip>;
8
+ export type DeleteFlag = z.infer<typeof deleteFlagOp>;
9
+ export declare const deleteFlag: (space: Space, env: Env, op: DeleteFlag) => OpResult;
@@ -0,0 +1,21 @@
1
+ import { flagsUri } from "../../../../helpers/uris.js";
2
+ import { empty, fail } from "../../../../helpers/opResult.js";
3
+ import { z } from "zod";
4
+ //#region src/modules/mcp/tools/operations/schema/flags/deleteFlag.ts
5
+ var deleteFlagOp = z.object({
6
+ type: z.literal("deleteFlag"),
7
+ name: z.string()
8
+ }).describe("Remove a feature flag.");
9
+ var deleteFlag = (space, env, op) => {
10
+ const flags = space.schema.flags ?? {};
11
+ if (!Object.hasOwn(flags, op.name)) return fail("name", `The space declares no flag "${op.name}"`, `Read ${flagsUri(env)}`);
12
+ const { [op.name]: _removed, ...rest } = flags;
13
+ space.schema.flags = rest;
14
+ return {
15
+ ...empty(),
16
+ deleted: 1,
17
+ staleResources: [flagsUri(env)]
18
+ };
19
+ };
20
+ //#endregion
21
+ export { deleteFlag, deleteFlagOp };
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+ import { Space, OpResult } from '../../../../helpers';
3
+ import { Env } from '../../../../types';
4
+ export declare const upsertFlagOp: z.ZodObject<{
5
+ type: z.ZodLiteral<"upsertFlag">;
6
+ name: z.ZodString;
7
+ description: z.ZodOptional<z.ZodString>;
8
+ value: z.ZodBoolean;
9
+ rules: z.ZodOptional<z.ZodArray<z.ZodObject<{
10
+ when: z.ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, z.core.$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>;
11
+ value: z.ZodBoolean;
12
+ }, z.core.$strip>>>;
13
+ }, z.core.$strip>;
14
+ export type UpsertFlag = z.infer<typeof upsertFlagOp>;
15
+ export declare const upsertFlag: (space: Space, env: Env, op: UpsertFlag) => OpResult;
@@ -0,0 +1,38 @@
1
+ import { flagsUri } from "../../../../helpers/uris.js";
2
+ import { empty, fail } from "../../../../helpers/opResult.js";
3
+ import { ruleGroup } from "../shared.js";
4
+ import { z } from "zod";
5
+ import { isFlagName, isSchemaFlag } from "@plitzi/sdk-shared/flags";
6
+ //#region src/modules/mcp/tools/operations/schema/flags/upsertFlag.ts
7
+ var ruleInput = z.object({
8
+ when: ruleGroup,
9
+ value: z.boolean()
10
+ });
11
+ var upsertFlagOp = z.object({
12
+ type: z.literal("upsertFlag"),
13
+ name: z.string(),
14
+ description: z.string().optional(),
15
+ value: z.boolean().describe("When no rule matches"),
16
+ rules: z.array(ruleInput).optional()
17
+ }).describe("Declare or replace a feature flag (guide: Feature flags).");
18
+ var upsertFlag = (space, env, op) => {
19
+ if (!isFlagName(op.name)) return fail("name", `"${op.name}" is not a flag name`, "Letters, digits and \"_\", starting with a letter or \"_\"");
20
+ const flag = {
21
+ ...op.description ? { description: op.description } : {},
22
+ value: op.value,
23
+ rules: op.rules ?? []
24
+ };
25
+ if (!isSchemaFlag(flag)) return fail("rules", "A rule is { when: { combinator, rules: [...] }, value: true | false }", "Fix the rule groups");
26
+ const existed = Object.hasOwn(space.schema.flags ?? {}, op.name);
27
+ space.schema.flags = {
28
+ ...space.schema.flags,
29
+ [op.name]: flag
30
+ };
31
+ return {
32
+ ...empty(),
33
+ ...existed ? { updated: 1 } : { created: 1 },
34
+ staleResources: [flagsUri(env)]
35
+ };
36
+ };
37
+ //#endregion
38
+ export { upsertFlag, upsertFlagOp };
@@ -14,6 +14,8 @@ export * from './folders/upsertFolder';
14
14
  export * from './folders/deleteFolder';
15
15
  export * from './variables/upsertVariable';
16
16
  export * from './variables/deleteVariable';
17
+ export * from './flags/upsertFlag';
18
+ export * from './flags/deleteFlag';
17
19
  export * from './bindings/upsertBinding';
18
20
  export * from './bindings/patchBinding';
19
21
  export * from './bindings/deleteBinding';
@@ -21,3 +23,4 @@ export * from './interactions/upsertInteractionFlow';
21
23
  export * from './interactions/patchInteractionNode';
22
24
  export * from './interactions/deleteInteraction';
23
25
  export * from './settings/patchSettings';
26
+ export * from './components';
@@ -50,6 +50,8 @@ export declare const elementOps: {
50
50
  client: "client";
51
51
  shared: "shared";
52
52
  }>>;
53
+ flag: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
54
+ anchor: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
53
55
  }, import('zod/v4/core').$strip>;
54
56
  deleteElement: import('zod').ZodObject<{
55
57
  type: import('zod').ZodLiteral<"deleteElement">;
@@ -77,6 +79,7 @@ export declare const elementOps: {
77
79
  layout: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
78
80
  layoutContainer: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
79
81
  enabled: import('zod').ZodOptional<import('zod').ZodBoolean>;
82
+ flag: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
80
83
  }, import('zod/v4/core').$strip>;
81
84
  deletePage: import('zod').ZodObject<{
82
85
  type: import('zod').ZodLiteral<"deletePage">;
@@ -129,6 +132,20 @@ export declare const elementOps: {
129
132
  type: import('zod').ZodLiteral<"deleteVariable">;
130
133
  name: import('zod').ZodString;
131
134
  }, import('zod/v4/core').$strip>;
135
+ upsertFlag: import('zod').ZodObject<{
136
+ type: import('zod').ZodLiteral<"upsertFlag">;
137
+ name: import('zod').ZodString;
138
+ description: import('zod').ZodOptional<import('zod').ZodString>;
139
+ value: import('zod').ZodBoolean;
140
+ rules: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodObject<{
141
+ when: import('zod').ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, import('zod/v4/core').$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>;
142
+ value: import('zod').ZodBoolean;
143
+ }, import('zod/v4/core').$strip>>>;
144
+ }, import('zod/v4/core').$strip>;
145
+ deleteFlag: import('zod').ZodObject<{
146
+ type: import('zod').ZodLiteral<"deleteFlag">;
147
+ name: import('zod').ZodString;
148
+ }, import('zod/v4/core').$strip>;
132
149
  upsertBinding: import('zod').ZodObject<{
133
150
  type: import('zod').ZodLiteral<"upsertBinding">;
134
151
  pageRef: import('zod').ZodString;
@@ -238,6 +255,7 @@ export declare const elementOps: {
238
255
  patchSettings: import('zod').ZodObject<{
239
256
  type: import('zod').ZodLiteral<"patchSettings">;
240
257
  customCss: import('zod').ZodOptional<import('zod').ZodString>;
258
+ notifications: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodNullable<import('zod').ZodString>>>;
241
259
  keepState: import('zod').ZodOptional<import('zod').ZodBoolean>;
242
260
  stateStorage: import('zod').ZodOptional<import('zod').ZodEnum<{
243
261
  localStorage: "localStorage";
@@ -283,4 +301,17 @@ export declare const elementOps: {
283
301
  presence: import('zod').ZodOptional<import('zod').ZodBoolean>;
284
302
  }, import('zod/v4/core').$strip>>>>;
285
303
  }, import('zod/v4/core').$strip>;
304
+ upsertComponent: import('zod').ZodObject<{
305
+ type: import('zod').ZodLiteral<"upsertComponent">;
306
+ ref: import('zod').ZodString;
307
+ label: import('zod').ZodOptional<import('zod').ZodString>;
308
+ props: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnknown>>;
309
+ slots: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodString>>;
310
+ fromRef: import('zod').ZodOptional<import('zod').ZodString>;
311
+ instanceRef: import('zod').ZodOptional<import('zod').ZodString>;
312
+ }, import('zod/v4/core').$strip>;
313
+ deleteComponent: import('zod').ZodObject<{
314
+ type: import('zod').ZodLiteral<"deleteComponent">;
315
+ ref: import('zod').ZodString;
316
+ }, import('zod/v4/core').$strip>;
286
317
  };
@@ -1,11 +1,15 @@
1
1
  import { deleteBindingOp } from "./bindings/deleteBinding.js";
2
2
  import { patchBindingOp } from "./bindings/patchBinding.js";
3
3
  import { upsertBindingOp } from "./bindings/upsertBinding.js";
4
+ import { deleteComponentOp } from "./components/deleteComponent.js";
5
+ import { upsertComponentOp } from "./components/upsertComponent.js";
4
6
  import { deleteElementOp } from "./elements/deleteElement.js";
5
7
  import { moveElementOp } from "./elements/moveElement.js";
6
8
  import { patchElementOp } from "./elements/patchElement.js";
7
9
  import { repeatElementOp } from "./elements/repeatElement.js";
8
10
  import { upsertElementOp } from "./elements/upsertElement.js";
11
+ import { deleteFlagOp } from "./flags/deleteFlag.js";
12
+ import { upsertFlagOp } from "./flags/upsertFlag.js";
9
13
  import { deleteFolderOp } from "./folders/deleteFolder.js";
10
14
  import { upsertFolderOp } from "./folders/upsertFolder.js";
11
15
  import { deleteInteractionOp } from "./interactions/deleteInteraction.js";
@@ -33,13 +37,17 @@ var elementOps = {
33
37
  deleteFolder: deleteFolderOp,
34
38
  upsertVariable: upsertVariableOp,
35
39
  deleteVariable: deleteVariableOp,
40
+ upsertFlag: upsertFlagOp,
41
+ deleteFlag: deleteFlagOp,
36
42
  upsertBinding: upsertBindingOp,
37
43
  patchBinding: patchBindingOp,
38
44
  deleteBinding: deleteBindingOp,
39
45
  upsertInteractionFlow: upsertInteractionFlowOp,
40
46
  patchInteractionNode: patchInteractionNodeOp,
41
47
  deleteInteraction: deleteInteractionOp,
42
- patchSettings: patchSettingsOp
48
+ patchSettings: patchSettingsOp,
49
+ upsertComponent: upsertComponentOp,
50
+ deleteComponent: deleteComponentOp
43
51
  };
44
52
  //#endregion
45
53
  export { elementOps };
@@ -11,6 +11,7 @@ export declare const upsertPageOp: z.ZodObject<{
11
11
  layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
12
12
  layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
13
13
  enabled: z.ZodOptional<z.ZodBoolean>;
14
+ flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
14
15
  }, z.core.$strip>;
15
16
  export type UpsertPage = z.infer<typeof upsertPageOp>;
16
17
  export declare const upsertPage: (space: Space, env: Env, op: UpsertPage) => OpResult;
@@ -1,7 +1,9 @@
1
1
  import { findFolderByRef, findLayoutByRef, findPageByRef, indexAddPage, resolveRef } from "../../../../helpers/space.js";
2
2
  import { layoutsUri, pageUri, pagesUri } from "../../../../helpers/uris.js";
3
3
  import { empty, fail } from "../../../../helpers/opResult.js";
4
+ import { elementFlag } from "../shared.js";
4
5
  import { guardNewRef } from "../write.js";
6
+ import { flagGateOf } from "@plitzi/sdk-authoring";
5
7
  import { z } from "zod";
6
8
  //#region src/modules/mcp/tools/operations/schema/pages/upsertPage.ts
7
9
  var upsertPageOp = z.object({
@@ -13,7 +15,8 @@ var upsertPageOp = z.object({
13
15
  default: z.boolean().optional(),
14
16
  layout: z.string().nullable().optional().describe("Ref of the layout shell this page renders inside; \"\" or null detaches it. See plitzi://schema/{env}/layouts."),
15
17
  layoutContainer: z.string().nullable().optional().describe("Ref of the container INSIDE that shell where this page body goes — the slot. Must belong to it."),
16
- enabled: z.boolean().optional().describe("false disables the page in the published SDK runtime (not routable/accessible to end users); it stays editable here. Defaults to true")
18
+ enabled: z.boolean().optional().describe("false disables the page in the published SDK runtime (not routable/accessible to end users); it stays editable here. Defaults to true"),
19
+ flag: elementFlag.nullable().optional()
17
20
  }).describe("Create a page, or update it when ref already exists (only the fields you pass change).");
18
21
  var upsertPage = (space, env, op) => {
19
22
  const slug = op.slug === void 0 ? void 0 : op.slug.replace(/^\/+/, "");
@@ -63,6 +66,8 @@ var upsertPage = (space, env, op) => {
63
66
  ...layoutValue !== void 0 ? { layout: layoutValue } : {},
64
67
  ...slotValue !== void 0 ? { layoutContainer: slotValue } : {}
65
68
  };
69
+ if (op.flag === null) Reflect.deleteProperty(existing.definition, "flag");
70
+ else if (op.flag !== void 0) existing.definition.flag = flagGateOf(op.flag, `Page "${op.ref}"`);
66
71
  return {
67
72
  ...empty(),
68
73
  updated: 1,
@@ -92,7 +97,8 @@ var upsertPage = (space, env, op) => {
92
97
  label: op.label ?? op.ref,
93
98
  type: "page",
94
99
  items: [],
95
- styleSelectors: { base: "" }
100
+ styleSelectors: { base: "" },
101
+ ...op.flag ? { flag: flagGateOf(op.flag, `Page "${op.ref}"`) } : {}
96
102
  }
97
103
  };
98
104
  space.schema.flat[id] = page;
@@ -4,6 +4,7 @@ import { Env } from '../../../../types';
4
4
  export declare const patchSettingsOp: z.ZodObject<{
5
5
  type: z.ZodLiteral<"patchSettings">;
6
6
  customCss: z.ZodOptional<z.ZodString>;
7
+ notifications: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>;
7
8
  keepState: z.ZodOptional<z.ZodBoolean>;
8
9
  stateStorage: z.ZodOptional<z.ZodEnum<{
9
10
  localStorage: "localStorage";
@@ -1,5 +1,6 @@
1
1
  import { settingsUri } from "../../../../helpers/uris.js";
2
2
  import { empty, fail } from "../../../../helpers/opResult.js";
3
+ import { NOTIFICATIONS_FIELDS, splitNotificationsCss, withNotificationsCss } from "@plitzi/sdk-authoring";
3
4
  import { z } from "zod";
4
5
  import { channelProblems } from "@plitzi/sdk-shared/realtime";
5
6
  //#region src/modules/mcp/tools/operations/schema/settings/patchSettings.ts
@@ -21,9 +22,11 @@ var channel = z.object({
21
22
  publish: z.enum(["clients", "server"]).optional(),
22
23
  presence: z.boolean().optional()
23
24
  });
25
+ var notifications = z.record(z.string(), z.string().nullable());
24
26
  var patchSettingsOp = z.object({
25
27
  type: z.literal("patchSettings"),
26
28
  customCss: z.string().optional().describe("Raw global CSS for the whole space (keyframes, @font-face, resets)"),
29
+ notifications: notifications.optional().describe(`The toasts' look: ${NOTIFICATIONS_FIELDS.join(", ")} — CSS values; null removes one`),
27
30
  keepState: z.boolean().optional().describe("Keep runtime state (setState keys) across reloads"),
28
31
  stateStorage: z.enum(["localStorage", "sessionStorage"]).optional(),
29
32
  transientState: z.array(z.string()).optional().describe("Top-level state keys never kept"),
@@ -44,7 +47,7 @@ var patchSettingsOp = z.object({
44
47
  sessionGate: z.enum(["optimistic", "strict"]).optional().describe("Render from the stored session and confirm behind it (default), or wait for the confirmation"),
45
48
  sessionRevalidateSeconds: z.number().optional(),
46
49
  channels: z.record(z.string(), channel.nullable()).optional().describe("Realtime channels by topic pattern (`board:{id}`); null removes one. See the guide")
47
- }).describe("Merge space-level settings — global CSS, kept state, auth, realtime channels. Only the fields sent change. customCss is for site-wide CSS, never to style one element (attach a definition for that).");
50
+ }).describe("Merge space-level settings — global CSS, notifications, kept state, auth, realtime channels. Only the fields sent change. customCss is for site-wide CSS, never to style one element (attach a definition for that).");
48
51
  /** The space's channels with the patch merged in, pattern by pattern — `null` takes one out. */
49
52
  var mergeChannels = (current, patch) => {
50
53
  const merged = {
@@ -53,8 +56,23 @@ var mergeChannels = (current, patch) => {
53
56
  };
54
57
  return Object.fromEntries(Object.entries(merged).filter((entry) => entry[1] !== null));
55
58
  };
59
+ /**
60
+ * The space's `customCss` with the patch's own CSS and notifications in it. Both live in that one string — the
61
+ * notifications as the rule authoring writes for them — so each is read out of it, changed apart, and written back
62
+ * together: a new `customCss` keeps the notifications, and new notifications keep the space's CSS.
63
+ */
64
+ var patchCustomCss = (current, customCss, patch) => {
65
+ const was = splitNotificationsCss(current);
66
+ const sent = customCss === void 0 ? void 0 : splitNotificationsCss(customCss);
67
+ const merged = {
68
+ ...was.notifications,
69
+ ...sent?.notifications,
70
+ ...patch
71
+ };
72
+ return withNotificationsCss(sent?.customCss ?? was.customCss, Object.fromEntries(Object.entries(merged).filter((entry) => typeof entry[1] === "string")));
73
+ };
56
74
  var patchSettings = (space, env, op) => {
57
- const { type, channels, ...patch } = op;
75
+ const { type, channels, customCss, notifications: notificationsPatch, ...patch } = op;
58
76
  for (const [pattern, declaration] of Object.entries(channels ?? {})) {
59
77
  const [problem] = declaration === null ? [] : channelProblems(pattern, declaration);
60
78
  if (problem) return fail(`channels["${pattern}"]`, `Channel "${pattern}": ${problem}.`, "Fix the pattern or declaration and send it again");
@@ -62,6 +80,14 @@ var patchSettings = (space, env, op) => {
62
80
  const next = { ...space.schema.settings };
63
81
  for (const [key, value] of Object.entries(patch)) next[key] = value;
64
82
  if (channels) next.channels = mergeChannels(space.schema.settings.channels, channels);
83
+ if (customCss !== void 0 || notificationsPatch) {
84
+ const current = space.schema.settings.customCss;
85
+ try {
86
+ next.customCss = patchCustomCss(typeof current === "string" ? current : "", customCss, notificationsPatch);
87
+ } catch (error) {
88
+ return fail("notifications", error instanceof Error ? error.message : String(error), "Write one CSS value per field — a colour, a length or a token like var(--card)");
89
+ }
90
+ }
65
91
  space.schema.settings = next;
66
92
  return {
67
93
  ...empty(),
@@ -18,6 +18,8 @@ export interface ElementInput {
18
18
  };
19
19
  initialState?: InitialStateInput;
20
20
  runtime?: ElementRuntime;
21
+ flag?: string;
22
+ anchor?: string;
21
23
  children?: ElementInput[];
22
24
  }
23
25
  export declare const styleRefs: z.ZodObject<{
@@ -33,6 +35,10 @@ export declare const elementRuntime: z.ZodEnum<{
33
35
  client: "client";
34
36
  shared: "shared";
35
37
  }>;
38
+ /** The feature flag an element exists under — `'name'` while on, `'!name'` while off. Not a visibility. */
39
+ export declare const elementFlag: z.ZodString;
40
+ /** The element's `id` in the DOM, so `/page#anchor` lands on it — the one pattern every writer holds it to. */
41
+ export declare const elementAnchor: z.ZodString;
36
42
  export declare const initialStateInput: z.ZodObject<{
37
43
  styleVariant: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>>;
38
44
  visibility: z.ZodOptional<z.ZodBoolean>;
@@ -56,6 +62,8 @@ export declare const elementShape: {
56
62
  client: "client";
57
63
  shared: "shared";
58
64
  }>>;
65
+ flag: z.ZodOptional<z.ZodString>;
66
+ anchor: z.ZodOptional<z.ZodString>;
59
67
  };
60
68
  export declare const elementInput: z.ZodType<ElementInput>;
61
69
  export declare const position: z.ZodEnum<{
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ import { FLAG_GATE_PATTERN } from "@plitzi/sdk-shared/flags";
3
+ import { ANCHOR_PATTERN } from "@plitzi/sdk-shared/schema/anchor";
2
4
  //#region src/modules/mcp/tools/operations/schema/shared.ts
3
5
  var combinator = z.enum(["and", "or"]);
4
6
  var operator = z.enum([
@@ -64,6 +66,10 @@ var elementRuntime = z.enum([
64
66
  "client",
65
67
  "shared"
66
68
  ]).describe("Where this element renders: \"shared\" (default, both), \"client\" (browser only), or \"server\" (SSR only). An apiContainer MUST be \"server\" to read through a connector — a client one calls its own `query` URL instead.");
69
+ /** The feature flag an element exists under — `'name'` while on, `'!name'` while off. Not a visibility. */
70
+ var elementFlag = z.string().regex(FLAG_GATE_PATTERN).describe("Feature flag: `name` renders only while on, `!name` only while off");
71
+ /** The element's `id` in the DOM, so `/page#anchor` lands on it — the one pattern every writer holds it to. */
72
+ var elementAnchor = z.string().regex(ANCHOR_PATTERN).describe("Its DOM id, for `/page#anchor` and the `hash` of a link; one per page");
67
73
  var initialStateInput = z.object({
68
74
  styleVariant: styleVariantInput.optional().describe("Variant each attached class uses: { className: { base|slot: variantName | [names] } }"),
69
75
  visibility: z.boolean().optional().describe("Initial visibility of the element")
@@ -76,7 +82,9 @@ var elementShape = {
76
82
  props: z.record(z.string(), z.unknown()).optional().describe("Full replacement on update"),
77
83
  style: styleRefs.optional().describe("Definition refs per slot; style the element by attaching a definition"),
78
84
  initialState: initialStateInput.optional().describe("Applied style variant(s) and initial visibility (see plitzi://guide styling)"),
79
- runtime: elementRuntime.optional()
85
+ runtime: elementRuntime.optional(),
86
+ flag: elementFlag.optional(),
87
+ anchor: elementAnchor.optional()
80
88
  };
81
89
  var elementInput = z.lazy(() => z.object({
82
90
  ...elementShape,
@@ -133,4 +141,4 @@ var interactionNode = z.object({
133
141
  preview: z.record(z.string(), z.unknown()).optional()
134
142
  });
135
143
  //#endregion
136
- export { bindingCategory, bindingInput, elementInput, elementRuntime, elementShape, initialStateInput, interactionNode, interactionNodeType, position, ruleGroup, scalar, styleRefs, styleVariantInput };
144
+ export { bindingCategory, bindingInput, elementAnchor, elementFlag, elementInput, elementRuntime, elementShape, initialStateInput, interactionNode, interactionNodeType, position, ruleGroup, scalar, styleRefs, styleVariantInput };
@@ -2,6 +2,7 @@ import { computeVersion } from "../../../helpers/computeVersion.js";
2
2
  import { descendantCount, elementById, getLayoutElements, getPageElements, nameOf, orderedChildren, pageFoldersOf, pageRefOfElement, pagesUsingLayout, slugRouteParams, spaceIndex, strOr } from "../../../helpers/space.js";
3
3
  import { bindingsToAI, flowsFromInteractions } from "../../../helpers/interactions.js";
4
4
  import { definitionToAI, globalStylesForType, idStyleToAI } from "../style/translator.js";
5
+ import { flagSpecOf, splitNotificationsCss } from "@plitzi/sdk-authoring";
5
6
  import { descendants } from "@plitzi/sdk-schema/helpers/elementTree";
6
7
  //#region src/modules/mcp/tools/operations/schema/translator.ts
7
8
  var splitClasses = (value) => value ? value.split(/\s+/).filter(Boolean) : [];
@@ -182,6 +183,7 @@ var elementDetailToAI = (schema, el, style) => {
182
183
  childRefs: children.length > 0 ? children.map((child) => child.id) : void 0
183
184
  };
184
185
  if (el.definition.runtime) detail.runtime = el.definition.runtime;
186
+ if (el.definition.flag) detail.flag = flagSpecOf(el.definition.flag);
185
187
  const initialState = initialStateToAI(el);
186
188
  if (initialState) detail.initialState = initialState;
187
189
  const bindings = bindingsToAI(el.definition.bindings);
@@ -223,6 +225,12 @@ var schemaVariablesToAI = (schema, includeSubValues = true) => {
223
225
  var settingsToAI = (schema) => {
224
226
  const result = {};
225
227
  for (const [key, value] of Object.entries(schema.settings)) result[key] = value;
228
+ if (typeof schema.settings.customCss === "string") {
229
+ const { notifications, customCss } = splitNotificationsCss(schema.settings.customCss);
230
+ if (customCss) result.customCss = customCss;
231
+ else delete result.customCss;
232
+ if (Object.keys(notifications).length > 0) result.notifications = notifications;
233
+ }
226
234
  return result;
227
235
  };
228
236
  //#endregion