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 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
- const server = new Server({ name: "bricks-mcp-server", version: "0.3.0" }, { capabilities: { tools: {} } });
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 () => wpRequest("/ping"),
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.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": {