bricks-mcp-server 0.3.0 → 0.5.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 +7 -1
- package/dist/index.js +108 -6
- package/dist/setup.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,13 @@ WP_URL=https://yoursite.tld WP_USER=you WP_APP_PASSWORD="…" npx -y bricks-mcp-
|
|
|
66
66
|
|
|
67
67
|
## Tools
|
|
68
68
|
|
|
69
|
-
`bricks_ping`, `bricks_list_pages`, `bricks_get_page`, `bricks_update_page`, `bricks_list_templates`, `bricks_create_template`, `bricks_get_global_classes`, `bricks_update_global_classes`, `bricks_get_theme_styles`.
|
|
69
|
+
`bricks_ping`, `bricks_list_pages`, `bricks_get_page`, `bricks_update_page`, `bricks_create_page`, `bricks_list_templates`, `bricks_create_template`, `bricks_get_global_classes`, `bricks_update_global_classes`, `bricks_get_theme_styles`, `bricks_list_media`, `bricks_reindex_query_filters`.
|
|
70
|
+
|
|
71
|
+
Highlights (server ≥0.5.0, plugin ≥0.7.0):
|
|
72
|
+
|
|
73
|
+
- **Optimistic locking** — `bricks_get_page` returns `modified`; pass it as `expected_modified` on `bricks_update_page` to get a 409 instead of overwriting edits made in the builder since you read the page.
|
|
74
|
+
- **`bricks_create_page`** — create pages/posts/CPT entries (draft by default), optionally with Bricks content.
|
|
75
|
+
- **`bricks_ping`** echoes the effective `WP_URL`/user and warns when the configured host doesn't match the site that answered (stale config, redirect, proxy).
|
|
70
76
|
|
|
71
77
|
## License
|
|
72
78
|
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,16 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { wpRequest, diagnose, formatDiagnosis } from "./wp-client.js";
|
|
6
|
+
import { wpRequest, loadConfig, diagnose, formatDiagnosis } from "./wp-client.js";
|
|
7
|
+
/** Extract the hostname from a URL, or null if it can't be parsed. */
|
|
8
|
+
function hostOf(url) {
|
|
9
|
+
try {
|
|
10
|
+
return new URL(url).host.toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
7
16
|
// Subcommand dispatch: `setup` runs the interactive wizard, `doctor` runs the
|
|
8
17
|
// diagnostic ladder once and exits. No subcommand → start the MCP stdio server.
|
|
9
18
|
const subcommand = process.argv[2];
|
|
@@ -18,14 +27,41 @@ if (subcommand === "doctor" || subcommand === "diagnose") {
|
|
|
18
27
|
console.log(formatDiagnosis(d));
|
|
19
28
|
process.exit(d.problem ? 1 : 0);
|
|
20
29
|
}
|
|
21
|
-
|
|
30
|
+
// Announce the effective target on startup. stdout is reserved for the MCP
|
|
31
|
+
// protocol on stdio, so this goes to stderr — it shows up in Claude Code's MCP
|
|
32
|
+
// logs and makes a wrong/stale WP_URL visible before any tool is even called.
|
|
33
|
+
if (process.env.WP_URL) {
|
|
34
|
+
console.error(`[bricks-mcp] target WP_URL=${process.env.WP_URL} WP_USER=${process.env.WP_USER ?? "?"}`);
|
|
35
|
+
}
|
|
36
|
+
const server = new Server({ name: "bricks-mcp-server", version: "0.5.0" }, { capabilities: { tools: {} } });
|
|
22
37
|
const tools = [
|
|
23
38
|
{
|
|
24
39
|
name: "bricks_ping",
|
|
25
|
-
description: "Verify the WordPress connection and report Bricks + WP version.",
|
|
40
|
+
description: "Verify the WordPress connection and report which site you're connected to (wp_url + site that answered), Bricks + WP version.",
|
|
26
41
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
27
42
|
schema: z.object({}),
|
|
28
|
-
handler: async () =>
|
|
43
|
+
handler: async () => {
|
|
44
|
+
// Echo the config this *process* was launched with, up front. If Claude
|
|
45
|
+
// Code is still holding a stale config (old process not restarted after a
|
|
46
|
+
// site switch), wp_url makes it obvious without guessing from permalinks.
|
|
47
|
+
const { baseUrl, user } = loadConfig();
|
|
48
|
+
const res = (await wpRequest("/ping"));
|
|
49
|
+
const configHost = hostOf(baseUrl);
|
|
50
|
+
const siteHost = typeof res.site_url === "string" ? hostOf(res.site_url) : null;
|
|
51
|
+
const mismatch = configHost !== null && siteHost !== null && configHost !== siteHost;
|
|
52
|
+
return {
|
|
53
|
+
wp_url: baseUrl,
|
|
54
|
+
wp_user_config: user,
|
|
55
|
+
...res,
|
|
56
|
+
...(mismatch
|
|
57
|
+
? {
|
|
58
|
+
warning: `⚠️ El host configurado (WP_URL=${configHost}) NO coincide con el sitio que respondió (${siteHost}). ` +
|
|
59
|
+
`Puede ser un redirect/proxy, o una configuración MCP antigua todavía activa. ` +
|
|
60
|
+
`Si acabas de cambiar de sitio, reinicia Claude Code por completo para que tome la nueva config.`,
|
|
61
|
+
}
|
|
62
|
+
: {}),
|
|
63
|
+
};
|
|
64
|
+
},
|
|
29
65
|
},
|
|
30
66
|
{
|
|
31
67
|
name: "bricks_get_schema",
|
|
@@ -67,7 +103,7 @@ const tools = [
|
|
|
67
103
|
},
|
|
68
104
|
{
|
|
69
105
|
name: "bricks_get_page",
|
|
70
|
-
description: "Get a page/post/template by ID, including its Bricks content/header/footer/settings JSON.",
|
|
106
|
+
description: "Get a page/post/template by ID, including its Bricks content/header/footer/settings JSON. The response includes `modified` — pass it as expected_modified on bricks_update_page to avoid clobbering edits made in the builder since you read it.",
|
|
71
107
|
inputSchema: {
|
|
72
108
|
type: "object",
|
|
73
109
|
properties: { id: { type: "number" } },
|
|
@@ -79,11 +115,15 @@ const tools = [
|
|
|
79
115
|
},
|
|
80
116
|
{
|
|
81
117
|
name: "bricks_update_page",
|
|
82
|
-
description: "Update Bricks content/header/footer/settings for a page/post/template. Pass any subset of fields. The 'content' field must be an array of Bricks element objects.",
|
|
118
|
+
description: "Update Bricks content/header/footer/settings for a page/post/template. Pass any subset of fields. The 'content' field must be an array of Bricks element objects. ALWAYS pass expected_modified (the `modified` value from bricks_get_page) — the server returns 409 if the page changed since, so you never overwrite the user's builder edits.",
|
|
83
119
|
inputSchema: {
|
|
84
120
|
type: "object",
|
|
85
121
|
properties: {
|
|
86
122
|
id: { type: "number" },
|
|
123
|
+
expected_modified: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: "The `modified` value you got from bricks_get_page. If the page changed since, the update is rejected (409) — re-fetch, merge, retry.",
|
|
126
|
+
},
|
|
87
127
|
title: { type: "string" },
|
|
88
128
|
content: { type: "array", description: "Bricks elements array" },
|
|
89
129
|
header: { type: "array" },
|
|
@@ -95,6 +135,7 @@ const tools = [
|
|
|
95
135
|
},
|
|
96
136
|
schema: z.object({
|
|
97
137
|
id: z.number().int().positive(),
|
|
138
|
+
expected_modified: z.string().optional(),
|
|
98
139
|
title: z.string().optional(),
|
|
99
140
|
content: z.array(z.any()).optional(),
|
|
100
141
|
header: z.array(z.any()).optional(),
|
|
@@ -106,6 +147,67 @@ const tools = [
|
|
|
106
147
|
return wpRequest(`/pages/${id}`, { method: "PUT", body });
|
|
107
148
|
},
|
|
108
149
|
},
|
|
150
|
+
{
|
|
151
|
+
name: "bricks_create_page",
|
|
152
|
+
description: "Create a new page, post, or CPT entry (e.g. publicacion, noticia, evento), optionally with Bricks content. Defaults to draft status so nothing goes live without human review. Not for Bricks templates — use bricks_create_template for those.",
|
|
153
|
+
inputSchema: {
|
|
154
|
+
type: "object",
|
|
155
|
+
properties: {
|
|
156
|
+
title: { type: "string" },
|
|
157
|
+
post_type: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Registered post type slug (default 'page'). CPTs allowed.",
|
|
160
|
+
},
|
|
161
|
+
status: {
|
|
162
|
+
type: "string",
|
|
163
|
+
description: "draft (default) | publish | private | pending",
|
|
164
|
+
},
|
|
165
|
+
slug: { type: "string" },
|
|
166
|
+
content: { type: "array", description: "Bricks elements array" },
|
|
167
|
+
settings: { type: "object" },
|
|
168
|
+
},
|
|
169
|
+
required: ["title"],
|
|
170
|
+
additionalProperties: false,
|
|
171
|
+
},
|
|
172
|
+
schema: z.object({
|
|
173
|
+
title: z.string(),
|
|
174
|
+
post_type: z.string().optional(),
|
|
175
|
+
status: z.string().optional(),
|
|
176
|
+
slug: z.string().optional(),
|
|
177
|
+
content: z.array(z.any()).optional(),
|
|
178
|
+
settings: z.record(z.any()).optional(),
|
|
179
|
+
}),
|
|
180
|
+
handler: async (args) => wpRequest("/pages", { method: "POST", body: args }),
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "bricks_list_media",
|
|
184
|
+
description: "Search the WordPress media library (read-only). Returns attachment id, url, alt, mime, dimensions — use the id/url in Bricks image settings.",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
search: { type: "string" },
|
|
189
|
+
mime: {
|
|
190
|
+
type: "string",
|
|
191
|
+
description: "MIME filter, e.g. 'image' (default), 'image/svg+xml', 'video'.",
|
|
192
|
+
},
|
|
193
|
+
per_page: { type: "number", description: "1-100, default 30" },
|
|
194
|
+
},
|
|
195
|
+
additionalProperties: false,
|
|
196
|
+
},
|
|
197
|
+
schema: z.object({
|
|
198
|
+
search: z.string().optional(),
|
|
199
|
+
mime: z.string().optional(),
|
|
200
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
201
|
+
}),
|
|
202
|
+
handler: async (args) => wpRequest("/media", { query: args }),
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "bricks_reindex_query_filters",
|
|
206
|
+
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.",
|
|
207
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
208
|
+
schema: z.object({}),
|
|
209
|
+
handler: async () => wpRequest("/query-filters/reindex", { method: "POST" }),
|
|
210
|
+
},
|
|
109
211
|
{
|
|
110
212
|
name: "bricks_list_templates",
|
|
111
213
|
description: "List Bricks templates. Optional filter by template type (section, header, footer, popup, content, etc.).",
|
package/dist/setup.js
CHANGED
|
@@ -157,6 +157,14 @@ export async function runSetup() {
|
|
|
157
157
|
"Elige 1, 2 o 3: ")).trim();
|
|
158
158
|
if (client === "1") {
|
|
159
159
|
if (hasClaudeCli()) {
|
|
160
|
+
// `claude mcp add` refuses to overwrite an existing name (silent no-op),
|
|
161
|
+
// which leaves a stale config pointing at the wrong site. Remove first so
|
|
162
|
+
// the add always takes effect. Ignore its exit code (nothing to remove is
|
|
163
|
+
// fine).
|
|
164
|
+
spawnSync("claude", ["mcp", "remove", "bricks", "-s", "user"], {
|
|
165
|
+
stdio: "ignore",
|
|
166
|
+
shell: process.platform === "win32",
|
|
167
|
+
});
|
|
160
168
|
const args = [
|
|
161
169
|
"mcp",
|
|
162
170
|
"add",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bricks-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"bin": {
|