webstudio 0.284.0 → 0.285.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/lib/cli.js +505 -133
- package/lib/content-runtime.js +50 -50
- package/package.json +21 -21
- package/templates/cloudflare/package.json +1 -1
- package/templates/defaults/package.json +8 -8
- package/templates/react-router/package.json +8 -8
- package/templates/react-router-cloudflare/package.json +1 -1
- package/templates/ssg/package.json +6 -6
package/lib/cli.js
CHANGED
|
@@ -7640,6 +7640,12 @@ const serializeJsonDeterministically = (value2) => {
|
|
|
7640
7640
|
}
|
|
7641
7641
|
return result2;
|
|
7642
7642
|
};
|
|
7643
|
+
const areJsonValuesEqual = (left, right) => {
|
|
7644
|
+
if (left === void 0 || right === void 0) {
|
|
7645
|
+
return left === right;
|
|
7646
|
+
}
|
|
7647
|
+
return serializeJsonDeterministically(left) === serializeJsonDeterministically(right);
|
|
7648
|
+
};
|
|
7643
7649
|
const compareStrings = (left, right) => {
|
|
7644
7650
|
if (left < right) {
|
|
7645
7651
|
return -1;
|
|
@@ -8159,7 +8165,7 @@ const assetResourceContentOptions = discriminatedUnion("mode", [
|
|
|
8159
8165
|
length: number$2().int().positive().max(contentEngineLimits.hydratedRangeBytes)
|
|
8160
8166
|
}),
|
|
8161
8167
|
object({
|
|
8162
|
-
mode: literal$2("markdown-body"),
|
|
8168
|
+
mode: literal$2("markdown-body-ref"),
|
|
8163
8169
|
maxBytes: number$2().int().positive().max(contentEngineLimits.hydratedFileBytes).optional()
|
|
8164
8170
|
})
|
|
8165
8171
|
]);
|
|
@@ -8804,7 +8810,7 @@ const hydrateAssetResourceResult = async ({
|
|
|
8804
8810
|
});
|
|
8805
8811
|
}
|
|
8806
8812
|
let text2;
|
|
8807
|
-
if (options.mode === "markdown-body") {
|
|
8813
|
+
if (options.mode === "markdown-body-ref") {
|
|
8808
8814
|
try {
|
|
8809
8815
|
text2 = (await extractMarkdownBody(bytes, item.readLength || 1)).body;
|
|
8810
8816
|
const references = assetReferences?.[item.identity.contentRef];
|
|
@@ -9229,12 +9235,6 @@ const getAssetQueryFieldValue = (document2, path2, runtimeAsset) => {
|
|
|
9229
9235
|
}
|
|
9230
9236
|
return value2;
|
|
9231
9237
|
};
|
|
9232
|
-
const equalJson = (left, right) => {
|
|
9233
|
-
if (left === void 0 || right === void 0) {
|
|
9234
|
-
return left === right;
|
|
9235
|
-
}
|
|
9236
|
-
return serializeJsonDeterministically(left) === serializeJsonDeterministically(right);
|
|
9237
|
-
};
|
|
9238
9238
|
const isEmpty$1 = (value2) => {
|
|
9239
9239
|
if (typeof value2 === "string" || Array.isArray(value2)) {
|
|
9240
9240
|
return value2.length === 0;
|
|
@@ -9258,19 +9258,21 @@ const matchesAssetQueryFilter = (document2, filter2, runtimeAsset) => {
|
|
|
9258
9258
|
return isEmpty$1(value2) === filter2.value;
|
|
9259
9259
|
}
|
|
9260
9260
|
if (filter2.operator === "eq") {
|
|
9261
|
-
return
|
|
9261
|
+
return areJsonValuesEqual(value2, filter2.value);
|
|
9262
9262
|
}
|
|
9263
9263
|
if (filter2.operator === "ne") {
|
|
9264
|
-
return
|
|
9264
|
+
return areJsonValuesEqual(value2, filter2.value) === false;
|
|
9265
9265
|
}
|
|
9266
9266
|
if (filter2.operator === "in") {
|
|
9267
|
-
return filter2.value.some(
|
|
9267
|
+
return filter2.value.some(
|
|
9268
|
+
(candidate) => areJsonValuesEqual(value2, candidate)
|
|
9269
|
+
);
|
|
9268
9270
|
}
|
|
9269
9271
|
if (filter2.operator === "contains") {
|
|
9270
9272
|
if (typeof value2 === "string" && typeof filter2.value === "string") {
|
|
9271
9273
|
return value2.includes(filter2.value);
|
|
9272
9274
|
}
|
|
9273
|
-
return Array.isArray(value2) && value2.some((candidate) =>
|
|
9275
|
+
return Array.isArray(value2) && value2.some((candidate) => areJsonValuesEqual(candidate, filter2.value));
|
|
9274
9276
|
}
|
|
9275
9277
|
if (filter2.operator === "startsWith") {
|
|
9276
9278
|
return typeof value2 === "string" && typeof filter2.value === "string" && value2.startsWith(filter2.value);
|
|
@@ -9297,6 +9299,10 @@ const matchesAssetQueryWhere = (document2, where, runtimeAsset) => evaluateQuery
|
|
|
9297
9299
|
where,
|
|
9298
9300
|
(filter2) => matchesAssetQueryFilter(document2, filter2, runtimeAsset)
|
|
9299
9301
|
) === true;
|
|
9302
|
+
const supportsAssetQueryContent = ({
|
|
9303
|
+
document: document2,
|
|
9304
|
+
content: content2
|
|
9305
|
+
}) => content2.mode !== "markdown-body-ref" || document2.mimeType === "text/markdown" || document2.extension === "md";
|
|
9300
9306
|
const compareAssetQuerySortValues = (left, right) => {
|
|
9301
9307
|
const leftMissing = left === void 0 || left === null;
|
|
9302
9308
|
const rightMissing = right === void 0 || right === null;
|
|
@@ -9394,7 +9400,7 @@ const executeAssetQuery = async ({
|
|
|
9394
9400
|
}
|
|
9395
9401
|
const matched = documents.flatMap((document2) => {
|
|
9396
9402
|
const runtimeAsset = runtimeAssets?.[document2._id];
|
|
9397
|
-
if (matchesAssetQueryWhere(document2, query.where, runtimeAsset) === false) {
|
|
9403
|
+
if (supportsAssetQueryContent({ document: document2, content: query.content }) === false || matchesAssetQueryWhere(document2, query.where, runtimeAsset) === false) {
|
|
9398
9404
|
return [];
|
|
9399
9405
|
}
|
|
9400
9406
|
const item = toQueryItem(document2, query.output, runtimeAsset);
|
|
@@ -9545,7 +9551,10 @@ const getContentDocumentCandidateQueryIds = ({
|
|
|
9545
9551
|
plan,
|
|
9546
9552
|
available
|
|
9547
9553
|
}) => plan.queries.filter(
|
|
9548
|
-
(
|
|
9554
|
+
(query) => supportsAssetQueryContent({
|
|
9555
|
+
document: document2,
|
|
9556
|
+
content: query.content
|
|
9557
|
+
}) && evaluateWhere({ document: document2, where: query.where, available }) !== false
|
|
9549
9558
|
).map(({ id: id2 }) => id2);
|
|
9550
9559
|
const isContentDocumentCandidate = (input2) => getContentDocumentCandidateQueryIds(input2).length > 0;
|
|
9551
9560
|
const hasDynamicWhere = (where) => getQueryConditions(where).some(({ value: value2 }) => value2.type === "dynamic");
|
|
@@ -9562,7 +9571,7 @@ const selectContentHydrationCandidates = ({
|
|
|
9562
9571
|
continue;
|
|
9563
9572
|
}
|
|
9564
9573
|
const matched = documents.filter(
|
|
9565
|
-
(document2) => evaluateWhere({ document: document2, where: query.where, available: "all" }) !== false
|
|
9574
|
+
(document2) => supportsAssetQueryContent({ document: document2, content: query.content }) && evaluateWhere({ document: document2, where: query.where, available: "all" }) !== false
|
|
9566
9575
|
);
|
|
9567
9576
|
if (hasDynamicWhere(query.where) || usesRuntimeWhere(query.where) || query.sort.some(({ field }) => isAssetQueryRuntimeField(field[0])) || query.limit.type === "dynamic" || query.offset.type === "dynamic") {
|
|
9568
9577
|
for (const document2 of matched) {
|
|
@@ -9710,6 +9719,9 @@ const getStandardFieldPaths = (query) => {
|
|
|
9710
9719
|
addField(fields, [field]);
|
|
9711
9720
|
}
|
|
9712
9721
|
}
|
|
9722
|
+
if (query.content.mode === "markdown-body-ref") {
|
|
9723
|
+
addField(fields, ["extension"]);
|
|
9724
|
+
}
|
|
9713
9725
|
return sortFields(fields.values());
|
|
9714
9726
|
};
|
|
9715
9727
|
const getQueryRequirements = (query) => ({
|
|
@@ -25942,10 +25954,16 @@ const generateResources = ({
|
|
|
25942
25954
|
`;
|
|
25943
25955
|
generated += generatedVariables;
|
|
25944
25956
|
generated += generatedRequests;
|
|
25957
|
+
const actionResourceIds = /* @__PURE__ */ new Set();
|
|
25958
|
+
for (const prop2 of props2.values()) {
|
|
25959
|
+
if (prop2.type === "resource" && generatedResourceIds.has(prop2.value)) {
|
|
25960
|
+
actionResourceIds.add(prop2.value);
|
|
25961
|
+
}
|
|
25962
|
+
}
|
|
25945
25963
|
generated += ` const _data = new Map<string, ResourceRequest>([
|
|
25946
25964
|
`;
|
|
25947
25965
|
for (const dataSource2 of dataSources.values()) {
|
|
25948
|
-
if (dataSource2.type === "resource" && generatedResourceIds.has(dataSource2.resourceId)) {
|
|
25966
|
+
if (dataSource2.type === "resource" && generatedResourceIds.has(dataSource2.resourceId) && actionResourceIds.has(dataSource2.resourceId) === false) {
|
|
25949
25967
|
const name2 = scope2.getName(dataSource2.resourceId, dataSource2.name);
|
|
25950
25968
|
generated += ` ["${name2}", ${name2}],
|
|
25951
25969
|
`;
|
|
@@ -28820,6 +28838,11 @@ const runtimeOperationContractData = [
|
|
|
28820
28838
|
conflictResolution: {
|
|
28821
28839
|
type: "string",
|
|
28822
28840
|
enum: ["ours", "theirs", "merge"]
|
|
28841
|
+
},
|
|
28842
|
+
rootStyleConflictResolution: {
|
|
28843
|
+
description: 'How to resolve conflicting global root-local declarations: "ours" keeps the target project values; "theirs" uses the incoming source values. Required when conflicts exist.',
|
|
28844
|
+
type: "string",
|
|
28845
|
+
enum: ["ours", "theirs"]
|
|
28823
28846
|
}
|
|
28824
28847
|
},
|
|
28825
28848
|
required: ["sourceData", "pageId"]
|
|
@@ -42417,6 +42440,11 @@ const runtimeOperationContractData = [
|
|
|
42417
42440
|
conflictResolution: {
|
|
42418
42441
|
type: "string",
|
|
42419
42442
|
enum: ["ours", "theirs", "merge"]
|
|
42443
|
+
},
|
|
42444
|
+
rootStyleConflictResolution: {
|
|
42445
|
+
description: 'How to resolve conflicting global root-local declarations: "ours" keeps the target project values; "theirs" uses the incoming source values. Required when conflicts exist.',
|
|
42446
|
+
type: "string",
|
|
42447
|
+
enum: ["ours", "theirs"]
|
|
42420
42448
|
}
|
|
42421
42449
|
},
|
|
42422
42450
|
required: ["targetFolderId", "item"],
|
|
@@ -92952,7 +92980,7 @@ const runtimeOperationContractData = [
|
|
|
92952
92980
|
properties: {
|
|
92953
92981
|
mode: {
|
|
92954
92982
|
type: "string",
|
|
92955
|
-
const: "markdown-body"
|
|
92983
|
+
const: "markdown-body-ref"
|
|
92956
92984
|
},
|
|
92957
92985
|
maxBytes: {
|
|
92958
92986
|
type: "integer",
|
|
@@ -93336,7 +93364,7 @@ const runtimeOperationContractData = [
|
|
|
93336
93364
|
properties: {
|
|
93337
93365
|
mode: {
|
|
93338
93366
|
type: "string",
|
|
93339
|
-
const: "markdown-body"
|
|
93367
|
+
const: "markdown-body-ref"
|
|
93340
93368
|
},
|
|
93341
93369
|
maxBytes: {
|
|
93342
93370
|
type: "integer",
|
|
@@ -93794,7 +93822,7 @@ const runtimeOperationContractData = [
|
|
|
93794
93822
|
properties: {
|
|
93795
93823
|
mode: {
|
|
93796
93824
|
type: "string",
|
|
93797
|
-
const: "markdown-body"
|
|
93825
|
+
const: "markdown-body-ref"
|
|
93798
93826
|
},
|
|
93799
93827
|
maxBytes: {
|
|
93800
93828
|
type: "integer",
|
|
@@ -94344,7 +94372,7 @@ const runtimeOperationContractData = [
|
|
|
94344
94372
|
properties: {
|
|
94345
94373
|
mode: {
|
|
94346
94374
|
type: "string",
|
|
94347
|
-
const: "markdown-body"
|
|
94375
|
+
const: "markdown-body-ref"
|
|
94348
94376
|
},
|
|
94349
94377
|
maxBytes: {
|
|
94350
94378
|
type: "integer",
|
|
@@ -95563,7 +95591,7 @@ const runtimeOperationContractData = [
|
|
|
95563
95591
|
}
|
|
95564
95592
|
}
|
|
95565
95593
|
},
|
|
95566
|
-
required: ["resourceId", "
|
|
95594
|
+
required: ["resourceId", "propIds"],
|
|
95567
95595
|
additionalProperties: {}
|
|
95568
95596
|
},
|
|
95569
95597
|
readNamespaces: [
|
|
@@ -130474,9 +130502,6 @@ const upsertResourceProp$1 = (state, input2, context) => {
|
|
|
130474
130502
|
headers: resourceInput2.headers,
|
|
130475
130503
|
body: resourceInput2.body
|
|
130476
130504
|
});
|
|
130477
|
-
const dataSource2 = build2.dataSources.find(
|
|
130478
|
-
(dataSource22) => dataSource22.type === "resource" && dataSource22.resourceId === resourceId2
|
|
130479
|
-
);
|
|
130480
130505
|
const existingProp = findProp(build2.props, input2.instanceId, input2.propName);
|
|
130481
130506
|
const nextProp = createValidatedPropValueFromInput(
|
|
130482
130507
|
{
|
|
@@ -130495,19 +130520,16 @@ const upsertResourceProp$1 = (state, input2, context) => {
|
|
|
130495
130520
|
props: build2.props,
|
|
130496
130521
|
nextProps: [nextProp.prop]
|
|
130497
130522
|
});
|
|
130498
|
-
const dataSourceId2 = dataSource2?.id ?? context.createId();
|
|
130499
130523
|
return createRuntimeMutation({
|
|
130500
130524
|
payload: compactBuilderPatchPayload([
|
|
130501
130525
|
...createResourceUpsertPatchPayload({
|
|
130502
130526
|
build: build2,
|
|
130503
130527
|
resource: resource2,
|
|
130504
|
-
|
|
130505
|
-
scopeInstanceId: input2.scopeInstanceId ?? input2.instanceId,
|
|
130506
|
-
dataSourceName: input2.dataSourceName ?? resource2.name
|
|
130528
|
+
exposeAsDataSource: false
|
|
130507
130529
|
}),
|
|
130508
130530
|
...propPayload
|
|
130509
130531
|
]),
|
|
130510
|
-
result: { resourceId: resourceId2, dataSourceId:
|
|
130532
|
+
result: { resourceId: resourceId2, dataSourceId: void 0, propIds },
|
|
130511
130533
|
invalidatesNamespaces: [
|
|
130512
130534
|
"pages",
|
|
130513
130535
|
"instances",
|
|
@@ -135012,6 +135034,62 @@ const buildMergedBreakpointIds = (fragmentBreakpoints, existingBreakpoints, opti
|
|
|
135012
135034
|
}
|
|
135013
135035
|
return mergedBreakpointIds;
|
|
135014
135036
|
};
|
|
135037
|
+
const findLocalStyleSourceIds = ({
|
|
135038
|
+
instanceId: instanceId2,
|
|
135039
|
+
styleSourceSelections,
|
|
135040
|
+
styleSources
|
|
135041
|
+
}) => {
|
|
135042
|
+
const localStyleSourceIds = new Set(
|
|
135043
|
+
Array.from(styleSources).filter((styleSource2) => styleSource2.type === "local").map((styleSource2) => styleSource2.id)
|
|
135044
|
+
);
|
|
135045
|
+
const selection = Array.from(styleSourceSelections).find(
|
|
135046
|
+
(candidate) => candidate.instanceId === instanceId2
|
|
135047
|
+
);
|
|
135048
|
+
return selection?.values.filter((id2) => localStyleSourceIds.has(id2)) ?? [];
|
|
135049
|
+
};
|
|
135050
|
+
const detectRootStyleConflicts = ({
|
|
135051
|
+
fragmentStyleSources,
|
|
135052
|
+
fragmentStyleSourceSelections,
|
|
135053
|
+
fragmentStyles,
|
|
135054
|
+
existingStyleSources,
|
|
135055
|
+
existingStyleSourceSelections,
|
|
135056
|
+
existingStyles,
|
|
135057
|
+
mergedBreakpointIds
|
|
135058
|
+
}) => {
|
|
135059
|
+
const incomingStyleSourceIds = new Set(
|
|
135060
|
+
findLocalStyleSourceIds({
|
|
135061
|
+
instanceId: ROOT_INSTANCE_ID,
|
|
135062
|
+
styleSourceSelections: fragmentStyleSourceSelections,
|
|
135063
|
+
styleSources: fragmentStyleSources
|
|
135064
|
+
})
|
|
135065
|
+
);
|
|
135066
|
+
const existingStyleSourceId = findLocalStyleSourceIds({
|
|
135067
|
+
instanceId: ROOT_INSTANCE_ID,
|
|
135068
|
+
styleSourceSelections: existingStyleSourceSelections.values(),
|
|
135069
|
+
styleSources: existingStyleSources.values()
|
|
135070
|
+
}).at(-1);
|
|
135071
|
+
if (incomingStyleSourceIds.size === 0 || existingStyleSourceId === void 0) {
|
|
135072
|
+
return [];
|
|
135073
|
+
}
|
|
135074
|
+
const conflicts = [];
|
|
135075
|
+
for (const incomingStyle of fragmentStyles) {
|
|
135076
|
+
if (incomingStyleSourceIds.has(incomingStyle.styleSourceId) === false) {
|
|
135077
|
+
continue;
|
|
135078
|
+
}
|
|
135079
|
+
const normalizedIncomingStyle = {
|
|
135080
|
+
...incomingStyle,
|
|
135081
|
+
styleSourceId: existingStyleSourceId,
|
|
135082
|
+
breakpointId: mergedBreakpointIds.get(incomingStyle.breakpointId) ?? incomingStyle.breakpointId
|
|
135083
|
+
};
|
|
135084
|
+
const existingStyle = existingStyles.get(
|
|
135085
|
+
getStyleDeclKey(normalizedIncomingStyle)
|
|
135086
|
+
);
|
|
135087
|
+
if (existingStyle !== void 0 && toValue(existingStyle.value) !== toValue(incomingStyle.value)) {
|
|
135088
|
+
conflicts.push({ existingStyle, incomingStyle });
|
|
135089
|
+
}
|
|
135090
|
+
}
|
|
135091
|
+
return conflicts;
|
|
135092
|
+
};
|
|
135015
135093
|
const getStyleSourceStylesSignature = (styleSourceId2, styles, breakpoints, mergedBreakpointIds) => {
|
|
135016
135094
|
const tokenStyles = styles.filter((decl) => decl.styleSourceId === styleSourceId2).map((decl) => {
|
|
135017
135095
|
const breakpointId2 = mergedBreakpointIds.get(decl.breakpointId) ?? decl.breakpointId;
|
|
@@ -135706,6 +135784,17 @@ const insertFragmentBreakpointsMutable = ({
|
|
|
135706
135784
|
}
|
|
135707
135785
|
return { mergedBreakpointIds, didMergeBreakpointsDueToLimit };
|
|
135708
135786
|
};
|
|
135787
|
+
const buildFragmentInsertionBreakpointIds = ({
|
|
135788
|
+
fragmentBreakpoints,
|
|
135789
|
+
targetBreakpoints
|
|
135790
|
+
}) => {
|
|
135791
|
+
let generatedIdIndex = 0;
|
|
135792
|
+
return insertFragmentBreakpointsMutable({
|
|
135793
|
+
fragment: { breakpoints: fragmentBreakpoints },
|
|
135794
|
+
breakpoints: new Map(targetBreakpoints),
|
|
135795
|
+
createId: () => `conflict-detection-breakpoint-${generatedIdIndex++}`
|
|
135796
|
+
}).mergedBreakpointIds;
|
|
135797
|
+
};
|
|
135709
135798
|
const insertWebstudioFragmentCopy = ({
|
|
135710
135799
|
data: data2,
|
|
135711
135800
|
fragment,
|
|
@@ -136017,6 +136106,47 @@ const detectFragmentTokenConflicts = ({
|
|
|
136017
136106
|
mergedBreakpointIds
|
|
136018
136107
|
});
|
|
136019
136108
|
};
|
|
136109
|
+
const detectFragmentRootStyleConflicts = ({
|
|
136110
|
+
fragment,
|
|
136111
|
+
targetData
|
|
136112
|
+
}) => {
|
|
136113
|
+
const mergedBreakpointIds = buildFragmentInsertionBreakpointIds({
|
|
136114
|
+
fragmentBreakpoints: fragment.breakpoints,
|
|
136115
|
+
targetBreakpoints: targetData.breakpoints
|
|
136116
|
+
});
|
|
136117
|
+
return detectRootStyleConflicts({
|
|
136118
|
+
fragmentStyleSources: fragment.styleSources,
|
|
136119
|
+
fragmentStyleSourceSelections: fragment.styleSourceSelections,
|
|
136120
|
+
fragmentStyles: fragment.styles,
|
|
136121
|
+
existingStyleSources: targetData.styleSources,
|
|
136122
|
+
existingStyleSourceSelections: targetData.styleSourceSelections,
|
|
136123
|
+
existingStyles: targetData.styles,
|
|
136124
|
+
mergedBreakpointIds
|
|
136125
|
+
});
|
|
136126
|
+
};
|
|
136127
|
+
const resolveFragmentRootStyleConflicts = ({
|
|
136128
|
+
fragment,
|
|
136129
|
+
targetData,
|
|
136130
|
+
resolution: resolution2
|
|
136131
|
+
}) => {
|
|
136132
|
+
if (resolution2 !== "ours") {
|
|
136133
|
+
return fragment;
|
|
136134
|
+
}
|
|
136135
|
+
const conflictingStyles = new Set(
|
|
136136
|
+
detectFragmentRootStyleConflicts({ fragment, targetData }).map(
|
|
136137
|
+
(conflict) => conflict.incomingStyle
|
|
136138
|
+
)
|
|
136139
|
+
);
|
|
136140
|
+
if (conflictingStyles.size === 0) {
|
|
136141
|
+
return fragment;
|
|
136142
|
+
}
|
|
136143
|
+
return {
|
|
136144
|
+
...fragment,
|
|
136145
|
+
styles: fragment.styles.filter(
|
|
136146
|
+
(style) => conflictingStyles.has(style) === false
|
|
136147
|
+
)
|
|
136148
|
+
};
|
|
136149
|
+
};
|
|
136020
136150
|
const insertIndexInput$1 = z$2.number().int().nonnegative();
|
|
136021
136151
|
const createSharedSlot = ({
|
|
136022
136152
|
id: id2,
|
|
@@ -136430,6 +136560,41 @@ const detachSharedSlotChildrenMutable = ({
|
|
|
136430
136560
|
createId
|
|
136431
136561
|
});
|
|
136432
136562
|
};
|
|
136563
|
+
const getWarningKey = ({ instanceId: instanceId2, message }) => `${instanceId2}\0${message}`;
|
|
136564
|
+
const getFragmentPlacementContentModelWarnings = ({
|
|
136565
|
+
children,
|
|
136566
|
+
instances,
|
|
136567
|
+
props: props2,
|
|
136568
|
+
metas,
|
|
136569
|
+
parentSelector = []
|
|
136570
|
+
}) => {
|
|
136571
|
+
const warnings = /* @__PURE__ */ new Map();
|
|
136572
|
+
for (const child of children) {
|
|
136573
|
+
if (child.type !== "id") {
|
|
136574
|
+
continue;
|
|
136575
|
+
}
|
|
136576
|
+
isTreeSatisfyingContentModel({
|
|
136577
|
+
instances,
|
|
136578
|
+
props: props2,
|
|
136579
|
+
metas,
|
|
136580
|
+
instanceSelector: [child.value, ...parentSelector],
|
|
136581
|
+
onError: (message, instanceSelector) => {
|
|
136582
|
+
const warning2 = { message, instanceId: instanceSelector[0] };
|
|
136583
|
+
warnings.set(getWarningKey(warning2), warning2);
|
|
136584
|
+
}
|
|
136585
|
+
});
|
|
136586
|
+
}
|
|
136587
|
+
return Array.from(warnings.values());
|
|
136588
|
+
};
|
|
136589
|
+
const getNewFragmentContentModelWarnings = ({
|
|
136590
|
+
warnings,
|
|
136591
|
+
allowedWarnings
|
|
136592
|
+
}) => {
|
|
136593
|
+
const allowedWarningKeys = new Set(allowedWarnings.map(getWarningKey));
|
|
136594
|
+
return warnings.filter(
|
|
136595
|
+
(warning2) => allowedWarningKeys.has(getWarningKey(warning2)) === false
|
|
136596
|
+
);
|
|
136597
|
+
};
|
|
136433
136598
|
const getCollectionDropTarget = (instances, dropTarget) => {
|
|
136434
136599
|
const [parentId, grandparentId] = dropTarget.parentSelector;
|
|
136435
136600
|
const parent = instances.get(parentId);
|
|
@@ -139322,7 +139487,7 @@ const inspectInstance$1 = (state, input2) => {
|
|
|
139322
139487
|
}
|
|
139323
139488
|
const depths = getInstanceDepths(instances, [input2.instanceId]);
|
|
139324
139489
|
const parents = getInstanceParents(instances);
|
|
139325
|
-
const include = new Set(input2.include ?? []);
|
|
139490
|
+
const include = new Set(input2.include ?? ["props"]);
|
|
139326
139491
|
const details = serializeInstanceSummary(
|
|
139327
139492
|
instance2,
|
|
139328
139493
|
depths.get(instance2.id) ?? 0,
|
|
@@ -214583,6 +214748,7 @@ const createInsertFragmentMutation = ({
|
|
|
214583
214748
|
contentMode = false,
|
|
214584
214749
|
additionalAvailableVariables = [],
|
|
214585
214750
|
getResultDetails,
|
|
214751
|
+
validateContentModel = true,
|
|
214586
214752
|
context
|
|
214587
214753
|
}) => {
|
|
214588
214754
|
const mutationState = getRequiredComponentInsertState(state);
|
|
@@ -214652,6 +214818,43 @@ const createInsertFragmentMutation = ({
|
|
|
214652
214818
|
return child;
|
|
214653
214819
|
}
|
|
214654
214820
|
);
|
|
214821
|
+
if (validateContentModel) {
|
|
214822
|
+
const validationInstances = new Map(nextData.instances);
|
|
214823
|
+
const validationParent = {
|
|
214824
|
+
...parent,
|
|
214825
|
+
children: mode === "replace" ? insertedChildren : [
|
|
214826
|
+
...parentChildren.slice(0, insertIndex),
|
|
214827
|
+
...insertedChildren,
|
|
214828
|
+
...parentChildren.slice(insertIndex)
|
|
214829
|
+
]
|
|
214830
|
+
};
|
|
214831
|
+
validationInstances.set(parent.id, validationParent);
|
|
214832
|
+
const { instanceSelector: parentSelector } = findPageAndSelectorByInstanceId(
|
|
214833
|
+
mutationState.pages,
|
|
214834
|
+
validationInstances,
|
|
214835
|
+
parent.id
|
|
214836
|
+
);
|
|
214837
|
+
const allowedWarnings = context.allowLegacyContentModelWarnings ? getFragmentPlacementContentModelWarnings({
|
|
214838
|
+
children: insertedChildren,
|
|
214839
|
+
instances: validationInstances,
|
|
214840
|
+
props: nextData.props,
|
|
214841
|
+
metas: componentMetas
|
|
214842
|
+
}) : [];
|
|
214843
|
+
const warnings = getFragmentPlacementContentModelWarnings({
|
|
214844
|
+
children: insertedChildren,
|
|
214845
|
+
instances: validationInstances,
|
|
214846
|
+
props: nextData.props,
|
|
214847
|
+
metas: componentMetas,
|
|
214848
|
+
parentSelector
|
|
214849
|
+
});
|
|
214850
|
+
const [contentModelError] = getNewFragmentContentModelWarnings({
|
|
214851
|
+
warnings,
|
|
214852
|
+
allowedWarnings
|
|
214853
|
+
});
|
|
214854
|
+
if (contentModelError !== void 0) {
|
|
214855
|
+
return throwBuilderRuntimeError("BAD_REQUEST", contentModelError.message);
|
|
214856
|
+
}
|
|
214857
|
+
}
|
|
214655
214858
|
const createResult = (parentInstanceId2, removedInstanceIds) => ({
|
|
214656
214859
|
instanceIds: Array.from(newInstanceIds.values()).filter(
|
|
214657
214860
|
(instanceId2) => instanceId2 !== ROOT_INSTANCE_ID && mutationState.instances.has(instanceId2) === false
|
|
@@ -214798,6 +215001,7 @@ const insertComponent$1 = (state, input2, context) => {
|
|
|
214798
215001
|
templates: templates2,
|
|
214799
215002
|
mode: input2.mode,
|
|
214800
215003
|
insertIndex: input2.insertIndex,
|
|
215004
|
+
validateContentModel: false,
|
|
214801
215005
|
context
|
|
214802
215006
|
});
|
|
214803
215007
|
};
|
|
@@ -215241,6 +215445,12 @@ const migrateLoadedData = (state) => {
|
|
|
215241
215445
|
invalidatesNamespaces: webstudioDataNamespaces
|
|
215242
215446
|
});
|
|
215243
215447
|
};
|
|
215448
|
+
const collectPageTransferItems = (item) => {
|
|
215449
|
+
if (item.type === "page" || item.type === "template") {
|
|
215450
|
+
return [item];
|
|
215451
|
+
}
|
|
215452
|
+
return item.children.flatMap(collectPageTransferItems);
|
|
215453
|
+
};
|
|
215244
215454
|
const pageTransferPageInput = z$2.object({
|
|
215245
215455
|
type: z$2.literal("page"),
|
|
215246
215456
|
page: z$2.custom(),
|
|
@@ -215305,7 +215515,10 @@ const pageCopyInput = z$2.object({
|
|
|
215305
215515
|
sourceData: sourceWebstudioDataInput,
|
|
215306
215516
|
pageId: z$2.string().describe("ID of the page in sourceData to copy into this project."),
|
|
215307
215517
|
parentFolderId: z$2.string().optional(),
|
|
215308
|
-
conflictResolution: z$2.enum(["ours", "theirs", "merge"]).optional()
|
|
215518
|
+
conflictResolution: z$2.enum(["ours", "theirs", "merge"]).optional(),
|
|
215519
|
+
rootStyleConflictResolution: z$2.enum(["ours", "theirs"]).optional().describe(
|
|
215520
|
+
'How to resolve conflicting global root-local declarations: "ours" keeps the target project values; "theirs" uses the incoming source values. Required when conflicts exist.'
|
|
215521
|
+
)
|
|
215309
215522
|
});
|
|
215310
215523
|
const folderDuplicateInput = z$2.object({
|
|
215311
215524
|
projectId: z$2.string(),
|
|
@@ -215424,6 +215637,7 @@ const copyPageRootAndBodyMutable = ({
|
|
|
215424
215637
|
projectId,
|
|
215425
215638
|
metas,
|
|
215426
215639
|
conflictResolution,
|
|
215640
|
+
rootStyleConflictResolution,
|
|
215427
215641
|
contentModeCopyableProp,
|
|
215428
215642
|
createId = nanoid,
|
|
215429
215643
|
contentMode = false
|
|
@@ -215441,6 +215655,7 @@ const copyPageRootAndBodyMutable = ({
|
|
|
215441
215655
|
projectId,
|
|
215442
215656
|
metas,
|
|
215443
215657
|
conflictResolution,
|
|
215658
|
+
rootStyleConflictResolution,
|
|
215444
215659
|
systemDataSourceId,
|
|
215445
215660
|
contentModeCopyableProp,
|
|
215446
215661
|
createId,
|
|
@@ -215455,6 +215670,7 @@ const copyPageFragmentsMutable = ({
|
|
|
215455
215670
|
projectId,
|
|
215456
215671
|
metas,
|
|
215457
215672
|
conflictResolution,
|
|
215673
|
+
rootStyleConflictResolution,
|
|
215458
215674
|
contentModeCopyableProp,
|
|
215459
215675
|
onBreakpointLimitMerge,
|
|
215460
215676
|
createId = nanoid,
|
|
@@ -215466,7 +215682,11 @@ const copyPageFragmentsMutable = ({
|
|
|
215466
215682
|
if (contentMode === false && rootFragment !== void 0) {
|
|
215467
215683
|
insertWebstudioFragmentCopy({
|
|
215468
215684
|
data: target,
|
|
215469
|
-
fragment:
|
|
215685
|
+
fragment: resolveFragmentRootStyleConflicts({
|
|
215686
|
+
fragment: rootFragment,
|
|
215687
|
+
targetData: target,
|
|
215688
|
+
resolution: rootStyleConflictResolution
|
|
215689
|
+
}),
|
|
215470
215690
|
availableVariables: findAvailableVariables({
|
|
215471
215691
|
...target,
|
|
215472
215692
|
startingInstanceId: ROOT_INSTANCE_ID
|
|
@@ -215660,6 +215880,7 @@ const copyPageMutable = ({
|
|
|
215660
215880
|
target,
|
|
215661
215881
|
projectId,
|
|
215662
215882
|
conflictResolution,
|
|
215883
|
+
rootStyleConflictResolution,
|
|
215663
215884
|
createId = nanoid
|
|
215664
215885
|
}) => {
|
|
215665
215886
|
const page2 = findPageByIdOrPath(source.pageId, source.data.pages);
|
|
@@ -215673,6 +215894,7 @@ const copyPageMutable = ({
|
|
|
215673
215894
|
systemDataSourceId: page2.systemDataSourceId,
|
|
215674
215895
|
projectId,
|
|
215675
215896
|
conflictResolution,
|
|
215897
|
+
rootStyleConflictResolution,
|
|
215676
215898
|
createId
|
|
215677
215899
|
});
|
|
215678
215900
|
if (copied === void 0) {
|
|
@@ -215867,6 +216089,21 @@ const duplicatePage$1 = (state, input2, context) => {
|
|
|
215867
216089
|
invalidatesNamespaces: pageCopyNamespaces
|
|
215868
216090
|
});
|
|
215869
216091
|
};
|
|
216092
|
+
const requireRootStyleConflictResolution = ({
|
|
216093
|
+
rootFragment,
|
|
216094
|
+
targetData,
|
|
216095
|
+
resolution: resolution2
|
|
216096
|
+
}) => {
|
|
216097
|
+
if (resolution2 === void 0 && rootFragment !== void 0 && detectFragmentRootStyleConflicts({
|
|
216098
|
+
fragment: rootFragment,
|
|
216099
|
+
targetData
|
|
216100
|
+
}).length > 0) {
|
|
216101
|
+
return throwBuilderRuntimeError(
|
|
216102
|
+
"CONFLICT",
|
|
216103
|
+
"Global root style conflicts require an explicit rootStyleConflictResolution (ours keeps target values; theirs uses incoming values)"
|
|
216104
|
+
);
|
|
216105
|
+
}
|
|
216106
|
+
};
|
|
215870
216107
|
const copyPage$1 = (state, input2, context) => {
|
|
215871
216108
|
const data2 = getRequiredWebstudioData(state);
|
|
215872
216109
|
const parentFolderId = input2.parentFolderId ?? data2.pages.rootFolderId;
|
|
@@ -215877,6 +216114,15 @@ const copyPage$1 = (state, input2, context) => {
|
|
|
215877
216114
|
build: data2,
|
|
215878
216115
|
assets: Array.from(data2.assets.values())
|
|
215879
216116
|
});
|
|
216117
|
+
const sourcePage = findPageByIdOrPath(input2.pageId, input2.sourceData.pages);
|
|
216118
|
+
if (sourcePage === void 0) {
|
|
216119
|
+
return throwBuilderRuntimeError("BAD_REQUEST", "Page could not be copied");
|
|
216120
|
+
}
|
|
216121
|
+
requireRootStyleConflictResolution({
|
|
216122
|
+
rootFragment: extractWebstudioFragment(input2.sourceData, ROOT_INSTANCE_ID),
|
|
216123
|
+
targetData: before,
|
|
216124
|
+
resolution: input2.rootStyleConflictResolution
|
|
216125
|
+
});
|
|
215880
216126
|
let pageId2;
|
|
215881
216127
|
const { payload } = produceWebstudioDataMutation(before, (draft) => {
|
|
215882
216128
|
pageId2 = insertPageCopyMutable({
|
|
@@ -215884,6 +216130,7 @@ const copyPage$1 = (state, input2, context) => {
|
|
|
215884
216130
|
target: { data: draft, folderId: parentFolderId },
|
|
215885
216131
|
projectId: input2.projectId,
|
|
215886
216132
|
conflictResolution: input2.conflictResolution,
|
|
216133
|
+
rootStyleConflictResolution: input2.rootStyleConflictResolution,
|
|
215887
216134
|
createId: context.createId
|
|
215888
216135
|
});
|
|
215889
216136
|
});
|
|
@@ -216220,6 +216467,7 @@ const insertPageCopyFromFragmentsMutable = ({
|
|
|
216220
216467
|
target,
|
|
216221
216468
|
projectId,
|
|
216222
216469
|
conflictResolution,
|
|
216470
|
+
rootStyleConflictResolution,
|
|
216223
216471
|
contentModeCopyableProp,
|
|
216224
216472
|
onBreakpointLimitMerge,
|
|
216225
216473
|
createId = nanoid
|
|
@@ -216231,6 +216479,7 @@ const insertPageCopyFromFragmentsMutable = ({
|
|
|
216231
216479
|
systemDataSourceId: source.page.systemDataSourceId,
|
|
216232
216480
|
projectId,
|
|
216233
216481
|
conflictResolution,
|
|
216482
|
+
rootStyleConflictResolution,
|
|
216234
216483
|
contentModeCopyableProp,
|
|
216235
216484
|
onBreakpointLimitMerge,
|
|
216236
216485
|
createId
|
|
@@ -216250,6 +216499,7 @@ const insertTemplateCopyFromFragmentsMutable = ({
|
|
|
216250
216499
|
target,
|
|
216251
216500
|
projectId,
|
|
216252
216501
|
conflictResolution,
|
|
216502
|
+
rootStyleConflictResolution,
|
|
216253
216503
|
contentModeCopyableProp,
|
|
216254
216504
|
onBreakpointLimitMerge,
|
|
216255
216505
|
createId = nanoid
|
|
@@ -216261,6 +216511,7 @@ const insertTemplateCopyFromFragmentsMutable = ({
|
|
|
216261
216511
|
systemDataSourceId: void 0,
|
|
216262
216512
|
projectId,
|
|
216263
216513
|
conflictResolution,
|
|
216514
|
+
rootStyleConflictResolution,
|
|
216264
216515
|
contentModeCopyableProp,
|
|
216265
216516
|
onBreakpointLimitMerge,
|
|
216266
216517
|
createId
|
|
@@ -216279,7 +216530,10 @@ const pageTransferInsertInput = z$2.object({
|
|
|
216279
216530
|
projectId: z$2.string(),
|
|
216280
216531
|
targetFolderId: z$2.string(),
|
|
216281
216532
|
item: pageTransferItemInput,
|
|
216282
|
-
conflictResolution: z$2.enum(["ours", "theirs", "merge"]).optional()
|
|
216533
|
+
conflictResolution: z$2.enum(["ours", "theirs", "merge"]).optional(),
|
|
216534
|
+
rootStyleConflictResolution: z$2.enum(["ours", "theirs"]).optional().describe(
|
|
216535
|
+
'How to resolve conflicting global root-local declarations: "ours" keeps the target project values; "theirs" uses the incoming source values. Required when conflicts exist.'
|
|
216536
|
+
)
|
|
216283
216537
|
});
|
|
216284
216538
|
const createPageCopyData = ({
|
|
216285
216539
|
data: data2,
|
|
@@ -216371,6 +216625,7 @@ const insertFolderCopyFromDataMutable = ({
|
|
|
216371
216625
|
target,
|
|
216372
216626
|
projectId,
|
|
216373
216627
|
conflictResolution,
|
|
216628
|
+
rootStyleConflictResolution,
|
|
216374
216629
|
contentModeCopyableProp,
|
|
216375
216630
|
onBreakpointLimitMerge,
|
|
216376
216631
|
forceFolderCopySuffix = false,
|
|
@@ -216384,6 +216639,7 @@ const insertFolderCopyFromDataMutable = ({
|
|
|
216384
216639
|
target,
|
|
216385
216640
|
projectId,
|
|
216386
216641
|
conflictResolution,
|
|
216642
|
+
rootStyleConflictResolution,
|
|
216387
216643
|
contentModeCopyableProp,
|
|
216388
216644
|
onBreakpointLimitMerge,
|
|
216389
216645
|
forceFolderCopySuffix,
|
|
@@ -216400,6 +216656,12 @@ const insertPageTransferItem$1 = (state, input2, context) => {
|
|
|
216400
216656
|
build: data2,
|
|
216401
216657
|
assets: Array.from(data2.assets.values())
|
|
216402
216658
|
});
|
|
216659
|
+
const rootFragment = collectPageTransferItems(input2.item)[0]?.rootFragment;
|
|
216660
|
+
requireRootStyleConflictResolution({
|
|
216661
|
+
rootFragment,
|
|
216662
|
+
targetData: before,
|
|
216663
|
+
resolution: input2.rootStyleConflictResolution
|
|
216664
|
+
});
|
|
216403
216665
|
let didReachBreakpointLimit = false;
|
|
216404
216666
|
const onBreakpointLimitMerge = () => {
|
|
216405
216667
|
didReachBreakpointLimit = true;
|
|
@@ -216412,6 +216674,7 @@ const insertPageTransferItem$1 = (state, input2, context) => {
|
|
|
216412
216674
|
target: { data: draft, folderId: input2.targetFolderId },
|
|
216413
216675
|
projectId: input2.projectId,
|
|
216414
216676
|
conflictResolution: input2.conflictResolution,
|
|
216677
|
+
rootStyleConflictResolution: input2.rootStyleConflictResolution,
|
|
216415
216678
|
onBreakpointLimitMerge,
|
|
216416
216679
|
createId: context.createId
|
|
216417
216680
|
});
|
|
@@ -216421,6 +216684,7 @@ const insertPageTransferItem$1 = (state, input2, context) => {
|
|
|
216421
216684
|
target: { data: draft },
|
|
216422
216685
|
projectId: input2.projectId,
|
|
216423
216686
|
conflictResolution: input2.conflictResolution,
|
|
216687
|
+
rootStyleConflictResolution: input2.rootStyleConflictResolution,
|
|
216424
216688
|
onBreakpointLimitMerge,
|
|
216425
216689
|
createId: context.createId
|
|
216426
216690
|
});
|
|
@@ -216430,6 +216694,7 @@ const insertPageTransferItem$1 = (state, input2, context) => {
|
|
|
216430
216694
|
target: { data: draft, parentFolderId: input2.targetFolderId },
|
|
216431
216695
|
projectId: input2.projectId,
|
|
216432
216696
|
conflictResolution: input2.conflictResolution,
|
|
216697
|
+
rootStyleConflictResolution: input2.rootStyleConflictResolution,
|
|
216433
216698
|
onBreakpointLimitMerge,
|
|
216434
216699
|
createId: context.createId
|
|
216435
216700
|
});
|
|
@@ -216456,6 +216721,7 @@ const insertFolderCopyFromDataWithContextMutable = ({
|
|
|
216456
216721
|
target,
|
|
216457
216722
|
projectId,
|
|
216458
216723
|
conflictResolution,
|
|
216724
|
+
rootStyleConflictResolution,
|
|
216459
216725
|
contentModeCopyableProp,
|
|
216460
216726
|
onBreakpointLimitMerge,
|
|
216461
216727
|
forceFolderCopySuffix,
|
|
@@ -216492,6 +216758,7 @@ const insertFolderCopyFromDataWithContextMutable = ({
|
|
|
216492
216758
|
target: { data: target.data, parentFolderId: newFolder.id },
|
|
216493
216759
|
projectId,
|
|
216494
216760
|
conflictResolution,
|
|
216761
|
+
rootStyleConflictResolution,
|
|
216495
216762
|
contentModeCopyableProp,
|
|
216496
216763
|
onBreakpointLimitMerge,
|
|
216497
216764
|
forceFolderCopySuffix,
|
|
@@ -216506,6 +216773,7 @@ const insertFolderCopyFromDataWithContextMutable = ({
|
|
|
216506
216773
|
target: { data: target.data, folderId: newFolder.id },
|
|
216507
216774
|
projectId,
|
|
216508
216775
|
conflictResolution,
|
|
216776
|
+
rootStyleConflictResolution,
|
|
216509
216777
|
contentModeCopyableProp,
|
|
216510
216778
|
onBreakpointLimitMerge,
|
|
216511
216779
|
createId
|
|
@@ -229572,7 +229840,7 @@ const runtimeOutputSchemas = {
|
|
|
229572
229840
|
"resources.upsert": looseObject({ resourceId: id, dataSourceId: id }),
|
|
229573
229841
|
"resources.upsertProp": looseObject({
|
|
229574
229842
|
resourceId: id,
|
|
229575
|
-
dataSourceId: id,
|
|
229843
|
+
dataSourceId: id.optional(),
|
|
229576
229844
|
propIds: stringArray
|
|
229577
229845
|
}),
|
|
229578
229846
|
"resources.delete": looseObject({
|
|
@@ -235299,7 +235567,7 @@ class StdioServerTransport {
|
|
|
235299
235567
|
const projectBuildDocs = {
|
|
235300
235568
|
"accessibility-review": '<!-- Adapted for Webstudio MCP from Community-Access/accessibility-agents, web-accessibility-wizard/SKILL.md (MIT): https://github.com/Community-Access/accessibility-agents/blob/main/codex-skills/web-accessibility-wizard/SKILL.md -->\n\n# Webstudio Accessibility Review\n\nUse this workflow when creating or reviewing a page, component, form, menu,\ndialog, or responsive layout. It is an LLM-assisted review, not proof of WCAG\nconformance. Do not claim that a page is accessible solely because this review\nhas no findings.\n\n## Evidence First\n\n1. Read `meta.index` and use focused project tools. Do not inspect Webstudio\n source code or generated files for normal project reviews.\n2. Run `audit` with `{"scopes":["accessibility"]}` before changing\n anything. It detects deterministic metadata issues such as labels, landmark\n structure, positive tabindex values, invalid static ARIA states, and unmuted\n autoplay. Fix confirmed static findings first.\n3. For each changed route, use `preview.start` and capture screenshots with\n `screenshot` at desktop, tablet, and mobile widths. Use `waitForSelector`\n or `waitForTimeout` only when the page has an actual delayed ready state.\n4. Use `get-page-by-path`, `list-instances`, `inspect-instance`, and focused\n prop/style reads to verify semantics and authored state that screenshots\n cannot prove.\n5. Make only supported, evidence-based fixes with semantic MCP tools. Recheck\n the affected route and the static accessibility search after every fix.\n\n## Review Areas\n\nReview these areas in priority order. Record the evidence for every finding.\n\n### 1. Names, Semantics, and Structure\n\n- Every image has purposeful alternative text or an explicitly empty alt when\n decorative. Image-submit inputs need a non-empty alt label describing their\n action.\n- For `missing-image-description` findings, inspect the rendered image in its\n page context and call `set-image-descriptions` with either a concise generated\n description or `decorative: true`. Re-run the audit after saving the decision.\n- Buttons, links, icon-only controls, and form controls have accessible names.\n- Prefer native HTML semantics over ARIA roles.\n- Non-native elements using `role="button"` or `role="link"` must be\n keyboard-focusable.\n- Use one sensible page h1, ordered heading levels, meaningful link text, and\n a main landmark.\n- Iframes have titles. Static HTML ids are unique, ARIA reference props point\n to real ids, and static boolean/select/numeric ARIA values are valid.\n- Focusable controls must not be hidden from assistive technology with\n `aria-hidden`.\n\n### 2. Keyboard and Focus\n\n- Interactive controls should be native or have a documented keyboard model.\n- Visible focus must not be removed without a visible replacement.\n- Hover-only information or controls need a keyboard-accessible equivalent.\n- Do not use positive tabindex values.\n- Mark keyboard behavior as **manual verification required** when the available\n evidence cannot prove tab order, Escape handling, or focus restoration.\n\n### 3. Dialogs, Menus, and Other Overlays\n\n- Dialogs need an accessible name and a close action.\n- Opening an overlay should move focus into it, trap focus where appropriate,\n and restore focus to its trigger on close.\n- Menus, popovers, disclosures, and tabs must expose their expanded/selected\n state and remain usable without a pointer.\n- If the current MCP/browser tools cannot exercise the interaction, report it\n as a manual verification item instead of guessing.\n\n### 4. Forms and Errors\n\n- Inputs have labels, required state, helpers, and errors associated with the\n right control.\n- Invalid controls expose `aria-invalid` when applicable.\n- Error, loading, and success feedback is not conveyed only by color or a\n transient visual toast.\n- Submit controls do not become unexplained dead ends when disabled.\n\n### 5. Visual, Responsive, Media, and Motion\n\n- At each target width, text remains readable, controls remain reachable, and\n no content is clipped, overlapped, or dependent on hover alone.\n- Do not rely only on color, position, shape, or iconography to convey meaning.\n- Keep non-text contrast, text contrast, focus indicators, and small text as\n visual review items unless measured evidence is available.\n- Videos with meaningful speech need captions; non-essential motion should\n respect reduced-motion preferences. Do not autoplay sound.\n\n### 6. Dynamic Content and Data\n\n- Loading, empty, error, and success states have understandable visible text.\n- Important dynamic updates and validation feedback need an appropriate\n announcement strategy.\n- Check repeated resource-driven content for unique labels, headings, ids, and\n meaningful empty states.\n\n## Findings and Fixes\n\nClassify every item as one of:\n\n- **Confirmed:** directly shown by `audit`, project data, or a rendered\n screenshot.\n- **Likely:** a credible issue inferred from rendered evidence; explain what\n would confirm it.\n- **Manual verification required:** needs keyboard, screen-reader, contrast, or\n interaction testing that the available tools cannot prove.\n\nFor each item include: severity (`critical`, `high`, `medium`, or `low`), page\npath, affected element or instance id when known, evidence, short user impact,\nand the smallest supported fix. Fix critical and high items first. Do not add\nARIA attributes, alt text, labels, or live regions when their meaning cannot be\nknown from the project; ask the user or report the missing context.\n\nFinish with a concise summary of fixed findings, remaining findings, and manual\nverification items. Use screenshots as evidence of the rendered state, not as\nproof of accessibility compliance.\n',
|
|
235301
235569
|
expressions: '# Webstudio Expressions\n\nUse an expression only when a value must be computed at runtime from scoped\ndata. Use direct text and direct prop values for fixed content.\n\n## Source Format\n\nExpression-capable MCP fields receive JavaScript expression source as a JSON\nstring. Send one expression, without a `return` statement or surrounding\nfunction. For example:\n\n```json\n{ "binding": { "type": "expression", "value": "post.title ?? \\"Untitled\\"" } }\n```\n\nInside that string, use readable JavaScript syntax rather than serialized JSON.\nFor object expressions, leave identifier property names unquoted, for example\n`{ query: queryText, variables: { slug: system.params.slug } }`. Quote a property\nname only when JavaScript requires it, such as `{ "published-at": date }`. Do\nnot pass a JSON-stringified object as an expression.\n\nDo not send a fixed prop string as an expression. Use `update-props` with\n`type:"string"`. Page metadata and resource URLs accept plain fixed strings and\nnormalize them for storage. Expression-only resource headers, search parameters,\nand bodies accept `{ "type": "literal", "value": "fixed text" }` when the\nvalue is not dynamic.\n\n## Scope\n\n- Data variables are available on their scope instance and descendants.\n- An inner variable with the same name masks the outer variable.\n- A scoped resource result is a variable. Read its payload from its result\n wrapper, usually `resourceName.data`; APIs may nest the desired value deeper.\n- Collection creates internal `collectionItem` and `collectionItemKey`\n parameters. They are available only to that Collection\'s descendants.\n Preserve those generated parameters and do not reuse encoded parameter ids\n copied from another Collection.\n- Array Collection iteration exposes the current item. Object iteration exposes\n the current key and value.\n- The built-in `system` context is available only where supplied by the runtime.\n Its documented fields are `system.origin`, `system.pathname`, `system.params`,\n and `system.search`. There is no `system.path`.\n- Actions expose only their declared arguments, such as `event`, plus data that\n is in scope.\n\nRead `list-variables`, `list-resources`, `inspect-instance`, and existing\nbindings before writing an expression. Do not guess identifier names. Syntax is\nvalidated when a mutation is submitted. A valid expression that references an\nidentifier unavailable in that scope is accepted with a structured warning\ncontaining the field path, source range, affected record, and remediation.\n\n## Supported Syntax\n\nExpressions support literals, arrays, objects, property and index access,\noptional chaining, unary and arithmetic operators, comparisons, logical\noperators, nullish coalescing, ternaries, and template literals.\n\nSupported string methods:\n\n{{allowedStringMethods}}\n\nSupported array methods:\n\n{{allowedArrayMethods}}\n\nOther values support `toString`. Arbitrary global functions and arbitrary\nmethod calls are not supported.\n\n## Unsupported Syntax\n\nDo not use statements, declarations, functions, arrow functions, classes,\n`new`, `this`, `await`, imports, tagged templates, sequence expressions,\nincrement/decrement, or destructuring assignment. Assignment is allowed only\ninside actions. Use an explicit assignment there rather than `++` or `--`.\n\n## Common Examples\n\n- Text: `post.title ?? "Untitled"`\n- Prop: `post.url`\n- Nested API array for Collection: `posts.data.items`\n- Resource URL: `"https://api.example.com/posts?tag=" + filters.tag`\n- Header: `"Bearer " + auth.token`\n- Search parameter: `String(filters.page ?? 1)` is not supported because global\n function calls are forbidden; use `(filters.page ?? 1).toString()` instead.\n- Resource body: `{ query: queryText, variables: { slug: system.params.slug } }`\n- Conditional: `featured ? "Featured" : "Standard"`\n- Safe nested access: `post.author?.name ?? "Unknown author"`\n\n## Collections\n\nWhenever an array or object should render repeated UI, call `insert-collection`\nwith the complete iterable and one repeated-item JSX root. Do not pass the\nresponse wrapper or one indexed item. The command creates the Collection and\nits private item parameters atomically, then renders the item root once per\nentry. Bind descendants with expressions such as `collectionItem.name`; for\nobject iteration, `collectionItemKey` contains the current key. Wrap multiple\nrepeated siblings in one `ws.element` root.\n\n## Verification\n\nInspect every returned expression warning. Correct warnings that indicate a\nmisspelled or unavailable variable, then run `verify-bindings` for persisted\nsyntax, scope, and reference integrity. A warning does not roll back the\nmutation, and successful static verification does not prove runtime data has\nthe expected shape. `verify-bindings` never executes external resources or\nresolves rendered values. Preview representative data, empty/null data, and\nCollection item counts; use `audit` for relevant structural findings.\n',
|
|
235302
|
-
"mcp-startup-guidance": 'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output. Before editing a Webstudio project, do one small discovery step, then act. For delegated design-system or “use every component” tasks, the first MCP command is `workflow.next {"goal":"design-system-page"}`; report that returned checkpoint to the parent/user and stop until continued. For a simple authored/styled section, the intended path is literal: call `meta.index`, call `meta.
|
|
235570
|
+
"mcp-startup-guidance": 'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output. Before editing a Webstudio project, do one small discovery step, then act. For delegated design-system or “use every component” tasks, the first MCP command is `workflow.next {"goal":"design-system-page"}`; report that returned checkpoint to the parent/user and stop until continued. For a simple authored/styled section, the intended path is literal: call `meta.index`, call `meta.get-more-tools` with `{"tools":["insert-fragment"]}`, read the target parent id with `list-pages`/`get-page-by-path`/`list-instances` only if needed, then call `insert-fragment`. Prefer `insert-fragment` for authored/styled sections; use `insert-component` only when inserting one automatic component template. Do not grep source files, dump full MCP resources, or write parser scripts before trying focused MCP calls. Good first calls are `meta.index` for orientation, `meta.guide` for a goal workflow, `workflow.next` for one bounded delegated phase, `meta.get-more-tools` for exact tool schemas, and `components.coverage-plan`, `components.coverage-insert-next`, `components.find`, or `components.get` for component work. Do not call every discovery tool up front. For delegated or non-streaming agents whose parent cannot see live stderr/stdout, treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, report its command/result, and wait for the parent before the next MCP command. Do not run long shell loops of shortcut or `webstudio mcp single-op-call` commands; they hide progress from the parent. Do not take a broad task such as creating a full design-system page as one execution unit. Instead call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint to the parent, wait until the parent continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For design-system or “use every component” tasks, start with `workflow.next`, acknowledge its checkpoint only after parent/user continuation, then run compact `components.coverage-plan`, checkpoint, create the page, checkpoint, then call `components.coverage-insert-next` once per coverage checkpoint, and finish with the `presentation-pass` workflow phase. Coverage 72/72 is necessary but not sufficient: the page must be organized into styled, real-world design-system examples instead of raw unstyled component dumps. Request component coverage details with `components.coverage-plan {"detail":"roots"}` or `components.coverage-plan {"detail":"parts"}` only when needed. `list-pages` does not accept `detail`; use `list-pages {}` or `get-page-by-path` for page lookup. Read overview resources such as `webstudio://project/guide`, `webstudio://project/tools-overview`, and `webstudio://project/components-overview` only when focused tools are insufficient. Do not dump or parse full resources such as `webstudio://project/tools` or `webstudio://project/components` unless the focused tools are insufficient. Run one-shot shortcut or explicit `webstudio mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. For experiments, pass `--dry-run` to local-capable mutations. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project. If a one-shot call returns checkpoint.required, the CLI persists `CHECKPOINT_REQUIRED` across later one-shot calls until you report the checkpoint, wait until continued, and call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially. Complete one bounded phase and return a checkpoint before continuing. Follow the guide: read ids and Builder breakpoints first, use focused component discovery for component ids, props, and content model composition, prefer semantic tools over `apply-patch`, use direct value tools for fixed text/props, use bindings only for dynamic expressions/resources/actions, page metadata and fixed resource URLs accept plain strings, put page/resource update fields under `values`, do not edit generated files for normal content/design changes, and use `preview.start` plus screenshot/vision for visual work.\n'
|
|
235303
235571
|
};
|
|
235304
235572
|
const readProjectBuildDoc = (name2) => projectBuildDocs[name2];
|
|
235305
235573
|
const componentBindings = {
|
|
@@ -235590,7 +235858,7 @@ const isProjectSessionPreviewMode = (value2) => projectSessionPreviewModes.some(
|
|
|
235590
235858
|
const getRequestParams = (request) => isRecord(request) && isRecord(request.params) ? request.params : {};
|
|
235591
235859
|
const emptyInputSchema = {
|
|
235592
235860
|
type: "object",
|
|
235593
|
-
description: "Pass this MCP tool's JSON arguments. Use meta.
|
|
235861
|
+
description: "Pass this MCP tool's JSON arguments. Use meta.get-more-tools for examples and required fields. For authored content with styles, prefer insert-fragment so the CLI converts JSX into Webstudio data.",
|
|
235594
235862
|
additionalProperties: false
|
|
235595
235863
|
};
|
|
235596
235864
|
const textInputSchema = (description2) => ({
|
|
@@ -235903,7 +236171,7 @@ const getCompactSchemaProperty = (schema2) => {
|
|
|
235903
236171
|
return compact;
|
|
235904
236172
|
}
|
|
235905
236173
|
return {
|
|
235906
|
-
description: "Complex structured value. Use meta.
|
|
236174
|
+
description: "Complex structured value. Use meta.get-more-tools with this exact tool name for its complete schema."
|
|
235907
236175
|
};
|
|
235908
236176
|
};
|
|
235909
236177
|
const getHandshakeInputSchema = (schema2, maxInlineSize = maxInlineMcpInputSchemaSize) => {
|
|
@@ -235925,7 +236193,7 @@ const getHandshakeInputSchema = (schema2, maxInlineSize = maxInlineMcpInputSchem
|
|
|
235925
236193
|
})
|
|
235926
236194
|
),
|
|
235927
236195
|
required: schema2.required,
|
|
235928
|
-
description: "Compact handshake schema. Use meta.
|
|
236196
|
+
description: "Compact handshake schema. Use meta.get-more-tools with this exact tool name for the complete input schema."
|
|
235929
236197
|
},
|
|
235930
236198
|
detailedInputSchema: schema2
|
|
235931
236199
|
};
|
|
@@ -235947,7 +236215,7 @@ const getZodMcpInputSchema = (schema2) => getHandshakeInputSchema(getZodObjectSc
|
|
|
235947
236215
|
const insertCollectionMcpInputSchema = getOperationInputSchema({
|
|
235948
236216
|
inputSchema: getInputSchemaMetadata(insertCollectionMcpInput).inputJsonSchema
|
|
235949
236217
|
});
|
|
235950
|
-
const assetsResourceResultDescription = "Pass query as structured tool input using Webstudio JavaScript expressions rather than a JSON-stringified expression or manually authored resource body. Every reachable Assets resource contributes to one shared published database, so create only one final resource per rendered query; update an existing resource instead of creating a replacement, and remove obsolete duplicates. Keep static filters, limits, and offsets literal so bounded overview queries can be materialized. Use output mode fields, select only rendered fields, keep includeMetadata false, and use content mode none when file content is not rendered. For a Markdown detail page, query the Markdown asset directly and use content mode markdown-body; compilation keeps only its document reference in the bundle and fetches the selected body from Asset storage at runtime. Bind the resolved body from item.content.text. Assets expose an ID-keyed map at <dataSourceName>.data and collection information at <dataSourceName>.meta.";
|
|
236218
|
+
const assetsResourceResultDescription = "Pass query as structured tool input using Webstudio JavaScript expressions rather than a JSON-stringified expression or manually authored resource body. Every reachable Assets resource contributes to one shared published database, so create only one final resource per rendered query; update an existing resource instead of creating a replacement, and remove obsolete duplicates. Keep static filters, limits, and offsets literal so bounded overview queries can be materialized. Use output mode fields, select only rendered fields, keep includeMetadata false, and use content mode none when file content is not rendered. For a Markdown detail page, query the Markdown asset directly and use content mode markdown-body-ref; compilation keeps only its document reference in the bundle and fetches the selected body from Asset storage at runtime. Bind the resolved body from item.content.text. Assets expose an ID-keyed map at <dataSourceName>.data and collection information at <dataSourceName>.meta.";
|
|
235951
236219
|
const mcpOperationOverrides = /* @__PURE__ */ new Map([
|
|
235952
236220
|
[
|
|
235953
236221
|
"insert-fragment",
|
|
@@ -236085,6 +236353,9 @@ const getUnsupportedInputFieldHint = ({
|
|
|
236085
236353
|
if (field === "detail" && (command === "get-page" || command === "get-page-by-path")) {
|
|
236086
236354
|
return " Use get-page/get-page-by-path for page metadata, list-instances to inspect page root contents, and inspect-instance for props, styles, children, bindings, or sources.";
|
|
236087
236355
|
}
|
|
236356
|
+
if (command === "list-instances" && field === "instanceId") {
|
|
236357
|
+
return " Use rootInstanceId to list a subtree, or inspect-instance to inspect one element.";
|
|
236358
|
+
}
|
|
236088
236359
|
return "";
|
|
236089
236360
|
};
|
|
236090
236361
|
const assertKnownInputFields = ({
|
|
@@ -236625,7 +236896,7 @@ const mcpArgumentExamples = {
|
|
|
236625
236896
|
{ goal: "design-system-page" },
|
|
236626
236897
|
{ goal: "design-system-page", phase: "dry-run-section" }
|
|
236627
236898
|
],
|
|
236628
|
-
"meta.
|
|
236899
|
+
"meta.get-more-tools": [
|
|
236629
236900
|
{ tools: ["insert-fragment"] },
|
|
236630
236901
|
{ tools: ["insert-component"] },
|
|
236631
236902
|
{ brief: "update-styles" }
|
|
@@ -236770,7 +237041,7 @@ const mcpArgumentExamples = {
|
|
|
236770
237041
|
{
|
|
236771
237042
|
parentInstanceId: "parent-id",
|
|
236772
237043
|
data: { type: "expression", value: "Posts.data.items" },
|
|
236773
|
-
itemFragment:
|
|
237044
|
+
itemFragment: "<ws.element ws:tag='article'><ws.element ws:tag='h2'>{expression`collectionItem.title ?? 'Untitled'`}</ws.element></ws.element>"
|
|
236774
237045
|
},
|
|
236775
237046
|
{
|
|
236776
237047
|
parentInstanceId: "parent-id",
|
|
@@ -236778,32 +237049,32 @@ const mcpArgumentExamples = {
|
|
|
236778
237049
|
type: "json",
|
|
236779
237050
|
value: [{ name: "Starter" }, { name: "Pro" }]
|
|
236780
237051
|
},
|
|
236781
|
-
itemFragment:
|
|
237052
|
+
itemFragment: "<ws.element ws:tag='div'>{expression`collectionItem.name`}</ws.element>"
|
|
236782
237053
|
}
|
|
236783
237054
|
],
|
|
236784
237055
|
"insert-fragment": [
|
|
236785
237056
|
{
|
|
236786
237057
|
parentInstanceId: "parent-id",
|
|
236787
|
-
fragment:
|
|
237058
|
+
fragment: "<ws.element ws:tag='section' ws:style={css`padding: 32px; display: grid; gap: 16px;`}><ws.element ws:tag='h2'>Northstar Product OS</ws.element><ws.element ws:tag='p'>Reusable patterns for teams.</ws.element></ws.element>"
|
|
236788
237059
|
},
|
|
236789
237060
|
{
|
|
236790
237061
|
parentInstanceId: "parent-id",
|
|
236791
|
-
fragment:
|
|
237062
|
+
fragment: "<ws.element ws:tag='section' style={{ padding: 32, borderRadius: 16 }}><ws.element ws:tag='h2'>Operations Console</ws.element><ws.element ws:tag='p'>Semantic section with React-style object styles converted into editable Webstudio styles.</ws.element></ws.element>"
|
|
236792
237063
|
},
|
|
236793
237064
|
{
|
|
236794
237065
|
parentInstanceId: "parent-id",
|
|
236795
|
-
fragment:
|
|
237066
|
+
fragment: "<ws.element ws:tag='section' ws:tokens={[token('accent', css`color: #0f766e;`)]} ws:style={css`display: grid; gap: 12px;`}><ws.element ws:tag='h2'>Token Example</ws.element><ws.element ws:tag='button' onClick={new ActionValue(['event'], expression`console.log(event)`)}>Track launch</ws.element></ws.element>"
|
|
236796
237067
|
},
|
|
236797
237068
|
{
|
|
236798
237069
|
parentInstanceId: "parent-id",
|
|
236799
|
-
fragment:
|
|
237070
|
+
fragment: "<ws.element ws:tag='section'><radix.Switch><radix.SwitchThumb /></radix.Switch></ws.element>"
|
|
236800
237071
|
}
|
|
236801
237072
|
],
|
|
236802
237073
|
"insert-fragment-verified": [
|
|
236803
237074
|
{
|
|
236804
237075
|
parentInstanceId: "parent-id",
|
|
236805
237076
|
pagePath: "/pricing",
|
|
236806
|
-
fragment:
|
|
237077
|
+
fragment: "<ws.element ws:tag='section'><ws.element ws:tag='h2'>Pricing</ws.element></ws.element>"
|
|
236807
237078
|
}
|
|
236808
237079
|
],
|
|
236809
237080
|
"update-text": [
|
|
@@ -237098,7 +237369,7 @@ const mcpArgumentExamples = {
|
|
|
237098
237369
|
]
|
|
237099
237370
|
},
|
|
237100
237371
|
limit: 1,
|
|
237101
|
-
content: { mode: "markdown-body", maxBytes: 1048576 }
|
|
237372
|
+
content: { mode: "markdown-body-ref", maxBytes: 1048576 }
|
|
237102
237373
|
}
|
|
237103
237374
|
}
|
|
237104
237375
|
],
|
|
@@ -237475,12 +237746,12 @@ const sessionTools = [
|
|
|
237475
237746
|
}
|
|
237476
237747
|
}),
|
|
237477
237748
|
createProjectSessionMcpTool({
|
|
237478
|
-
name: "meta.
|
|
237749
|
+
name: "meta.get-more-tools",
|
|
237479
237750
|
description: 'Return detailed tool metadata and examples. Prefer exact tool names, for example {"tools":["insert-fragment"]}. To search, pass a string brief such as {"brief":"style updates"}.',
|
|
237480
237751
|
inputSchema: toolDetailsInputSchema,
|
|
237481
237752
|
annotations: {
|
|
237482
|
-
command: "meta.
|
|
237483
|
-
operationId: "meta.
|
|
237753
|
+
command: "meta.get-more-tools",
|
|
237754
|
+
operationId: "meta.get-more-tools",
|
|
237484
237755
|
method: "session",
|
|
237485
237756
|
permit: "api",
|
|
237486
237757
|
localCapable: true,
|
|
@@ -238239,7 +238510,7 @@ const capabilityAreas = [
|
|
|
238239
238510
|
tools: [
|
|
238240
238511
|
"meta.index",
|
|
238241
238512
|
"meta.guide",
|
|
238242
|
-
"meta.
|
|
238513
|
+
"meta.get-more-tools",
|
|
238243
238514
|
"workflow.next",
|
|
238244
238515
|
"inspect-auth-context",
|
|
238245
238516
|
"inspect-design-context",
|
|
@@ -238305,6 +238576,7 @@ const capabilityAreas = [
|
|
|
238305
238576
|
"delete-instance",
|
|
238306
238577
|
"list-texts",
|
|
238307
238578
|
"update-text",
|
|
238579
|
+
"set-text-content",
|
|
238308
238580
|
"replace-text",
|
|
238309
238581
|
"replace-prop-text",
|
|
238310
238582
|
"update-props",
|
|
@@ -238424,13 +238696,13 @@ const getToolNamesInput = (input2) => {
|
|
|
238424
238696
|
const value2 = input2.tools;
|
|
238425
238697
|
if (Array.isArray(value2) === false) {
|
|
238426
238698
|
throw new Error(
|
|
238427
|
-
`meta.
|
|
238699
|
+
`meta.get-more-tools input.tools must be an array of strings when provided. Received ${typeof value2}.`
|
|
238428
238700
|
);
|
|
238429
238701
|
}
|
|
238430
238702
|
return value2.map((tool, index2) => {
|
|
238431
238703
|
if (typeof tool !== "string" || tool === "") {
|
|
238432
238704
|
throw new Error(
|
|
238433
|
-
`meta.
|
|
238705
|
+
`meta.get-more-tools input.tools[${index2}] must be a non-empty string.`
|
|
238434
238706
|
);
|
|
238435
238707
|
}
|
|
238436
238708
|
return tool;
|
|
@@ -238481,7 +238753,7 @@ const getTemplateInput = (input2) => {
|
|
|
238481
238753
|
const getInsertFragmentInput = async (input2) => {
|
|
238482
238754
|
if (isPlainRecord$1(input2) === false) {
|
|
238483
238755
|
throw new Error(
|
|
238484
|
-
|
|
238756
|
+
`insert-fragment requires {"parentInstanceId":"...","fragment":"<ws.element ws:tag='section' />"}.`
|
|
238485
238757
|
);
|
|
238486
238758
|
}
|
|
238487
238759
|
if ("parentId" in input2 && "parentInstanceId" in input2 === false) {
|
|
@@ -238504,7 +238776,7 @@ const getInsertFragmentInput = async (input2) => {
|
|
|
238504
238776
|
}
|
|
238505
238777
|
if (typeof input2.fragment !== "string") {
|
|
238506
238778
|
throw new Error(
|
|
238507
|
-
|
|
238779
|
+
`insert-fragment requires fragment as a Webstudio JSX string, for example {"fragment":"<ws.element ws:tag='section' />"}.`
|
|
238508
238780
|
);
|
|
238509
238781
|
}
|
|
238510
238782
|
const fragment = await parseWebstudioJsxFragment(input2.fragment);
|
|
@@ -239710,8 +239982,13 @@ const getComponentDetails = (component) => {
|
|
|
239710
239982
|
].filter(Boolean).join(" ")
|
|
239711
239983
|
};
|
|
239712
239984
|
};
|
|
239985
|
+
const insertFragmentInputFilePath = ".temp/insert-fragment.json";
|
|
239986
|
+
const insertFragmentInputFileExample = {
|
|
239987
|
+
parentInstanceId: "parent-id",
|
|
239988
|
+
fragment: "<ws.element ws:tag='section' ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag='h2'>Section title</ws.element><ws.element ws:tag='p'>Section copy.</ws.element></ws.element>"
|
|
239989
|
+
};
|
|
239713
239990
|
const getToolCatalogOverview = (tools) => ({
|
|
239714
|
-
usage: 'Short tool overview. Do one small discovery step, then act. Start with meta.index or meta.guide({"brief":"Create a pricing page"}). Use meta.
|
|
239991
|
+
usage: 'Short tool overview. Do one small discovery step, then act. Start with meta.index or meta.guide({"brief":"Create a pricing page"}). Use meta.get-more-tools({"tools":["insert-fragment"]}) for exact details, or read the bounded webstudio://project/tools catalog.',
|
|
239715
239992
|
count: tools.length,
|
|
239716
239993
|
capabilities: filterCapabilities(tools).map((capability) => ({
|
|
239717
239994
|
area: capability.area,
|
|
@@ -239737,15 +240014,15 @@ const getMetaIndex = (tools, guidance) => {
|
|
|
239737
240014
|
].filter((tool) => names.has(tool)),
|
|
239738
240015
|
discovery: {
|
|
239739
240016
|
overview: "Do not call every discovery tool up front. Use this meta.index response for orientation, then call at most one focused discovery tool before acting.",
|
|
239740
|
-
tools: 'Use meta.
|
|
239741
|
-
insertFragment:
|
|
240017
|
+
tools: 'Use meta.get-more-tools({"tools":["insert-fragment"]}) for the primary authored/styled insertion tool. Use {"brief":"style updates"} only for search. Page through webstudio://project/tools only when broader operation discovery is necessary.',
|
|
240018
|
+
insertFragment: `Primary authored/styled insertion command shape: save ${JSON.stringify(insertFragmentInputFileExample)} as ${insertFragmentInputFilePath}, then run node packages/cli/local.js insert-fragment --input-file ${insertFragmentInputFilePath} --dry-run. Use parentInstanceId, not parentId. Use Webstudio components/helpers such as ws.element, radix.*, css, token, expression, and ActionValue. Use ws:style={css\`...\`} for Webstudio-native CSS, or style={{ padding: 24 }} for React-style object syntax converted into editable Webstudio styles. Use node packages/cli/local.js mcp single-op-call insert-fragment only when you need the explicit MCP form.`,
|
|
239742
240019
|
resources: "Use MCP resources/list to discover overview and full resources.",
|
|
239743
240020
|
components: 'Use components.list({"source":"all"}) for shadcn-compatible registry items, templates.list({}) for templates, components.summary for a compact catalog, components.coverage-plan for design-system/all-component tasks, components.coverage-insert-next({"pagePath":"/design-system","parentInstanceId":"root-id"}) for one checkpoint-safe coverage insertion, components.coverage-status({"pagePath":"/design-system"}) to verify progress, components.search({"brief":"radix select"}) to search, and components.get({"component":"@webstudio-is/sdk-components-react-radix:Select"}) or templates.get({"component":"@webstudio-is/sdk-components-react-radix:Select"}) for one item. Do not dump or parse webstudio://project/components unless those focused tools are insufficient.',
|
|
239744
240021
|
guide: 'Use meta.guide({"brief":"Create a design system page using every component"}) for a goal-specific workflow.',
|
|
239745
240022
|
expressions: "Read webstudio://project/expressions before authoring unfamiliar expressions, Collection item bindings, or dynamic resource fields.",
|
|
239746
240023
|
accessibility: "For an accessibility review, read webstudio://project/accessibility-review, run audit with scopes [accessibility], then verify changed routes with preview and screenshots.",
|
|
239747
240024
|
workflow: 'Use workflow.next({"goal":"design-system-page"}) for one bounded phase when delegated/non-streaming agents must return progress instead of silently running a broad task.',
|
|
239748
|
-
details: 'Use meta.
|
|
240025
|
+
details: 'Use meta.get-more-tools({"tools":["insert-fragment"]}) for matching params and examples.'
|
|
239749
240026
|
},
|
|
239750
240027
|
delegatedAgentRule: "If your parent cannot see live command output, treat each checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as webstudio meta.index or one explicit webstudio mcp single-op-call command, report its command/result, and wait before the next MCP command.",
|
|
239751
240028
|
rules: [
|
|
@@ -239780,14 +240057,14 @@ const metaGoalGuides = [
|
|
|
239780
240057
|
"verify-page-responsive"
|
|
239781
240058
|
],
|
|
239782
240059
|
workflow: [
|
|
239783
|
-
'Call meta.
|
|
240060
|
+
'Call meta.get-more-tools with {"tools":["create-assets-resource"]} once for the complete nested query contract. Use exact tool names, not brief search, and do not repeat discovery for this workflow.',
|
|
239784
240061
|
`Create one Blog asset folder. Upload all Markdown source files together in one upload-assets call with assetsDir ".webstudio/assets". Put queryable metadata such as slug, title, author, publishedAt, excerpt, and draft in each file's frontmatter. Every file must use {"name":"<filename>.md","type":"file","format":"md","folderId":"<blog-folder-id>","meta":{}}. Do not create companion JSON descriptors, use a combined format value, or retry a failed mutation; report its actionable error instead.`,
|
|
239785
240062
|
'Ensure the blog has exactly two Builder pages: an overview at the fixed path "/blog" and one detail page at the dynamic path "/blog/:slug". Create each page once with a committed call; do not dry-run it. Both pages load their content from Assets resources. Do not create one page per post or copy Markdown content into page-specific static structures.',
|
|
239786
240063
|
"Every reachable Assets data source contributes its query to one shared published database. Keep exactly one final Assets resource for the overview and one for the detail page. Never create a placeholder, preview copy, or repair replacement; update the existing scoped resource when requirements change and remove obsolete duplicates.",
|
|
239787
240064
|
'Field paths are arrays of segments, for example field:["extension"]. Literal query values use {"type":"literal","value":"..."}; raw strings are runtime expressions. Keep every overview filter value, limit, and offset literal so the bounded metadata-only result can be materialized instead of retaining its fields across every article. Use a deterministic secondary ID sort. Query Markdown posts with static extension and blog-folder constraints before any dynamic condition. Use output.mode:"fields", includeMetadata:false, and only fields rendered by that route.',
|
|
239788
|
-
'Keep content.mode:"none" on the overview and use content.mode:"markdown-body" only on the detail route. The detail query should have exactly one dynamic value, system.params.slug, a literal limit of 1, and only the title and author metadata rendered above Markdown Embed. The published database keeps only the Markdown document reference and fetches the selected body from Asset storage at runtime.',
|
|
240065
|
+
'Keep content.mode:"none" on the overview and use content.mode:"markdown-body-ref" only on the detail route. The detail query should have exactly one dynamic value, system.params.slug, a literal limit of 1, and only the title and author metadata rendered above Markdown Embed. The published database keeps only the Markdown document reference and fetches the selected body from Asset storage at runtime.',
|
|
239789
240066
|
"Call create-assets-resource exactly once with recipe.overviewResource after substituting the returned /blog root id and Blog folder id. Then call it exactly once with recipe.detailResource after substituting the returned /blog/:slug root id and the same Blog folder id.",
|
|
239790
|
-
'Call insert-collection exactly once with the entire recipe.overviewCollection object and exactly once with the entire recipe.detailCollection object, changing only parentInstanceId to the returned root id. Do not reshape or stringify any field: data must remain the recipe object {"type":"expression","value":"posts.data"} or {"type":"expression","value":"post.data"}. Do not improvise another fragment or call meta.
|
|
240067
|
+
'Call insert-collection exactly once with the entire recipe.overviewCollection object and exactly once with the entire recipe.detailCollection object, changing only parentInstanceId to the returned root id. Do not reshape or stringify any field: data must remain the recipe object {"type":"expression","value":"posts.data"} or {"type":"expression","value":"post.data"}. Do not improvise another fragment or call meta.get-more-tools again.',
|
|
239791
240068
|
"Validate both queries and preview the detail query with one concrete slug before saving dynamic expressions. Query-preview diagnostics report this query separately from the merged published database; use the merged database measurement when checking the deployment limit. The merged database must contain every source document without truncation, no embedded Markdown contents, and only one materialized overview query.",
|
|
239792
240069
|
"Verify only after both Collections succeed and confirm that both pages load their content from Assets. Call verify-page-responsive once for /blog and once for one concrete detail route, including empty/not-found behavior, before finishing. If any call fails, stop and report it without retrying."
|
|
239793
240070
|
],
|
|
@@ -239870,7 +240147,7 @@ const metaGoalGuides = [
|
|
|
239870
240147
|
["properties", "author"]
|
|
239871
240148
|
]
|
|
239872
240149
|
},
|
|
239873
|
-
content: { mode: "markdown-body" }
|
|
240150
|
+
content: { mode: "markdown-body-ref" }
|
|
239874
240151
|
}
|
|
239875
240152
|
},
|
|
239876
240153
|
overviewCollection: {
|
|
@@ -239950,7 +240227,7 @@ const metaGoalGuides = [
|
|
|
239950
240227
|
],
|
|
239951
240228
|
workflow: [
|
|
239952
240229
|
"Inspect the project's existing auth resources, variables, page settings, and agent instructions before choosing a provider workflow. Call inspect-auth-context exactly once instead of calling get-project-settings, list-pages, list-resources, or list-variables separately; use at most one focused search-project call only when that bundle does not identify the auth convention. Treat its pages section as authoritative for route existence. Do not call get-page-by-path to confirm that /account is absent. Reuse that convention; do not add a second auth system implicitly.",
|
|
239953
|
-
"Do not call meta.index after this guide. If an exact mutation schema is still needed, make at most one meta.
|
|
240230
|
+
"Do not call meta.index after this guide. If an exact mutation schema is still needed, make at most one meta.get-more-tools call listing all immediately required authoring tools rather than rediscovering them one at a time.",
|
|
239954
240231
|
"Never place credentials, service-role keys, refresh tokens, private session values, or authenticated response bodies in project data, command output, screenshots, agent instructions, or error reports. Ask the user to configure secrets in the provider/server environment.",
|
|
239955
240232
|
"Keep all four auth states in the editable component structure even when bindings select only one at runtime. Give each state a visible label using the exact terms signed-out, loading, signed-in, and failed-auth so authors can inspect and verify every state. Use page basic auth only when the user asks for Webstudio's fixed login/password gate; it is not Supabase or Firebase authentication.",
|
|
239956
240233
|
"Use focused resources and variables for public client configuration and session-shaped data. Keep authorization enforcement and privileged provider calls server-side; a hidden Builder element is not an authorization boundary.",
|
|
@@ -239975,7 +240252,7 @@ const metaGoalGuides = [
|
|
|
239975
240252
|
"audit"
|
|
239976
240253
|
],
|
|
239977
240254
|
workflow: [
|
|
239978
|
-
"The guide and MCP handshake already provide the required tool schemas. Do not call meta.index or meta.
|
|
240255
|
+
"The guide and MCP handshake already provide the required tool schemas. Do not call meta.index or meta.get-more-tools for this workflow.",
|
|
239979
240256
|
"Use upload-asset or upload-assets with the local filename, detected format, and complete family, style, and weight metadata. Use the returned asset ids directly; do not read a project snapshot to rediscover uploaded assets.",
|
|
239980
240257
|
"Use update-asset to correct font metadata without re-uploading the binary.",
|
|
239981
240258
|
"After font mutations, call verify-font-assets exactly once with every changed asset id. It refreshes the asset namespace and returns the persisted metadata in one bounded verification call; do not call refresh or get-asset separately."
|
|
@@ -239996,7 +240273,7 @@ const metaGoalGuides = [
|
|
|
239996
240273
|
"screenshot.diff"
|
|
239997
240274
|
],
|
|
239998
240275
|
workflow: [
|
|
239999
|
-
"The guide and MCP handshake already provide the required tool schemas. Do not call meta.index or meta.
|
|
240276
|
+
"The guide and MCP handshake already provide the required tool schemas. Do not call meta.index or meta.get-more-tools for this workflow.",
|
|
240000
240277
|
"Interpret the supplied design before mutating: identify page sections, responsive behavior, reusable patterns, assets, typography, color, spacing, and interaction states. Ask for missing source assets rather than inventing brand-critical content.",
|
|
240001
240278
|
"Before the first mutation, call inspect-design-context exactly once instead of calling list-pages, list-breakpoints, list-design-tokens, list-assets, or list-variables separately. Use one list-instances call only when needed to inspect a representative existing page pattern. Do not call get-styles: the bounded design context and optional focused instance result provide the reusable design-system evidence needed here without risking an oversized style dump. Reuse exact existing values, breakpoint ids, and patterns; do not create a parallel design system from approximate screenshot colors or spacing.",
|
|
240002
240279
|
"Call create-page exactly once and use its returned rootInstanceId as the insertion parent. Insert the complete semantic page in one fragment when practical, and use the insertion result's instanceIds for follow-up token attachments. Do not call list-instances after the first mutation to rediscover ids already returned by mutations.",
|
|
@@ -240044,7 +240321,7 @@ const serializeMetaGuideTool = (tool, includeHandshakeFields) => includeHandshak
|
|
|
240044
240321
|
};
|
|
240045
240322
|
const getMetaGuide = (brief, tools, guidance) => {
|
|
240046
240323
|
const goalGuide = metaGoalGuides.find(({ pattern }) => pattern.test(brief));
|
|
240047
|
-
const matches = goalGuide === void 0 ? getMatchingTools(brief, tools).slice(0, 12) :
|
|
240324
|
+
const matches = goalGuide === void 0 ? getMatchingTools(brief, tools).slice(0, 12) : getExactToolSelection(goalGuide.tools, tools).tools;
|
|
240048
240325
|
const canVerifyVisually = tools.some((tool) => tool.name === "preview.start") && tools.some((tool) => tool.name === "screenshot");
|
|
240049
240326
|
const canDiffScreenshots = tools.some(
|
|
240050
240327
|
(tool) => tool.name === "screenshot.diff"
|
|
@@ -240066,7 +240343,7 @@ const getMetaGuide = (brief, tools, guidance) => {
|
|
|
240066
240343
|
(tool) => serializeMetaGuideTool(tool, goalGuide === void 0)
|
|
240067
240344
|
),
|
|
240068
240345
|
...goalGuide !== void 0 && "recipe" in goalGuide ? { recipe: goalGuide.recipe } : {},
|
|
240069
|
-
more: goalGuide === void 0 ? "The MCP handshake provides top-level argument contracts and required fields, while this guide includes exact examples plus complete schemas for selected complex tools. Call meta.
|
|
240346
|
+
more: goalGuide === void 0 ? "The MCP handshake provides top-level argument contracts and required fields, while this guide includes exact examples plus complete schemas for selected complex tools. Call meta.get-more-tools once with all needed tool names only when a nested input shape is not covered here or when you need server/local behavior that the guide does not cover." : "The MCP client loads each named tool's exact argument contract before calling it. This guide includes a complete schema only for selected complex inputs. Call meta.get-more-tools once with all needed tool names only when the client does not expose a nested input shape or when you need server/local behavior that the guide does not cover."
|
|
240070
240347
|
};
|
|
240071
240348
|
};
|
|
240072
240349
|
const getWorkflowInput = (input2) => {
|
|
@@ -240113,10 +240390,17 @@ const designSystemWorkflowPhases = {
|
|
|
240113
240390
|
},
|
|
240114
240391
|
"dry-run-section": {
|
|
240115
240392
|
purpose: "Validate one tiny authored/styled JSX smoke fragment without committing.",
|
|
240116
|
-
allowedTools: ["meta.
|
|
240117
|
-
commandPattern:
|
|
240393
|
+
allowedTools: ["meta.get-more-tools", "components.get", "insert-fragment"],
|
|
240394
|
+
commandPattern: "node packages/cli/local.js insert-fragment --input-file .temp/design-system-section.json --dry-run",
|
|
240395
|
+
inputFile: {
|
|
240396
|
+
path: ".temp/design-system-section.json",
|
|
240397
|
+
contents: {
|
|
240398
|
+
parentInstanceId: "root-id",
|
|
240399
|
+
fragment: "<ws.element ws:tag='section' ws:style={css`padding: 24px;`}><ws.element ws:tag='h2'>Design System</ws.element></ws.element>"
|
|
240400
|
+
}
|
|
240401
|
+
},
|
|
240118
240402
|
constraints: [
|
|
240119
|
-
"
|
|
240403
|
+
"Save inputFile.contents at inputFile.path, replacing only root-id, then use the commandPattern as-is.",
|
|
240120
240404
|
"Keep the dry-run fragment tiny, ideally under 500 characters.",
|
|
240121
240405
|
"Do not design the real page in this phase.",
|
|
240122
240406
|
"Use ws.element tags or components confirmed by components.get; do not use deprecated $.Box, $.Heading, $.Paragraph, or $.Button.",
|
|
@@ -240132,7 +240416,7 @@ const designSystemWorkflowPhases = {
|
|
|
240132
240416
|
"commit-section": {
|
|
240133
240417
|
purpose: "Commit exactly one previously validated authored/styled JSX section or one template root.",
|
|
240134
240418
|
allowedTools: ["insert-fragment", "insert-component"],
|
|
240135
|
-
commandPattern:
|
|
240419
|
+
commandPattern: "node packages/cli/local.js insert-fragment --input-file .temp/design-system-section.json",
|
|
240136
240420
|
expectedReturn: ["committed version", "inserted root instance id"],
|
|
240137
240421
|
nextPhase: "coverage-batch"
|
|
240138
240422
|
},
|
|
@@ -240196,12 +240480,25 @@ const getWorkflowNext = (input2) => {
|
|
|
240196
240480
|
allPhases: workflowPhaseNames
|
|
240197
240481
|
};
|
|
240198
240482
|
};
|
|
240199
|
-
const
|
|
240483
|
+
const getExactToolSelection = (toolNames, tools) => {
|
|
240200
240484
|
const toolByName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
240201
|
-
|
|
240202
|
-
|
|
240203
|
-
|
|
240204
|
-
|
|
240485
|
+
const selectedTools = [];
|
|
240486
|
+
const missingTools = [];
|
|
240487
|
+
const includedToolNames = /* @__PURE__ */ new Set();
|
|
240488
|
+
for (const requestedName of toolNames) {
|
|
240489
|
+
const resolvedName = resolveToolName(requestedName);
|
|
240490
|
+
const tool = toolByName.get(resolvedName);
|
|
240491
|
+
if (tool === void 0) {
|
|
240492
|
+
missingTools.push(requestedName);
|
|
240493
|
+
continue;
|
|
240494
|
+
}
|
|
240495
|
+
if (includedToolNames.has(resolvedName)) {
|
|
240496
|
+
continue;
|
|
240497
|
+
}
|
|
240498
|
+
includedToolNames.add(resolvedName);
|
|
240499
|
+
selectedTools.push(tool);
|
|
240500
|
+
}
|
|
240501
|
+
return { tools: selectedTools, missingTools };
|
|
240205
240502
|
};
|
|
240206
240503
|
const serializeToolDetails = (tool) => ({
|
|
240207
240504
|
name: tool.name,
|
|
@@ -240264,16 +240561,17 @@ const paginateDiscoveryResource = (items, input2) => {
|
|
|
240264
240561
|
};
|
|
240265
240562
|
};
|
|
240266
240563
|
const getMoreTools = (brief, toolNames, tools) => {
|
|
240267
|
-
const exactTools =
|
|
240564
|
+
const { tools: exactTools, missingTools } = getExactToolSelection(
|
|
240565
|
+
toolNames,
|
|
240566
|
+
tools
|
|
240567
|
+
);
|
|
240268
240568
|
const matchedTools = toolNames.length > 0 ? exactTools : getMatchingTools(brief, tools);
|
|
240269
240569
|
const limitedTools = matchedTools.slice(0, 12);
|
|
240270
240570
|
return {
|
|
240271
240571
|
usage: 'Prefer { tools: ["exact-tool-name"] } for precise details. Brief search is capped to avoid oversized responses; refine the brief or pass exact tool names when omittedCount is greater than 0.',
|
|
240272
240572
|
brief,
|
|
240273
240573
|
requestedTools: toolNames,
|
|
240274
|
-
missingTools
|
|
240275
|
-
(name2) => exactTools.some((tool) => tool.name === name2) === false
|
|
240276
|
-
),
|
|
240574
|
+
missingTools,
|
|
240277
240575
|
count: matchedTools.length,
|
|
240278
240576
|
omittedCount: Math.max(0, matchedTools.length - limitedTools.length),
|
|
240279
240577
|
tools: limitedTools.map(serializeToolDetails)
|
|
@@ -240282,7 +240580,7 @@ const getMoreTools = (brief, toolNames, tools) => {
|
|
|
240282
240580
|
const readOnlySessionTools = /* @__PURE__ */ new Set([
|
|
240283
240581
|
"meta.index",
|
|
240284
240582
|
"meta.guide",
|
|
240285
|
-
"meta.
|
|
240583
|
+
"meta.get-more-tools",
|
|
240286
240584
|
"status",
|
|
240287
240585
|
"components.summary",
|
|
240288
240586
|
"components.list",
|
|
@@ -240296,7 +240594,8 @@ const readOnlySessionTools = /* @__PURE__ */ new Set([
|
|
|
240296
240594
|
"preview.status"
|
|
240297
240595
|
]);
|
|
240298
240596
|
const toolAliases = /* @__PURE__ */ new Map([
|
|
240299
|
-
["get-component-coverage-plan", "components.coverage-plan"]
|
|
240597
|
+
["get-component-coverage-plan", "components.coverage-plan"],
|
|
240598
|
+
["meta.get_more_tools", "meta.get-more-tools"]
|
|
240300
240599
|
]);
|
|
240301
240600
|
const resolveToolName = (name2) => toolAliases.get(name2) ?? name2;
|
|
240302
240601
|
const isReadOnlyProjectSessionMcpTool = (tool) => tool.annotations.method === "query" || tool.annotations.method === "session" && readOnlySessionTools.has(tool.name);
|
|
@@ -240329,7 +240628,7 @@ const sdkDetailedOptionalSchemaProperties = /* @__PURE__ */ new Set([
|
|
|
240329
240628
|
"confirmDestructive",
|
|
240330
240629
|
"confirmationToken"
|
|
240331
240630
|
]);
|
|
240332
|
-
const getSdkSchemaProperty = (value2) => {
|
|
240631
|
+
const getSdkSchemaProperty = (value2, preserveRequiredShape = false) => {
|
|
240333
240632
|
if (typeof value2 === "boolean") {
|
|
240334
240633
|
return value2;
|
|
240335
240634
|
}
|
|
@@ -240337,12 +240636,26 @@ const getSdkSchemaProperty = (value2) => {
|
|
|
240337
240636
|
Object.entries(value2).filter(([key]) => sdkScalarSchemaKeys.has(key))
|
|
240338
240637
|
);
|
|
240339
240638
|
if (value2.items !== void 0) {
|
|
240340
|
-
result2.items = getSdkSchemaProperty(value2.items);
|
|
240639
|
+
result2.items = getSdkSchemaProperty(value2.items, preserveRequiredShape);
|
|
240640
|
+
}
|
|
240641
|
+
if (preserveRequiredShape && Array.isArray(value2.required) && value2.required.length > 0) {
|
|
240642
|
+
result2.required = value2.required;
|
|
240643
|
+
const required = new Set(value2.required);
|
|
240644
|
+
const properties2 = Object.fromEntries(
|
|
240645
|
+
Object.entries(value2.properties ?? {}).flatMap(
|
|
240646
|
+
([name2, property2]) => required.has(name2) ? [[name2, getSdkSchemaProperty(property2)]] : []
|
|
240647
|
+
)
|
|
240648
|
+
);
|
|
240649
|
+
if (Object.keys(properties2).length > 0) {
|
|
240650
|
+
result2.properties = properties2;
|
|
240651
|
+
}
|
|
240341
240652
|
}
|
|
240342
240653
|
for (const key of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
|
|
240343
240654
|
const branches = value2[key];
|
|
240344
240655
|
if (Array.isArray(branches)) {
|
|
240345
|
-
result2[key] = branches.map(
|
|
240656
|
+
result2[key] = branches.map(
|
|
240657
|
+
(branch) => getSdkSchemaProperty(branch, preserveRequiredShape)
|
|
240658
|
+
);
|
|
240346
240659
|
}
|
|
240347
240660
|
}
|
|
240348
240661
|
return result2;
|
|
@@ -240363,13 +240676,24 @@ const getSdkInputSchema = (schema2, includeOptionalProperties) => {
|
|
|
240363
240676
|
type: "object",
|
|
240364
240677
|
additionalProperties: schema2.additionalProperties === void 0 ? false : getSdkSchemaProperty(schema2.additionalProperties),
|
|
240365
240678
|
...Object.keys(properties2).length === 0 ? {} : { properties: properties2 },
|
|
240366
|
-
...required.size === 0 ? {} : { required: [...required] }
|
|
240679
|
+
...required.size === 0 ? {} : { required: [...required] },
|
|
240680
|
+
...Object.fromEntries(
|
|
240681
|
+
["allOf", "anyOf", "oneOf"].flatMap((key) => {
|
|
240682
|
+
const branches = schema2[key];
|
|
240683
|
+
return Array.isArray(branches) ? [
|
|
240684
|
+
[
|
|
240685
|
+
key,
|
|
240686
|
+
branches.map((branch) => getSdkSchemaProperty(branch, true))
|
|
240687
|
+
]
|
|
240688
|
+
] : [];
|
|
240689
|
+
})
|
|
240690
|
+
)
|
|
240367
240691
|
};
|
|
240368
240692
|
};
|
|
240369
240693
|
const sdkDescribedToolNames = /* @__PURE__ */ new Set([
|
|
240370
240694
|
"meta.index",
|
|
240371
240695
|
"meta.guide",
|
|
240372
|
-
"meta.
|
|
240696
|
+
"meta.get-more-tools",
|
|
240373
240697
|
"workflow.next",
|
|
240374
240698
|
...metaGoalGuides.flatMap(({ tools }) => tools)
|
|
240375
240699
|
]);
|
|
@@ -241224,10 +241548,10 @@ const createProjectSessionMcpCore = ({
|
|
|
241224
241548
|
if (name2 === "workflow.next") {
|
|
241225
241549
|
return toCheckpointedMetaResult(name2, getWorkflowNext(input2));
|
|
241226
241550
|
}
|
|
241227
|
-
if (name2 === "meta.
|
|
241551
|
+
if (name2 === "meta.get-more-tools") {
|
|
241228
241552
|
return toMetaResult(
|
|
241229
241553
|
getMoreTools(
|
|
241230
|
-
getBrief(input2, "meta.
|
|
241554
|
+
getBrief(input2, "meta.get-more-tools"),
|
|
241231
241555
|
getToolNamesInput(input2),
|
|
241232
241556
|
listTools()
|
|
241233
241557
|
)
|
|
@@ -242671,7 +242995,7 @@ const serverOnlyRouterOperationMetadata = {
|
|
|
242671
242995
|
properties: {
|
|
242672
242996
|
mode: {
|
|
242673
242997
|
type: "string",
|
|
242674
|
-
const: "markdown-body"
|
|
242998
|
+
const: "markdown-body-ref"
|
|
242675
242999
|
},
|
|
242676
243000
|
maxBytes: {
|
|
242677
243001
|
type: "integer",
|
|
@@ -243052,7 +243376,7 @@ const serverOnlyRouterOperationMetadata = {
|
|
|
243052
243376
|
properties: {
|
|
243053
243377
|
mode: {
|
|
243054
243378
|
type: "string",
|
|
243055
|
-
const: "markdown-body"
|
|
243379
|
+
const: "markdown-body-ref"
|
|
243056
243380
|
},
|
|
243057
243381
|
maxBytes: {
|
|
243058
243382
|
type: "integer",
|
|
@@ -243837,7 +244161,7 @@ const curatedPublicApiOperationDocumentation = [
|
|
|
243837
244161
|
},
|
|
243838
244162
|
{
|
|
243839
244163
|
command: "inspect-instance",
|
|
243840
|
-
description: "Show
|
|
244164
|
+
description: "Show one element with its classes and custom attributes by default. Include styles, children, bindings, sources, and ancestors as needed",
|
|
243841
244165
|
requiredOptions: ["instance", "json"],
|
|
243842
244166
|
examples: [
|
|
243843
244167
|
"webstudio inspect-instance --instance instance-id --include props,styles,children,ancestors --json"
|
|
@@ -243884,7 +244208,7 @@ const curatedPublicApiOperationDocumentation = [
|
|
|
243884
244208
|
command: "insert-fragment",
|
|
243885
244209
|
description: "Insert authored/styled Webstudio JSX with components, text, props, tokens, and styles. The CLI converts the JSX string to structured Webstudio data before mutation.",
|
|
243886
244210
|
examples: [
|
|
243887
|
-
|
|
244211
|
+
`MCP tool: insert-fragment {"parentInstanceId":"parent-id","fragment":"<ws.element ws:tag='section' />"}`
|
|
243888
244212
|
]
|
|
243889
244213
|
},
|
|
243890
244214
|
{
|
|
@@ -243955,6 +244279,14 @@ const curatedPublicApiOperationDocumentation = [
|
|
|
243955
244279
|
'webstudio update-text --instance instance-id --child-index 0 --text "user.name" --mode expression --json'
|
|
243956
244280
|
]
|
|
243957
244281
|
},
|
|
244282
|
+
{
|
|
244283
|
+
command: "set-text-content",
|
|
244284
|
+
description: 'Replace all text content on an element instance with operation "set", or remove it with operation "reset"',
|
|
244285
|
+
examples: [
|
|
244286
|
+
'MCP tool: set-text-content {"operation":"set","instanceId":"instance-id","text":"Launch faster","mode":"text"}',
|
|
244287
|
+
'MCP tool: set-text-content {"operation":"reset","instanceId":"instance-id"}'
|
|
244288
|
+
]
|
|
244289
|
+
},
|
|
243958
244290
|
{
|
|
243959
244291
|
command: "replace-text",
|
|
243960
244292
|
description: "Replace bounded literal text children across a page or project; use a separate command instead of an update-text replace mode",
|
|
@@ -244144,7 +244476,7 @@ const curatedPublicApiOperationDocumentation = [
|
|
|
244144
244476
|
},
|
|
244145
244477
|
{
|
|
244146
244478
|
command: "create-assets-resource",
|
|
244147
|
-
description: 'Create a scoped Assets resource. Omit query to use the minimal default query. For an explicit query, minimize the content database by selecting only fields the page renders, keeping includeMetadata false, and using content mode none.
|
|
244479
|
+
description: 'Create a scoped Assets resource. Omit query to use the minimal default query. For an explicit query, minimize the content database by selecting only fields the page renders, keeping includeMetadata false, and using content mode none. Use markdown-body-ref when querying a Markdown body directly; it requires storage-backed document resolution and never embeds the body in the content database. A structured document may instead select a field such as { "$ref": "./article.md#body" }. Preview concrete queries and inspect size diagnostics before saving.',
|
|
244148
244480
|
requiredOptions: ["input", "json"],
|
|
244149
244481
|
examples: [
|
|
244150
244482
|
"webstudio create-assets-resource --input assets-resource.json --json"
|
|
@@ -244152,7 +244484,7 @@ const curatedPublicApiOperationDocumentation = [
|
|
|
244152
244484
|
},
|
|
244153
244485
|
{
|
|
244154
244486
|
command: "update-assets-resource",
|
|
244155
|
-
description: 'Update an Assets resource. Set query to null to restore the minimal default query. Keep explicit queries storage-efficient by selecting only rendered fields, keeping includeMetadata false, and using content mode none.
|
|
244487
|
+
description: 'Update an Assets resource. Set query to null to restore the minimal default query. Keep explicit queries storage-efficient by selecting only rendered fields, keeping includeMetadata false, and using content mode none. Use markdown-body-ref for a directly queried Markdown body; it requires storage-backed document resolution and never embeds the body in the content database. A structured document may instead select a field such as { "$ref": "./article.md#body" }.',
|
|
244156
244488
|
requiredOptions: ["input", "json"],
|
|
244157
244489
|
examples: [
|
|
244158
244490
|
"webstudio update-assets-resource --input assets-resource-update.json --json"
|
|
@@ -246076,7 +246408,7 @@ class HandledCliError extends Error {
|
|
|
246076
246408
|
}
|
|
246077
246409
|
const isHandledCliError = (error) => error instanceof HandledCliError;
|
|
246078
246410
|
const name = "webstudio";
|
|
246079
|
-
const version$1 = "0.
|
|
246411
|
+
const version$1 = "0.285.1";
|
|
246080
246412
|
const description = "Webstudio CLI";
|
|
246081
246413
|
const author = "Webstudio <github@webstudio.is>";
|
|
246082
246414
|
const homepage = "https://webstudio.is";
|
|
@@ -257520,9 +257852,7 @@ const createFramework = async (options = {}) => {
|
|
|
257520
257852
|
}
|
|
257521
257853
|
const dynamic = isPathnamePattern(pagePath2);
|
|
257522
257854
|
if (dynamic && prerenderPaths.length === 0) {
|
|
257523
|
-
|
|
257524
|
-
`Dynamic SSG page ${JSON.stringify(pagePath2)} has no enumerable Assets query paths`
|
|
257525
|
-
);
|
|
257855
|
+
return [];
|
|
257526
257856
|
}
|
|
257527
257857
|
const route = generateVikeRoute(pagePath2);
|
|
257528
257858
|
const entries = [
|
|
@@ -258646,11 +258976,11 @@ const build = async (options) => {
|
|
|
258646
258976
|
await prebuild(options);
|
|
258647
258977
|
};
|
|
258648
258978
|
const cliDocs = {
|
|
258649
|
-
"api-use-cases": '# CLI API Use Cases\n\n## Link/configure one project\n\nCommands:\n\n- webstudio init --link <api-share-link> --json\n\nNotes:\n\n- Writes local project id and global origin/token config.\n\n## Import synced project bundle into another project\n\nCommands:\n\n- webstudio sync\n- webstudio import --to <destination-share-link>\n- MCP tool: import {"to":"<destination-share-link>"}\n\nNotes:\n\n- Imports local `.webstudio/data.json` into the destination project.\n- Destination share link must allow build/import access.\n- Use `--skip-assets` only when asset rows and files should not be imported.\n\n## Identify current token\n\nCommands:\n\n- MCP tool: whoami {}\n\n## Check token permissions\n\nCommands:\n\n- webstudio permissions --json\n\n## Inspect project/build/version\n\nCommands:\n\n- MCP tool: inspect {}\n- MCP tool: snapshot {"include":["pages","instances","styles"]}\n\n## Discover CLI/API capabilities\n\nCommands:\n\n- webstudio schema api\n- webstudio schema mcp\n- webstudio man --json\n- webstudio man llm --json\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"Create a pricing page"}\n- MCP tool: meta.get_more_tools {"brief":"update-styles"}\n- webstudio mcp list-resources\n- webstudio mcp read-resource webstudio://project/guide\n- webstudio mcp read-resource webstudio://project/expressions\n\nNotes:\n\n- Use `webstudio schema mcp` for a compact machine-readable MCP tool overview. Add `--verbose` or use focused `meta.get_more_tools` calls only when exact input schemas are needed.\n- Use focused MCP tools for discovery first: `meta.index`, `meta.guide`, `meta.get_more_tools`, `components.list`, `components.summary`, `components.search`, `components.get`, `templates.list`, and `templates.get`. Protocol clients can use `resources/list` and `resources/read`; shell agents can use `webstudio mcp list-resources` and `webstudio mcp read-resource <uri>`. Read longer resources such as `webstudio://project/tools` and `webstudio://project/components` only when focused tools are insufficient.\n- `components.summary` returns counts by default; request `{"detail":"components","limit":20}` for paginated entries. Registry list tools return compact metadata, while `components.get` and `templates.get` return focused full details.\n- Read `webstudio://project/expressions` before authoring unfamiliar computed text, prop bindings, resource expressions, actions, or Collection item bindings.\n- From a shell, call one MCP tool with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio components.summary`. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `--input-file` for large payloads.\n\n## Inspect external shadcn registry items\n\nCommands:\n\n- webstudio registry inspect --source https://example.com/r/registry.json --item button --json\n- webstudio registry inspect --source ./registry.json --item dialog --json\n- webstudio registry inspect --source https://example.com/r/button.json --json\n\nNotes:\n\n- Reads a local or remote registry item without installing files or changing the configured Webstudio project.\n- Returns the item name, description, package and registry dependencies, file paths/targets, available docs, and a read-only compatibility report.\n- The report explicitly says whether installation or editable-component conversion is supported, lists declared requirements and manual steps, and says when arbitrary source code was not analyzed.\n- This is an inspection step only. It does not install files or change the configured project.\n\n## Understand what MCP can do\n\nCommands:\n\n- webstudio man mcp\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"What can Webstudio MCP do?"}\n\nMCP lets agents work on one configured Webstudio project. Agents can:\n\n- Inspect the linked project, token permissions, and latest editable build.\n- Read selected project data for audits, migrations, and repair.\n- Search labels, text, props, resource URLs, asset metadata, and styles.\n- Audit accessibility, security, SEO, performance settings, unused assets, ineffective Collection styles, and unused or duplicate style data.\n- Create and edit pages, folders, redirects, breakpoints, and page templates.\n- Create pages from reusable templates.\n- Update page metadata, SEO fields, auth settings, and marketplace metadata.\n- Insert components and styled JSX sections.\n- Create data-driven lists, grids, cards, and similar repeated UI from array or object data in one Collection operation.\n- Move, copy, wrap, unwrap, convert, rename, retag, and delete elements.\n- Update text, rich text, props, bindings, and actions.\n- Create and update local styles, design tokens, style sources, and CSS variables.\n- Create static data variables and JSON variables.\n- Create HTTP, GraphQL, and system resources.\n- Use system resources for sitemap, current date, and assets.\n- Bind resources to rendered data or form/action props.\n- Manage nested asset folders and upload, inspect, move, duplicate, download, replace, delete, and inspect usage for assets.\n- Publish, unpublish, inspect publish jobs, and manage custom domains.\n- Start preview, capture screenshots, compare screenshot diffs, and use OCR when installed.\n\n## Inspect and refresh MCP session cache\n\nCommands:\n\n- MCP tool: status {}\n- MCP tool: status {"verbose":true}\n- MCP tool: refresh {"namespaces":["pages","instances","styles"]}\n- MCP tool: reset-session {}\n\nNotes:\n\n- Use status before a task to understand the cached ProjectSession state.\n- Use status with `{"verbose":true}` only when debugging full namespaces, freshness, compatibility, or diagnostics.\n- Use refresh when project data may have changed outside the current MCP session.\n- Use reset-session when local cached state is corrupt or incompatible.\n\n## Visually verify rendered work with AI vision\n\nCommands:\n\n- MCP tool: preview.start {"host":"127.0.0.1","port":5173}\n- MCP tool: preview.status {}\n- MCP tool: preview.stop {}\n- MCP tool: screenshot {"path":"/","output":".webstudio/screenshots/home-current.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"path":"/pricing","output":".webstudio/screenshots/pricing-current.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"baseUrl":"http://127.0.0.1:5177","path":"/pricing","output":".webstudio/screenshots/pricing-current.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/home-before.png","currentPath":".webstudio/screenshots/home-current.png","outputDir":".webstudio/screenshots/diff"}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/home-before.png","currentPath":".webstudio/screenshots/home-current.png","outputDir":".webstudio/screenshots/diff","expectedText":["Pricing","Start free"]}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/home-before.png","currentPath":".webstudio/screenshots/home-current.png","outputDir":".webstudio/screenshots/diff","expectedVisual":{"maxMismatchPercentage":2,"maxChangedRegions":3,"dominantColorChange":{"channel":"luminance","direction":"increase","minMagnitude":10}}}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/pricing-before.png","currentPath":".webstudio/screenshots/pricing-current.png","outputDir":".webstudio/screenshots/diff"}\n- MCP tool: vision.install-ocr {"confirm":true}\n\nNotes:\n\n- Use this after page/content/style mutations so a vision-capable AI can see the generated site from the current MCP session. Use `path`; never pass a Webstudio Builder/share URL or capture Builder chrome.\n- For multi-page work, capture every changed page by `path` through the same preview server; no click navigation is required.\n- Iterative mode is the default: after MCP mutations, path screenshots ensure generated project files are current, wait for the exact session version, and perform an ordinary page reload without Vite HMR. The preview server and browser stay alive. Use `{"mode":"production"}` only for release-like verification; rendered audit does this automatically.\n- Do not call `preview.start` through one-shot `webstudio mcp single-op-call`: it is long-lived. From a shell, use `webstudio mcp run` with preview.start, screenshot, and preview.stop in one shared process, or use a real long-running MCP client.\n- From one-shot shell calls or another process, pass `baseUrl` with `path` to capture an already-running preview/site without generating, building, starting, or restarting preview.\n- Use preview.stop only in the same long-running MCP server or `webstudio mcp run` process that started preview. A separate one-shot `single-op-call` process does not own another process\'s preview controller.\n- Use waitForSelector when the rendered app has a reliable ready marker, waitUntil:"networkidle" for network-heavy pages, and waitForTimeout only for final visual settling.\n- Preview installs generated app dependencies under `.webstudio/preview` and reuses them across regenerations.\n- Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.\n- If dependency installation fails, check npm and network configuration, then reinstall or update the Webstudio CLI if the problem persists.\n- When a baseline exists, use screenshot.diff once per baseline/current page or viewport pair to get changed regions, OCR textAnalysis, and diff artifact paths before deciding whether the result matches. Pass expectedText for explicit pass/fail current-screen text assertions with found and missing text. Pass expectedVisual for pass/fail limits on pixel mismatch percentage, changed-region count, or the overall dominant color/brightness direction.\n- If screenshot.diff reports OCR unavailable and the user agrees to install it, call vision.install-ocr {"confirm":true}; otherwise continue with pixel diff and visual inspection.\n- Compare the PNG, OCR text evidence, and diff artifacts against the user\'s intent for layout, typography, colors, spacing, imagery, and responsive framing; then iterate with focused mutations.\n- Root CLI equivalent: `webstudio screenshot --path /pricing --output pricing.png` generates a temporary production preview, captures that route, and stops the server. For repeated captures, keep `webstudio preview` running and pass its absolute URL to `webstudio screenshot`.\n\n## List pages\n\nCommands:\n\n- MCP tool: list-pages {}\n- MCP tool: list-folders {}\n\n## Read page by id\n\nCommands:\n\n- MCP tool: get-page {"pageId":"<pageId>"}\n\n## Read page by path\n\nCommands:\n\n- MCP tool: get-page-by-path {"path":"/pricing"}\n\n## Create page\n\nCommands:\n\n- MCP tool: create-page {"name":"Pricing","path":"/pricing"}\n- MCP tool: create-page {"name":"Pricing","path":"/pricing","title":"Pricing","meta":{"description":"Plans for teams"}}\n\nNotes:\n\n- `name`, `path`, page `title`, and metadata text fields accept plain fixed values.\n- For computed page titles or metadata, send JavaScript expression code such as `pageTitle ?? "Pricing"`.\n\n## Update page settings/metadata\n\nCommands:\n\n- MCP tool: update-page {"pageId":"<pageId>","values":{"title":"Pricing","meta":{"description":"Plans","status":200}}}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"meta":{"auth":{"method":"basic","login":"<login>","password":"<password>"}}}}\n\nNotes:\n\n- Page `title` and metadata text fields accept plain fixed values.\n- For computed page titles or metadata, send JavaScript expression code such as `pageTitle ?? "Pricing"`.\n- Page `status` accepts a fixed HTTP status code as a number from 200 through 599 or a JavaScript expression string for a dynamic status.\n\n## Read project settings\n\nCommands:\n\n- MCP tool: get-project-settings {}\n\nNotes:\n\n- Read `meta.agentInstructions` before making project changes. It contains the project\'s own guidance for AI agents.\n- Agent instructions are shared project guidance. Do not store credentials or other secrets there.\n\n## Update project settings\n\nCommands:\n\n- MCP tool: update-project-settings {"meta":{"siteName":"Acme"}}\n- MCP tool: update-project-settings {"meta":{"agentInstructions":"Use existing design tokens and keep product copy concise."}}\n\n## Read marketplace product\n\nCommands:\n\n- MCP tool: get-marketplace-product {}\n\n## Update marketplace product\n\nCommands:\n\n- MCP tool: update-marketplace-product {"category":"pageTemplates","name":"Acme Template","thumbnailAssetId":"asset-id","author":"Acme Studio","email":"hello@example.com","website":"https://example.com","issues":"","description":"Reusable template project for Acme landing pages."}\n\n## Submit marketplace product\n\nCommands:\n\n- MCP tool: upload-asset {"asset":{"name":"marketplace-thumbnail.png","type":"image","format":"png","meta":{"width":1200,"height":630}},"assetsDir":".webstudio/assets"}\n- MCP tool: update-marketplace-product {"category":"pageTemplates","name":"Acme Template","thumbnailAssetId":"<uploadedAssetId>","author":"Acme Studio","email":"hello@example.com","website":"https://example.com","issues":"","description":"Reusable template project for Acme landing pages."}\n- MCP tool: publish {"target":"production"}\n- MCP tool: submit-marketplace-product {"acknowledgePublicSubmission":true}\n\nNotes:\n\n- Wait for the production publish to complete before submitting the product for review.\n- Submission requires complete, valid marketplace metadata.\n\n## List redirects\n\nCommands:\n\n- MCP tool: list-redirects {}\n\n## Create redirect\n\nCommands:\n\n- MCP tool: create-redirect {"old":"/old","new":"/new","status":301}\n\n## Update redirect\n\nCommands:\n\n- MCP tool: update-redirect {"old":"/old","values":{"new":"/newer","status":302}}\n- MCP tool: update-redirect {"old":"/old","values":{"status":null}}\n\n## Delete redirect\n\nCommands:\n\n- MCP tool: delete-redirect {"old":"/old"}\n\n## Set redirects\n\nCommands:\n\n- MCP tool: set-redirects {"redirects":[{"old":"/old","new":"/new","status":"301"}]}\n\n## List breakpoints\n\nCommands:\n\n- MCP tool: list-breakpoints {}\n\n## Create breakpoint\n\nCommands:\n\n- MCP tool: create-breakpoint {"label":"Tablet","maxWidth":991}\n\n## Update breakpoint\n\nCommands:\n\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"label":"Tablet","maxWidth":1023}}\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"condition":null,"minWidth":768}}\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"minWidth":null,"maxWidth":null,"condition":"(hover: hover)"}}\n\n## Delete breakpoint\n\nCommands:\n\n- MCP tool: delete-breakpoint {"breakpointId":"tablet"}\n\n## Duplicate page\n\nCommands:\n\n- MCP tool: duplicate-page {"pageId":"<pageId>","name":"Pricing Copy","path":"/pricing-copy"}\n- MCP tool: duplicate-page {"pageId":"<pageId>","name":"Paris","path":"/paris","substitutions":{"text":{"London":"Paris"},"variables":{"city":{"type":"string","value":"Paris"}}}}\n- webstudio duplicate-page --page <pageId> --name Paris --path /paris --substitutions \'{"text":{"London":"Paris"},"variables":{"city":{"type":"string","value":"Paris"}}}\' --json\n\nNotes:\n\n- Text substitutions replace exact fixed text only in the duplicated page\'s text children, string props, title, and metadata.\n- Variable substitutions are keyed by copied source-variable name and use typed variable values. The operation rejects missing or ambiguous names without committing a partial duplicate.\n- Existing expressions and cloned variable/resource references keep their remapped ids.\n\n## List page templates\n\nCommands:\n\n- MCP tool: list-page-templates {}\n\n## Create page template\n\nCommands:\n\n- MCP tool: create-page-template {"name":"Landing Template","title":"Landing"}\n\n## Update page template\n\nCommands:\n\n- MCP tool: update-page-template {"templateId":"<templateId>","values":{"name":"Article Template","meta":{"description":"Reusable article layout"}}}\n\n## Delete page template\n\nCommands:\n\n- MCP tool: delete-page-template {"templateId":"<templateId>"}\n\n## Duplicate page template\n\nCommands:\n\n- MCP tool: duplicate-page-template {"templateId":"<templateId>"}\n\n## Reorder page template\n\nCommands:\n\n- MCP tool: reorder-page-template {"sourceTemplateId":"<sourceTemplateId>","targetTemplateId":"<targetTemplateId>","position":"before"}\n\n## Create page from template\n\nCommands:\n\n- MCP tool: create-page-from-template {"templateId":"<templateId>","name":"Landing","path":"/landing"}\n\n## Delete page\n\nCommands:\n\n- MCP tool: delete-page {"pageId":"<pageId>"}\n\n## List folders\n\nCommands:\n\n- MCP tool: list-folders {}\n- MCP tool: list-pages {}\n\n## Create folder\n\nCommands:\n\n- MCP tool: create-folder {"name":"Blog","slug":"blog"}\n\n## Update folder\n\nCommands:\n\n- MCP tool: update-folder {"folderId":"<folderId>","values":{"name":"Blog","slug":"blog"}}\n\n## Delete folder\n\nCommands:\n\n- MCP tool: delete-folder {"folderId":"<folderId>"}\n\n## List element instances\n\nCommands:\n\n- MCP tool: list-instances {"pagePath":"/","maxDepth":3}\n\n## Inspect one element instance\n\nCommands:\n\n- MCP tool: inspect-instance {"instanceId":"<instanceId>","include":["props","styles","children"]}\n\n## Insert authored JSX or one component template\n\nCommands:\n\n- MCP tool: insert-fragment {"parentInstanceId":"<instanceId>","fragment":"<ws.element ws:tag=\\"section\\" ws:style={css`padding: 32px;`}><ws.element ws:tag=\\"h2\\">Product OS</ws.element><radix.Switch><radix.SwitchThumb /></radix.Switch></ws.element>"}\n- MCP tool: insert-component {"parentInstanceId":"<instanceId>","component":"@webstudio-is/sdk-components-react-radix:Switch"}\n- MCP tool: insert-component {"parentInstanceId":"<instanceId>","component":"Form"}\n\nNotes:\n\n- Use MCP `insert-fragment` as the default way to author styled component trees. It converts JSX to a structured fragment before mutation.\n- Use only exact component ids returned by `components.search`, `components.get`, or `templates.get`. Never derive or guess component ids.\n- The `ws:` namespace contains specific Webstudio core components; it is not HTML-tag shorthand. Use `<ws.element ws:tag="div">` for a native `div` and `<ws.element ws:tag="form">` for a native form, never `<ws.div>` or `<ws.form>`.\n- For Webstudio\'s complete form structure, discover the Form component and insert its automatic template with `insert-component` using component `"Form"`.\n- MCP receives JSX as a JSON string because MCP arguments are JSON. The CLI converts it locally before the runtime mutation, so the project session receives structured Webstudio data, not JSX source.\n- In `insert-fragment` JSX, use `ws:style={css\\`...\\`}`for Webstudio-native CSS, or use React-style object syntax such as`style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.\n- Do not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`.\n- Use Webstudio prop names such as `class` and `for`; do not use React aliases `className` or `htmlFor`.\n- Use Webstudio actions for event/action props, for example `onClick={new ActionValue(["event"], expression\\`console.log(event)\\`)}`. Do not pass JavaScript functions such as `onClick={() => ...}`.\n- Plain prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.\n- Template-backed components used in JSX must include required child/part components explicitly under the same parent structure as the template, for example `<radix.Switch><radix.SwitchThumb /></radix.Switch>`.\n- Webstudio applies a registered template automatically when using `insert-component`, so composed components such as Switch include required child parts and styles.\n- Use `components.list`, `components.summary`, `components.search`, `components.get`, `templates.list`, and `templates.get` to discover known registry items, component ids, props, templates, insertability, and content model. Read `webstudio://project/components` only when those focused tools are insufficient.\n- Component/template registry items use a shadcn-compatible top-level shape plus Webstudio-specific superset metadata in `meta`. They are for Builder/MCP discovery, not a published shadcn install registry yet.\n- Known components with `contentModel.category: "none"` are not standalone-insertable; insert their root component template instead so required providers/parents are included.\n- Unknown custom component ids are a low-level extension mechanism, not a discovery fallback. Agents must not synthesize them.\n\n## Make a region editable in Content mode\n\nCommands:\n\n- MCP tool: insert-component {"parentInstanceId":"<instanceId>","component":"ws:block"}\n- MCP tool: inspect-instance {"instanceId":"<instanceId>","include":["children"]}\n\nNotes:\n\n- When a page will be handed to a Content-mode editor, wrap every region they should be able to edit in a Content Block (`ws:block`). Content-mode editors can edit text and supported props only in Content Block descendants. Content outside those blocks remains read-only, even when it looks like ordinary editable text.\n- Put reusable insertable options inside the Content Block\'s `ws:block-template` child. A template is source material, not editor content: editors cannot edit or delete it directly. When an editor inserts a template, its copy becomes a direct child of the Content Block and is editable.\n- Before handing off a page, verify with `inspect-instance` that the intended text, images, and links are inside a Content Block, and that templates include all required styling because Content-mode editors cannot use the Style panel.\n\n## Move elements\n\nCommands:\n\n- MCP tool: move-instance {"moves":"moves.json contents"}\n\nNotes:\n\n- Use `position: "end"` to append an instance. Repeating this for A and then B preserves the final order A, B.\n- A numeric `insertIndex` addresses the target parent\'s children before the moved instance is removed. Use it for exact placement; do not calculate the last index to append.\n- Moves in one `moves` array are applied sequentially in array order.\n\n## Clone element subtree\n\nCommands:\n\n- MCP tool: clone-instance {"sourceInstanceId":"<instanceId>","targetParentInstanceId":"<targetParentId>"}\n\n## Delete element subtree\n\nCommands:\n\n- MCP tool: delete-instance {"instanceIds":["<instanceId>"]}\n\n## List text/expression children\n\nCommands:\n\n- MCP tool: list-texts {"pagePath":"/"}\n\n## Update text child\n\nCommands:\n\n- MCP tool: update-text {"instanceId":"<instanceId>","childIndex":0,"text":"Launch faster"}\n\n## Replace bounded literal text\n\nCommands:\n\n- MCP tool: replace-text {"find":"Start free","replace":"Get started","match":"exact","pagePath":"/pricing","limit":20}\n\nNotes:\n\n- This changes only literal text children, never expression children. Scope it to pagePath or pageId and set a limit before a broad replacement.\n\n## Replace bounded static prop text\n\nCommands:\n\n- MCP tool: replace-prop-text {"find":"old.example.com","replace":"www.example.com","match":"substring","names":["href","code"],"limit":20}\n\nNotes:\n\n- This changes only static string props such as href, alt, aria-label, title, and HTML embed code. It never changes expressions, resources, actions, assets, or other dynamic bindings. Use names or instanceIds and a limit to narrow the change.\n\n## Replace bounded resource text\n\nCommands:\n\n- MCP tool: replace-resource-text {"find":"api.old.example.com","replace":"api.example.com","fields":["url"],"limit":20}\n\nNotes:\n\n- This changes resource names and fixed URL literals only. It skips dynamic URL expressions, headers, search parameters, request bodies, and GraphQL query code.\n\n## Update props\n\nCommands:\n\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: replace-prop-text {"find":"Old label","replace":"New label","names":["aria-label","title"],"limit":20}\n\nNotes:\n\n- Use this for fixed prop values such as `aria-label`, `alt`, `id`, static `href`, and other direct string/number/boolean/json prop values.\n\n## Add JSON-LD structured data\n\nCommands:\n\n- MCP tool: components.get {"component":"JsonLd"}\n- MCP tool: insert-component {"parentInstanceId":"<headSlotInstanceId>","component":"JsonLd"}\n- MCP tool: update-props {"updates":[{"instanceId":"<jsonLdInstanceId>","name":"code","type":"string","value":"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Organization\\",\\"name\\":\\"Acme\\"}"}]}\n- MCP tool: audit {"scopes":["seo"],"pagePath":"/"}\n\nNotes:\n\n- Prefer placing `JsonLd` inside `HeadSlot`.\n- Store `code` as a JSON object or array encoded as a compact string. The Builder formats it for editing.\n- The semantic prop update rejects malformed JSON and structurally invalid fixed JSON-LD with a precise JSON path.\n- The SEO audit also warns about a missing top-level `@context`, unknown or superseded Schema.org terms, properties unsupported by the supplied type, and incompatible primitive value types.\n- Schema.org vocabulary findings are warnings because custom vocabularies and extensions remain valid. Dynamic JSON-LD is marked as skipped for rendered validation.\n- Do not use bindings just to set static text.\n\n## Delete props\n\nCommands:\n\n- MCP tool: delete-props {"deletions":"props.json contents"}\n\n## Bind props to expressions/resources/actions\n\nCommands:\n\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n\nNotes:\n\n- Use this only when the prop should remain dynamic: expression, resource, action, or an existing scoped runtime context value such as `system`.\n- For a fixed string value, use `update-props` with `type:"string"` and a direct `value` instead.\n\n## Read styles\n\nCommands:\n\n- MCP tool: get-styles {"instanceIds":["<instanceId>"],"includeTokens":true}\n\n## Update local styles\n\nCommands:\n\n- MCP tool: update-styles {"updates":"styles.json contents"}\n\n## Delete local styles\n\nCommands:\n\n- MCP tool: delete-styles {"deletions":"styles.json contents"}\n\n## Replace matching style values\n\nCommands:\n\n- MCP tool: replace-styles {"property":"color","fromValue":{"type":"keyword","value":"red"},"toValue":{"type":"keyword","value":"blue"}}\n\n## List design tokens\n\nCommands:\n\n- MCP tool: list-design-tokens {}\n- MCP tool: list-design-tokens {"withUsage":true}\n- MCP tool: list-design-tokens {"verbose":true}\n\nNotes:\n\n- The default response is compact and includes token id, name, declaration count, and optional usage count.\n- Use `verbose:true` only when you need the full inline style declarations.\n\n## Create design tokens\n\nCommands:\n\n- MCP tool: create-design-token {"tokens":"tokens.json contents"}\n\n## Update design token styles\n\nCommands:\n\n- MCP tool: update-design-token-styles {"designTokenId":"<tokenId>","updates":"styles.json contents"}\n\n## Delete design token styles\n\nCommands:\n\n- MCP tool: delete-design-token-styles {"designTokenId":"<tokenId>","deletions":"styles.json contents"}\n\n## Attach design token to instances\n\nCommands:\n\n- MCP tool: attach-design-token {"designTokenId":"<tokenId>","instanceIds":"instances.json contents"}\n\n## Detach design token from instances\n\nCommands:\n\n- MCP tool: detach-design-token {"designTokenId":"<tokenId>","instanceIds":"instances.json contents"}\n\n## Extract design token from local styles\n\nCommands:\n\n- MCP tool: extract-design-token {"instanceIds":["<instanceId>"],"name":"Brand Primary","removeLocalProps":["color"]}\n\n## List CSS variables\n\nCommands:\n\n- MCP tool: list-css-variables {"withUsage":true}\n\n## Define CSS variables\n\nCommands:\n\n- MCP tool: define-css-variable {"vars":"vars.json contents"}\n\n## Delete CSS variables\n\nCommands:\n\n- MCP tool: delete-css-variable {"names":"names.json contents","force":true}\n\n## Rewrite CSS variable references\n\nCommands:\n\n- MCP tool: rewrite-css-variable-refs {"map":"variables.json contents"}\n\n## List data variables\n\nCommands:\n\n- MCP tool: list-variables {}\n- MCP tool: list-variables {"scopeInstanceId":"<instanceId>"}\n\nNotes:\n\n- Data variables live in the internal `dataSources` namespace.\n- For raw `snapshot`, request the public `variables` namespace rather than the internal `dataSources` name. Raw patch payloads still use `dataSources` when applying direct changes.\n- Scope variables to the instance where they should become available. Descendants can use them in expressions, and nested variables with the same name mask outer variables.\n\n## Create data variable\n\nCommands:\n\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"title","value":{"type":"string","value":"Hello"}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"count","value":{"type":"number","value":3}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"featured","value":{"type":"boolean","value":true}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"tags","value":{"type":"json","value":["news","product"]}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"filters","value":{"type":"json","value":{"tag":"news"}}}\n\nNotes:\n\n- Data variable values support `string`, `number`, `boolean`, and `json`. Use `json` for all arrays and objects.\n- Parameters are internal scoped runtime values provided by pages, collections, or components. They are not a public authoring surface: do not create, update, or delete parameter records. Use data variables/resources for user-authored data, and reference documented context values such as `system` only where they are already in scope.\n\n## Update data variable\n\nCommands:\n\n- MCP tool: update-variable {"dataSourceId":"<variableId>","values":{"value":{"type":"json","value":{"count":1}}}}\n\n## Delete data variable\n\nCommands:\n\n- MCP tool: delete-variable {"dataSourceId":"<variableId>"}\n\n## List resources\n\nCommands:\n\n- MCP tool: list-resources {}\n- MCP tool: list-resources {"scopeInstanceId":"<instanceId>"}\n\n## Create resource\n\nCommands:\n\n- MCP tool: create-resource {"resource":{"name":"Posts","method":"get","url":"https://api.example.com/posts","headers":[]}}\n- MCP tool: create-resource {"resource":{"name":"Posts","method":"get","url":"\\"https://api.example.com/posts?tag=\\" + filters.tag","headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"posts"}\n- MCP tool: create-resource {"resource":{"name":"Filtered Posts","method":"get","url":"https://api.example.com/posts","searchParams":[{"name":"tag","value":"filters.tag"},{"name":"source","value":{"type":"literal","value":"website"}}],"headers":[{"name":"Authorization","value":"\\"Bearer \\" + auth.token"}]},"scopeInstanceId":"<instanceId>","dataSourceName":"posts"}\n- MCP tool: create-resource {"resource":{"name":"Post GraphQL","control":"graphql","method":"post","url":"https://api.example.com/graphql","headers":[{"name":"Content-Type","value":{"type":"literal","value":"application/json"}}],"body":"{ query: \\"query Post($slug: String!) { post(slug: $slug) { title } }\\", variables: { slug: system.params.slug } }"},"scopeInstanceId":"<instanceId>","dataSourceName":"post"}\n- MCP tool: create-resource {"resource":{"name":"Current Date","control":"system","method":"get","url":"/$resources/current-date","headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"currentDate"}\n\nNotes:\n\n- Resource `url` accepts plain fixed URLs and paths such as `https://api.example.com/posts` and `/$resources/current-date`.\n- Resource `url` can also be a JavaScript expression when it is computed, such as `"https://api.example.com/posts?tag=" + filters.tag`.\n- Header values, search parameter values, and body accept expressions for dynamic values. For fixed text, use `{"type":"literal","value":"application/json"}`; Webstudio stores the required string expression for you.\n- Search parameter values, header values, and body expressions can read scoped variables and documented runtime context values such as `system` when they are available at the resource scope.\n- Add `scopeInstanceId` and `dataSourceName` when the resource result should be exposed as a scoped read data variable. Scoped resources are generated into the page resource `data` map and may be loaded during page rendering. Use this for read-oriented resources such as GET CMS/API data.\n- For submit/write/action resources, create the resource without `scopeInstanceId`, then bind a component prop such as a Form `action` with `bind-props` and `binding.type: "resource"`. Prop-bound resources are generated into the page resource `action` map instead of the read `data` map. Use this for POST, PUT, DELETE, webhooks, GraphQL submissions, and other explicit action flows.\n- Resource `method` can be `get`, `post`, `put`, or `delete`. Use GET for read data, POST for creates/GraphQL/webhooks/form submissions, PUT for full updates or replacements, and DELETE for deletion actions.\n- Optional `control` values are `graphql` and `system`. Use `graphql` for GraphQL-style requests, usually POST with a query body. Use `system` for built-in resources such as `"/$resources/sitemap.xml"`, `"/$resources/current-date"`, and `"/$resources/assets"` and when the resource should use the built-in `system` parameter. System fields are `system.origin`, `system.pathname`, `system.params`, and `system.search`.\n\n## Update resource\n\nCommands:\n\n- MCP tool: update-resource {"resourceId":"<resourceId>","values":{"url":"https://api.example.com/posts"}}\n- MCP tool: replace-resource-text {"find":"api.old.example.com","replace":"api.example.com","fields":["url"],"limit":20}\n\n## Query Markdown assets\n\nCommands:\n\n- MCP tool: get-asset-field-catalog {}\n- MCP tool: validate-asset-query {"query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":"md"},{"field":["properties","draft"],"operator":"ne","value":true}]},"limit":20}}\n- MCP tool: create-assets-resource {"name":"All assets","scopeInstanceId":"<instanceId>","dataSourceName":"assets"}\n- MCP tool: create-assets-resource {"name":"Published posts","scopeInstanceId":"<instanceId>","dataSourceName":"posts","query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":{"type":"literal","value":"md"}},{"field":["properties","draft"],"operator":"ne","value":{"type":"literal","value":true}}]},"sort":[{"field":["properties","publishedAt"],"direction":"desc"}],"limit":{"type":"literal","value":20},"output":{"mode":"fields","includeMetadata":false,"fields":[["properties","title"],["properties","slug"],["properties","publishedAt"],["properties","excerpt"]]},"content":{"mode":"none"}}}\n- MCP tool: create-assets-resource {"name":"Post by slug or ID","scopeInstanceId":"<instanceId>","dataSourceName":"post","query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":{"type":"literal","value":"md"}},{"field":["properties","slug"],"operator":"eq","value":"system.params.slug"}]},"limit":{"type":"literal","value":1},"output":{"mode":"fields","includeMetadata":false,"fields":[["properties","title"],["properties","publishedAt"]]},"content":{"mode":"markdown-body"}}}\n- MCP tool: list-assets-resources {}\n- MCP tool: get-assets-resource {"resourceId":"<resourceId>"}\n- MCP tool: update-assets-resource {"resourceId":"<resourceId>","values":{"query":null}}\n- MCP tool: preview-asset-query {"query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":"md"},{"field":["properties","slug"],"operator":"eq","value":"hello-world"}]},"limit":1,"output":{"mode":"fields","includeMetadata":false,"fields":[["properties","title"]]},"content":{"mode":"markdown-body"}}}\n\nNotes:\n\n- Read the field catalog before authoring unfamiliar queries. It includes dynamic schema-less frontmatter paths such as `properties.author.name`, observed types, optionality, and mixed-type state without downloading Markdown files.\n- Minimize the deployed content database by using `output.mode:"fields"` and selecting only fields the rendered page needs. Keep `includeMetadata:false` unless the rendered value needs file metadata such as name, path, MIME type, or creation date, and avoid `output.mode:"all"` as a convenience default. Query diagnostics are returned separately and do not require metadata output. Filters and sorting may still require their referenced fields in the database.\n- Every reachable Assets data source contributes to the shared published database. Keep one final resource per rendered query. Update an existing scoped resource rather than creating a placeholder, preview copy, or repair replacement, and remove obsolete duplicates.\n- Keep bounded overview filters, limits, and offsets literal and add a deterministic ID tie-breaker to the sort. With `content.mode:"none"`, compilation can materialize the small overview result instead of retaining its output fields across every candidate document. Use runtime expressions only for genuinely dynamic values such as the detail slug.\n- Combine filters with `where.all` (AND) and `where.any` (OR), including nested groups. Filter values, limit, and offset on a saved resource may be Webstudio expressions evaluated at render time. Preview queries use concrete JSON values.\n- Query Markdown files directly and use `content.mode:"markdown-body"` when rendering their bodies. The published database retains metadata and a document reference, then fetches only the selected Markdown files from Asset storage at runtime; it does not embed their bodies. Filter and paginate before content is loaded.\n- `full` and bounded `range` continue to request embedded file bytes. Use them only when the caller requires the complete source or a byte range.\n- In Markdown, reference sibling Assets with conventional relative URLs such as `../images/hero.png`. Deferred `markdown-body` content resolves matching files against the Markdown file\'s folder and emits the correct Builder or published Asset URL. Keep external URLs absolute.\n- Markdown Embed renders sanitized authored HTML for figures, captions, audio, video, and iframes. Scripts, inline event handlers, `srcdoc`, and unsafe URL protocols are removed. Executable component composition remains separate future MDX work.\n- Preview each query with concrete values before saving it. Inspect `__diagnostics__.query` for the temporary query-only footprint and `__diagnostics__.database` for the merged database built from all reachable Assets queries. Only `database.usedBytes` counts toward `database.maxBytes`; query sizes must not be summed and are not separate allowances. Compare `usedBytes`, `unboundedBytes`, and `truncated` within both scopes. A completed Markdown blog should include every source document without truncation, contain no embedded Markdown bodies, and retain only its intended materialized overview query. When merged usage approaches the limit, remove duplicate reachable resources first, then unused output fields, then narrow candidate files.\n- Read Assets from the ID-keyed map at `<dataSourceName>.data`, with collection information such as `totalCount` and `hasMore` at `<dataSourceName>.meta`. Bind a listing Collection to `posts.data` and a one-result detail Collection to `post.data`. On each value, selected file fields such as `id`, `name`, and `extension` are top-level, Markdown frontmatter and JSON fields are under `properties`, and a resolved Markdown body is at `content.text`.\n- Assets has one response shape and always executes a structured query. Omit `query` to use the default query, which selects URL and optional image dimensions. Provide query configuration to control filtering, sorting, pagination, selected fields, or file content. Set `values.query:null` to restore the default query.\n- `create-assets-resource` and `update-assets-resource` are the semantic authoring path. Do not construct the internal query URL, headers, or body expression manually.\n- The shared metadata index is maintained automatically and is emitted only when a configured Assets resource is reachable.\n\n## Delete resource\n\nCommands:\n\n- MCP tool: delete-resource {"resourceId":"<resourceId>"}\n\n## List assets\n\nCommands:\n\n- MCP tool: list-assets {"withUsage":true}\n- MCP tool: list-assets {"verbose":true}\n\nNotes:\n\n- Compact results include each asset\'s folder id. Use `verbose:true` to include complete records for a page of assets, or `get-asset` to read one complete record including description, folder, creation time, and image/font metadata.\n- Image asset descriptions are the default alt text for asset-backed Image components.\n- To generate missing descriptions, inspect the image in its rendered page or asset source, write a concise description of its purpose, and save it on the asset rather than duplicating it on each Image instance.\n\n## Get asset\n\nCommands:\n\n- MCP tool: get-asset {"assetId":"<assetId>"}\n\n## List asset folders\n\nCommands:\n\n- MCP tool: list-asset-folders {}\n\n## Create asset folder\n\nCommands:\n\n- MCP tool: create-asset-folder {"name":"Marketing"}\n- MCP tool: create-asset-folder {"name":"Photos","parentId":"<parentFolderId>"}\n\n## Update asset folder\n\nCommands:\n\n- MCP tool: update-asset-folder {"folderId":"<folderId>","values":{"name":"Brand"}}\n- MCP tool: update-asset-folder {"folderId":"<folderId>","values":{"parentId":"<parentFolderId>"}}\n- MCP tool: update-asset-folder {"folderId":"<folderId>","values":{"parentId":null}}\n\nNotes:\n\n- Updating `parentId` is the folder equivalent of cut and paste. Use `null` to move a folder to Root.\n\n## Duplicate asset folder\n\nCommands:\n\n- MCP tool: duplicate-asset-folder {"folderId":"<folderId>"}\n- MCP tool: duplicate-asset-folder {"folderId":"<folderId>","parentId":"<targetFolderId>"}\n\nNotes:\n\n- Duplication recursively copies descendant folders and assets. This is the folder equivalent of copy and paste.\n\n## Delete asset folder\n\nCommands:\n\n- MCP tool: delete-asset-folder {"folderId":"<folderId>"}\n\nNotes:\n\n- Deleting a folder recursively deletes its descendant folders and assets.\n\n## Update asset metadata\n\nCommands:\n\n- MCP tool: update-asset {"assetId":"<assetId>","values":{"description":"Team collaborating around a whiteboard"}}\n- MCP tool: update-asset {"assetId":"<fontAssetId>","values":{"meta":{"family":"Rajdhani","style":"normal","weight":600}}}\n\nNotes:\n\n- Use an empty description only when the image is intentionally decorative.\n- Updating an image asset description updates the default alt text wherever that asset is used with an asset-backed alt prop.\n- Font metadata updates merge with the detected metadata and are validated before committing; use this to correct a family, style, or weight after upload.\n\n## Generate missing image descriptions with an agent\n\nCommands:\n\n- MCP tool: audit {"scopes":["accessibility"],"verbose":true}\n- MCP tool: set-image-descriptions {"updates":[{"assetId":"hero-id","description":"Team collaborating around a whiteboard"},{"assetId":"texture-id","decorative":true}]}\n- MCP tool: audit {"scopes":["accessibility"]}\n\nNotes:\n\n- Start from `missing-image-description` findings. Inspect each image in its rendered page context before writing text.\n- The vision-capable agent generates the wording; the CLI validates and stores it but does not contain its own vision model.\n- Use `decorative:true` only when the image adds no information. This intentionally stores an empty description so later audits do not report it as missing.\n- Re-run the accessibility audit after the update. Asset-backed Image components use the saved asset description as their default alt text.\n\n## Manage fonts\n\nCommands:\n\n- MCP tool: list-fonts {"includeSystem":true}\n- MCP tool: list-assets {"type":"font"}\n- MCP tool: upload-asset {"asset":{"name":"acme-sans.woff2","type":"font","format":"woff2","meta":{"family":"Acme Sans","style":"normal","weight":400}},"assetsDir":".webstudio/assets"}\n- MCP tool: update-styles {"updates":"styles.json contents"}\n\nNotes:\n\n- Use `list-fonts` to discover uploaded families and system stacks. Upload/delete fonts through the existing asset tools, then apply a family with a `font-family` style declaration.\n\n## Upload one asset\n\nCommands:\n\n- MCP tool: upload-asset {"asset":{"name":"image.png","type":"image","format":"png","meta":{"width":1200,"height":630}},"assetsDir":".webstudio/assets"}\n- MCP tool: upload-asset {"asset":{"name":"image.png","type":"image","format":"png","folderId":"<folderId>","meta":{"width":1200,"height":630}},"assetsDir":".webstudio/assets"}\n\n## Upload asset batch\n\nCommands:\n\n- MCP tool: upload-assets {"assets":[{"name":"image.png","type":"image","format":"png","meta":{"width":1200,"height":630}}],"assetsDir":".webstudio/assets"}\n\n## Duplicate asset\n\nCommands:\n\n- MCP tool: duplicate-asset {"assetId":"<assetId>"}\n- MCP tool: duplicate-asset {"assetId":"<assetId>","folderId":"<targetFolderId>"}\n- MCP tool: duplicate-asset {"assetId":"<assetId>","folderId":null}\n\nNotes:\n\n- Duplication is the asset equivalent of copy and paste. Updating `folderId` is the equivalent of cut and paste; use `null` for Root.\n\n## Download asset\n\nCommands:\n\n- MCP tool: download-asset {"assetId":"<assetId>"}\n\n## Find asset usage\n\nCommands:\n\n- MCP tool: find-asset-usage {"assetId":"<assetId>"}\n\n## Replace asset references\n\nCommands:\n\n- MCP tool: replace-asset {"fromAssetId":"<oldAssetId>","toAssetId":"<newAssetId>"}\n\n## Delete assets\n\nCommands:\n\n- MCP tool: delete-asset {"assetIdsOrPrefixes":["<assetId>"],"force":true}\n\n## Publish project\n\nCommands:\n\n- webstudio publish deploy --target production --json\n\n## List publishes\n\nCommands:\n\n- webstudio publish list --json\n\n## Check publish job\n\nCommands:\n\n- webstudio publish status --job <buildId> --json\n\n## Unpublish\n\nCommands:\n\n- webstudio publish unpublish --target production --confirm --json\n\n## List domains\n\nCommands:\n\n- webstudio domains list --json\n\n## Create domain\n\nCommands:\n\n- webstudio domains create --domain example.com --json\n\n## Update domain\n\nCommands:\n\n- webstudio domains update --domain-id <domainId> --domain www.example.com --json\n\n## Delete domain\n\nCommands:\n\n- webstudio domains delete --domain-id <domainId> --confirm --json\n\n## Verify domain\n\nCommands:\n\n- webstudio domains verify --domain-id <domainId> --json\n\n## Make arbitrary store-level changes\n\nCommands:\n\n- MCP tool: inspect {}\n- MCP tool: snapshot {"include":["<namespace>"]}\n- MCP tool: apply-patch {"baseVersion":"<version>","transactions":"patch.json contents"}\n\nNotes:\n\n- Use only when no semantic command exists.\n\n## Manage marketplace metadata\n\nCommands:\n\n- MCP tool: get-marketplace-product {}\n- MCP tool: update-marketplace-product {"category":"pageTemplates","name":"Acme Template","thumbnailAssetId":"asset-id","author":"Acme Studio","email":"hello@example.com","website":"https://example.com","issues":"","description":"Reusable template project for Acme landing pages."}\n- MCP tool: submit-marketplace-product {"acknowledgePublicSubmission":true}\n\nPatch namespaces:\n\n- marketplaceProduct\n\n## Search and inspect safely\n\nCommands:\n\n- MCP tool: search-project {"query":"pricing"}\n- MCP tool: search-project {"query":"api.example.com","scopes":["resources"]}\n- MCP tool: list-instances {"pagePath":"/","maxDepth":5}\n- MCP tool: inspect-instance {"instanceId":"<instanceId>","include":["props","styles","children"]}\n- MCP tool: list-texts {"pagePath":"/"}\n- MCP tool: list-assets {"withUsage":true}\n- MCP tool: find-asset-usage {"assetId":"<assetId>"}\n- MCP tool: snapshot {"include":["pages","instances","props","resources","assets"]}\n\nNotes:\n\n- Use `search-project` for query-driven lookup across labels, text, prop values, resource URLs, asset metadata, and styles. Use `audit` for project health findings.\n\n## Audit project quality\n\nCommands:\n\n- webstudio audit --json\n- webstudio audit --scopes accessibility --scopes seo --json\n- webstudio audit --page-path /pricing --json\n- webstudio audit --scopes accessibility --verbose --json\n- webstudio audit --rendered --page-path /pricing --json\n- webstudio audit --rendered --route-example post=/blog/hello --json\n- webstudio audit --rendered --image-domain images.example.com --json\n- MCP tool: audit {}\n- MCP tool: audit {"scopes":["accessibility","security"],"severities":["error","warning"]}\n- MCP tool: audit {"scopes":["accessibility"],"verbose":true}\n- MCP tool: audit {"scopes":["craft"],"verbose":true}\n- MCP tool: audit {"rendered":true,"verbose":true}\n\nNotes:\n\n- With no scopes, `audit` checks accessibility, security, SEO, performance settings, unused assets, ineffective Collection styles, non-GET resources exposed as render-time data, and unused or duplicate style data.\n- Craft is opt-in and read-only. Run `audit` with `scopes:["craft"]` to detect whether the project is not using Craft, partially compatible, or compatible with the versioned Craft 1.2 profile. `profileStatuses` includes the University-doc provenance and the smallest safe next action. The audit never installs Craft or changes a non-Craft project.\n- The `performance` scope reports disabled atomic CSS generation. A rendered audit also measures broken, eager below-fold, and oversized images, browser-marked render-blocking resources, and legacy font formats.\n- Rendered image and resource metrics run only when the selected scopes include `performance`; responsive layout dimensions remain available whenever `rendered:true` is requested.\n- Compact findings include stable ids, severity, message, and location. Use `--verbose` or `{"verbose":true}` for evidence, explanation, suggested remediation, skipped-check details, and manual-check workflows.\n- `summary` counts all findings before severity filtering and pagination.\n- `contractVersion` identifies the audit response contract. Handle a new value before assuming existing fields retain the same meaning.\n- Expression-, resource-, and parameter-backed values that cannot be checked reliably appear in `skippedChecks`; they are not treated as passing or failing.\n- Page filters apply to page-owned accessibility, security, and SEO checks. Asset and style usage remain project-wide to avoid false unused findings.\n- Continue paginated results with `cursor`. Restart the audit if the project version changes.\n- Verbose skipped-check and manual-check details are included on the first findings page only; their total counts remain available on every page.\n- `manualChecks` describes responsive, hierarchy, and contrast checks that require preview screenshots and vision.\n- Focused audits return only manual checks relevant to their selected scopes.\n- In a long-lived MCP session, `{"rendered":true}` reuses preview and screenshot\n tools to capture every static page at mobile, desktop, and Builder breakpoint\n edges. Compact output reports rendered check/issue/failure counts; verbose\n output includes screenshot paths and measured layout dimensions.\n- Dynamic route templates are skipped unless `--route-example <pageId>=<path>`\n (or MCP `routeExamples`) supplies a concrete path. The path must not contain\n unresolved `:` or `*` parameters.\n- Plans above 120 captures return a short-lived confirmation token. Review the\n unchanged plan, then rerun with `--confirm-large-run` and\n `--confirmation-token`.\n- Detailed rendered evidence is stored in a versioned manifest under\n `.webstudio/audits`; compact output includes its path and screenshot count.\n- Rendered checks also report broken images, eager images below the fold, and\n image sources more than 2x their rendered dimensions in both axes, including\n Webstudio instance ids and measured dimensions when available.\n- Rendered checks include sanitized Resource Timing evidence and report\n browser-marked render-blocking resources plus legacy `.ttf`, `.otf`, and\n `.woff` fonts without applying a universal transfer-size threshold.\n- Fix findings through semantic mutation commands, then rerun `audit` to confirm their deterministic finding ids disappeared.\n\n## Verify dynamic bindings\n\nCommands:\n\n- webstudio verify-bindings --json\n- MCP tool: verify-bindings {"pagePath":"/pricing"}\n- MCP tool: verify-bindings {"instanceId":"<instanceId>","limit":50}\n\nNotes:\n\n- Statically checks persisted text expressions, expression/action/resource/parameter props, resource expressions, and page metadata.\n- Findings distinguish invalid syntax, unknown or out-of-scope variables, stale internal data-source ids, and missing resource or parameter references.\n- Page and instance filters can be combined when the instance belongs to the selected page. Continue findings with `cursor`.\n- This operation does not resolve rendered values or execute external resources. Preview representative loading, empty, error, and populated states after static findings are fixed.\n\n## Refactor targeted content\n\nCommands:\n\n- MCP tool: list-instances {"pagePath":"/"}\n- MCP tool: list-texts {"pagePath":"/"}\n- MCP tool: update-text {"instanceId":"<instanceId>","childIndex":0,"text":"Launch faster"}\n- MCP tool: replace-text {"find":"Old headline","replace":"New headline","match":"exact","pagePath":"/pricing","limit":20}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"title":"Pricing","meta":{"description":"Plans"}}}\n- MCP tool: update-resource {"resourceId":"<resourceId>","values":{"url":"https://api.example.com/posts"}}\n- MCP tool: replace-asset {"fromAssetId":"<oldAssetId>","toAssetId":"<newAssetId>"}\n- MCP tool: replace-styles {"property":"color","fromValue":{"type":"keyword","value":"red"},"toValue":{"type":"keyword","value":"blue"}}\n- MCP tool: rewrite-css-variable-refs {"map":"variables.json contents"}\n\nNotes:\n\n- Use focused reads first, then mutate only matching instances, props, metadata, resource URLs, assets, or style references. Use `replace-text` for bounded literal text changes, `replace-prop-text` for bounded static prop text, `replace-resource-text` for fixed resource names/URLs, and `update-text` for one known child or expressions.\n\n## Optimize existing project\n\nCommands:\n\n- MCP tool: list-pages {}\n- MCP tool: list-folders {}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"title":"Pricing","meta":{"description":"Plans"}}}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: list-breakpoints {}\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"maxWidth":1023}}\n- MCP tool: get-styles {"instanceIds":["<instanceId>"],"includeTokens":true}\n- MCP tool: update-styles {"updates":"styles.json contents"}\n- MCP tool: attach-design-token {"designTokenId":"<tokenId>","instanceIds":"instances.json contents"}\n- MCP tool: update-project-settings {"meta":{"siteName":"Acme"}}\n\nNotes:\n\n- Use this for SEO metadata, accessibility labels, responsive behavior, token consistency, and project settings.\n\n## Connect external data\n\nCommands:\n\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"title","value":{"type":"string","value":"Hello"}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"tags","value":{"type":"json","value":["news","product"]}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"filters","value":{"type":"json","value":{"tag":"news"}}}\n- MCP tool: create-resource {"resource":{"name":"Posts","method":"get","url":"https://api.example.com/posts","searchParams":[{"name":"tag","value":"filters.tag"},{"name":"source","value":{"type":"literal","value":"website"}}],"headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"posts"}\n- MCP tool: create-resource {"resource":{"name":"Post GraphQL","control":"graphql","method":"post","url":"https://api.example.com/graphql","headers":[{"name":"Content-Type","value":{"type":"literal","value":"application/json"}}],"body":"{ query: \\"query Post($slug: String!) { post(slug: $slug) { title } }\\", variables: { slug: system.params.slug } }"},"scopeInstanceId":"<instanceId>","dataSourceName":"post"}\n- MCP tool: create-resource {"resource":{"name":"Current Date","control":"system","method":"get","url":"/$resources/current-date","headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"currentDate"}\n- MCP tool: update-resource {"resourceId":"<resourceId>","values":{"url":"https://api.example.com/posts"}}\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n- MCP tool: insert-fragment {"parentInstanceId":"<instanceId>","fragment":"<ws.collection>{/_ collection content _/}</ws.collection>"}\n\nNotes:\n\n- Use this for CMS sections, blog listings, Ghost/headless CMS pages, n8n-style integrations, and API URLs built from variables.\n- For read data, expose GET resources as scoped data variables with `scopeInstanceId`/`dataSourceName` and read the loaded result from the resource result wrapper, usually `.data`.\n- For writes, webhooks, GraphQL submissions, and deletes, prefer unscoped resources bound to Form `action` props so they become action resources instead of auto-loaded read resources.\n- Use direct props for fixed values and prop bindings only when a prop must read a data variable, resource, action, or documented runtime context value such as `system`.\n\n## Render an array or object as repeated content\n\nCommands:\n\n- MCP tool: insert-collection {"parentInstanceId":"<instanceId>","data":{"type":"expression","value":"posts.data.items"},"itemFragment":"<ws.element ws:tag=\\"article\\"><ws.element ws:tag=\\"h2\\">{expression`collectionItem.title`}</ws.element></ws.element>"}\n- MCP tool: inspect-instance {"instanceId":"<collectionId>","include":["props","bindings","children"]}\n\nNotes:\n\n- Use Collection whenever an array or object from a resource or data variable should render a list, grid, cards, table rows, options, tabs, or other repeated UI.\n- Pass `insert-collection` the complete array or object. Do not pass the resource response wrapper or one indexed item. External resource arrays are commonly nested under the scoped resource result\'s `data` field or deeper.\n- Pass one repeated-item Webstudio JSX root. The command creates the Collection, its private current-item/current-key parameters, the iterable binding, and descendant item bindings atomically.\n- Collection renders the item root once for every entry. Use `expression` values such as `collectionItem.title` in descendant text and props. Object iteration also exposes `collectionItemKey`.\n- Wrap multiple repeated sibling instances in one Element inside Collection.\n- For repeated Radix items such as accordion items, tabs, or menu options, bind a stable unique id or slug to every required `value` prop.\n- See the [Collection documentation](https://docs.webstudio.is/university/core-components/collection) for the equivalent Builder workflow.\n\n## Support dynamic runtime behavior\n\nCommands:\n\n- MCP tool: integrate-runtime-ui {"parentInstanceId":"<instanceId>","resources":[{"resource":{"name":"Seats","method":"get","url":"https://api.example.com/seats","headers":[]},"dataSourceName":"Seats","exposeAsDataSource":true}],"structure":{"type":"collection","data":{"type":"expression","value":"Seats.data"},"itemFragment":{"children":[{"type":"id","value":"seat"}],"instances":[{"type":"instance","id":"seat","component":"Text","children":[{"type":"expression","value":"collectionItem.label"}]}],"props":[],"dataSources":[],"resources":[],"styleSources":[],"styleSourceSelections":[],"styles":[],"breakpoints":[],"assets":[]}},"retainedBehavior":[{"instanceId":"<scriptInstanceId>","responsibility":"Seat selection behavior"}]}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n- MCP tool: create-resource {"resource":{"name":"Seats","method":"get","url":"https://api.example.com/seats","headers":[]}}\n- MCP tool: snapshot {"include":["instances","props","resources"]}\n- MCP tool: apply-patch {"baseVersion":"<version>","transactions":"patch.json contents"}\n\nNotes:\n\n- Use `integrate-runtime-ui` to create variables/resources, insert one editable fragment or Collection, and add safe data bindings in one transaction.\n- List existing script-owned responsibilities under `retainedBehavior`. The operation preserves those instances and never evaluates or accepts replacement script bodies.\n- `unsupportedConversions` records behavior that cannot be represented safely. Dry-run returns the complete transaction and the same retained/unsupported report without changing the project.\n- New actions and HtmlEmbed scripts are intentionally rejected. Create normal editable components and data bindings; keep opaque runtime behavior in existing script instances.\n\n## Build authenticated pages\n\nCommands:\n\n- MCP tool: meta.guide {"brief":"Build a Supabase-authenticated account page"}\n- MCP tool: create-page {"name":"Account","path":"/account"}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"meta":{"auth":{"method":"basic","login":"<login>","password":"<password>"}}}}\n- MCP tool: create-resource {"resource":{"name":"Session","method":"get","url":"https://api.example.com/session","headers":[]}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"user","value":{"type":"json","value":{}}}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n\nNotes:\n\n- Inspect and reuse the project\'s existing auth convention before authoring. Do\n not add a second provider or session model implicitly.\n- Model signed-out, loading, signed-in, and failed-auth states explicitly.\n- Never store credentials, service-role keys, refresh tokens, private session\n values, or authenticated response bodies in project data, command output,\n screenshots, agent instructions, or error reports. Privileged provider calls\n and authorization enforcement belong server-side.\n- Basic auth is semantic today. Provider-specific Supabase/Firebase setup still\n uses the existing resource, variable, prop, binding, and embed tools; there is\n no provider-specific installer.\n\n## Generate from design input\n\nCommands:\n\n- MCP tool: meta.guide {"brief":"Recreate this Figma design as a responsive page"}\n- MCP tool: create-page {"name":"Landing","path":"/landing"}\n- MCP tool: create-design-token {"tokens":"tokens.json contents"}\n- MCP tool: define-css-variable {"vars":"vars.json contents"}\n- MCP tool: list-breakpoints {}\n- MCP tool: insert-fragment {"parentInstanceId":"<instanceId>","fragment":"<ws.element ws:tag=\\"section\\"><ws.element ws:tag=\\"p\\">Section copy</ws.element></ws.element>"}\n- MCP tool: update-styles {"updates":[{"instanceId":"<instanceId>","breakpointId":"<breakpointId-from-list-breakpoints>","property":"padding-left","value":{"type":"unit","unit":"px","value":24}}]}\n- MCP tool: preview.start {"host":"127.0.0.1","port":5173}\n- MCP tool: screenshot {"path":"/landing","output":"landing-desktop.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"path":"/landing","output":"landing-mobile.png","viewport":{"width":390,"height":844},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"baseUrl":"http://127.0.0.1:5177","path":"/landing","output":"landing-desktop.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n\nNotes:\n\n- Use this after the agent can inspect the supplied design. There is no direct\n Figma, screenshot, Inception, or `design.md` import command.\n- Inspect and reuse existing variables, tokens, styles, components, assets, and\n page patterns before authoring. Build semantic editable structure rather than\n flattening the design into an image or absolute-positioned approximation.\n- Verify one familiar viewport inside every distinct Builder breakpoint range,\n then run rendered audit and inspect the screenshots before completion.\n\n## Cross-project maintenance\n\nCommands:\n\n- webstudio mcp run .temp/projects.json\n- webstudio mcp run .temp/projects.json --dry-run\n- webstudio mcp run .temp/projects.json --approve-mutations --concurrency 2\n\nNotes:\n\n- Put shared `calls` and a `projects` array of independently linked project roots in the existing `mcp run` manifest. Project roots are relative to the manifest file.\n- Each project uses its own config, authentication, ProjectSession storage, checkpoint, and failure boundary. Confirmed successful calls are checkpointed. Reads can resume automatically; a mutation interrupted after dispatch is reported as ambiguous and is not replayed automatically, preventing silent duplicate writes.\n- Focus the manifest on bounded reads or audits first. Use per-call `dryRun`, global `--dry-run`, or explicitly approve committed mutations with `--approve-mutations` after reviewing the manifest.\n\n# Known CLI Gaps\n\n## Provider-specific authenticated pages\n\nMissing:\nCLI supports page basic auth and generic resources/props/embeds, but not guided Supabase/Firebase auth setup.\n\nCurrent fallback:\nCall `meta.guide` with the provider-authenticated page goal, then create the\npage, resources, variables, props, bindings, and embeds with existing semantic\ntools.\n\nSuggested commands:\n\n- setup-auth-page\n\n## Generate from design input\n\nMissing:\nNo command imports Figma, screenshots, Inception output, or design.md and turns it into pages/tokens/layout.\n\nCurrent fallback:\nCall `meta.guide` with the design-input goal, let the agent inspect the supplied\ndesign, then use semantic page, token, asset, fragment, style, preview,\nscreenshot, and audit tools. Use `apply-patch` only when no semantic operation\nfits.\n\nSuggested commands:\n\n- generate-from-design\n\n## Built-in cross-project maintenance\n\nMissing:\nPublic API and CLI intentionally operate on one configured project at a time; there is no built-in multi-project discovery or loop runner.\n\nCurrent fallback:\nRun the CLI from an external script that reconfigures one project/session at a time.\n\nSuggested commands:\n\n- none\n',
|
|
258979
|
+
"api-use-cases": '# CLI API Use Cases\n\n## Link/configure one project\n\nCommands:\n\n- webstudio init --link <api-share-link> --json\n\nNotes:\n\n- Writes local project id and global origin/token config.\n\n## Import synced project bundle into another project\n\nCommands:\n\n- webstudio sync\n- webstudio import --to <destination-share-link>\n- MCP tool: import {"to":"<destination-share-link>"}\n\nNotes:\n\n- Imports local `.webstudio/data.json` into the destination project.\n- Destination share link must allow build/import access.\n- Use `--skip-assets` only when asset rows and files should not be imported.\n\n## Identify current token\n\nCommands:\n\n- MCP tool: whoami {}\n\n## Check token permissions\n\nCommands:\n\n- webstudio permissions --json\n\n## Inspect project/build/version\n\nCommands:\n\n- MCP tool: inspect {}\n- MCP tool: snapshot {"include":["pages","instances","styles"]}\n\n## Discover CLI/API capabilities\n\nCommands:\n\n- webstudio schema api\n- webstudio schema mcp\n- webstudio man --json\n- webstudio man llm --json\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"Create a pricing page"}\n- MCP tool: meta.get-more-tools {"brief":"update-styles"}\n- webstudio mcp list-resources\n- webstudio mcp read-resource webstudio://project/guide\n- webstudio mcp read-resource webstudio://project/expressions\n\nNotes:\n\n- Use `webstudio schema mcp` for a compact machine-readable MCP tool overview. Add `--verbose` or use focused `meta.get-more-tools` calls only when exact input schemas are needed.\n- Use focused MCP tools for discovery first: `meta.index`, `meta.guide`, `meta.get-more-tools`, `components.list`, `components.summary`, `components.search`, `components.get`, `templates.list`, and `templates.get`. Protocol clients can use `resources/list` and `resources/read`; shell agents can use `webstudio mcp list-resources` and `webstudio mcp read-resource <uri>`. Read longer resources such as `webstudio://project/tools` and `webstudio://project/components` only when focused tools are insufficient.\n- `components.summary` returns counts by default; request `{"detail":"components","limit":20}` for paginated entries. Registry list tools return compact metadata, while `components.get` and `templates.get` return focused full details.\n- Read `webstudio://project/expressions` before authoring unfamiliar computed text, prop bindings, resource expressions, actions, or Collection item bindings.\n- From a shell, call one MCP tool with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio components.summary`. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `--input-file` for large payloads.\n\n## Inspect external shadcn registry items\n\nCommands:\n\n- webstudio registry inspect --source https://example.com/r/registry.json --item button --json\n- webstudio registry inspect --source ./registry.json --item dialog --json\n- webstudio registry inspect --source https://example.com/r/button.json --json\n\nNotes:\n\n- Reads a local or remote registry item without installing files or changing the configured Webstudio project.\n- Returns the item name, description, package and registry dependencies, file paths/targets, available docs, and a read-only compatibility report.\n- The report explicitly says whether installation or editable-component conversion is supported, lists declared requirements and manual steps, and says when arbitrary source code was not analyzed.\n- This is an inspection step only. It does not install files or change the configured project.\n\n## Understand what MCP can do\n\nCommands:\n\n- webstudio man mcp\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"What can Webstudio MCP do?"}\n\nMCP lets agents work on one configured Webstudio project. Agents can:\n\n- Inspect the linked project, token permissions, and latest editable build.\n- Read selected project data for audits, migrations, and repair.\n- Search labels, text, props, resource URLs, asset metadata, and styles.\n- Audit accessibility, security, SEO, performance settings, unused assets, ineffective Collection styles, and unused or duplicate style data.\n- Create and edit pages, folders, redirects, breakpoints, and page templates.\n- Create pages from reusable templates.\n- Update page metadata, SEO fields, auth settings, and marketplace metadata.\n- Insert components and styled JSX sections.\n- Create data-driven lists, grids, cards, and similar repeated UI from array or object data in one Collection operation.\n- Move, copy, wrap, unwrap, convert, rename, retag, and delete elements.\n- Update text, rich text, props, bindings, and actions.\n- Create and update local styles, design tokens, style sources, and CSS variables.\n- Create static data variables and JSON variables.\n- Create HTTP, GraphQL, and system resources.\n- Use system resources for sitemap, current date, and assets.\n- Bind resources to rendered data or form/action props.\n- Manage nested asset folders and upload, inspect, move, duplicate, download, replace, delete, and inspect usage for assets.\n- Publish, unpublish, inspect publish jobs, and manage custom domains.\n- Start preview, capture screenshots, compare screenshot diffs, and use OCR when installed.\n\n## Inspect and refresh MCP session cache\n\nCommands:\n\n- MCP tool: status {}\n- MCP tool: status {"verbose":true}\n- MCP tool: refresh {"namespaces":["pages","instances","styles"]}\n- MCP tool: reset-session {}\n\nNotes:\n\n- Use status before a task to understand the cached ProjectSession state.\n- Use status with `{"verbose":true}` only when debugging full namespaces, freshness, compatibility, or diagnostics.\n- Use refresh when project data may have changed outside the current MCP session.\n- Use reset-session when local cached state is corrupt or incompatible.\n\n## Visually verify rendered work with AI vision\n\nCommands:\n\n- MCP tool: preview.start {"host":"127.0.0.1","port":5173}\n- MCP tool: preview.status {}\n- MCP tool: preview.stop {}\n- MCP tool: screenshot {"path":"/","output":".webstudio/screenshots/home-current.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"path":"/pricing","output":".webstudio/screenshots/pricing-current.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"baseUrl":"http://127.0.0.1:5177","path":"/pricing","output":".webstudio/screenshots/pricing-current.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/home-before.png","currentPath":".webstudio/screenshots/home-current.png","outputDir":".webstudio/screenshots/diff"}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/home-before.png","currentPath":".webstudio/screenshots/home-current.png","outputDir":".webstudio/screenshots/diff","expectedText":["Pricing","Start free"]}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/home-before.png","currentPath":".webstudio/screenshots/home-current.png","outputDir":".webstudio/screenshots/diff","expectedVisual":{"maxMismatchPercentage":2,"maxChangedRegions":3,"dominantColorChange":{"channel":"luminance","direction":"increase","minMagnitude":10}}}\n- MCP tool: screenshot.diff {"baselinePath":".webstudio/screenshots/pricing-before.png","currentPath":".webstudio/screenshots/pricing-current.png","outputDir":".webstudio/screenshots/diff"}\n- MCP tool: vision.install-ocr {"confirm":true}\n\nNotes:\n\n- Use this after page/content/style mutations so a vision-capable AI can see the generated site from the current MCP session. Use `path`; never pass a Webstudio Builder/share URL or capture Builder chrome.\n- For multi-page work, capture every changed page by `path` through the same preview server; no click navigation is required.\n- Iterative mode is the default: after MCP mutations, path screenshots ensure generated project files are current, wait for the exact session version, and perform an ordinary page reload without Vite HMR. The preview server and browser stay alive. Use `{"mode":"production"}` only for release-like verification; rendered audit does this automatically.\n- Do not call `preview.start` through one-shot `webstudio mcp single-op-call`: it is long-lived. From a shell, use `webstudio mcp run` with preview.start, screenshot, and preview.stop in one shared process, or use a real long-running MCP client.\n- From one-shot shell calls or another process, pass `baseUrl` with `path` to capture an already-running preview/site without generating, building, starting, or restarting preview.\n- Use preview.stop only in the same long-running MCP server or `webstudio mcp run` process that started preview. A separate one-shot `single-op-call` process does not own another process\'s preview controller.\n- Use waitForSelector when the rendered app has a reliable ready marker, waitUntil:"networkidle" for network-heavy pages, and waitForTimeout only for final visual settling.\n- Preview installs generated app dependencies under `.webstudio/preview` and reuses them across regenerations.\n- Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.\n- If dependency installation fails, check npm and network configuration, then reinstall or update the Webstudio CLI if the problem persists.\n- When a baseline exists, use screenshot.diff once per baseline/current page or viewport pair to get changed regions, OCR textAnalysis, and diff artifact paths before deciding whether the result matches. Pass expectedText for explicit pass/fail current-screen text assertions with found and missing text. Pass expectedVisual for pass/fail limits on pixel mismatch percentage, changed-region count, or the overall dominant color/brightness direction.\n- If screenshot.diff reports OCR unavailable and the user agrees to install it, call vision.install-ocr {"confirm":true}; otherwise continue with pixel diff and visual inspection.\n- Compare the PNG, OCR text evidence, and diff artifacts against the user\'s intent for layout, typography, colors, spacing, imagery, and responsive framing; then iterate with focused mutations.\n- Root CLI equivalent: `webstudio screenshot --path /pricing --output pricing.png` generates a temporary production preview, captures that route, and stops the server. For repeated captures, keep `webstudio preview` running and pass its absolute URL to `webstudio screenshot`.\n\n## List pages\n\nCommands:\n\n- MCP tool: list-pages {}\n- MCP tool: list-folders {}\n\n## Read page by id\n\nCommands:\n\n- MCP tool: get-page {"pageId":"<pageId>"}\n\n## Read page by path\n\nCommands:\n\n- MCP tool: get-page-by-path {"path":"/pricing"}\n\n## Create page\n\nCommands:\n\n- MCP tool: create-page {"name":"Pricing","path":"/pricing"}\n- MCP tool: create-page {"name":"Pricing","path":"/pricing","title":"Pricing","meta":{"description":"Plans for teams"}}\n\nNotes:\n\n- `name`, `path`, page `title`, and metadata text fields accept plain fixed values.\n- For computed page titles or metadata, send JavaScript expression code such as `pageTitle ?? "Pricing"`.\n\n## Update page settings/metadata\n\nCommands:\n\n- MCP tool: update-page {"pageId":"<pageId>","values":{"title":"Pricing","meta":{"description":"Plans","status":200}}}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"meta":{"auth":{"method":"basic","login":"<login>","password":"<password>"}}}}\n\nNotes:\n\n- Page `title` and metadata text fields accept plain fixed values.\n- For computed page titles or metadata, send JavaScript expression code such as `pageTitle ?? "Pricing"`.\n- Page `status` accepts a fixed HTTP status code as a number from 200 through 599 or a JavaScript expression string for a dynamic status.\n\n## Read project settings\n\nCommands:\n\n- MCP tool: get-project-settings {}\n\nNotes:\n\n- Read `meta.agentInstructions` before making project changes. It contains the project\'s own guidance for AI agents.\n- Agent instructions are shared project guidance. Do not store credentials or other secrets there.\n\n## Update project settings\n\nCommands:\n\n- MCP tool: update-project-settings {"meta":{"siteName":"Acme"}}\n- MCP tool: update-project-settings {"meta":{"agentInstructions":"Use existing design tokens and keep product copy concise."}}\n\n## Read marketplace product\n\nCommands:\n\n- MCP tool: get-marketplace-product {}\n\n## Update marketplace product\n\nCommands:\n\n- MCP tool: update-marketplace-product {"category":"pageTemplates","name":"Acme Template","thumbnailAssetId":"asset-id","author":"Acme Studio","email":"hello@example.com","website":"https://example.com","issues":"","description":"Reusable template project for Acme landing pages."}\n\n## Submit marketplace product\n\nCommands:\n\n- MCP tool: upload-asset {"asset":{"name":"marketplace-thumbnail.png","type":"image","format":"png","meta":{"width":1200,"height":630}},"assetsDir":".webstudio/assets"}\n- MCP tool: update-marketplace-product {"category":"pageTemplates","name":"Acme Template","thumbnailAssetId":"<uploadedAssetId>","author":"Acme Studio","email":"hello@example.com","website":"https://example.com","issues":"","description":"Reusable template project for Acme landing pages."}\n- MCP tool: publish {"target":"production"}\n- MCP tool: submit-marketplace-product {"acknowledgePublicSubmission":true}\n\nNotes:\n\n- Wait for the production publish to complete before submitting the product for review.\n- Submission requires complete, valid marketplace metadata.\n\n## List redirects\n\nCommands:\n\n- MCP tool: list-redirects {}\n\n## Create redirect\n\nCommands:\n\n- MCP tool: create-redirect {"old":"/old","new":"/new","status":301}\n\n## Update redirect\n\nCommands:\n\n- MCP tool: update-redirect {"old":"/old","values":{"new":"/newer","status":302}}\n- MCP tool: update-redirect {"old":"/old","values":{"status":null}}\n\n## Delete redirect\n\nCommands:\n\n- MCP tool: delete-redirect {"old":"/old"}\n\n## Set redirects\n\nCommands:\n\n- MCP tool: set-redirects {"redirects":[{"old":"/old","new":"/new","status":"301"}]}\n\n## List breakpoints\n\nCommands:\n\n- MCP tool: list-breakpoints {}\n\n## Create breakpoint\n\nCommands:\n\n- MCP tool: create-breakpoint {"label":"Tablet","maxWidth":991}\n\n## Update breakpoint\n\nCommands:\n\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"label":"Tablet","maxWidth":1023}}\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"condition":null,"minWidth":768}}\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"minWidth":null,"maxWidth":null,"condition":"(hover: hover)"}}\n\n## Delete breakpoint\n\nCommands:\n\n- MCP tool: delete-breakpoint {"breakpointId":"tablet"}\n\n## Duplicate page\n\nCommands:\n\n- MCP tool: duplicate-page {"pageId":"<pageId>","name":"Pricing Copy","path":"/pricing-copy"}\n- MCP tool: duplicate-page {"pageId":"<pageId>","name":"Paris","path":"/paris","substitutions":{"text":{"London":"Paris"},"variables":{"city":{"type":"string","value":"Paris"}}}}\n- webstudio duplicate-page --page <pageId> --name Paris --path /paris --substitutions \'{"text":{"London":"Paris"},"variables":{"city":{"type":"string","value":"Paris"}}}\' --json\n\nNotes:\n\n- Text substitutions replace exact fixed text only in the duplicated page\'s text children, string props, title, and metadata.\n- Variable substitutions are keyed by copied source-variable name and use typed variable values. The operation rejects missing or ambiguous names without committing a partial duplicate.\n- Existing expressions and cloned variable/resource references keep their remapped ids.\n\n## List page templates\n\nCommands:\n\n- MCP tool: list-page-templates {}\n\n## Create page template\n\nCommands:\n\n- MCP tool: create-page-template {"name":"Landing Template","title":"Landing"}\n\n## Update page template\n\nCommands:\n\n- MCP tool: update-page-template {"templateId":"<templateId>","values":{"name":"Article Template","meta":{"description":"Reusable article layout"}}}\n\n## Delete page template\n\nCommands:\n\n- MCP tool: delete-page-template {"templateId":"<templateId>"}\n\n## Duplicate page template\n\nCommands:\n\n- MCP tool: duplicate-page-template {"templateId":"<templateId>"}\n\n## Reorder page template\n\nCommands:\n\n- MCP tool: reorder-page-template {"sourceTemplateId":"<sourceTemplateId>","targetTemplateId":"<targetTemplateId>","position":"before"}\n\n## Create page from template\n\nCommands:\n\n- MCP tool: create-page-from-template {"templateId":"<templateId>","name":"Landing","path":"/landing"}\n\n## Delete page\n\nCommands:\n\n- MCP tool: delete-page {"pageId":"<pageId>"}\n\n## List folders\n\nCommands:\n\n- MCP tool: list-folders {}\n- MCP tool: list-pages {}\n\n## Create folder\n\nCommands:\n\n- MCP tool: create-folder {"name":"Blog","slug":"blog"}\n\n## Update folder\n\nCommands:\n\n- MCP tool: update-folder {"folderId":"<folderId>","values":{"name":"Blog","slug":"blog"}}\n\n## Delete folder\n\nCommands:\n\n- MCP tool: delete-folder {"folderId":"<folderId>"}\n\n## List element instances\n\nCommands:\n\n- MCP tool: list-instances {"pagePath":"/","maxDepth":3}\n\n## Inspect one element instance\n\nCommands:\n\n- MCP tool: inspect-instance {"instanceId":"<instanceId>","include":["props","styles","children"]}\n\n## Insert authored JSX or one component template\n\nCommands:\n\n- MCP tool: insert-fragment {"parentInstanceId":"<instanceId>","fragment":"<ws.element ws:tag=\'section\' ws:style={css`padding: 32px;`}><ws.element ws:tag=\'h2\'>Product OS</ws.element><radix.Switch><radix.SwitchThumb /></radix.Switch></ws.element>"}\n- MCP tool: insert-component {"parentInstanceId":"<instanceId>","component":"@webstudio-is/sdk-components-react-radix:Switch"}\n- MCP tool: insert-component {"parentInstanceId":"<instanceId>","component":"Form"}\n\nNotes:\n\n- Use MCP `insert-fragment` as the default way to author styled component trees. It converts JSX to a structured fragment before mutation.\n- Use only exact component ids returned by `components.search`, `components.get`, or `templates.get`. Never derive or guess component ids.\n- The `ws:` namespace contains specific Webstudio core components; it is not HTML-tag shorthand. Use `<ws.element ws:tag="div">` for a native `div` and `<ws.element ws:tag="form">` for a native form, never `<ws.div>` or `<ws.form>`.\n- For Webstudio\'s complete form structure, discover the Form component and insert its automatic template with `insert-component` using component `"Form"`.\n- MCP receives JSX as a JSON string because MCP arguments are JSON. The CLI converts it locally before the runtime mutation, so the project session receives structured Webstudio data, not JSX source.\n- In `insert-fragment` JSX, use ``ws:style={css`...`}`` for Webstudio-native CSS, or use React-style object syntax such as `style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.\n- Do not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`.\n- Use Webstudio prop names such as `class` and `for`; do not use React aliases `className` or `htmlFor`.\n- Use Webstudio actions for event/action props, for example `onClick={new ActionValue(["event"], expression\\`console.log(event)\\`)}`. Do not pass JavaScript functions such as `onClick={() => ...}`.\n- Plain prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.\n- Template-backed components used in JSX must include required child/part components explicitly under the same parent structure as the template, for example `<radix.Switch><radix.SwitchThumb /></radix.Switch>`.\n- Webstudio applies a registered template automatically when using `insert-component`, so composed components such as Switch include required child parts and styles.\n- Use `components.list`, `components.summary`, `components.search`, `components.get`, `templates.list`, and `templates.get` to discover known registry items, component ids, props, templates, insertability, and content model. Read `webstudio://project/components` only when those focused tools are insufficient.\n- Component/template registry items use a shadcn-compatible top-level shape plus Webstudio-specific superset metadata in `meta`. They are for Builder/MCP discovery, not a published shadcn install registry yet.\n- Known components with `contentModel.category: "none"` are not standalone-insertable; insert their root component template instead so required providers/parents are included.\n- Unknown custom component ids are a low-level extension mechanism, not a discovery fallback. Agents must not synthesize them.\n\n## Make a region editable in Content mode\n\nCommands:\n\n- MCP tool: insert-component {"parentInstanceId":"<instanceId>","component":"ws:block"}\n- MCP tool: inspect-instance {"instanceId":"<instanceId>","include":["children"]}\n\nNotes:\n\n- When a page will be handed to a Content-mode editor, wrap every region they should be able to edit in a Content Block (`ws:block`). Content-mode editors can edit text and supported props only in Content Block descendants. Content outside those blocks remains read-only, even when it looks like ordinary editable text.\n- Put reusable insertable options inside the Content Block\'s `ws:block-template` child. A template is source material, not editor content: editors cannot edit or delete it directly. When an editor inserts a template, its copy becomes a direct child of the Content Block and is editable.\n- Before handing off a page, verify with `inspect-instance` that the intended text, images, and links are inside a Content Block, and that templates include all required styling because Content-mode editors cannot use the Style panel.\n\n## Move elements\n\nCommands:\n\n- MCP tool: move-instance {"moves":"moves.json contents"}\n\nNotes:\n\n- Use `position: "end"` to append an instance. Repeating this for A and then B preserves the final order A, B.\n- A numeric `insertIndex` addresses the target parent\'s children before the moved instance is removed. Use it for exact placement; do not calculate the last index to append.\n- Moves in one `moves` array are applied sequentially in array order.\n\n## Clone element subtree\n\nCommands:\n\n- MCP tool: clone-instance {"sourceInstanceId":"<instanceId>","targetParentInstanceId":"<targetParentId>"}\n\n## Delete element subtree\n\nCommands:\n\n- MCP tool: delete-instance {"instanceIds":["<instanceId>"]}\n\n## List text/expression children\n\nCommands:\n\n- MCP tool: list-texts {"pagePath":"/"}\n\n## Update text child\n\nCommands:\n\n- MCP tool: update-text {"instanceId":"<instanceId>","childIndex":0,"text":"Launch faster"}\n\n## Replace bounded literal text\n\nCommands:\n\n- MCP tool: replace-text {"find":"Start free","replace":"Get started","match":"exact","pagePath":"/pricing","limit":20}\n\nNotes:\n\n- This changes only literal text children, never expression children. Scope it to pagePath or pageId and set a limit before a broad replacement.\n\n## Replace bounded static prop text\n\nCommands:\n\n- MCP tool: replace-prop-text {"find":"old.example.com","replace":"www.example.com","match":"substring","names":["href","code"],"limit":20}\n\nNotes:\n\n- This changes only static string props such as href, alt, aria-label, title, and HTML embed code. It never changes expressions, resources, actions, assets, or other dynamic bindings. Use names or instanceIds and a limit to narrow the change.\n\n## Replace bounded resource text\n\nCommands:\n\n- MCP tool: replace-resource-text {"find":"api.old.example.com","replace":"api.example.com","fields":["url"],"limit":20}\n\nNotes:\n\n- This changes resource names and fixed URL literals only. It skips dynamic URL expressions, headers, search parameters, request bodies, and GraphQL query code.\n\n## Update props\n\nCommands:\n\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: replace-prop-text {"find":"Old label","replace":"New label","names":["aria-label","title"],"limit":20}\n\nNotes:\n\n- Use this for fixed prop values such as `aria-label`, `alt`, `id`, static `href`, and other direct string/number/boolean/json prop values.\n\n## Add JSON-LD structured data\n\nCommands:\n\n- MCP tool: components.get {"component":"JsonLd"}\n- MCP tool: insert-component {"parentInstanceId":"<headSlotInstanceId>","component":"JsonLd"}\n- MCP tool: update-props {"updates":[{"instanceId":"<jsonLdInstanceId>","name":"code","type":"string","value":"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Organization\\",\\"name\\":\\"Acme\\"}"}]}\n- MCP tool: audit {"scopes":["seo"],"pagePath":"/"}\n\nNotes:\n\n- Prefer placing `JsonLd` inside `HeadSlot`.\n- Store `code` as a JSON object or array encoded as a compact string. The Builder formats it for editing.\n- The semantic prop update rejects malformed JSON and structurally invalid fixed JSON-LD with a precise JSON path.\n- The SEO audit also warns about a missing top-level `@context`, unknown or superseded Schema.org terms, properties unsupported by the supplied type, and incompatible primitive value types.\n- Schema.org vocabulary findings are warnings because custom vocabularies and extensions remain valid. Dynamic JSON-LD is marked as skipped for rendered validation.\n- Do not use bindings just to set static text.\n\n## Delete props\n\nCommands:\n\n- MCP tool: delete-props {"deletions":"props.json contents"}\n\n## Bind props to expressions/resources/actions\n\nCommands:\n\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n\nNotes:\n\n- Use this only when the prop should remain dynamic: expression, resource, action, or an existing scoped runtime context value such as `system`.\n- For a fixed string value, use `update-props` with `type:"string"` and a direct `value` instead.\n\n## Read styles\n\nCommands:\n\n- MCP tool: get-styles {"instanceIds":["<instanceId>"],"includeTokens":true}\n\n## Update local styles\n\nCommands:\n\n- MCP tool: update-styles {"updates":"styles.json contents"}\n\n## Delete local styles\n\nCommands:\n\n- MCP tool: delete-styles {"deletions":"styles.json contents"}\n\n## Replace matching style values\n\nCommands:\n\n- MCP tool: replace-styles {"property":"color","fromValue":{"type":"keyword","value":"red"},"toValue":{"type":"keyword","value":"blue"}}\n\n## List design tokens\n\nCommands:\n\n- MCP tool: list-design-tokens {}\n- MCP tool: list-design-tokens {"withUsage":true}\n- MCP tool: list-design-tokens {"verbose":true}\n\nNotes:\n\n- The default response is compact and includes token id, name, declaration count, and optional usage count.\n- Use `verbose:true` only when you need the full inline style declarations.\n\n## Create design tokens\n\nCommands:\n\n- MCP tool: create-design-token {"tokens":"tokens.json contents"}\n\n## Update design token styles\n\nCommands:\n\n- MCP tool: update-design-token-styles {"designTokenId":"<tokenId>","updates":"styles.json contents"}\n\n## Delete design token styles\n\nCommands:\n\n- MCP tool: delete-design-token-styles {"designTokenId":"<tokenId>","deletions":"styles.json contents"}\n\n## Attach design token to instances\n\nCommands:\n\n- MCP tool: attach-design-token {"designTokenId":"<tokenId>","instanceIds":"instances.json contents"}\n\n## Detach design token from instances\n\nCommands:\n\n- MCP tool: detach-design-token {"designTokenId":"<tokenId>","instanceIds":"instances.json contents"}\n\n## Extract design token from local styles\n\nCommands:\n\n- MCP tool: extract-design-token {"instanceIds":["<instanceId>"],"name":"Brand Primary","removeLocalProps":["color"]}\n\n## List CSS variables\n\nCommands:\n\n- MCP tool: list-css-variables {"withUsage":true}\n\n## Define CSS variables\n\nCommands:\n\n- MCP tool: define-css-variable {"vars":"vars.json contents"}\n\n## Delete CSS variables\n\nCommands:\n\n- MCP tool: delete-css-variable {"names":"names.json contents","force":true}\n\n## Rewrite CSS variable references\n\nCommands:\n\n- MCP tool: rewrite-css-variable-refs {"map":"variables.json contents"}\n\n## List data variables\n\nCommands:\n\n- MCP tool: list-variables {}\n- MCP tool: list-variables {"scopeInstanceId":"<instanceId>"}\n\nNotes:\n\n- Data variables live in the internal `dataSources` namespace.\n- For raw `snapshot`, request the public `variables` namespace rather than the internal `dataSources` name. Raw patch payloads still use `dataSources` when applying direct changes.\n- Scope variables to the instance where they should become available. Descendants can use them in expressions, and nested variables with the same name mask outer variables.\n\n## Create data variable\n\nCommands:\n\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"title","value":{"type":"string","value":"Hello"}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"count","value":{"type":"number","value":3}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"featured","value":{"type":"boolean","value":true}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"tags","value":{"type":"json","value":["news","product"]}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"filters","value":{"type":"json","value":{"tag":"news"}}}\n\nNotes:\n\n- Data variable values support `string`, `number`, `boolean`, and `json`. Use `json` for all arrays and objects.\n- Parameters are internal scoped runtime values provided by pages, collections, or components. They are not a public authoring surface: do not create, update, or delete parameter records. Use data variables/resources for user-authored data, and reference documented context values such as `system` only where they are already in scope.\n\n## Update data variable\n\nCommands:\n\n- MCP tool: update-variable {"dataSourceId":"<variableId>","values":{"value":{"type":"json","value":{"count":1}}}}\n\n## Delete data variable\n\nCommands:\n\n- MCP tool: delete-variable {"dataSourceId":"<variableId>"}\n\n## List resources\n\nCommands:\n\n- MCP tool: list-resources {}\n- MCP tool: list-resources {"scopeInstanceId":"<instanceId>"}\n\n## Create resource\n\nCommands:\n\n- MCP tool: create-resource {"resource":{"name":"Posts","method":"get","url":"https://api.example.com/posts","headers":[]}}\n- MCP tool: create-resource {"resource":{"name":"Posts","method":"get","url":"\'https://api.example.com/posts?tag=\' + filters.tag","headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"posts"}\n- MCP tool: create-resource {"resource":{"name":"Filtered Posts","method":"get","url":"https://api.example.com/posts","searchParams":[{"name":"tag","value":"filters.tag"},{"name":"source","value":{"type":"literal","value":"website"}}],"headers":[{"name":"Authorization","value":"\'Bearer \' + auth.token"}]},"scopeInstanceId":"<instanceId>","dataSourceName":"posts"}\n- MCP tool: create-resource {"resource":{"name":"Post GraphQL","control":"graphql","method":"post","url":"https://api.example.com/graphql","headers":[{"name":"Content-Type","value":{"type":"literal","value":"application/json"}}],"body":"{ query: \'query Post($slug: String!) { post(slug: $slug) { title } }\', variables: { slug: system.params.slug } }"},"scopeInstanceId":"<instanceId>","dataSourceName":"post"}\n- MCP tool: create-resource {"resource":{"name":"Current Date","control":"system","method":"get","url":"/$resources/current-date","headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"currentDate"}\n\nNotes:\n\n- Resource `url` accepts plain fixed URLs and paths such as `https://api.example.com/posts` and `/$resources/current-date`.\n- Resource `url` can also be a JavaScript expression when it is computed, such as `"https://api.example.com/posts?tag=" + filters.tag`.\n- Header values, search parameter values, and body accept expressions for dynamic values. For fixed text, use `{"type":"literal","value":"application/json"}`; Webstudio stores the required string expression for you.\n- Search parameter values, header values, and body expressions can read scoped variables and documented runtime context values such as `system` when they are available at the resource scope.\n- Add `scopeInstanceId` and `dataSourceName` when the resource result should be exposed as a scoped read data variable. Scoped resources are generated into the page resource `data` map and may be loaded during page rendering. Use this for read-oriented resources such as GET CMS/API data.\n- For submit/write/action resources, create the resource without `scopeInstanceId`, then bind a component prop such as a Form `action` with `bind-props` and `binding.type: "resource"`. Prop-bound resources are generated into the page resource `action` map instead of the read `data` map. Use this for POST, PUT, DELETE, webhooks, GraphQL submissions, and other explicit action flows.\n- Resource `method` can be `get`, `post`, `put`, or `delete`. Use GET for read data, POST for creates/GraphQL/webhooks/form submissions, PUT for full updates or replacements, and DELETE for deletion actions.\n- Optional `control` values are `graphql` and `system`. Use `graphql` for GraphQL-style requests, usually POST with a query body. Use `system` for built-in resources such as `"/$resources/sitemap.xml"`, `"/$resources/current-date"`, and `"/$resources/assets"` and when the resource should use the built-in `system` parameter. System fields are `system.origin`, `system.pathname`, `system.params`, and `system.search`.\n\n## Update resource\n\nCommands:\n\n- MCP tool: update-resource {"resourceId":"<resourceId>","values":{"url":"https://api.example.com/posts"}}\n- MCP tool: replace-resource-text {"find":"api.old.example.com","replace":"api.example.com","fields":["url"],"limit":20}\n\n## Query Markdown assets\n\nCommands:\n\n- MCP tool: get-asset-field-catalog {}\n- MCP tool: validate-asset-query {"query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":"md"},{"field":["properties","draft"],"operator":"ne","value":true}]},"limit":20}}\n- MCP tool: create-assets-resource {"name":"All assets","scopeInstanceId":"<instanceId>","dataSourceName":"assets"}\n- MCP tool: create-assets-resource {"name":"Published posts","scopeInstanceId":"<instanceId>","dataSourceName":"posts","query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":{"type":"literal","value":"md"}},{"field":["properties","draft"],"operator":"ne","value":{"type":"literal","value":true}}]},"sort":[{"field":["properties","publishedAt"],"direction":"desc"}],"limit":{"type":"literal","value":20},"output":{"mode":"fields","includeMetadata":false,"fields":[["properties","title"],["properties","slug"],["properties","publishedAt"],["properties","excerpt"]]},"content":{"mode":"none"}}}\n- MCP tool: create-assets-resource {"name":"Post by slug or ID","scopeInstanceId":"<instanceId>","dataSourceName":"post","query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":{"type":"literal","value":"md"}},{"field":["properties","slug"],"operator":"eq","value":"system.params.slug"}]},"limit":{"type":"literal","value":1},"output":{"mode":"fields","includeMetadata":false,"fields":[["properties","title"],["properties","publishedAt"]]},"content":{"mode":"markdown-body-ref"}}}\n- MCP tool: list-assets-resources {}\n- MCP tool: get-assets-resource {"resourceId":"<resourceId>"}\n- MCP tool: update-assets-resource {"resourceId":"<resourceId>","values":{"query":null}}\n- MCP tool: preview-asset-query {"query":{"where":{"all":[{"field":["extension"],"operator":"eq","value":"md"},{"field":["properties","slug"],"operator":"eq","value":"hello-world"}]},"limit":1,"output":{"mode":"fields","includeMetadata":false,"fields":[["properties","title"]]},"content":{"mode":"markdown-body-ref"}}}\n\nNotes:\n\n- Read the field catalog before authoring unfamiliar queries. It includes dynamic schema-less frontmatter paths such as `properties.author.name`, observed types, optionality, and mixed-type state without downloading Markdown files.\n- Minimize the deployed content database by using `output.mode:"fields"` and selecting only fields the rendered page needs. Keep `includeMetadata:false` unless the rendered value needs file metadata such as name, path, MIME type, or creation date, and avoid `output.mode:"all"` as a convenience default. Query diagnostics are returned separately and do not require metadata output. Filters and sorting may still require their referenced fields in the database.\n- Every reachable Assets data source contributes to the shared published database. Keep one final resource per rendered query. Update an existing scoped resource rather than creating a placeholder, preview copy, or repair replacement, and remove obsolete duplicates.\n- Keep bounded overview filters, limits, and offsets literal and add a deterministic ID tie-breaker to the sort. With `content.mode:"none"`, compilation can materialize the small overview result instead of retaining its output fields across every candidate document. Use runtime expressions only for genuinely dynamic values such as the detail slug.\n- Combine filters with `where.all` (AND) and `where.any` (OR), including nested groups. Filter values, limit, and offset on a saved resource may be Webstudio expressions evaluated at render time. Preview queries use concrete JSON values.\n- Query Markdown files directly and use `content.mode:"markdown-body-ref"` when rendering their bodies. The published database retains metadata and a document reference, then fetches only the selected Markdown files from Asset storage at runtime; it does not embed their bodies. Filter and paginate before content is loaded.\n- `full` and bounded `range` continue to request embedded file bytes. Use them only when the caller requires the complete source or a byte range.\n- In Markdown, reference sibling Assets with conventional relative URLs such as `../images/hero.png`. Deferred `markdown-body-ref` content resolves matching files against the Markdown file\'s folder and emits the correct Builder or published Asset URL. Keep external URLs absolute.\n- Markdown Embed renders sanitized authored HTML for figures, captions, audio, video, and iframes. Scripts, inline event handlers, `srcdoc`, and unsafe URL protocols are removed. Executable component composition remains separate future MDX work.\n- Preview each query with concrete values before saving it. Inspect `__diagnostics__.query` for the temporary query-only footprint and `__diagnostics__.database` for the merged database built from all reachable Assets queries. Only `database.usedBytes` counts toward `database.maxBytes`; query sizes must not be summed and are not separate allowances. Compare `usedBytes`, `unboundedBytes`, and `truncated` within both scopes. A completed Markdown blog should include every source document without truncation, contain no embedded Markdown bodies, and retain only its intended materialized overview query. When merged usage approaches the limit, remove duplicate reachable resources first, then unused output fields, then narrow candidate files.\n- Read Assets from the ID-keyed map at `<dataSourceName>.data`, with collection information such as `totalCount` and `hasMore` at `<dataSourceName>.meta`. Bind a listing Collection to `posts.data` and a one-result detail Collection to `post.data`. On each value, selected file fields such as `id`, `name`, and `extension` are top-level, Markdown frontmatter and JSON fields are under `properties`, and a resolved Markdown body is at `content.text`.\n- Assets has one response shape and always executes a structured query. Omit `query` to use the default query, which selects URL and optional image dimensions. Provide query configuration to control filtering, sorting, pagination, selected fields, or file content. Set `values.query:null` to restore the default query.\n- `create-assets-resource` and `update-assets-resource` are the semantic authoring path. Do not construct the internal query URL, headers, or body expression manually.\n- The shared metadata index is maintained automatically and is emitted only when a configured Assets resource is reachable.\n\n## Delete resource\n\nCommands:\n\n- MCP tool: delete-resource {"resourceId":"<resourceId>"}\n\n## List assets\n\nCommands:\n\n- MCP tool: list-assets {"withUsage":true}\n- MCP tool: list-assets {"verbose":true}\n\nNotes:\n\n- Compact results include each asset\'s folder id. Use `verbose:true` to include complete records for a page of assets, or `get-asset` to read one complete record including description, folder, creation time, and image/font metadata.\n- Image asset descriptions are the default alt text for asset-backed Image components.\n- To generate missing descriptions, inspect the image in its rendered page or asset source, write a concise description of its purpose, and save it on the asset rather than duplicating it on each Image instance.\n\n## Get asset\n\nCommands:\n\n- MCP tool: get-asset {"assetId":"<assetId>"}\n\n## List asset folders\n\nCommands:\n\n- MCP tool: list-asset-folders {}\n\n## Create asset folder\n\nCommands:\n\n- MCP tool: create-asset-folder {"name":"Marketing"}\n- MCP tool: create-asset-folder {"name":"Photos","parentId":"<parentFolderId>"}\n\n## Update asset folder\n\nCommands:\n\n- MCP tool: update-asset-folder {"folderId":"<folderId>","values":{"name":"Brand"}}\n- MCP tool: update-asset-folder {"folderId":"<folderId>","values":{"parentId":"<parentFolderId>"}}\n- MCP tool: update-asset-folder {"folderId":"<folderId>","values":{"parentId":null}}\n\nNotes:\n\n- Updating `parentId` is the folder equivalent of cut and paste. Use `null` to move a folder to Root.\n\n## Duplicate asset folder\n\nCommands:\n\n- MCP tool: duplicate-asset-folder {"folderId":"<folderId>"}\n- MCP tool: duplicate-asset-folder {"folderId":"<folderId>","parentId":"<targetFolderId>"}\n\nNotes:\n\n- Duplication recursively copies descendant folders and assets. This is the folder equivalent of copy and paste.\n\n## Delete asset folder\n\nCommands:\n\n- MCP tool: delete-asset-folder {"folderId":"<folderId>"}\n\nNotes:\n\n- Deleting a folder recursively deletes its descendant folders and assets.\n\n## Update asset metadata\n\nCommands:\n\n- MCP tool: update-asset {"assetId":"<assetId>","values":{"description":"Team collaborating around a whiteboard"}}\n- MCP tool: update-asset {"assetId":"<fontAssetId>","values":{"meta":{"family":"Rajdhani","style":"normal","weight":600}}}\n\nNotes:\n\n- Use an empty description only when the image is intentionally decorative.\n- Updating an image asset description updates the default alt text wherever that asset is used with an asset-backed alt prop.\n- Font metadata updates merge with the detected metadata and are validated before committing; use this to correct a family, style, or weight after upload.\n\n## Generate missing image descriptions with an agent\n\nCommands:\n\n- MCP tool: audit {"scopes":["accessibility"],"verbose":true}\n- MCP tool: set-image-descriptions {"updates":[{"assetId":"hero-id","description":"Team collaborating around a whiteboard"},{"assetId":"texture-id","decorative":true}]}\n- MCP tool: audit {"scopes":["accessibility"]}\n\nNotes:\n\n- Start from `missing-image-description` findings. Inspect each image in its rendered page context before writing text.\n- The vision-capable agent generates the wording; the CLI validates and stores it but does not contain its own vision model.\n- Use `decorative:true` only when the image adds no information. This intentionally stores an empty description so later audits do not report it as missing.\n- Re-run the accessibility audit after the update. Asset-backed Image components use the saved asset description as their default alt text.\n\n## Manage fonts\n\nCommands:\n\n- MCP tool: list-fonts {"includeSystem":true}\n- MCP tool: list-assets {"type":"font"}\n- MCP tool: upload-asset {"asset":{"name":"acme-sans.woff2","type":"font","format":"woff2","meta":{"family":"Acme Sans","style":"normal","weight":400}},"assetsDir":".webstudio/assets"}\n- MCP tool: update-styles {"updates":"styles.json contents"}\n\nNotes:\n\n- Use `list-fonts` to discover uploaded families and system stacks. Upload/delete fonts through the existing asset tools, then apply a family with a `font-family` style declaration.\n\n## Upload one asset\n\nCommands:\n\n- MCP tool: upload-asset {"asset":{"name":"image.png","type":"image","format":"png","meta":{"width":1200,"height":630}},"assetsDir":".webstudio/assets"}\n- MCP tool: upload-asset {"asset":{"name":"image.png","type":"image","format":"png","folderId":"<folderId>","meta":{"width":1200,"height":630}},"assetsDir":".webstudio/assets"}\n\n## Upload asset batch\n\nCommands:\n\n- MCP tool: upload-assets {"assets":[{"name":"image.png","type":"image","format":"png","meta":{"width":1200,"height":630}}],"assetsDir":".webstudio/assets"}\n\n## Duplicate asset\n\nCommands:\n\n- MCP tool: duplicate-asset {"assetId":"<assetId>"}\n- MCP tool: duplicate-asset {"assetId":"<assetId>","folderId":"<targetFolderId>"}\n- MCP tool: duplicate-asset {"assetId":"<assetId>","folderId":null}\n\nNotes:\n\n- Duplication is the asset equivalent of copy and paste. Updating `folderId` is the equivalent of cut and paste; use `null` for Root.\n\n## Download asset\n\nCommands:\n\n- MCP tool: download-asset {"assetId":"<assetId>"}\n\n## Find asset usage\n\nCommands:\n\n- MCP tool: find-asset-usage {"assetId":"<assetId>"}\n\n## Replace asset references\n\nCommands:\n\n- MCP tool: replace-asset {"fromAssetId":"<oldAssetId>","toAssetId":"<newAssetId>"}\n\n## Delete assets\n\nCommands:\n\n- MCP tool: delete-asset {"assetIdsOrPrefixes":["<assetId>"],"force":true}\n\n## Publish project\n\nCommands:\n\n- webstudio publish deploy --target production --json\n\n## List publishes\n\nCommands:\n\n- webstudio publish list --json\n\n## Check publish job\n\nCommands:\n\n- webstudio publish status --job <buildId> --json\n\n## Unpublish\n\nCommands:\n\n- webstudio publish unpublish --target production --confirm --json\n\n## List domains\n\nCommands:\n\n- webstudio domains list --json\n\n## Create domain\n\nCommands:\n\n- webstudio domains create --domain example.com --json\n\n## Update domain\n\nCommands:\n\n- webstudio domains update --domain-id <domainId> --domain www.example.com --json\n\n## Delete domain\n\nCommands:\n\n- webstudio domains delete --domain-id <domainId> --confirm --json\n\n## Verify domain\n\nCommands:\n\n- webstudio domains verify --domain-id <domainId> --json\n\n## Make arbitrary store-level changes\n\nCommands:\n\n- MCP tool: inspect {}\n- MCP tool: snapshot {"include":["<namespace>"]}\n- MCP tool: apply-patch {"baseVersion":"<version>","transactions":"patch.json contents"}\n\nNotes:\n\n- Use only when no semantic command exists.\n\n## Manage marketplace metadata\n\nCommands:\n\n- MCP tool: get-marketplace-product {}\n- MCP tool: update-marketplace-product {"category":"pageTemplates","name":"Acme Template","thumbnailAssetId":"asset-id","author":"Acme Studio","email":"hello@example.com","website":"https://example.com","issues":"","description":"Reusable template project for Acme landing pages."}\n- MCP tool: submit-marketplace-product {"acknowledgePublicSubmission":true}\n\nPatch namespaces:\n\n- marketplaceProduct\n\n## Search and inspect safely\n\nCommands:\n\n- MCP tool: search-project {"query":"pricing"}\n- MCP tool: search-project {"query":"api.example.com","scopes":["resources"]}\n- MCP tool: list-instances {"pagePath":"/","maxDepth":5}\n- MCP tool: inspect-instance {"instanceId":"<instanceId>","include":["props","styles","children"]}\n- MCP tool: list-texts {"pagePath":"/"}\n- MCP tool: list-assets {"withUsage":true}\n- MCP tool: find-asset-usage {"assetId":"<assetId>"}\n- MCP tool: snapshot {"include":["pages","instances","props","resources","assets"]}\n\nNotes:\n\n- Use `search-project` for query-driven lookup across labels, text, prop values, resource URLs, asset metadata, and styles. Use `audit` for project health findings.\n\n## Audit project quality\n\nCommands:\n\n- webstudio audit --json\n- webstudio audit --scopes accessibility --scopes seo --json\n- webstudio audit --page-path /pricing --json\n- webstudio audit --scopes accessibility --verbose --json\n- webstudio audit --rendered --page-path /pricing --json\n- webstudio audit --rendered --route-example post=/blog/hello --json\n- webstudio audit --rendered --image-domain images.example.com --json\n- MCP tool: audit {}\n- MCP tool: audit {"scopes":["accessibility","security"],"severities":["error","warning"]}\n- MCP tool: audit {"scopes":["accessibility"],"verbose":true}\n- MCP tool: audit {"scopes":["craft"],"verbose":true}\n- MCP tool: audit {"rendered":true,"verbose":true}\n\nNotes:\n\n- With no scopes, `audit` checks accessibility, security, SEO, performance settings, unused assets, ineffective Collection styles, non-GET resources exposed as render-time data, and unused or duplicate style data.\n- Craft is opt-in and read-only. Run `audit` with `scopes:["craft"]` to detect whether the project is not using Craft, partially compatible, or compatible with the versioned Craft 1.2 profile. `profileStatuses` includes the University-doc provenance and the smallest safe next action. The audit never installs Craft or changes a non-Craft project.\n- The `performance` scope reports disabled atomic CSS generation. A rendered audit also measures broken, eager below-fold, and oversized images, browser-marked render-blocking resources, and legacy font formats.\n- Rendered image and resource metrics run only when the selected scopes include `performance`; responsive layout dimensions remain available whenever `rendered:true` is requested.\n- Compact findings include stable ids, severity, message, and location. Use `--verbose` or `{"verbose":true}` for evidence, explanation, suggested remediation, skipped-check details, and manual-check workflows.\n- `summary` counts all findings before severity filtering and pagination.\n- `contractVersion` identifies the audit response contract. Handle a new value before assuming existing fields retain the same meaning.\n- Expression-, resource-, and parameter-backed values that cannot be checked reliably appear in `skippedChecks`; they are not treated as passing or failing.\n- Page filters apply to page-owned accessibility, security, and SEO checks. Asset and style usage remain project-wide to avoid false unused findings.\n- Continue paginated results with `cursor`. Restart the audit if the project version changes.\n- Verbose skipped-check and manual-check details are included on the first findings page only; their total counts remain available on every page.\n- `manualChecks` describes responsive, hierarchy, and contrast checks that require preview screenshots and vision.\n- Focused audits return only manual checks relevant to their selected scopes.\n- In a long-lived MCP session, `{"rendered":true}` reuses preview and screenshot\n tools to capture every static page at mobile, desktop, and Builder breakpoint\n edges. Compact output reports rendered check/issue/failure counts; verbose\n output includes screenshot paths and measured layout dimensions.\n- Dynamic route templates are skipped unless `--route-example <pageId>=<path>`\n (or MCP `routeExamples`) supplies a concrete path. The path must not contain\n unresolved `:` or `*` parameters.\n- Plans above 120 captures return a short-lived confirmation token. Review the\n unchanged plan, then rerun with `--confirm-large-run` and\n `--confirmation-token`.\n- Detailed rendered evidence is stored in a versioned manifest under\n `.webstudio/audits`; compact output includes its path and screenshot count.\n- Rendered checks also report broken images, eager images below the fold, and\n image sources more than 2x their rendered dimensions in both axes, including\n Webstudio instance ids and measured dimensions when available.\n- Rendered checks include sanitized Resource Timing evidence and report\n browser-marked render-blocking resources plus legacy `.ttf`, `.otf`, and\n `.woff` fonts without applying a universal transfer-size threshold.\n- Fix findings through semantic mutation commands, then rerun `audit` to confirm their deterministic finding ids disappeared.\n\n## Verify dynamic bindings\n\nCommands:\n\n- webstudio verify-bindings --json\n- MCP tool: verify-bindings {"pagePath":"/pricing"}\n- MCP tool: verify-bindings {"instanceId":"<instanceId>","limit":50}\n\nNotes:\n\n- Statically checks persisted text expressions, expression/action/resource/parameter props, resource expressions, and page metadata.\n- Findings distinguish invalid syntax, unknown or out-of-scope variables, stale internal data-source ids, and missing resource or parameter references.\n- Page and instance filters can be combined when the instance belongs to the selected page. Continue findings with `cursor`.\n- This operation does not resolve rendered values or execute external resources. Preview representative loading, empty, error, and populated states after static findings are fixed.\n\n## Refactor targeted content\n\nCommands:\n\n- MCP tool: list-instances {"pagePath":"/"}\n- MCP tool: list-texts {"pagePath":"/"}\n- MCP tool: update-text {"instanceId":"<instanceId>","childIndex":0,"text":"Launch faster"}\n- MCP tool: replace-text {"find":"Old headline","replace":"New headline","match":"exact","pagePath":"/pricing","limit":20}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"title":"Pricing","meta":{"description":"Plans"}}}\n- MCP tool: update-resource {"resourceId":"<resourceId>","values":{"url":"https://api.example.com/posts"}}\n- MCP tool: replace-asset {"fromAssetId":"<oldAssetId>","toAssetId":"<newAssetId>"}\n- MCP tool: replace-styles {"property":"color","fromValue":{"type":"keyword","value":"red"},"toValue":{"type":"keyword","value":"blue"}}\n- MCP tool: rewrite-css-variable-refs {"map":"variables.json contents"}\n\nNotes:\n\n- Use focused reads first, then mutate only matching instances, props, metadata, resource URLs, assets, or style references. Use `replace-text` for bounded literal text changes, `replace-prop-text` for bounded static prop text, `replace-resource-text` for fixed resource names/URLs, and `update-text` for one known child or expressions.\n\n## Optimize existing project\n\nCommands:\n\n- MCP tool: list-pages {}\n- MCP tool: list-folders {}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"title":"Pricing","meta":{"description":"Plans"}}}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: list-breakpoints {}\n- MCP tool: update-breakpoint {"breakpointId":"tablet","values":{"maxWidth":1023}}\n- MCP tool: get-styles {"instanceIds":["<instanceId>"],"includeTokens":true}\n- MCP tool: update-styles {"updates":"styles.json contents"}\n- MCP tool: attach-design-token {"designTokenId":"<tokenId>","instanceIds":"instances.json contents"}\n- MCP tool: update-project-settings {"meta":{"siteName":"Acme"}}\n\nNotes:\n\n- Use this for SEO metadata, accessibility labels, responsive behavior, token consistency, and project settings.\n\n## Connect external data\n\nCommands:\n\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"title","value":{"type":"string","value":"Hello"}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"tags","value":{"type":"json","value":["news","product"]}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"filters","value":{"type":"json","value":{"tag":"news"}}}\n- MCP tool: create-resource {"resource":{"name":"Posts","method":"get","url":"https://api.example.com/posts","searchParams":[{"name":"tag","value":"filters.tag"},{"name":"source","value":{"type":"literal","value":"website"}}],"headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"posts"}\n- MCP tool: create-resource {"resource":{"name":"Post GraphQL","control":"graphql","method":"post","url":"https://api.example.com/graphql","headers":[{"name":"Content-Type","value":{"type":"literal","value":"application/json"}}],"body":"{ query: \'query Post($slug: String!) { post(slug: $slug) { title } }\', variables: { slug: system.params.slug } }"},"scopeInstanceId":"<instanceId>","dataSourceName":"post"}\n- MCP tool: create-resource {"resource":{"name":"Current Date","control":"system","method":"get","url":"/$resources/current-date","headers":[]},"scopeInstanceId":"<instanceId>","dataSourceName":"currentDate"}\n- MCP tool: update-resource {"resourceId":"<resourceId>","values":{"url":"https://api.example.com/posts"}}\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n- MCP tool: insert-fragment {"parentInstanceId":"<instanceId>","fragment":"<ws.collection>{/_ collection content _/}</ws.collection>"}\n\nNotes:\n\n- Use this for CMS sections, blog listings, Ghost/headless CMS pages, n8n-style integrations, and API URLs built from variables.\n- For read data, expose GET resources as scoped data variables with `scopeInstanceId`/`dataSourceName` and read the loaded result from the resource result wrapper, usually `.data`.\n- For writes, webhooks, GraphQL submissions, and deletes, prefer unscoped resources bound to Form `action` props so they become action resources instead of auto-loaded read resources.\n- Use direct props for fixed values and prop bindings only when a prop must read a data variable, resource, action, or documented runtime context value such as `system`.\n\n## Render an array or object as repeated content\n\nCommands:\n\n- MCP tool: insert-collection {"parentInstanceId":"<instanceId>","data":{"type":"expression","value":"posts.data.items"},"itemFragment":"<ws.element ws:tag=\'article\'><ws.element ws:tag=\'h2\'>{expression`collectionItem.title`}</ws.element></ws.element>"}\n- MCP tool: inspect-instance {"instanceId":"<collectionId>","include":["props","bindings","children"]}\n\nNotes:\n\n- Use Collection whenever an array or object from a resource or data variable should render a list, grid, cards, table rows, options, tabs, or other repeated UI.\n- Pass `insert-collection` the complete array or object. Do not pass the resource response wrapper or one indexed item. External resource arrays are commonly nested under the scoped resource result\'s `data` field or deeper.\n- Pass one repeated-item Webstudio JSX root. The command creates the Collection, its private current-item/current-key parameters, the iterable binding, and descendant item bindings atomically.\n- Collection renders the item root once for every entry. Use `expression` values such as `collectionItem.title` in descendant text and props. Object iteration also exposes `collectionItemKey`.\n- Wrap multiple repeated sibling instances in one Element inside Collection.\n- For repeated Radix items such as accordion items, tabs, or menu options, bind a stable unique id or slug to every required `value` prop.\n- See the [Collection documentation](https://docs.webstudio.is/university/core-components/collection) for the equivalent Builder workflow.\n\n## Support dynamic runtime behavior\n\nCommands:\n\n- MCP tool: integrate-runtime-ui {"parentInstanceId":"<instanceId>","resources":[{"resource":{"name":"Seats","method":"get","url":"https://api.example.com/seats","headers":[]},"dataSourceName":"Seats","exposeAsDataSource":true}],"structure":{"type":"collection","data":{"type":"expression","value":"Seats.data"},"itemFragment":{"children":[{"type":"id","value":"seat"}],"instances":[{"type":"instance","id":"seat","component":"Text","children":[{"type":"expression","value":"collectionItem.label"}]}],"props":[],"dataSources":[],"resources":[],"styleSources":[],"styleSourceSelections":[],"styles":[],"breakpoints":[],"assets":[]}},"retainedBehavior":[{"instanceId":"<scriptInstanceId>","responsibility":"Seat selection behavior"}]}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n- MCP tool: create-resource {"resource":{"name":"Seats","method":"get","url":"https://api.example.com/seats","headers":[]}}\n- MCP tool: snapshot {"include":["instances","props","resources"]}\n- MCP tool: apply-patch {"baseVersion":"<version>","transactions":"patch.json contents"}\n\nNotes:\n\n- Use `integrate-runtime-ui` to create variables/resources, insert one editable fragment or Collection, and add safe data bindings in one transaction.\n- List existing script-owned responsibilities under `retainedBehavior`. The operation preserves those instances and never evaluates or accepts replacement script bodies.\n- `unsupportedConversions` records behavior that cannot be represented safely. Dry-run returns the complete transaction and the same retained/unsupported report without changing the project.\n- New actions and HtmlEmbed scripts are intentionally rejected. Create normal editable components and data bindings; keep opaque runtime behavior in existing script instances.\n\n## Build authenticated pages\n\nCommands:\n\n- MCP tool: meta.guide {"brief":"Build a Supabase-authenticated account page"}\n- MCP tool: create-page {"name":"Account","path":"/account"}\n- MCP tool: update-page {"pageId":"<pageId>","values":{"meta":{"auth":{"method":"basic","login":"<login>","password":"<password>"}}}}\n- MCP tool: create-resource {"resource":{"name":"Session","method":"get","url":"https://api.example.com/session","headers":[]}}\n- MCP tool: create-variable {"scopeInstanceId":"<instanceId>","name":"user","value":{"type":"json","value":{}}}\n- MCP tool: update-props {"updates":"props.json contents"}\n- MCP tool: bind-props {"bindings":"bindings.json contents"}\n\nNotes:\n\n- Inspect and reuse the project\'s existing auth convention before authoring. Do\n not add a second provider or session model implicitly.\n- Model signed-out, loading, signed-in, and failed-auth states explicitly.\n- Never store credentials, service-role keys, refresh tokens, private session\n values, or authenticated response bodies in project data, command output,\n screenshots, agent instructions, or error reports. Privileged provider calls\n and authorization enforcement belong server-side.\n- Basic auth is semantic today. Provider-specific Supabase/Firebase setup still\n uses the existing resource, variable, prop, binding, and embed tools; there is\n no provider-specific installer.\n\n## Generate from design input\n\nCommands:\n\n- MCP tool: meta.guide {"brief":"Recreate this Figma design as a responsive page"}\n- MCP tool: create-page {"name":"Landing","path":"/landing"}\n- MCP tool: create-design-token {"tokens":"tokens.json contents"}\n- MCP tool: define-css-variable {"vars":"vars.json contents"}\n- MCP tool: list-breakpoints {}\n- MCP tool: insert-fragment {"parentInstanceId":"<instanceId>","fragment":"<ws.element ws:tag=\'section\'><ws.element ws:tag=\'p\'>Section copy</ws.element></ws.element>"}\n- MCP tool: update-styles {"updates":[{"instanceId":"<instanceId>","breakpointId":"<breakpointId-from-list-breakpoints>","property":"padding-left","value":{"type":"unit","unit":"px","value":24}}]}\n- MCP tool: preview.start {"host":"127.0.0.1","port":5173}\n- MCP tool: screenshot {"path":"/landing","output":"landing-desktop.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"path":"/landing","output":"landing-mobile.png","viewport":{"width":390,"height":844},"waitUntil":"load","waitForTimeout":250}\n- MCP tool: screenshot {"baseUrl":"http://127.0.0.1:5177","path":"/landing","output":"landing-desktop.png","viewport":{"width":1440,"height":900},"waitUntil":"load","waitForTimeout":250}\n\nNotes:\n\n- Use this after the agent can inspect the supplied design. There is no direct\n Figma, screenshot, Inception, or `design.md` import command.\n- Inspect and reuse existing variables, tokens, styles, components, assets, and\n page patterns before authoring. Build semantic editable structure rather than\n flattening the design into an image or absolute-positioned approximation.\n- Verify one familiar viewport inside every distinct Builder breakpoint range,\n then run rendered audit and inspect the screenshots before completion.\n\n## Cross-project maintenance\n\nCommands:\n\n- webstudio mcp run .temp/projects.json\n- webstudio mcp run .temp/projects.json --dry-run\n- webstudio mcp run .temp/projects.json --approve-mutations --concurrency 2\n\nNotes:\n\n- Put shared `calls` and a `projects` array of independently linked project roots in the existing `mcp run` manifest. Project roots are relative to the manifest file.\n- Each project uses its own config, authentication, ProjectSession storage, checkpoint, and failure boundary. Confirmed successful calls are checkpointed. Reads can resume automatically; a mutation interrupted after dispatch is reported as ambiguous and is not replayed automatically, preventing silent duplicate writes.\n- Focus the manifest on bounded reads or audits first. Use per-call `dryRun`, global `--dry-run`, or explicitly approve committed mutations with `--approve-mutations` after reviewing the manifest.\n\n# Known CLI Gaps\n\n## Provider-specific authenticated pages\n\nMissing:\nCLI supports page basic auth and generic resources/props/embeds, but not guided Supabase/Firebase auth setup.\n\nCurrent fallback:\nCall `meta.guide` with the provider-authenticated page goal, then create the\npage, resources, variables, props, bindings, and embeds with existing semantic\ntools.\n\nSuggested commands:\n\n- setup-auth-page\n\n## Generate from design input\n\nMissing:\nNo command imports Figma, screenshots, Inception output, or design.md and turns it into pages/tokens/layout.\n\nCurrent fallback:\nCall `meta.guide` with the design-input goal, let the agent inspect the supplied\ndesign, then use semantic page, token, asset, fragment, style, preview,\nscreenshot, and audit tools. Use `apply-patch` only when no semantic operation\nfits.\n\nSuggested commands:\n\n- generate-from-design\n\n## Built-in cross-project maintenance\n\nMissing:\nPublic API and CLI intentionally operate on one configured project at a time; there is no built-in multi-project discovery or loop runner.\n\nCurrent fallback:\nRun the CLI from an external script that reconfigures one project/session at a time.\n\nSuggested commands:\n\n- none\n',
|
|
258650
258980
|
"manual-api": '# Webstudio API CLI Manual\n\nThe API commands operate on the single project configured by:\n\n- .webstudio/config.json: projectId\n- global Webstudio config: origin and token\n\n- Pass --json to API/discovery commands that support it. Do not add --json to top-level commands unless their help/schema documents it.\n- Never pass a project id. Commands use configured project only.\n- Read ids before writing. Do not invent ids for existing records.\n- stdout is one JSON object. stderr is diagnostics.\n- Prefer MCP semantic tools for detailed project edits. Use MCP apply-patch only when no semantic tool exists.\n\n## Start\n\n{{start}}\n\n## Read First\n\n{{readFirst}}\n\n## Project Session Cache\n\n- CLI commands use one local ProjectSession snapshot for the configured project.\n- Local-capable reads use cached namespaces when compatible and fetch only missing or stale namespaces.\n- Local-capable mutations build patches from the local snapshot, then commit with the cached build version.\n- Successful mutation commits update the local snapshot only after the remote commit succeeds.\n- Server-only commands run remotely and invalidate/refetch namespaces declared by the operation catalog.\n- Use --refresh on local-capable commands to refresh required namespaces before running.\n- Successful JSON responses include compact meta.session with operationId, buildId, version, source, committed, namespaceCounts, diagnosticCount, non-empty diagnostic summaries, and optional compatibilityVersion.\n\n## CLI Capability Inventory\n\nFor a short end-consumer summary of what MCP can do, see\n`manual mcp` / `webstudio man mcp`. The MCP inventory describes the same\nproject, page, element, style, data, asset, publish, domain, and visual\nverification capabilities without internal command names.\n\n### Top-Level Commands\n\n{{topLevelCapabilityIndex}}\n\n### High-Level API Commands By Area\n\n{{apiCapabilityIndex}}\n\n### MCP Tool Operations\n\nThese are MCP tools. From a shell, call them with the shortcut form `webstudio <tool> \'<json>\'` or with the explicit form `webstudio mcp single-op-call <tool> \'<json>\'`:\n\n{{mcpOnlyCommandIndex}}\n\n## Task Recipes\n\n{{taskRecipeIndex}}\n\n## Use Case Index\n\n{{useCaseIndex}}\n\n## Known CLI Gaps\n\n{{knownCliGapIndex}}\n\n## Input File Shapes\n\n{{inputFileShapeIndex}}\n\n## Raw Patch Fallback\n\napply-patch accepts either BuildPatchTransaction[] or { "transactions": BuildPatchTransaction[] }.\n\nEach transaction has:\n\n{\n"id": "patch-transaction-label",\n"payload": [\n{\n"namespace": "projectSettings",\n"patches": [\n{ "op": "replace", "path": ["meta", "siteName"], "value": "New Site" }\n]\n}\n]\n}\n\nThe transaction id is a patch label used for optimistic synchronization. It is\nnot a Builder record id. Do not invent ids for pages, instances, props,\nbreakpoints, resources, variables, folders, assets, or other project records.\n\nPatch paths are JSON-patch-like paths into Builder store data. Map-like namespaces use ids as the first path item.\n\nSupported namespaces:\n\n- pages: redirects, page records, and folders\n- projectSettings: project-wide metadata and compiler settings\n- instances: element instances and children, including text/expression children\n- props: element props, bindings, page references, resource bindings\n- styles: CSS declarations keyed by style declaration key\n- styleSources: local style sources and reusable design tokens\n- styleSourceSelections: instance-to-style-source connections\n- dataSources: data variables, parameters, and resource data sources\n- resources: data resource definitions\n- assets: project asset records handled by the existing asset patch path\n- breakpoints: responsive breakpoints\n- marketplaceProduct: marketplace metadata\n\n## Data Sources\n\n`dataSources` is the internal Builder namespace for variables. Public API, CLI,\nand MCP tools expose it through two user-facing groups:\n\n- data variables: `list-variables`, `create-variable`, `update-variable`, and\n `delete-variable`\n- data resources: `list-resources`, `create-resource`, `update-resource`, and\n `delete-resource`\n\nFor raw `snapshot`, request the public `variables` namespace rather than the\ninternal `dataSources` name. Raw patch payloads still use `dataSources` when\napplying direct changes.\n\nVariables can be scoped to an instance. Expressions under that instance can use\nthe variable by name; nested variables with the same name mask outer variables.\nVariable values support `string`, `number`, `boolean`, and `json`. Use `json`\nfor all arrays and objects, including tags, selected categories, and nested API\nfilter state.\nParameters are internal scoped runtime values provided by pages, collections,\nor components. They are not a public authoring surface: do not create, update,\nor delete parameter records. Public tools should preserve existing parameter\nrecords and may reference documented context values such as `system` in\nexpressions where they are already in scope.\n\nResource `url` accepts plain fixed URLs and paths, for example\n`https://api.example.com/posts` or `/$resources/current-date`. Dynamic URLs can\ncombine strings and variables, for example\n`"https://api.example.com/posts?tag=" + filters.tag`. Prefer `searchParams` for\nquery parameters that should be encoded separately:\n`[{ "name": "tag", "value": "filters.tag" }]`. Header values, search parameter\nvalues, and bodies are expressions for dynamic content. For fixed text, use\n`{ "type": "literal", "value": "application/json" }`; Webstudio stores the\nrequired string expression. Headers can still read variables such as\n`"Bearer " + auth.token`, and GraphQL bodies can return objects such as\n`{ query: "...", variables: { slug: system.params.slug } }`.\n\nCreate a GET resource with `scopeInstanceId` when the fetched resource result\nshould be available as a read data variable. Scoped GET resources default to\n`exposeAsDataSource: true`, are generated into the page resource `data` map,\nand may be loaded during page rendering. Use `dataSourceName` to choose the\nvariable name.\n\nFor submit/write/action resources, create the resource without\n`scopeInstanceId`, then bind a component prop such as a Form `action` to the\nresource with `bind-props` and `binding.type: "resource"`. Prop-bound resources\nare generated into the page resource `action` map instead of the read `data`\nmap. Use this shape for POST, PUT, DELETE, webhook, and other resources that\nshould run only from an explicit form/action flow, not merely because the page\nrendered.\n\nPOST, PUT, and DELETE resources default to `exposeAsDataSource: false` even\nwhen a scope is supplied. Set `exposeAsDataSource: true` only when a write-method\nresource intentionally provides render-time data, such as a read-only GraphQL\nPOST query. A scope is required, and the result includes a warning because the\nrequest may execute during page rendering. Set `exposeAsDataSource: false` on\n`update-resource` to detach an existing render-time data source.\n\nResource `method` can be `get`, `post`, `put`, or `delete`. Use GET for read\ndata. Use POST for creates, GraphQL requests, webhooks, and form submissions.\nUse PUT for full updates/replacements. Use DELETE for deletion actions.\nOptional `control` values are `graphql` and `system`: `graphql` marks a\nGraphQL-style resource, usually POST with a query body; `system` marks a\nresource intended to use the built-in `system` parameter or one of the built-in\nlocal resource URLs: `"/$resources/sitemap.xml"`,\n`"/$resources/current-date"`, and `"/$resources/assets"`. The system parameter\nfields are `system.origin`, `system.pathname`, `system.params`, and\n`system.search`.\n\nUse prop bindings for dynamic values that read variables or resources; use\ndirect props for static values.\n\nCommit raw patch:\n\nMCP tool: apply-patch\n\n## Raw Patch Examples\n\nRename the site:\n\n[\n{\n"id": "patch-site-name",\n"payload": [\n{\n"namespace": "projectSettings",\n"patches": [\n{ "op": "add", "path": ["meta", "siteName"], "value": "Acme Studio" }\n]\n}\n]\n}\n]\n\nUpdate page title metadata:\n\n[\n{\n"id": "patch-page-title",\n"payload": [\n{\n"namespace": "pages",\n"patches": [\n{ "op": "replace", "path": ["pages", "page-id", "meta", "title"], "value": "Pricing" }\n]\n}\n]\n}\n]\n\nUpdate a text child on an element:\n\n[\n{\n"id": "patch-text",\n"payload": [\n{\n"namespace": "instances",\n"patches": [\n{ "op": "replace", "path": ["instance-id", "children", 0, "value"], "value": "Launch faster" }\n]\n}\n]\n}\n]\n\nCreate records with semantic operations such as create-variable,\ncreate-resource, create-design-token, create-page, create-folder,\nand create-breakpoint. Raw patch rejects generated record\ncreation, collection replacement, record replacement with a different `id`, and\nrecord id field mutations in id-keyed namespaces because Webstudio must generate\nand preserve record ids.\n\n## Safety Rules\n\n- For MCP apply-patch, read the latest version with MCP snapshot before writing.\n- Reuse ids from MCP snapshot output when updating existing records.\n- Do not create generated records, replace generated record collections, replace records with different ids, or mutate record id fields with raw patch. Use semantic create operations so Webstudio generates ids.\n- If apply-patch reports a version conflict, read the latest build and regenerate the patch.\n- Prefer semantic MCP read tools for discovery, then use MCP snapshot for exact patch paths.\n\n## Command Index\n\n{{commandIndex}}\n',
|
|
258651
|
-
"manual-llm": '# Webstudio CLI Manual for LLMs\n\nUse this order. Stop only when a command returns ok:false.\n\nIf you are inside the Webstudio monorepo, the first command discovery should use\nthe local CLI exactly as `node packages/cli/local.js ...` from the repo root. Do\nnot use `packages/cli/bin.js` for local source-tree work; it is the packaged\nbuild entry and may use stale built output. Do not use `pnpm exec webstudio`,\n`pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can\nresolve an older binary.\n\nFor delegated design-system or “use every component” tasks, skip the generic warm-up sequence and start with exactly one MCP command: `webstudio workflow.next \'{"goal":"design-system-page"}\'`. Report that returned checkpoint to the parent/user and stop until continued.\n\n## Use MCP locally or optionally connect a client\n\nDo not install, register, or connect an MCP server merely because the user asks\nyou to edit a Webstudio project. If Webstudio MCP tools are already available,\nuse them. If you have shell access, use the local CLI shortcuts such as\n`webstudio meta.index` and `webstudio list-pages`; they expose the same project\noperations without changing client configuration or restarting the app.\n\nOnly when the user explicitly asks for persistent native MCP integration, run\nthe command for their client:\n\n- Claude Code: `webstudio connect claude`\n- Codex: `webstudio connect codex`\n- Cursor: `webstudio connect cursor`\n- VS Code or GitHub Copilot: `webstudio connect vscode`\n\nRun project operations from the linked project root. If the folder is not\nlinked, ask for an editable Builder share link and run\n`webstudio init --link <share-link> --json`. You can then use local CLI\nshortcuts immediately. Do not run `webstudio sync`, `webstudio connect`, or\nrestart the app unless the user specifically wants native MCP registration:\nMCP reads and edits the latest editable Builder build directly, including for\nprojects that have never been published. Treat the share link as a credential\nand do not include it in committed files, logs, screenshots, or issue reports.\n\nThe optional `connect` command verifies project access before changing client configuration. For\nClaude Code, Cursor, and VS Code it safely merges the `webstudio` server into\nthe client\'s project configuration. For Codex it runs both `codex mcp add` and\n`codex mcp get webstudio`; do not repeat those commands separately. Follow the\nreload, restart, or approval instruction printed by `connect`, then verify the\nloaded MCP connection by asking the client to use Webstudio MCP and list the\nproject pages. Use `--print` only to inspect the generated setup without\nchanging configuration or requiring project access.\n\n## Always\n\n1. webstudio permissions --json\n2. For bounded shell workflows, call MCP tools directly through the CLI shortcut, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and useful when you need to make the MCP boundary obvious. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for multiple calls in one shared CLI session. Use a normal JSON file path for large batches. Use long-running `webstudio mcp` only when your environment is a real MCP client. Do not manually send raw JSON-RPC to `webstudio mcp` from a shell or PTY.\n3. Read MCP `meta.index`, for example `webstudio meta.index`.\n4. Use focused MCP calls with concrete JSON: `webstudio meta.guide \'{"brief":"Create a design system page using every component"}\'`, `webstudio meta.get_more_tools \'{"tools":["insert-fragment"]}\'`, `webstudio components.list \'{"source":"all"}\'`, `webstudio components.coverage-plan`, `webstudio components.search \'{"brief":"radix select"}\'`, `webstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`, `webstudio templates.list`, and `webstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`.\n5. Read overview resources `webstudio://project/tools-overview` or `webstudio://project/components-overview` when useful. Read full resources `webstudio://project/tools` or `webstudio://project/components` only when focused tools are insufficient.\n6. Pick focused MCP read tool.\n7. Pick semantic MCP write tool.\n\nUse `webstudio schema mcp` for a compact MCP tool overview. Add `--verbose` only when exact input schemas for all tools are truly needed; otherwise prefer focused `meta.get_more_tools` and `components.*` calls.\n\nRun these commands from the linked project root. Use the MCP startup status line\'s absolute root for local files; write temporary scripts and artifacts under `<project root>/.temp`, not under a parent workspace.\n\nMonorepo quick path for a simple styled section:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call meta.get_more_tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js mcp single-op-call insert-fragment \'{"parentInstanceId":"parent-id","fragment":"<ws.element ws:tag=\\"section\\" ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag="h2">Launch Kit</ws.element><ws.element ws:tag="p">A focused section created with Webstudio JSX.</ws.element><ws.element ws:tag="button">Get started</ws.element></ws.element>"}\' --dry-run\n```\n\nThe same local shortcut form is shorter and preferred for simple shell steps:\n\n```sh\nnode packages/cli/local.js meta.index\nnode packages/cli/local.js meta.get_more_tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js insert-fragment \'{"parentInstanceId":"parent-id","fragment":"<ws.element ws:tag=\\"section\\" ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag="h2">Launch Kit</ws.element><ws.element ws:tag="p">A focused section created with Webstudio JSX.</ws.element><ws.element ws:tag="button">Get started</ws.element></ws.element>"}\' --dry-run\n```\n\nFor this simple path, do not grep source files, dump full MCP resources, or write parser scripts first. Use `list-pages`, `get-page-by-path`, or `list-instances` only to get the target `parentInstanceId`.\n\nWhen authoring JSX for `insert-fragment`, use Webstudio component helpers and Webstudio style syntax. Use `ws:style={css\\`...\\`}`for Webstudio-native CSS, or use React-style object syntax such as`style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.\n\nWhen the task says another user will edit a page in Content mode, use a Content Block (`ws:block`) around every editable region. Content-mode users can edit text and supported props only in descendants of that block; content outside it is read-only. Put reusable insertable options in the block\'s `ws:block-template` child. Do not put intended editor content inside that template container: templates are protected source material, while an inserted template copy becomes an editable direct child of the Content Block. Verify this structure before handoff.\n\nDo not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`. JSX fragments are declarative project data; use the built-in Webstudio helpers instead.\n\nUse Webstudio prop names in JSX: `class`, `for`, `aria-label`, and other HTML/Webstudio names. Do not use React-only aliases such as `className` or `htmlFor`; the runtime rejects them with the Webstudio prop name to use.\n\nUse Webstudio actions for event/action props. Do not pass JavaScript functions such as `onClick={() => ...}`; the runtime rejects them because functions cannot be persisted as Webstudio project data.\n\n```tsx\n<ws.element\n ws:tag="button"\n onClick={new ActionValue(["event"], expression`console.log(event)`)}\n>\n Open\n</ws.element>\n```\n\nPlain JSX prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.\n\nIf a component has a registered template with required parts, JSX must include those parts explicitly under the same parent structure as the template, for example `<radix.Switch><radix.SwitchThumb /></radix.Switch>`. Use `insert-component` when you want Webstudio to apply one component template automatically.\n\n## Animation Components\n\nBefore creating animation examples, inspect the exact components with focused discovery:\n\n```sh\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:AnimateChildren"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:AnimateText"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:StaggerAnimation"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:VideoAnimation"}\'\n```\n\nUse Animation Group (`AnimateChildren`) as the controller. Put normal instances directly inside it, or put Text Animation, Stagger Animation, or Video Animation directly inside it. Text, Stagger, and Video are helper components with `contentModel.category: "none"` and should not be used as standalone section roots.\n\nDefine timing and CSS changes on the Animation Group `action` prop. Use `type:"view"` for viewport entry/exit progress and `type:"scroll"` for scroll-progress timelines. For in animations, keep the canvas styles as the final state and use `fill:"backwards"` with keyframes that describe the starting state. For out animations, use `fill:"forwards"` with keyframes that describe the ending state.\n\nText Animation settings: `slidingWindow` defaults to `5`, `easing` defaults to `linear`, and `splitBy` defaults to `char`. Use `splitBy:"space"` for word-by-word animation. The parent Animation Group keyframes provide the actual opacity, translate, scale, or other styles.\n\nStagger Animation settings: `slidingWindow` defaults to `1` and `easing` defaults to `linear`. It applies parent Animation Group progress across its direct children. Use `slidingWindow:0` for instant sequential steps, `1` for one child at a time, and values above `1` for overlapping waves.\n\nVideo Animation settings: `timeline` is a boolean. Prefer `insert-component` for Video Animation so the Video child template is inserted, then configure the Video child asset/source. Use short, seek-friendly videos for smooth scroll-linked playback.\n\nUse JSX fragments for authored animation structures when you need styled, editable examples. Put the final visual state in `ws:style` and put the starting or ending animated state in the Animation Group `action` keyframes. Include an explicit `offset` on every keyframe: use `offset: 0` for starting-state keyframes with `fill:"backwards"` and `offset: 1` for ending-state keyframes with `fill:"forwards"`.\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n axis: "block",\n animations: [\n {\n name: "Fade up on entry",\n timing: {\n fill: "backwards",\n rangeStart: ["entry", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["entry", { type: "unit", value: 100, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n opacity: { type: "unit", value: 0, unit: "number" },\n translate: {\n type: "tuple",\n value: [\n { type: "unit", value: 0, unit: "number" },\n { type: "unit", value: 24, unit: "px" },\n ],\n },\n },\n },\n ],\n },\n ],\n }}\n>\n <ws.element\n ws:tag="section"\n ws:style={css`\n display: grid;\n gap: 16px;\n padding: 48px;\n border-radius: 24px;\n background: #111827;\n color: white;\n `}\n >\n <ws.element ws:tag="h2">Launch metrics</ws.element>\n <ws.element ws:tag="p">\n A polished card that fades up as it enters the viewport.\n </ws.element>\n </ws.element>\n</animation.AnimateChildren>\n```\n\nFor Text Animation, keep `animation.AnimateText` as the direct child of Animation Group and place the text-containing element inside it:\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n animations: [\n {\n name: "Parallax In",\n timing: {\n fill: "backwards",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 70, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n translate: {\n type: "tuple",\n value: [\n { type: "unit", value: 0, unit: "number" },\n { type: "unit", value: 100, unit: "px" },\n ],\n },\n },\n },\n ],\n },\n {\n name: "Opacity In",\n timing: {\n fill: "backwards",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 70, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n opacity: { type: "unit", value: 0, unit: "number" },\n },\n },\n ],\n },\n {\n name: "Scale In",\n timing: {\n fill: "backwards",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 70, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n scale: {\n type: "tuple",\n value: [\n { type: "unit", value: 5, unit: "number" },\n { type: "unit", value: 5, unit: "number" },\n ],\n },\n },\n },\n ],\n },\n {\n name: "Parallax Out",\n timing: {\n fill: "forwards",\n rangeStart: ["cover", { type: "unit", value: 50, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 100, unit: "%" }],\n },\n keyframes: [\n {\n offset: 1,\n styles: {\n translate: {\n type: "tuple",\n value: [\n { type: "unit", value: 0, unit: "number" },\n { type: "unit", value: -100, unit: "px" },\n ],\n },\n scale: {\n type: "tuple",\n value: [\n { type: "unit", value: 5, unit: "number" },\n { type: "unit", value: 5, unit: "number" },\n ],\n },\n opacity: { type: "unit", value: 0, unit: "number" },\n },\n },\n ],\n },\n ],\n insetStart: { type: "unit", value: 5, unit: "%" },\n insetEnd: { type: "unit", value: 5, unit: "%" },\n isPinned: true,\n }}\n>\n <animation.AnimateText\n splitBy="space"\n slidingWindow={5}\n easing="easeOutQuart"\n >\n <ws.element ws:tag="h2">Animate words with controlled rhythm</ws.element>\n </animation.AnimateText>\n</animation.AnimateChildren>\n```\n\nFor Stagger Animation, put the repeated cards or rows directly inside `animation.StaggerAnimation`:\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n animations: [\n {\n timing: {\n fill: "backwards",\n rangeStart: ["contain", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["contain", { type: "unit", value: 30, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n opacity: { type: "unit", value: 0, unit: "number" },\n },\n },\n ],\n },\n ],\n }}\n>\n <animation.StaggerAnimation>\n <ws.element\n ws:tag="article"\n ws:style={css`\n padding: 20px;\n border: 1px solid #d1d5db;\n border-radius: 16px;\n `}\n >\n Plan\n </ws.element>\n <ws.element\n ws:tag="article"\n ws:style={css`\n padding: 20px;\n border: 1px solid #d1d5db;\n border-radius: 16px;\n `}\n >\n Build\n </ws.element>\n <ws.element\n ws:tag="article"\n ws:style={css`\n padding: 20px;\n border: 1px solid #d1d5db;\n border-radius: 16px;\n `}\n >\n Launch\n </ws.element>\n </animation.StaggerAnimation>\n</animation.AnimateChildren>\n```\n\nFor Video Animation, use the registered template via `insert-component` when possible. If you author JSX, include the Video child explicitly:\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n animations: [\n {\n name: "Video progress",\n timing: {\n fill: "both",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 100, unit: "%" }],\n },\n keyframes: [{ offset: 0, styles: {} }],\n },\n ],\n }}\n>\n <animation.VideoAnimation timeline={true}>\n <$.Video\n preload="auto"\n autoPlay={true}\n muted={true}\n playsInline={true}\n crossOrigin="anonymous"\n />\n </animation.VideoAnimation>\n</animation.AnimateChildren>\n```\n\n## Command Surface Boundary\n\n- Use top-level `webstudio ...` shell commands for setup, sync/import/build/preview/screenshot, permissions, publish/domains, schema, registry inspection, man, and starting MCP.\n- Use MCP tools for Builder project data manipulation: pages, instances/components, props, text, styles, tokens, variables, resources, assets, breakpoints, redirects, and raw patches.\n- From a shell, call MCP tools with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio insert-fragment \'<json>\' --dry-run`. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `--input-file` for large payloads.\n- Inside the Webstudio monorepo, call the local CLI as its own command: `node packages/cli/local.js ...`. Do not wrap the CLI call in `pwd && ...`, command substitution, `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`.\n- For experiments, pass `--dry-run` to local-capable mutation calls. Read the computed transaction from `meta.session.transaction` and its base build version from `meta.session.version`. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.\n- For bounded multi-step shell work, run inline JSON with `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'`; this reuses one CLI session without raw JSON-RPC. For large batches, write `{ "calls": [{ "tool": "..." }] }` to a normal JSON file and run `webstudio mcp run .temp/mcp-calls.json`.\n- Use JSON strings for `brief` fields. Never pass boolean flags such as `{"brief":true}`.\n- Treat `webstudio mcp single-op-call` and `webstudio mcp run` stderr lines as progress checkpoints; stdout remains JSON on both success and failure. On failure, parse stdout for `{ "ok": false, "error": { "code": "...", "message": "..." } }` before deciding what to fix.\n- If a CLI/MCP tool crashes, hangs, gives a confusing error, needs an undocumented workaround, or forces source-code inspection for normal usage, ask the user to report it in Discord `#help` at https://wstd.us/community. Give them a complete copy-paste report with the goal, expected behavior, actual error, exact command/tool call, stdout JSON, stderr/lifecycle logs, environment, workaround, and secrets redacted.\n- Run one-shot `webstudio mcp single-op-call` commands sequentially against a linked `.webstudio` folder. If a command returns `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially.\n- In delegated or non-streaming agent environments, do not batch many MCP calls silently and do not wrap many shortcut or `webstudio mcp single-op-call` commands in a shell loop. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, report that command/result, then wait for the parent to continue. Do not take a broad task such as creating a full design-system page as one execution unit. Call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint, wait until the parent continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For all-component design-system pages, checkpoint after workflow planning, discovery, page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with `workflow.next {"goal":"design-system-page","phase":"presentation-pass"}`. Coverage 72/72 is necessary but not sufficient: the page must be organized into styled, real-world examples, not raw unstyled component dumps.\n- For design-system or “use every component” tasks, start with compact `webstudio components.coverage-plan`, checkpoint, then request component coverage details with `webstudio components.coverage-plan \'{"detail":"roots","offset":20}\'` or `{"detail":"parts"}` only when needed. Do not pass `detail` to `list-pages`; use `list-pages {}` or `get-page-by-path` for page lookup.\n- MCP tool shortcuts are only for MCP tools. If a shortcut is ambiguous with a real top-level command, the real top-level command wins; use `webstudio mcp single-op-call <tool> \'<json>\'` to force the MCP path.\n\n## LLM Implementation Process\n\nUse this process for user requests that change Webstudio content, layout, styles, assets, pages, redirects, resources, or publishing state:\n\n1. Discover capabilities with `webstudio man --json`, `webstudio schema api`, `webstudio schema mcp`, MCP `meta.index`, `meta.guide`, `meta.get_more_tools`, `components.list`, `components.summary`, `components.coverage-plan`, `components.search`, `components.get`, `templates.list`, and `templates.get`. From a shell, prefer shortcut calls such as `webstudio meta.index` and `webstudio components.search \'{"brief":"button"}\'` for these focused tool calls; use `webstudio mcp single-op-call` when you need the explicit MCP form. Read full resources such as `webstudio://project/tools` and `webstudio://project/components` only when needed. Do not write scripts to parse full MCP discovery JSON for normal lookup.\n2. Inspect current project state with semantic reads such as `get-project-settings`, `list-pages`, `get-page-by-path`, `list-instances`, `inspect-instance`, `get-styles`, `list-assets`, `list-breakpoints`, and `snapshot` only when needed. Before changing a project, read `get-project-settings` and follow any non-empty `meta.agentInstructions`. These are shared project instructions, not a place for secrets.\n3. Mutate the Webstudio project with semantic MCP write tools first. Prefer MCP `insert-fragment` for authored/styled sections, use `insert-component` only for one automatic component template, then `update-text`, `update-props`, `update-styles`, `upload-asset`, `create-page`, and page/project settings tools over raw patches.\n4. Use `apply-patch` only when no semantic tool covers the required change, and only after reading the latest snapshot/version.\n5. For visual/design work, regenerate or preview the generated app, capture a screenshot, inspect it with vision, and iterate before final response.\n6. Report what changed and what verification ran.\n\n## Visual Design Workflow\n\nFor requests involving visible HTML/CSS, layout, typography, colors, imagery, responsive behavior, or screenshots:\n\n1. Read editable Webstudio structure first: pages, instances, props, styles, breakpoints, assets, and relevant text.\n2. Do not use generated route/component files as the source of truth for editable content.\n3. Make edits through Webstudio semantic commands/MCP tools so the result stays editable in Builder and survives the next `webstudio build`.\n4. Keep generated project files current, start preview, and capture the changed page with `screenshot`.\n5. Use `screenshot.diff` when a baseline exists and inspect screenshot/diff artifacts with vision before finishing.\n6. If vision or screenshot tooling is unavailable, state that explicitly and explain what fallback verification was used.\n\n## Responsive Verification Workflow\n\nFor responsive page work, use Builder breakpoints as the source of truth:\n\n1. Read breakpoints with `list-breakpoints` before deciding responsive behavior.\n2. Apply responsive styles with existing Builder breakpoint ids; do not invent CSS media queries or breakpoint names when Webstudio breakpoint data exists.\n3. Pick screenshot viewport widths from the project breakpoints: include a desktop width, each defined max-width or min-width edge, and a narrow mobile width.\n4. Capture each viewport with `screenshot`, for example `{"path":"/","output":"home-375.png","viewport":{"width":375,"height":812}}` and `{"path":"/","output":"home-1440.png","viewport":{"width":1440,"height":900}}`.\n5. Inspect every viewport screenshot with vision before finishing, checking layout, overflow, hidden content, text wrapping, and breakpoint-specific style changes.\n6. If any viewport fails, update styles through semantic Webstudio tools and repeat screenshots for the affected breakpoints.\n\n## Generated Files Guardrails\n\n- Do not edit `app/__generated__`, generated route files, generated page files, generated CSS, or build output for normal Webstudio content/design requests.\n- Do not replace generated page components with handcrafted app code unless the user explicitly asks for code-only export customization.\n- Generated files are build artifacts and may be overwritten by `webstudio build`.\n- If a task truly requires generated app customization, keep it outside `app/__generated__` where possible and explain that it is not editable Webstudio content.\n\n## Values vs Bindings\n\nBefore authoring unfamiliar expressions, read `webstudio://project/expressions` with MCP `resources/read` or `webstudio mcp read-resource webstudio://project/expressions`. It documents the supported expression subset, method allowlist, scope, Collection context, and validation limits.\n\n- Use direct value tools for fixed content. For one visible text child, use `update-text` with plain `text`. For a bounded multi-instance literal replacement, use `replace-text` with `find`, `replace`, `pagePath` or `pageId`, and `limit`; it does not change expression children. Use `replace-prop-text` for bounded changes inside static string props, optionally limited to prop names or instance ids; it never changes dynamic bindings. For static props such as `aria-label`, `alt`, `id`, `class`, `href`, or button labels stored as props, use `update-props` with the prop\'s direct type/value.\n- Use `bind-props` only when the prop must stay dynamic: an expression, resource result, action, or existing scoped runtime context such as `system`. Do not use `bind-props` just to set a fixed string.\n- Direct prop string example: `{"updates":[{"instanceId":"button-id","name":"aria-label","type":"string","value":"Open menu"}]}`.\n- Expression binding example: `{"bindings":[{"instanceId":"link-id","name":"href","binding":{"type":"expression","value":"currentPost.url"}}]}`.\n- Page metadata fields such as `title`, `description`, `language`, `redirect`, and custom meta content accept plain fixed text. For computed values, pass JavaScript expression code such as `pageTitle ?? "Pricing | Acme"`.\n- Page `status` accepts a fixed HTTP status code as a number from 200 through 599, for example `302`. For a dynamic status, pass JavaScript expression code such as `system.status`.\n- Page metadata update example: use `update-page` with `{"pageId":"page-id","values":{"title":"Pricing | Acme","meta":{"description":"Plans for teams"}}}`.\n- Draft a page with `update-page` and `{"pageId":"page-id","values":{"isDraft":true}}`. It remains editable and previewable but is omitted from every publish target, including staging, and from sitemap output.\n- Stage a draft page for a future publish with `{"pageId":"page-id","values":{"isDraft":false}}`. This clears draft state but does not deploy the site. The home page and `/*` catch-all page cannot be drafts.\n- Resource `url` accepts plain fixed URLs and paths. For computed URLs, pass JavaScript expression code such as `"https://api.example.com/items?tag=" + filters.tag`. Resource header values, search parameter values, and text bodies accept expressions for dynamic values; for fixed text, use `{ "type": "literal", "value": "application/json" }`.\n- Resource update example: use `update-resource` with `{"resourceId":"resource-id","values":{"url":"https://api.example.com/items"}}`.\n- Assets is one system resource with one response shape and always executes a structured query. `create-assets-resource` without `query` uses the default URL and optional image-dimensions output. Provide query configuration to control filtering, sorting, pagination, selected fields, or file content.\n- For a Markdown-backed blog, create exactly two Builder page definitions: a fixed `/blog` overview and one `/blog/:slug` detail page. Both pages load content through Assets resources. Never create one Builder page per post or duplicate Markdown content into static page structures. Read `get-asset-field-catalog`, validate each structured query with `validate-asset-query`, then call `create-assets-resource` or `update-assets-resource`. Set `values.query:null` to restore the default query.\n- Optimize every explicit Assets query for the deployed content-database size. Use `output.mode:"fields"` and select only fields that are actually rendered or otherwise required by the query. Keep `includeMetadata:false` unless the rendered value needs file metadata; diagnostics are returned separately. Do not use `output.mode:"all"` as a convenience default.\n- Every reachable Assets data source contributes to the shared database. Keep one final resource per rendered query. Update an existing scoped resource instead of creating a placeholder, preview copy, or repair replacement, and remove obsolete duplicate resources and data sources.\n- Make a bounded overview fully static: use literal values for its filters, limit, and offset, add a deterministic ID tie-breaker to its sort, and use `content.mode:"none"`. This lets compilation materialize the small overview result instead of retaining overview-only fields across every candidate article. Reserve runtime expressions for values that are truly dynamic, such as `system.params.slug` on the detail route.\n- Query Markdown files directly and use `content.mode:"markdown-body"` when rendering their bodies. The published database keeps only metadata and document references, filters and paginates first, and fetches the selected Markdown bodies from Asset storage at runtime. Do not create companion JSON descriptors merely to avoid embedding Markdown.\n- `full` and bounded `range` request embedded file bytes. Use them only when the caller explicitly requires the complete source or a byte range.\n- Deferred Markdown bodies exclude frontmatter and resolve conventional relative links and images such as `../images/hero.png` to matching Assets. Markdown Embed permits sanitized figures, audio, video, and iframes, but removes scripts, inline event handlers, and unsafe URLs.\n- Assets expose an ID-keyed map at `<dataSourceName>.data` and collection information at `<dataSourceName>.meta`. Bind a listing Collection to `posts.data` and a one-result detail Collection to `post.data`; each item value contains selected fields and its item key is the asset ID. Read frontmatter or JSON fields from `item.properties` and the resolved Markdown body from `item.content.text`.\n- Use `preview-asset-query` with concrete values before binding expressions in the saved resource. Inspect `__diagnostics__.query` for the temporary query-only footprint and `__diagnostics__.database` for the merged database built from all reachable Assets queries. Only `database.usedBytes` counts toward `database.maxBytes`; query sizes are not separate allowances and must not be summed. Compare `usedBytes`, `unboundedBytes`, and `truncated` within both scopes. A finished Markdown blog must include every source document without truncation, contain no embedded Markdown bodies, and retain only the intended materialized overview query. When merged usage approaches the limit, remove duplicate reachable resources first, then unused output fields, then narrow candidate files. Inspect saved mode and configuration with `list-assets-resources` or `get-assets-resource`; shared index maintenance is automatic.\n- Data variable values support `string`, `number`, `boolean`, and `json`. Use `json` for all arrays, objects, filters, and nested data.\n- Parameters are internal scoped runtime values from pages, collections, or components. They are not a public authoring surface: do not create, update, or delete parameter records. Public tools should preserve existing parameter records and may reference documented context values such as `system` in expressions where they are already in scope.\n- Use scoped resources for read data. A GET resource created with `scopeInstanceId`/`dataSourceName` defaults to `exposeAsDataSource:true`, becomes a scoped resource data variable, is generated into the page resource `data` map, and may be loaded while rendering the page. Read the loaded resource result from its wrapper, usually `.data`.\n- Use prop-bound resources for actions. A resource created without `scopeInstanceId` and bound to a component prop such as Form `action` with `bind-props` and `binding.type: "resource"` becomes an action resource in the page resource `action` map. Use this for POST, PUT, DELETE, webhooks, GraphQL submissions, and anything that should run only from an explicit form/action flow.\n- POST, PUT, and DELETE resources default to `exposeAsDataSource:false`, even with a scope. Set `exposeAsDataSource:true` only for an intentional render-time read such as a GraphQL POST query; provide `scopeInstanceId` and inspect the returned warning. Set it to `false` during `update-resource` to detach existing render-time exposure.\n- For dynamic resource query parameters prefer `searchParams`, for example `{"name":"tag","value":"filters.tag"}`. Use `{"type":"literal","value":"website"}` for fixed request text. Header values can be expressions such as `"\\"Bearer \\" + auth.token"`. Body can be an object expression, including GraphQL payloads such as `{ query: "...", variables: { slug: system.params.slug } }`.\n- Resource methods are `get`, `post`, `put`, and `delete`. Optional resource controls are `graphql` and `system`. Use `control:"graphql"` for GraphQL POST resources with query bodies. Use `control:"system"` for built-in local resource URLs such as `"/$resources/current-date"` and for resources reading the built-in `system` parameter. The built-in system fields are `system.origin`, `system.pathname`, `system.params`, and `system.search`; do not use `system.path`.\n- Whenever an array or object from a resource or data variable should render repeated UI, call `insert-collection` with the complete iterable and one repeated-item JSX root. The command creates the Collection, private item parameters, iterable binding, and descendant item bindings atomically. Use `collectionItem` and `collectionItemKey` expressions in the item JSX. Wrap multiple repeated siblings in one Element, and give repeated Radix items stable unique `value` bindings.\n- Expressions are single JavaScript expressions, not statements or functions. Functions, arrow functions, classes, `new`, `this`, `await`, imports, arbitrary calls, increment/decrement, and assignment outside actions are unsupported. Prefer optional chaining, nullish coalescing, ternaries, property/index access, operators, and the documented string/array methods.\n\n## Pick Read Command\n\n{{readFirst}}\n\n## Pick Write Command\n\n{{taskRecipeIndex}}\n\n## Raw Patch Only If Needed\n\n1. Use MCP tool: snapshot.\n2. Write BuildPatchTransaction[].\n3. Use MCP tool: apply-patch.\n\n## MCP Argument Examples\n\nMCP tools receive JSON argument objects, not CLI flags. Use these shapes:\n\n{{mcpArgumentExampleIndex}}\n\n## Rules\n\n- Never guess ids for existing records. Read them first.\n- Never use project ids from user input. Commands use the configured project.\n- Use --refresh before a local-capable command when cached data may be stale.\n- Pass --json only to commands whose help/schema documents it. Do not add --json to top-level commands such as sync unless supported.\n- On VERSION_CONFLICT, read MCP snapshot again, regenerate the patch, then retry.\n- Treat stdout JSON as the API contract and stderr as diagnostics.\n- For visual/design work, verify the rendered result with vision before finishing.\n- Do not edit generated files for normal Webstudio content/design requests.\n- Use direct values for static strings and bindings only for dynamic expressions/resources/actions.\n- Use plain fixed text where documented. Only encode a quoted JavaScript string literal when a field is explicitly documented as an expression-only value.\n- Confirm destructive commands with --confirm only when user requested deletion/unpublish/replacement.\n- Use webstudio schema api for machine-readable top-level command metadata and webstudio schema mcp for MCP tool schemas.\n\n## Known Gaps\n\n{{knownCliGapIndex}}\n',
|
|
258652
|
-
"manual-mcp": '# Webstudio MCP Manual\n\n`webstudio mcp` starts a stdio MCP server for real MCP clients. Shell users can call MCP tools with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. `webstudio mcp single-op-call` is the explicit equivalent and prints the structured JSON result. `webstudio mcp run` runs multiple MCP tool calls from inline JSON or a normal JSON file in one shared CLI session. Do not manually type or pipe raw JSON-RPC frames into `webstudio mcp` from an interactive shell or PTY.\n\n## Startup\n\nIf you are already working with a shell-capable agent, it can use the local CLI\ndirectly. Native MCP client registration is optional. Give the editable Builder\nshare link only when the trusted agent asks for it. Treat the share link as a\ncredential: do not include it in committed files, screenshots, logs, or issue\nreports.\n\n1. Configure a project with `webstudio init --link <api-share-link> --json`.\n2. Check capabilities with `webstudio permissions --json`.\n3. Use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for individual MCP tool calls. Use the explicit equivalent `webstudio mcp single-op-call <tool> \'<json>\'` when you need to force the MCP path, or `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for bounded multi-call workflows. Use `webstudio mcp run .temp/mcp-calls.json` for large batches.\n4. Start discovery with `meta.index`, then call focused tools with concrete JSON, for example `webstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'`.\n\nDo not run `webstudio sync`, install an MCP server, change client configuration,\nor restart the app for this local CLI workflow.\n\nWhen the user explicitly wants persistent native MCP integration, run\n`webstudio connect claude`, `webstudio connect codex`, `webstudio connect\ncursor`, or `webstudio connect vscode`. This optional command changes client\nconfiguration, so follow its client-specific reload or restart instruction.\nUse `--print` to inspect the generated setup without changing configuration or\nrequiring project access. For Codex, `connect` registers and verifies the server\nthrough the Codex CLI. Before changing client configuration, `connect` verifies\nthat the saved project endpoint is reachable and its credential is accepted.\n\nStart MCP from the linked Webstudio project root. The lifecycle status line prints that absolute root; create local scripts, screenshots, and temporary artifacts under that root, for example `<project root>/.temp/script.mjs`. If the shell starts in a parent workspace, `cd` into the project root first or use absolute paths.\n\nWhen developing inside the Webstudio monorepo, start the local CLI exactly as `node packages/cli/local.js mcp` from the repo root. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can resolve an older binary.\n\nWhile the server is running, stdout is reserved for MCP JSON-RPC messages. Do not print human text from the server process. The server advertises MCP `logging` capability and emits sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`; stderr also mirrors these sparse lifecycle fallback lines prefixed with `[webstudio mcp]`.\n\n## One-Shot Tool Calls\n\nUse the shortcut `webstudio <tool> \'<json>\'` when you are operating from a shell and need one MCP tool result. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and avoids writing temporary Node.js stdio client scripts.\n\nExamples:\n\n```sh\nwebstudio mcp single-op-call meta.index\nwebstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio mcp single-op-call meta.get_more_tools \'{"tools":["insert-fragment"]}\'\nwebstudio mcp single-op-call components.list \'{"source":"all"}\'\nwebstudio mcp single-op-call components.coverage-plan\nwebstudio mcp single-op-call components.search \'{"brief":"radix select"}\'\nwebstudio mcp single-op-call components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call templates.list\nwebstudio mcp single-op-call templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json\n```\n\nShortcut equivalents:\n\n```sh\nwebstudio meta.index\nwebstudio meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio meta.get_more_tools \'{"tools":["insert-fragment"]}\'\nwebstudio components.list \'{"source":"all"}\'\nwebstudio components.coverage-plan\nwebstudio components.search \'{"brief":"radix select"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio templates.list\nwebstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio insert-fragment --input-file .temp/insert-fragment.json\n```\n\nRules:\n\n- Inside the Webstudio monorepo, replace `webstudio` in the examples above with `node packages/cli/local.js`, for example `node packages/cli/local.js meta.index`.\n- For a simple authored/styled section, run `meta.index`, then `meta.get_more_tools \'{"tools":["insert-fragment"]}\'`, then `insert-fragment`. Do not grep source files, dump full MCP resources, or write parser scripts first.\n- In `insert-fragment` JSX, use `ws:style={css\\`...\\`}`for Webstudio-native CSS, or use React-style object syntax such as`style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.\n- Prefer JSX for authored/styled content. Common `insert-fragment` inputs:\n\n```jsonl\n{"parentInstanceId":"root-id","fragment":"<ws.element ws:tag=\\"section\\" ws:style={css`padding: 32px; display: grid; gap: 16px;`}><ws.element ws:tag="h2">Northstar Product OS</ws.element><ws.element ws:tag="p">Reusable patterns for teams.</ws.element></ws.element>"}\n{"parentInstanceId":"root-id","fragment":"<ws.element ws:tag=\\"section\\" style={{ padding: 32, borderRadius: 16 }}><ws.element ws:tag="h2">Operations Console</ws.element><ws.element ws:tag="p">React-style object styles become editable Webstudio styles.</ws.element></ws.element>"}\n{"parentInstanceId":"root-id","fragment":"<ws.element ws:tag=\\"section\\" ws:tokens={[token(\\"accent\\", css`color: #0f766e;`)]}><ws.element ws:tag="button" onClick={new ActionValue([\\"event\\"], expression`console.log(event)`)}>Track launch</ws.element></ws.element>"}\n{"parentInstanceId":"root-id","fragment":"<ws.element ws:tag=\\"section\\"><radix.Switch><radix.SwitchThumb /></radix.Switch></ws.element>"}\n```\n\n- Do not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`.\n- Use Webstudio prop names such as `class` and `for`; do not use React aliases `className` or `htmlFor`.\n- Use Webstudio actions for event/action props, for example `onClick={new ActionValue(["event"], expression\\`console.log(event)\\`)}`. Do not pass JavaScript functions such as `onClick={() => ...}`.\n- Plain prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.\n- Template-backed components used in JSX must include required child/part components explicitly under the same parent structure as the template, for example `<radix.Switch><radix.SwitchThumb /></radix.Switch>`. Use `insert-component` when you want one automatic registered component template.\n- The positional input is JSON and defaults to `{}`.\n- Use `--input-file` for large mutation payloads.\n- Use `--dry-run` with local-capable mutation tools when you need a patch plan without committing. The computed transaction is returned in `meta.session.transaction`, and `meta.session.version` is its base build version. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.\n- The command prints JSON to stdout for both success and failure. Success uses the same `structuredContent` shape MCP tools return: `{ "ok": true, "data": ..., "meta": ... }`. Failure prints `{ "ok": false, "error": { "code": "...", "message": "..." }, "meta": ... }` and exits nonzero.\n- The command writes sparse progress to stderr, including start, success/failure, elapsed time, and committed status when the tool returns session metadata.\n- Invalid argument types fail loudly with path-specific messages, for example `meta.guide input.brief must be a string when provided`.\n- Run one-shot shortcut or `mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially.\n- To work with another previously linked project without changing the directory\'s default link, start MCP or a shell call with `--project <projectId>`, for example `webstudio mcp --project <projectId>` or `webstudio mcp single-op-call list-pages --project <projectId>`. Selected projects use isolated local session and checkpoint files.\n- If you are a delegated agent and your parent cannot see live stderr/stdout, do not run a long sequence of shortcut or `mcp single-op-call` commands silently and do not wrap many calls in a shell loop. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one `webstudio <tool>` or `webstudio mcp single-op-call` command, report that command/result, then wait before the next MCP command. For all-component design-system pages, checkpoint after discovery, checkpoint after page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with the `presentation-pass` workflow phase. Coverage alone is not completion; organize examples into styled sections/cards.\n\n## Reporting CLI/MCP Issues\n\nIf a CLI/MCP tool gives a confusing error, crashes, hangs, produces invalid output, requires an undocumented workaround, or makes you inspect source code to understand normal usage, ask the user to report it in the Webstudio Discord `#help` channel: https://wstd.us/community.\n\nGive the user a complete copy-paste report. Include only non-secret values: never include auth tokens, private URLs, cookies, API keys, passwords, or proprietary project data. Redact them as `<redacted>`.\n\nCopy-paste template:\n\n````md\nWebstudio CLI/MCP issue report\n\nWhat I was trying to do:\n<short user goal, for example "Create a resource from an external API and render it in a collection">\n\nWhat I expected:\n<what should have happened>\n\nWhat happened instead:\n<exact error, confusing behavior, hang, missing docs, or workaround required>\n\nCommand/tool used:\n\n```sh\n<exact command or MCP tool call, with tokens/secrets redacted>\n```\n\nStructured output / error:\n\n```json\n<stdout JSON or MCP structuredContent, if available, with secrets redacted>\n```\n\nStderr / lifecycle logs:\n\n```txt\n<stderr lines, timings, checkpoint messages, or stack trace, with secrets redacted>\n```\n\nEnvironment:\n\n- CLI command path: <webstudio / node packages/cli/local.js / other>\n- Webstudio CLI version: <from command output if known>\n- OS: <macOS / Windows / Linux / unknown>\n- Node version: <node -v if known>\n- Project/session state: <linked project, local .webstudio session, preview, MCP server, or unknown>\n\nWorkaround tried:\n<what the agent/user tried next, and whether it worked>\n\nWhy this should be improved:\n<one sentence: better error message, docs, schema, tool behavior, etc.>\n````\n\n## Shared-Session Shell Runs\n\nUse `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` when you are operating from a shell and need several MCP tool calls to share one CLI session without hand-writing JSON-RPC. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.\n\nUse `mcp run` for long-lived tools such as `preview.start`. A one-shot `mcp single-op-call preview.start` cannot keep ownership of a preview server for a later screenshot or stop call. Put `preview.start`, `screenshot`, and `preview.stop` in one shared `mcp run` process, or use a real long-running MCP client.\n\nInput shape:\n\n```json\n{\n "calls": [\n { "tool": "meta.index" },\n { "tool": "components.find", "input": { "brief": "radix select" } }\n ]\n}\n```\n\nRules:\n\n- The command prints JSON to stdout for both success and failure. It stops at the first failed call and prints partial results in `{ "ok": false, "error": ..., "data": { "completedCalls": ..., "results": [...] }, "meta": ... }`, then exits nonzero.\n- If a call returns `checkpoint.required`, read-only discovery and inspection remain available, but mutations and state-changing session tools return `CHECKPOINT_REQUIRED`. Stop and report the checkpoint to the parent/user. Only after the parent/user continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}` before continuing mutations.\n- For `mcp single-op-call`, checkpoint requirements persist across later one-shot CLI processes until you call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`.\n- Use this instead of manually sending JSON-RPC frames to `webstudio mcp` from a shell.\n\n### Cross-project batches\n\nAdd `projects` to the same `mcp run` manifest to run focused reads, audits, or dry runs across independently linked project roots:\n\n```json\n{\n "concurrency": 2,\n "calls": [\n { "tool": "status" },\n { "tool": "audit", "input": {} },\n {\n "tool": "update-project-settings",\n "input": { "meta": { "siteName": "Reviewed" } },\n "dryRun": true\n }\n ],\n "projects": [\n { "id": "site-a", "root": "../site-a" },\n { "id": "site-b", "root": "../site-b" }\n ]\n}\n```\n\nProject roots and an optional `progressFile` are resolved relative to the manifest file. Each project may provide its own `calls` instead of using the top-level calls. Each root must already be linked with its own `.webstudio/config.json`; the runner creates an independently authenticated ProjectSession and uses root-scoped session, audit, preview-data, and checkpoint paths without changing the process working directory.\n\nConcurrency defaults to 2, is capped at 16, and can be set in the manifest or overridden with `--concurrency`. A failure is reported for that project while other projects continue. Progress is saved after every successful call; rerunning with the default `--resume` skips completed projects and starts failed projects after their last confirmed successful call. Reads and dry runs may be retried. A committed mutation interrupted after dispatch is marked `AMBIGUOUS_MUTATION_RESULT` and is never replayed automatically; inspect that project before deciding how to continue. Use `--no-resume` only to intentionally start the complete manifest over.\n\nCommitted mutation tools are rejected in a projects batch unless the command includes `--approve-mutations`. Review the complete manifest before granting approval. `--dry-run` applies to every call and does not require mutation approval. The final stdout object is compact: project counts, one status/error record per project, elapsed time, and the progress-file path rather than every tool result.\n\n## Discovery\n\nUse MCP itself after startup, or call the same tools with `webstudio mcp single-op-call`:\n\n- `tools/list`: machine-readable available tools\n- `resources/list`: available overview and full JSON resources\n- `meta.index`: concise capability catalog\n- `meta.guide`: workflow for a user goal; call with a string brief such as `{"brief":"Create a pricing page"}`\n- `meta.get_more_tools`: detailed params, examples, namespaces, and local/server behavior; prefer exact names such as `{"tools":["insert-fragment"]}` when you know them\n- `components.list`: compact registry metadata for visible components and templates; use a focused get tool for complete details\n- `components.summary`: component counts by default; use `{"detail":"components","limit":20}` for paginated entries\n- `components.coverage-plan`: compact paged plan for design-system coverage tasks that need every component; default returns counts plus the first root page, use `{"detail":"roots"}`, `{"detail":"parts"}`, or `{"detail":"full"}` for more\n- `components.coverage-status`: page-specific covered/missing component report with `missingRoots` and `missingParts`\n- `components.search`: focused component/template search by id, namespace, label, category, or content model\n- `components.find`: compatibility alias for focused component search\n- `components.get`: full metadata for one component id\n- `templates.list`: compact metadata for template-backed insertions only\n- `templates.get`: full registry item and payload metadata for one template\n\nComponent and template registry items use a shadcn-compatible top-level shape plus Webstudio-specific superset metadata in `meta`. Use `meta.runtime` for component ids, props, states, content model, and source identity; `meta.authoring` for composition and accessibility guidance; and `meta.builder` for template insertion details and expected project-data namespaces. These items are for Builder/MCP discovery and are not a published shadcn install registry yet.\n\nPrefer the focused `components.*` tools over dumping `webstudio://project/components`. Do not write local scripts to parse full MCP discovery JSON for common component lookup.\nFor “use every component” or design-system pages, start with compact `components.coverage-plan`, checkpoint, then page through roots/parts instead of dumping the full catalog.\n\n## Consumer Capabilities\n\nMCP lets agents work on one configured Webstudio project at a time. In consumer\nterms, agents can:\n\n- Check which project they are connected to.\n- Check what the share link is allowed to do.\n- Inspect project metadata and the latest editable build.\n- Read selected project data for audits and repair.\n- Apply precise project changes against a known version.\n- List, inspect, create, update, delete, duplicate, copy, and reorder pages.\n- Set the home page.\n- Preserve old page paths for redirects or history.\n- Read and update page titles, descriptions, metadata, auth settings, and SEO fields.\n- List, create, update, duplicate, move, and delete page folders.\n- List, create, update, delete, duplicate, reorder, and reuse page templates.\n- Create pages from reusable templates.\n- Read and update project site settings.\n- Read and update marketplace product metadata.\n- List, create, update, delete, and replace redirects.\n- List, create, update, and delete responsive breakpoints.\n- List and inspect page elements.\n- Insert registered components.\n- Insert styled JSX fragments.\n- Move, reparent, clone, duplicate, wrap, unwrap, convert, rename, retag, and delete elements.\n- Fill grid cells.\n- List and update text children.\n- Update plain text and expression text.\n- Update structured rich text.\n- Add, update, delete, and bind element props.\n- Bind props to expressions, resources, actions, and runtime system values.\n- Read, add, update, delete, and replace local styles.\n- Update selected style-source styles.\n- List, create, update, attach, detach, extract, duplicate, rename, lock, unlock, reorder, clear, and delete design tokens and style sources.\n- List, define, rename, delete, and rewrite CSS variables.\n- List, create, update, and delete static data variables.\n- Create string, number, boolean, and JSON variables. Arrays use JSON.\n- Delete unused data variables.\n- List, create, update, upsert, bind, and delete resources.\n- Create HTTP resources.\n- Create GraphQL resources.\n- Create system resources.\n- Use built-in system resources for sitemap, current date, and assets.\n- List and inspect complete asset metadata; upload, download, update, move, duplicate, find usage for, replace, and delete assets.\n- List, create, rename, move, recursively duplicate, and recursively delete nested asset folders.\n- Publish to staging or production.\n- Publish to selected domains.\n- List publish builds.\n- Check publish job status.\n- Unpublish staging or production deployments.\n- List, create, update, delete, and verify custom domains.\n- Start and stop preview.\n- Capture screenshots of generated pages.\n- Compare screenshots against baselines.\n- Install OCR support for richer visual checks.\n\nUseful resources:\n\n- `webstudio://project/status`: compact current ProjectSession status\n- `webstudio://project/tools-overview`: small operation overview by capability area\n- `webstudio://project/components-overview`: small component overview with ids, labels, namespaces, and categories\n- `webstudio://project/tools`: full operation catalog; read only when focused metadata is insufficient\n- `webstudio://project/components`: full component catalog with props, states, and content model composition constraints; read only when `components.summary`, `components.find`, and `components.get` are insufficient\n- `webstudio://project/guide`: concise discovery guide\n- `webstudio://project/expressions`: expression syntax, scope, supported methods, bindings, Collection iteration context, and verification\n- `webstudio://project/accessibility-review`: evidence-based LLM accessibility-review workflow using project checks, preview, and screenshots\n\n## MCP SDK Client Imports\n\nWhen writing a local Node.js MCP client script, use the official MCP SDK package and these exact ESM imports:\n\nInside the Webstudio monorepo this package is available at the repo root. In another project, install it first with `pnpm add -D @modelcontextprotocol/sdk`.\n\n```js\nimport { Client } from "@modelcontextprotocol/sdk/client/index.js";\nimport { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";\nimport { LoggingMessageNotificationSchema } from "@modelcontextprotocol/sdk/types.js";\n```\n\nMinimal stdio client for the local Webstudio CLI:\n\n```js\nconst client = new Client({ name: "webstudio-agent", version: "1.0.0" });\n\nclient.setNotificationHandler(\n LoggingMessageNotificationSchema,\n (notification) => {\n console.error(`[mcp] ${notification.params.data}`);\n }\n);\n\nconst transport = new StdioClientTransport({\n command: "node",\n args: ["packages/cli/local.js", "mcp"],\n cwd: process.cwd(),\n stderr: "inherit",\n});\n\nawait client.connect(transport);\n\nconst index = await client.callTool({\n name: "meta.index",\n arguments: {},\n});\nconsole.log(JSON.stringify(index.structuredContent, null, 2));\n\nawait client.close();\n```\n\nUse `node packages/cli/local.js mcp` from the Webstudio monorepo root for local development, or `webstudio mcp` from a linked project where the CLI is installed. Keep stdout for JSON-RPC/structured results and surface MCP logging notifications or stderr lifecycle lines as progress.\n\n## Core Rules\n\n- stdout is reserved for MCP JSON-RPC while the server is running.\n- Operate on the configured project only.\n- Read ids before writing.\n- Prefer semantic tools over `apply-patch`.\n- Use `status` and `refresh` when cached namespaces may be stale. Pass `status {"verbose":true}` only when debugging full namespace arrays, freshness, compatibility, or diagnostic details.\n- A mutation is durable only when `meta.session.committed` is true.\n- For visual/design work, verify the rendered result with vision before finishing.\n\n## Vision Verification Loop\n\nVision-capable AI can use MCP to see what it is building:\n\n{{mcpVisionVerificationLoopMarkdown}}\n\nGenerated app setup:\n\n{{mcpGeneratedAppDependencyNotes}}\n\n## MCP Argument Examples\n\nMCP tools receive JSON argument objects:\n\n{{mcpArgumentExampleIndex}}\n\n## Screenshot Verification\n\n{{screenshotVerificationSummary}}\n',
|
|
258653
|
-
"mcp-startup-epilogue": 'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output.\n\nPlain `webstudio mcp` starts the stdio MCP server for real MCP clients. Do not manually type or pipe raw JSON-RPC frames into it from an interactive shell or PTY. From a shell, use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for one bounded tool call. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for small multi-call batches in one shared CLI session. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.\n\nPut each local CLI call in its own shell command; do not chain helper commands with `&&`, `;`, command substitution, or shell wrappers around the CLI when reporting a CLI step. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`; those can resolve an older binary. For example:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call meta.get_more_tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js mcp single-op-call insert-fragment \'{"parentInstanceId":"parent-id","fragment":"<ws.element ws:tag=\\"section\\" ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag="h2">Launch Kit</ws.element><ws.element ws:tag="p">A focused section created with Webstudio JSX.</ws.element><ws.element ws:tag="button">Get started</ws.element></ws.element>"}\' --dry-run\n```\n\nThe shorter local shortcut form is preferred for simple shell steps:\n\n```sh\nnode packages/cli/local.js meta.index\nnode packages/cli/local.js meta.get_more_tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js insert-fragment \'{"parentInstanceId":"parent-id","fragment":"<ws.element ws:tag=\\"section\\" ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag="h2">Launch Kit</ws.element><ws.element ws:tag="p">A focused section created with Webstudio JSX.</ws.element><ws.element ws:tag="button">Get started</ws.element></ws.element>"}\' --dry-run\n```\n\nFor simple authored/styled sections, run the three commands above. Do not grep source files, dump full MCP resources, or write parser scripts first. Use `list-pages`, `get-page-by-path`, or `list-instances` only when you still need the target `parentInstanceId`.\n\nWhen building for a Content-mode editor, use a Content Block (`ws:block`) around every region that editor must change. Content-mode text and supported props are editable only in Content Block descendants; content outside is read-only. Keep reusable insertable source options in the block\'s `ws:block-template` child. Templates themselves are protected; an editor\'s inserted copy becomes an editable direct child of the Content Block. Verify this structure before handoff.\n\nRun it from the linked Webstudio project root. The startup status line prints that absolute root; use it for local files such as `<project root>/.temp/script.mjs`, screenshots, and generated artifacts.\n\nFor experiments, pass `--dry-run` to local-capable mutation calls. Read the computed transaction from `meta.session.transaction` and its base build version from `meta.session.version`. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.\n\nStartup marks cached ProjectSession data stale so MCP tools read the current Builder dev build.\n\nAfter startup, use focused discovery tools with concrete JSON. For delegated design-system or “use every component” tasks, start with exactly one command: `webstudio workflow.next \'{"goal":"design-system-page"}\'`; report the returned checkpoint to the parent/user and stop until continued. For other tasks, read `meta.index` first. From a shell, `webstudio mcp list-tools` is a concise alias for the initial tool catalog. Then call shortcuts such as `webstudio meta.guide \'{"brief":"Create a design system page using every component"}\'`, `webstudio workflow.next \'{"goal":"design-system-page"}\'`, `webstudio meta.get_more_tools \'{"tools":["insert-fragment"]}\'`, `webstudio components.list \'{"source":"all"}\'`, `webstudio components.summary`, `webstudio components.search \'{"brief":"radix select"}\'`, `webstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`, `webstudio templates.list`, and `webstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`. Use `webstudio mcp single-op-call` when you need the explicit MCP form. Prefer `insert-fragment` for authored/styled sections; use `insert-component` only when inserting one automatic component template. In JSX, use `ws:style={css\\`...\\`}`for styles,`class`/`for`instead of`className`/`htmlFor`, `ActionValue`for actions instead of JavaScript functions such as`onClick={() => ...}`, only JSON-compatible plain prop values, and no host globals or dynamic code APIs. For bounded multi-step shell work, run inline JSON like `webstudio mcp run \'[{"tool":"meta.index"},{"tool":"components.search","input":{"brief":"button"}}]\'`; this reuses one CLI session without raw JSON-RPC. For larger batches, write `{ "calls": [{ "tool": "..." }] }`to`.temp/mcp-calls.json`and run`webstudio mcp run .temp/mcp-calls.json`. If any call returns `checkpoint.required`, `mcp run`stops immediately before later calls; report the checkpoint and call`checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`before continuing. For design-system or “use every component” tasks, call`workflow.next {"goal":"design-system-page"}`for the next bounded phase, report its checkpoint, wait until the parent/user continues, call`checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, then run only that phase. Start coverage phases with compact`webstudio components.coverage-plan`, checkpoint, then request component coverage details with `components.coverage-plan {"detail":"roots"}`or`components.coverage-plan {"detail":"parts"}`. Do not pass `detail`to`list-pages`; use `list-pages {}`or`get-page-by-path`for page lookup. Read MCP overview resources`webstudio://project/guide`, `webstudio://project/tools-overview`, and `webstudio://project/components-overview`when useful. Read full resources`webstudio://project/tools`and`webstudio://project/components` only when focused tools are insufficient; do not dump or parse full discovery JSON for common lookup.\n\nAfter startup, MCP clients discover capabilities with `tools/list`, `resources/list`, `meta.index`, `meta.guide`, and `meta.get_more_tools`.\n\nstdout is reserved for MCP JSON-RPC messages while the server is running.\n\nThe server advertises MCP `logging` capability and sends sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`. It also writes the same sparse lifecycle status lines to stderr, prefixed with `[webstudio mcp]`, including a final ready line. Treat the process as healthy and long-running after either ready signal; surface tool lifecycle logs as progress checkpoints and interact through MCP JSON-RPC on stdin/stdout.\n\n`webstudio mcp single-op-call` also writes sparse lifecycle lines to stderr for each one-shot call. Use those stderr lines as progress checkpoints and parse stdout as JSON on both success and failure. Failed one-shot calls print `{ "ok": false, "error": { "code": "...", "message": "..." }, "meta": ... }` to stdout and exit nonzero. If a one-shot call returns checkpoint.required, read-only discovery remains available, while later mutations fail with `CHECKPOINT_REQUIRED` until you report the checkpoint and call `webstudio mcp single-op-call checkpoint.ack \'{"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}\'`.\n\n`webstudio mcp run` also writes sparse lifecycle lines to stderr and prints one JSON result on success or failure. It stops at the first failed call or checkpoint requirement, including `CHECKPOINT_REQUIRED`, and preserves partial results so delegated agents can report exactly where they stopped.\n\nIf a CLI/MCP tool crashes, hangs, gives a confusing error, needs an undocumented workaround, or forces source-code inspection for normal usage, ask the user to report it in the Webstudio Discord `#help` channel at https://wstd.us/community. Give the user a complete copy-paste report with the goal, expected behavior, actual error, exact command/tool call, stdout JSON, stderr/lifecycle logs, environment, workaround, and secrets redacted.\n\nIf you are running as a delegated or non-streaming agent whose parent cannot see live stderr/stdout, do not batch many MCP calls silently and do not run long shell loops of shortcut or `webstudio mcp single-op-call` commands. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, return a concise checkpoint with that command/result, and wait for the parent to continue. Do not take a broad task such as creating a full design-system page as one execution unit. Call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint, wait until the parent/user continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For all-component design-system pages, checkpoint after workflow planning, discovery, page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with the `presentation-pass` workflow phase.\n',
|
|
258981
|
+
"manual-llm": '# Webstudio CLI Manual for LLMs\n\nUse this order. Stop only when a command returns ok:false.\n\nIf you are inside the Webstudio monorepo, the first command discovery should use\nthe local CLI exactly as `node packages/cli/local.js ...` from the repo root. Do\nnot use `packages/cli/bin.js` for local source-tree work; it is the packaged\nbuild entry and may use stale built output. Do not use `pnpm exec webstudio`,\n`pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can\nresolve an older binary.\n\nFor delegated design-system or “use every component” tasks, skip the generic warm-up sequence and start with exactly one MCP command: `webstudio workflow.next \'{"goal":"design-system-page"}\'`. Report that returned checkpoint to the parent/user and stop until continued.\n\n## Use MCP locally or optionally connect a client\n\nDo not install, register, or connect an MCP server merely because the user asks\nyou to edit a Webstudio project. If Webstudio MCP tools are already available,\nuse them. If you have shell access, use the local CLI shortcuts such as\n`webstudio meta.index` and `webstudio list-pages`; they expose the same project\noperations without changing client configuration or restarting the app.\n\nOnly when the user explicitly asks for persistent native MCP integration, run\nthe command for their client:\n\n- Claude Code: `webstudio connect claude`\n- Codex: `webstudio connect codex`\n- Cursor: `webstudio connect cursor`\n- VS Code or GitHub Copilot: `webstudio connect vscode`\n\nRun project operations from the linked project root. If the folder is not\nlinked, ask for an editable Builder share link and run\n`webstudio init --link <share-link> --json`. You can then use local CLI\nshortcuts immediately. Do not run `webstudio sync`, `webstudio connect`, or\nrestart the app unless the user specifically wants native MCP registration:\nMCP reads and edits the latest editable Builder build directly, including for\nprojects that have never been published. Treat the share link as a credential\nand do not include it in committed files, logs, screenshots, or issue reports.\n\nThe optional `connect` command verifies project access before changing client configuration. For\nClaude Code, Cursor, and VS Code it safely merges the `webstudio` server into\nthe client\'s project configuration. For Codex it runs both `codex mcp add` and\n`codex mcp get webstudio`; do not repeat those commands separately. Follow the\nreload, restart, or approval instruction printed by `connect`, then verify the\nloaded MCP connection by asking the client to use Webstudio MCP and list the\nproject pages. Use `--print` only to inspect the generated setup without\nchanging configuration or requiring project access.\n\n## Always\n\n1. webstudio permissions --json\n2. For bounded shell workflows, call MCP tools directly through the CLI shortcut, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and useful when you need to make the MCP boundary obvious. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for multiple calls in one shared CLI session. Use a normal JSON file path for large batches. Use long-running `webstudio mcp` only when your environment is a real MCP client. Do not manually send raw JSON-RPC to `webstudio mcp` from a shell or PTY.\n3. Read MCP `meta.index`, for example `webstudio meta.index`.\n4. Use focused MCP calls with concrete JSON: `webstudio meta.guide \'{"brief":"Create a design system page using every component"}\'`, `webstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'`, `webstudio components.list \'{"source":"all"}\'`, `webstudio components.coverage-plan`, `webstudio components.search \'{"brief":"radix select"}\'`, `webstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`, `webstudio templates.list`, and `webstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`.\n5. Read overview resources `webstudio://project/tools-overview` or `webstudio://project/components-overview` when useful. Read full resources `webstudio://project/tools` or `webstudio://project/components` only when focused tools are insufficient.\n6. Pick focused MCP read tool.\n7. Pick semantic MCP write tool.\n\nUse `webstudio schema mcp` for a compact MCP tool overview. Add `--verbose` only when exact input schemas for all tools are truly needed; otherwise prefer focused `meta.get-more-tools` and `components.*` calls.\n\nRun these commands from the linked project root. Use the MCP startup status line\'s absolute root for local files; write temporary scripts and artifacts under `<project root>/.temp`, not under a parent workspace.\n\nMonorepo quick path for a simple styled section:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nSave the readable payload in `.temp/insert-fragment.json`:\n\n```json\n{\n "parentInstanceId": "parent-id",\n "fragment": "<ws.element ws:tag=\'section\' ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag=\'h2\'>Launch Kit</ws.element><ws.element ws:tag=\'p\'>A focused section created with Webstudio JSX.</ws.element><ws.element ws:tag=\'button\'>Get started</ws.element></ws.element>"\n}\n```\n\nSingle quotes inside the JSX keep the JSON valid without backslash-escaped attributes. The same local shortcut form is shorter and preferred for simple shell steps:\n\n```sh\nnode packages/cli/local.js meta.index\nnode packages/cli/local.js meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nFor this simple path, do not grep source files, dump full MCP resources, or write parser scripts first. Use `list-pages`, `get-page-by-path`, or `list-instances` only to get the target `parentInstanceId`.\n\nWhen authoring JSX for `insert-fragment`, use Webstudio component helpers and Webstudio style syntax. Use `ws:style={css\\`...\\`}`for Webstudio-native CSS, or use React-style object syntax such as`style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.\n\nWhen the task says another user will edit a page in Content mode, use a Content Block (`ws:block`) around every editable region. Content-mode users can edit text and supported props only in descendants of that block; content outside it is read-only. Put reusable insertable options in the block\'s `ws:block-template` child. Do not put intended editor content inside that template container: templates are protected source material, while an inserted template copy becomes an editable direct child of the Content Block. Verify this structure before handoff.\n\nDo not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`. JSX fragments are declarative project data; use the built-in Webstudio helpers instead.\n\nUse Webstudio prop names in JSX: `class`, `for`, `aria-label`, and other HTML/Webstudio names. Do not use React-only aliases such as `className` or `htmlFor`; the runtime rejects them with the Webstudio prop name to use.\n\nUse Webstudio actions for event/action props. Do not pass JavaScript functions such as `onClick={() => ...}`; the runtime rejects them because functions cannot be persisted as Webstudio project data.\n\n```tsx\n<ws.element\n ws:tag="button"\n onClick={new ActionValue(["event"], expression`console.log(event)`)}\n>\n Open\n</ws.element>\n```\n\nPlain JSX prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.\n\nIf a component has a registered template with required parts, JSX must include those parts explicitly under the same parent structure as the template, for example `<radix.Switch><radix.SwitchThumb /></radix.Switch>`. Use `insert-component` when you want Webstudio to apply one component template automatically.\n\n## Animation Components\n\nBefore creating animation examples, inspect the exact components with focused discovery:\n\n```sh\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:AnimateChildren"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:AnimateText"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:StaggerAnimation"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-animation:VideoAnimation"}\'\n```\n\nUse Animation Group (`AnimateChildren`) as the controller. Put normal instances directly inside it, or put Text Animation, Stagger Animation, or Video Animation directly inside it. Text, Stagger, and Video are helper components with `contentModel.category: "none"` and should not be used as standalone section roots.\n\nDefine timing and CSS changes on the Animation Group `action` prop. Use `type:"view"` for viewport entry/exit progress and `type:"scroll"` for scroll-progress timelines. For in animations, keep the canvas styles as the final state and use `fill:"backwards"` with keyframes that describe the starting state. For out animations, use `fill:"forwards"` with keyframes that describe the ending state.\n\nText Animation settings: `slidingWindow` defaults to `5`, `easing` defaults to `linear`, and `splitBy` defaults to `char`. Use `splitBy:"space"` for word-by-word animation. The parent Animation Group keyframes provide the actual opacity, translate, scale, or other styles.\n\nStagger Animation settings: `slidingWindow` defaults to `1` and `easing` defaults to `linear`. It applies parent Animation Group progress across its direct children. Use `slidingWindow:0` for instant sequential steps, `1` for one child at a time, and values above `1` for overlapping waves.\n\nVideo Animation settings: `timeline` is a boolean. Prefer `insert-component` for Video Animation so the Video child template is inserted, then configure the Video child asset/source. Use short, seek-friendly videos for smooth scroll-linked playback.\n\nUse JSX fragments for authored animation structures when you need styled, editable examples. Put the final visual state in `ws:style` and put the starting or ending animated state in the Animation Group `action` keyframes. Include an explicit `offset` on every keyframe: use `offset: 0` for starting-state keyframes with `fill:"backwards"` and `offset: 1` for ending-state keyframes with `fill:"forwards"`.\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n axis: "block",\n animations: [\n {\n name: "Fade up on entry",\n timing: {\n fill: "backwards",\n rangeStart: ["entry", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["entry", { type: "unit", value: 100, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n opacity: { type: "unit", value: 0, unit: "number" },\n translate: {\n type: "tuple",\n value: [\n { type: "unit", value: 0, unit: "number" },\n { type: "unit", value: 24, unit: "px" },\n ],\n },\n },\n },\n ],\n },\n ],\n }}\n>\n <ws.element\n ws:tag="section"\n ws:style={css`\n display: grid;\n gap: 16px;\n padding: 48px;\n border-radius: 24px;\n background: #111827;\n color: white;\n `}\n >\n <ws.element ws:tag="h2">Launch metrics</ws.element>\n <ws.element ws:tag="p">\n A polished card that fades up as it enters the viewport.\n </ws.element>\n </ws.element>\n</animation.AnimateChildren>\n```\n\nFor Text Animation, keep `animation.AnimateText` as the direct child of Animation Group and place the text-containing element inside it:\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n animations: [\n {\n name: "Parallax In",\n timing: {\n fill: "backwards",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 70, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n translate: {\n type: "tuple",\n value: [\n { type: "unit", value: 0, unit: "number" },\n { type: "unit", value: 100, unit: "px" },\n ],\n },\n },\n },\n ],\n },\n {\n name: "Opacity In",\n timing: {\n fill: "backwards",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 70, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n opacity: { type: "unit", value: 0, unit: "number" },\n },\n },\n ],\n },\n {\n name: "Scale In",\n timing: {\n fill: "backwards",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 70, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n scale: {\n type: "tuple",\n value: [\n { type: "unit", value: 5, unit: "number" },\n { type: "unit", value: 5, unit: "number" },\n ],\n },\n },\n },\n ],\n },\n {\n name: "Parallax Out",\n timing: {\n fill: "forwards",\n rangeStart: ["cover", { type: "unit", value: 50, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 100, unit: "%" }],\n },\n keyframes: [\n {\n offset: 1,\n styles: {\n translate: {\n type: "tuple",\n value: [\n { type: "unit", value: 0, unit: "number" },\n { type: "unit", value: -100, unit: "px" },\n ],\n },\n scale: {\n type: "tuple",\n value: [\n { type: "unit", value: 5, unit: "number" },\n { type: "unit", value: 5, unit: "number" },\n ],\n },\n opacity: { type: "unit", value: 0, unit: "number" },\n },\n },\n ],\n },\n ],\n insetStart: { type: "unit", value: 5, unit: "%" },\n insetEnd: { type: "unit", value: 5, unit: "%" },\n isPinned: true,\n }}\n>\n <animation.AnimateText\n splitBy="space"\n slidingWindow={5}\n easing="easeOutQuart"\n >\n <ws.element ws:tag="h2">Animate words with controlled rhythm</ws.element>\n </animation.AnimateText>\n</animation.AnimateChildren>\n```\n\nFor Stagger Animation, put the repeated cards or rows directly inside `animation.StaggerAnimation`:\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n animations: [\n {\n timing: {\n fill: "backwards",\n rangeStart: ["contain", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["contain", { type: "unit", value: 30, unit: "%" }],\n },\n keyframes: [\n {\n offset: 0,\n styles: {\n opacity: { type: "unit", value: 0, unit: "number" },\n },\n },\n ],\n },\n ],\n }}\n>\n <animation.StaggerAnimation>\n <ws.element\n ws:tag="article"\n ws:style={css`\n padding: 20px;\n border: 1px solid #d1d5db;\n border-radius: 16px;\n `}\n >\n Plan\n </ws.element>\n <ws.element\n ws:tag="article"\n ws:style={css`\n padding: 20px;\n border: 1px solid #d1d5db;\n border-radius: 16px;\n `}\n >\n Build\n </ws.element>\n <ws.element\n ws:tag="article"\n ws:style={css`\n padding: 20px;\n border: 1px solid #d1d5db;\n border-radius: 16px;\n `}\n >\n Launch\n </ws.element>\n </animation.StaggerAnimation>\n</animation.AnimateChildren>\n```\n\nFor Video Animation, use the registered template via `insert-component` when possible. If you author JSX, include the Video child explicitly:\n\n```tsx\n<animation.AnimateChildren\n action={{\n type: "view",\n animations: [\n {\n name: "Video progress",\n timing: {\n fill: "both",\n rangeStart: ["cover", { type: "unit", value: 0, unit: "%" }],\n rangeEnd: ["cover", { type: "unit", value: 100, unit: "%" }],\n },\n keyframes: [{ offset: 0, styles: {} }],\n },\n ],\n }}\n>\n <animation.VideoAnimation timeline={true}>\n <$.Video\n preload="auto"\n autoPlay={true}\n muted={true}\n playsInline={true}\n crossOrigin="anonymous"\n />\n </animation.VideoAnimation>\n</animation.AnimateChildren>\n```\n\n## Command Surface Boundary\n\n- Use top-level `webstudio ...` shell commands for setup, sync/import/build/preview/screenshot, permissions, publish/domains, schema, registry inspection, man, and starting MCP.\n- Use MCP tools for Builder project data manipulation: pages, instances/components, props, text, styles, tokens, variables, resources, assets, breakpoints, redirects, and raw patches.\n- From a shell, call MCP tools with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio insert-fragment \'<json>\' --dry-run`. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `--input-file` for large payloads.\n- Inside the Webstudio monorepo, call the local CLI as its own command: `node packages/cli/local.js ...`. Do not wrap the CLI call in `pwd && ...`, command substitution, `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`.\n- For experiments, pass `--dry-run` to local-capable mutation calls. Read the computed transaction from `meta.session.transaction` and its base build version from `meta.session.version`. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.\n- For bounded multi-step shell work, run inline JSON with `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'`; this reuses one CLI session without raw JSON-RPC. For large batches, write `{ "calls": [{ "tool": "..." }] }` to a normal JSON file and run `webstudio mcp run .temp/mcp-calls.json`.\n- Use JSON strings for `brief` fields. Never pass boolean flags such as `{"brief":true}`.\n- Treat `webstudio mcp single-op-call` and `webstudio mcp run` stderr lines as progress checkpoints; stdout remains JSON on both success and failure. On failure, parse stdout for `{ "ok": false, "error": { "code": "...", "message": "..." } }` before deciding what to fix.\n- If a CLI/MCP tool crashes, hangs, gives a confusing error, needs an undocumented workaround, or forces source-code inspection for normal usage, ask the user to report it in Discord `#help` at https://wstd.us/community. Give them a complete copy-paste report with the goal, expected behavior, actual error, exact command/tool call, stdout JSON, stderr/lifecycle logs, environment, workaround, and secrets redacted.\n- Run one-shot `webstudio mcp single-op-call` commands sequentially against a linked `.webstudio` folder. If a command returns `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially.\n- In delegated or non-streaming agent environments, do not batch many MCP calls silently and do not wrap many shortcut or `webstudio mcp single-op-call` commands in a shell loop. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, report that command/result, then wait for the parent to continue. Do not take a broad task such as creating a full design-system page as one execution unit. Call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint, wait until the parent continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For all-component design-system pages, checkpoint after workflow planning, discovery, page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with `workflow.next {"goal":"design-system-page","phase":"presentation-pass"}`. Coverage 72/72 is necessary but not sufficient: the page must be organized into styled, real-world examples, not raw unstyled component dumps.\n- For design-system or “use every component” tasks, start with compact `webstudio components.coverage-plan`, checkpoint, then request component coverage details with `webstudio components.coverage-plan \'{"detail":"roots","offset":20}\'` or `{"detail":"parts"}` only when needed. Do not pass `detail` to `list-pages`; use `list-pages {}` or `get-page-by-path` for page lookup.\n- MCP tool shortcuts are only for MCP tools. If a shortcut is ambiguous with a real top-level command, the real top-level command wins; use `webstudio mcp single-op-call <tool> \'<json>\'` to force the MCP path.\n\n## LLM Implementation Process\n\nUse this process for user requests that change Webstudio content, layout, styles, assets, pages, redirects, resources, or publishing state:\n\n1. Discover capabilities with `webstudio man --json`, `webstudio schema api`, `webstudio schema mcp`, MCP `meta.index`, `meta.guide`, `meta.get-more-tools`, `components.list`, `components.summary`, `components.coverage-plan`, `components.search`, `components.get`, `templates.list`, and `templates.get`. From a shell, prefer shortcut calls such as `webstudio meta.index` and `webstudio components.search \'{"brief":"button"}\'` for these focused tool calls; use `webstudio mcp single-op-call` when you need the explicit MCP form. Read full resources such as `webstudio://project/tools` and `webstudio://project/components` only when needed. Do not write scripts to parse full MCP discovery JSON for normal lookup.\n2. Inspect current project state with semantic reads such as `get-project-settings`, `list-pages`, `get-page-by-path`, `list-instances`, `inspect-instance`, `get-styles`, `list-assets`, `list-breakpoints`, and `snapshot` only when needed. Before changing a project, read `get-project-settings` and follow any non-empty `meta.agentInstructions`. These are shared project instructions, not a place for secrets.\n3. Mutate the Webstudio project with semantic MCP write tools first. Prefer MCP `insert-fragment` for authored/styled sections, use `insert-component` only for one automatic component template, then `update-text`, `update-props`, `update-styles`, `upload-asset`, `create-page`, and page/project settings tools over raw patches.\n4. Use `apply-patch` only when no semantic tool covers the required change, and only after reading the latest snapshot/version.\n5. For visual/design work, regenerate or preview the generated app, capture a screenshot, inspect it with vision, and iterate before final response.\n6. Report what changed and what verification ran.\n\n## Visual Design Workflow\n\nFor requests involving visible HTML/CSS, layout, typography, colors, imagery, responsive behavior, or screenshots:\n\n1. Read editable Webstudio structure first: pages, instances, props, styles, breakpoints, assets, and relevant text.\n2. Do not use generated route/component files as the source of truth for editable content.\n3. Make edits through Webstudio semantic commands/MCP tools so the result stays editable in Builder and survives the next `webstudio build`.\n4. Keep generated project files current, start preview, and capture the changed page with `screenshot`.\n5. Use `screenshot.diff` when a baseline exists and inspect screenshot/diff artifacts with vision before finishing.\n6. If vision or screenshot tooling is unavailable, state that explicitly and explain what fallback verification was used.\n\n## Responsive Verification Workflow\n\nFor responsive page work, use Builder breakpoints as the source of truth:\n\n1. Read breakpoints with `list-breakpoints` before deciding responsive behavior.\n2. Apply responsive styles with existing Builder breakpoint ids; do not invent CSS media queries or breakpoint names when Webstudio breakpoint data exists.\n3. Pick screenshot viewport widths from the project breakpoints: include a desktop width, each defined max-width or min-width edge, and a narrow mobile width.\n4. Capture each viewport with `screenshot`, for example `{"path":"/","output":"home-375.png","viewport":{"width":375,"height":812}}` and `{"path":"/","output":"home-1440.png","viewport":{"width":1440,"height":900}}`.\n5. Inspect every viewport screenshot with vision before finishing, checking layout, overflow, hidden content, text wrapping, and breakpoint-specific style changes.\n6. If any viewport fails, update styles through semantic Webstudio tools and repeat screenshots for the affected breakpoints.\n\n## Generated Files Guardrails\n\n- Do not edit `app/__generated__`, generated route files, generated page files, generated CSS, or build output for normal Webstudio content/design requests.\n- Do not replace generated page components with handcrafted app code unless the user explicitly asks for code-only export customization.\n- Generated files are build artifacts and may be overwritten by `webstudio build`.\n- If a task truly requires generated app customization, keep it outside `app/__generated__` where possible and explain that it is not editable Webstudio content.\n\n## Values vs Bindings\n\nBefore authoring unfamiliar expressions, read `webstudio://project/expressions` with MCP `resources/read` or `webstudio mcp read-resource webstudio://project/expressions`. It documents the supported expression subset, method allowlist, scope, Collection context, and validation limits.\n\n- Use direct value tools for fixed content. For one visible text child, use `update-text` with plain `text`. For a bounded multi-instance literal replacement, use `replace-text` with `find`, `replace`, `pagePath` or `pageId`, and `limit`; it does not change expression children. Use `replace-prop-text` for bounded changes inside static string props, optionally limited to prop names or instance ids; it never changes dynamic bindings. For static props such as `aria-label`, `alt`, `id`, `class`, `href`, or button labels stored as props, use `update-props` with the prop\'s direct type/value.\n- Use `bind-props` only when the prop must stay dynamic: an expression, resource result, action, or existing scoped runtime context such as `system`. Do not use `bind-props` just to set a fixed string.\n- Direct prop string example: `{"updates":[{"instanceId":"button-id","name":"aria-label","type":"string","value":"Open menu"}]}`.\n- Expression binding example: `{"bindings":[{"instanceId":"link-id","name":"href","binding":{"type":"expression","value":"currentPost.url"}}]}`.\n- Page metadata fields such as `title`, `description`, `language`, `redirect`, and custom meta content accept plain fixed text. For computed values, pass JavaScript expression code such as `pageTitle ?? "Pricing | Acme"`.\n- Page `status` accepts a fixed HTTP status code as a number from 200 through 599, for example `302`. For a dynamic status, pass JavaScript expression code such as `system.status`.\n- Page metadata update example: use `update-page` with `{"pageId":"page-id","values":{"title":"Pricing | Acme","meta":{"description":"Plans for teams"}}}`.\n- Draft a page with `update-page` and `{"pageId":"page-id","values":{"isDraft":true}}`. It remains editable and previewable but is omitted from every publish target, including staging, and from sitemap output.\n- Stage a draft page for a future publish with `{"pageId":"page-id","values":{"isDraft":false}}`. This clears draft state but does not deploy the site. The home page and `/*` catch-all page cannot be drafts.\n- Resource `url` accepts plain fixed URLs and paths. For computed URLs, pass JavaScript expression code such as `"https://api.example.com/items?tag=" + filters.tag`. Resource header values, search parameter values, and text bodies accept expressions for dynamic values; for fixed text, use `{ "type": "literal", "value": "application/json" }`.\n- Resource update example: use `update-resource` with `{"resourceId":"resource-id","values":{"url":"https://api.example.com/items"}}`.\n- Assets is one system resource with one response shape and always executes a structured query. `create-assets-resource` without `query` uses the default URL and optional image-dimensions output. Provide query configuration to control filtering, sorting, pagination, selected fields, or file content.\n- For a Markdown-backed blog, create exactly two Builder page definitions: a fixed `/blog` overview and one `/blog/:slug` detail page. Both pages load content through Assets resources. Never create one Builder page per post or duplicate Markdown content into static page structures. Read `get-asset-field-catalog`, validate each structured query with `validate-asset-query`, then call `create-assets-resource` or `update-assets-resource`. Set `values.query:null` to restore the default query.\n- Optimize every explicit Assets query for the deployed content-database size. Use `output.mode:"fields"` and select only fields that are actually rendered or otherwise required by the query. Keep `includeMetadata:false` unless the rendered value needs file metadata; diagnostics are returned separately. Do not use `output.mode:"all"` as a convenience default.\n- Every reachable Assets data source contributes to the shared database. Keep one final resource per rendered query. Update an existing scoped resource instead of creating a placeholder, preview copy, or repair replacement, and remove obsolete duplicate resources and data sources.\n- Make a bounded overview fully static: use literal values for its filters, limit, and offset, add a deterministic ID tie-breaker to its sort, and use `content.mode:"none"`. This lets compilation materialize the small overview result instead of retaining overview-only fields across every candidate article. Reserve runtime expressions for values that are truly dynamic, such as `system.params.slug` on the detail route.\n- Query Markdown files directly and use `content.mode:"markdown-body-ref"` when rendering their bodies. The published database keeps only metadata and document references, filters and paginates first, and fetches the selected Markdown bodies from Asset storage at runtime. Do not create companion JSON descriptors merely to avoid embedding Markdown.\n- `full` and bounded `range` request embedded file bytes. Use them only when the caller explicitly requires the complete source or a byte range.\n- Deferred Markdown bodies exclude frontmatter and resolve conventional relative links and images such as `../images/hero.png` to matching Assets. Markdown Embed permits sanitized figures, audio, video, and iframes, but removes scripts, inline event handlers, and unsafe URLs.\n- Assets expose an ID-keyed map at `<dataSourceName>.data` and collection information at `<dataSourceName>.meta`. Bind a listing Collection to `posts.data` and a one-result detail Collection to `post.data`; each item value contains selected fields and its item key is the asset ID. Read frontmatter or JSON fields from `item.properties` and the resolved Markdown body from `item.content.text`.\n- Use `preview-asset-query` with concrete values before binding expressions in the saved resource. Inspect `__diagnostics__.query` for the temporary query-only footprint and `__diagnostics__.database` for the merged database built from all reachable Assets queries. Only `database.usedBytes` counts toward `database.maxBytes`; query sizes are not separate allowances and must not be summed. Compare `usedBytes`, `unboundedBytes`, and `truncated` within both scopes. A finished Markdown blog must include every source document without truncation, contain no embedded Markdown bodies, and retain only the intended materialized overview query. When merged usage approaches the limit, remove duplicate reachable resources first, then unused output fields, then narrow candidate files. Inspect saved mode and configuration with `list-assets-resources` or `get-assets-resource`; shared index maintenance is automatic.\n- Data variable values support `string`, `number`, `boolean`, and `json`. Use `json` for all arrays, objects, filters, and nested data.\n- Parameters are internal scoped runtime values from pages, collections, or components. They are not a public authoring surface: do not create, update, or delete parameter records. Public tools should preserve existing parameter records and may reference documented context values such as `system` in expressions where they are already in scope.\n- Use scoped resources for read data. A GET resource created with `scopeInstanceId`/`dataSourceName` defaults to `exposeAsDataSource:true`, becomes a scoped resource data variable, is generated into the page resource `data` map, and may be loaded while rendering the page. Read the loaded resource result from its wrapper, usually `.data`.\n- Use prop-bound resources for actions. A resource created without `scopeInstanceId` and bound to a component prop such as Form `action` with `bind-props` and `binding.type: "resource"` becomes an action resource in the page resource `action` map. Use this for POST, PUT, DELETE, webhooks, GraphQL submissions, and anything that should run only from an explicit form/action flow.\n- POST, PUT, and DELETE resources default to `exposeAsDataSource:false`, even with a scope. Set `exposeAsDataSource:true` only for an intentional render-time read such as a GraphQL POST query; provide `scopeInstanceId` and inspect the returned warning. Set it to `false` during `update-resource` to detach existing render-time exposure.\n- For dynamic resource query parameters prefer `searchParams`, for example `{"name":"tag","value":"filters.tag"}`. Use `{"type":"literal","value":"website"}` for fixed request text. Header values can use an expression such as `"Bearer " + auth.token`. Body can be an object expression, including GraphQL payloads such as `{ query: "...", variables: { slug: system.params.slug } }`.\n- Resource methods are `get`, `post`, `put`, and `delete`. Optional resource controls are `graphql` and `system`. Use `control:"graphql"` for GraphQL POST resources with query bodies. Use `control:"system"` for built-in local resource URLs such as `"/$resources/current-date"` and for resources reading the built-in `system` parameter. The built-in system fields are `system.origin`, `system.pathname`, `system.params`, and `system.search`; do not use `system.path`.\n- Whenever an array or object from a resource or data variable should render repeated UI, call `insert-collection` with the complete iterable and one repeated-item JSX root. The command creates the Collection, private item parameters, iterable binding, and descendant item bindings atomically. Use `collectionItem` and `collectionItemKey` expressions in the item JSX. Wrap multiple repeated siblings in one Element, and give repeated Radix items stable unique `value` bindings.\n- Expressions are single JavaScript expressions, not statements or functions. Functions, arrow functions, classes, `new`, `this`, `await`, imports, arbitrary calls, increment/decrement, and assignment outside actions are unsupported. Prefer optional chaining, nullish coalescing, ternaries, property/index access, operators, and the documented string/array methods.\n\n## Pick Read Command\n\n{{readFirst}}\n\n## Pick Write Command\n\n{{taskRecipeIndex}}\n\n## Raw Patch Only If Needed\n\n1. Use MCP tool: snapshot.\n2. Write BuildPatchTransaction[].\n3. Use MCP tool: apply-patch.\n\n## MCP Argument Examples\n\nMCP tools receive JSON argument objects, not CLI flags. Use these shapes:\n\n{{mcpArgumentExampleIndex}}\n\n## Rules\n\n- Never guess ids for existing records. Read them first.\n- Never use project ids from user input. Commands use the configured project.\n- Use --refresh before a local-capable command when cached data may be stale.\n- Pass --json only to commands whose help/schema documents it. Do not add --json to top-level commands such as sync unless supported.\n- On VERSION_CONFLICT, read MCP snapshot again, regenerate the patch, then retry.\n- Treat stdout JSON as the API contract and stderr as diagnostics.\n- For visual/design work, verify the rendered result with vision before finishing.\n- Do not edit generated files for normal Webstudio content/design requests.\n- Use direct values for static strings and bindings only for dynamic expressions/resources/actions.\n- Use plain fixed text where documented. Only encode a quoted JavaScript string literal when a field is explicitly documented as an expression-only value.\n- Confirm destructive commands with --confirm only when user requested deletion/unpublish/replacement.\n- Use webstudio schema api for machine-readable top-level command metadata and webstudio schema mcp for MCP tool schemas.\n\n## Known Gaps\n\n{{knownCliGapIndex}}\n',
|
|
258982
|
+
"manual-mcp": '# Webstudio MCP Manual\n\n`webstudio mcp` starts a stdio MCP server for real MCP clients. Shell users can call MCP tools with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. `webstudio mcp single-op-call` is the explicit equivalent and prints the structured JSON result. `webstudio mcp run` runs multiple MCP tool calls from inline JSON or a normal JSON file in one shared CLI session. Do not manually type or pipe raw JSON-RPC frames into `webstudio mcp` from an interactive shell or PTY.\n\n## Startup\n\nIf you are already working with a shell-capable agent, it can use the local CLI\ndirectly. Native MCP client registration is optional. Give the editable Builder\nshare link only when the trusted agent asks for it. Treat the share link as a\ncredential: do not include it in committed files, screenshots, logs, or issue\nreports.\n\n1. Configure a project with `webstudio init --link <api-share-link> --json`.\n2. Check capabilities with `webstudio permissions --json`.\n3. Use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for individual MCP tool calls. Use the explicit equivalent `webstudio mcp single-op-call <tool> \'<json>\'` when you need to force the MCP path, or `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for bounded multi-call workflows. Use `webstudio mcp run .temp/mcp-calls.json` for large batches.\n4. Start discovery with `meta.index`, then call focused tools with concrete JSON, for example `webstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'`.\n\nDo not run `webstudio sync`, install an MCP server, change client configuration,\nor restart the app for this local CLI workflow.\n\nWhen the user explicitly wants persistent native MCP integration, run\n`webstudio connect claude`, `webstudio connect codex`, `webstudio connect\ncursor`, or `webstudio connect vscode`. This optional command changes client\nconfiguration, so follow its client-specific reload or restart instruction.\nUse `--print` to inspect the generated setup without changing configuration or\nrequiring project access. For Codex, `connect` registers and verifies the server\nthrough the Codex CLI. Before changing client configuration, `connect` verifies\nthat the saved project endpoint is reachable and its credential is accepted.\n\nStart MCP from the linked Webstudio project root. The lifecycle status line prints that absolute root; create local scripts, screenshots, and temporary artifacts under that root, for example `<project root>/.temp/script.mjs`. If the shell starts in a parent workspace, `cd` into the project root first or use absolute paths.\n\nWhen developing inside the Webstudio monorepo, start the local CLI exactly as `node packages/cli/local.js mcp` from the repo root. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can resolve an older binary.\n\nWhile the server is running, stdout is reserved for MCP JSON-RPC messages. Do not print human text from the server process. The server advertises MCP `logging` capability and emits sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`; stderr also mirrors these sparse lifecycle fallback lines prefixed with `[webstudio mcp]`.\n\n## One-Shot Tool Calls\n\nUse the shortcut `webstudio <tool> \'<json>\'` when you are operating from a shell and need one MCP tool result. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and avoids writing temporary Node.js stdio client scripts.\n\nExamples:\n\n```sh\nwebstudio mcp single-op-call meta.index\nwebstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nwebstudio mcp single-op-call components.list \'{"source":"all"}\'\nwebstudio mcp single-op-call components.coverage-plan\nwebstudio mcp single-op-call components.search \'{"brief":"radix select"}\'\nwebstudio mcp single-op-call components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call templates.list\nwebstudio mcp single-op-call templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json\n```\n\nShortcut equivalents:\n\n```sh\nwebstudio meta.index\nwebstudio meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nwebstudio components.list \'{"source":"all"}\'\nwebstudio components.coverage-plan\nwebstudio components.search \'{"brief":"radix select"}\'\nwebstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio templates.list\nwebstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio insert-fragment --input-file .temp/insert-fragment.json\n```\n\n### Tool name convention\n\nMCP tool names are opaque strings, not JavaScript property access. A dot separates a namespace from its tool name, and every segment uses lowercase kebab-case. For example, `components.coverage-insert-next` is the `coverage-insert-next` tool in the `components` namespace. Pass the complete name as one CLI argument: `webstudio components.coverage-insert-next`.\n\n### Readable fragment inputs\n\nPrefer `--input-file` for JSX so JSON and shell quoting do not obscure the fragment. For example, save this as `.temp/insert-fragment.json`:\n\n```json\n{\n "parentInstanceId": "root-id",\n "fragment": "<ws.element ws:tag=\'section\' ws:style={css`padding: 32px; display: grid; gap: 16px;`}><ws.element ws:tag=\'h2\'>Northstar Product OS</ws.element><ws.element ws:tag=\'p\'>Reusable patterns for teams.</ws.element></ws.element>"\n}\n```\n\nThen run `webstudio insert-fragment --input-file .temp/insert-fragment.json`. Single quotes inside the JSX keep the JSON valid and readable without backslash-escaped attributes.\n\nWrite and review larger fragments as JSX before placing them in the `fragment` field. Common patterns:\n\n```tsx\n<ws.element\n ws:tag="section"\n style={{ padding: 32, borderRadius: 16 }}\n>\n <ws.element ws:tag="h2">Operations Console</ws.element>\n <ws.element ws:tag="p">\n React-style object styles become editable Webstudio styles.\n </ws.element>\n</ws.element>\n\n<ws.element\n ws:tag="section"\n ws:tokens={[token("accent", css`color: #0f766e;`)]}\n>\n <ws.element\n ws:tag="button"\n onClick={new ActionValue(["event"], expression`console.log(event)`)}\n >\n Track launch\n </ws.element>\n</ws.element>\n\n<ws.element ws:tag="section">\n <radix.Switch>\n <radix.SwitchThumb />\n </radix.Switch>\n</ws.element>\n```\n\nRules:\n\n- Inside the Webstudio monorepo, replace `webstudio` in the examples above with `node packages/cli/local.js`, for example `node packages/cli/local.js meta.index`.\n- For a simple authored/styled section, run `meta.index`, then `meta.get-more-tools \'{"tools":["insert-fragment"]}\'`, then `insert-fragment`. Do not grep source files, dump full MCP resources, or write parser scripts first.\n- In `insert-fragment` JSX, use ``ws:style={css`...`}`` for Webstudio-native CSS, or use React-style object syntax such as `style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.\n- Do not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`.\n- Use Webstudio prop names such as `class` and `for`; do not use React aliases `className` or `htmlFor`.\n- Use Webstudio actions for event/action props, for example `onClick={new ActionValue(["event"], expression\\`console.log(event)\\`)}`. Do not pass JavaScript functions such as `onClick={() => ...}`.\n- Plain prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.\n- Template-backed components used in JSX must include required child/part components explicitly under the same parent structure as the template, for example `<radix.Switch><radix.SwitchThumb /></radix.Switch>`. Use `insert-component` when you want one automatic registered component template.\n- The positional input is JSON and defaults to `{}`.\n- Use `--input-file` for large mutation payloads.\n- Use `--dry-run` with local-capable mutation tools when you need a patch plan without committing. The computed transaction is returned in `meta.session.transaction`, and `meta.session.version` is its base build version. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.\n- The command prints JSON to stdout for both success and failure. Success uses the same `structuredContent` shape MCP tools return: `{ "ok": true, "data": ..., "meta": ... }`. Failure prints `{ "ok": false, "error": { "code": "...", "message": "..." }, "meta": ... }` and exits nonzero.\n- The command writes sparse progress to stderr, including start, success/failure, elapsed time, and committed status when the tool returns session metadata.\n- Invalid argument types fail loudly with path-specific messages, for example `meta.guide input.brief must be a string when provided`.\n- Run one-shot shortcut or `mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially.\n- To work with another previously linked project without changing the directory\'s default link, start MCP or a shell call with `--project <projectId>`, for example `webstudio mcp --project <projectId>` or `webstudio mcp single-op-call list-pages --project <projectId>`. Selected projects use isolated local session and checkpoint files.\n- If you are a delegated agent and your parent cannot see live stderr/stdout, do not run a long sequence of shortcut or `mcp single-op-call` commands silently and do not wrap many calls in a shell loop. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one `webstudio <tool>` or `webstudio mcp single-op-call` command, report that command/result, then wait before the next MCP command. For all-component design-system pages, checkpoint after discovery, checkpoint after page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with the `presentation-pass` workflow phase. Coverage alone is not completion; organize examples into styled sections/cards.\n\n## Reporting CLI/MCP Issues\n\nIf a CLI/MCP tool gives a confusing error, crashes, hangs, produces invalid output, requires an undocumented workaround, or makes you inspect source code to understand normal usage, ask the user to report it in the Webstudio Discord `#help` channel: https://wstd.us/community.\n\nGive the user a complete copy-paste report. Include only non-secret values: never include auth tokens, private URLs, cookies, API keys, passwords, or proprietary project data. Redact them as `<redacted>`.\n\nCopy-paste template:\n\n````md\nWebstudio CLI/MCP issue report\n\nWhat I was trying to do:\n<short user goal, for example "Create a resource from an external API and render it in a collection">\n\nWhat I expected:\n<what should have happened>\n\nWhat happened instead:\n<exact error, confusing behavior, hang, missing docs, or workaround required>\n\nCommand/tool used:\n\n```sh\n<exact command or MCP tool call, with tokens/secrets redacted>\n```\n\nStructured output / error:\n\n```json\n<stdout JSON or MCP structuredContent, if available, with secrets redacted>\n```\n\nStderr / lifecycle logs:\n\n```txt\n<stderr lines, timings, checkpoint messages, or stack trace, with secrets redacted>\n```\n\nEnvironment:\n\n- CLI command path: <webstudio / node packages/cli/local.js / other>\n- Webstudio CLI version: <from command output if known>\n- OS: <macOS / Windows / Linux / unknown>\n- Node version: <node -v if known>\n- Project/session state: <linked project, local .webstudio session, preview, MCP server, or unknown>\n\nWorkaround tried:\n<what the agent/user tried next, and whether it worked>\n\nWhy this should be improved:\n<one sentence: better error message, docs, schema, tool behavior, etc.>\n````\n\n## Shared-Session Shell Runs\n\nUse `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` when you are operating from a shell and need several MCP tool calls to share one CLI session without hand-writing JSON-RPC. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.\n\nUse `mcp run` for long-lived tools such as `preview.start`. A one-shot `mcp single-op-call preview.start` cannot keep ownership of a preview server for a later screenshot or stop call. Put `preview.start`, `screenshot`, and `preview.stop` in one shared `mcp run` process, or use a real long-running MCP client.\n\nInput shape:\n\n```json\n{\n "calls": [\n { "tool": "meta.index" },\n { "tool": "components.find", "input": { "brief": "radix select" } }\n ]\n}\n```\n\nRules:\n\n- The command prints JSON to stdout for both success and failure. It stops at the first failed call and prints partial results in `{ "ok": false, "error": ..., "data": { "completedCalls": ..., "results": [...] }, "meta": ... }`, then exits nonzero.\n- If a call returns `checkpoint.required`, read-only discovery and inspection remain available, but mutations and state-changing session tools return `CHECKPOINT_REQUIRED`. Stop and report the checkpoint to the parent/user. Only after the parent/user continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}` before continuing mutations.\n- For `mcp single-op-call`, checkpoint requirements persist across later one-shot CLI processes until you call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`.\n- Use this instead of manually sending JSON-RPC frames to `webstudio mcp` from a shell.\n\n### Cross-project batches\n\nAdd `projects` to the same `mcp run` manifest to run focused reads, audits, or dry runs across independently linked project roots:\n\n```json\n{\n "concurrency": 2,\n "calls": [\n { "tool": "status" },\n { "tool": "audit", "input": {} },\n {\n "tool": "update-project-settings",\n "input": { "meta": { "siteName": "Reviewed" } },\n "dryRun": true\n }\n ],\n "projects": [\n { "id": "site-a", "root": "../site-a" },\n { "id": "site-b", "root": "../site-b" }\n ]\n}\n```\n\nProject roots and an optional `progressFile` are resolved relative to the manifest file. Each project may provide its own `calls` instead of using the top-level calls. Each root must already be linked with its own `.webstudio/config.json`; the runner creates an independently authenticated ProjectSession and uses root-scoped session, audit, preview-data, and checkpoint paths without changing the process working directory.\n\nConcurrency defaults to 2, is capped at 16, and can be set in the manifest or overridden with `--concurrency`. A failure is reported for that project while other projects continue. Progress is saved after every successful call; rerunning with the default `--resume` skips completed projects and starts failed projects after their last confirmed successful call. Reads and dry runs may be retried. A committed mutation interrupted after dispatch is marked `AMBIGUOUS_MUTATION_RESULT` and is never replayed automatically; inspect that project before deciding how to continue. Use `--no-resume` only to intentionally start the complete manifest over.\n\nCommitted mutation tools are rejected in a projects batch unless the command includes `--approve-mutations`. Review the complete manifest before granting approval. `--dry-run` applies to every call and does not require mutation approval. The final stdout object is compact: project counts, one status/error record per project, elapsed time, and the progress-file path rather than every tool result.\n\n## Discovery\n\nUse MCP itself after startup, or call the same tools with `webstudio mcp single-op-call`:\n\n- `tools/list`: machine-readable available tools\n- `resources/list`: available overview and full JSON resources\n- `meta.index`: concise capability catalog\n- `meta.guide`: workflow for a user goal; call with a string brief such as `{"brief":"Create a pricing page"}`\n- `meta.get-more-tools`: detailed params, examples, namespaces, and local/server behavior; prefer exact names such as `{"tools":["insert-fragment"]}` when you know them\n- `components.list`: compact registry metadata for visible components and templates; use a focused get tool for complete details\n- `components.summary`: component counts by default; use `{"detail":"components","limit":20}` for paginated entries\n- `components.coverage-plan`: compact paged plan for design-system coverage tasks that need every component; default returns counts plus the first root page, use `{"detail":"roots"}`, `{"detail":"parts"}`, or `{"detail":"full"}` for more\n- `components.coverage-status`: page-specific covered/missing component report with `missingRoots` and `missingParts`\n- `components.search`: focused component/template search by id, namespace, label, category, or content model\n- `components.find`: compatibility alias for focused component search\n- `components.get`: full metadata for one component id\n- `templates.list`: compact metadata for template-backed insertions only\n- `templates.get`: full registry item and payload metadata for one template\n\nComponent and template registry items use a shadcn-compatible top-level shape plus Webstudio-specific superset metadata in `meta`. Use `meta.runtime` for component ids, props, states, content model, and source identity; `meta.authoring` for composition and accessibility guidance; and `meta.builder` for template insertion details and expected project-data namespaces. These items are for Builder/MCP discovery and are not a published shadcn install registry yet.\n\nPrefer the focused `components.*` tools over dumping `webstudio://project/components`. Do not write local scripts to parse full MCP discovery JSON for common component lookup.\nFor “use every component” or design-system pages, start with compact `components.coverage-plan`, checkpoint, then page through roots/parts instead of dumping the full catalog.\n\n## Consumer Capabilities\n\nMCP lets agents work on one configured Webstudio project at a time. In consumer\nterms, agents can:\n\n- Check which project they are connected to.\n- Check what the share link is allowed to do.\n- Inspect project metadata and the latest editable build.\n- Read selected project data for audits and repair.\n- Apply precise project changes against a known version.\n- List, inspect, create, update, delete, duplicate, copy, and reorder pages.\n- Set the home page.\n- Preserve old page paths for redirects or history.\n- Read and update page titles, descriptions, metadata, auth settings, and SEO fields.\n- List, create, update, duplicate, move, and delete page folders.\n- List, create, update, delete, duplicate, reorder, and reuse page templates.\n- Create pages from reusable templates.\n- Read and update project site settings.\n- Read and update marketplace product metadata.\n- List, create, update, delete, and replace redirects.\n- List, create, update, and delete responsive breakpoints.\n- List and inspect page elements.\n- Insert registered components.\n- Insert styled JSX fragments.\n- Move, reparent, clone, duplicate, wrap, unwrap, convert, rename, retag, and delete elements.\n- Fill grid cells.\n- List and update text children.\n- Update plain text and expression text.\n- Update structured rich text.\n- Add, update, delete, and bind element props.\n- Bind props to expressions, resources, actions, and runtime system values.\n- Read, add, update, delete, and replace local styles.\n- Update selected style-source styles.\n- List, create, update, attach, detach, extract, duplicate, rename, lock, unlock, reorder, clear, and delete design tokens and style sources.\n- List, define, rename, delete, and rewrite CSS variables.\n- List, create, update, and delete static data variables.\n- Create string, number, boolean, and JSON variables. Arrays use JSON.\n- Delete unused data variables.\n- List, create, update, upsert, bind, and delete resources.\n- Create HTTP resources.\n- Create GraphQL resources.\n- Create system resources.\n- Use built-in system resources for sitemap, current date, and assets.\n- List and inspect complete asset metadata; upload, download, update, move, duplicate, find usage for, replace, and delete assets.\n- List, create, rename, move, recursively duplicate, and recursively delete nested asset folders.\n- Publish to staging or production.\n- Publish to selected domains.\n- List publish builds.\n- Check publish job status.\n- Unpublish staging or production deployments.\n- List, create, update, delete, and verify custom domains.\n- Start and stop preview.\n- Capture screenshots of generated pages.\n- Compare screenshots against baselines.\n- Install OCR support for richer visual checks.\n\nUseful resources:\n\n- `webstudio://project/status`: compact current ProjectSession status\n- `webstudio://project/tools-overview`: small operation overview by capability area\n- `webstudio://project/components-overview`: small component overview with ids, labels, namespaces, and categories\n- `webstudio://project/tools`: full operation catalog; read only when focused metadata is insufficient\n- `webstudio://project/components`: full component catalog with props, states, and content model composition constraints; read only when `components.summary`, `components.find`, and `components.get` are insufficient\n- `webstudio://project/guide`: concise discovery guide\n- `webstudio://project/expressions`: expression syntax, scope, supported methods, bindings, Collection iteration context, and verification\n- `webstudio://project/accessibility-review`: evidence-based LLM accessibility-review workflow using project checks, preview, and screenshots\n\n## MCP SDK Client Imports\n\nWhen writing a local Node.js MCP client script, use the official MCP SDK package and these exact ESM imports:\n\nInside the Webstudio monorepo this package is available at the repo root. In another project, install it first with `pnpm add -D @modelcontextprotocol/sdk`.\n\n```js\nimport { Client } from "@modelcontextprotocol/sdk/client/index.js";\nimport { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";\nimport { LoggingMessageNotificationSchema } from "@modelcontextprotocol/sdk/types.js";\n```\n\nMinimal stdio client for the local Webstudio CLI:\n\n```js\nconst client = new Client({ name: "webstudio-agent", version: "1.0.0" });\n\nclient.setNotificationHandler(\n LoggingMessageNotificationSchema,\n (notification) => {\n console.error(`[mcp] ${notification.params.data}`);\n }\n);\n\nconst transport = new StdioClientTransport({\n command: "node",\n args: ["packages/cli/local.js", "mcp"],\n cwd: process.cwd(),\n stderr: "inherit",\n});\n\nawait client.connect(transport);\n\nconst index = await client.callTool({\n name: "meta.index",\n arguments: {},\n});\nconsole.log(JSON.stringify(index.structuredContent, null, 2));\n\nawait client.close();\n```\n\nUse `node packages/cli/local.js mcp` from the Webstudio monorepo root for local development, or `webstudio mcp` from a linked project where the CLI is installed. Keep stdout for JSON-RPC/structured results and surface MCP logging notifications or stderr lifecycle lines as progress.\n\n## Core Rules\n\n- stdout is reserved for MCP JSON-RPC while the server is running.\n- Operate on the configured project only.\n- Read ids before writing.\n- Prefer semantic tools over `apply-patch`.\n- Use `status` and `refresh` when cached namespaces may be stale. Pass `status {"verbose":true}` only when debugging full namespace arrays, freshness, compatibility, or diagnostic details.\n- A mutation is durable only when `meta.session.committed` is true.\n- For visual/design work, verify the rendered result with vision before finishing.\n\n## Vision Verification Loop\n\nVision-capable AI can use MCP to see what it is building:\n\n{{mcpVisionVerificationLoopMarkdown}}\n\nGenerated app setup:\n\n{{mcpGeneratedAppDependencyNotes}}\n\n## MCP Argument Examples\n\nMCP tools receive JSON argument objects:\n\n{{mcpArgumentExampleIndex}}\n\n## Screenshot Verification\n\n{{screenshotVerificationSummary}}\n',
|
|
258983
|
+
"mcp-startup-epilogue": 'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output.\n\nPlain `webstudio mcp` starts the stdio MCP server for real MCP clients. Do not manually type or pipe raw JSON-RPC frames into it from an interactive shell or PTY. From a shell, use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for one bounded tool call. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for small multi-call batches in one shared CLI session. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.\n\nPut each local CLI call in its own shell command; do not chain helper commands with `&&`, `;`, command substitution, or shell wrappers around the CLI when reporting a CLI step. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`; those can resolve an older binary. For example:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nSave the input as `.temp/insert-fragment.json`; use single quotes for JSX attributes so the JSON needs no backslash escaping:\n\n```json\n{\n "parentInstanceId": "parent-id",\n "fragment": "<ws.element ws:tag=\'section\' ws:style={css`padding: 32px; display: grid; gap: 12px;`}><ws.element ws:tag=\'h2\'>Launch Kit</ws.element><ws.element ws:tag=\'p\'>A focused section created with Webstudio JSX.</ws.element><ws.element ws:tag=\'button\'>Get started</ws.element></ws.element>"\n}\n```\n\nThe shorter local shortcut form is preferred for simple shell steps:\n\n```sh\nnode packages/cli/local.js meta.index\nnode packages/cli/local.js meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nFor simple authored/styled sections, run the three commands above. Do not grep source files, dump full MCP resources, or write parser scripts first. Use `list-pages`, `get-page-by-path`, or `list-instances` only when you still need the target `parentInstanceId`.\n\nWhen building for a Content-mode editor, use a Content Block (`ws:block`) around every region that editor must change. Content-mode text and supported props are editable only in Content Block descendants; content outside is read-only. Keep reusable insertable source options in the block\'s `ws:block-template` child. Templates themselves are protected; an editor\'s inserted copy becomes an editable direct child of the Content Block. Verify this structure before handoff.\n\nRun it from the linked Webstudio project root. The startup status line prints that absolute root; use it for local files such as `<project root>/.temp/script.mjs`, screenshots, and generated artifacts.\n\nFor experiments, pass `--dry-run` to local-capable mutation calls. Read the computed transaction from `meta.session.transaction` and its base build version from `meta.session.version`. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.\n\nStartup marks cached ProjectSession data stale so MCP tools read the current Builder dev build.\n\nAfter startup, use focused discovery tools with concrete JSON. For delegated design-system or “use every component” tasks, start with exactly one command: `webstudio workflow.next \'{"goal":"design-system-page"}\'`; report the returned checkpoint to the parent/user and stop until continued. For other tasks, read `meta.index` first. From a shell, `webstudio mcp list-tools` is a concise alias for the initial tool catalog. Then call shortcuts such as `webstudio meta.guide \'{"brief":"Create a design system page using every component"}\'`, `webstudio workflow.next \'{"goal":"design-system-page"}\'`, `webstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'`, `webstudio components.list \'{"source":"all"}\'`, `webstudio components.summary`, `webstudio components.search \'{"brief":"radix select"}\'`, `webstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`, `webstudio templates.list`, and `webstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`. Use `webstudio mcp single-op-call` when you need the explicit MCP form. Prefer `insert-fragment` for authored/styled sections; use `insert-component` only when inserting one automatic component template. In JSX, use `ws:style={css\\`...\\`}`for styles,`class`/`for`instead of`className`/`htmlFor`, `ActionValue`for actions instead of JavaScript functions such as`onClick={() => ...}`, only JSON-compatible plain prop values, and no host globals or dynamic code APIs. For bounded multi-step shell work, run inline JSON like `webstudio mcp run \'[{"tool":"meta.index"},{"tool":"components.search","input":{"brief":"button"}}]\'`; this reuses one CLI session without raw JSON-RPC. For larger batches, write `{ "calls": [{ "tool": "..." }] }`to`.temp/mcp-calls.json`and run`webstudio mcp run .temp/mcp-calls.json`. If any call returns `checkpoint.required`, `mcp run`stops immediately before later calls; report the checkpoint and call`checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`before continuing. For design-system or “use every component” tasks, call`workflow.next {"goal":"design-system-page"}`for the next bounded phase, report its checkpoint, wait until the parent/user continues, call`checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, then run only that phase. Start coverage phases with compact`webstudio components.coverage-plan`, checkpoint, then request component coverage details with `components.coverage-plan {"detail":"roots"}`or`components.coverage-plan {"detail":"parts"}`. Do not pass `detail`to`list-pages`; use `list-pages {}`or`get-page-by-path`for page lookup. Read MCP overview resources`webstudio://project/guide`, `webstudio://project/tools-overview`, and `webstudio://project/components-overview`when useful. Read full resources`webstudio://project/tools`and`webstudio://project/components` only when focused tools are insufficient; do not dump or parse full discovery JSON for common lookup.\n\nAfter startup, MCP clients discover capabilities with `tools/list`, `resources/list`, `meta.index`, `meta.guide`, and `meta.get-more-tools`.\n\nstdout is reserved for MCP JSON-RPC messages while the server is running.\n\nThe server advertises MCP `logging` capability and sends sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`. It also writes the same sparse lifecycle status lines to stderr, prefixed with `[webstudio mcp]`, including a final ready line. Treat the process as healthy and long-running after either ready signal; surface tool lifecycle logs as progress checkpoints and interact through MCP JSON-RPC on stdin/stdout.\n\n`webstudio mcp single-op-call` also writes sparse lifecycle lines to stderr for each one-shot call. Use those stderr lines as progress checkpoints and parse stdout as JSON on both success and failure. Failed one-shot calls print `{ "ok": false, "error": { "code": "...", "message": "..." }, "meta": ... }` to stdout and exit nonzero. If a one-shot call returns checkpoint.required, read-only discovery remains available, while later mutations fail with `CHECKPOINT_REQUIRED` until you report the checkpoint and call `webstudio mcp single-op-call checkpoint.ack \'{"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}\'`.\n\n`webstudio mcp run` also writes sparse lifecycle lines to stderr and prints one JSON result on success or failure. It stops at the first failed call or checkpoint requirement, including `CHECKPOINT_REQUIRED`, and preserves partial results so delegated agents can report exactly where they stopped.\n\nIf a CLI/MCP tool crashes, hangs, gives a confusing error, needs an undocumented workaround, or forces source-code inspection for normal usage, ask the user to report it in the Webstudio Discord `#help` channel at https://wstd.us/community. Give the user a complete copy-paste report with the goal, expected behavior, actual error, exact command/tool call, stdout JSON, stderr/lifecycle logs, environment, workaround, and secrets redacted.\n\nIf you are running as a delegated or non-streaming agent whose parent cannot see live stderr/stdout, do not batch many MCP calls silently and do not run long shell loops of shortcut or `webstudio mcp single-op-call` commands. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, return a concise checkpoint with that command/result, and wait for the parent to continue. Do not take a broad task such as creating a full design-system page as one execution unit. Call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint, wait until the parent/user continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For all-component design-system pages, checkpoint after workflow planning, discovery, page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with the `presentation-pass` workflow phase.\n',
|
|
258654
258984
|
"mcp-vision": '# MCP Vision Verification\n\n## Generated App Dependency Notes\n\n- `preview.start` and `webstudio preview` install generated app dependencies under `.webstudio/preview` and reuse them across regenerations.\n- Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.\n- If dependency installation fails, check npm and network configuration, then reinstall or update the Webstudio CLI if the problem persists.\n\n## Visual Verification Rule\n\nFor visual/design work, use `preview.start` and `screenshot({ path })` so vision can inspect the current MCP session before finishing and verify that generated project files are current. Iterative preview is the default: it keeps one generated-site server and browser alive, regenerates changed files, and performs an ordinary page reload without Vite HMR. Use `mode: "production"` only for release-like verification; rendered audit selects it automatically. `preview.start` is long-lived and cannot be used through one-shot `mcp single-op-call`; from a shell, use `webstudio mcp run` for preview.start/screenshot/preview.stop in one shared process, or use a long-running MCP server. When a baseline exists, use screenshot.diff to get pixel regions, OCR text changes, and diff PNG artifacts.\n\nAn authenticated project share URL is used with `webstudio init --link`; it is\nnot a generated-site preview URL. Project screenshots and rendered audits use\nthe generated local preview owned by the current CLI/MCP process. Do not pass a\nBuilder/share URL to `screenshot`, even without query parameters. Use `path`\nafter starting preview, or use `baseUrl` only for an intentional generated site\nthat is already running. Path captures verify the generated-site root marker\nand fail instead of returning a screenshot of Builder chrome.\n\n## Vision Verification Loop\n\n- Make focused page/content/style changes with semantic MCP tools.\n- Call preview.start once to keep the iterative generated site running. In shell-driven workflows, run preview.start, screenshot, and preview.stop inside one `webstudio mcp run` call so they share the same preview owner.\n- {{dependency-notes}}\n- After MCP mutations, path-based screenshots regenerate the current session in place, wait for its exact project version, and normally reload the route. The server and browser remain alive. From one-shot shell calls or another process, pass `baseUrl` with `path` to capture an already-running generated site without starting it. Use preview.stop only in the same long-running MCP server or `webstudio mcp run` process that started preview; a separate one-shot `single-op-call` process does not own another process\'s preview controller.\n- For multi-page work, capture each changed page by path through the same preview server, for example screenshot({ path: "/" }), screenshot({ path: "/pricing" }), and screenshot({ path: "/about" }). The screenshot tool navigates directly to the requested route; no browser click navigation is required.\n- For responsive work, call list-breakpoints first, then capture screenshots at viewport widths based on the Builder breakpoints plus a narrow mobile and desktop width.\n- Call screenshot with { path: "/" } or the changed page path and viewport such as { width: 375, height: 812 } and { width: 1440, height: 900 }. For an existing preview in another process, call screenshot with { baseUrl: "http://127.0.0.1:5177", path: "/" }. Use waitForSelector when the page has a reliable ready marker, waitUntil:"networkidle" for network-heavy pages, and waitForTimeout only for final visual settling.\n- {{diff}} When a baseline PNG exists, call screenshot.diff with baselinePath, currentPath, and outputDir for each page/viewport pair. Add expectedText when a specific visible phrase must be present; its assertions report pass/fail plus found and missing text. Add expectedVisual to set pass/fail limits for mismatch percentage, the number of changed regions, or an overall dominant color/brightness direction.\n- {{diff}} Read screenshot.diff textAnalysis: it reports OCR status plus text that appeared, disappeared, moved, changed content, or changed font/style geometry. If OCR is unavailable, expectedText assertions fail and textAnalysis reports why; ask the user for permission to install Tesseract, then call vision.install-ocr with { "confirm": true }, or rely on visual inspection.\n- Inspect every viewport PNG and any diff artifacts with vision, then compare layout, OCR text evidence, color, spacing, imagery, and responsive framing against the user intent.\n- If the screenshot does not match, apply another focused mutation and repeat screenshot verification.\n\n## Workflow Summary With Diff\n\nFor visual/design work, call preview.start once to start the iterative generated-site preview, then screenshot({ path, viewport }) after each focused mutation; path screenshots regenerate changed files and reload the route in the existing server and browser. For responsive work, use list-breakpoints and capture each changed page at Builder breakpoint widths plus mobile and desktop widths. Use screenshot.diff on each baseline/current page or viewport pair when a baseline exists, then inspect pixel regions, OCR textAnalysis, and PNG/diff artifacts with vision before finishing. Use mode: "production" only for release-like verification.\n\n## Workflow Summary Without Diff\n\nFor visual/design work, call preview.start once to start the iterative generated-site preview, then screenshot({ path, viewport }) after each focused mutation; path screenshots regenerate changed files and reload the route in the existing server and browser. For responsive work, use list-breakpoints and capture each changed page at Builder breakpoint widths plus mobile and desktop widths. Inspect every PNG with vision before finishing. Use mode: "production" only for release-like verification.\n\nEach screenshot result includes rendered `layout` metrics when the local browser\nprovides them. `layout.horizontalOverflow: true` is deterministic evidence that\nthe rendered document exceeds the requested viewport width. Use vision for\nclipping, wrapping, hierarchy, and other judgments that layout dimensions alone\ncannot establish.\n\nPass `includeImageMetrics: true` when an ordinary screenshot needs\n`layout.images`; rendered audit enables it automatically. The array includes\neach rendered image\'s Webstudio instance id when available, loading mode,\ncompletion state, natural dimensions, rendered dimensions, and document\nposition. Rendered audits use this evidence to report broken images, eager\nloading below the fold, and sources more than 2x the rendered dimensions in\nboth axes. Oversized-source results are optimization evidence, not universal\nperformance conformance.\n\nPass `includeResourceMetrics: true` when an ordinary screenshot needs sanitized\nResource Timing evidence; rendered audit enables it automatically. Resource\nmetrics contain only the URL pathname, initiator type, transfer/body sizes,\nduration, and browser-provided render-blocking status. Origins and query strings\nare omitted. Rendered audits report explicitly blocking resources and legacy\n`.ttf`, `.otf`, or `.woff` font files without applying a universal byte-size\nbudget.\n\n## Screenshot Verification Summary\n\nInside a long-running MCP server, call preview.start once, then use screenshot({ path, viewport }) for fast repeated checks across multiple pages. Iterative mode is the default: after MCP mutations, path screenshots regenerate changed files and reload the requested route while keeping the server and browser alive. Use mode: "production" only for release-like verification. From one-shot shell calls or another process, use screenshot({ baseUrl, path, viewport }) to capture an already-running preview/site without generating, building, starting, or restarting preview. Use path values such as "/", "/pricing", or "/about" to capture specific generated routes. For responsive work, read list-breakpoints and capture one familiar device viewport inside each Builder breakpoint range before using vision. Screenshot waits for load by default, then fonts and two layout frames; pass waitForSelector for app readiness, waitUntil:"networkidle" for network-heavy pages, and waitForTimeout for final settling. When a baseline exists, use screenshot.diff for changed regions, OCR textAnalysis, and diff artifacts on each baseline/current screenshot pair. Outside MCP, use `webstudio screenshot --path /pricing --output pricing.png` for one temporary generated preview capture, or keep `webstudio preview` running and pass its absolute URL to `webstudio screenshot` for repeated captures.\n\n## Screenshot Diff Evidence\n\n- Pixel evidence: total mismatch, changed regions, dominant color/luminance direction, diffPath, and contextDiffPath.\n- OCR evidence: textAnalysis.status, provider, and changes for appeared/disappeared/content_changed/moved/font_changed text. expectedText adds pass/fail assertions plus found and missing current-screen text. expectedVisual adds pass/fail quantitative checks for mismatch percentage, changed-region count, and the overall dominant color/brightness direction.\n- OCR dependency: screenshot.diff uses the system tesseract binary when available. If missing, it returns ocr_unavailable_tesseract_not_found_or_failed and still returns pixel evidence.\n- OCR install: MCP cannot prompt. Ask the user first; if they agree, call vision.install-ocr with { "confirm": true }. If automatic install is unavailable, follow the returned installUrl.\n- Final judgment: OCR and pixel diff are evidence. A vision-capable model must still inspect screenshots/diff artifacts and compare the rendered result to user intent.\n'
|
|
258655
258985
|
};
|
|
258656
258986
|
const cliDocTitles = {
|
|
@@ -258679,7 +259009,7 @@ const cliDocSections = {
|
|
|
258679
259009
|
},
|
|
258680
259010
|
"manual-llm": {
|
|
258681
259011
|
implementationProcess: [
|
|
258682
|
-
`Discover capabilities with webstudio man --json, webstudio schema api, webstudio schema mcp, MCP meta.index, meta.guide, meta.
|
|
259012
|
+
`Discover capabilities with webstudio man --json, webstudio schema api, webstudio schema mcp, MCP meta.index, meta.guide, meta.get-more-tools, components.list, components.summary, components.coverage-plan, components.search, components.get, templates.list, and templates.get. From a shell, prefer shortcut calls such as webstudio meta.index and webstudio components.search '{"brief":"button"}' for these focused tool calls; use webstudio mcp single-op-call when you need the explicit MCP form. Read full resources such as webstudio://project/tools and webstudio://project/components only when needed. Do not write scripts to parse full MCP discovery JSON for normal lookup.`,
|
|
258683
259013
|
"Inspect current project state with semantic reads such as get-project-settings, list-pages, get-page-by-path, list-instances, inspect-instance, get-styles, list-assets, list-breakpoints, and snapshot only when needed. Before changing a project, read get-project-settings and follow any non-empty meta.agentInstructions. These are shared project instructions, not a place for secrets.",
|
|
258684
259014
|
"Mutate the Webstudio project with semantic MCP write tools first. Prefer MCP insert-fragment for authored/styled sections, use insert-component only for one automatic component template, then update-text, update-props, update-styles, upload-asset, create-page, and page/project settings tools over raw patches.",
|
|
258685
259015
|
"Use apply-patch only when no semantic tool covers the required change, and only after reading the latest snapshot/version.",
|
|
@@ -258725,7 +259055,7 @@ const cliDocSections = {
|
|
|
258725
259055
|
'Optimize every explicit Assets query for the deployed content-database size. Use output.mode:"fields" and select only fields that are actually rendered or otherwise required by the query. Keep includeMetadata:false unless the rendered value needs file metadata; diagnostics are returned separately. Do not use output.mode:"all" as a convenience default.',
|
|
258726
259056
|
"Every reachable Assets data source contributes to the shared database. Keep one final resource per rendered query. Update an existing scoped resource instead of creating a placeholder, preview copy, or repair replacement, and remove obsolete duplicate resources and data sources.",
|
|
258727
259057
|
'Make a bounded overview fully static: use literal values for its filters, limit, and offset, add a deterministic ID tie-breaker to its sort, and use content.mode:"none". This lets compilation materialize the small overview result instead of retaining overview-only fields across every candidate article. Reserve runtime expressions for values that are truly dynamic, such as system.params.slug on the detail route.',
|
|
258728
|
-
'Query Markdown files directly and use content.mode:"markdown-body" when rendering their bodies. The published database keeps only metadata and document references, filters and paginates first, and fetches the selected Markdown bodies from Asset storage at runtime. Do not create companion JSON descriptors merely to avoid embedding Markdown.',
|
|
259058
|
+
'Query Markdown files directly and use content.mode:"markdown-body-ref" when rendering their bodies. The published database keeps only metadata and document references, filters and paginates first, and fetches the selected Markdown bodies from Asset storage at runtime. Do not create companion JSON descriptors merely to avoid embedding Markdown.',
|
|
258729
259059
|
"full and bounded range request embedded file bytes. Use them only when the caller explicitly requires the complete source or a byte range.",
|
|
258730
259060
|
"Deferred Markdown bodies exclude frontmatter and resolve conventional relative links and images such as ../images/hero.png to matching Assets. Markdown Embed permits sanitized figures, audio, video, and iframes, but removes scripts, inline event handlers, and unsafe URLs.",
|
|
258731
259061
|
"Assets expose an ID-keyed map at <dataSourceName>.data and collection information at <dataSourceName>.meta. Bind a listing Collection to posts.data and a one-result detail Collection to post.data; each item value contains selected fields and its item key is the asset ID. Read frontmatter or JSON fields from item.properties and the resolved Markdown body from item.content.text.",
|
|
@@ -258735,7 +259065,7 @@ const cliDocSections = {
|
|
|
258735
259065
|
"Use scoped resources for read data. A GET resource created with scopeInstanceId/dataSourceName defaults to exposeAsDataSource:true, becomes a scoped resource data variable, is generated into the page resource data map, and may be loaded while rendering the page. Read the loaded resource result from its wrapper, usually .data.",
|
|
258736
259066
|
'Use prop-bound resources for actions. A resource created without scopeInstanceId and bound to a component prop such as Form action with bind-props and binding.type: "resource" becomes an action resource in the page resource action map. Use this for POST, PUT, DELETE, webhooks, GraphQL submissions, and anything that should run only from an explicit form/action flow.',
|
|
258737
259067
|
"POST, PUT, and DELETE resources default to exposeAsDataSource:false, even with a scope. Set exposeAsDataSource:true only for an intentional render-time read such as a GraphQL POST query; provide scopeInstanceId and inspect the returned warning. Set it to false during update-resource to detach existing render-time exposure.",
|
|
258738
|
-
'For dynamic resource query parameters prefer searchParams, for example {"name":"tag","value":"filters.tag"}. Use {"type":"literal","value":"website"} for fixed request text. Header values can
|
|
259068
|
+
'For dynamic resource query parameters prefer searchParams, for example {"name":"tag","value":"filters.tag"}. Use {"type":"literal","value":"website"} for fixed request text. Header values can use an expression such as "Bearer " + auth.token. Body can be an object expression, including GraphQL payloads such as { query: "...", variables: { slug: system.params.slug } }.',
|
|
258739
259069
|
'Resource methods are get, post, put, and delete. Optional resource controls are graphql and system. Use control:"graphql" for GraphQL POST resources with query bodies. Use control:"system" for built-in local resource URLs such as "/$resources/current-date" and for resources reading the built-in system parameter. The built-in system fields are system.origin, system.pathname, system.params, and system.search; do not use system.path.',
|
|
258740
259070
|
"Whenever an array or object from a resource or data variable should render repeated UI, call insert-collection with the complete iterable and one repeated-item JSX root. The command creates the Collection, private item parameters, iterable binding, and descendant item bindings atomically. Use collectionItem and collectionItemKey expressions in the item JSX. Wrap multiple repeated siblings in one Element, and give repeated Radix items stable unique value bindings.",
|
|
258741
259071
|
"Expressions are single JavaScript expressions, not statements or functions. Functions, arrow functions, classes, new, this, await, imports, arbitrary calls, increment/decrement, and assignment outside actions are unsupported. Prefer optional chaining, nullish coalescing, ternaries, property/index access, operators, and the documented string/array methods."
|
|
@@ -264860,10 +265190,28 @@ const getDocumentBytes = (entry2, plan) => getUtf8ByteLength(
|
|
|
264860
265190
|
projectContentDatabaseDocument({ document: entry2.document, plan })
|
|
264861
265191
|
)
|
|
264862
265192
|
);
|
|
264863
|
-
const
|
|
265193
|
+
const selectMarkdownBodyReferenceIds = ({
|
|
264864
265194
|
entries,
|
|
264865
|
-
documentGraph,
|
|
264866
265195
|
plan
|
|
265196
|
+
}) => {
|
|
265197
|
+
if (plan === void 0) {
|
|
265198
|
+
return /* @__PURE__ */ new Set();
|
|
265199
|
+
}
|
|
265200
|
+
return selectContentHydrationCandidates({
|
|
265201
|
+
documents: entries.map(({ document: document2 }) => document2),
|
|
265202
|
+
plan: {
|
|
265203
|
+
...plan,
|
|
265204
|
+
queries: plan.queries.filter(
|
|
265205
|
+
({ content: content2 }) => content2.mode === "markdown-body-ref"
|
|
265206
|
+
)
|
|
265207
|
+
}
|
|
265208
|
+
});
|
|
265209
|
+
};
|
|
265210
|
+
const excludeReferencedMarkdownBodies = ({
|
|
265211
|
+
entries,
|
|
265212
|
+
documentGraph,
|
|
265213
|
+
plan,
|
|
265214
|
+
referencedIds
|
|
264867
265215
|
}) => {
|
|
264868
265216
|
if (plan === void 0) {
|
|
264869
265217
|
return entries;
|
|
@@ -264871,7 +265219,7 @@ const excludeDeferredMarkdownBodies = ({
|
|
|
264871
265219
|
const embeddedQueryPlan = {
|
|
264872
265220
|
...plan,
|
|
264873
265221
|
queries: plan.queries.filter(
|
|
264874
|
-
({ content: content2 }) => content2.mode !== "markdown-body"
|
|
265222
|
+
({ content: content2 }) => content2.mode !== "markdown-body-ref"
|
|
264875
265223
|
)
|
|
264876
265224
|
};
|
|
264877
265225
|
const embeddedIds = selectContentHydrationCandidates({
|
|
@@ -264879,8 +265227,18 @@ const excludeDeferredMarkdownBodies = ({
|
|
|
264879
265227
|
plan: embeddedQueryPlan
|
|
264880
265228
|
});
|
|
264881
265229
|
const graphNodeIds = new Set(documentGraph.nodes.map(({ id: id2 }) => id2));
|
|
265230
|
+
const missingNodeIds = [...referencedIds].filter(
|
|
265231
|
+
(assetId2) => graphNodeIds.has(assetId2) === false
|
|
265232
|
+
);
|
|
265233
|
+
if (missingNodeIds.length > 0) {
|
|
265234
|
+
throw new Error(
|
|
265235
|
+
`Markdown body reference queries require graph nodes for: ${missingNodeIds.join(
|
|
265236
|
+
", "
|
|
265237
|
+
)}`
|
|
265238
|
+
);
|
|
265239
|
+
}
|
|
264882
265240
|
return entries.map((entry2) => {
|
|
264883
|
-
if (
|
|
265241
|
+
if (referencedIds.has(entry2.assetId) === false || embeddedIds.has(entry2.assetId)) {
|
|
264884
265242
|
return entry2;
|
|
264885
265243
|
}
|
|
264886
265244
|
const {
|
|
@@ -265057,8 +265415,20 @@ const compileContentArtifact = async ({
|
|
|
265057
265415
|
if (Number.isSafeInteger(maxBytes) === false || maxBytes <= 0) {
|
|
265058
265416
|
throw new Error("Content database byte limit must be a positive integer");
|
|
265059
265417
|
}
|
|
265418
|
+
const referencedMarkdownBodyIds = selectMarkdownBodyReferenceIds({
|
|
265419
|
+
entries,
|
|
265420
|
+
plan
|
|
265421
|
+
});
|
|
265422
|
+
if (referencedMarkdownBodyIds.size > 0 && documentGraph === void 0) {
|
|
265423
|
+
throw new Error("Markdown body reference queries require a document graph");
|
|
265424
|
+
}
|
|
265060
265425
|
if (documentGraph !== void 0) {
|
|
265061
|
-
entries =
|
|
265426
|
+
entries = excludeReferencedMarkdownBodies({
|
|
265427
|
+
entries,
|
|
265428
|
+
documentGraph,
|
|
265429
|
+
plan,
|
|
265430
|
+
referencedIds: referencedMarkdownBodyIds
|
|
265431
|
+
});
|
|
265062
265432
|
}
|
|
265063
265433
|
validateEntries$1({ projectId, entries });
|
|
265064
265434
|
const documentGraphArtifact = documentGraph === void 0 ? void 0 : await createDocumentGraphArtifact(documentGraph);
|
|
@@ -265372,7 +265742,7 @@ const discoverSnapshotAssetReferences = async ({
|
|
|
265372
265742
|
plan: {
|
|
265373
265743
|
...plan,
|
|
265374
265744
|
queries: plan.queries.filter(
|
|
265375
|
-
({ content: content2 }) => content2.mode === "markdown-body"
|
|
265745
|
+
({ content: content2 }) => content2.mode === "markdown-body-ref"
|
|
265376
265746
|
)
|
|
265377
265747
|
}
|
|
265378
265748
|
});
|
|
@@ -265402,13 +265772,13 @@ const queryNeedsDocumentGraph = (query) => {
|
|
|
265402
265772
|
if (query.limit.type === "literal" && typeof query.limit.value === "number" && query.limit.value <= 0) {
|
|
265403
265773
|
return false;
|
|
265404
265774
|
}
|
|
265405
|
-
if (query.content.mode === "markdown-body") {
|
|
265775
|
+
if (query.content.mode === "markdown-body-ref") {
|
|
265406
265776
|
return true;
|
|
265407
265777
|
}
|
|
265408
265778
|
const queryPlan = createContentCompilationPlan([query]);
|
|
265409
265779
|
return queryPlan !== void 0 && requiresStructuredProperties(queryPlan);
|
|
265410
265780
|
};
|
|
265411
|
-
const
|
|
265781
|
+
const planReferencesMarkdownBodies = (plan) => plan?.queries.some(({ content: content2 }) => content2.mode === "markdown-body-ref") === true;
|
|
265412
265782
|
const discoverSnapshotDocumentGraph = async (snapshot, entries, plan) => {
|
|
265413
265783
|
if (snapshot.loadDocumentSources === void 0 || plan !== void 0 && plan.queries.some(queryNeedsDocumentGraph) === false) {
|
|
265414
265784
|
return;
|
|
@@ -265454,7 +265824,7 @@ const discoverSnapshotDocumentGraph = async (snapshot, entries, plan) => {
|
|
|
265454
265824
|
)
|
|
265455
265825
|
}
|
|
265456
265826
|
});
|
|
265457
|
-
return graph.edges.length === 0 &&
|
|
265827
|
+
return graph.edges.length === 0 && planReferencesMarkdownBodies(plan) === false ? void 0 : graph;
|
|
265458
265828
|
};
|
|
265459
265829
|
const materializeContentSnapshot = async ({
|
|
265460
265830
|
snapshot,
|
|
@@ -269447,13 +269817,14 @@ const inputFileShapes = {
|
|
|
269447
269817
|
}
|
|
269448
269818
|
]
|
|
269449
269819
|
};
|
|
269820
|
+
const formatJsonCodeBlock = (value2) => ["```json", JSON.stringify(value2, void 0, 2), "```"].join("\n");
|
|
269450
269821
|
const inputFileShapeIndex = Object.entries(inputFileShapes).map(([name2, value2]) => `${name2}:
|
|
269451
269822
|
|
|
269452
|
-
${
|
|
269823
|
+
${formatJsonCodeBlock(value2)}`).join("\n\n");
|
|
269453
269824
|
const mcpArgumentExampleIndex = Object.entries(mcpArgumentExamples).map(
|
|
269454
269825
|
([name2, examples]) => `### ${name2}
|
|
269455
269826
|
|
|
269456
|
-
${examples.map(
|
|
269827
|
+
${examples.map(formatJsonCodeBlock).join("\n\n")}`
|
|
269457
269828
|
).join("\n\n");
|
|
269458
269829
|
const mcpVisionVerificationLoop = getVisionVerificationLoop({
|
|
269459
269830
|
includeDiff: true
|
|
@@ -269597,7 +269968,7 @@ const allManual = [
|
|
|
269597
269968
|
"- `resources/list`",
|
|
269598
269969
|
"- `meta.index`",
|
|
269599
269970
|
"- `meta.guide`",
|
|
269600
|
-
"- `meta.
|
|
269971
|
+
"- `meta.get-more-tools`",
|
|
269601
269972
|
"- `webstudio://project/tools`",
|
|
269602
269973
|
"- `webstudio://project/components`",
|
|
269603
269974
|
"",
|
|
@@ -269682,7 +270053,7 @@ const topics = {
|
|
|
269682
270053
|
"resources/list",
|
|
269683
270054
|
"meta.index",
|
|
269684
270055
|
"meta.guide",
|
|
269685
|
-
"meta.
|
|
270056
|
+
"meta.get-more-tools"
|
|
269686
270057
|
],
|
|
269687
270058
|
resources: [
|
|
269688
270059
|
"webstudio://project/status",
|
|
@@ -269759,7 +270130,7 @@ const topics = {
|
|
|
269759
270130
|
"resources/list",
|
|
269760
270131
|
"meta.index",
|
|
269761
270132
|
"meta.guide",
|
|
269762
|
-
"meta.
|
|
270133
|
+
"meta.get-more-tools"
|
|
269763
270134
|
],
|
|
269764
270135
|
resources: [
|
|
269765
270136
|
"webstudio://project/status",
|
|
@@ -279118,7 +279489,7 @@ const getSelectedMcpToolSchemas = (toolFilter) => {
|
|
|
279118
279489
|
const tool = toolSchemaByName.get(toolName);
|
|
279119
279490
|
if (tool === void 0) {
|
|
279120
279491
|
throw new Error(
|
|
279121
|
-
`Unknown MCP tool "${toolName}". Use webstudio schema mcp for tool names, or webstudio meta.
|
|
279492
|
+
`Unknown MCP tool "${toolName}". Use webstudio schema mcp for tool names, or webstudio meta.get-more-tools '{"tools":["insert-fragment"]}' for focused discovery.`
|
|
279122
279493
|
);
|
|
279123
279494
|
}
|
|
279124
279495
|
return tool;
|
|
@@ -279148,11 +279519,11 @@ const createMcpSchema = (options, selectedTools = mcpToolSchema) => {
|
|
|
279148
279519
|
command: "webstudio mcp",
|
|
279149
279520
|
singleOpCallCommand: "webstudio mcp single-op-call <tool> '<json>'",
|
|
279150
279521
|
focusedToolNames: focused ? selectedTools.map(({ name: name2 }) => name2) : void 0,
|
|
279151
|
-
usage: focused ? "Focused MCP tool schema. Use this when you know the tool name and need exact input fields." : detailOptions.verbose ? "Full MCP tool schema. This output is large; prefer compact output plus focused meta.
|
|
279522
|
+
usage: focused ? "Focused MCP tool schema. Use this when you know the tool name and need exact input fields." : detailOptions.verbose ? "Full MCP tool schema. This output is large; prefer compact output plus focused meta.get-more-tools/components.* calls for normal LLM workflows." : "Compact MCP tool summary. Use --verbose only when complete input and output schemas are needed.",
|
|
279152
279523
|
discovery: [
|
|
279153
279524
|
"webstudio mcp single-op-call meta.index",
|
|
279154
279525
|
`webstudio mcp single-op-call meta.guide '{"brief":"Create a design system page using every component"}'`,
|
|
279155
|
-
`webstudio mcp single-op-call meta.
|
|
279526
|
+
`webstudio mcp single-op-call meta.get-more-tools '{"tools":["insert-fragment"]}'`,
|
|
279156
279527
|
`webstudio mcp single-op-call components.list '{"source":"all"}'`,
|
|
279157
279528
|
"webstudio mcp single-op-call components.coverage-plan",
|
|
279158
279529
|
`webstudio mcp single-op-call components.search '{"brief":"radix select"}'`,
|
|
@@ -279183,7 +279554,7 @@ const apiSchema = {
|
|
|
279183
279554
|
"resources/list",
|
|
279184
279555
|
"meta.index",
|
|
279185
279556
|
"meta.guide",
|
|
279186
|
-
"meta.
|
|
279557
|
+
"meta.get-more-tools"
|
|
279187
279558
|
],
|
|
279188
279559
|
resources: [
|
|
279189
279560
|
"webstudio://project/status",
|
|
@@ -279198,7 +279569,7 @@ const apiSchema = {
|
|
|
279198
279569
|
"Styles, design tokens, CSS variables, and breakpoints",
|
|
279199
279570
|
"Data variables, resources, assets, publishing, domains, screenshots, and visual diffing"
|
|
279200
279571
|
],
|
|
279201
|
-
boundary: "Builder project data manipulation is MCP-level. Use tools/list, meta.index, meta.
|
|
279572
|
+
boundary: "Builder project data manipulation is MCP-level. Use tools/list, meta.index, meta.get-more-tools, and webstudio://project/tools for exact MCP tool schemas."
|
|
279202
279573
|
},
|
|
279203
279574
|
session: {
|
|
279204
279575
|
stateFile: ".webstudio/project-session.json",
|
|
@@ -279703,9 +280074,10 @@ const topLevelCommandNames = /* @__PURE__ */ new Set([
|
|
|
279703
280074
|
const mcpOnlyToolNames = new Set(
|
|
279704
280075
|
listProjectSessionMcpTools(publicApiOperations).map((tool) => tool.name).filter((name2) => topLevelCommandNames.has(name2) === false)
|
|
279705
280076
|
);
|
|
280077
|
+
const namespacedMcpToolPattern = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$/i;
|
|
279706
280078
|
const getTopLevelMcpToolHint = (args) => {
|
|
279707
280079
|
const tool = args.find(
|
|
279708
|
-
(arg) =>
|
|
280080
|
+
(arg) => namespacedMcpToolPattern.test(arg) || mcpOnlyToolNames.has(arg)
|
|
279709
280081
|
);
|
|
279710
280082
|
if (tool === void 0) {
|
|
279711
280083
|
return;
|
|
@@ -279749,7 +280121,7 @@ const getTopLevelMcpToolForwardArgs = (args) => {
|
|
|
279749
280121
|
if (tool === "screenshot" && rest[0]?.trimStart().startsWith("{")) {
|
|
279750
280122
|
return ["mcp", "single-op-call", tool, ...rest];
|
|
279751
280123
|
}
|
|
279752
|
-
if (
|
|
280124
|
+
if (namespacedMcpToolPattern.test(tool) || mcpOnlyToolNames.has(tool)) {
|
|
279753
280125
|
return [
|
|
279754
280126
|
"mcp",
|
|
279755
280127
|
"single-op-call",
|
|
@@ -279762,13 +280134,13 @@ const rootCliEpilogue = [
|
|
|
279762
280134
|
"Project editing / LLM quick start:",
|
|
279763
280135
|
" webstudio man project-editing",
|
|
279764
280136
|
" webstudio meta.index",
|
|
279765
|
-
` webstudio meta.
|
|
279766
|
-
|
|
280137
|
+
` webstudio meta.get-more-tools '{"tools":["insert-fragment"]}'`,
|
|
280138
|
+
" webstudio insert-fragment --input-file .temp/insert-fragment.json --dry-run",
|
|
279767
280139
|
"",
|
|
279768
280140
|
"Equivalent explicit MCP form:",
|
|
279769
280141
|
" webstudio mcp single-op-call meta.index",
|
|
279770
|
-
` webstudio mcp single-op-call meta.
|
|
279771
|
-
|
|
280142
|
+
` webstudio mcp single-op-call meta.get-more-tools '{"tools":["insert-fragment"]}'`,
|
|
280143
|
+
" webstudio mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json --dry-run",
|
|
279772
280144
|
"",
|
|
279773
280145
|
"Inside the Webstudio monorepo, use: node packages/cli/local.js ...",
|
|
279774
280146
|
"MCP tool shortcuts are forwarded to mcp single-op-call for shell-driven agents."
|