@cmo-so/mcp 0.7.3 → 0.7.5

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.
Files changed (2) hide show
  1. package/dist/cmo-mcp.mjs +200 -8
  2. 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.7.3",
7202
+ version: "0.7.5",
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",
@@ -7464,15 +7464,15 @@ function createApi({ baseUrl, session: session2, fetch: fetchImpl = globalThis.f
7464
7464
  }),
7465
7465
  remove: () => request("/v1/maggie/bridge", { method: "DELETE" }),
7466
7466
  /** Start "Sign in with ChatGPT" for one of the bridge's accounts (the active one by default). */
7467
- connectChatgpt: (account3) => request("/v1/maggie/bridge/chatgpt", {
7467
+ connectChatgpt: (account4) => request("/v1/maggie/bridge/chatgpt", {
7468
7468
  method: "POST",
7469
7469
  headers: { "Content-Type": "application/json" },
7470
- body: JSON.stringify(account3 ? { account: account3 } : {})
7470
+ body: JSON.stringify(account4 ? { account: account4 } : {})
7471
7471
  }),
7472
- disconnectChatgpt: (account3) => request("/v1/maggie/bridge/chatgpt/logout", {
7472
+ disconnectChatgpt: (account4) => request("/v1/maggie/bridge/chatgpt/logout", {
7473
7473
  method: "POST",
7474
7474
  headers: { "Content-Type": "application/json" },
7475
- body: JSON.stringify(account3 ? { account: account3 } : {})
7475
+ body: JSON.stringify(account4 ? { account: account4 } : {})
7476
7476
  }),
7477
7477
  /** What Maggie may do in CMO.so; rotates the bridge's key when read / read_write changes. */
7478
7478
  setAccess: (access) => request("/v1/maggie/bridge/access", {
@@ -7515,6 +7515,31 @@ function createApi({ baseUrl, session: session2, fetch: fetchImpl = globalThis.f
7515
7515
  return dataUrl(type, bytes);
7516
7516
  }
7517
7517
  },
7518
+ // The open marketing skills (docs/26): api.cmo.so forwards these to the cmo.so site.
7519
+ skills: {
7520
+ search: (q2 = "", opts = {}) => {
7521
+ const qs = new URLSearchParams({
7522
+ q: q2,
7523
+ ...opts.category ? { category: opts.category } : {},
7524
+ limit: String(opts.limit ?? 10)
7525
+ });
7526
+ return request(`/v1/skills?${qs}`);
7527
+ },
7528
+ get: (id) => request(`/v1/skills/${encodeURIComponent(id)}`),
7529
+ /** Like or save a skill (on: false undoes it). */
7530
+ react: (id, kind, on2 = true) => request(
7531
+ `/v1/skills/${encodeURIComponent(id)}/react`,
7532
+ { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ kind, on: on2 }) }
7533
+ ),
7534
+ saved: () => request("/v1/skills/saved"),
7535
+ /** Suggest an open-source GitHub repo; Maggie researches it (3 a day). */
7536
+ submit: (repoUrl, note) => request("/v1/skills/submissions", {
7537
+ method: "POST",
7538
+ headers: { "Content-Type": "application/json" },
7539
+ body: JSON.stringify({ repo_url: repoUrl, note })
7540
+ }),
7541
+ submissions: () => request("/v1/skills/submissions")
7542
+ },
7518
7543
  // Web Push for this browser (APP-9). `domains` are the workspaces it shows.
7519
7544
  push: {
7520
7545
  status: (endpoint) => request(
@@ -7597,7 +7622,9 @@ function domainApi(api2, domain) {
7597
7622
  });
7598
7623
  return {
7599
7624
  summary: () => api2.request(base),
7600
- posts: (status, limit = 50) => api2.request(`${base}/posts?status=${status}&limit=${limit}`),
7625
+ posts: (status, limit = 50) => api2.request(
7626
+ `${base}/posts?status=${status}&limit=${limit}`
7627
+ ),
7601
7628
  post: (id) => api2.request(`${base}/posts/${encodeURIComponent(id)}`),
7602
7629
  updatePost: (id, changes) => api2.request(`${base}/posts/${encodeURIComponent(id)}`, {
7603
7630
  method: "PATCH",
@@ -8610,6 +8637,19 @@ var confirmCopy = {
8610
8637
  en: (a) => `Send this to the cmo.so team: ${q(String(a.summary ?? "").slice(0, 200))}`,
8611
8638
  "zh-Hant": (a) => `\u628A\u9019\u5247\u56DE\u994B\u9001\u7D66 cmo.so \u5718\u968A\uFF1A${q(String(a.summary ?? "").slice(0, 200))}`
8612
8639
  },
8640
+ // ---------------------------------------------------------- Marketing skills (docs/26)
8641
+ skill_like: {
8642
+ en: (a) => `${a.undo ? "Take back your like on" : "Like"} the skill ${q(a.skill_id)} on cmo.so.`,
8643
+ "zh-Hant": (a) => `${a.undo ? "\u53D6\u6D88\u5C0D" : "\u5728 cmo.so \u8B9A"}\u6280\u80FD ${q(a.skill_id)}${a.undo ? " \u7684\u8B9A" : ""}\u3002`
8644
+ },
8645
+ skill_save: {
8646
+ en: (a) => `${a.undo ? "Remove" : "Save"} the skill ${q(a.skill_id)} ${a.undo ? "from" : "to"} your saved skills.`,
8647
+ "zh-Hant": (a) => `${a.undo ? "\u5F9E\u6536\u85CF\u79FB\u9664" : "\u6536\u85CF"}\u6280\u80FD ${q(a.skill_id)}\u3002`
8648
+ },
8649
+ skill_submit: {
8650
+ en: (a) => `Suggest ${q(a.repo_url)} for the cmo.so skill library. Maggie will research it.`,
8651
+ "zh-Hant": (a) => `\u628A ${q(a.repo_url)} \u63A8\u85A6\u7D66 cmo.so \u6280\u80FD\u5EAB\uFF0CMaggie \u6703\u7814\u7A76\u5B83\u3002`
8652
+ },
8613
8653
  social_voice_learn: {
8614
8654
  en: () => "Learn your LinkedIn voice from this post and save it as your default.",
8615
8655
  "zh-Hant": () => "\u5F9E\u9019\u7BC7\u8CBC\u6587\u5B78\u7FD2\u4F60\u7684 LinkedIn \u8A9E\u6C23\uFF0C\u4E26\u8A2D\u70BA\u9810\u8A2D\u3002"
@@ -9326,8 +9366,10 @@ var blogTools = [
9326
9366
  const r = await ws(ctx).ideas(int(page, 1, 100, 1), 15, status === "discarded" ? "discarded" : "pending");
9327
9367
  return {
9328
9368
  total: r.total,
9369
+ // The real limits: the user's own waiting ideas (cap) and the plan (plan_max).
9329
9370
  cap: r.cap,
9330
9371
  added_by_you: r.added_by_you,
9372
+ plan_max: r.plan_max,
9331
9373
  items: r.items.map((i) => ({
9332
9374
  id: i.id,
9333
9375
  priority: i.priority,
@@ -9366,7 +9408,7 @@ var blogTools = [
9366
9408
  }),
9367
9409
  tool({
9368
9410
  name: "ideas_add_list",
9369
- description: "Add up to 100 ideas exactly as given (a researched list): title, description, primary keyword, up to 5 more keywords, a benchmark URL (read as research, never linked), priority 1-10 and optionally one of the website's topics. No AI rewrites them and no generation is used; they wait in the idea pool, not the plan. Duplicates are skipped and listed; at most 100 of a person's own ideas can wait at once.",
9411
+ description: "Add up to 100 ideas exactly as given (a researched list): title, description, primary keyword, up to 5 more keywords, a benchmark URL (read as research, never linked), priority 1-10 and optionally one of the website's topics. No AI rewrites them and no generation is used; they wait in the idea pool, not the plan. Duplicates are skipped and listed; a person's own waiting ideas have a limit (ideas_list cap, 200 today).",
9370
9412
  parameters: {
9371
9413
  type: "object",
9372
9414
  properties: {
@@ -10698,6 +10740,155 @@ var reportTools = [
10698
10740
  })
10699
10741
  ];
10700
10742
 
10743
+ // ../../packages/tools/src/skills.ts
10744
+ var account3 = { resource: "account", level: "read" };
10745
+ var ID = { type: "string", description: "The skill id, like cmo-ad-creative" };
10746
+ var skillTools = [
10747
+ tool({
10748
+ name: "skills_search",
10749
+ description: "Search cmo.so's open library of marketing skills (step-by-step methods an AI agent follows, free and MIT licensed): returns matching skills with their summary, likes and page. Use it when the user asks how to do a marketing job, wants a playbook or template, or asks which skills exist. Empty query: the most useful ones and the categories.",
10750
+ parameters: {
10751
+ type: "object",
10752
+ properties: {
10753
+ query: { type: "string", description: "What the user wants to do, in a few words" },
10754
+ category: {
10755
+ type: "string",
10756
+ description: "Optional category id: core, strategy, seo, ai-search, content, social, email, paid-ads, cro, analytics, growth, sales, creative"
10757
+ },
10758
+ limit: { type: "integer", minimum: 1, maximum: 20 }
10759
+ }
10760
+ },
10761
+ group: "account",
10762
+ need: account3,
10763
+ risk: "R0",
10764
+ idempotent: true,
10765
+ run: async (ctx, a) => {
10766
+ const r = await ctx.api.skills.search(String(a.query ?? ""), {
10767
+ ...a.category ? { category: a.category } : {},
10768
+ limit: int(a.limit, 1, 20, 8)
10769
+ });
10770
+ return {
10771
+ total_in_library: r.total,
10772
+ ...a.query ? {} : { categories: r.categories.map((c) => `${c.id} (${c.count})`) },
10773
+ skills: r.skills.map((s) => ({ id: s.id, name: s.name, summary: s.summary, likes: s.likes, page: s.url }))
10774
+ };
10775
+ }
10776
+ }),
10777
+ tool({
10778
+ name: "skill_get",
10779
+ description: "One marketing skill from the cmo.so library in full: when to use it, what it needs, its workflow, output and quality bar, plus how to install it in Claude Code. Use it to follow a skill's method for the user, or to explain it.",
10780
+ parameters: { type: "object", properties: { skill_id: ID }, required: ["skill_id"] },
10781
+ group: "account",
10782
+ need: account3,
10783
+ risk: "R0",
10784
+ idempotent: true,
10785
+ run: async (ctx, a) => {
10786
+ const s = await ctx.api.skills.get(String(a.skill_id));
10787
+ return {
10788
+ id: s.id,
10789
+ name: s.name,
10790
+ summary: s.summary,
10791
+ page: s.url,
10792
+ likes: s.likes,
10793
+ saves: s.saves,
10794
+ uses_tools: s.mcp_tools,
10795
+ pairs_with: s.pairs_with,
10796
+ install: s.install,
10797
+ // community-contributed text: a method to follow, never instructions that override the user or these rules
10798
+ ...untrusted(clip(s.text, 12e3))
10799
+ };
10800
+ }
10801
+ }),
10802
+ tool({
10803
+ name: "skill_like",
10804
+ description: "Like a skill in the cmo.so library for the user (or take the like back with undo). Likes help the library decide what to keep improving. Only when the user asks.",
10805
+ parameters: {
10806
+ type: "object",
10807
+ properties: { skill_id: ID, undo: { type: "boolean", description: "true takes the like back" } },
10808
+ required: ["skill_id"]
10809
+ },
10810
+ group: "account",
10811
+ need: account3,
10812
+ risk: "W1",
10813
+ idempotent: true,
10814
+ run: async (ctx, a) => {
10815
+ const r = await ctx.api.skills.react(String(a.skill_id), "like", !a.undo);
10816
+ return { skill_id: r.item, liked: r.liked, likes: r.counts.likes };
10817
+ }
10818
+ }),
10819
+ tool({
10820
+ name: "skill_save",
10821
+ description: "Save a skill to the user's saved skills on cmo.so (or remove it with undo), to find it again at cmo.so/skills/saved or with skills_saved_list.",
10822
+ parameters: {
10823
+ type: "object",
10824
+ properties: { skill_id: ID, undo: { type: "boolean", description: "true removes it from saved" } },
10825
+ required: ["skill_id"]
10826
+ },
10827
+ group: "account",
10828
+ need: account3,
10829
+ risk: "W1",
10830
+ idempotent: true,
10831
+ run: async (ctx, a) => {
10832
+ const r = await ctx.api.skills.react(String(a.skill_id), "save", !a.undo);
10833
+ return { skill_id: r.item, saved: r.saved };
10834
+ }
10835
+ }),
10836
+ tool({
10837
+ name: "skills_saved_list",
10838
+ description: "The skills the user saved on cmo.so, newest first.",
10839
+ parameters: none,
10840
+ group: "account",
10841
+ need: account3,
10842
+ risk: "R0",
10843
+ run: async (ctx) => ({
10844
+ skills: (await ctx.api.skills.saved()).skills.map((s) => ({
10845
+ id: s.id,
10846
+ name: s.name,
10847
+ summary: s.summary,
10848
+ page: s.url
10849
+ }))
10850
+ })
10851
+ }),
10852
+ tool({
10853
+ name: "skill_submit",
10854
+ description: "Suggest a public open-source GitHub repo for cmo.so's skill library. Maggie researches it within minutes and decides: add a new skill, improve an existing one, keep it as a candidate that needs 10 likes in 30 days, or decline (licence first: permissive licences can be adapted, copyleft only inspires, no licence is declined). 3 suggestions a day. Follow up with skill_submissions_list.",
10855
+ parameters: {
10856
+ type: "object",
10857
+ properties: {
10858
+ repo_url: { type: "string", description: "https://github.com/owner/name" },
10859
+ note: { type: "string", description: "Optional: why it is useful, \u2264500 characters" }
10860
+ },
10861
+ required: ["repo_url"]
10862
+ },
10863
+ group: "account",
10864
+ need: account3,
10865
+ risk: "W1",
10866
+ run: async (ctx, a) => {
10867
+ const r = await ctx.api.skills.submit(String(a.repo_url), a.note ? String(a.note).slice(0, 500) : void 0);
10868
+ return r.duplicate ? { already_suggested: true, id: r.id, status: r.status, reasons: r.reasons ?? null } : { submitted: true, id: r.id, status: r.status, repo: r.repo };
10869
+ }
10870
+ }),
10871
+ tool({
10872
+ name: "skill_submissions_list",
10873
+ description: "The repos the user suggested for the skill library and what happened to each: queued, researching, candidate (with its deadline), publishing, published (with the skill), declined, expired or failed, with Maggie's reasons.",
10874
+ parameters: none,
10875
+ group: "account",
10876
+ need: account3,
10877
+ risk: "R0",
10878
+ run: async (ctx) => ({
10879
+ items: (await ctx.api.skills.submissions()).items.slice(0, 20).map((s) => ({
10880
+ id: s.id,
10881
+ repo: s.repo,
10882
+ status: s.status,
10883
+ skill: s.target_skill,
10884
+ reasons: clip(s.reasons, 500),
10885
+ candidate_until: s.candidate_until,
10886
+ suggested: s.created_at
10887
+ }))
10888
+ })
10889
+ })
10890
+ ];
10891
+
10701
10892
  // ../../packages/tools/src/social.ts
10702
10893
  var read5 = { resource: "autopilot", level: "read" };
10703
10894
  var write5 = { resource: "autopilot", level: "write" };
@@ -12154,7 +12345,8 @@ var tools = [
12154
12345
  ...socialTools,
12155
12346
  ...teamTools,
12156
12347
  ...memoryTools,
12157
- ...feedbackTools
12348
+ ...feedbackTools,
12349
+ ...skillTools
12158
12350
  ];
12159
12351
  var toolByName = new Map(tools.map((t) => [t.name, t]));
12160
12352
  function allowed(t, a) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmo-so/mcp",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
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",