@cmo-so/mcp 0.9.0 → 0.9.2
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/cmo-mcp.mjs +159 -18
- package/package.json +1 -1
package/dist/cmo-mcp.mjs
CHANGED
|
@@ -7199,7 +7199,7 @@ var require_dist = __commonJS({
|
|
|
7199
7199
|
// package.json
|
|
7200
7200
|
var package_default = {
|
|
7201
7201
|
name: "@cmo-so/cli",
|
|
7202
|
-
version: "0.9.
|
|
7202
|
+
version: "0.9.2",
|
|
7203
7203
|
description: "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
|
|
7204
7204
|
type: "module",
|
|
7205
7205
|
license: "MIT",
|
|
@@ -7784,8 +7784,14 @@ function domainApi(api2, domain) {
|
|
|
7784
7784
|
/** How a posted Instagram post is doing (read from Instagram, kept 15 minutes; `refresh` reads again). */
|
|
7785
7785
|
socialInsights: (id, refresh = false) => api2.request(`${base}/social/${id}/insights${refresh ? "?refresh=true" : ""}`),
|
|
7786
7786
|
deletePostedSocial: (id) => api2.request(`${base}/social/${id}/published`, { method: "DELETE" }),
|
|
7787
|
-
/**
|
|
7788
|
-
|
|
7787
|
+
/**
|
|
7788
|
+
* Change a posted post. LinkedIn changes its text in place (likes and comments stay); with `repost`, and on
|
|
7789
|
+
* the other channels, the old one comes down and the new text goes out now.
|
|
7790
|
+
*/
|
|
7791
|
+
republishSocial: (id, socialCopy, repost = false) => post(`${base}/social/${id}/republish`, {
|
|
7792
|
+
...socialCopy ? { social_copy: socialCopy } : {},
|
|
7793
|
+
...repost ? { repost: true } : {}
|
|
7794
|
+
}),
|
|
7789
7795
|
restoreSocial: (id) => post(`${base}/social/${id}/restore`),
|
|
7790
7796
|
pauseSocial: () => post(`${base}/social/pause`),
|
|
7791
7797
|
/**
|
|
@@ -8785,6 +8791,18 @@ var confirmCopy = {
|
|
|
8785
8791
|
return n.length > 1 ? `\u6279\u51C6 ${n.length} \u7BC7\u8CBC\u6587\uFF1A${n.map((i) => `#${i}`).join("\u3001")}\u3002\u6BCF\u7BC7\u5728\u63A5\u4E0B\u4F86\u7684\u767C\u6587\u6642\u9593\u767C\u51FA\uFF0C\u4F60\u7684\u4EBA\u8108\u90FD\u770B\u5F97\u5230\u3002` : `\u6279\u51C6\u8CBC\u6587 #${n[0]}\u3002\u5B83\u6703\u5728\u4E0B\u4E00\u500B\u767C\u6587\u6642\u9593\u767C\u51FA\uFF0C\u4F60\u7684\u4EBA\u8108\u90FD\u770B\u5F97\u5230\u3002`;
|
|
8786
8792
|
}
|
|
8787
8793
|
},
|
|
8794
|
+
social_publish_now: {
|
|
8795
|
+
en: (a) => `Post #${a.id} on its channel now, not at its posting time (even while posting is paused). Everyone who follows you can see it.`,
|
|
8796
|
+
"zh-Hant": (a) => `\u7ACB\u5373\u5728\u5B83\u7684\u983B\u9053\u767C\u51FA\u8CBC\u6587 #${a.id}\uFF0C\u4E0D\u7B49\u767C\u6587\u6642\u9593\uFF08\u66AB\u505C\u767C\u6587\u6642\u4E5F\u6703\u767C\uFF09\u3002\u767C\u51FA\u5F8C\u95DC\u6CE8\u4F60\u7684\u4EBA\u90FD\u770B\u5F97\u5230\u3002`
|
|
8797
|
+
},
|
|
8798
|
+
social_posted_update: {
|
|
8799
|
+
en: (a) => a.repost ? `Take post #${a.id} down (its likes and comments go) and put it up again${a.social_copy ? ` as ${q(a.social_copy)}` : ""}.` : `Change the text of the posted post #${a.id} to ${q(a.social_copy)}. On LinkedIn it stays up with its likes and comments; on other channels it comes down and goes out again.`,
|
|
8800
|
+
"zh-Hant": (a) => a.repost ? `\u64A4\u4E0B\u8CBC\u6587 #${a.id}\uFF08\u8B9A\u548C\u7559\u8A00\u6703\u6D88\u5931\uFF09\uFF0C\u518D\u91CD\u65B0\u767C\u51FA${a.social_copy ? `\uFF0C\u5167\u5BB9\u6539\u70BA ${q(a.social_copy)}` : ""}\u3002` : `\u628A\u5DF2\u767C\u51FA\u7684\u8CBC\u6587 #${a.id} \u6539\u70BA ${q(a.social_copy)}\u3002LinkedIn \u4E0A\u539F\u5730\u4FEE\u6539\uFF0C\u8B9A\u548C\u7559\u8A00\u4FDD\u7559\uFF1B\u5176\u4ED6\u983B\u9053\u6703\u64A4\u4E0B\u5F8C\u91CD\u767C\u3002`
|
|
8801
|
+
},
|
|
8802
|
+
social_posted_delete: {
|
|
8803
|
+
en: (a) => `Take post #${a.id} down from its channel, with its likes and comments. This can't be undone.`,
|
|
8804
|
+
"zh-Hant": (a) => `\u628A\u8CBC\u6587 #${a.id} \u5F9E\u5B83\u7684\u983B\u9053\u64A4\u4E0B\uFF0C\u8B9A\u548C\u7559\u8A00\u4E00\u8D77\u522A\u9664\uFF0C\u7121\u6CD5\u5FA9\u539F\u3002`
|
|
8805
|
+
},
|
|
8788
8806
|
social_skip: { en: (a) => `Skip post #${a.id}.`, "zh-Hant": (a) => `\u8DF3\u904E\u8CBC\u6587 #${a.id}\u3002` },
|
|
8789
8807
|
social_restore: {
|
|
8790
8808
|
en: (a) => `Bring back post #${a.id}.`,
|
|
@@ -8838,11 +8856,11 @@ var confirmCopy = {
|
|
|
8838
8856
|
},
|
|
8839
8857
|
social_pictures_attach: {
|
|
8840
8858
|
en: (a) => {
|
|
8841
|
-
const n = Array.isArray(a.urls) ? a.urls.length : 0;
|
|
8859
|
+
const n = (Array.isArray(a.urls) ? a.urls.length : 0) + (Array.isArray(a.files) ? a.files.length : 0);
|
|
8842
8860
|
return `${a.append ? "Add" : "Put"} your ${n} picture${n === 1 ? "" : "s"} ${a.append ? "after the pictures on" : "on"} post #${a.id}${a.append ? "" : ", in place of its picture"} (free). On LinkedIn a set goes out as a swipeable PDF carousel.`;
|
|
8843
8861
|
},
|
|
8844
8862
|
"zh-Hant": (a) => {
|
|
8845
|
-
const n = Array.isArray(a.urls) ? a.urls.length : 0;
|
|
8863
|
+
const n = (Array.isArray(a.urls) ? a.urls.length : 0) + (Array.isArray(a.files) ? a.files.length : 0);
|
|
8846
8864
|
return `${a.append ? `\u5728\u8CBC\u6587 #${a.id} \u73FE\u6709\u5716\u7247\u5F8C\u52A0\u4E0A` : `\u628A\u8CBC\u6587 #${a.id} \u7684\u5716\u7247\u63DB\u6210`}\u4F60\u7684 ${n} \u5F35\u5716\u7247\uFF08\u514D\u8CBB\uFF09\u3002\u5728 LinkedIn \u4E0A\uFF0C\u4E00\u7D44\u5716\u7247\u6703\u4EE5\u53EF\u6ED1\u52D5\u7684 PDF \u8F2A\u64AD\u767C\u51FA\u3002`;
|
|
8847
8865
|
}
|
|
8848
8866
|
},
|
|
@@ -11613,6 +11631,17 @@ var channelsParam = {
|
|
|
11613
11631
|
items: { type: "string", enum: CHANNEL_KEYS },
|
|
11614
11632
|
description: "Which channels to write for: linkedin, facebook, instagram, google_business. Leave out to write for every connected channel (one post each, one credit each); give one only when the user names it. YouTube and TikTok take a video: use social_video_add."
|
|
11615
11633
|
};
|
|
11634
|
+
var FILE_SCHEMA = {
|
|
11635
|
+
type: "object",
|
|
11636
|
+
properties: {
|
|
11637
|
+
download_url: { type: "string" },
|
|
11638
|
+
file_id: { type: "string" },
|
|
11639
|
+
mime_type: { type: "string" },
|
|
11640
|
+
file_name: { type: "string" }
|
|
11641
|
+
},
|
|
11642
|
+
required: ["download_url", "file_id"]
|
|
11643
|
+
};
|
|
11644
|
+
var fileLinks = (value) => (Array.isArray(value) ? value : value ? [value] : []).map((f) => String(f?.download_url ?? "").trim()).filter(Boolean);
|
|
11616
11645
|
var queuedAt = positionParam("the posts", "Leave out for the front (the next posting times).");
|
|
11617
11646
|
var channelsOf = (value) => {
|
|
11618
11647
|
const list2 = Array.isArray(value) ? value.map((v) => String(v).toLowerCase()).filter((v) => CHANNEL_KEYS.includes(v)) : [];
|
|
@@ -11736,6 +11765,59 @@ var socialTools = [
|
|
|
11736
11765
|
return { approved: results.filter((r) => r.ok).length, results };
|
|
11737
11766
|
}
|
|
11738
11767
|
}),
|
|
11768
|
+
tool({
|
|
11769
|
+
name: "social_publish_now",
|
|
11770
|
+
description: "Post one social post on its channel now instead of at its posting time: a draft waiting for approval, a queued post, or one that didn't go out. It goes even while posting is paused. It goes out in the background: the answer says it is publishing; read social_post_get after a minute for its link (or why it couldn't go). Not while its picture is being made.",
|
|
11771
|
+
parameters: { type: "object", properties: { id: { type: "integer" } }, required: ["id"] },
|
|
11772
|
+
group: "social",
|
|
11773
|
+
need: write5,
|
|
11774
|
+
risk: "W3",
|
|
11775
|
+
gates: ["VER"],
|
|
11776
|
+
run: async (ctx, { id }) => {
|
|
11777
|
+
const n = Number(id);
|
|
11778
|
+
if (!Number.isInteger(n) || n <= 0) throw new Error("Which post?");
|
|
11779
|
+
const post = await ws(ctx).publishSocialNow(n, { background: true });
|
|
11780
|
+
return post.status === "published" ? post : { ...post, note: "Posting now. Check social_post_get in a minute for its link." };
|
|
11781
|
+
}
|
|
11782
|
+
}),
|
|
11783
|
+
tool({
|
|
11784
|
+
name: "social_posted_update",
|
|
11785
|
+
description: "Change a social post that has already gone out. On LinkedIn the new text replaces the old in place: the post keeps its likes and comments (its pictures can't change this way). With repost, and on Facebook, Instagram and Google, the old post comes down with its likes and comments and the new version goes out now. Without social_copy and with repost, it goes out again as it is (e.g. after new pictures).",
|
|
11786
|
+
parameters: {
|
|
11787
|
+
type: "object",
|
|
11788
|
+
properties: {
|
|
11789
|
+
id: { type: "integer" },
|
|
11790
|
+
social_copy: { type: "string", description: "The post's new text" },
|
|
11791
|
+
repost: { type: "boolean", description: "Take it down and put it up again instead of editing it" }
|
|
11792
|
+
},
|
|
11793
|
+
required: ["id"]
|
|
11794
|
+
},
|
|
11795
|
+
group: "social",
|
|
11796
|
+
need: write5,
|
|
11797
|
+
risk: "W3",
|
|
11798
|
+
gates: ["VER"],
|
|
11799
|
+
run: async (ctx, { id, social_copy, repost }) => {
|
|
11800
|
+
const n = Number(id);
|
|
11801
|
+
if (!Number.isInteger(n) || n <= 0) throw new Error("Which post?");
|
|
11802
|
+
const copy = typeof social_copy === "string" ? social_copy.trim() : "";
|
|
11803
|
+
if (!copy && repost !== true) throw new Error("Give the new text (social_copy), or repost: true.");
|
|
11804
|
+
return ws(ctx).republishSocial(n, copy || void 0, repost === true);
|
|
11805
|
+
}
|
|
11806
|
+
}),
|
|
11807
|
+
tool({
|
|
11808
|
+
name: "social_posted_delete",
|
|
11809
|
+
description: "Take a social post that has gone out down from its channel (LinkedIn, Facebook, Instagram, Google). Its likes and comments go with it; this can't be undone.",
|
|
11810
|
+
parameters: { type: "object", properties: { id: { type: "integer" } }, required: ["id"] },
|
|
11811
|
+
group: "social",
|
|
11812
|
+
need: write5,
|
|
11813
|
+
risk: "W3",
|
|
11814
|
+
gates: ["VER"],
|
|
11815
|
+
run: async (ctx, { id }) => {
|
|
11816
|
+
const n = Number(id);
|
|
11817
|
+
if (!Number.isInteger(n) || n <= 0) throw new Error("Which post?");
|
|
11818
|
+
return ws(ctx).deletePostedSocial(n);
|
|
11819
|
+
}
|
|
11820
|
+
}),
|
|
11739
11821
|
tool({
|
|
11740
11822
|
name: "social_skip",
|
|
11741
11823
|
description: "Skip a LinkedIn post (it won't be posted; it can be restored).",
|
|
@@ -11982,27 +12064,35 @@ var socialTools = [
|
|
|
11982
12064
|
}),
|
|
11983
12065
|
tool({
|
|
11984
12066
|
name: "social_pictures_attach",
|
|
11985
|
-
description: "Put the user's own pictures on a post, in order: public https links to image files (JPEG, PNG or WebP
|
|
12067
|
+
description: "Put the user's own pictures on a post, in order: pictures from this chat (files: ones the user added or that were made here) or public https links to image files (urls), JPEG, PNG or WebP, for example carousel slides. 1-20 pictures; append adds them after the post's current ones. Free (no AI). They replace Maggie's picture; a post from an idea shares them with its other channels. Instagram and Facebook post the first 10, Google Business the first. On LinkedIn a set goes out as a swipeable PDF carousel unless social_carousel_set says images. If you can pass neither, give the user social_upload_link. Preview first.",
|
|
11986
12068
|
parameters: {
|
|
11987
12069
|
type: "object",
|
|
11988
12070
|
properties: {
|
|
11989
12071
|
id: { type: "integer", description: "The post (from social_list), not gone out yet" },
|
|
12072
|
+
files: {
|
|
12073
|
+
type: "array",
|
|
12074
|
+
items: FILE_SCHEMA,
|
|
12075
|
+
description: "Pictures from this chat, in the order they should appear"
|
|
12076
|
+
},
|
|
11990
12077
|
urls: {
|
|
11991
12078
|
type: "array",
|
|
11992
12079
|
items: { type: "string" },
|
|
11993
|
-
description: "Public https links to the image files, in the
|
|
12080
|
+
description: "Public https links to the image files, in order (after the files, when both are given)"
|
|
11994
12081
|
},
|
|
11995
12082
|
append: { type: "boolean", description: "Add after the post's current pictures instead of replacing them" }
|
|
11996
12083
|
},
|
|
11997
|
-
required: ["id"
|
|
12084
|
+
required: ["id"]
|
|
11998
12085
|
},
|
|
12086
|
+
fileParams: ["files"],
|
|
11999
12087
|
group: "social",
|
|
12000
12088
|
need: write5,
|
|
12001
12089
|
risk: "W1",
|
|
12002
12090
|
gates: ["VER"],
|
|
12003
|
-
run: async (ctx, { id, urls, append }) => {
|
|
12004
|
-
const list2 = (Array.isArray(urls) ? urls : []).map((u) => String(u).trim()).filter(
|
|
12005
|
-
|
|
12091
|
+
run: async (ctx, { id, urls, files, append }) => {
|
|
12092
|
+
const list2 = [...fileLinks(files), ...(Array.isArray(urls) ? urls : []).map((u) => String(u).trim())].filter(
|
|
12093
|
+
Boolean
|
|
12094
|
+
);
|
|
12095
|
+
if (!list2.length) throw new Error("Give the pictures (files from this chat, or https links).");
|
|
12006
12096
|
if (list2.length > 20) throw new Error("A post takes up to 20 pictures.");
|
|
12007
12097
|
if (list2.some((u) => !u.startsWith("https://"))) throw new Error("Every picture needs a public https link.");
|
|
12008
12098
|
const out = await ws(ctx).setSocialPictures(Number(id), { urls: list2, append: append === true });
|
|
@@ -12015,30 +12105,57 @@ var socialTools = [
|
|
|
12015
12105
|
}),
|
|
12016
12106
|
tool({
|
|
12017
12107
|
name: "social_carousel_pdf_attach",
|
|
12018
|
-
description: "A LinkedIn post goes out as this PDF: a swipeable document carousel (a public https link to
|
|
12108
|
+
description: "A LinkedIn post goes out as this PDF: a swipeable document carousel. The PDF from this chat (file: one the user added or that was made here) or a public https link to it (url); up to 300 pages and 20 MB, no password. Its first 20 pages also become the post's pictures (what its other channels post). Free. Change the pictures later and the carousel is made from them instead. If you can pass neither, give the user social_upload_link. Preview first.",
|
|
12019
12109
|
parameters: {
|
|
12020
12110
|
type: "object",
|
|
12021
12111
|
properties: {
|
|
12022
12112
|
id: { type: "integer", description: "A LinkedIn post (from social_list), not gone out yet" },
|
|
12113
|
+
file: { ...FILE_SCHEMA, description: "The PDF from this chat" },
|
|
12023
12114
|
url: { type: "string", description: "Public https link to the PDF file" },
|
|
12024
12115
|
title: {
|
|
12025
12116
|
type: "string",
|
|
12026
12117
|
description: "Shown on the carousel (up to 120 characters); the post's topic if left out"
|
|
12027
12118
|
}
|
|
12028
12119
|
},
|
|
12029
|
-
required: ["id"
|
|
12120
|
+
required: ["id"]
|
|
12030
12121
|
},
|
|
12122
|
+
fileParams: ["file"],
|
|
12031
12123
|
group: "social",
|
|
12032
12124
|
need: write5,
|
|
12033
12125
|
risk: "W1",
|
|
12034
12126
|
gates: ["VER", "LI"],
|
|
12035
|
-
run: async (ctx, { id, url, title }) => {
|
|
12036
|
-
const link = String(url ?? "").trim();
|
|
12037
|
-
if (!link.startsWith("https://"))
|
|
12127
|
+
run: async (ctx, { id, url, file, title }) => {
|
|
12128
|
+
const link = fileLinks(file)[0] ?? String(url ?? "").trim();
|
|
12129
|
+
if (!link.startsWith("https://"))
|
|
12130
|
+
throw new Error("Give the PDF (a file from this chat, or a public https link).");
|
|
12038
12131
|
const out = await ws(ctx).attachSocialCarouselPdf(Number(id), { url: link, ...title ? { title } : {} });
|
|
12039
12132
|
return { id: out.id, pages: out.pages, pictures: out.pictures, carousel: out.carousel ?? null };
|
|
12040
12133
|
}
|
|
12041
12134
|
}),
|
|
12135
|
+
tool({
|
|
12136
|
+
name: "social_upload_link",
|
|
12137
|
+
description: "A link that opens one post's picture in the CMO.so app, where the user uploads their own photos (up to 20, a carousel on LinkedIn) or, on a computer, a PDF carousel themselves. Use it when you can't pass the files (this chat can't send files to tools, or they are on the user's device). Give the link; nothing changes until they upload.",
|
|
12138
|
+
parameters: {
|
|
12139
|
+
type: "object",
|
|
12140
|
+
properties: { id: { type: "integer", description: "The post (from social_list), not gone out yet" } },
|
|
12141
|
+
required: ["id"]
|
|
12142
|
+
},
|
|
12143
|
+
group: "social",
|
|
12144
|
+
need: read5,
|
|
12145
|
+
risk: "R0",
|
|
12146
|
+
run: async (ctx, { id }) => {
|
|
12147
|
+
const post = await ws(ctx).socialPost(Number(id));
|
|
12148
|
+
if (["published", "publishing", "cancelled"].includes(String(post.status)))
|
|
12149
|
+
throw new Error("That post has already gone out (or was skipped): its pictures can't change.");
|
|
12150
|
+
const site = ctx.domain ? `&site=${encodeURIComponent(ctx.domain)}` : "";
|
|
12151
|
+
return {
|
|
12152
|
+
id: post.id,
|
|
12153
|
+
channel: post.provider ?? null,
|
|
12154
|
+
url: `https://app.cmo.so/social?picture=${post.id}${site}`,
|
|
12155
|
+
how: post.provider === "linkedin" ? "Open it, then Upload several photos (in order), or on a computer Upload a PDF carousel." : "Open it, then Upload several photos (in order)."
|
|
12156
|
+
};
|
|
12157
|
+
}
|
|
12158
|
+
}),
|
|
12042
12159
|
tool({
|
|
12043
12160
|
name: "social_carousel_set",
|
|
12044
12161
|
description: "How a LinkedIn post with a set of pictures goes out: format pdf (a swipeable PDF carousel made from the pictures, the default) or images (several pictures); and the carousel's title. Preview first.",
|
|
@@ -23167,6 +23284,30 @@ function description(t) {
|
|
|
23167
23284
|
].filter(Boolean);
|
|
23168
23285
|
return `${t.description}${notes.length ? ` (${notes.join("; ")}.)` : ""} Changes data: call without confirm for a preview, then with confirm: true and the confirmation after the user agrees.`;
|
|
23169
23286
|
}
|
|
23287
|
+
function fileMeta(t) {
|
|
23288
|
+
return t.fileParams?.length ? { "openai/fileParams": t.fileParams } : void 0;
|
|
23289
|
+
}
|
|
23290
|
+
function withFreshFiles(t, signed, now) {
|
|
23291
|
+
if (!t.fileParams?.length) return signed;
|
|
23292
|
+
const fresh = /* @__PURE__ */ new Map();
|
|
23293
|
+
const collect = (v) => {
|
|
23294
|
+
for (const f of Array.isArray(v) ? v : [v])
|
|
23295
|
+
if (f && typeof f === "object" && typeof f.file_id === "string")
|
|
23296
|
+
fresh.set(f.file_id, f);
|
|
23297
|
+
};
|
|
23298
|
+
for (const p of t.fileParams) collect(now[p]);
|
|
23299
|
+
const swap = (v) => {
|
|
23300
|
+
if (Array.isArray(v)) return v.map(swap);
|
|
23301
|
+
if (v && typeof v === "object" && typeof v.file_id === "string") {
|
|
23302
|
+
const f = fresh.get(v.file_id);
|
|
23303
|
+
return f && typeof f.download_url === "string" ? { ...v, download_url: f.download_url } : v;
|
|
23304
|
+
}
|
|
23305
|
+
return v;
|
|
23306
|
+
};
|
|
23307
|
+
const out = { ...signed };
|
|
23308
|
+
for (const p of t.fileParams) if (p in out) out[p] = swap(out[p]);
|
|
23309
|
+
return out;
|
|
23310
|
+
}
|
|
23170
23311
|
function toolArgs(args) {
|
|
23171
23312
|
const out = {};
|
|
23172
23313
|
for (const [k, v] of Object.entries(args ?? {})) if (!CONTROL_ARGS.includes(k) && v !== void 0) out[k] = v;
|
|
@@ -23296,7 +23437,7 @@ function createMcpServer(deps) {
|
|
|
23296
23437
|
description: description(t),
|
|
23297
23438
|
inputSchema: inputSchema(t),
|
|
23298
23439
|
annotations: { title: t.name.replace(/_/g, " "), ...annotations(t) },
|
|
23299
|
-
...deps.widgets && toolMeta(t.name) ? { _meta: toolMeta(t.name) } : {}
|
|
23440
|
+
...fileMeta(t) || deps.widgets && toolMeta(t.name) ? { _meta: { ...deps.widgets ? toolMeta(t.name) : {}, ...fileMeta(t) } } : {}
|
|
23300
23441
|
}))
|
|
23301
23442
|
};
|
|
23302
23443
|
});
|
|
@@ -23332,7 +23473,7 @@ function createMcpServer(deps) {
|
|
|
23332
23473
|
return fail(
|
|
23333
23474
|
access.keyAccess === "read" && isWrite(t) ? "This key can only read. Reconnect with write access to make changes." : `You don't have permission to do this on ${domain ?? "this website"}.`
|
|
23334
23475
|
);
|
|
23335
|
-
const callArgs = approval ? approval.calls[0]?.args ?? {} : toolArgs(args);
|
|
23476
|
+
const callArgs = approval ? withFreshFiles(t, approval.calls[0]?.args ?? {}, toolArgs(args)) : toolArgs(args);
|
|
23336
23477
|
const ctx = { api: api2, domain, client: "mcp" };
|
|
23337
23478
|
if (!isWrite(t)) return ok(await t.run(ctx, callArgs), viewForTool(t.name) ? { workspace: domain } : void 0);
|
|
23338
23479
|
let standing = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmo-so/mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "CMO.so's MCP server for Claude Desktop, Cursor, VS Code and other MCP clients: manage your websites, blog posts, LinkedIn queue and results with Maggie.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|