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.
- package/README.md +15 -5
- package/dist/main.mjs +187 -90
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
# atelier-mcp
|
|
2
2
|
|
|
3
|
-
An AI architect that
|
|
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
|
-
|
|
11
|
-
|
|
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
|
|
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 —
|
|
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(
|
|
6964
|
-
let input =
|
|
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 [
|
|
7217
|
-
wsComponent.path =
|
|
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/
|
|
10663
|
-
|
|
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,
|
|
10867
|
-
if (!
|
|
10958
|
+
function getElementAtPath(obj, path8) {
|
|
10959
|
+
if (!path8)
|
|
10868
10960
|
return obj;
|
|
10869
|
-
return
|
|
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(
|
|
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(
|
|
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 = (
|
|
23025
|
-
const paths =
|
|
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:
|
|
23033
|
-
const paths = splitPath(
|
|
23124
|
+
const { groups, path: path8 } = extractGroupsFromPath(routePath);
|
|
23125
|
+
const paths = splitPath(path8);
|
|
23034
23126
|
return replaceGroupMarks(paths, groups);
|
|
23035
23127
|
};
|
|
23036
|
-
var extractGroupsFromPath = (
|
|
23128
|
+
var extractGroupsFromPath = (path8) => {
|
|
23037
23129
|
const groups = [];
|
|
23038
|
-
|
|
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:
|
|
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
|
|
23101
|
-
return tryDecodeURI(
|
|
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 = (
|
|
23119
|
-
if (
|
|
23210
|
+
var checkOptionalParameter = (path8) => {
|
|
23211
|
+
if (path8.charCodeAt(path8.length - 1) !== 63 || !path8.includes(":")) {
|
|
23120
23212
|
return null;
|
|
23121
23213
|
}
|
|
23122
|
-
const segments =
|
|
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,
|
|
23355
|
+
constructor(request, path8 = "/", matchResult = [[]]) {
|
|
23264
23356
|
this.raw = request;
|
|
23265
|
-
this.path =
|
|
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,
|
|
24018
|
-
for (const p of [
|
|
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(
|
|
24076
|
-
const subApp = this.basePath(
|
|
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(
|
|
24194
|
+
basePath(path8) {
|
|
24103
24195
|
const subApp = this.#clone();
|
|
24104
|
-
subApp._basePath = mergePath(this._basePath,
|
|
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(
|
|
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,
|
|
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(
|
|
24312
|
+
this.#addRoute(METHOD_NAME_ALL, mergePath(path8, "*"), handler);
|
|
24221
24313
|
return this;
|
|
24222
24314
|
}
|
|
24223
|
-
#addRoute(method,
|
|
24315
|
+
#addRoute(method, path8, handler, baseRoutePath) {
|
|
24224
24316
|
method = method.toUpperCase();
|
|
24225
|
-
|
|
24317
|
+
path8 = mergePath(this._basePath, path8);
|
|
24226
24318
|
const r = {
|
|
24227
24319
|
basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath,
|
|
24228
|
-
path:
|
|
24320
|
+
path: path8,
|
|
24229
24321
|
method,
|
|
24230
24322
|
handler
|
|
24231
24323
|
};
|
|
24232
|
-
this.router.add(method,
|
|
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
|
|
24246
|
-
const matchResult = this.router.match(method,
|
|
24337
|
+
const path8 = this.getPath(request, { env });
|
|
24338
|
+
const matchResult = this.router.match(method, path8);
|
|
24247
24339
|
const c = new Context(request, {
|
|
24248
|
-
path:
|
|
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,
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
24535
|
-
return wildcardRegExpCache[
|
|
24536
|
-
|
|
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,
|
|
24650
|
+
const [pathErrorCheckOnly, path8, handlers] = routesWithStaticPathFlag[i];
|
|
24559
24651
|
if (pathErrorCheckOnly) {
|
|
24560
|
-
staticMap[
|
|
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(
|
|
24658
|
+
paramAssoc = trie.insert(path8, j, pathErrorCheckOnly);
|
|
24567
24659
|
} catch (e) {
|
|
24568
|
-
throw e === PATH_ERROR ? new UnsupportedPathError(
|
|
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,
|
|
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(
|
|
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,
|
|
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 (
|
|
24637
|
-
|
|
24728
|
+
if (path8 === "/*") {
|
|
24729
|
+
path8 = "*";
|
|
24638
24730
|
}
|
|
24639
|
-
const paramCount = (
|
|
24640
|
-
if (/\*$/.test(
|
|
24641
|
-
const re = buildWildcardRegExp(
|
|
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][
|
|
24736
|
+
middleware[m][path8] ||= findMiddleware(middleware[m], path8) || findMiddleware(middleware[METHOD_NAME_ALL], path8) || [];
|
|
24645
24737
|
});
|
|
24646
24738
|
} else {
|
|
24647
|
-
middleware[method][
|
|
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(
|
|
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((
|
|
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((
|
|
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,
|
|
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,
|
|
24814
|
+
this.#routes.push([method, path8, handler]);
|
|
24723
24815
|
}
|
|
24724
|
-
match(method,
|
|
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,
|
|
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,
|
|
24881
|
+
insert(method, path8, handler) {
|
|
24790
24882
|
this.#order = ++this.#order;
|
|
24791
24883
|
let curNode = this;
|
|
24792
|
-
const parts = splitRoutingPath(
|
|
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,
|
|
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(
|
|
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 =
|
|
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 =
|
|
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,
|
|
24957
|
-
const results = checkOptionalParameter(
|
|
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,
|
|
25056
|
+
this.#node.insert(method, path8, handler);
|
|
24965
25057
|
}
|
|
24966
|
-
match(method,
|
|
24967
|
-
return this.#node.search(method,
|
|
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:
|
|
26589
|
-
const fullPath = [...
|
|
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,
|
|
26797
|
+
constructor(parent, value, path8, key) {
|
|
26706
26798
|
this._cachedPath = [];
|
|
26707
26799
|
this.parent = parent;
|
|
26708
26800
|
this.data = value;
|
|
26709
|
-
this._path =
|
|
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
|
-
|
|
35661
|
-
|
|
35662
|
-
|
|
35663
|
-
|
|
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 ??
|
|
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.
|
|
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.
|
|
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"
|