one-design-mcp 0.2.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +14 -1
  2. package/dist/server.js +173 -28
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -103,6 +103,17 @@ the same email (`sign_in`). The session is kept in `~/.config/one-design/mcp-acc
103
103
 
104
104
  Needs Node.js 20 or newer.
105
105
 
106
+ **One command** (adds it to Claude Desktop, Cursor and Claude Code, wherever they're installed, and
107
+ keeps a `.bak` of any config it changes):
108
+
109
+ ```bash
110
+ npx -y one-design-mcp setup
111
+ ```
112
+
113
+ `--dry-run` shows what it would change, `--app desktop|cursor|code` picks one app, and `--remove`
114
+ takes it out again. An agent with a terminal (Claude Code, Cursor) can run it for you: ask it to
115
+ "set up the One Design MCP server with npx -y one-design-mcp setup".
116
+
106
117
  **Claude Code**
107
118
 
108
119
  ```bash
@@ -129,6 +140,8 @@ claude mcp add one-design -- node /path/to/one-design/mcp/dist/server.js
129
140
 
130
141
  The build takes the site's public Supabase address and anon key from `../.env.local` (or
131
142
  `ONE_DESIGN_SUPABASE_URL` / `ONE_DESIGN_SUPABASE_ANON_KEY`) so sign-in and checkout work.
132
- `ONE_DESIGN_PAYMENTS=off` makes a free-beta build with every tool open. Link Lock's encryption is
143
+ `ONE_DESIGN_PAYMENTS=off` makes a free-beta build with every tool open. These are build settings
144
+ only: at run time nothing a user sets changes the plan (no environment variables, and the plan is
145
+ never read back from the saved session; it's checked against the account and kept in memory). Link Lock's encryption is
133
146
  the website's own (`src/lib/linklock.ts`), bundled at build time, so links made here open on
134
147
  one.design. Plan rules mirror `src/lib/plans.ts` and `src/lib/plan.ts`: keep `src/plan.ts` in step.
package/dist/server.js CHANGED
@@ -1,10 +1,122 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res, err) => function __init() {
5
+ if (err) throw err[0];
6
+ try {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ } catch (e) {
9
+ throw err = [e], e;
10
+ }
11
+ };
3
12
  var __export = (target, all) => {
4
13
  for (var name in all)
5
14
  __defProp(target, name, { get: all[name], enumerable: true });
6
15
  };
7
16
 
17
+ // src/setup.ts
18
+ var setup_exports = {};
19
+ __export(setup_exports, {
20
+ setup: () => setup
21
+ });
22
+ import { promises as fs9, existsSync } from "node:fs";
23
+ import path9 from "node:path";
24
+ import os7 from "node:os";
25
+ import { execFileSync as execFileSync2 } from "node:child_process";
26
+ function entry() {
27
+ return { command: npxPath(), args: ["-y", "one-design-mcp@latest"] };
28
+ }
29
+ function npxPath() {
30
+ try {
31
+ const found = execFileSync2(process.platform === "win32" ? "where" : "which", ["npx"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).split(/\r?\n/)[0].trim();
32
+ if (found) return found;
33
+ } catch {
34
+ }
35
+ const beside = path9.join(path9.dirname(process.execPath), process.platform === "win32" ? "npx.cmd" : "npx");
36
+ return existsSync(beside) ? beside : "npx";
37
+ }
38
+ function desktopConfig() {
39
+ const home = os7.homedir();
40
+ if (process.platform === "darwin") return path9.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
41
+ if (process.platform === "win32") return path9.join(process.env.APPDATA ?? path9.join(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
42
+ return path9.join(process.env.XDG_CONFIG_HOME ?? path9.join(home, ".config"), "Claude", "claude_desktop_config.json");
43
+ }
44
+ async function editJson(file, remove, dry) {
45
+ if (!existsSync(path9.dirname(file))) return "not installed here, skipped";
46
+ let config = {};
47
+ let before = "";
48
+ if (existsSync(file)) {
49
+ before = await fs9.readFile(file, "utf8");
50
+ try {
51
+ config = before.trim() ? JSON.parse(before) : {};
52
+ } catch {
53
+ return `its config isn\u2019t valid JSON, left alone: ${file}`;
54
+ }
55
+ }
56
+ const servers = { ...config.mcpServers ?? {} };
57
+ const had = NAME in servers;
58
+ if (remove) {
59
+ if (!had) return "not set up there, nothing to remove";
60
+ delete servers[NAME];
61
+ } else servers[NAME] = entry();
62
+ const after = JSON.stringify({ ...config, mcpServers: servers }, null, 2) + "\n";
63
+ if (after === before) return "already set up";
64
+ if (dry) return remove ? "would remove One Design" : had ? "would update One Design" : "would add One Design";
65
+ await fs9.mkdir(path9.dirname(file), { recursive: true });
66
+ if (before) await fs9.writeFile(`${file}.bak`, before);
67
+ await fs9.writeFile(file, after);
68
+ return `${remove ? "removed" : had ? "updated" : "added"} (${file}${before ? ", old copy saved as .bak" : ""})`;
69
+ }
70
+ function claudeCode(remove, dry) {
71
+ try {
72
+ execFileSync2(process.platform === "win32" ? "where" : "which", ["claude"], { stdio: "ignore" });
73
+ } catch {
74
+ return "not installed here, skipped";
75
+ }
76
+ const list = (() => {
77
+ try {
78
+ return execFileSync2("claude", ["mcp", "list"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
79
+ } catch {
80
+ return "";
81
+ }
82
+ })();
83
+ const had = new RegExp(`^${NAME}:`, "m").test(list);
84
+ if (remove) {
85
+ if (!had) return "not set up there, nothing to remove";
86
+ if (dry) return "would remove One Design";
87
+ execFileSync2("claude", ["mcp", "remove", NAME, "--scope", "user"], { stdio: "ignore" });
88
+ return "removed";
89
+ }
90
+ if (had) return "already set up";
91
+ if (dry) return "would add One Design (for every project)";
92
+ execFileSync2("claude", ["mcp", "add", "--scope", "user", NAME, "--", "npx", "-y", "one-design-mcp@latest"], { stdio: "ignore" });
93
+ return "added (for every project)";
94
+ }
95
+ async function setup(argv) {
96
+ const dry = argv.includes("--dry-run");
97
+ const remove = argv.includes("--remove");
98
+ const only = argv[argv.indexOf("--app") + 1];
99
+ const want = (app) => !argv.includes("--app") || only === app;
100
+ const lines = [];
101
+ if (want("desktop")) lines.push(`Claude Desktop: ${await editJson(desktopConfig(), remove, dry)}`);
102
+ if (want("cursor")) lines.push(`Cursor: ${await editJson(cursorConfig(), remove, dry)}`);
103
+ if (want("code")) lines.push(`Claude Code: ${claudeCode(remove, dry)}`);
104
+ console.log(`One Design for Claude${dry ? " (dry run, nothing changed)" : ""}
105
+
106
+ ${lines.map((l) => ` ${l}`).join("\n")}
107
+ `);
108
+ if (!dry && !remove && lines.some((l) => /added|updated/.test(l)))
109
+ console.log("Next: quit and reopen the app (on a Mac, \u2318Q), then ask Claude \u201CWhat can One Design do?\u201D.\nOther MCP apps: add a server with the command npx and the arguments -y one-design-mcp@latest.");
110
+ }
111
+ var NAME, cursorConfig;
112
+ var init_setup = __esm({
113
+ "src/setup.ts"() {
114
+ "use strict";
115
+ NAME = "one-design";
116
+ cursorConfig = () => path9.join(os7.homedir(), ".cursor", "mcp.json");
117
+ }
118
+ });
119
+
8
120
  // src/server.ts
9
121
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
122
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -15,16 +127,33 @@ import path3 from "node:path";
15
127
  import os3 from "node:os";
16
128
 
17
129
  // src/images.ts
18
- import sharp from "sharp";
19
- import { promises as fs } from "node:fs";
130
+ import sharpLib from "sharp";
131
+ import { promises as fs, rmSync, statSync } from "node:fs";
20
132
  import path from "node:path";
21
133
  import os from "node:os";
22
- import { execFile } from "node:child_process";
134
+ import { execFile, execFileSync } from "node:child_process";
23
135
  import { promisify } from "node:util";
24
136
  var EXT = { jpeg: ".jpg", png: ".png", webp: ".webp" };
25
137
  var IMAGE_FILE = /\.(jpe?g|png|webp|avif|gif|tiff?|heic|heif)$/i;
26
138
  var ToolError = class extends Error {
27
139
  };
140
+ var heicCopies = /* @__PURE__ */ new Map();
141
+ process.once("exit", () => heicCopies.forEach((tmp) => rmSync(tmp, { force: true })));
142
+ function decodable(input) {
143
+ if (typeof input !== "string" || !/\.(heic|heif)$/i.test(input) || process.platform !== "darwin") return input;
144
+ const key = `${path.resolve(input)}:${statSync(input, { throwIfNoEntry: false })?.mtimeMs ?? 0}`;
145
+ const known = heicCopies.get(key);
146
+ if (known) return known;
147
+ const tmp = path.join(os.tmpdir(), `one-design-${process.pid}-${heicCopies.size}-${Math.random().toString(36).slice(2, 8)}.jpg`);
148
+ try {
149
+ execFileSync("sips", ["-s", "format", "jpeg", "-s", "formatOptions", "100", input, "--out", tmp], { stdio: "ignore" });
150
+ } catch {
151
+ throw new ToolError(`Couldn\u2019t open ${path.basename(input)} as a HEIC photo. It may be damaged.`);
152
+ }
153
+ heicCopies.set(key, tmp);
154
+ return tmp;
155
+ }
156
+ var sharp = Object.assign((input, options) => sharpLib(decodable(input), options), sharpLib);
28
157
  async function openReadable(file) {
29
158
  if (!/\.(heic|heif)$/i.test(file)) return { path: file, done: async () => void 0 };
30
159
  if (process.platform !== "darwin") throw new ToolError(`${path.basename(file)} is a HEIC photo, which can only be opened here on a Mac. Convert it at https://one.design/heic-convert first.`);
@@ -206,9 +335,9 @@ import { promises as fs2 } from "node:fs";
206
335
  import path2 from "node:path";
207
336
  import os2 from "node:os";
208
337
  import { exec } from "node:child_process";
209
- var SB_URL = process.env.ONE_DESIGN_SUPABASE_URL || (true ? "https://jaznopltkefnwooyksrk.supabase.co" : "");
210
- var SB_KEY = process.env.ONE_DESIGN_SUPABASE_ANON_KEY || (true ? "sb_publishable_WfXjMAOVoXE9dtJMfJCshw_9rfMboU7" : "");
211
- var PAYMENTS = (process.env.ONE_DESIGN_PAYMENTS || (true ? "on" : "on")).toLowerCase();
338
+ var SB_URL = true ? "https://jaznopltkefnwooyksrk.supabase.co" : "";
339
+ var SB_KEY = true ? "sb_publishable_WfXjMAOVoXE9dtJMfJCshw_9rfMboU7" : "";
340
+ var PAYMENTS = (true ? "on" : "on").toLowerCase();
212
341
  var SITE = "https://one.design";
213
342
  var PRICING_URL = `${SITE}/pricing`;
214
343
  var ACCOUNT_URL = `${SITE}/account`;
@@ -259,18 +388,19 @@ function isPaid(s) {
259
388
  if (["active", "on_trial", "past_due"].includes(s.status)) return true;
260
389
  return s.status === "cancelled" && !!s.ends_at && new Date(s.ends_at) > /* @__PURE__ */ new Date();
261
390
  }
391
+ var checked = null;
262
392
  async function currentPlan(force = false) {
263
393
  if (freeBeta()) return { plan: "pro", label: "Free beta: every tool is open to everyone for now" };
264
394
  if (!accountsAvailable()) return { plan: "free", label: "Free (sign-in isn\u2019t set up in this build)" };
265
395
  const s = await freshSession();
266
396
  if (!s) return { plan: "free", label: "Free (not signed in)" };
267
- if (!force && s.plan && s.checked_at && Date.now() - s.checked_at < 10 * 60 * 1e3) return { plan: s.plan, email: s.email, label: s.planLabel ?? s.plan };
397
+ if (!force && checked && checked.user === s.user_id && Date.now() - checked.at < 10 * 60 * 1e3) return { plan: checked.plan, email: s.email, label: checked.label };
268
398
  const r = await sb(`/rest/v1/subscriptions?select=plan,status,ends_at&user_id=eq.${s.user_id}`, { token: s.access_token });
269
399
  const rows = r.ok ? await r.json() : [];
270
400
  const sub = rows[0] ?? null;
271
401
  const paid = isPaid(sub);
272
402
  const label = paid ? sub.plan === "lifetime" ? "Pro (Lifetime)" : "Pro" : "Free";
273
- await writeSession({ ...s, plan: paid ? "pro" : "free", planLabel: label, checked_at: Date.now() });
403
+ if (r.ok) checked = { user: s.user_id, plan: paid ? "pro" : "free", label, at: Date.now() };
274
404
  return { plan: paid ? "pro" : "free", email: s.email, label };
275
405
  }
276
406
  async function sendCode(email) {
@@ -289,6 +419,7 @@ async function verifyCode(email, code) {
289
419
  return currentPlan(true);
290
420
  }
291
421
  async function signOut() {
422
+ checked = null;
292
423
  await writeSession(null);
293
424
  }
294
425
  async function checkoutUrl() {
@@ -307,6 +438,12 @@ async function billingPortalUrl() {
307
438
  const j = await r.json().catch(() => ({}));
308
439
  return j.url ?? ACCOUNT_URL;
309
440
  }
441
+ async function librariesGrandfathered() {
442
+ const s = await freshSession();
443
+ if (!s) return false;
444
+ const r = await sb("/rest/v1/rpc/libraries_grandfathered", { method: "POST", body: "{}", token: s.access_token });
445
+ return r.ok && await r.json().catch(() => false) === true;
446
+ }
310
447
  async function brandLibraries() {
311
448
  const s = await freshSession();
312
449
  if (!s) throw new Error("Sign in first (sign_in), with the email you use on one.design.");
@@ -320,7 +457,7 @@ function openInBrowser(url) {
320
457
  }
321
458
  function proPitch(what) {
322
459
  return [
323
- `${what} is part of One Design Pro.`,
460
+ `${what} ${/^your |s$/i.test(what) ? "are" : "is"} part of One Design Pro.`,
324
461
  `Pro opens every tool (here and on one.design), batches of up to ${LIMITS.pro.batch} files instead of ${LIMITS.free.batch}, ${LIMITS.pro.qrCodes} QR codes at a time, and Brand Libraries.`,
325
462
  `Lifetime is \xA3${PRICES.lifetime} once (was \xA3${PRICES.lifetimeWas}), launch price, no subscription.`,
326
463
  'Say "buy Pro" and I\u2019ll open checkout (buy_pro). Already bought it? Sign in with that email (sign_in) and it unlocks here.'
@@ -584,7 +721,9 @@ function register(t2) {
584
721
  });
585
722
  t2.tool({
586
723
  name: "brand_library",
587
- siteId: "brand-library",
724
+ // Not gated by the usual Pro check: accounts that had libraries before they became Pro-only keep
725
+ // them (checked below, and enforced by the database)
726
+ siteId: "",
588
727
  title: "Your Brand Libraries",
589
728
  description: "Reads the Brand Libraries synced to the signed-in one.design account: colours (name and HEX), fonts, logos. Use the colours in other tools (QR codes, watermarks, store shots, bento). save_logos writes the logos to a folder so other tools can use them.",
590
729
  openWorld: true,
@@ -593,7 +732,8 @@ function register(t2) {
593
732
  save_logos: z.boolean().default(false).describe("Save the logos (and fonts) as files."),
594
733
  output_dir: z.string().optional().describe("Where to save them. Default: ~/Downloads/one-design.")
595
734
  },
596
- async run(a) {
735
+ async run(a, ctx) {
736
+ if (ctx.plan !== "pro" && !await librariesGrandfathered()) return text(proPitch("Your Brand Libraries"), true);
597
737
  let libs = await brandLibraries();
598
738
  if (a.library) libs = libs.filter((l) => l.name.toLowerCase() === a.library.toLowerCase());
599
739
  if (!libs.length) throw new ToolError(a.library ? `No Brand Library called ${a.library}.` : "This account has no Brand Libraries yet. Make one at https://one.design/brand.");
@@ -4989,9 +5129,9 @@ async function postage(img, o) {
4989
5129
  const inset = bordered ? margin : Math.round(hole * 2.2);
4990
5130
  const value = o.value.trim();
4991
5131
  if (value) {
4992
- const fs9 = Math.round(unit * 0.09);
4993
- const tw = await measure(value, fs9, 400, "serif");
4994
- const pad = fs9 * 0.35;
5132
+ const fs10 = Math.round(unit * 0.09);
5133
+ const tw = await measure(value, fs10, 400, "serif");
5134
+ const pad = fs10 * 0.35;
4995
5135
  const bx = Math.max(0, Math.round(margin + iw - tw - pad * 2 - (bordered ? 0 : inset)));
4996
5136
  const by = Math.max(0, Math.round(inset));
4997
5137
  const flat = await sharp(pic).flatten({ background: o.paper }).raw().toBuffer({ resolveWithObject: true });
@@ -5000,7 +5140,7 @@ async function postage(img, o) {
5000
5140
  const x0 = Math.max(0, bx - margin);
5001
5141
  const y0 = Math.max(0, by - margin);
5002
5142
  const x1 = Math.min(iw, bx - margin + Math.round(tw + pad * 2));
5003
- const y1 = Math.min(ih, by - margin + Math.round(fs9 + pad * 1.2));
5143
+ const y1 = Math.min(ih, by - margin + Math.round(fs10 + pad * 1.2));
5004
5144
  for (let y = y0; y < y1; y++)
5005
5145
  for (let x = x0; x < x1; x++) {
5006
5146
  const i = (y * iw + x) * flat.info.channels;
@@ -5008,18 +5148,18 @@ async function postage(img, o) {
5008
5148
  cnt++;
5009
5149
  }
5010
5150
  const under = cnt ? sum / cnt : 255;
5011
- body.push(text2(margin + iw - tw - pad - (bordered ? 0 : inset), fromTop(inset + pad * 0.6, fs9), value, { size: fs9, font: "serif", fill: under < 110 ? o.paper : inkOnPaper }));
5151
+ body.push(text2(margin + iw - tw - pad - (bordered ? 0 : inset), fromTop(inset + pad * 0.6, fs10), value, { size: fs10, font: "serif", fill: under < 110 ? o.paper : inkOnPaper }));
5012
5152
  }
5013
5153
  const caption = o.caption.trim();
5014
5154
  if (caption) {
5015
5155
  if (band) {
5016
- const fs9 = await fitSize(caption, W - margin * 2, Math.round(band * 0.6), 400, "serif");
5017
- body.push(text2(W / 2, fromMiddle(margin + ih + band / 2 + margin * 0.3, fs9), caption, { size: fs9, font: "serif", fill: inkOnPaper, anchor: "middle" }));
5156
+ const fs10 = await fitSize(caption, W - margin * 2, Math.round(band * 0.6), 400, "serif");
5157
+ body.push(text2(W / 2, fromMiddle(margin + ih + band / 2 + margin * 0.3, fs10), caption, { size: fs10, font: "serif", fill: inkOnPaper, anchor: "middle" }));
5018
5158
  } else {
5019
5159
  const max = Math.round(unit * 0.085);
5020
- const fs9 = await fitSize(caption, W - inset * 2, max, 400, "serif");
5160
+ const fs10 = await fitSize(caption, W - inset * 2, max, 400, "serif");
5021
5161
  defs.push(`<filter id="capshadow" x="-20%" y="-50%" width="140%" height="200%"><feDropShadow dx="0" dy="0" stdDeviation="${n(max * 0.2)}" flood-color="#000000" flood-opacity="0.6"/></filter>`);
5022
- body.push(text2(W / 2, fromMiddle(H - inset - max * 0.55, fs9), caption, { size: fs9, font: "serif", fill: o.paper, anchor: "middle", extra: 'filter="url(#capshadow)"' }));
5162
+ body.push(text2(W / 2, fromMiddle(H - inset - max * 0.55, fs10), caption, { size: fs10, font: "serif", fill: o.paper, anchor: "middle", extra: 'filter="url(#capshadow)"' }));
5023
5163
  }
5024
5164
  }
5025
5165
  const holes = [];
@@ -5056,8 +5196,8 @@ async function rubber(img, o, seed) {
5056
5196
  image(gap + line2 * 3, gap + line2 * 3, iw, ih, dataUri2(inked, "image/png"))
5057
5197
  ];
5058
5198
  if (band) {
5059
- const fs9 = await fitSize(caption, W - line2 * 10, Math.round(band * 0.5), 700);
5060
- body.push(text2(W / 2, fromMiddle(H - line2 * 3 - band / 2 - gap * 0.3, fs9), caption, { size: fs9, weight: 700, fill: o.ink, anchor: "middle" }));
5199
+ const fs10 = await fitSize(caption, W - line2 * 10, Math.round(band * 0.5), 700);
5200
+ body.push(text2(W / 2, fromMiddle(H - line2 * 3 - band / 2 - gap * 0.3, fs10), caption, { size: fs10, weight: 700, fill: o.ink, anchor: "middle" }));
5061
5201
  }
5062
5202
  const png = await rasterise(svgDoc(W, H, body.join(""))).png().toBuffer();
5063
5203
  return wearOut(png, distress(W, H, o.wear / 100, seed));
@@ -5078,10 +5218,10 @@ async function seal(img, o, seed) {
5078
5218
  ];
5079
5219
  const ringText = o.caption.trim().toUpperCase();
5080
5220
  const ringR = (c - line2 + inner) / 2;
5081
- const fs9 = Math.round((c - line2 - inner) * 0.52);
5221
+ const fs10 = Math.round((c - line2 - inner) * 0.52);
5082
5222
  const chars = [...ringText];
5083
5223
  const widths2 = [];
5084
- for (const ch of chars) widths2.push(await measure(ch, fs9, 700));
5224
+ for (const ch of chars) widths2.push(await measure(ch, fs10, 700));
5085
5225
  const total = widths2.reduce((a2, b) => a2 + b, 0);
5086
5226
  const spread = chars.length ? Math.min(Math.PI * 1.7, total * 1.18 / ringR) : 0;
5087
5227
  let a = -Math.PI / 2 - spread / 2;
@@ -5090,13 +5230,13 @@ async function seal(img, o, seed) {
5090
5230
  a += step / 2;
5091
5231
  const x = c + Math.cos(a) * ringR;
5092
5232
  const y = c + Math.sin(a) * ringR;
5093
- body.push(`<g transform="translate(${n(x)},${n(y)}) rotate(${n((a + Math.PI / 2) * 180 / Math.PI)})">${text2(0, fromMiddle(0, fs9), ch, { size: fs9, weight: 700, fill: o.ink, anchor: "middle" })}</g>`);
5233
+ body.push(`<g transform="translate(${n(x)},${n(y)}) rotate(${n((a + Math.PI / 2) * 180 / Math.PI)})">${text2(0, fromMiddle(0, fs10), ch, { size: fs10, weight: 700, fill: o.ink, anchor: "middle" })}</g>`);
5094
5234
  a += step / 2;
5095
5235
  });
5096
5236
  const star = (ang) => {
5097
5237
  const x = c + Math.cos(ang) * ringR;
5098
5238
  const y = c + Math.sin(ang) * ringR;
5099
- const R = fs9 * 0.4;
5239
+ const R = fs10 * 0.4;
5100
5240
  const pts = Array.from({ length: 10 }, (_, i) => {
5101
5241
  const rr2 = i % 2 ? R * 0.42 : R;
5102
5242
  const t2 = -Math.PI / 2 + i * Math.PI / 5;
@@ -6040,7 +6180,7 @@ ${notes.join("\n")}` : ""
6040
6180
  const r = { x, y, w: cols.start[tile.col + tile.colSpan - 1] + cols.size[tile.col + tile.colSpan - 1] - x, h: rows.start[tile.row + tile.rowSpan - 1] + rows.size[tile.row + tile.rowSpan - 1] - y };
6041
6181
  if (r.w < 1 || r.h < 1) continue;
6042
6182
  defs.push(`<clipPath id="t${i}"><path d="${rr(r.x, r.y, r.w, r.h, Math.max(0, Math.min(a.radius, r.w / 2, r.h / 2)))}"/></clipPath>`);
6043
- const parts = [`<rect x="${n(r.x)}" y="${n(r.y)}" width="${n(r.w)}" height="${n(r.h)}" fill="${tile.fill}"/>`];
6183
+ const parts = tile.kind === "photo" && tile.file ? [] : [`<rect x="${n(r.x)}" y="${n(r.y)}" width="${n(r.w)}" height="${n(r.h)}" fill="${tile.fill}"/>`];
6044
6184
  if (tile.kind === "photo") {
6045
6185
  if (tile.file) {
6046
6186
  let href;
@@ -6203,7 +6343,12 @@ ${notes.join("\n")}` : "");
6203
6343
  }
6204
6344
 
6205
6345
  // src/server.ts
6206
- var VERSION = "0.2.0";
6346
+ var VERSION = "0.2.2";
6347
+ if (process.argv[2] === "setup") {
6348
+ const { setup: setup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
6349
+ await setup2(process.argv.slice(3));
6350
+ process.exit(0);
6351
+ }
6207
6352
  var server = new McpServer({ name: "one-design", title: "One Design", version: VERSION, websiteUrl: "https://one.design" }, { instructions: INSTRUCTIONS });
6208
6353
  var t = toolkit(server);
6209
6354
  for (const m of [basics_exports, optimise_exports, privacy_docs_exports, brand_exports, account_exports]) m.register(t);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one-design-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Every one.design tool for Claude and other MCP apps: compress, resize, convert, privacy, brand, store screenshots, PDFs and more, all on your own computer.",
5
5
  "type": "module",
6
6
  "bin": {