@charisol/plexo-mcp 1.0.2 → 1.0.4

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
@@ -33,7 +33,7 @@ Add to your `claude_desktop_config.json`:
33
33
  "args": ["-y", "@charisol/plexo-mcp"],
34
34
  "env": {
35
35
  "PLEXO_API_KEY": "plexo_sk_your_key_here",
36
- "PLEXO_BASE_URL": "https://plexobuilder.com"
36
+ "PLEXO_BASE_URL": "https://plexo.charisol.io"
37
37
  }
38
38
  }
39
39
  }
@@ -60,7 +60,7 @@ Add to `.cursor/mcp.json`:
60
60
 
61
61
  ### Option 3: Browser Authentication Flow
62
62
  If `PLEXO_API_KEY` is omitted, the tool cleanly prompts:
63
- `Please log into your Plexo account to authenticate: https://plexobuilder.com/mcp/login`
63
+ `Please log into your Plexo account to authenticate: https://plexo.charisol.io/mcp/login`
64
64
 
65
65
  ---
66
66
 
@@ -99,6 +99,22 @@ class PlexoClient {
99
99
  }
100
100
  return data;
101
101
  }
102
+ async getStrataTokens(projectId) {
103
+ this.checkAuth();
104
+ const url = new URL(`${this.baseUrl}/api/v1/strata/tokens`);
105
+ if (projectId) {
106
+ url.searchParams.set("projectId", projectId);
107
+ }
108
+ const res = await fetch(url.toString(), {
109
+ method: "GET",
110
+ headers: this.getHeaders(),
111
+ });
112
+ const data = (await res.json());
113
+ if (!res.ok) {
114
+ throw new Error(data?.error || `Failed to fetch Strata tokens (Status ${res.status})`);
115
+ }
116
+ return data;
117
+ }
102
118
  async listTemplates() {
103
119
  this.checkAuth();
104
120
  const res = await fetch(`${this.baseUrl}/api/templates`, {
package/dist/config.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getConfig = getConfig;
4
4
  function getConfig() {
5
- const baseUrl = process.env.PLEXO_BASE_URL || "https://plexobuilder.com";
5
+ const baseUrl = process.env.PLEXO_BASE_URL || "https://plexo.charisol.io";
6
6
  const apiKey = process.env.PLEXO_API_KEY;
7
7
  return {
8
8
  baseUrl,
package/dist/index.js CHANGED
@@ -167,7 +167,7 @@ EXAMPLE VALID designJson PAYLOAD (hero row, plus a 3-item product grid row):
167
167
  name: "create_email_template",
168
168
  description: `Creates and saves a responsive HTML email template for newsletters or promotional campaigns.
169
169
 
170
- Uses the SAME fully-hydrated layout schema as publish_landing_page — designJson MUST be { "body": { "style": {...}, "rows": [{ "id", "style", "columns": [{ "id", "width", "elements": [{ "id", "type", "style", "attributes" }] }] }] } }. There is no shorthand row format; rows without a 'columns' array are rejected. See publish_landing_page's description for a full worked example. Email templates are saved but never published to a domain.`,
170
+ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJson MUST be { "body": { "style": {...}, "rows": [{ "id", "style", "columns": [{ "id", "width", "elements": [{ "id", "type", "style", "attributes" }] }] }] } }. There is no shorthand row format; rows without a 'columns' array are rejected. Text content for heading/paragraph/text/button elements MUST be under attributes.text (NOT attributes.content) — a "content" key is silently ignored by the editor and renders as empty/placeholder text. See publish_landing_page's description for a full worked example. Email templates are saved but never published to a domain.`,
171
171
  inputSchema: {
172
172
  type: "object",
173
173
  properties: {
@@ -178,6 +178,74 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
178
178
  designJson: {
179
179
  type: "object",
180
180
  description: "Plexo layout schema object (same shape as publish_landing_page's designJson) containing body style and a fully hydrated rows array with columns and elements.",
181
+ required: ["body"],
182
+ properties: {
183
+ body: {
184
+ type: "object",
185
+ required: ["style", "rows"],
186
+ properties: {
187
+ style: {
188
+ type: "object",
189
+ description: "Global CSS style and page metadata.",
190
+ required: ["backgroundColor", "color", "fontFamily", "htmlTitle"],
191
+ properties: {
192
+ backgroundColor: { type: "string", description: "Background color of the email in hex format (e.g. '#F4F2F8')." },
193
+ color: { type: "string", description: "Default text color of the email in hex format." },
194
+ fontFamily: { type: "string", description: "Font family of the email text (e.g. 'Arial, Helvetica, sans-serif')." },
195
+ htmlTitle: { type: "string", description: "The HTML title of the email (matches the subject/brand name)." },
196
+ },
197
+ },
198
+ rows: {
199
+ type: "array",
200
+ description: "Array of row objects. Every row MUST already have a 'columns' array — there is no shorthand row type; rows without 'columns' are rejected.",
201
+ items: {
202
+ type: "object",
203
+ required: ["id", "style", "columns"],
204
+ properties: {
205
+ id: { type: "string" },
206
+ style: { type: "object" },
207
+ columns: {
208
+ type: "array",
209
+ description: "Required. One entry per column.",
210
+ items: {
211
+ type: "object",
212
+ required: ["id", "width", "elements"],
213
+ properties: {
214
+ id: { type: "string" },
215
+ width: { type: "string", description: "Percentage width (e.g. '100%', '50%', '33.33%')." },
216
+ elements: {
217
+ type: "array",
218
+ items: {
219
+ type: "object",
220
+ required: ["id", "type", "style", "attributes"],
221
+ properties: {
222
+ type: {
223
+ type: "string",
224
+ enum: ["heading", "paragraph", "text", "button", "card", "image", "menu", "social", "divider", "spacer", "form_container", "input", "textarea", "select", "carousel", "html", "icon", "table", "timer", "video"],
225
+ },
226
+ style: { type: "object" },
227
+ attributes: {
228
+ type: "object",
229
+ description: "Component attributes. IMPORTANT: text content for 'heading'/'paragraph'/'text'/'button' elements goes under the key 'text' (NOT 'content'). Images use 'src'/'alt'. Buttons/links use 'href'.",
230
+ properties: {
231
+ text: { type: "string", description: "Visible text for heading/paragraph/text/button elements." },
232
+ href: { type: "string", description: "Link URL for button/card elements." },
233
+ src: { type: "string", description: "Image URL for image elements." },
234
+ alt: { type: "string", description: "Alt text for image elements." },
235
+ },
236
+ },
237
+ },
238
+ },
239
+ },
240
+ },
241
+ },
242
+ },
243
+ },
244
+ },
245
+ },
246
+ },
247
+ },
248
+ },
181
249
  },
182
250
  compiledHtml: {
183
251
  type: "string",
@@ -187,6 +255,23 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
187
255
  required: ["designJson"],
188
256
  },
189
257
  },
258
+ {
259
+ name: "get_strata_tokens",
260
+ description: `Checks whether the account has a Strata design-system project connected and, if so, fetches its design tokens (colors, typography, spacing, etc).
261
+
262
+ Call this BEFORE publish_landing_page or create_email_template whenever the user wants their brand/Strata colors and styles used. Call with no arguments first to check the currently connected project. If the result has "connected": false, ask the user for their Strata project id (found in their Strata project's snapshot/share URL) and call this tool again with that projectId — it connects the account to that project (remembered for future calls) and fetches its tokens in the same call.
263
+
264
+ Map the returned tokens into designJson style fields: color-type tokens into backgroundColor/color/borderColor, spacing-type tokens into padding/margin values, and any font-related tokens into fontFamily/fontSize. Prefer tokens whose "name" hints at their role (e.g. a name containing "primary", "background", "text", "accent").`,
265
+ inputSchema: {
266
+ type: "object",
267
+ properties: {
268
+ projectId: {
269
+ type: "string",
270
+ description: "Optional. The user's Strata project id to connect (or switch to). Omit to check/use the previously connected project, if any.",
271
+ },
272
+ },
273
+ },
274
+ },
190
275
  {
191
276
  name: "update_template",
192
277
  description: `Edits an existing landing page or email template in place, under the same template ID. Recompiles and re-saves the HTML from the new designJson — the editable URL stays the same, and for a published landing page the live URL and domain are unchanged too (this does not publish/unpublish or change the domain; call publish_landing_page again with a domain for that).
@@ -260,7 +345,7 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page/create_email_
260
345
  properties: {
261
346
  domain: {
262
347
  type: "string",
263
- description: "Domain name to delete (e.g. 'acme.plexobuilder.com' or 'myacme.com')",
348
+ description: "Domain name to delete (e.g. 'acme.plexo.charisol.io' or 'myacme.com')",
264
349
  },
265
350
  domainId: {
266
351
  type: "string",
@@ -300,6 +385,17 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
300
385
  ],
301
386
  };
302
387
  }
388
+ case "get_strata_tokens": {
389
+ const result = await client.getStrataTokens(args?.projectId);
390
+ return {
391
+ content: [
392
+ {
393
+ type: "text",
394
+ text: JSON.stringify(result, null, 2),
395
+ },
396
+ ],
397
+ };
398
+ }
303
399
  case "update_template": {
304
400
  const result = await client.updateTemplate(args);
305
401
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charisol/plexo-mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Model Context Protocol (MCP) Server for Plexo - Landing page generation, email templates, publishing, and analytics.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -136,6 +136,26 @@ export class PlexoClient {
136
136
  return data;
137
137
  }
138
138
 
139
+ async getStrataTokens(projectId?: string): Promise<any> {
140
+ this.checkAuth();
141
+ const url = new URL(`${this.baseUrl}/api/v1/strata/tokens`);
142
+ if (projectId) {
143
+ url.searchParams.set("projectId", projectId);
144
+ }
145
+
146
+ const res = await fetch(url.toString(), {
147
+ method: "GET",
148
+ headers: this.getHeaders(),
149
+ });
150
+
151
+ const data = (await res.json()) as any;
152
+ if (!res.ok) {
153
+ throw new Error(data?.error || `Failed to fetch Strata tokens (Status ${res.status})`);
154
+ }
155
+
156
+ return data;
157
+ }
158
+
139
159
  async listTemplates(): Promise<any> {
140
160
  this.checkAuth();
141
161
  const res = await fetch(`${this.baseUrl}/api/templates`, {
package/src/config.ts CHANGED
@@ -4,7 +4,7 @@ export interface PlexoConfig {
4
4
  }
5
5
 
6
6
  export function getConfig(): PlexoConfig {
7
- const baseUrl = process.env.PLEXO_BASE_URL || "https://plexobuilder.com";
7
+ const baseUrl = process.env.PLEXO_BASE_URL || "https://plexo.charisol.io";
8
8
  const apiKey = process.env.PLEXO_API_KEY;
9
9
 
10
10
  return {
package/src/index.ts CHANGED
@@ -174,7 +174,7 @@ EXAMPLE VALID designJson PAYLOAD (hero row, plus a 3-item product grid row):
174
174
  name: "create_email_template",
175
175
  description: `Creates and saves a responsive HTML email template for newsletters or promotional campaigns.
176
176
 
177
- Uses the SAME fully-hydrated layout schema as publish_landing_page — designJson MUST be { "body": { "style": {...}, "rows": [{ "id", "style", "columns": [{ "id", "width", "elements": [{ "id", "type", "style", "attributes" }] }] }] } }. There is no shorthand row format; rows without a 'columns' array are rejected. See publish_landing_page's description for a full worked example. Email templates are saved but never published to a domain.`,
177
+ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJson MUST be { "body": { "style": {...}, "rows": [{ "id", "style", "columns": [{ "id", "width", "elements": [{ "id", "type", "style", "attributes" }] }] }] } }. There is no shorthand row format; rows without a 'columns' array are rejected. Text content for heading/paragraph/text/button elements MUST be under attributes.text (NOT attributes.content) — a "content" key is silently ignored by the editor and renders as empty/placeholder text. See publish_landing_page's description for a full worked example. Email templates are saved but never published to a domain.`,
178
178
  inputSchema: {
179
179
  type: "object",
180
180
  properties: {
@@ -185,6 +185,74 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
185
185
  designJson: {
186
186
  type: "object",
187
187
  description: "Plexo layout schema object (same shape as publish_landing_page's designJson) containing body style and a fully hydrated rows array with columns and elements.",
188
+ required: ["body"],
189
+ properties: {
190
+ body: {
191
+ type: "object",
192
+ required: ["style", "rows"],
193
+ properties: {
194
+ style: {
195
+ type: "object",
196
+ description: "Global CSS style and page metadata.",
197
+ required: ["backgroundColor", "color", "fontFamily", "htmlTitle"],
198
+ properties: {
199
+ backgroundColor: { type: "string", description: "Background color of the email in hex format (e.g. '#F4F2F8')." },
200
+ color: { type: "string", description: "Default text color of the email in hex format." },
201
+ fontFamily: { type: "string", description: "Font family of the email text (e.g. 'Arial, Helvetica, sans-serif')." },
202
+ htmlTitle: { type: "string", description: "The HTML title of the email (matches the subject/brand name)." },
203
+ },
204
+ },
205
+ rows: {
206
+ type: "array",
207
+ description: "Array of row objects. Every row MUST already have a 'columns' array — there is no shorthand row type; rows without 'columns' are rejected.",
208
+ items: {
209
+ type: "object",
210
+ required: ["id", "style", "columns"],
211
+ properties: {
212
+ id: { type: "string" },
213
+ style: { type: "object" },
214
+ columns: {
215
+ type: "array",
216
+ description: "Required. One entry per column.",
217
+ items: {
218
+ type: "object",
219
+ required: ["id", "width", "elements"],
220
+ properties: {
221
+ id: { type: "string" },
222
+ width: { type: "string", description: "Percentage width (e.g. '100%', '50%', '33.33%')." },
223
+ elements: {
224
+ type: "array",
225
+ items: {
226
+ type: "object",
227
+ required: ["id", "type", "style", "attributes"],
228
+ properties: {
229
+ type: {
230
+ type: "string",
231
+ enum: ["heading", "paragraph", "text", "button", "card", "image", "menu", "social", "divider", "spacer", "form_container", "input", "textarea", "select", "carousel", "html", "icon", "table", "timer", "video"],
232
+ },
233
+ style: { type: "object" },
234
+ attributes: {
235
+ type: "object",
236
+ description: "Component attributes. IMPORTANT: text content for 'heading'/'paragraph'/'text'/'button' elements goes under the key 'text' (NOT 'content'). Images use 'src'/'alt'. Buttons/links use 'href'.",
237
+ properties: {
238
+ text: { type: "string", description: "Visible text for heading/paragraph/text/button elements." },
239
+ href: { type: "string", description: "Link URL for button/card elements." },
240
+ src: { type: "string", description: "Image URL for image elements." },
241
+ alt: { type: "string", description: "Alt text for image elements." },
242
+ },
243
+ },
244
+ },
245
+ },
246
+ },
247
+ },
248
+ },
249
+ },
250
+ },
251
+ },
252
+ },
253
+ },
254
+ },
255
+ },
188
256
  },
189
257
  compiledHtml: {
190
258
  type: "string",
@@ -194,6 +262,23 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
194
262
  required: ["designJson"],
195
263
  },
196
264
  },
265
+ {
266
+ name: "get_strata_tokens",
267
+ description: `Checks whether the account has a Strata design-system project connected and, if so, fetches its design tokens (colors, typography, spacing, etc).
268
+
269
+ Call this BEFORE publish_landing_page or create_email_template whenever the user wants their brand/Strata colors and styles used. Call with no arguments first to check the currently connected project. If the result has "connected": false, ask the user for their Strata project id (found in their Strata project's snapshot/share URL) and call this tool again with that projectId — it connects the account to that project (remembered for future calls) and fetches its tokens in the same call.
270
+
271
+ Map the returned tokens into designJson style fields: color-type tokens into backgroundColor/color/borderColor, spacing-type tokens into padding/margin values, and any font-related tokens into fontFamily/fontSize. Prefer tokens whose "name" hints at their role (e.g. a name containing "primary", "background", "text", "accent").`,
272
+ inputSchema: {
273
+ type: "object",
274
+ properties: {
275
+ projectId: {
276
+ type: "string",
277
+ description: "Optional. The user's Strata project id to connect (or switch to). Omit to check/use the previously connected project, if any.",
278
+ },
279
+ },
280
+ },
281
+ },
197
282
  {
198
283
  name: "update_template",
199
284
  description: `Edits an existing landing page or email template in place, under the same template ID. Recompiles and re-saves the HTML from the new designJson — the editable URL stays the same, and for a published landing page the live URL and domain are unchanged too (this does not publish/unpublish or change the domain; call publish_landing_page again with a domain for that).
@@ -267,7 +352,7 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page/create_email_
267
352
  properties: {
268
353
  domain: {
269
354
  type: "string",
270
- description: "Domain name to delete (e.g. 'acme.plexobuilder.com' or 'myacme.com')",
355
+ description: "Domain name to delete (e.g. 'acme.plexo.charisol.io' or 'myacme.com')",
271
356
  },
272
357
  domainId: {
273
358
  type: "string",
@@ -311,6 +396,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
311
396
  };
312
397
  }
313
398
 
399
+ case "get_strata_tokens": {
400
+ const result = await client.getStrataTokens((args as any)?.projectId);
401
+ return {
402
+ content: [
403
+ {
404
+ type: "text",
405
+ text: JSON.stringify(result, null, 2),
406
+ },
407
+ ],
408
+ };
409
+ }
410
+
314
411
  case "update_template": {
315
412
  const result = await client.updateTemplate(args as any);
316
413
  return {