@charisol/plexo-mcp 1.0.7 → 1.0.8
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/dist/blogTools.js +690 -0
- package/dist/client/plexoClient.js +107 -0
- package/dist/index.js +17 -3
- package/package.json +1 -1
- package/src/blogTools.ts +695 -0
- package/src/client/plexoClient.ts +130 -0
- package/src/index.ts +17 -3
|
@@ -273,5 +273,112 @@ class PlexoClient {
|
|
|
273
273
|
}
|
|
274
274
|
return data;
|
|
275
275
|
}
|
|
276
|
+
/** Fetches a single template's full details, including designJson — used by the blog
|
|
277
|
+
* layout tools to read back a layout template's current content (there's no other
|
|
278
|
+
* templateId-scoped GET in the public API; list endpoints only return metadata). */
|
|
279
|
+
async getTemplate(templateId) {
|
|
280
|
+
this.checkAuth();
|
|
281
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/${encodeURIComponent(templateId)}`, {
|
|
282
|
+
method: "GET",
|
|
283
|
+
headers: this.getHeaders(),
|
|
284
|
+
});
|
|
285
|
+
const data = (await res.json());
|
|
286
|
+
if (!res.ok) {
|
|
287
|
+
throw new Error(data?.error || `Failed to fetch template (Status ${res.status})`);
|
|
288
|
+
}
|
|
289
|
+
return data;
|
|
290
|
+
}
|
|
291
|
+
/** Shared request helper for every /api/blog/{templateId}/... route below — these are
|
|
292
|
+
* the same admin endpoints the Plexo dashboard's own blog UI calls, and (like every
|
|
293
|
+
* other route in this client) accept the same Bearer/x-api-key auth as the rest of the
|
|
294
|
+
* public API (see resolveUser/resolveBlogAdminSite server-side). */
|
|
295
|
+
async blogFetch(method, templateId, path, body) {
|
|
296
|
+
this.checkAuth();
|
|
297
|
+
const res = await fetch(`${this.baseUrl}/api/blog/${encodeURIComponent(templateId)}${path}`, {
|
|
298
|
+
method,
|
|
299
|
+
headers: this.getHeaders(),
|
|
300
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
301
|
+
});
|
|
302
|
+
const data = (await res.json().catch(() => ({})));
|
|
303
|
+
if (!res.ok) {
|
|
304
|
+
throw new Error(data?.error || `Blog request failed (Status ${res.status})`);
|
|
305
|
+
}
|
|
306
|
+
return data;
|
|
307
|
+
}
|
|
308
|
+
/** Idempotent — returns the existing layout template id if one's already attached to
|
|
309
|
+
* this kind rather than creating a duplicate. Pair with updateTemplate() to set its
|
|
310
|
+
* designJson (see blogTools.ts's designBlogLayout). */
|
|
311
|
+
async ensureBlogLayout(templateId, kind) {
|
|
312
|
+
return this.blogFetch("POST", templateId, `/layout/${kind}`);
|
|
313
|
+
}
|
|
314
|
+
/** Detaches the layout (reverts to the default theme) without deleting the underlying Template row. */
|
|
315
|
+
async resetBlogLayout(templateId, kind) {
|
|
316
|
+
return this.blogFetch("DELETE", templateId, `/layout/${kind}`);
|
|
317
|
+
}
|
|
318
|
+
async getBlogSite(templateId) {
|
|
319
|
+
return this.blogFetch("GET", templateId, "/site");
|
|
320
|
+
}
|
|
321
|
+
async updateBlogSite(templateId, patch) {
|
|
322
|
+
return this.blogFetch("PATCH", templateId, "/site", patch);
|
|
323
|
+
}
|
|
324
|
+
async listBlogPosts(templateId, params = {}) {
|
|
325
|
+
const query = new URLSearchParams();
|
|
326
|
+
if (params.status)
|
|
327
|
+
query.set("status", params.status);
|
|
328
|
+
if (params.search)
|
|
329
|
+
query.set("search", params.search);
|
|
330
|
+
if (params.categoryId)
|
|
331
|
+
query.set("categoryId", params.categoryId);
|
|
332
|
+
const qs = query.toString();
|
|
333
|
+
return this.blogFetch("GET", templateId, `/posts${qs ? `?${qs}` : ""}`);
|
|
334
|
+
}
|
|
335
|
+
async getBlogPost(templateId, postId) {
|
|
336
|
+
return this.blogFetch("GET", templateId, `/posts/${encodeURIComponent(postId)}`);
|
|
337
|
+
}
|
|
338
|
+
async createBlogPost(templateId, payload) {
|
|
339
|
+
return this.blogFetch("POST", templateId, "/posts", payload);
|
|
340
|
+
}
|
|
341
|
+
async updateBlogPost(templateId, postId, payload) {
|
|
342
|
+
return this.blogFetch("PATCH", templateId, `/posts/${encodeURIComponent(postId)}`, payload);
|
|
343
|
+
}
|
|
344
|
+
async deleteBlogPost(templateId, postId) {
|
|
345
|
+
return this.blogFetch("DELETE", templateId, `/posts/${encodeURIComponent(postId)}`);
|
|
346
|
+
}
|
|
347
|
+
async listBlogCategories(templateId) {
|
|
348
|
+
return this.blogFetch("GET", templateId, "/categories");
|
|
349
|
+
}
|
|
350
|
+
async createBlogCategory(templateId, payload) {
|
|
351
|
+
return this.blogFetch("POST", templateId, "/categories", payload);
|
|
352
|
+
}
|
|
353
|
+
async deleteBlogCategory(templateId, categoryId) {
|
|
354
|
+
return this.blogFetch("DELETE", templateId, `/categories/${encodeURIComponent(categoryId)}`);
|
|
355
|
+
}
|
|
356
|
+
async listBlogTags(templateId) {
|
|
357
|
+
return this.blogFetch("GET", templateId, "/tags");
|
|
358
|
+
}
|
|
359
|
+
async createBlogTag(templateId, name) {
|
|
360
|
+
return this.blogFetch("POST", templateId, "/tags", { name });
|
|
361
|
+
}
|
|
362
|
+
async deleteBlogTag(templateId, tagId) {
|
|
363
|
+
return this.blogFetch("DELETE", templateId, `/tags/${encodeURIComponent(tagId)}`);
|
|
364
|
+
}
|
|
365
|
+
async listBlogAuthors(templateId) {
|
|
366
|
+
return this.blogFetch("GET", templateId, "/authors");
|
|
367
|
+
}
|
|
368
|
+
async createBlogAuthor(templateId, payload) {
|
|
369
|
+
return this.blogFetch("POST", templateId, "/authors", payload);
|
|
370
|
+
}
|
|
371
|
+
async deleteBlogAuthor(templateId, authorId) {
|
|
372
|
+
return this.blogFetch("DELETE", templateId, `/authors/${encodeURIComponent(authorId)}`);
|
|
373
|
+
}
|
|
374
|
+
async listBlogComments(templateId, status) {
|
|
375
|
+
return this.blogFetch("GET", templateId, `/comments${status ? `?status=${encodeURIComponent(status)}` : ""}`);
|
|
376
|
+
}
|
|
377
|
+
async moderateBlogComment(templateId, commentId, status) {
|
|
378
|
+
return this.blogFetch("PATCH", templateId, `/comments/${encodeURIComponent(commentId)}`, { status });
|
|
379
|
+
}
|
|
380
|
+
async deleteBlogComment(templateId, commentId) {
|
|
381
|
+
return this.blogFetch("DELETE", templateId, `/comments/${encodeURIComponent(commentId)}`);
|
|
382
|
+
}
|
|
276
383
|
}
|
|
277
384
|
exports.PlexoClient = PlexoClient;
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
|
5
5
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
6
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
7
|
const plexoClient_js_1 = require("./client/plexoClient.js");
|
|
8
|
+
const blogTools_js_1 = require("./blogTools.js");
|
|
8
9
|
const server = new index_js_1.Server({
|
|
9
10
|
name: "plexo-mcp",
|
|
10
11
|
version: "1.0.0",
|
|
@@ -129,7 +130,7 @@ EXAMPLE VALID designJson PAYLOAD (hero row, plus a 3-item product grid row):
|
|
|
129
130
|
properties: {
|
|
130
131
|
type: {
|
|
131
132
|
type: "string",
|
|
132
|
-
enum: ["heading", "paragraph", "text", "button", "card", "image", "menu", "social", "divider", "spacer", "form_container", "input", "textarea", "select", "carousel", "html", "icon", "table", "timer", "video"],
|
|
133
|
+
enum: ["heading", "paragraph", "text", "button", "card", "image", "menu", "social", "divider", "spacer", "form_container", "input", "textarea", "select", "carousel", "html", "icon", "table", "timer", "video", "accordion", "blog_title", "blog_content", "blog_featured_image", "blog_date", "blog_author", "blog_categories", "blog_comments", "blog_post_list"],
|
|
133
134
|
},
|
|
134
135
|
style: { type: "object" },
|
|
135
136
|
attributes: { type: "object", description: "Component attributes (text, href, src, title, description, links, fields). For type 'html', raw markup goes under 'htmlContent' — NOT 'text'." },
|
|
@@ -221,7 +222,7 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
|
|
|
221
222
|
properties: {
|
|
222
223
|
type: {
|
|
223
224
|
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
|
+
enum: ["heading", "paragraph", "text", "button", "card", "image", "menu", "social", "divider", "spacer", "form_container", "input", "textarea", "select", "carousel", "html", "icon", "table", "timer", "video", "accordion", "blog_title", "blog_content", "blog_featured_image", "blog_date", "blog_author", "blog_categories", "blog_comments", "blog_post_list"],
|
|
225
226
|
},
|
|
226
227
|
style: { type: "object" },
|
|
227
228
|
attributes: {
|
|
@@ -496,6 +497,7 @@ To edit a page's CONTENT (designJson), use update_template with the same templat
|
|
|
496
497
|
},
|
|
497
498
|
},
|
|
498
499
|
},
|
|
500
|
+
...blogTools_js_1.BLOG_TOOL_DEFS,
|
|
499
501
|
],
|
|
500
502
|
};
|
|
501
503
|
});
|
|
@@ -687,8 +689,20 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
687
689
|
],
|
|
688
690
|
};
|
|
689
691
|
}
|
|
690
|
-
default:
|
|
692
|
+
default: {
|
|
693
|
+
if (blogTools_js_1.BLOG_TOOL_NAMES.has(name)) {
|
|
694
|
+
const result = await (0, blogTools_js_1.handleBlogToolCall)(name, args || {}, client);
|
|
695
|
+
return {
|
|
696
|
+
content: [
|
|
697
|
+
{
|
|
698
|
+
type: "text",
|
|
699
|
+
text: JSON.stringify(result, null, 2),
|
|
700
|
+
},
|
|
701
|
+
],
|
|
702
|
+
};
|
|
703
|
+
}
|
|
691
704
|
throw new Error(`Unknown tool: ${name}`);
|
|
705
|
+
}
|
|
692
706
|
}
|
|
693
707
|
}
|
|
694
708
|
catch (error) {
|
package/package.json
CHANGED