bricks-mcp-server 0.9.0 → 0.14.0
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 +11 -1
- package/dist/contracts.js +79 -0
- package/dist/index.js +117 -1
- package/package.json +47 -45
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ WP_URL=https://yoursite.tld WP_USER=you WP_APP_PASSWORD="…" npx -y bricks-mcp-
|
|
|
66
66
|
|
|
67
67
|
## Tools
|
|
68
68
|
|
|
69
|
-
`
|
|
69
|
+
`bricks_start_here`, `bricks_ping`, page/element/media tools, `bricks_get_design_context`, `bricks_manage_global_data`, `bricks_apply_global_class_operations`, `bricks_manage_template`, `bricks_get_preview_url`, and Query Filters reindexing.
|
|
70
70
|
|
|
71
71
|
Highlights (server ≥0.5.0, plugin ≥0.7.0):
|
|
72
72
|
|
|
@@ -77,3 +77,13 @@ Highlights (server ≥0.5.0, plugin ≥0.7.0):
|
|
|
77
77
|
## License
|
|
78
78
|
|
|
79
79
|
GPL-2.0-or-later
|
|
80
|
+
|
|
81
|
+
### Media safety
|
|
82
|
+
|
|
83
|
+
Use bricks_upload_media for URL/base64 uploads, bricks_find_media_usage before deletion, and bricks_delete_media only after references are cleared. Private URLs, SVG, unsafe MIME types, and files over 10 MB are rejected.
|
|
84
|
+
### Lote 6 safety
|
|
85
|
+
|
|
86
|
+
- Call `bricks_manage_global_data` with `action: "get"`, then reuse its `hash` in `action: "replace"`.
|
|
87
|
+
- Prefer `bricks_apply_global_class_operations` over full-array replacement.
|
|
88
|
+
- `bricks_manage_template` supports get/update/clone/delete; updates require `expected_modified`.
|
|
89
|
+
- Signed preview URLs expire after 60–900 seconds and must be treated as bearer secrets.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const updatePageSchema = z.object({ id: z.number().int().positive(), expected_modified: z.string().optional(), title: z.string().optional(), status: z.enum(["draft", "publish", "private", "pending"]).optional(), post_content: z.string().optional(), excerpt: z.string().optional(), date: z.string().optional(), featured_media: z.number().int().nonnegative().optional(), terms: z.record(z.any()).optional(), acf: z.record(z.any()).optional(), meta: z.record(z.any()).optional(), content: z.array(z.any()).optional(), header: z.array(z.any()).optional(), footer: z.array(z.any()).optional(), settings: z.record(z.any()).optional() });
|
|
3
|
+
export const uploadMediaSchema = z.object({ url: z.string().url().optional(), base64: z.string().min(1).optional(), filename: z.string().min(1).optional(), title: z.string().optional(), alt: z.string().optional() }).refine(v => Boolean(v.url) !== Boolean(v.base64), "Provide exactly one source: url or base64.");
|
|
4
|
+
export const elementOperationSchema = z.discriminatedUnion("op", [z.object({ op: z.literal("insert"), element: z.record(z.any()), parent: z.union([z.string(), z.number()]).optional(), position: z.number().int().nonnegative().optional() }), z.object({ op: z.literal("update"), id: z.string().min(1), changes: z.record(z.any()) }), z.object({ op: z.literal("move"), id: z.string().min(1), parent: z.union([z.string(), z.number()]), position: z.number().int().nonnegative().optional() }), z.object({ op: z.literal("delete"), id: z.string().min(1) })]);
|
|
5
|
+
export const applyElementOperationsSchema = z.object({ id: z.number().int().positive(), expected_modified: z.string().min(1), field: z.enum(["content", "header", "footer"]).optional(), operations: z.array(elementOperationSchema).min(1) });
|
|
6
|
+
export const designContextSchema = z.object({ include: z.array(z.enum(["classes", "variables", "palettes", "theme_styles", "breakpoints", "components", "global_settings"])).optional(), compact: z.boolean().optional() });
|
|
7
|
+
export const globalScopeSchema = z.enum(["global-variables", "color-palettes", "theme-styles", "components", "breakpoints"]);
|
|
8
|
+
export const globalDataUpdateSchema = z.object({ scope: globalScopeSchema, action: z.enum(["get", "replace"]), expected_hash: z.string().length(64).optional(), value: z.array(z.any()).or(z.record(z.any())).optional() }).superRefine((v, ctx) => { if (v.action === "replace" && (!v.expected_hash || v.value === undefined))
|
|
9
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "expected_hash and value are required for replace" }); });
|
|
10
|
+
export const globalClassOperationSchema = z.discriminatedUnion("op", [z.object({ op: z.literal("add"), class: z.record(z.any()) }), z.object({ op: z.literal("update"), id: z.string().min(1), changes: z.record(z.any()) }), z.object({ op: z.literal("delete"), id: z.string().min(1) })]);
|
|
11
|
+
export const globalClassOperationsSchema = z.object({ expected_hash: z.string().length(64), operations: z.array(globalClassOperationSchema).min(1) });
|
|
12
|
+
export const manageTemplateSchema = z.object({ id: z.number().int().positive(), action: z.enum(["get", "update", "clone", "delete"]), expected_modified: z.string().optional(), title: z.string().optional(), status: z.enum(["draft", "publish", "private"]).optional(), type: z.string().optional(), content: z.array(z.any()).optional(), settings: z.record(z.any()).optional(), conditions: z.array(z.any()).optional(), force: z.boolean().optional() }).superRefine((v, ctx) => { if (v.action === "update" && !v.expected_modified)
|
|
13
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "expected_modified is required for update" }); });
|
|
14
|
+
export const previewUrlSchema = z.object({ id: z.number().int().positive(), ttl: z.number().int().min(60).max(900).optional() });
|
|
15
|
+
export const dynamicDataSchema = z.object({ post_id: z.number().int().positive(), user_id: z.number().int().positive().optional(), context: z.enum(["text", "link", "image", "media"]).optional(), items: z.array(z.string().min(1).max(20000)).min(1).max(50) });
|
|
16
|
+
export const revisionsSchema = z.object({ post_id: z.number().int().positive(), action: z.enum(["list", "compare", "restore"]), from: z.union([z.number().int().positive(), z.literal("current")]).optional(), to: z.union([z.number().int().positive(), z.literal("current")]).optional(), revision_id: z.number().int().positive().optional(), expected_modified: z.string().optional() }).superRefine((v, ctx) => { if (v.action === "compare" && (v.from === undefined || v.to === undefined))
|
|
17
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "from and to are required" }); if (v.action === "restore" && (!v.revision_id || !v.expected_modified))
|
|
18
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "revision_id and expected_modified are required" }); });
|
|
19
|
+
export const customCssSchema = z.object({ action: z.enum(["get", "update"]), scope: z.enum(["global", "post"]), post_id: z.number().int().positive().optional(), css: z.string().max(204800).optional(), expected_hash: z.string().length(64).optional() }).superRefine((v, ctx) => { if (v.scope === "post" && !v.post_id)
|
|
20
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "post_id is required" }); if (v.action === "update" && (v.css === undefined || !v.expected_hash))
|
|
21
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "css and expected_hash are required" }); });
|
|
22
|
+
export function buildToolRequest(name, args) {
|
|
23
|
+
if (name === "bricks_update_page") {
|
|
24
|
+
const parsed = updatePageSchema.parse(args);
|
|
25
|
+
const { id, ...body } = parsed;
|
|
26
|
+
return { path: `/pages/${id}`, method: "PUT", body };
|
|
27
|
+
}
|
|
28
|
+
if (name === "bricks_apply_element_operations") {
|
|
29
|
+
const parsed = applyElementOperationsSchema.parse(args);
|
|
30
|
+
const { id, ...body } = parsed;
|
|
31
|
+
return { path: `/pages/${id}/element-operations`, method: "POST", body };
|
|
32
|
+
}
|
|
33
|
+
if (name === "bricks_get_element") {
|
|
34
|
+
const id = z.number().int().positive().parse(args.id);
|
|
35
|
+
const elementId = z.string().min(1).parse(args.element_id);
|
|
36
|
+
return { path: `/pages/${id}/elements/${elementId}`, method: "GET", query: args.field ? { field: String(args.field) } : undefined };
|
|
37
|
+
}
|
|
38
|
+
if (name === "bricks_get_design_context") {
|
|
39
|
+
const parsed = designContextSchema.parse(args);
|
|
40
|
+
return { path: "/design-context", method: "GET", query: { ...(parsed.include ? { include: parsed.include.join(",") } : {}), ...(parsed.compact ? { compact: "true" } : {}) } };
|
|
41
|
+
}
|
|
42
|
+
if (name === "bricks_manage_global_data") {
|
|
43
|
+
const parsed = globalDataUpdateSchema.parse(args);
|
|
44
|
+
const { scope, action, ...body } = parsed;
|
|
45
|
+
return action === "get" ? { path: `/global-data/${scope}`, method: "GET" } : { path: `/global-data/${scope}`, method: "PUT", body };
|
|
46
|
+
}
|
|
47
|
+
if (name === "bricks_apply_global_class_operations")
|
|
48
|
+
return { path: "/global-classes/operations", method: "POST", body: globalClassOperationsSchema.parse(args) };
|
|
49
|
+
if (name === "bricks_manage_template") {
|
|
50
|
+
const parsed = manageTemplateSchema.parse(args);
|
|
51
|
+
const { id, ...body } = parsed;
|
|
52
|
+
return { path: `/templates/${id}/manage`, method: "POST", body };
|
|
53
|
+
}
|
|
54
|
+
if (name === "bricks_get_preview_url") {
|
|
55
|
+
const parsed = previewUrlSchema.parse(args);
|
|
56
|
+
return { path: `/preview/${parsed.id}`, method: "GET", query: parsed.ttl ? { ttl: String(parsed.ttl) } : undefined };
|
|
57
|
+
}
|
|
58
|
+
if (name === "bricks_resolve_dynamic_data")
|
|
59
|
+
return { path: "/dynamic-data/resolve", method: "POST", body: dynamicDataSchema.parse(args) };
|
|
60
|
+
if (name === "bricks_manage_revisions") {
|
|
61
|
+
const parsed = revisionsSchema.parse(args);
|
|
62
|
+
const { post_id, action, ...body } = parsed;
|
|
63
|
+
return action === "list" ? { path: `/revisions/${post_id}`, method: "GET" } : { path: `/revisions/${post_id}/${action}`, method: "POST", body };
|
|
64
|
+
}
|
|
65
|
+
if (name === "bricks_manage_custom_css") {
|
|
66
|
+
const parsed = customCssSchema.parse(args);
|
|
67
|
+
const { action, scope, post_id, ...body } = parsed;
|
|
68
|
+
const query = { scope, ...(post_id ? { post_id: String(post_id) } : {}) };
|
|
69
|
+
return action === "get" ? { path: "/custom-css", method: "GET", query } : { path: "/custom-css", method: "PUT", query, body };
|
|
70
|
+
}
|
|
71
|
+
if (name === "bricks_upload_media")
|
|
72
|
+
return { path: "/media", method: "POST", body: uploadMediaSchema.parse(args) };
|
|
73
|
+
if (name === "bricks_find_media_usage")
|
|
74
|
+
return { path: `/media/${z.number().int().positive().parse(args.id)}/usage`, method: "GET" };
|
|
75
|
+
if (name === "bricks_delete_media")
|
|
76
|
+
return { path: `/media/${z.number().int().positive().parse(args.id)}`, method: "DELETE", query: args.force ? { force: "true" } : undefined };
|
|
77
|
+
throw new Error(`Unsupported tool request: ${name}`);
|
|
78
|
+
}
|
|
79
|
+
export function negotiateCapabilities(startHere) { const c = startHere.capabilities ?? {}; return { canWrite: !c.dry_run && !c.allowlist?.read_only, canUploadMedia: c.media_upload !== false, canForceDelete: c.force_delete_allowed === true, acf: c.acf_active === true, queryFilters: c.query_filters === true }; }
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ if (subcommand === "doctor" || subcommand === "diagnose") {
|
|
|
33
33
|
if (process.env.WP_URL) {
|
|
34
34
|
console.error(`[bricks-mcp] target WP_URL=${process.env.WP_URL} WP_USER=${process.env.WP_USER ?? "?"}`);
|
|
35
35
|
}
|
|
36
|
-
const server = new Server({ name: "bricks-mcp-server", version: "0.
|
|
36
|
+
const server = new Server({ name: "bricks-mcp-server", version: "0.14.0" }, { capabilities: { tools: {} } });
|
|
37
37
|
const tools = [
|
|
38
38
|
{
|
|
39
39
|
name: "bricks_start_here",
|
|
@@ -296,6 +296,45 @@ const tools = [
|
|
|
296
296
|
handler: async (args) => wpRequest("/media", { query: args }),
|
|
297
297
|
},
|
|
298
298
|
{
|
|
299
|
+
name: "bricks_upload_media",
|
|
300
|
+
description: "Upload media from one public URL or base64. Blocks SSRF, unsafe MIME, files over 10 MB, and SVG.",
|
|
301
|
+
inputSchema: { type: "object", properties: { url: { type: "string" }, base64: { type: "string" }, filename: { type: "string" }, title: { type: "string" }, alt: { type: "string" } }, additionalProperties: false },
|
|
302
|
+
schema: z.object({ url: z.string().url().optional(), base64: z.string().min(1).optional(), filename: z.string().min(1).optional(), title: z.string().optional(), alt: z.string().optional() }).refine(v => Boolean(v.url) !== Boolean(v.base64), "Provide exactly one source."),
|
|
303
|
+
handler: async (args) => wpRequest("/media", { method: "POST", body: args }),
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: "bricks_find_media_usage",
|
|
307
|
+
description: "Find posts and Bricks data that reference an attachment. Call before deletion.",
|
|
308
|
+
inputSchema: { type: "object", properties: { id: { type: "number" } }, required: ["id"], additionalProperties: false },
|
|
309
|
+
schema: z.object({ id: z.number().int().positive() }),
|
|
310
|
+
handler: async (args) => wpRequest(`/media/${args.id}/usage`),
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: "bricks_delete_media",
|
|
314
|
+
description: "Delete an unused attachment. Trash by default; force:true is permanent and default-off.",
|
|
315
|
+
inputSchema: { type: "object", properties: { id: { type: "number" }, force: { type: "boolean" } }, required: ["id"], additionalProperties: false },
|
|
316
|
+
schema: z.object({ id: z.number().int().positive(), force: z.boolean().optional() }),
|
|
317
|
+
handler: async (args) => wpRequest(`/media/${args.id}`, { method: "DELETE", query: args.force ? { force: "true" } : undefined }),
|
|
318
|
+
}, {
|
|
319
|
+
name: "bricks_get_element",
|
|
320
|
+
description: "Read one Bricks element and its complete descendant subtree without loading the full page tree.",
|
|
321
|
+
inputSchema: { type: "object", properties: { id: { type: "number" }, element_id: { type: "string" }, field: { type: "string", description: "content (default), header, or footer" } }, required: ["id", "element_id"], additionalProperties: false },
|
|
322
|
+
schema: z.object({ id: z.number().int().positive(), element_id: z.string().min(1), field: z.enum(["content", "header", "footer"]).optional() }),
|
|
323
|
+
handler: async (args) => wpRequest(`/pages/${args.id}/elements/${args.element_id}`, { query: args.field ? { field: args.field } : undefined }),
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: "bricks_apply_element_operations",
|
|
327
|
+
description: "Atomically apply insert/update/move/delete operations to one Bricks element tree. Requires expected_modified. The whole batch validates before one snapshot and one write; dry-run returns the diff without persistence.",
|
|
328
|
+
inputSchema: { type: "object", properties: { id: { type: "number" }, expected_modified: { type: "string" }, field: { type: "string" }, operations: { type: "array", items: { type: "object" } } }, required: ["id", "expected_modified", "operations"], additionalProperties: false },
|
|
329
|
+
schema: z.object({ id: z.number().int().positive(), expected_modified: z.string().min(1), field: z.enum(["content", "header", "footer"]).optional(), operations: z.array(z.object({ op: z.enum(["insert", "update", "move", "delete"]) }).passthrough()).min(1) }),
|
|
330
|
+
handler: async (args) => { const { id, ...body } = args; return wpRequest(`/pages/${id}/element-operations`, { method: "POST", body }); },
|
|
331
|
+
}, {
|
|
332
|
+
name: "bricks_get_design_context",
|
|
333
|
+
description: "Read selected Bricks design-system sections: classes, variables, palettes, theme styles, breakpoints, components, and global settings. Use compact:true for counts/names only and include to control token usage.",
|
|
334
|
+
inputSchema: { type: "object", properties: { include: { type: "array", items: { type: "string" } }, compact: { type: "boolean" } }, additionalProperties: false },
|
|
335
|
+
schema: z.object({ include: z.array(z.enum(["classes", "variables", "palettes", "theme_styles", "breakpoints", "components", "global_settings"])).optional(), compact: z.boolean().optional() }),
|
|
336
|
+
handler: async (args) => wpRequest("/design-context", { query: { include: args.include?.join(","), compact: args.compact ? "true" : undefined } }),
|
|
337
|
+
}, {
|
|
299
338
|
name: "bricks_reindex_query_filters",
|
|
300
339
|
description: "Regenerate the Bricks Query Filters index. Call this after writing/editing any filter-* element via MCP — filters don't render until the index is rebuilt.",
|
|
301
340
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
@@ -333,6 +372,69 @@ const tools = [
|
|
|
333
372
|
}),
|
|
334
373
|
handler: async (args) => wpRequest("/templates", { method: "POST", body: args }),
|
|
335
374
|
},
|
|
375
|
+
{
|
|
376
|
+
name: "bricks_manage_global_data",
|
|
377
|
+
description: "Read or safely replace one versioned Bricks global-data scope. Call action:get first for the current value/hash, then action:replace with expected_hash; dry-run and snapshot policy apply.",
|
|
378
|
+
inputSchema: { type: "object", properties: { scope: { type: "string", enum: ["global-variables", "color-palettes", "theme-styles", "components", "breakpoints"] }, action: { type: "string", enum: ["get", "replace"] }, expected_hash: { type: "string" }, value: { type: ["array", "object"] } }, required: ["scope", "action"], additionalProperties: false },
|
|
379
|
+
schema: z.object({ scope: z.enum(["global-variables", "color-palettes", "theme-styles", "components", "breakpoints"]), action: z.enum(["get", "replace"]), expected_hash: z.string().length(64).optional(), value: z.union([z.array(z.any()), z.record(z.any())]).optional() }).refine(v => v.action !== "replace" || (Boolean(v.expected_hash) && v.value !== undefined), "expected_hash and value are required for replace"),
|
|
380
|
+
handler: async (args) => { const { scope, action, ...body } = args; return action === "get" ? wpRequest(`/global-data/${scope}`) : wpRequest(`/global-data/${scope}`, { method: "PUT", body }); },
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: "bricks_apply_global_class_operations",
|
|
384
|
+
description: "Atomically add, update, or delete individual global classes without replacing the entire class library. Requires the hash from bricks_get_global_classes and rejects duplicate names or IDs.",
|
|
385
|
+
inputSchema: { type: "object", properties: { expected_hash: { type: "string" }, operations: { type: "array", items: { type: "object" } } }, required: ["expected_hash", "operations"], additionalProperties: false },
|
|
386
|
+
schema: z.object({ expected_hash: z.string().length(64), operations: z.array(z.object({ op: z.enum(["add", "update", "delete"]) }).passthrough()).min(1) }),
|
|
387
|
+
handler: async (args) => wpRequest("/global-classes/operations", { method: "POST", body: args }),
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "bricks_manage_template",
|
|
391
|
+
description: "Long-tail template dispatcher: get full template data, update atomically with expected_modified, clone, or trash/delete. Updates create a Bricks-compatible WordPress revision and snapshots.",
|
|
392
|
+
inputSchema: { type: "object", properties: { id: { type: "number" }, action: { type: "string", enum: ["get", "update", "clone", "delete"] }, expected_modified: { type: "string" }, title: { type: "string" }, status: { type: "string" }, type: { type: "string" }, content: { type: "array" }, settings: { type: "object" }, conditions: { type: "array" }, force: { type: "boolean" } }, required: ["id", "action"], additionalProperties: false },
|
|
393
|
+
schema: z.object({ id: z.number().int().positive(), action: z.enum(["get", "update", "clone", "delete"]), expected_modified: z.string().optional(), title: z.string().optional(), status: z.enum(["draft", "publish", "private"]).optional(), type: z.string().optional(), content: z.array(z.any()).optional(), settings: z.record(z.any()).optional(), conditions: z.array(z.any()).optional(), force: z.boolean().optional() }).refine(v => v.action !== "update" || Boolean(v.expected_modified), "expected_modified is required for update"),
|
|
394
|
+
handler: async (args) => { const { id, action, ...body } = args; return action === "get" ? wpRequest(`/templates/${id}`) : wpRequest(`/templates/${id}/manage`, { method: "POST", body: { action, ...body } }); },
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: "bricks_get_preview_url",
|
|
398
|
+
description: "Generate a short-lived signed bearer URL (60–900 seconds) for visually checking a draft, private page, or template in a browser. Treat the URL as a secret until it expires.",
|
|
399
|
+
inputSchema: { type: "object", properties: { id: { type: "number" }, ttl: { type: "number", minimum: 60, maximum: 900 } }, required: ["id"], additionalProperties: false },
|
|
400
|
+
schema: z.object({ id: z.number().int().positive(), ttl: z.number().int().min(60).max(900).optional() }),
|
|
401
|
+
handler: async (args) => wpRequest(`/preview/${args.id}`, { query: args.ttl ? { ttl: String(args.ttl) } : undefined }),
|
|
402
|
+
}, {
|
|
403
|
+
name: "bricks_resolve_dynamic_data",
|
|
404
|
+
description: "Preview 1–50 Bricks dynamic-data strings against an explicit post and optional user context. Uses Bricks' public renderer and blocks remote {echo} execution.",
|
|
405
|
+
inputSchema: { type: "object", properties: { post_id: { type: "number" }, user_id: { type: "number" }, context: { type: "string", enum: ["text", "link", "image", "media"] }, items: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 50 } }, required: ["post_id", "items"], additionalProperties: false },
|
|
406
|
+
schema: z.object({ post_id: z.number().int().positive(), user_id: z.number().int().positive().optional(), context: z.enum(["text", "link", "image", "media"]).optional(), items: z.array(z.string().min(1).max(20000)).min(1).max(50) }),
|
|
407
|
+
handler: async (args) => wpRequest("/dynamic-data/resolve", { method: "POST", body: args }),
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "bricks_manage_revisions",
|
|
411
|
+
description: "List, compare, or restore Bricks-aware WordPress revisions. Restore requires expected_modified, honors dry-run, and creates an undo revision first.",
|
|
412
|
+
inputSchema: { type: "object", properties: { post_id: { type: "number" }, action: { type: "string", enum: ["list", "compare", "restore"] }, from: { type: ["number", "string"] }, to: { type: ["number", "string"] }, revision_id: { type: "number" }, expected_modified: { type: "string" } }, required: ["post_id", "action"], additionalProperties: false },
|
|
413
|
+
schema: z.object({ post_id: z.number().int().positive(), action: z.enum(["list", "compare", "restore"]), from: z.union([z.number().int().positive(), z.literal("current")]).optional(), to: z.union([z.number().int().positive(), z.literal("current")]).optional(), revision_id: z.number().int().positive().optional(), expected_modified: z.string().optional() }).superRefine((v, ctx) => { if (v.action === "compare" && (v.from === undefined || v.to === undefined))
|
|
414
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "from and to are required for compare" }); if (v.action === "restore" && (!v.revision_id || !v.expected_modified))
|
|
415
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "revision_id and expected_modified are required for restore" }); }),
|
|
416
|
+
handler: async (args) => { const { post_id, action, ...body } = args; if (action === "list")
|
|
417
|
+
return wpRequest(`/revisions/${post_id}`); return wpRequest(`/revisions/${post_id}/${action}`, { method: "POST", body }); },
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: "bricks_manage_custom_css",
|
|
421
|
+
description: "Read or update global/post Bricks Custom CSS. Update requires the current hash, honors dry-run, snapshots the previous value, and regenerates assets.",
|
|
422
|
+
inputSchema: { type: "object", properties: { action: { type: "string", enum: ["get", "update"] }, scope: { type: "string", enum: ["global", "post"] }, post_id: { type: "number" }, css: { type: "string" }, expected_hash: { type: "string" } }, required: ["action", "scope"], additionalProperties: false },
|
|
423
|
+
schema: z.object({ action: z.enum(["get", "update"]), scope: z.enum(["global", "post"]), post_id: z.number().int().positive().optional(), css: z.string().max(204800).optional(), expected_hash: z.string().length(64).optional() }).superRefine((v, ctx) => { if (v.scope === "post" && !v.post_id)
|
|
424
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "post_id is required for post scope" }); if (v.action === "update" && (v.css === undefined || !v.expected_hash))
|
|
425
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "css and expected_hash are required for update" }); }),
|
|
426
|
+
handler: async (args) => { const { action, scope, post_id, ...body } = args; const query = { scope: String(scope), ...(post_id ? { post_id: String(post_id) } : {}) }; return action === "get" ? wpRequest("/custom-css", { query }) : wpRequest("/custom-css", { method: "PUT", query, body }); },
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: "bricks_manage_integrations",
|
|
430
|
+
description: "Inspect/create WooCommerce Bricks templates or inspect/test the configured signed outbound webhook.",
|
|
431
|
+
inputSchema: { type: "object", properties: { integration: { type: "string", enum: ["woocommerce", "webhook"] }, action: { type: "string", enum: ["status", "types", "list", "create", "test"] }, type: { type: "string" }, title: { type: "string" }, status: { type: "string" }, content: { type: "array" }, settings: { type: "object" }, conditions: { type: "array" } }, required: ["integration", "action"], additionalProperties: false },
|
|
432
|
+
schema: z.object({ integration: z.enum(["woocommerce", "webhook"]), action: z.enum(["status", "types", "list", "create", "test"]), type: z.string().optional(), title: z.string().optional(), status: z.enum(["draft", "publish", "private"]).optional(), content: z.array(z.any()).optional(), settings: z.record(z.any()).optional(), conditions: z.array(z.any()).optional() }).superRefine((v, ctx) => { if (v.integration === "webhook" && !["status", "test"].includes(v.action))
|
|
433
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "webhook supports status or test" }); if (v.integration === "woocommerce" && v.action === "create" && (!v.type || !v.title))
|
|
434
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "type and title are required for create" }); }),
|
|
435
|
+
handler: async (args) => { const { integration, ...body } = args; if (integration === "webhook")
|
|
436
|
+
return body.action === "status" ? wpRequest("/webhooks") : wpRequest("/webhooks", { method: "POST", body }); return body.action === "status" ? wpRequest("/woocommerce/templates") : wpRequest("/woocommerce/templates", { method: "POST", body }); },
|
|
437
|
+
},
|
|
336
438
|
{
|
|
337
439
|
name: "bricks_get_global_classes",
|
|
338
440
|
description: "Read Bricks global classes and their categories — useful before editing pages so styling stays consistent.",
|
|
@@ -376,9 +478,23 @@ const TOOL_ANNOTATIONS = {
|
|
|
376
478
|
bricks_list_pages: { readOnlyHint: true },
|
|
377
479
|
bricks_get_page: { readOnlyHint: true },
|
|
378
480
|
bricks_list_templates: { readOnlyHint: true },
|
|
481
|
+
bricks_manage_global_data: { readOnlyHint: false, destructiveHint: true },
|
|
482
|
+
bricks_apply_global_class_operations: { readOnlyHint: false, destructiveHint: true },
|
|
483
|
+
bricks_manage_template: { readOnlyHint: false, destructiveHint: true },
|
|
484
|
+
bricks_get_preview_url: { readOnlyHint: true },
|
|
379
485
|
bricks_get_global_classes: { readOnlyHint: true },
|
|
380
486
|
bricks_get_theme_styles: { readOnlyHint: true },
|
|
487
|
+
bricks_get_design_context: { readOnlyHint: true },
|
|
488
|
+
bricks_resolve_dynamic_data: { readOnlyHint: true },
|
|
489
|
+
bricks_manage_revisions: { readOnlyHint: false, destructiveHint: true },
|
|
490
|
+
bricks_manage_custom_css: { readOnlyHint: false, destructiveHint: true },
|
|
491
|
+
bricks_manage_integrations: { readOnlyHint: false, destructiveHint: false },
|
|
492
|
+
bricks_get_element: { readOnlyHint: true },
|
|
493
|
+
bricks_apply_element_operations: { readOnlyHint: false, destructiveHint: true },
|
|
381
494
|
bricks_list_media: { readOnlyHint: true },
|
|
495
|
+
bricks_find_media_usage: { readOnlyHint: true },
|
|
496
|
+
bricks_upload_media: { readOnlyHint: false, destructiveHint: false },
|
|
497
|
+
bricks_delete_media: { readOnlyHint: false, destructiveHint: true },
|
|
382
498
|
bricks_create_page: { readOnlyHint: false, destructiveHint: false },
|
|
383
499
|
bricks_create_template: { readOnlyHint: false, destructiveHint: false },
|
|
384
500
|
bricks_reindex_query_filters: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
|
package/package.json
CHANGED
|
@@ -1,45 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "bricks-mcp-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Provider-agnostic MCP server that exposes Bricks Builder pages, templates, global classes and theme styles as tools. Works with any MCP-compatible client (Claude Code, Codex CLI, etc.).",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"bricks-mcp-server": "dist/index.js",
|
|
8
|
-
"bricks-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"README.md"
|
|
13
|
-
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"start": "node dist/index.js",
|
|
17
|
-
"dev": "tsc --watch",
|
|
18
|
-
"setup": "node dist/index.js setup",
|
|
19
|
-
"doctor": "node dist/index.js doctor",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"bricks
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"claude
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "bricks-mcp-server",
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "Provider-agnostic MCP server that exposes Bricks Builder pages, templates, global classes and theme styles as tools. Works with any MCP-compatible client (Claude Code, Codex CLI, etc.).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bricks-mcp-server": "dist/index.js",
|
|
8
|
+
"bricks-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"setup": "node dist/index.js setup",
|
|
19
|
+
"doctor": "node dist/index.js doctor",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"test": "npm run build && node --test test/*.test.mjs"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"bricks",
|
|
27
|
+
"bricks-builder",
|
|
28
|
+
"wordpress",
|
|
29
|
+
"claude",
|
|
30
|
+
"claude-code",
|
|
31
|
+
"ai"
|
|
32
|
+
],
|
|
33
|
+
"author": "Juan Leonardo",
|
|
34
|
+
"license": "GPL-2.0-or-later",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
40
|
+
"bricks-ai-workspace": "file:..",
|
|
41
|
+
"zod": "^3.23.8"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20.12.0",
|
|
45
|
+
"typescript": "^5.5.0"
|
|
46
|
+
}
|
|
47
|
+
}
|