atelier-mcp 0.2.0 → 0.3.0

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 +15 -5
  2. package/dist/main.mjs +187 -90
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,17 +1,27 @@
1
1
  # atelier-mcp
2
2
 
3
- An AI architect that lives inside your MCP client (Claude Code, Claude Desktop, …) — for **Vietnamese tube houses**: floor plans with proper TCVN symbols, a live two-way browser editor (drag walls, type exact mm), furnished 3D walkthrough, sun study, rough cost estimate, and a numbered A3 PDF / DXF / glTF / IFC documentation set.
3
+ An AI architect that plugs into **any MCP client** Claude Code, OpenAI Codex CLI, Gemini CLI, Claude Desktop, Cursor… — for **Vietnamese tube houses**: floor plans with proper TCVN symbols, a live two-way browser editor (drag walls, type exact mm), furnished 3D walkthrough, sun study, rough cost estimate, and a numbered A3 PDF / DXF / glTF / IFC documentation set.
4
4
 
5
5
  Bản gốc tiếng Việt + toàn bộ tài liệu thiết kế: [github.com/lebac-svg/Atelier](https://github.com/lebac-svg/Atelier)
6
6
 
7
7
  ## Install
8
8
 
9
+ Atelier is a standard MCP server, not tied to Claude. Stand in your house folder (one folder = one house — the model lives there) and run ONE command — it detects every MCP client on your machine (Claude Code, Codex CLI, Gemini CLI, Claude Desktop, Cursor) and registers with each:
10
+
11
+ ```bash
12
+ cd my-house
13
+ npx -y atelier-mcp setup
14
+ ```
15
+
16
+ Or register manually with your client of choice:
17
+
9
18
  ```bash
10
- cd my-house # one folder = one house (the model lives here)
11
- claude mcp add atelier -- npx -y atelier-mcp
19
+ claude mcp add atelier -- npx -y atelier-mcp # Claude Code
20
+ codex mcp add atelier -- npx -y atelier-mcp # OpenAI Codex CLI
21
+ gemini mcp add atelier npx -y atelier-mcp # Gemini CLI
12
22
  ```
13
23
 
14
- That's it. Open Claude Code in that folder and say *"design me a 4×16m tube house, 2 floors, 3 bedrooms"*.
24
+ That's it. Open your agent in that folder and say *"design me a 4×16m tube house, 2 floors, 3 bedrooms"*. Works best with a model that reads Vietnamese well (tool descriptions are Vietnamese-first) and a client that shows images from tool results — the agent inspects its own drawings.
15
25
 
16
26
  Optional, for PNG/PDF export (first time only):
17
27
 
@@ -19,7 +29,7 @@ Optional, for PNG/PDF export (first time only):
19
29
  npx playwright install chromium
20
30
  ```
21
31
 
22
- Claude Desktop — add to `claude_desktop_config.json`:
32
+ GUI clients (Claude Desktop, Cursor, …) have no working folder set `ATELIER_DIR`, e.g. in `claude_desktop_config.json`:
23
33
 
24
34
  ```json
25
35
  {
package/dist/main.mjs CHANGED
@@ -6960,8 +6960,8 @@ var require_utils = __commonJS({
6960
6960
  }
6961
6961
  return ind;
6962
6962
  }
6963
- function removeDotSegments(path7) {
6964
- let input = path7;
6963
+ function removeDotSegments(path8) {
6964
+ let input = path8;
6965
6965
  const output = [];
6966
6966
  let nextSlash = -1;
6967
6967
  let len2 = 0;
@@ -7213,8 +7213,8 @@ var require_schemes = __commonJS({
7213
7213
  wsComponent.secure = void 0;
7214
7214
  }
7215
7215
  if (wsComponent.resourceName) {
7216
- const [path7, query] = wsComponent.resourceName.split("?");
7217
- wsComponent.path = path7 && path7 !== "/" ? path7 : void 0;
7216
+ const [path8, query] = wsComponent.resourceName.split("?");
7217
+ wsComponent.path = path8 && path8 !== "/" ? path8 : void 0;
7218
7218
  wsComponent.query = query;
7219
7219
  wsComponent.resourceName = void 0;
7220
7220
  }
@@ -10659,9 +10659,101 @@ var init_pdf = __esm({
10659
10659
  }
10660
10660
  });
10661
10661
 
10662
- // src/main.ts
10663
- import { mkdirSync as mkdirSync5 } from "node:fs";
10662
+ // src/setup.ts
10663
+ var setup_exports = {};
10664
+ __export(setup_exports, {
10665
+ cursorConfigPath: () => cursorConfigPath,
10666
+ desktopConfigPath: () => desktopConfigPath,
10667
+ runSetup: () => runSetup,
10668
+ upsertMcpJson: () => upsertMcpJson
10669
+ });
10670
+ import { spawnSync } from "node:child_process";
10671
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
10664
10672
  import path6 from "node:path";
10673
+ function upsertMcpJson(existingText, houseDir) {
10674
+ let cfg = {};
10675
+ if (existingText != null && existingText.trim() !== "") {
10676
+ cfg = JSON.parse(existingText);
10677
+ }
10678
+ const servers = cfg.mcpServers ?? {};
10679
+ servers.atelier = {
10680
+ command: SERVER_ARGS[0],
10681
+ args: SERVER_ARGS.slice(1),
10682
+ env: { ATELIER_DIR: houseDir }
10683
+ };
10684
+ cfg.mcpServers = servers;
10685
+ return JSON.stringify(cfg, null, 2) + "\n";
10686
+ }
10687
+ function desktopConfigPath(platform, env) {
10688
+ const dir = platform === "win32" ? env.APPDATA && path6.join(env.APPDATA, "Claude") : platform === "darwin" ? env.HOME && path6.join(env.HOME, "Library", "Application Support", "Claude") : env.HOME && path6.join(env.HOME, ".config", "Claude");
10689
+ if (!dir || !existsSync4(dir)) return null;
10690
+ return path6.join(dir, "claude_desktop_config.json");
10691
+ }
10692
+ function cursorConfigPath(env) {
10693
+ const home = env.HOME ?? env.USERPROFILE;
10694
+ if (!home) return null;
10695
+ const dir = path6.join(home, ".cursor");
10696
+ return existsSync4(dir) ? path6.join(dir, "mcp.json") : null;
10697
+ }
10698
+ function tryCli(bin, args) {
10699
+ const probe = spawnSync(bin, ["--version"], { shell: true, stdio: "ignore", timeout: 2e4 });
10700
+ if (probe.status !== 0) return { found: false, ok: false, out: "" };
10701
+ const r = spawnSync(bin, args, { shell: true, encoding: "utf8", timeout: 6e4 });
10702
+ const out = `${r.stdout ?? ""}${r.stderr ?? ""}`.trim().split("\n").at(-1) ?? "";
10703
+ return { found: true, ok: r.status === 0, out };
10704
+ }
10705
+ function runSetup(houseDir, io = { log: (s) => console.error(s) }) {
10706
+ const results = [];
10707
+ for (const c of CLI_CLIENTS) {
10708
+ const r = tryCli(c.bin, c.addArgs);
10709
+ if (!r.found) {
10710
+ results.push({ client: c.name, status: "not found", detail: `'${c.bin}' is not on PATH` });
10711
+ } else if (r.ok) {
10712
+ results.push({ client: c.name, status: "registered", detail: r.out || `${c.bin} ${c.addArgs.join(" ")}` });
10713
+ } else {
10714
+ results.push({ client: c.name, status: "error", detail: r.out || "command failed" });
10715
+ }
10716
+ }
10717
+ const jsonTargets = [
10718
+ { name: "Claude Desktop", file: desktopConfigPath(process.platform, process.env) },
10719
+ { name: "Cursor", file: cursorConfigPath(process.env) }
10720
+ ];
10721
+ for (const t of jsonTargets) {
10722
+ if (!t.file) {
10723
+ results.push({ client: t.name, status: "not found", detail: "app config folder not present" });
10724
+ continue;
10725
+ }
10726
+ try {
10727
+ const before = existsSync4(t.file) ? readFileSync6(t.file, "utf8") : null;
10728
+ mkdirSync5(path6.dirname(t.file), { recursive: true });
10729
+ writeFileSync5(t.file, upsertMcpJson(before, houseDir), "utf8");
10730
+ results.push({ client: t.name, status: "registered", detail: `${t.file} (ATELIER_DIR=${houseDir}) \u2014 restart the app` });
10731
+ } catch (e) {
10732
+ results.push({ client: t.name, status: "error", detail: `${t.file}: ${e instanceof Error ? e.message : String(e)} \u2014 edit manually per README` });
10733
+ }
10734
+ }
10735
+ const icon = { registered: "\u2714", "not found": "\xB7", error: "\u2718" };
10736
+ io.log(`atelier-mcp setup \u2014 house folder: ${houseDir}`);
10737
+ for (const r of results) io.log(`${icon[r.status]} ${r.client}: ${r.status}${r.detail ? ` \u2014 ${r.detail}` : ""}`);
10738
+ const n = results.filter((r) => r.status === "registered").length;
10739
+ io.log(n > 0 ? `Done: registered with ${n} client(s). Open your agent here and say "design me a tube house".` : `No MCP client found. Install one (Claude Code, Codex CLI, Gemini CLI, Claude Desktop, Cursor) or register manually \u2014 see https://github.com/lebac-svg/Atelier#install-30-seconds`);
10740
+ return results;
10741
+ }
10742
+ var SERVER_ARGS, CLI_CLIENTS;
10743
+ var init_setup = __esm({
10744
+ "src/setup.ts"() {
10745
+ SERVER_ARGS = ["npx", "-y", "atelier-mcp"];
10746
+ CLI_CLIENTS = [
10747
+ { name: "Claude Code", bin: "claude", addArgs: ["mcp", "add", "atelier", "--", ...SERVER_ARGS] },
10748
+ { name: "Codex CLI", bin: "codex", addArgs: ["mcp", "add", "atelier", "--", ...SERVER_ARGS] },
10749
+ { name: "Gemini CLI", bin: "gemini", addArgs: ["mcp", "add", "atelier", ...SERVER_ARGS] }
10750
+ ];
10751
+ }
10752
+ });
10753
+
10754
+ // src/main.ts
10755
+ import { mkdirSync as mkdirSync6 } from "node:fs";
10756
+ import path7 from "node:path";
10665
10757
  import { fileURLToPath as fileURLToPath3 } from "node:url";
10666
10758
 
10667
10759
  // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
@@ -10863,10 +10955,10 @@ function assignProp(target, prop, value) {
10863
10955
  configurable: true
10864
10956
  });
10865
10957
  }
10866
- function getElementAtPath(obj, path7) {
10867
- if (!path7)
10958
+ function getElementAtPath(obj, path8) {
10959
+ if (!path8)
10868
10960
  return obj;
10869
- return path7.reduce((acc, key) => acc?.[key], obj);
10961
+ return path8.reduce((acc, key) => acc?.[key], obj);
10870
10962
  }
10871
10963
  function promiseAllObject(promisesObj) {
10872
10964
  const keys = Object.keys(promisesObj);
@@ -11186,11 +11278,11 @@ function aborted(x, startIndex = 0) {
11186
11278
  }
11187
11279
  return false;
11188
11280
  }
11189
- function prefixIssues(path7, issues) {
11281
+ function prefixIssues(path8, issues) {
11190
11282
  return issues.map((iss) => {
11191
11283
  var _a;
11192
11284
  (_a = iss).path ?? (_a.path = []);
11193
- iss.path.unshift(path7);
11285
+ iss.path.unshift(path8);
11194
11286
  return iss;
11195
11287
  });
11196
11288
  }
@@ -23021,26 +23113,26 @@ var handleParsingNestedValues = (form, key, value) => {
23021
23113
  };
23022
23114
 
23023
23115
  // ../../node_modules/.pnpm/hono@4.12.29/node_modules/hono/dist/utils/url.js
23024
- var splitPath = (path7) => {
23025
- const paths = path7.split("/");
23116
+ var splitPath = (path8) => {
23117
+ const paths = path8.split("/");
23026
23118
  if (paths[0] === "") {
23027
23119
  paths.shift();
23028
23120
  }
23029
23121
  return paths;
23030
23122
  };
23031
23123
  var splitRoutingPath = (routePath) => {
23032
- const { groups, path: path7 } = extractGroupsFromPath(routePath);
23033
- const paths = splitPath(path7);
23124
+ const { groups, path: path8 } = extractGroupsFromPath(routePath);
23125
+ const paths = splitPath(path8);
23034
23126
  return replaceGroupMarks(paths, groups);
23035
23127
  };
23036
- var extractGroupsFromPath = (path7) => {
23128
+ var extractGroupsFromPath = (path8) => {
23037
23129
  const groups = [];
23038
- path7 = path7.replace(/\{[^}]+\}/g, (match2, index) => {
23130
+ path8 = path8.replace(/\{[^}]+\}/g, (match2, index) => {
23039
23131
  const mark = `@${index}`;
23040
23132
  groups.push([mark, match2]);
23041
23133
  return mark;
23042
23134
  });
23043
- return { groups, path: path7 };
23135
+ return { groups, path: path8 };
23044
23136
  };
23045
23137
  var replaceGroupMarks = (paths, groups) => {
23046
23138
  for (let i = groups.length - 1; i >= 0; i--) {
@@ -23097,8 +23189,8 @@ var getPath = (request) => {
23097
23189
  const queryIndex = url.indexOf("?", i);
23098
23190
  const hashIndex = url.indexOf("#", i);
23099
23191
  const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
23100
- const path7 = url.slice(start, end);
23101
- return tryDecodeURI(path7.includes("%25") ? path7.replace(/%25/g, "%2525") : path7);
23192
+ const path8 = url.slice(start, end);
23193
+ return tryDecodeURI(path8.includes("%25") ? path8.replace(/%25/g, "%2525") : path8);
23102
23194
  } else if (charCode === 63 || charCode === 35) {
23103
23195
  break;
23104
23196
  }
@@ -23115,11 +23207,11 @@ var mergePath = (base, sub2, ...rest) => {
23115
23207
  }
23116
23208
  return `${base?.[0] === "/" ? "" : "/"}${base}${sub2 === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub2?.[0] === "/" ? sub2.slice(1) : sub2}`}`;
23117
23209
  };
23118
- var checkOptionalParameter = (path7) => {
23119
- if (path7.charCodeAt(path7.length - 1) !== 63 || !path7.includes(":")) {
23210
+ var checkOptionalParameter = (path8) => {
23211
+ if (path8.charCodeAt(path8.length - 1) !== 63 || !path8.includes(":")) {
23120
23212
  return null;
23121
23213
  }
23122
- const segments = path7.split("/");
23214
+ const segments = path8.split("/");
23123
23215
  const results = [];
23124
23216
  let basePath = "";
23125
23217
  segments.forEach((segment) => {
@@ -23260,9 +23352,9 @@ var HonoRequest = class {
23260
23352
  */
23261
23353
  path;
23262
23354
  bodyCache = {};
23263
- constructor(request, path7 = "/", matchResult = [[]]) {
23355
+ constructor(request, path8 = "/", matchResult = [[]]) {
23264
23356
  this.raw = request;
23265
- this.path = path7;
23357
+ this.path = path8;
23266
23358
  this.#matchResult = matchResult;
23267
23359
  this.#validatedData = {};
23268
23360
  }
@@ -24014,8 +24106,8 @@ var Hono = class _Hono {
24014
24106
  return this;
24015
24107
  };
24016
24108
  });
24017
- this.on = (method, path7, ...handlers) => {
24018
- for (const p of [path7].flat()) {
24109
+ this.on = (method, path8, ...handlers) => {
24110
+ for (const p of [path8].flat()) {
24019
24111
  this.#path = p;
24020
24112
  for (const m of [method].flat()) {
24021
24113
  handlers.map((handler) => {
@@ -24072,8 +24164,8 @@ var Hono = class _Hono {
24072
24164
  * app.route("/api", app2) // GET /api/user
24073
24165
  * ```
24074
24166
  */
24075
- route(path7, app) {
24076
- const subApp = this.basePath(path7);
24167
+ route(path8, app) {
24168
+ const subApp = this.basePath(path8);
24077
24169
  app.routes.map((r) => {
24078
24170
  let handler;
24079
24171
  if (app.errorHandler === errorHandler) {
@@ -24099,9 +24191,9 @@ var Hono = class _Hono {
24099
24191
  * const api = new Hono().basePath('/api')
24100
24192
  * ```
24101
24193
  */
24102
- basePath(path7) {
24194
+ basePath(path8) {
24103
24195
  const subApp = this.#clone();
24104
- subApp._basePath = mergePath(this._basePath, path7);
24196
+ subApp._basePath = mergePath(this._basePath, path8);
24105
24197
  return subApp;
24106
24198
  }
24107
24199
  /**
@@ -24175,7 +24267,7 @@ var Hono = class _Hono {
24175
24267
  * })
24176
24268
  * ```
24177
24269
  */
24178
- mount(path7, applicationHandler, options) {
24270
+ mount(path8, applicationHandler, options) {
24179
24271
  let replaceRequest;
24180
24272
  let optionHandler;
24181
24273
  if (options) {
@@ -24202,7 +24294,7 @@ var Hono = class _Hono {
24202
24294
  return [c.env, executionContext];
24203
24295
  };
24204
24296
  replaceRequest ||= (() => {
24205
- const mergedPath = mergePath(this._basePath, path7);
24297
+ const mergedPath = mergePath(this._basePath, path8);
24206
24298
  const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
24207
24299
  return (request) => {
24208
24300
  const url = new URL(request.url);
@@ -24217,19 +24309,19 @@ var Hono = class _Hono {
24217
24309
  }
24218
24310
  await next();
24219
24311
  };
24220
- this.#addRoute(METHOD_NAME_ALL, mergePath(path7, "*"), handler);
24312
+ this.#addRoute(METHOD_NAME_ALL, mergePath(path8, "*"), handler);
24221
24313
  return this;
24222
24314
  }
24223
- #addRoute(method, path7, handler, baseRoutePath) {
24315
+ #addRoute(method, path8, handler, baseRoutePath) {
24224
24316
  method = method.toUpperCase();
24225
- path7 = mergePath(this._basePath, path7);
24317
+ path8 = mergePath(this._basePath, path8);
24226
24318
  const r = {
24227
24319
  basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath,
24228
- path: path7,
24320
+ path: path8,
24229
24321
  method,
24230
24322
  handler
24231
24323
  };
24232
- this.router.add(method, path7, [handler, r]);
24324
+ this.router.add(method, path8, [handler, r]);
24233
24325
  this.routes.push(r);
24234
24326
  }
24235
24327
  #handleError(err, c) {
@@ -24242,10 +24334,10 @@ var Hono = class _Hono {
24242
24334
  if (method === "HEAD") {
24243
24335
  return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
24244
24336
  }
24245
- const path7 = this.getPath(request, { env });
24246
- const matchResult = this.router.match(method, path7);
24337
+ const path8 = this.getPath(request, { env });
24338
+ const matchResult = this.router.match(method, path8);
24247
24339
  const c = new Context(request, {
24248
- path: path7,
24340
+ path: path8,
24249
24341
  matchResult,
24250
24342
  env,
24251
24343
  executionCtx,
@@ -24345,7 +24437,7 @@ var Hono = class _Hono {
24345
24437
 
24346
24438
  // ../../node_modules/.pnpm/hono@4.12.29/node_modules/hono/dist/router/reg-exp-router/matcher.js
24347
24439
  var emptyParam = [];
24348
- function match(method, path7) {
24440
+ function match(method, path8) {
24349
24441
  const matchers = this.buildAllMatchers();
24350
24442
  const match2 = ((method2, path22) => {
24351
24443
  const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
@@ -24361,7 +24453,7 @@ function match(method, path7) {
24361
24453
  return [matcher[1][index], match3];
24362
24454
  });
24363
24455
  this.match = match2;
24364
- return match2(method, path7);
24456
+ return match2(method, path8);
24365
24457
  }
24366
24458
 
24367
24459
  // ../../node_modules/.pnpm/hono@4.12.29/node_modules/hono/dist/router/reg-exp-router/node.js
@@ -24476,12 +24568,12 @@ var Node = class _Node {
24476
24568
  var Trie = class {
24477
24569
  #context = { varIndex: 0 };
24478
24570
  #root = new Node();
24479
- insert(path7, index, pathErrorCheckOnly) {
24571
+ insert(path8, index, pathErrorCheckOnly) {
24480
24572
  const paramAssoc = [];
24481
24573
  const groups = [];
24482
24574
  for (let i = 0; ; ) {
24483
24575
  let replaced = false;
24484
- path7 = path7.replace(/\{[^}]+\}/g, (m) => {
24576
+ path8 = path8.replace(/\{[^}]+\}/g, (m) => {
24485
24577
  const mark = `@\\${i}`;
24486
24578
  groups[i] = [mark, m];
24487
24579
  i++;
@@ -24492,7 +24584,7 @@ var Trie = class {
24492
24584
  break;
24493
24585
  }
24494
24586
  }
24495
- const tokens = path7.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
24587
+ const tokens = path8.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
24496
24588
  for (let i = groups.length - 1; i >= 0; i--) {
24497
24589
  const [mark] = groups[i];
24498
24590
  for (let j = tokens.length - 1; j >= 0; j--) {
@@ -24531,9 +24623,9 @@ var Trie = class {
24531
24623
  // ../../node_modules/.pnpm/hono@4.12.29/node_modules/hono/dist/router/reg-exp-router/router.js
24532
24624
  var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
24533
24625
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
24534
- function buildWildcardRegExp(path7) {
24535
- return wildcardRegExpCache[path7] ??= new RegExp(
24536
- path7 === "*" ? "" : `^${path7.replace(
24626
+ function buildWildcardRegExp(path8) {
24627
+ return wildcardRegExpCache[path8] ??= new RegExp(
24628
+ path8 === "*" ? "" : `^${path8.replace(
24537
24629
  /\/\*$|([.\\+*[^\]$()])/g,
24538
24630
  (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
24539
24631
  )}$`
@@ -24555,17 +24647,17 @@ function buildMatcherFromPreprocessedRoutes(routes) {
24555
24647
  );
24556
24648
  const staticMap = /* @__PURE__ */ Object.create(null);
24557
24649
  for (let i = 0, j = -1, len2 = routesWithStaticPathFlag.length; i < len2; i++) {
24558
- const [pathErrorCheckOnly, path7, handlers] = routesWithStaticPathFlag[i];
24650
+ const [pathErrorCheckOnly, path8, handlers] = routesWithStaticPathFlag[i];
24559
24651
  if (pathErrorCheckOnly) {
24560
- staticMap[path7] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
24652
+ staticMap[path8] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
24561
24653
  } else {
24562
24654
  j++;
24563
24655
  }
24564
24656
  let paramAssoc;
24565
24657
  try {
24566
- paramAssoc = trie.insert(path7, j, pathErrorCheckOnly);
24658
+ paramAssoc = trie.insert(path8, j, pathErrorCheckOnly);
24567
24659
  } catch (e) {
24568
- throw e === PATH_ERROR ? new UnsupportedPathError(path7) : e;
24660
+ throw e === PATH_ERROR ? new UnsupportedPathError(path8) : e;
24569
24661
  }
24570
24662
  if (pathErrorCheckOnly) {
24571
24663
  continue;
@@ -24599,12 +24691,12 @@ function buildMatcherFromPreprocessedRoutes(routes) {
24599
24691
  }
24600
24692
  return [regexp, handlerMap, staticMap];
24601
24693
  }
24602
- function findMiddleware(middleware, path7) {
24694
+ function findMiddleware(middleware, path8) {
24603
24695
  if (!middleware) {
24604
24696
  return void 0;
24605
24697
  }
24606
24698
  for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
24607
- if (buildWildcardRegExp(k).test(path7)) {
24699
+ if (buildWildcardRegExp(k).test(path8)) {
24608
24700
  return [...middleware[k]];
24609
24701
  }
24610
24702
  }
@@ -24618,7 +24710,7 @@ var RegExpRouter = class {
24618
24710
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
24619
24711
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
24620
24712
  }
24621
- add(method, path7, handler) {
24713
+ add(method, path8, handler) {
24622
24714
  const middleware = this.#middleware;
24623
24715
  const routes = this.#routes;
24624
24716
  if (!middleware || !routes) {
@@ -24633,18 +24725,18 @@ var RegExpRouter = class {
24633
24725
  });
24634
24726
  });
24635
24727
  }
24636
- if (path7 === "/*") {
24637
- path7 = "*";
24728
+ if (path8 === "/*") {
24729
+ path8 = "*";
24638
24730
  }
24639
- const paramCount = (path7.match(/\/:/g) || []).length;
24640
- if (/\*$/.test(path7)) {
24641
- const re = buildWildcardRegExp(path7);
24731
+ const paramCount = (path8.match(/\/:/g) || []).length;
24732
+ if (/\*$/.test(path8)) {
24733
+ const re = buildWildcardRegExp(path8);
24642
24734
  if (method === METHOD_NAME_ALL) {
24643
24735
  Object.keys(middleware).forEach((m) => {
24644
- middleware[m][path7] ||= findMiddleware(middleware[m], path7) || findMiddleware(middleware[METHOD_NAME_ALL], path7) || [];
24736
+ middleware[m][path8] ||= findMiddleware(middleware[m], path8) || findMiddleware(middleware[METHOD_NAME_ALL], path8) || [];
24645
24737
  });
24646
24738
  } else {
24647
- middleware[method][path7] ||= findMiddleware(middleware[method], path7) || findMiddleware(middleware[METHOD_NAME_ALL], path7) || [];
24739
+ middleware[method][path8] ||= findMiddleware(middleware[method], path8) || findMiddleware(middleware[METHOD_NAME_ALL], path8) || [];
24648
24740
  }
24649
24741
  Object.keys(middleware).forEach((m) => {
24650
24742
  if (method === METHOD_NAME_ALL || method === m) {
@@ -24662,7 +24754,7 @@ var RegExpRouter = class {
24662
24754
  });
24663
24755
  return;
24664
24756
  }
24665
- const paths = checkOptionalParameter(path7) || [path7];
24757
+ const paths = checkOptionalParameter(path8) || [path8];
24666
24758
  for (let i = 0, len2 = paths.length; i < len2; i++) {
24667
24759
  const path22 = paths[i];
24668
24760
  Object.keys(routes).forEach((m) => {
@@ -24689,13 +24781,13 @@ var RegExpRouter = class {
24689
24781
  const routes = [];
24690
24782
  let hasOwnRoute = method === METHOD_NAME_ALL;
24691
24783
  [this.#middleware, this.#routes].forEach((r) => {
24692
- const ownRoute = r[method] ? Object.keys(r[method]).map((path7) => [path7, r[method][path7]]) : [];
24784
+ const ownRoute = r[method] ? Object.keys(r[method]).map((path8) => [path8, r[method][path8]]) : [];
24693
24785
  if (ownRoute.length !== 0) {
24694
24786
  hasOwnRoute ||= true;
24695
24787
  routes.push(...ownRoute);
24696
24788
  } else if (method !== METHOD_NAME_ALL) {
24697
24789
  routes.push(
24698
- ...Object.keys(r[METHOD_NAME_ALL]).map((path7) => [path7, r[METHOD_NAME_ALL][path7]])
24790
+ ...Object.keys(r[METHOD_NAME_ALL]).map((path8) => [path8, r[METHOD_NAME_ALL][path8]])
24699
24791
  );
24700
24792
  }
24701
24793
  });
@@ -24715,13 +24807,13 @@ var SmartRouter = class {
24715
24807
  constructor(init) {
24716
24808
  this.#routers = init.routers;
24717
24809
  }
24718
- add(method, path7, handler) {
24810
+ add(method, path8, handler) {
24719
24811
  if (!this.#routes) {
24720
24812
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
24721
24813
  }
24722
- this.#routes.push([method, path7, handler]);
24814
+ this.#routes.push([method, path8, handler]);
24723
24815
  }
24724
- match(method, path7) {
24816
+ match(method, path8) {
24725
24817
  if (!this.#routes) {
24726
24818
  throw new Error("Fatal error");
24727
24819
  }
@@ -24736,7 +24828,7 @@ var SmartRouter = class {
24736
24828
  for (let i2 = 0, len22 = routes.length; i2 < len22; i2++) {
24737
24829
  router.add(...routes[i2]);
24738
24830
  }
24739
- res = router.match(method, path7);
24831
+ res = router.match(method, path8);
24740
24832
  } catch (e) {
24741
24833
  if (e instanceof UnsupportedPathError) {
24742
24834
  continue;
@@ -24786,10 +24878,10 @@ var Node2 = class _Node2 {
24786
24878
  }
24787
24879
  this.#patterns = [];
24788
24880
  }
24789
- insert(method, path7, handler) {
24881
+ insert(method, path8, handler) {
24790
24882
  this.#order = ++this.#order;
24791
24883
  let curNode = this;
24792
- const parts = splitRoutingPath(path7);
24884
+ const parts = splitRoutingPath(path8);
24793
24885
  const possibleKeys = [];
24794
24886
  for (let i = 0, len2 = parts.length; i < len2; i++) {
24795
24887
  const p = parts[i];
@@ -24838,12 +24930,12 @@ var Node2 = class _Node2 {
24838
24930
  }
24839
24931
  }
24840
24932
  }
24841
- search(method, path7) {
24933
+ search(method, path8) {
24842
24934
  const handlerSets = [];
24843
24935
  this.#params = emptyParams;
24844
24936
  const curNode = this;
24845
24937
  let curNodes = [curNode];
24846
- const parts = splitPath(path7);
24938
+ const parts = splitPath(path8);
24847
24939
  const curNodesQueue = [];
24848
24940
  const len2 = parts.length;
24849
24941
  let partOffsets = null;
@@ -24885,13 +24977,13 @@ var Node2 = class _Node2 {
24885
24977
  if (matcher instanceof RegExp) {
24886
24978
  if (partOffsets === null) {
24887
24979
  partOffsets = new Array(len2);
24888
- let offset = path7[0] === "/" ? 1 : 0;
24980
+ let offset = path8[0] === "/" ? 1 : 0;
24889
24981
  for (let p = 0; p < len2; p++) {
24890
24982
  partOffsets[p] = offset;
24891
24983
  offset += parts[p].length + 1;
24892
24984
  }
24893
24985
  }
24894
- const restPathString = path7.substring(partOffsets[i]);
24986
+ const restPathString = path8.substring(partOffsets[i]);
24895
24987
  const m = matcher.exec(restPathString);
24896
24988
  if (m) {
24897
24989
  params[name] = m[0];
@@ -24953,18 +25045,18 @@ var TrieRouter = class {
24953
25045
  constructor() {
24954
25046
  this.#node = new Node2();
24955
25047
  }
24956
- add(method, path7, handler) {
24957
- const results = checkOptionalParameter(path7);
25048
+ add(method, path8, handler) {
25049
+ const results = checkOptionalParameter(path8);
24958
25050
  if (results) {
24959
25051
  for (let i = 0, len2 = results.length; i < len2; i++) {
24960
25052
  this.#node.insert(method, results[i], handler);
24961
25053
  }
24962
25054
  return;
24963
25055
  }
24964
- this.#node.insert(method, path7, handler);
25056
+ this.#node.insert(method, path8, handler);
24965
25057
  }
24966
- match(method, path7) {
24967
- return this.#node.search(method, path7);
25058
+ match(method, path8) {
25059
+ return this.#node.search(method, path8);
24968
25060
  }
24969
25061
  };
24970
25062
 
@@ -26585,8 +26677,8 @@ function getErrorMap() {
26585
26677
 
26586
26678
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
26587
26679
  var makeIssue = (params) => {
26588
- const { data, path: path7, errorMaps, issueData } = params;
26589
- const fullPath = [...path7, ...issueData.path || []];
26680
+ const { data, path: path8, errorMaps, issueData } = params;
26681
+ const fullPath = [...path8, ...issueData.path || []];
26590
26682
  const fullIssue = {
26591
26683
  ...issueData,
26592
26684
  path: fullPath
@@ -26702,11 +26794,11 @@ var errorUtil;
26702
26794
 
26703
26795
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
26704
26796
  var ParseInputLazyPath = class {
26705
- constructor(parent, value, path7, key) {
26797
+ constructor(parent, value, path8, key) {
26706
26798
  this._cachedPath = [];
26707
26799
  this.parent = parent;
26708
26800
  this.data = value;
26709
- this._path = path7;
26801
+ this._path = path8;
26710
26802
  this._key = key;
26711
26803
  }
26712
26804
  get path() {
@@ -35657,10 +35749,15 @@ function queryModel(p, select, computed) {
35657
35749
  }
35658
35750
 
35659
35751
  // src/main.ts
35660
- var pkgRoot = path6.resolve(path6.dirname(fileURLToPath3(import.meta.url)), "..");
35661
- process.env.ATELIER_FONT_DIR ??= path6.join(pkgRoot, "assets", "fonts");
35662
- var baseDir = path6.resolve(process.env.ATELIER_DIR ?? ".");
35663
- mkdirSync5(baseDir, { recursive: true });
35752
+ if (process.argv[2] === "setup") {
35753
+ const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
35754
+ const results = runSetup2(path7.resolve(process.env.ATELIER_DIR ?? "."));
35755
+ process.exit(results.some((r) => r.status === "error") ? 1 : 0);
35756
+ }
35757
+ var pkgRoot = path7.resolve(path7.dirname(fileURLToPath3(import.meta.url)), "..");
35758
+ process.env.ATELIER_FONT_DIR ??= path7.join(pkgRoot, "assets", "fonts");
35759
+ var baseDir = path7.resolve(process.env.ATELIER_DIR ?? ".");
35760
+ mkdirSync6(baseDir, { recursive: true });
35664
35761
  var store = new ProjectStore(baseDir);
35665
35762
  try {
35666
35763
  store.openProject();
@@ -35668,7 +35765,7 @@ try {
35668
35765
  } catch {
35669
35766
  console.error(`[atelier] ch\u01B0a c\xF3 d\u1EF1 \xE1n t\u1EA1i ${baseDir} \u2014 ch\u1EDD project_new/project_open`);
35670
35767
  }
35671
- var live = new LiveServer(store, { webDist: process.env.ATELIER_WEB_DIST ?? path6.join(pkgRoot, "web") });
35768
+ var live = new LiveServer(store, { webDist: process.env.ATELIER_WEB_DIST ?? path7.join(pkgRoot, "web") });
35672
35769
  var server = createAtelierServer(store, live);
35673
35770
  await server.connect(new StdioServerTransport());
35674
35771
  console.error("[atelier] MCP server s\u1EB5n s\xE0ng (stdio)");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "atelier-mcp",
3
- "version": "0.2.0",
4
- "description": "AI architect MCP server for Vietnamese tube houses — TCVN-standard plans, live two-way browser editor, furnished 3D walkthrough, PDF/DXF/IFC export. Run it inside your house-project folder from Claude Code, Claude Desktop, or any MCP client.",
3
+ "version": "0.3.0",
4
+ "description": "AI architect MCP server for Vietnamese tube houses — TCVN-standard plans, live two-way browser editor, furnished 3D walkthrough, PDF/DXF/IFC export. Works with any MCP client: Claude Code, Codex CLI, Gemini CLI, Claude Desktop, Cursor. Run it inside your house-project folder.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "atelier-mcp": "bin/atelier-mcp.mjs"