@ainecto/mcp 0.3.0 → 0.5.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.
@@ -5,23 +5,55 @@ import { createHash } from "crypto";
5
5
  import { mkdir, readFile, rename, rm, stat, writeFile, chmod } from "fs/promises";
6
6
  import { homedir } from "os";
7
7
  import { dirname, join } from "path";
8
+
9
+ // src/core/harness/role.ts
10
+ var HARNESS_ROLES = ["design", "development", "test", "validation"];
11
+ var ROLE_SCOPE_PREFIX = "ai-erd:role:";
12
+ var ROLE_HEADER = "X-AI-ERD-Role";
13
+ var ROLE_ENV_VAR = "AINECTO_ROLE";
14
+ var LEGACY_ROLE_ENV_VAR = "AI_ERD_ROLE";
15
+ function resolveRole(input = {}) {
16
+ if (input.role !== void 0) {
17
+ return { role: assertRole(input.role), source: "flag" };
18
+ }
19
+ const envVars = input.envVars ?? process.env;
20
+ for (const envVar of [ROLE_ENV_VAR, LEGACY_ROLE_ENV_VAR]) {
21
+ const fromEnv = envVars[envVar];
22
+ if (fromEnv && fromEnv.trim()) {
23
+ return { role: assertRole(fromEnv), source: "env", envVar };
24
+ }
25
+ }
26
+ return { source: "none" };
27
+ }
28
+ function assertRole(value) {
29
+ const normalized = value.trim().toLowerCase();
30
+ if (HARNESS_ROLES.includes(normalized)) {
31
+ return normalized;
32
+ }
33
+ throw new Error(
34
+ `Invalid role "${value}". Expected one of: ${HARNESS_ROLES.join(", ")}.`
35
+ );
36
+ }
37
+
38
+ // src/core/auth/tokenStore.ts
8
39
  var FileTokenStore = class {
9
40
  filePath;
10
41
  constructor(rootDir = join(homedir(), ".ainecto")) {
11
42
  this.filePath = join(rootDir, "tokens.json");
12
43
  }
13
- async load(endpoint, role) {
44
+ async load(endpoint) {
14
45
  const file = await this.readFile();
15
- return file[tokenKey(endpoint, role)];
46
+ return file[tokenKey(endpoint)];
16
47
  }
17
- async save(endpoint, token, role) {
48
+ async save(endpoint, token) {
49
+ assertNoRoleScope(token);
18
50
  const file = await this.readFile();
19
- file[tokenKey(endpoint, role)] = { ...token, endpoint, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
51
+ file[tokenKey(endpoint)] = { ...token, endpoint, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
20
52
  await this.writeFile(file);
21
53
  }
22
- async delete(endpoint, role) {
54
+ async delete(endpoint) {
23
55
  const file = await this.readFile();
24
- delete file[tokenKey(endpoint, role)];
56
+ delete file[tokenKey(endpoint)];
25
57
  await this.writeFile(file);
26
58
  }
27
59
  async readFile() {
@@ -56,9 +88,15 @@ var FileTokenStore = class {
56
88
  await chmodIfPermissive(this.filePath, 384);
57
89
  }
58
90
  };
59
- function tokenKey(endpoint, role) {
60
- const material = role && role.trim() ? `${endpoint}#role=${role.trim().toLowerCase()}` : endpoint;
61
- return createHash("sha256").update(material).digest("hex");
91
+ function tokenKey(endpoint) {
92
+ return createHash("sha256").update(endpoint).digest("hex");
93
+ }
94
+ function assertNoRoleScope(token) {
95
+ if (token.scope?.split(/\s+/).some((scope) => scope.startsWith(ROLE_SCOPE_PREFIX))) {
96
+ throw new Error(
97
+ "Refusing to store a role-scoped access token: sign-in is one per machine and the role comes from the repository. Run `ainecto auth login` again."
98
+ );
99
+ }
62
100
  }
63
101
  function isNodeError(error, code) {
64
102
  return error instanceof Error && "code" in error && error.code === code;
@@ -85,13 +123,20 @@ import { URLSearchParams } from "url";
85
123
 
86
124
  // src/core/config/endpoints.ts
87
125
  var DEFAULT_ENDPOINTS = {
88
- prod: "https://ai-erd.com/mcp",
126
+ prod: "https://ainecto.com/mcp",
89
127
  dev: "https://dev.ai-erd.com/mcp"
90
128
  };
129
+ var LEGACY_PROD_ENDPOINT = "https://ai-erd.com/mcp";
91
130
  var LEGACY_DEFAULT_ENDPOINTS = [
92
- "https://ainecto.com/mcp",
131
+ LEGACY_PROD_ENDPOINT,
93
132
  "https://dev.ainecto.com/mcp"
94
133
  ];
134
+ var TOKEN_SLOT_FALLBACKS = {
135
+ [DEFAULT_ENDPOINTS.prod]: [LEGACY_PROD_ENDPOINT]
136
+ };
137
+ function fallbackTokenEndpointsFor(endpoint) {
138
+ return TOKEN_SLOT_FALLBACKS[normalizeEndpoint(endpoint)] ?? [];
139
+ }
95
140
  function resolveEndpoint(input = {}) {
96
141
  const envVars = input.envVars ?? process.env;
97
142
  const env = input.env ?? "prod";
@@ -154,7 +199,7 @@ async function registerPublicClient(registrationEndpoint, redirectUri, fetchImpl
154
199
  method: "POST",
155
200
  headers: { "content-type": "application/json", accept: "application/json" },
156
201
  body: JSON.stringify({
157
- client_name: "Ainecto CLI",
202
+ client_name: "AiNecto CLI",
158
203
  redirect_uris: [redirectUri],
159
204
  token_endpoint_auth_method: "none",
160
205
  grant_types: ["authorization_code", "refresh_token"],
@@ -177,32 +222,16 @@ function isRecord(value) {
177
222
  return typeof value === "object" && value !== null && !Array.isArray(value);
178
223
  }
179
224
 
180
- // src/core/harness/role.ts
181
- var HARNESS_ROLES = ["design", "development", "test", "validation"];
182
- var ROLE_SCOPE_PREFIX = "ai-erd:role:";
183
- var ROLE_HEADER = "X-AI-ERD-Role";
184
- var ROLE_ENV_VAR = "AI_ERD_ROLE";
185
- function resolveRole(input = {}) {
186
- if (input.role !== void 0) {
187
- return { role: assertRole(input.role), source: "flag" };
188
- }
189
- const fromEnv = (input.envVars ?? process.env)[ROLE_ENV_VAR];
190
- if (fromEnv && fromEnv.trim()) {
191
- return { role: assertRole(fromEnv), source: "env" };
192
- }
193
- return { source: "none" };
194
- }
195
- function assertRole(value) {
196
- const normalized = value.trim().toLowerCase();
197
- if (HARNESS_ROLES.includes(normalized)) {
198
- return normalized;
199
- }
200
- throw new Error(
201
- `Invalid role "${value}". Expected one of: ${HARNESS_ROLES.join(", ")}.`
202
- );
203
- }
204
-
205
225
  // src/core/auth/oauth.ts
226
+ var DEFAULT_LOGIN_TIMEOUT_MS = 3e5;
227
+ var OAuthLoginError = class extends Error {
228
+ constructor(code, message) {
229
+ super(message);
230
+ this.code = code;
231
+ this.name = "OAuthLoginError";
232
+ }
233
+ code;
234
+ };
206
235
  var OAuthClient = class {
207
236
  constructor(options) {
208
237
  this.options = options;
@@ -220,21 +249,27 @@ var OAuthClient = class {
220
249
  assertEnvTokenEndpointAllowed(this.options.endpoint, this.envVars);
221
250
  return envToken;
222
251
  }
223
- const stored = await this.options.tokenStore.load(this.options.endpoint, this.options.role);
252
+ const stored = await this.loadStored();
224
253
  if (!stored) {
225
254
  return void 0;
226
255
  }
227
256
  if (!shouldRefresh(stored)) {
228
257
  return stored.accessToken;
229
258
  }
230
- return this.refreshStoredToken(stored);
259
+ if (this.options.readOnly) {
260
+ return isExpired(stored) ? void 0 : stored.accessToken;
261
+ }
262
+ if (!stored.refreshToken) {
263
+ return isExpired(stored) ? void 0 : stored.accessToken;
264
+ }
265
+ return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });
231
266
  }
232
267
  async refreshAfterUnauthorized() {
233
- const stored = await this.options.tokenStore.load(this.options.endpoint, this.options.role);
234
- if (!stored?.refreshToken) {
268
+ const stored = await this.loadStored();
269
+ if (this.options.readOnly || !stored?.refreshToken) {
235
270
  return void 0;
236
271
  }
237
- return this.refreshStoredToken(stored);
272
+ return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });
238
273
  }
239
274
  async login() {
240
275
  const pkce = generatePkcePair();
@@ -251,12 +286,14 @@ var OAuthClient = class {
251
286
  authorizeUrl.searchParams.set("code_challenge_method", "S256");
252
287
  authorizeUrl.searchParams.set("resource", this.options.endpoint);
253
288
  authorizeUrl.searchParams.set("state", state);
254
- if (this.options.role) {
255
- authorizeUrl.searchParams.set("scope", `mcp ${ROLE_SCOPE_PREFIX}${this.options.role}`);
256
- }
257
289
  assertTrustedEndpointUrl(authorizeUrl, "OAuth authorization URL");
258
- await this.openBrowserImpl(authorizeUrl.toString());
259
- const code = await loopback.waitForCode();
290
+ const url = authorizeUrl.toString();
291
+ this.options.onAuthorizeUrl?.(url);
292
+ const browserFailure = this.openBrowserImpl(url).then(() => new Promise(() => void 0));
293
+ const code = await withTimeout(
294
+ Promise.race([loopback.waitForCode(), browserFailure]),
295
+ this.options.loginTimeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS
296
+ );
260
297
  const token = await exchangeToken(metadata.tokenEndpoint, {
261
298
  grant_type: "authorization_code",
262
299
  code,
@@ -267,19 +304,38 @@ var OAuthClient = class {
267
304
  ...registration.clientSecret ? { client_secret: registration.clientSecret } : {}
268
305
  }, this.fetchImpl);
269
306
  const stored = toStoredToken(this.options.endpoint, token, metadata, registration);
270
- await this.options.tokenStore.save(this.options.endpoint, stored, this.options.role);
307
+ await this.options.tokenStore.save(this.options.endpoint, stored);
271
308
  return stored;
272
309
  } finally {
273
310
  await loopback.close();
274
311
  }
275
312
  }
313
+ /**
314
+ * ★대체로 읽는 옛 칸도 함께 지운다. 새 칸만 지우면 다음 읽기가 곧바로 옛 칸으로 대체돼
315
+ * «로그아웃했는데 여전히 로그인됨»이 된다. 로그인은 기계당 한 번이므로 옛 CLI 의 칸도 같이 끝난다.
316
+ */
276
317
  async logout() {
277
- await this.options.tokenStore.delete(this.options.endpoint, this.options.role);
318
+ await this.options.tokenStore.delete(this.options.endpoint);
319
+ for (const fallback of fallbackTokenEndpointsFor(this.options.endpoint)) {
320
+ await this.options.tokenStore.delete(fallback);
321
+ }
278
322
  }
279
- async refreshStoredToken(stored) {
280
- if (!stored.refreshToken) {
281
- return stored.accessToken;
323
+ /** 이 주소의 칸, 비었으면 옛 기본 주소의 칸({@link fallbackTokenEndpointsFor}). 쓰기는 늘 이 주소의 칸이다. */
324
+ async loadStored() {
325
+ const own = await this.options.tokenStore.load(this.options.endpoint);
326
+ if (own) {
327
+ return own;
328
+ }
329
+ for (const fallback of fallbackTokenEndpointsFor(this.options.endpoint)) {
330
+ const stored = await this.options.tokenStore.load(fallback);
331
+ if (stored) {
332
+ return stored;
333
+ }
282
334
  }
335
+ return void 0;
336
+ }
337
+ /** 부르는 쪽이 refresh token 이 있음을 이미 확인했다 — 타입이 그것을 요구한다. */
338
+ async refreshStoredToken(stored) {
283
339
  const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);
284
340
  const token = await exchangeToken(metadata.tokenEndpoint, {
285
341
  grant_type: "refresh_token",
@@ -289,7 +345,7 @@ var OAuthClient = class {
289
345
  resource: this.options.endpoint
290
346
  }, this.fetchImpl);
291
347
  const next = toStoredToken(this.options.endpoint, token, metadata, stored);
292
- await this.options.tokenStore.save(this.options.endpoint, next, this.options.role);
348
+ await this.options.tokenStore.save(this.options.endpoint, next);
293
349
  return next.accessToken;
294
350
  }
295
351
  };
@@ -301,6 +357,27 @@ function generatePkcePair() {
301
357
  function generateOAuthState() {
302
358
  return randomBytes(32).toString("base64url");
303
359
  }
360
+ function isExpired(token, now = Date.now()) {
361
+ return token.expiresAt !== void 0 && token.expiresAt <= now;
362
+ }
363
+ async function withTimeout(work, timeoutMs) {
364
+ let timer;
365
+ const timeout = new Promise((_, reject) => {
366
+ timer = setTimeout(() => {
367
+ const minutes = timeoutMs / 6e4;
368
+ const span = Number.isInteger(minutes) ? `${minutes} minute${minutes === 1 ? "" : "s"}` : `${timeoutMs} ms`;
369
+ reject(new OAuthLoginError(
370
+ "LOGIN_TIMEOUT",
371
+ `Sign-in was not completed within ${span}. Nothing was saved \u2014 run the same command again.`
372
+ ));
373
+ }, timeoutMs);
374
+ });
375
+ try {
376
+ return await Promise.race([work, timeout]);
377
+ } finally {
378
+ clearTimeout(timer);
379
+ }
380
+ }
304
381
  function shouldRefresh(token, now = Date.now()) {
305
382
  if (!token.expiresAt) {
306
383
  return false;
@@ -393,11 +470,11 @@ function toStoredToken(endpoint, token, metadata, previous) {
393
470
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
394
471
  };
395
472
  }
396
- async function createLoopbackReceiver(expectedState) {
473
+ async function createLoopbackReceiver(expectedState, host = "127.0.0.1") {
397
474
  let resolveCode;
398
475
  let rejectCode;
399
- const codePromise = new Promise((resolve2, reject) => {
400
- resolveCode = resolve2;
476
+ const codePromise = new Promise((resolve3, reject) => {
477
+ resolveCode = resolve3;
401
478
  rejectCode = reject;
402
479
  });
403
480
  codePromise.catch(() => void 0);
@@ -408,13 +485,13 @@ async function createLoopbackReceiver(expectedState) {
408
485
  const state = url.searchParams.get("state");
409
486
  if (error) {
410
487
  res.writeHead(400, { "content-type": "text/plain" });
411
- res.end("Ainecto CLI authorization failed. You can close this tab.");
488
+ res.end("AiNecto CLI authorization failed. You can close this tab.");
412
489
  rejectCode(new Error(`OAuth authorization failed: ${error}`));
413
490
  return;
414
491
  }
415
492
  if (!state || state !== expectedState) {
416
493
  res.writeHead(400, { "content-type": "text/plain" });
417
- res.end("Ainecto CLI authorization state mismatch. You can close this tab.");
494
+ res.end("AiNecto CLI authorization state mismatch. You can close this tab.");
418
495
  rejectCode(new Error("OAuth state mismatch."));
419
496
  return;
420
497
  }
@@ -425,10 +502,16 @@ async function createLoopbackReceiver(expectedState) {
425
502
  return;
426
503
  }
427
504
  res.writeHead(200, { "content-type": "text/plain" });
428
- res.end("Ainecto CLI authorization complete. You can close this tab.");
505
+ res.end("AiNecto CLI authorization complete. You can close this tab.");
429
506
  resolveCode(code);
430
507
  });
431
- await new Promise((resolve2) => server.listen(0, "127.0.0.1", resolve2));
508
+ await new Promise((resolve3, reject) => {
509
+ server.once("error", (error) => reject(new OAuthLoginError(
510
+ "CALLBACK_UNAVAILABLE",
511
+ `Could not start the local sign-in callback on ${host} (${error.message}). If this command runs in a sandbox, allow local network binding or run it outside the sandbox.`
512
+ )));
513
+ server.listen(0, host, () => resolve3());
514
+ });
432
515
  const address = server.address();
433
516
  if (!address || typeof address === "string") {
434
517
  throw new Error("Loopback OAuth server did not bind to a TCP port.");
@@ -436,7 +519,10 @@ async function createLoopbackReceiver(expectedState) {
436
519
  return {
437
520
  redirectUri: `http://127.0.0.1:${address.port}/callback`,
438
521
  waitForCode: () => codePromise,
439
- close: () => new Promise((resolve2, reject) => server.close((error) => error ? reject(error) : resolve2()))
522
+ close: () => new Promise((resolve3, reject) => {
523
+ server.closeAllConnections?.();
524
+ server.close((error) => error ? reject(error) : resolve3());
525
+ })
440
526
  };
441
527
  }
442
528
  function parseStringArray(value) {
@@ -460,10 +546,39 @@ function getBrowserOpenCommand(url, platform = process.platform) {
460
546
  }
461
547
  return { command: "xdg-open", args: [url] };
462
548
  }
463
- async function openBrowser(url) {
464
- const { command, args } = getBrowserOpenCommand(url);
465
- const child = spawn(command, args, { stdio: "ignore", detached: true });
466
- child.unref();
549
+ function launchBrowser(url, deps = {}) {
550
+ const platform = deps.platform ?? process.platform;
551
+ const { command, args } = getBrowserOpenCommand(url, platform);
552
+ const spawnImpl = deps.spawnImpl ?? spawn;
553
+ return new Promise((resolve3, reject) => {
554
+ const unavailable = (reason) => new OAuthLoginError(
555
+ "BROWSER_UNAVAILABLE",
556
+ `Could not open a browser on this machine (${command}: ${reason}). Sign-in needs a browser here.`
557
+ );
558
+ let child;
559
+ try {
560
+ child = spawnImpl(command, args, { stdio: "ignore", detached: true });
561
+ } catch (error) {
562
+ reject(unavailable(error instanceof Error ? error.message : String(error)));
563
+ return;
564
+ }
565
+ child.once("error", (error) => reject(unavailable(error.message)));
566
+ if (platform === "win32") {
567
+ child.once("spawn", () => resolve3());
568
+ } else {
569
+ child.once("exit", (code, signal) => {
570
+ if (code === 0) {
571
+ resolve3();
572
+ } else {
573
+ reject(unavailable(code === null ? `stopped by ${signal ?? "a signal"}` : `exited with code ${code}`));
574
+ }
575
+ });
576
+ }
577
+ child.unref();
578
+ });
579
+ }
580
+ function openBrowser(url) {
581
+ return launchBrowser(url);
467
582
  }
468
583
  function assertEnvTokenEndpointAllowed(endpoint, envVars) {
469
584
  if (envVars.AINECTO_ALLOW_CUSTOM_ENDPOINT_TOKEN === "1") {
@@ -534,6 +649,12 @@ async function readAllStdin(stdin) {
534
649
  return text;
535
650
  }
536
651
 
652
+ // src/core/version.ts
653
+ var CLI_VERSION = true ? "0.5.0" : "dev";
654
+ var PACKAGE_NAME = "@ainecto/mcp";
655
+ var LEGACY_PACKAGE_NAME = "@ai-erd/mcp";
656
+ var CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;
657
+
537
658
  // src/core/mcp/rpcClient.ts
538
659
  var McpRpcError = class extends Error {
539
660
  constructor(message, code, details) {
@@ -556,7 +677,7 @@ var McpRpcClient = class {
556
677
  return this.request("initialize", {
557
678
  protocolVersion: "2024-11-05",
558
679
  capabilities: {},
559
- clientInfo: { name: "@ainecto/mcp", version: "0.1.0" }
680
+ clientInfo: { name: PACKAGE_NAME, version: CLI_VERSION }
560
681
  });
561
682
  }
562
683
  async toolsList() {
@@ -731,55 +852,67 @@ function redactSecretString(value) {
731
852
 
732
853
  // src/core/catalog/generated.prod.ts
733
854
  var generatedTools = [
734
- { "mcpName": "mcp__ainecto__create_documents", "commandPath": ["documents", "create"], "group": "document", "description": "Create documents. If a document body is tens of KB or more, prefer request_upload_token (purpose=document.create) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Documents", "items": { "properties": { "body": { "description": "Deprecated compatibility alias for typePayload.body when document type is markdown. \uBCF8\uBB38\uC774 \uC218\uC2ED KB \uC774\uC0C1\uC73C\uB85C \uD06C\uBA74 LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.create)` \uB97C \uD638\uCD9C\uD574 uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C \uC9C1\uC811 PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "folderUuid": { "description": "Optional folder UUID", "type": "string" }, "projectUuid": { "description": "Project UUID", "type": "string" }, "title": { "description": "Document title", "type": "string" }, "type": { "description": "Document type. Enabled values: erd, markdown.", "enum": ["erd", "markdown"], "type": "string" }, "typePayload": { "description": "Type-specific payload for enabled document types. markdown: {body}.", "type": "object" } }, "required": ["projectUuid", "type", "title"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4aa976b9bb3b0eb6" },
735
- { "mcpName": "mcp__ainecto__create_folders", "commandPath": ["folders", "create"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Folders", "items": { "properties": { "name": { "description": "Folder name", "type": "string" }, "parentFolderUuid": { "description": "Parent folder UUID", "type": "string" }, "projectUuid": { "description": "Project UUID", "type": "string" } }, "required": ["projectUuid", "name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "d423d3c265631664" },
736
- { "mcpName": "mcp__ainecto__create_projects", "commandPath": ["projects", "create"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Projects", "items": { "properties": { "description": { "description": "Description", "type": "string" }, "name": { "description": "Project name", "type": "string" }, "projectKey": { "description": "Unique project key", "type": "string" }, "workspaceUuid": { "description": "Workspace UUID \u2014 null uses user's default workspace", "type": "string" } }, "required": ["name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "396c80c6cc2ba41a" },
737
- { "mcpName": "mcp__ainecto__create_workspaces", "commandPath": ["workspaces", "create"], "group": "workspace", "description": "", "inputSchema": { "properties": { "items": { "description": "Workspaces", "items": { "properties": { "description": { "description": "Description", "type": "string" }, "name": { "description": "Workspace name", "type": "string" } }, "required": ["name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "d663d461f9c9da65" },
738
- { "mcpName": "mcp__ainecto__delete_attachments", "commandPath": ["attachments", "delete"], "group": "attachment", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Attachment UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "6e55e6511d732199" },
739
- { "mcpName": "mcp__ainecto__delete_documents", "commandPath": ["documents", "delete"], "group": "document", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Document UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "5fbc7c142bb4a49d" },
740
- { "mcpName": "mcp__ainecto__delete_folders", "commandPath": ["folders", "delete"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Folder UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "49fa7c0f823871b2" },
741
- { "mcpName": "mcp__ainecto__delete_projects", "commandPath": ["projects", "delete"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Project UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "e1a86ddaa9638298" },
742
- { "mcpName": "mcp__ainecto__delete_workspaces", "commandPath": ["workspaces", "delete"], "group": "workspace", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Workspace UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "2aca644f7a105b06" },
743
- { "mcpName": "mcp__ainecto__disable_shares", "commandPath": ["tools", "run", "disable-shares"], "group": "generic", "description": "", "inputSchema": { "properties": { "targetType": { "description": "Share target type", "enum": ["project", "folder", "document"], "type": "string" }, "targetUuids": { "description": "Target UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["targetType", "targetUuids"], "type": "object" }, "required": ["targetType", "targetUuids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "a4b4358eff0faa97" },
744
- { "mcpName": "mcp__ainecto__enable_shares", "commandPath": ["tools", "run", "enable-shares"], "group": "generic", "description": "", "inputSchema": { "properties": { "targetType": { "description": "Share target type", "enum": ["project", "folder", "document"], "type": "string" }, "targetUuids": { "description": "Target UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["targetType", "targetUuids"], "type": "object" }, "required": ["targetType", "targetUuids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "ebace643de07e826" },
745
- { "mcpName": "mcp__ainecto__erd_apply_changes", "commandPath": ["erd", "apply-changes"], "group": "erd", "description": 'Required single-call mutation path for ERD create/update/delete changes. Put tables, columns, indexes, refs, enums, enum_values, and table groups in one operations array and call this once.\nRules: (1) all data field names are snake_case; (2) for create, generate your own UUIDs and put them in data.uuid, then later operations in the same batch may reference those UUIDs without read-back calls; (3) enum values are separate enum_value operations, not inline on enum; (4) ref.relationship uses short dbdiagram-style symbols: >, <, or -; (5) ref_column operations map FK columns explicitly, never automatically.\nDo not look for type-specific ERD CUD tools; this tool is the only ERD CUD entry point. If operations is very large, use mcp__ainecto__request_upload_token with purpose=mcp.call and send the tools/call payload with curl to save LLM tokens.\nOne-call worked example:\n{\n "documentUuid": "erd-doc-uuid",\n "description": "create users/posts ERD",\n "operations": [\n {"op":"create","type":"table","data":{"uuid":"tbl_users","name":"users","display_order":1}},\n {"op":"create","type":"column","data":{"uuid":"col_users_id","table_uuid":"tbl_users","name":"id","type":"bigint","is_pk":true,"is_not_null":true,"display_order":1}},\n {"op":"create","type":"column","data":{"uuid":"col_users_email","table_uuid":"tbl_users","name":"email","type":"varchar(255)","is_unique":true,"is_not_null":true,"display_order":2}},\n {"op":"create","type":"table","data":{"uuid":"tbl_posts","name":"posts","display_order":2}},\n {"op":"create","type":"column","data":{"uuid":"col_posts_id","table_uuid":"tbl_posts","name":"id","type":"bigint","is_pk":true,"is_not_null":true,"display_order":1}},\n {"op":"create","type":"column","data":{"uuid":"col_posts_author_id","table_uuid":"tbl_posts","name":"author_id","type":"bigint","is_not_null":true,"display_order":2}},\n {"op":"create","type":"column","data":{"uuid":"col_posts_status","table_uuid":"tbl_posts","name":"status","type":"post_status","is_not_null":true,"display_order":3}},\n {"op":"create","type":"enum","data":{"uuid":"enum_post_status","name":"post_status"}},\n {"op":"create","type":"enum_value","data":{"uuid":"enum_post_status_draft","enum_uuid":"enum_post_status","value":"draft","display_order":1}},\n {"op":"create","type":"enum_value","data":{"uuid":"enum_post_status_published","enum_uuid":"enum_post_status","value":"published","display_order":2}},\n {"op":"create","type":"ref","data":{"uuid":"ref_posts_author","name":"fk_posts_author","from_table_uuid":"tbl_posts","to_table_uuid":"tbl_users","relationship":">","on_delete":"cascade"}},\n {"op":"create","type":"ref_column","data":{"uuid":"refcol_posts_author","ref_uuid":"ref_posts_author","from_column_uuid":"col_posts_author_id","to_column_uuid":"col_users_id","display_order":1}}\n ]\n}', "inputSchema": { "properties": { "arrangeNewTables": { "description": "Whether to auto-place newly-created tables after the batch; defaults to true", "type": "boolean" }, "description": { "description": "Reason for batch (audit)", "type": "string" }, "documentUuid": { "description": "ERD document UUID", "type": "string" }, "operations": { "description": "Atomic create/update/delete operations", "items": { "description": "One ERD mutation. Use a single operations array with client-generated data.uuid values to create cross-referenced tables, columns, enums, refs, and ref_columns without read-back calls.", "discriminator": { "propertyName": "type" }, "oneOf": [{ "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table'.", "properties": { "alias": { "description": "Display alias.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "header_color": { "description": "Header color.", "type": "string" }, "name": { "description": "Table name. Required for create.", "type": "string" }, "note": { "description": "Table note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["name"], "title": "table data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table'.", "properties": { "alias": { "description": "Display alias.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "header_color": { "description": "Header color.", "type": "string" }, "name": { "description": "Table name. Required for create.", "type": "string" }, "note": { "description": "Table note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table", "description": "Must be 'table' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='column'.", "properties": { "default_value": { "description": "Default value.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "is_increment": { "description": "Auto-increment flag.", "type": "boolean" }, "is_not_null": { "description": "Not-null flag.", "type": "boolean" }, "is_pk": { "description": "Primary key flag.", "type": "boolean" }, "is_unique": { "description": "Unique flag.", "type": "boolean" }, "name": { "description": "Column name.", "type": "string" }, "note": { "description": "Column note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create; may be a data.uuid from an earlier table operation in this batch.", "type": "string" }, "type": { "description": "SQL data type. Use this exact field name; do not send data_type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["table_uuid"], "title": "column data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='column'.", "properties": { "default_value": { "description": "Default value.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "is_increment": { "description": "Auto-increment flag.", "type": "boolean" }, "is_not_null": { "description": "Not-null flag.", "type": "boolean" }, "is_pk": { "description": "Primary key flag.", "type": "boolean" }, "is_unique": { "description": "Unique flag.", "type": "boolean" }, "name": { "description": "Column name.", "type": "string" }, "note": { "description": "Column note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create; may be a data.uuid from an earlier table operation in this batch.", "type": "string" }, "type": { "description": "SQL data type. Use this exact field name; do not send data_type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "column data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "column", "description": "Must be 'column' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "column operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='index'.", "properties": { "is_pk": { "description": "Primary key index flag.", "type": "boolean" }, "is_unique": { "description": "Unique index flag.", "type": "boolean" }, "name": { "description": "Index name.", "type": "string" }, "note": { "description": "Index note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create.", "type": "string" }, "type": { "description": "Index type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["table_uuid"], "title": "index data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='index'.", "properties": { "is_pk": { "description": "Primary key index flag.", "type": "boolean" }, "is_unique": { "description": "Unique index flag.", "type": "boolean" }, "name": { "description": "Index name.", "type": "string" }, "note": { "description": "Index note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create.", "type": "string" }, "type": { "description": "Index type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "index data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "index", "description": "Must be 'index' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "index operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='index_column'.", "properties": { "column_uuid": { "description": "Column UUID. Required for create.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "index_uuid": { "description": "Index UUID. Required for create.", "type": "string" }, "sort_order": { "description": "Sort order, for example ASC or DESC.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["index_uuid", "column_uuid"], "title": "index_column data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='index_column'.", "properties": { "column_uuid": { "description": "Column UUID. Required for create.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "index_uuid": { "description": "Index UUID. Required for create.", "type": "string" }, "sort_order": { "description": "Sort order, for example ASC or DESC.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "index_column data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "index_column", "description": "Must be 'index_column' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "index_column operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='ref'.", "properties": { "from_table_uuid": { "description": "Source table UUID. Required for create.", "type": "string" }, "name": { "description": "Reference name.", "type": "string" }, "on_delete": { "description": "ON DELETE action.", "type": "string" }, "on_update": { "description": "ON UPDATE action.", "type": "string" }, "relationship": { "description": "Required for create. Use short dbdiagram-style relationship symbols.", "enum": [">", "<", "-"], "type": "string" }, "to_table_uuid": { "description": "Target table UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["from_table_uuid", "to_table_uuid", "relationship"], "title": "ref data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='ref'.", "properties": { "from_table_uuid": { "description": "Source table UUID. Required for create.", "type": "string" }, "name": { "description": "Reference name.", "type": "string" }, "on_delete": { "description": "ON DELETE action.", "type": "string" }, "on_update": { "description": "ON UPDATE action.", "type": "string" }, "relationship": { "description": "Required for create. Use short dbdiagram-style relationship symbols.", "enum": [">", "<", "-"], "type": "string" }, "to_table_uuid": { "description": "Target table UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "ref data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "ref", "description": "Must be 'ref' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "ref operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='ref_column'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "from_column_uuid": { "description": "Source column UUID. Required for create.", "type": "string" }, "ref_uuid": { "description": "Reference UUID. Required for create; may be a data.uuid from an earlier ref operation.", "type": "string" }, "to_column_uuid": { "description": "Target column UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["ref_uuid", "from_column_uuid", "to_column_uuid"], "title": "ref_column data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='ref_column'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "from_column_uuid": { "description": "Source column UUID. Required for create.", "type": "string" }, "ref_uuid": { "description": "Reference UUID. Required for create; may be a data.uuid from an earlier ref operation.", "type": "string" }, "to_column_uuid": { "description": "Target column UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "ref_column data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "ref_column", "description": "Must be 'ref_column' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "ref_column operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='enum'.", "properties": { "name": { "description": "Enum name.", "type": "string" }, "note": { "description": "Enum note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "enum data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='enum'.", "properties": { "name": { "description": "Enum name.", "type": "string" }, "note": { "description": "Enum note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "enum data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "enum", "description": "Must be 'enum' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "enum operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='enum_value'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "enum_uuid": { "description": "Parent enum UUID. Required for create; may be a data.uuid from an earlier enum operation.", "type": "string" }, "note": { "description": "Enum value note.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" }, "value": { "description": "Enum value. Inline enum values on enum are not persisted; create enum_value operations.", "type": "string" } }, "required": ["enum_uuid"], "title": "enum_value data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='enum_value'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "enum_uuid": { "description": "Parent enum UUID. Required for create; may be a data.uuid from an earlier enum operation.", "type": "string" }, "note": { "description": "Enum value note.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" }, "value": { "description": "Enum value. Inline enum values on enum are not persisted; create enum_value operations.", "type": "string" } }, "title": "enum_value data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "enum_value", "description": "Must be 'enum_value' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "enum_value operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table_group'.", "properties": { "color": { "description": "Group color.", "type": "string" }, "name": { "description": "Table group name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table_group'.", "properties": { "color": { "description": "Group color.", "type": "string" }, "name": { "description": "Table group name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table_group", "description": "Must be 'table_group' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table_group operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table_group_table'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "schema_name": { "description": "Table schema name.", "type": "string" }, "table_name": { "description": "Table name. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["group_uuid", "table_name"], "title": "table_group_table data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table_group_table'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "schema_name": { "description": "Table schema name.", "type": "string" }, "table_name": { "description": "Table name. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group_table data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table_group_table", "description": "Must be 'table_group_table' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table_group_table operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table_group_note'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "note_uuid": { "description": "Note UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "required": ["group_uuid", "note_uuid"], "title": "table_group_note data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table_group_note'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "note_uuid": { "description": "Note UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group_note data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table_group_note", "description": "Must be 'table_group_note' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table_group_note operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='filter'.", "properties": { "included_tables": { "description": "JSON string/list of included tables.", "type": "string" }, "is_default": { "description": "Default filter flag.", "type": "boolean" }, "name": { "description": "Filter name.", "type": "string" }, "table_groups": { "description": "JSON string/object of table groups.", "type": "string" }, "table_positions": { "description": "JSON string/object of table positions.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "filter data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='filter'.", "properties": { "included_tables": { "description": "JSON string/list of included tables.", "type": "string" }, "is_default": { "description": "Default filter flag.", "type": "boolean" }, "name": { "description": "Filter name.", "type": "string" }, "table_groups": { "description": "JSON string/object of table groups.", "type": "string" }, "table_positions": { "description": "JSON string/object of table positions.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "filter data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "filter", "description": "Must be 'filter' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "filter operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='note'.", "properties": { "color": { "description": "Note color.", "type": "string" }, "content": { "description": "Note content.", "type": "string" }, "height": { "description": "Height.", "type": "integer" }, "icon_name": { "description": "Icon name.", "type": "string" }, "position_x": { "description": "X position.", "type": "integer" }, "position_y": { "description": "Y position.", "type": "integer" }, "title": { "description": "Note title.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" }, "width": { "description": "Width.", "type": "integer" } }, "title": "note data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='note'.", "properties": { "color": { "description": "Note color.", "type": "string" }, "content": { "description": "Note content.", "type": "string" }, "height": { "description": "Height.", "type": "integer" }, "icon_name": { "description": "Icon name.", "type": "string" }, "position_x": { "description": "X position.", "type": "integer" }, "position_y": { "description": "Y position.", "type": "integer" }, "title": { "description": "Note title.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" }, "width": { "description": "Width.", "type": "integer" } }, "title": "note data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "note", "description": "Must be 'note' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "note operation", "type": "object" }], "properties": { "data": { "description": "Type-specific payload. All field names are snake_case. For create, data.uuid may be client-supplied and referenced by later operations in the same batch.", "oneOf": [{ "description": "Payload for operation.type='table'.", "properties": { "alias": { "description": "Display alias.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "header_color": { "description": "Header color.", "type": "string" }, "name": { "description": "Table name. Required for create.", "type": "string" }, "note": { "description": "Table note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table data", "type": "object" }, { "description": "Payload for operation.type='column'.", "properties": { "default_value": { "description": "Default value.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "is_increment": { "description": "Auto-increment flag.", "type": "boolean" }, "is_not_null": { "description": "Not-null flag.", "type": "boolean" }, "is_pk": { "description": "Primary key flag.", "type": "boolean" }, "is_unique": { "description": "Unique flag.", "type": "boolean" }, "name": { "description": "Column name.", "type": "string" }, "note": { "description": "Column note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create; may be a data.uuid from an earlier table operation in this batch.", "type": "string" }, "type": { "description": "SQL data type. Use this exact field name; do not send data_type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "column data", "type": "object" }, { "description": "Payload for operation.type='index'.", "properties": { "is_pk": { "description": "Primary key index flag.", "type": "boolean" }, "is_unique": { "description": "Unique index flag.", "type": "boolean" }, "name": { "description": "Index name.", "type": "string" }, "note": { "description": "Index note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create.", "type": "string" }, "type": { "description": "Index type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "index data", "type": "object" }, { "description": "Payload for operation.type='index_column'.", "properties": { "column_uuid": { "description": "Column UUID. Required for create.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "index_uuid": { "description": "Index UUID. Required for create.", "type": "string" }, "sort_order": { "description": "Sort order, for example ASC or DESC.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "index_column data", "type": "object" }, { "description": "Payload for operation.type='ref'.", "properties": { "from_table_uuid": { "description": "Source table UUID. Required for create.", "type": "string" }, "name": { "description": "Reference name.", "type": "string" }, "on_delete": { "description": "ON DELETE action.", "type": "string" }, "on_update": { "description": "ON UPDATE action.", "type": "string" }, "relationship": { "description": "Required for create. Use short dbdiagram-style relationship symbols.", "enum": [">", "<", "-"], "type": "string" }, "to_table_uuid": { "description": "Target table UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "ref data", "type": "object" }, { "description": "Payload for operation.type='ref_column'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "from_column_uuid": { "description": "Source column UUID. Required for create.", "type": "string" }, "ref_uuid": { "description": "Reference UUID. Required for create; may be a data.uuid from an earlier ref operation.", "type": "string" }, "to_column_uuid": { "description": "Target column UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "ref_column data", "type": "object" }, { "description": "Payload for operation.type='enum'.", "properties": { "name": { "description": "Enum name.", "type": "string" }, "note": { "description": "Enum note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "enum data", "type": "object" }, { "description": "Payload for operation.type='enum_value'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "enum_uuid": { "description": "Parent enum UUID. Required for create; may be a data.uuid from an earlier enum operation.", "type": "string" }, "note": { "description": "Enum value note.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" }, "value": { "description": "Enum value. Inline enum values on enum are not persisted; create enum_value operations.", "type": "string" } }, "title": "enum_value data", "type": "object" }, { "description": "Payload for operation.type='table_group'.", "properties": { "color": { "description": "Group color.", "type": "string" }, "name": { "description": "Table group name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group data", "type": "object" }, { "description": "Payload for operation.type='table_group_table'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "schema_name": { "description": "Table schema name.", "type": "string" }, "table_name": { "description": "Table name. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group_table data", "type": "object" }, { "description": "Payload for operation.type='table_group_note'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "note_uuid": { "description": "Note UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "table_group_note data", "type": "object" }, { "description": "Payload for operation.type='filter'.", "properties": { "included_tables": { "description": "JSON string/list of included tables.", "type": "string" }, "is_default": { "description": "Default filter flag.", "type": "boolean" }, "name": { "description": "Filter name.", "type": "string" }, "table_groups": { "description": "JSON string/object of table groups.", "type": "string" }, "table_positions": { "description": "JSON string/object of table positions.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" } }, "title": "filter data", "type": "object" }, { "description": "Payload for operation.type='note'.", "properties": { "color": { "description": "Note color.", "type": "string" }, "content": { "description": "Note content.", "type": "string" }, "height": { "description": "Height.", "type": "integer" }, "icon_name": { "description": "Icon name.", "type": "string" }, "position_x": { "description": "X position.", "type": "integer" }, "position_y": { "description": "Y position.", "type": "integer" }, "title": { "description": "Note title.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID. If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "type": "string" }, "width": { "description": "Width.", "type": "integer" } }, "title": "note data", "type": "object" }], "type": "object" }, "op": { "description": "Operation. For create, put any new UUID in data.uuid; for update/delete, put target UUID in operation.uuid.", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "description": "ERD entity type. This selects the operation.data shape.", "enum": ["table", "column", "index", "index_column", "ref", "ref_column", "enum", "enum_value", "table_group", "table_group_table", "table_group_note", "filter", "note"], "type": "string" }, "uuid": { "description": "Target UUID for update/delete. Do not use this for create; use data.uuid instead.", "type": "string" } }, "required": ["op", "type"], "type": "object" }, "type": "array" } }, "required": ["documentUuid", "description", "operations"], "type": "object" }, "required": ["description", "documentUuid", "operations"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "dd64d1f996060ab1" },
746
- { "mcpName": "mcp__ainecto__erd_arrange_tables", "commandPath": ["erd", "arrange-tables"], "group": "erd", "description": "Arrange ERD tables after edits. Default incremental mode places newly-created tables while preserving existing positions; full=true re-layouts the entire diagram. Use after several separate table edits to tidy the canvas.", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "full": { "description": "Re-layout the ENTIRE diagram (overwrites existing positions). Default false = arrange only newly-added (uncoordinated) tables to the top-right while preserving existing table positions. Call this after creating several tables to tidy them up.", "type": "boolean" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "d2e66548e3d60d30" },
747
- { "mcpName": "mcp__ainecto__erd_get_enum", "commandPath": ["erd", "get-enum"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "enumUuid": { "description": "Enum UUID", "type": "string" } }, "required": ["documentUuid", "enumUuid"], "type": "object" }, "required": ["documentUuid", "enumUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "1e477b3fadb50c6b" },
748
- { "mcpName": "mcp__ainecto__erd_get_ref", "commandPath": ["erd", "get-ref"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "refUuid": { "description": "Relationship UUID", "type": "string" } }, "required": ["documentUuid", "refUuid"], "type": "object" }, "required": ["documentUuid", "refUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "40bf1eb02e7a9730" },
749
- { "mcpName": "mcp__ainecto__erd_get_table", "commandPath": ["erd", "get-table"], "group": "erd", "description": "Get one ERD table with nested columns and indexes. Returns persistent table/column/index UUIDs usable directly as mcp__ainecto__erd_apply_changes update/delete targets and ref_column column references. Column fields use apply_changes snake_case names: uuid, table_uuid, name, type, is_pk, is_unique, is_not_null, is_increment, default_value, note, display_order. Index fields include uuid, name, columns (column UUIDs), is_unique, is_pk, type.", "inputSchema": { "description": "Get one ERD table with nested columns and indexes. Returns persistent table/column/index UUIDs usable directly as mcp__ainecto__erd_apply_changes update/delete targets and ref_column column references. Column fields use apply_changes snake_case names: uuid, table_uuid, name, type, is_pk, is_unique, is_not_null, is_increment, default_value, note, display_order. Index fields include uuid, name, columns (column UUIDs), is_unique, is_pk, type.", "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "tableUuid": { "description": "Table UUID. Response includes columns[] and indexes[] with persistent UUIDs for apply_changes.", "type": "string" } }, "required": ["documentUuid", "tableUuid"], "type": "object" }, "required": ["documentUuid", "tableUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "8edd40794f39f47a" },
750
- { "mcpName": "mcp__ainecto__erd_get_table_group", "commandPath": ["erd", "get-table-group"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "tableGroupUuid": { "description": "Table group UUID", "type": "string" } }, "required": ["documentUuid", "tableGroupUuid"], "type": "object" }, "required": ["documentUuid", "tableGroupUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "bcd417344639c0c9" },
751
- { "mcpName": "mcp__ainecto__erd_list_enums", "commandPath": ["erd", "list-enums"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "e0c2207905dbb068" },
752
- { "mcpName": "mcp__ainecto__erd_list_refs", "commandPath": ["erd", "list-refs"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "9ae2e04c310b57f2" },
753
- { "mcpName": "mcp__ainecto__erd_list_table_groups", "commandPath": ["erd", "list-table-groups"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "dc8811071df04ac2" },
754
- { "mcpName": "mcp__ainecto__erd_list_tables", "commandPath": ["erd", "list-tables"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "ca05b35b174fc209" },
755
- { "mcpName": "mcp__ainecto__get_attachment", "commandPath": ["attachment", "get"], "group": "attachment", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Attachment UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "12bee70f6d254a5d" },
756
- { "mcpName": "mcp__ainecto__get_document", "commandPath": ["document", "get"], "group": "document", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4949c8a5bb425491" },
757
- { "mcpName": "mcp__ainecto__get_project", "commandPath": ["project", "get"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Project UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "b4ee9097f4b80df4" },
758
- { "mcpName": "mcp__ainecto__get_version", "commandPath": ["version", "get"], "group": "generic", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "Document UUID", "type": "string" }, "versionNumber": { "description": "Version number", "type": "integer" } }, "required": ["documentUuid", "versionNumber"], "type": "object" }, "required": ["documentUuid", "versionNumber"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "ad81d04200d3d7f5" },
759
- { "mcpName": "mcp__ainecto__get_workspace", "commandPath": ["workspace", "get"], "group": "workspace", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Workspace UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "ef7bae555463116e" },
760
- { "mcpName": "mcp__ainecto__list_attachments", "commandPath": ["attachments", "list"], "group": "attachment", "description": "", "inputSchema": { "properties": { "projectUuid": { "description": "Project UUID", "type": "string" } }, "required": ["projectUuid"], "type": "object" }, "required": ["projectUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "a75657bb2e5eb383" },
761
- { "mcpName": "mcp__ainecto__list_documents", "commandPath": ["documents", "list"], "group": "document", "description": "", "inputSchema": { "properties": { "folderUuid": { "description": "Optional folder UUID", "type": "string" }, "limit": { "description": "Result cap when q is set (default 50)", "type": "integer" }, "projectUuid": { "description": "Project UUID", "type": "string" }, "q": { "description": "Optional full-text query. When provided, results come from the search index instead of folder listing.", "type": "string" }, "type": { "description": "Optional document type filter. Enabled values: erd, markdown.", "enum": ["erd", "markdown"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "8dca77c1db9372c4" },
762
- { "mcpName": "mcp__ainecto__list_folders", "commandPath": ["folders", "list"], "group": "generic", "description": "", "inputSchema": { "properties": { "projectUuid": { "description": "Project UUID", "type": "string" } }, "required": ["projectUuid"], "type": "object" }, "required": ["projectUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "f3cde57fe87d375c" },
763
- { "mcpName": "mcp__ainecto__list_projects", "commandPath": ["projects", "list"], "group": "generic", "description": "", "inputSchema": { "properties": { "limit": { "description": "Result cap when q is set (default 50)", "type": "integer" }, "q": { "description": "Optional full-text query", "type": "string" }, "workspaceUuid": { "description": "Optional Workspace UUID", "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "9174d6965d7e53f5" },
764
- { "mcpName": "mcp__ainecto__list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "82f8586d68bfcd7d" },
765
- { "mcpName": "mcp__ainecto__list_versions", "commandPath": ["versions", "list"], "group": "generic", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "Document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "370e4b8b8ec18e18" },
766
- { "mcpName": "mcp__ainecto__list_workspaces", "commandPath": ["workspaces", "list"], "group": "workspace", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "7c32c74000696b2f" },
767
- { "mcpName": "mcp__ainecto__markdown_append_document", "commandPath": ["markdown", "append-document"], "group": "markdown", "description": "Append to Markdown documents. If the appended body is tens of KB or more, prefer request_upload_token (purpose=document.append) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Appends", "items": { "properties": { "addition": { "description": "Body addition. \uCD94\uAC00\uD560 \uB0B4\uC6A9\uC774 \uD06C\uBA74 (\uC218\uC2ED KB \uC774\uC0C1) LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.append, scopeJson={documentUuid, separator})` \uB85C uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "separator": { "description": "Separator between existing body and addition", "type": "string" }, "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid", "addition"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "f189514b3cccd1cc" },
768
- { "mcpName": "mcp__ainecto__markdown_generate_document", "commandPath": ["markdown", "generate-document"], "group": "markdown", "description": "", "inputSchema": { "properties": { "items": { "description": "Generation requests", "items": { "properties": { "context": { "description": "Optional JSON context", "type": "string" }, "documentUuid": { "description": "Target document UUID", "type": "string" }, "prompt": { "description": "Generation prompt", "type": "string" } }, "required": ["documentUuid", "prompt"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "72114a2d2642ed92" },
769
- { "mcpName": "mcp__ainecto__move_documents", "commandPath": ["documents", "move"], "group": "document", "description": "", "inputSchema": { "properties": { "clientMutationId": { "description": "Optional mutation identifier for client retry tracing", "maxLength": 128, "minLength": 1, "pattern": "^[\\x21-\\x7E]{1,128}$", "type": "string" }, "items": { "description": "Moves", "items": { "properties": { "newFolderUuid": { "description": "New folder UUID \u2014 null/empty for root", "type": "string" }, "targetProjectUuid": { "description": "Target project UUID \u2014 omit to stay in current project", "type": "string" }, "targetWorkspaceUuid": { "description": "Guard only: must match targetProjectUuid workspace", "type": "string" }, "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "9758d6a262913a3f" },
770
- { "mcpName": "mcp__ainecto__move_folders", "commandPath": ["folders", "move"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Moves", "items": { "properties": { "newParentFolderUuid": { "description": "New parent UUID \u2014 null/empty for root", "type": "string" }, "uuid": { "description": "Folder UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "612a779b1fd176bb" },
771
- { "mcpName": "mcp__ainecto__request_upload_token", "commandPath": ["tools", "run", "request-upload-token"], "group": "attachment", "description": "Use this for large payloads of tens of KB or more to save LLM tokens. purpose=mcp.call returns a short-lived bearer token that can call any MCP tool, including mcp__ainecto__erd_apply_changes, via curl. purpose=document.create/update/append uploads document bodies directly to the upload endpoint with PUT/curl. Tokens are reusable for 5 minutes by default.", "inputSchema": { "properties": { "maxBytes": { "description": "\uCD5C\uB300 \uC5C5\uB85C\uB4DC byte (\uC11C\uBC84 \uCE21 \uD55C\uB3C4)", "type": "integer" }, "purpose": { "description": "Token \uC6A9\uB3C4. `mcp.call` \uC740 curl \uB85C /mcp \uD638\uCD9C, `document.create/update/append` \uB294 PUT body \uB85C \uBCF8\uBB38 \uC5C5\uB85C\uB4DC, `attachment.upload` \uB294 \uD30C\uC77C bytes \uC5C5\uB85C\uB4DC. \uCF58\uD150\uCE20\uAC00 \uD06C\uBA74 (\uC218\uC2ED KB \uC774\uC0C1) LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 \uC774 \uB3C4\uAD6C\uB85C 5\uBD84 \uC7AC\uC0AC\uC6A9 \uD1A0\uD070\uC744 \uBC1B\uACE0 curl \uB85C \uBCF4\uB0B4\uB294 \uAC83\uC744 \uAC15\uB825 \uAD8C\uC7A5.", "enum": ["mcp.call", "document.create", "document.update", "document.append", "attachment.upload"], "type": "string" }, "scopeJson": { "description": "\uC120\uD0DD \u2014 JSON scope. document.create \uB294 projectUuid, type, title \uC744 \uD3EC\uD568\uD558\uACE0 enabled type values \uB294 erd, markdown. document.update/document.append \uB294 documentUuid \uB97C \uD3EC\uD568. attachment.upload: {documentUuid, filename, contentType?, sizeBytes?}; storageKey \uB294 \uC11C\uBC84\uAC00 \uBC1C\uAE09 scope \uC5D0 \uCD94\uAC00.", "type": "string" }, "ttlSeconds": { "description": "TTL (\uCD08). \uAE30\uBCF8 300 (5\uBD84)", "type": "integer" } }, "required": ["purpose"], "type": "object" }, "required": ["purpose"], "payloadMode": "binary-upload", "destructive": false, "sourceCatalog": "prod", "schemaHash": "f1eb94523f43e72b" },
772
- { "mcpName": "mcp__ainecto__restore_versions", "commandPath": ["versions", "restore"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Restores", "items": { "properties": { "documentUuid": { "description": "Document UUID", "type": "string" }, "versionNumber": { "description": "Version number to restore", "type": "integer" } }, "required": ["documentUuid", "versionNumber"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "c4f448cc3b96c7a6" },
773
- { "mcpName": "mcp__ainecto__search_documents", "commandPath": ["documents", "search"], "group": "document", "description": "", "inputSchema": { "properties": { "limit": { "description": "\uACB0\uACFC \uAC1C\uC218 (\uAE30\uBCF8 20, \uCD5C\uB300 50)", "type": "integer" }, "offset": { "description": "\uD398\uC774\uC9C0\uB124\uC774\uC158 offset (\uAE30\uBCF8 0)", "type": "integer" }, "projectUuid": { "description": "\uD2B9\uC815 \uD504\uB85C\uC81D\uD2B8\uB85C \uD55C\uC815 (\uC120\uD0DD)", "type": "string" }, "q": { "description": "\uAC80\uC0C9 \uD0A4\uC6CC\uB4DC", "type": "string" }, "type": { "description": "Optional document type filter. Enabled values: erd, markdown.", "enum": ["erd", "markdown"], "type": "string" } }, "required": ["q"], "type": "object" }, "required": ["q"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "b88c3537086d0c7a" },
774
- { "mcpName": "mcp__ainecto__unarchive_documents", "commandPath": ["documents", "unarchive"], "group": "document", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Document UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "4aae7df04fba8779" },
775
- { "mcpName": "mcp__ainecto__unarchive_folders", "commandPath": ["folders", "unarchive"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Folder UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "e307cab0ebafd179" },
776
- { "mcpName": "mcp__ainecto__unarchive_projects", "commandPath": ["projects", "unarchive"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Project UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "15ea4a409d1549de" },
777
- { "mcpName": "mcp__ainecto__unarchive_workspaces", "commandPath": ["workspaces", "unarchive"], "group": "workspace", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Workspace UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "641de5b64ec75ab2" },
778
- { "mcpName": "mcp__ainecto__update_documents", "commandPath": ["documents", "update"], "group": "document", "description": "Update documents. If a replacement body is tens of KB or more, prefer request_upload_token (purpose=document.update) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "body": { "description": "Deprecated compatibility alias for typePayload.body when document type is markdown. \uBCF8\uBB38\uC774 \uC218\uC2ED KB \uC774\uC0C1\uC73C\uB85C \uD06C\uBA74 LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.update)` \uB97C \uD638\uCD9C\uD574 uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C \uC9C1\uC811 PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "title": { "description": "New title", "type": "string" }, "typePayload": { "description": "Type-specific payload for enabled document types. markdown: {body}.", "type": "object" }, "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "5bedb6e5f5ba4f8d" },
779
- { "mcpName": "mcp__ainecto__update_folders", "commandPath": ["folders", "update"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "name": { "description": "New name", "type": "string" }, "uuid": { "description": "Folder UUID", "type": "string" } }, "required": ["uuid", "name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "7bef3b4702df1f00" },
780
- { "mcpName": "mcp__ainecto__update_projects", "commandPath": ["projects", "update"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "description": { "description": "New description", "type": "string" }, "name": { "description": "New name", "type": "string" }, "uuid": { "description": "Project UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "10cc0d84aeb2192b" },
781
- { "mcpName": "mcp__ainecto__update_workspaces", "commandPath": ["workspaces", "update"], "group": "workspace", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "description": { "description": "New description", "type": "string" }, "name": { "description": "New name", "type": "string" }, "uuid": { "description": "Workspace UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "723e4f6d36ee9d02" },
782
- { "mcpName": "mcp__ainecto__upload_attachments", "commandPath": ["tools", "run", "upload-attachments"], "group": "attachment", "description": "", "inputSchema": { "properties": { "items": { "description": "Attachments to register", "items": { "properties": { "contentType": { "description": "MIME type", "type": "string" }, "documentUuid": { "description": "Parent document UUID", "type": "string" }, "filename": { "description": "Original file name", "type": "string" }, "sizeBytes": { "description": "Size in bytes", "type": "integer" }, "storageKey": { "description": "Storage key returned by request_upload_token purpose=attachment.upload", "type": "string" } }, "required": ["documentUuid", "filename", "storageKey"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "binary-upload", "destructive": false, "sourceCatalog": "prod", "schemaHash": "0622e590de3404dd" }
855
+ { "mcpName": "create_documents", "commandPath": ["documents", "create"], "group": "document", "description": "Create documents. If a document body is tens of KB or more, prefer request_upload_token (purpose=document.create) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Documents", "items": { "properties": { "body": { "description": "Deprecated compatibility alias for typePayload.body when document type is markdown. \uBCF8\uBB38\uC774 \uC218\uC2ED KB \uC774\uC0C1\uC73C\uB85C \uD06C\uBA74 LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.create)` \uB97C \uD638\uCD9C\uD574 uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C \uC9C1\uC811 PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "folderUuid": { "description": "Optional folder UUID", "type": "string" }, "projectUuid": { "description": "Project UUID", "type": "string" }, "title": { "description": "Document title", "type": "string" }, "type": { "description": "Document type. Creatable values: erd, markdown, diagram, openapi, html.", "enum": ["erd", "markdown", "diagram", "openapi", "html"], "type": "string" }, "typePayload": { "description": "Type-specific payload for enabled document types. markdown: {body}; diagram: {diagramSource,sourceLanguage,edgeStyle,isModule,metadata}; openapi: {content}; html: {content} (a full HTML page).", "type": "object" } }, "required": ["projectUuid", "type", "title"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "105d051791cf595b" },
856
+ { "mcpName": "create_folders", "commandPath": ["folders", "create"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Folders", "items": { "properties": { "name": { "description": "Folder name", "type": "string" }, "parentFolderUuid": { "description": "Parent folder UUID", "type": "string" }, "projectUuid": { "description": "Project UUID", "type": "string" } }, "required": ["projectUuid", "name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "30f45b887259609f" },
857
+ { "mcpName": "create_projects", "commandPath": ["projects", "create"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Projects", "items": { "properties": { "description": { "description": "Description", "type": "string" }, "name": { "description": "Project name", "type": "string" }, "projectKey": { "description": "Unique project key", "type": "string" }, "workspaceUuid": { "description": "Workspace UUID \u2014 null uses user's default workspace", "type": "string" } }, "required": ["name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "edf5cf688d7c039a" },
858
+ { "mcpName": "create_workspaces", "commandPath": ["workspaces", "create"], "group": "workspace", "description": "", "inputSchema": { "properties": { "items": { "description": "Workspaces", "items": { "properties": { "description": { "description": "Description", "type": "string" }, "name": { "description": "Workspace name", "type": "string" } }, "required": ["name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "d9d1b0d561828b8e" },
859
+ { "mcpName": "delete_attachments", "commandPath": ["attachments", "delete"], "group": "attachment", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Attachment UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "b22c24bffdfe3d7d" },
860
+ { "mcpName": "delete_documents", "commandPath": ["documents", "delete"], "group": "document", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Document UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "3a725ae70956c270" },
861
+ { "mcpName": "delete_folders", "commandPath": ["folders", "delete"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Folder UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "2254cde89c9e8f06" },
862
+ { "mcpName": "delete_projects", "commandPath": ["projects", "delete"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Project UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "6521c33a28cb5465" },
863
+ { "mcpName": "delete_workspaces", "commandPath": ["workspaces", "delete"], "group": "workspace", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Workspace UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "3177868221137066" },
864
+ { "mcpName": "diagram_apply_changes", "commandPath": ["tools", "run", "diagram-apply-changes"], "group": "generic", "description": "Required single-call mutation path for diagram node/edge/group create/update/delete changes. Put nodes, edges, and groups in one request and call this once. Do not look for type-specific diagram CUD tools; this tool is the only diagram CUD entry point.", "inputSchema": { "properties": { "description": { "description": "Reason for batch (audit)", "type": "string" }, "diagramUuid": { "description": "Diagram UUID", "type": "string" }, "edges": { "description": "Edge operations", "items": { "properties": { "action": { "description": "Operation kind", "enum": ["create", "update", "delete"], "type": "string" }, "bridgeType": { "description": "Bridge type", "type": "string" }, "fromUuid": { "description": "Source", "type": "string" }, "label": { "description": "Edge label", "type": "string" }, "lineStyle": { "description": "Line style", "type": "string" }, "strokeColor": { "description": "Stroke color", "type": "string" }, "strokeWidth": { "description": "Stroke width", "type": "integer" }, "toUuid": { "description": "Target", "type": "string" }, "uuid": { "description": "Required for update/delete", "type": "string" } }, "required": ["action"], "type": "object" }, "type": "array" }, "groups": { "description": "Group operations", "items": { "properties": { "action": { "description": "Operation kind", "enum": ["create", "update", "delete"], "type": "string" }, "collapsed": { "description": "Collapsed state", "type": "boolean" }, "color": { "description": "Group color", "type": "string" }, "height": { "description": "Explicit height; omit to keep it derived from member nodes", "type": "number" }, "linkMode": { "description": "Link mode", "type": "string" }, "linkedDiagramUuid": { "description": "Linked diagram UUID", "type": "string" }, "name": { "description": "Group name", "type": "string" }, "parentGroupUuid": { "description": "Parent group UUID", "type": "string" }, "sortOrder": { "description": "Sort order", "type": "integer" }, "uuid": { "description": "Required for update/delete", "type": "string" }, "width": { "description": "Explicit width; omit to keep it derived from member nodes", "type": "number" } }, "required": ["action"], "type": "object" }, "type": "array" }, "nodes": { "description": "Node operations", "items": { "properties": { "action": { "description": "Operation kind", "enum": ["create", "update", "delete"], "type": "string" }, "fillColor": { "description": "Fill color", "type": "string" }, "groupUuid": { "description": "Group UUID; empty string clears membership", "type": "string" }, "height": { "description": "Height", "type": "number" }, "shape": { "description": "Node shape (start/end/diamond/cylinder/parallelogram/stadium/hexagon/rect/roundRect/...)", "enum": ["rect", "roundRect", "stadium", "diamond", "decision", "circle", "start", "doubleCircle", "end", "hexagon", "cylinder", "parallelogram"], "type": "string" }, "strokeColor": { "description": "Stroke color", "type": "string" }, "text": { "description": "Node text/label. Supports newlines (\\n) for multi-line display.", "maxLength": 4e3, "type": "string" }, "textColor": { "description": "Text color", "type": "string" }, "type": { "description": "Node type", "type": "string" }, "uuid": { "description": "Required for update/delete; optional for create", "type": "string" }, "width": { "description": "Width", "type": "number" }, "x": { "description": "X", "type": "number" }, "y": { "description": "Y", "type": "number" } }, "required": ["action"], "type": "object" }, "type": "array" } }, "required": ["diagramUuid", "description"], "type": "object" }, "required": ["description", "diagramUuid"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "add78d8edce88c86" },
865
+ { "mcpName": "diagram_get_edge", "commandPath": ["tools", "run", "diagram-get-edge"], "group": "generic", "description": "", "inputSchema": { "properties": { "edgeUuid": { "description": "Edge UUID", "type": "string" } }, "required": ["edgeUuid"], "type": "object" }, "required": ["edgeUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "2c03c4f7daa69898" },
866
+ { "mcpName": "diagram_get_group", "commandPath": ["tools", "run", "diagram-get-group"], "group": "generic", "description": "", "inputSchema": { "properties": { "groupUuid": { "description": "Group UUID", "type": "string" } }, "required": ["groupUuid"], "type": "object" }, "required": ["groupUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "55c5aa27b8cf489d" },
867
+ { "mcpName": "diagram_get_node", "commandPath": ["tools", "run", "diagram-get-node"], "group": "generic", "description": "", "inputSchema": { "properties": { "nodeUuid": { "description": "Node UUID", "type": "string" } }, "required": ["nodeUuid"], "type": "object" }, "required": ["nodeUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "646d553238328a9e" },
868
+ { "mcpName": "diagram_get_pending_llm_requests", "commandPath": ["tools", "run", "diagram-get-pending-llm-requests"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "2143d616cf7822bf" },
869
+ { "mcpName": "diagram_list_edges", "commandPath": ["tools", "run", "diagram-list-edges"], "group": "generic", "description": "", "inputSchema": { "properties": { "diagramUuid": { "description": "Diagram document UUID", "type": "string" } }, "required": ["diagramUuid"], "type": "object" }, "required": ["diagramUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4645085bde616381" },
870
+ { "mcpName": "diagram_list_groups", "commandPath": ["tools", "run", "diagram-list-groups"], "group": "generic", "description": "", "inputSchema": { "properties": { "diagramUuid": { "description": "Diagram document UUID", "type": "string" } }, "required": ["diagramUuid"], "type": "object" }, "required": ["diagramUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "8a7ca45dd34d1aef" },
871
+ { "mcpName": "diagram_list_nodes", "commandPath": ["tools", "run", "diagram-list-nodes"], "group": "generic", "description": "", "inputSchema": { "properties": { "diagramUuid": { "description": "Diagram document UUID", "type": "string" } }, "required": ["diagramUuid"], "type": "object" }, "required": ["diagramUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "d444066b5ca6eeb4" },
872
+ { "mcpName": "diagram_submit_llm_result", "commandPath": ["tools", "run", "diagram-submit-llm-result"], "group": "generic", "description": "", "inputSchema": { "properties": { "cachedInputTokens": { "description": "Cached subset of provider total input tokens", "type": "integer" }, "creditsCharged": { "description": "Deprecated; ignored. Credits are calculated server-side from tokens.", "type": "integer" }, "inputTokens": { "description": "Provider total input tokens; Java persists uncached input after one normalization", "type": "integer" }, "outputTokens": { "description": "Output token count", "type": "integer" }, "result": { "description": "LLM result text", "type": "string" }, "uuid": { "description": "LLM request UUID", "type": "string" } }, "required": ["uuid", "result"], "type": "object" }, "required": ["result", "uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "870ace32642a770a" },
873
+ { "mcpName": "diagram_update_llm_request_status", "commandPath": ["tools", "run", "diagram-update-llm-request-status"], "group": "generic", "description": "", "inputSchema": { "properties": { "errorMessage": { "description": "Error message (when status=FAILED)", "type": "string" }, "status": { "description": "Target status \u2014 currently only FAILED is mutable from caller side", "enum": ["FAILED"], "type": "string" }, "uuid": { "description": "LLM request UUID", "type": "string" } }, "required": ["uuid", "status"], "type": "object" }, "required": ["status", "uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "9ad96e3dc5fd2a8f" },
874
+ { "mcpName": "disable_shares", "commandPath": ["tools", "run", "disable-shares"], "group": "generic", "description": "", "inputSchema": { "properties": { "targetType": { "description": "Share target type", "enum": ["project", "folder", "document"], "type": "string" }, "targetUuids": { "description": "Target UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["targetType", "targetUuids"], "type": "object" }, "required": ["targetType", "targetUuids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "bd1b310200b78420" },
875
+ { "mcpName": "enable_shares", "commandPath": ["tools", "run", "enable-shares"], "group": "generic", "description": "", "inputSchema": { "properties": { "targetType": { "description": "Share target type. A document gets a shareUrl anyone can open; a project gets a public API path only (there is no public project page); a folder token is stored but nothing public reads it yet.", "enum": ["project", "folder", "document"], "type": "string" }, "targetUuids": { "description": "Target UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["targetType", "targetUuids"], "type": "object" }, "required": ["targetType", "targetUuids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "18559d7c0af09d2c" },
876
+ { "mcpName": "erd_apply_changes", "commandPath": ["erd", "apply-changes"], "group": "erd", "description": 'Required single-call mutation path for ERD create/update/delete changes. Put tables, columns, indexes, refs, enums, enum_values, and table groups in one operations array and call this once.\nRules: (1) all data field names are snake_case; (2) for create, generate your own UUIDs and put them in data.uuid, then later operations in the same batch may reference those UUIDs without read-back calls; (3) enum values are separate enum_value operations, not inline on enum; (4) ref.relationship uses short dbdiagram-style symbols: >, <, or -; (5) ref_column operations map FK columns explicitly, never automatically.\nDo not look for type-specific ERD CUD tools; this tool is the only ERD CUD entry point. If operations is very large, use request_upload_token with purpose=mcp.call and send the tools/call payload with curl to save LLM tokens.\nOne-call worked example:\n{\n "documentUuid": "erd-doc-uuid",\n "description": "create users/posts ERD",\n "operations": [\n {"op":"create","type":"table","data":{"uuid":"tbl_users","name":"users","display_order":1}},\n {"op":"create","type":"column","data":{"uuid":"col_users_id","table_uuid":"tbl_users","name":"id","type":"bigint","is_pk":true,"is_not_null":true,"display_order":1}},\n {"op":"create","type":"column","data":{"uuid":"col_users_email","table_uuid":"tbl_users","name":"email","type":"varchar(255)","is_unique":true,"is_not_null":true,"display_order":2}},\n {"op":"create","type":"table","data":{"uuid":"tbl_posts","name":"posts","display_order":2}},\n {"op":"create","type":"column","data":{"uuid":"col_posts_id","table_uuid":"tbl_posts","name":"id","type":"bigint","is_pk":true,"is_not_null":true,"display_order":1}},\n {"op":"create","type":"column","data":{"uuid":"col_posts_author_id","table_uuid":"tbl_posts","name":"author_id","type":"bigint","is_not_null":true,"display_order":2}},\n {"op":"create","type":"column","data":{"uuid":"col_posts_status","table_uuid":"tbl_posts","name":"status","type":"post_status","is_not_null":true,"display_order":3}},\n {"op":"create","type":"enum","data":{"uuid":"enum_post_status","name":"post_status"}},\n {"op":"create","type":"enum_value","data":{"uuid":"enum_post_status_draft","enum_uuid":"enum_post_status","value":"draft","display_order":1}},\n {"op":"create","type":"enum_value","data":{"uuid":"enum_post_status_published","enum_uuid":"enum_post_status","value":"published","display_order":2}},\n {"op":"create","type":"ref","data":{"uuid":"ref_posts_author","name":"fk_posts_author","from_table_uuid":"tbl_posts","to_table_uuid":"tbl_users","relationship":">","on_delete":"cascade"}},\n {"op":"create","type":"ref_column","data":{"uuid":"refcol_posts_author","ref_uuid":"ref_posts_author","from_column_uuid":"col_posts_author_id","to_column_uuid":"col_users_id","display_order":1}}\n ]\n}', "inputSchema": { "properties": { "arrangeNewTables": { "description": "Whether to auto-place newly-created tables after the batch; defaults to true", "type": "boolean" }, "description": { "description": "Reason for batch (audit)", "type": "string" }, "documentUuid": { "description": "ERD document UUID", "type": "string" }, "operations": { "description": "Atomic create/update/delete operations", "items": { "description": "One ERD mutation. Use a single operations array with client-generated data.uuid values to create cross-referenced tables, columns, enums, refs, and ref_columns without read-back calls.", "discriminator": { "propertyName": "type" }, "oneOf": [{ "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table'.", "properties": { "alias": { "description": "Display alias.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "header_color": { "description": "Header color.", "type": "string" }, "name": { "description": "Table name. Required for create.", "type": "string" }, "note": { "description": "Table note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["name"], "title": "table data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table'.", "properties": { "alias": { "description": "Display alias.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "header_color": { "description": "Header color.", "type": "string" }, "name": { "description": "Table name. Required for create.", "type": "string" }, "note": { "description": "Table note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table", "description": "Must be 'table' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='column'.", "properties": { "default_value": { "description": "Default value.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "is_increment": { "description": "Auto-increment flag.", "type": "boolean" }, "is_not_null": { "description": "Not-null flag.", "type": "boolean" }, "is_pk": { "description": "Primary key flag.", "type": "boolean" }, "is_unique": { "description": "Unique flag.", "type": "boolean" }, "name": { "description": "Column name.", "type": "string" }, "note": { "description": "Column note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create; may be a data.uuid from an earlier table operation in this batch.", "type": "string" }, "type": { "description": "SQL data type. Use this exact field name; do not send data_type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["table_uuid"], "title": "column data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='column'.", "properties": { "default_value": { "description": "Default value.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "is_increment": { "description": "Auto-increment flag.", "type": "boolean" }, "is_not_null": { "description": "Not-null flag.", "type": "boolean" }, "is_pk": { "description": "Primary key flag.", "type": "boolean" }, "is_unique": { "description": "Unique flag.", "type": "boolean" }, "name": { "description": "Column name.", "type": "string" }, "note": { "description": "Column note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create; may be a data.uuid from an earlier table operation in this batch.", "type": "string" }, "type": { "description": "SQL data type. Use this exact field name; do not send data_type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "column data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "column", "description": "Must be 'column' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "column operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='index'.", "properties": { "is_pk": { "description": "Primary key index flag.", "type": "boolean" }, "is_unique": { "description": "Unique index flag.", "type": "boolean" }, "name": { "description": "Index name.", "type": "string" }, "note": { "description": "Index note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create.", "type": "string" }, "type": { "description": "Index type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["table_uuid"], "title": "index data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='index'.", "properties": { "is_pk": { "description": "Primary key index flag.", "type": "boolean" }, "is_unique": { "description": "Unique index flag.", "type": "boolean" }, "name": { "description": "Index name.", "type": "string" }, "note": { "description": "Index note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create.", "type": "string" }, "type": { "description": "Index type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "index data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "index", "description": "Must be 'index' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "index operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='index_column'.", "properties": { "column_uuid": { "description": "Column UUID. Required for create.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "index_uuid": { "description": "Index UUID. Required for create.", "type": "string" }, "sort_order": { "description": "Sort order, for example ASC or DESC.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["index_uuid", "column_uuid"], "title": "index_column data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='index_column'.", "properties": { "column_uuid": { "description": "Column UUID. Required for create.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "index_uuid": { "description": "Index UUID. Required for create.", "type": "string" }, "sort_order": { "description": "Sort order, for example ASC or DESC.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "index_column data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "index_column", "description": "Must be 'index_column' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "index_column operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='ref'.", "properties": { "from_table_uuid": { "description": "Source table UUID. Required for create.", "type": "string" }, "name": { "description": "Reference name.", "type": "string" }, "on_delete": { "description": "ON DELETE action.", "type": "string" }, "on_update": { "description": "ON UPDATE action.", "type": "string" }, "relationship": { "description": "Required for create. Use short dbdiagram-style relationship symbols.", "enum": [">", "<", "-"], "type": "string" }, "to_table_uuid": { "description": "Target table UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["from_table_uuid", "to_table_uuid", "relationship"], "title": "ref data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='ref'.", "properties": { "from_table_uuid": { "description": "Source table UUID. Required for create.", "type": "string" }, "name": { "description": "Reference name.", "type": "string" }, "on_delete": { "description": "ON DELETE action.", "type": "string" }, "on_update": { "description": "ON UPDATE action.", "type": "string" }, "relationship": { "description": "Required for create. Use short dbdiagram-style relationship symbols.", "enum": [">", "<", "-"], "type": "string" }, "to_table_uuid": { "description": "Target table UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "ref data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "ref", "description": "Must be 'ref' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "ref operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='ref_column'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "from_column_uuid": { "description": "Source column UUID. Required for create.", "type": "string" }, "ref_uuid": { "description": "Reference UUID. Required for create; may be a data.uuid from an earlier ref operation.", "type": "string" }, "to_column_uuid": { "description": "Target column UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["ref_uuid", "from_column_uuid", "to_column_uuid"], "title": "ref_column data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='ref_column'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "from_column_uuid": { "description": "Source column UUID. Required for create.", "type": "string" }, "ref_uuid": { "description": "Reference UUID. Required for create; may be a data.uuid from an earlier ref operation.", "type": "string" }, "to_column_uuid": { "description": "Target column UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "ref_column data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "ref_column", "description": "Must be 'ref_column' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "ref_column operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='enum'.", "properties": { "name": { "description": "Enum name.", "type": "string" }, "note": { "description": "Enum note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "enum data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='enum'.", "properties": { "name": { "description": "Enum name.", "type": "string" }, "note": { "description": "Enum note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "enum data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "enum", "description": "Must be 'enum' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "enum operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='enum_value'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "enum_uuid": { "description": "Parent enum UUID. Required for create; may be a data.uuid from an earlier enum operation.", "type": "string" }, "note": { "description": "Enum value note.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" }, "value": { "description": "Enum value. Inline enum values on enum are not persisted; create enum_value operations.", "type": "string" } }, "required": ["enum_uuid"], "title": "enum_value data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='enum_value'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "enum_uuid": { "description": "Parent enum UUID. Required for create; may be a data.uuid from an earlier enum operation.", "type": "string" }, "note": { "description": "Enum value note.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" }, "value": { "description": "Enum value. Inline enum values on enum are not persisted; create enum_value operations.", "type": "string" } }, "title": "enum_value data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "enum_value", "description": "Must be 'enum_value' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "enum_value operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table_group'.", "properties": { "color": { "description": "Group color.", "type": "string" }, "name": { "description": "Table group name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table_group'.", "properties": { "color": { "description": "Group color.", "type": "string" }, "name": { "description": "Table group name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table_group", "description": "Must be 'table_group' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table_group operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table_group_table'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "schema_name": { "description": "Table schema name.", "type": "string" }, "table_name": { "description": "Table name. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["group_uuid", "table_name"], "title": "table_group_table data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table_group_table'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "schema_name": { "description": "Table schema name.", "type": "string" }, "table_name": { "description": "Table name. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group_table data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table_group_table", "description": "Must be 'table_group_table' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table_group_table operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='table_group_note'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "note_uuid": { "description": "Note UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "required": ["group_uuid", "note_uuid"], "title": "table_group_note data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='table_group_note'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "note_uuid": { "description": "Note UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group_note data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "table_group_note", "description": "Must be 'table_group_note' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "table_group_note operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='filter'.", "properties": { "included_tables": { "description": "JSON string/list of included tables.", "type": "string" }, "is_default": { "description": "Default filter flag.", "type": "boolean" }, "name": { "description": "Filter name.", "type": "string" }, "table_groups": { "description": "JSON string/object of table groups.", "type": "string" }, "table_positions": { "description": "JSON string/object of table positions.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "filter data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='filter'.", "properties": { "included_tables": { "description": "JSON string/list of included tables.", "type": "string" }, "is_default": { "description": "Default filter flag.", "type": "boolean" }, "name": { "description": "Filter name.", "type": "string" }, "table_groups": { "description": "JSON string/object of table groups.", "type": "string" }, "table_positions": { "description": "JSON string/object of table positions.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "filter data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "filter", "description": "Must be 'filter' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "filter operation", "type": "object" }, { "allOf": [{ "if": { "properties": { "op": { "const": "create", "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": { "data": { "description": "Payload for operation.type='note'.", "properties": { "color": { "description": "Note color.", "type": "string" }, "content": { "description": "Note content.", "type": "string" }, "height": { "description": "Height.", "type": "integer" }, "icon_name": { "description": "Icon name.", "type": "string" }, "position_x": { "description": "X position.", "type": "integer" }, "position_y": { "description": "Y position.", "type": "integer" }, "title": { "description": "Note title.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" }, "width": { "description": "Width.", "type": "integer" } }, "title": "note data", "type": "object" } }, "required": ["data"], "type": "object" } }, { "if": { "properties": { "op": { "enum": ["update", "delete"], "type": "string" } }, "required": ["op"], "type": "object" }, "then": { "properties": {}, "required": ["uuid"], "type": "object" } }], "properties": { "data": { "description": "Payload for operation.type='note'.", "properties": { "color": { "description": "Note color.", "type": "string" }, "content": { "description": "Note content.", "type": "string" }, "height": { "description": "Height.", "type": "integer" }, "icon_name": { "description": "Icon name.", "type": "string" }, "position_x": { "description": "X position.", "type": "integer" }, "position_y": { "description": "Y position.", "type": "integer" }, "title": { "description": "Note title.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" }, "width": { "description": "Width.", "type": "integer" } }, "title": "note data", "type": "object" }, "op": { "description": "Operation", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "const": "note", "description": "Must be 'note' for this payload.", "type": "string" }, "uuid": { "description": "Target UUID for update/delete.", "type": "string" } }, "required": ["op", "type"], "title": "note operation", "type": "object" }], "properties": { "data": { "description": "Type-specific payload. All field names are snake_case. For create, data.uuid may be client-supplied and referenced by later operations in the same batch.", "oneOf": [{ "description": "Payload for operation.type='table'.", "properties": { "alias": { "description": "Display alias.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "header_color": { "description": "Header color.", "type": "string" }, "name": { "description": "Table name. Required for create.", "type": "string" }, "note": { "description": "Table note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table data", "type": "object" }, { "description": "Payload for operation.type='column'.", "properties": { "default_value": { "description": "Default value.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "is_increment": { "description": "Auto-increment flag.", "type": "boolean" }, "is_not_null": { "description": "Not-null flag.", "type": "boolean" }, "is_pk": { "description": "Primary key flag.", "type": "boolean" }, "is_unique": { "description": "Unique flag.", "type": "boolean" }, "name": { "description": "Column name.", "type": "string" }, "note": { "description": "Column note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create; may be a data.uuid from an earlier table operation in this batch.", "type": "string" }, "type": { "description": "SQL data type. Use this exact field name; do not send data_type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "column data", "type": "object" }, { "description": "Payload for operation.type='index'.", "properties": { "is_pk": { "description": "Primary key index flag.", "type": "boolean" }, "is_unique": { "description": "Unique index flag.", "type": "boolean" }, "name": { "description": "Index name.", "type": "string" }, "note": { "description": "Index note.", "type": "string" }, "table_uuid": { "description": "Parent table UUID. Required for create.", "type": "string" }, "type": { "description": "Index type.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "index data", "type": "object" }, { "description": "Payload for operation.type='index_column'.", "properties": { "column_uuid": { "description": "Column UUID. Required for create.", "type": "string" }, "display_order": { "description": "Display order.", "type": "integer" }, "index_uuid": { "description": "Index UUID. Required for create.", "type": "string" }, "sort_order": { "description": "Sort order, for example ASC or DESC.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "index_column data", "type": "object" }, { "description": "Payload for operation.type='ref'.", "properties": { "from_table_uuid": { "description": "Source table UUID. Required for create.", "type": "string" }, "name": { "description": "Reference name.", "type": "string" }, "on_delete": { "description": "ON DELETE action.", "type": "string" }, "on_update": { "description": "ON UPDATE action.", "type": "string" }, "relationship": { "description": "Required for create. Use short dbdiagram-style relationship symbols.", "enum": [">", "<", "-"], "type": "string" }, "to_table_uuid": { "description": "Target table UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "ref data", "type": "object" }, { "description": "Payload for operation.type='ref_column'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "from_column_uuid": { "description": "Source column UUID. Required for create.", "type": "string" }, "ref_uuid": { "description": "Reference UUID. Required for create; may be a data.uuid from an earlier ref operation.", "type": "string" }, "to_column_uuid": { "description": "Target column UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "ref_column data", "type": "object" }, { "description": "Payload for operation.type='enum'.", "properties": { "name": { "description": "Enum name.", "type": "string" }, "note": { "description": "Enum note.", "type": "string" }, "schema_name": { "description": "Schema name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "enum data", "type": "object" }, { "description": "Payload for operation.type='enum_value'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "enum_uuid": { "description": "Parent enum UUID. Required for create; may be a data.uuid from an earlier enum operation.", "type": "string" }, "note": { "description": "Enum value note.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" }, "value": { "description": "Enum value. Inline enum values on enum are not persisted; create enum_value operations.", "type": "string" } }, "title": "enum_value data", "type": "object" }, { "description": "Payload for operation.type='table_group'.", "properties": { "color": { "description": "Group color.", "type": "string" }, "name": { "description": "Table group name.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group data", "type": "object" }, { "description": "Payload for operation.type='table_group_table'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "schema_name": { "description": "Table schema name.", "type": "string" }, "table_name": { "description": "Table name. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group_table data", "type": "object" }, { "description": "Payload for operation.type='table_group_note'.", "properties": { "display_order": { "description": "Display order.", "type": "integer" }, "group_uuid": { "description": "Table group UUID. Required for create.", "type": "string" }, "note_uuid": { "description": "Note UUID. Required for create.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "table_group_note data", "type": "object" }, { "description": "Payload for operation.type='filter'.", "properties": { "included_tables": { "description": "JSON string/list of included tables.", "type": "string" }, "is_default": { "description": "Default filter flag.", "type": "boolean" }, "name": { "description": "Filter name.", "type": "string" }, "table_groups": { "description": "JSON string/object of table groups.", "type": "string" }, "table_positions": { "description": "JSON string/object of table positions.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" } }, "title": "filter data", "type": "object" }, { "description": "Payload for operation.type='note'.", "properties": { "color": { "description": "Note color.", "type": "string" }, "content": { "description": "Note content.", "type": "string" }, "height": { "description": "Height.", "type": "integer" }, "icon_name": { "description": "Icon name.", "type": "string" }, "position_x": { "description": "X position.", "type": "integer" }, "position_y": { "description": "Y position.", "type": "integer" }, "title": { "description": "Note title.", "type": "string" }, "uuid": { "description": "Optional on create: client-generated UUID (at most 36 characters, e.g. a standard UUID v4). If supplied, later operations in the same batch can reference it. Do not put create UUIDs in operation.uuid; operation.uuid is only the update/delete target.", "maxLength": 36, "type": "string" }, "width": { "description": "Width.", "type": "integer" } }, "title": "note data", "type": "object" }], "type": "object" }, "op": { "description": "Operation. For create, put any new UUID in data.uuid; for update/delete, put target UUID in operation.uuid.", "enum": ["create", "update", "delete"], "type": "string" }, "type": { "description": "ERD entity type. This selects the operation.data shape.", "enum": ["table", "column", "index", "index_column", "ref", "ref_column", "enum", "enum_value", "table_group", "table_group_table", "table_group_note", "filter", "note"], "type": "string" }, "uuid": { "description": "Target UUID for update/delete. Do not use this for create; use data.uuid instead.", "type": "string" } }, "required": ["op", "type"], "type": "object" }, "type": "array" } }, "required": ["documentUuid", "description", "operations"], "type": "object" }, "required": ["description", "documentUuid", "operations"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "87f5bcf5a253815a" },
877
+ { "mcpName": "erd_arrange_tables", "commandPath": ["erd", "arrange-tables"], "group": "erd", "description": "Arrange ERD tables after edits. Default incremental mode places newly-created tables while preserving existing positions; full=true re-layouts the entire diagram. Use after several separate table edits to tidy the canvas.", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "full": { "description": "Re-layout the ENTIRE diagram (overwrites existing positions). Default false = arrange only newly-added (uncoordinated) tables to the top-right while preserving existing table positions. Call this after creating several tables to tidy them up.", "type": "boolean" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "651fd0d54e3635ff" },
878
+ { "mcpName": "erd_get_enum", "commandPath": ["erd", "get-enum"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "enumUuid": { "description": "Enum UUID", "type": "string" } }, "required": ["documentUuid", "enumUuid"], "type": "object" }, "required": ["documentUuid", "enumUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "f0d79d7a1996939c" },
879
+ { "mcpName": "erd_get_ref", "commandPath": ["erd", "get-ref"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "refUuid": { "description": "Relationship UUID", "type": "string" } }, "required": ["documentUuid", "refUuid"], "type": "object" }, "required": ["documentUuid", "refUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "5b47fe7d99e60d4d" },
880
+ { "mcpName": "erd_get_table", "commandPath": ["erd", "get-table"], "group": "erd", "description": "Get one ERD table with nested columns and indexes. Returns persistent table/column/index UUIDs usable directly as erd_apply_changes update/delete targets and ref_column column references. Column fields use apply_changes snake_case names: uuid, table_uuid, name, type, is_pk, is_unique, is_not_null, is_increment, default_value, note, display_order. Index fields include uuid, name, columns (column UUIDs), is_unique, is_pk, type.", "inputSchema": { "description": "Get one ERD table with nested columns and indexes. Returns persistent table/column/index UUIDs usable directly as mcp__ainecto__erd_apply_changes update/delete targets and ref_column column references. Column fields use apply_changes snake_case names: uuid, table_uuid, name, type, is_pk, is_unique, is_not_null, is_increment, default_value, note, display_order. Index fields include uuid, name, columns (column UUIDs), is_unique, is_pk, type.", "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "tableUuid": { "description": "Table UUID. Response includes columns[] and indexes[] with persistent UUIDs for apply_changes.", "type": "string" } }, "required": ["documentUuid", "tableUuid"], "type": "object" }, "required": ["documentUuid", "tableUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "0dc13648bf3fbf8f" },
881
+ { "mcpName": "erd_get_table_group", "commandPath": ["erd", "get-table-group"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" }, "tableGroupUuid": { "description": "Table group UUID", "type": "string" } }, "required": ["documentUuid", "tableGroupUuid"], "type": "object" }, "required": ["documentUuid", "tableGroupUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "3db246dfe8663370" },
882
+ { "mcpName": "erd_list_enums", "commandPath": ["erd", "list-enums"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4d1f602fa487a0e4" },
883
+ { "mcpName": "erd_list_refs", "commandPath": ["erd", "list-refs"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "307d766c777bbe8c" },
884
+ { "mcpName": "erd_list_table_groups", "commandPath": ["erd", "list-table-groups"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "775bff53bbc21c9e" },
885
+ { "mcpName": "erd_list_tables", "commandPath": ["erd", "list-tables"], "group": "erd", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "ERD document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "34324dfc7ef461c8" },
886
+ { "mcpName": "get_attachment", "commandPath": ["attachment", "get"], "group": "attachment", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Attachment UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "1e0e7f1bf33b669c" },
887
+ { "mcpName": "get_document", "commandPath": ["document", "get"], "group": "document", "description": "Read a document. The body comes back for the types whose create/update take one: markdown \u2192 content; openapi \u2192 content (the spec text) + format/specVersion/pathCount/operationCount; html \u2192 content (the HTML text); diagram \u2192 diagramSource/sourceLanguage/edgeStyle/isModule/metadata; erd \u2192 metadata only (read tables/refs via erd_list_*).", "inputSchema": { "properties": { "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "5de278aad6b11bc5" },
888
+ { "mcpName": "get_project", "commandPath": ["project", "get"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Project UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "192403c3484ac774" },
889
+ { "mcpName": "get_version", "commandPath": ["version", "get"], "group": "generic", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "Document UUID", "type": "string" }, "versionNumber": { "description": "Version number", "type": "integer" } }, "required": ["documentUuid", "versionNumber"], "type": "object" }, "required": ["documentUuid", "versionNumber"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4e308c1a8c7203d1" },
890
+ { "mcpName": "get_workspace", "commandPath": ["workspace", "get"], "group": "workspace", "description": "", "inputSchema": { "properties": { "uuid": { "description": "Workspace UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "required": ["uuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "db6d1b4879d57c56" },
891
+ { "mcpName": "identity_get_self", "commandPath": ["tools", "run", "identity-get-self"], "group": "generic", "description": "Return the authenticated MCP caller identity summary; never returns email or token claims.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "f29c86804eb91700" },
892
+ { "mcpName": "list_attachments", "commandPath": ["attachments", "list"], "group": "attachment", "description": "", "inputSchema": { "properties": { "projectUuid": { "description": "Project UUID", "type": "string" } }, "required": ["projectUuid"], "type": "object" }, "required": ["projectUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "fae7152d1e4a930b" },
893
+ { "mcpName": "list_documents", "commandPath": ["documents", "list"], "group": "document", "description": "", "inputSchema": { "properties": { "folderUuid": { "description": "Optional folder UUID", "type": "string" }, "limit": { "description": "Result cap when q is set (default 50)", "type": "integer" }, "projectUuid": { "description": "Project UUID", "type": "string" }, "q": { "description": "Optional full-text query. When provided, results come from the search index instead of folder listing.", "type": "string" }, "type": { "description": "Optional document type filter. Readable values: erd, markdown, diagram, openapi, html.", "enum": ["erd", "markdown", "diagram", "openapi", "html"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4086c52be4dd0c15" },
894
+ { "mcpName": "list_folders", "commandPath": ["folders", "list"], "group": "generic", "description": "", "inputSchema": { "properties": { "projectUuid": { "description": "Project UUID", "type": "string" } }, "required": ["projectUuid"], "type": "object" }, "required": ["projectUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "461dc6735ac92c3b" },
895
+ { "mcpName": "list_projects", "commandPath": ["projects", "list"], "group": "generic", "description": "", "inputSchema": { "properties": { "limit": { "description": "Result cap when q is set (default 50)", "type": "integer" }, "q": { "description": "Optional full-text query", "type": "string" }, "workspaceUuid": { "description": "Optional Workspace UUID", "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "939bfcc088425771" },
896
+ { "mcpName": "list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "329250aecc06a298" },
897
+ { "mcpName": "list_versions", "commandPath": ["versions", "list"], "group": "generic", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "Document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "f08f9faee3ba9107" },
898
+ { "mcpName": "list_workspaces", "commandPath": ["workspaces", "list"], "group": "workspace", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "39dba0089fdbdf6a" },
899
+ { "mcpName": "markdown_append_document", "commandPath": ["markdown", "append-document"], "group": "markdown", "description": "Append to Markdown documents. If the appended body is tens of KB or more, prefer request_upload_token (purpose=document.append) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Appends", "items": { "properties": { "addition": { "description": "Body addition. \uCD94\uAC00\uD560 \uB0B4\uC6A9\uC774 \uD06C\uBA74 (\uC218\uC2ED KB \uC774\uC0C1) LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.append, scopeJson={documentUuid, separator})` \uB85C uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "separator": { "description": "Separator between existing body and addition", "type": "string" }, "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid", "addition"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "bf82401f055f29b0" },
900
+ { "mcpName": "markdown_generate_document", "commandPath": ["markdown", "generate-document"], "group": "markdown", "description": "", "inputSchema": { "properties": { "items": { "description": "Generation requests", "items": { "properties": { "context": { "description": "Optional JSON context", "type": "string" }, "documentUuid": { "description": "Target document UUID", "type": "string" }, "prompt": { "description": "Generation prompt", "type": "string" } }, "required": ["documentUuid", "prompt"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "1e4724ea42f25743" },
901
+ { "mcpName": "move_documents", "commandPath": ["documents", "move"], "group": "document", "description": "", "inputSchema": { "properties": { "clientMutationId": { "description": "Optional mutation identifier for client retry tracing", "maxLength": 128, "minLength": 1, "pattern": "^[\\x21-\\x7E]{1,128}$", "type": "string" }, "items": { "description": "Moves", "items": { "properties": { "newFolderUuid": { "description": "New folder UUID \u2014 null/empty for root", "type": "string" }, "targetProjectUuid": { "description": "Target project UUID \u2014 omit to stay in current project", "type": "string" }, "targetWorkspaceUuid": { "description": "Guard only: must match targetProjectUuid workspace", "type": "string" }, "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "45899053743a0c1e" },
902
+ { "mcpName": "move_folders", "commandPath": ["folders", "move"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Moves", "items": { "properties": { "newParentFolderUuid": { "description": "New parent UUID \u2014 null/empty for root", "type": "string" }, "uuid": { "description": "Folder UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "eff4458d2f35587a" },
903
+ { "mcpName": "move_projects", "commandPath": ["projects", "move"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Moves", "items": { "properties": { "targetWorkspaceUuid": { "description": "Destination workspace UUID (you need write access there)", "type": "string" }, "uuid": { "description": "Project UUID", "type": "string" } }, "required": ["uuid", "targetWorkspaceUuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "abe0196da3c48ad5" },
904
+ { "mcpName": "request_upload_token", "commandPath": ["tools", "run", "request-upload-token"], "group": "attachment", "description": "Use this for large payloads of tens of KB or more to save LLM tokens. purpose=mcp.call returns a short-lived bearer token that can call any MCP tool, including erd_apply_changes, via curl. purpose=document.create/update/append uploads document bodies directly to the upload endpoint with PUT/curl. Tokens are reusable for 5 minutes by default.", "inputSchema": { "properties": { "maxBytes": { "description": "\uCD5C\uB300 \uC5C5\uB85C\uB4DC byte (\uC11C\uBC84 \uCE21 \uD55C\uB3C4)", "type": "integer" }, "purpose": { "description": "Token \uC6A9\uB3C4. `mcp.call` \uC740 curl \uB85C /mcp \uD638\uCD9C, `document.create/update/append` \uB294 PUT body \uB85C \uBCF8\uBB38 \uC5C5\uB85C\uB4DC, `attachment.upload` \uB294 \uD30C\uC77C bytes \uC5C5\uB85C\uB4DC. \uCF58\uD150\uCE20\uAC00 \uD06C\uBA74 (\uC218\uC2ED KB \uC774\uC0C1) LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 \uC774 \uB3C4\uAD6C\uB85C 5\uBD84 \uC7AC\uC0AC\uC6A9 \uD1A0\uD070\uC744 \uBC1B\uACE0 curl \uB85C \uBCF4\uB0B4\uB294 \uAC83\uC744 \uAC15\uB825 \uAD8C\uC7A5.", "enum": ["mcp.call", "document.create", "document.update", "document.append", "attachment.upload"], "type": "string" }, "scopeJson": { "description": "\uC120\uD0DD \u2014 JSON scope. document.create \uB294 projectUuid, type, title \uC744 \uD3EC\uD568\uD558\uACE0 creatable type values \uB294 erd, markdown, diagram, openapi, html. document.update/document.append \uB294 documentUuid \uB97C \uD3EC\uD568. attachment.upload typed: {parentType:TEST_CASE|TEST_EXECUTION,parentUuid,filename,contentType,sizeBytes}.", "type": "string" }, "ttlSeconds": { "description": "TTL (\uCD08). \uAE30\uBCF8 300 (5\uBD84)", "type": "integer" } }, "required": ["purpose"], "type": "object" }, "required": ["purpose"], "payloadMode": "binary-upload", "destructive": false, "sourceCatalog": "prod", "schemaHash": "85867a646f6c652f" },
905
+ { "mcpName": "restore_versions", "commandPath": ["versions", "restore"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Restores", "items": { "properties": { "documentUuid": { "description": "Document UUID", "type": "string" }, "versionNumber": { "description": "Version number to restore", "type": "integer" } }, "required": ["documentUuid", "versionNumber"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "005b5b08a19fc605" },
906
+ { "mcpName": "search_documents", "commandPath": ["documents", "search"], "group": "document", "description": "", "inputSchema": { "properties": { "limit": { "description": "\uACB0\uACFC \uAC1C\uC218 (\uAE30\uBCF8 20, \uCD5C\uB300 50)", "type": "integer" }, "offset": { "description": "\uD398\uC774\uC9C0\uB124\uC774\uC158 offset (\uAE30\uBCF8 0)", "type": "integer" }, "projectUuid": { "description": "\uD2B9\uC815 \uD504\uB85C\uC81D\uD2B8\uB85C \uD55C\uC815 (\uC120\uD0DD)", "type": "string" }, "q": { "description": "\uAC80\uC0C9 \uD0A4\uC6CC\uB4DC", "type": "string" }, "type": { "description": "Optional document type filter. Readable values: erd, markdown, diagram, openapi, html.", "enum": ["erd", "markdown", "diagram", "openapi", "html"], "type": "string" } }, "required": ["q"], "type": "object" }, "required": ["q"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "d24e109383700433" },
907
+ { "mcpName": "unarchive_documents", "commandPath": ["documents", "unarchive"], "group": "document", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Document UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "f2810e71583a320f" },
908
+ { "mcpName": "unarchive_folders", "commandPath": ["folders", "unarchive"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Folder UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "2a24cc44696b7906" },
909
+ { "mcpName": "unarchive_projects", "commandPath": ["projects", "unarchive"], "group": "generic", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Project UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "6925e444671afe19" },
910
+ { "mcpName": "unarchive_workspaces", "commandPath": ["workspaces", "unarchive"], "group": "workspace", "description": "", "inputSchema": { "properties": { "uuids": { "description": "Workspace UUIDs", "items": { "type": "string" }, "type": "array" } }, "required": ["uuids"], "type": "object" }, "required": ["uuids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "8dafe9bc6378c3f2" },
911
+ { "mcpName": "update_documents", "commandPath": ["documents", "update"], "group": "document", "description": "Update documents. If a replacement body is tens of KB or more, prefer request_upload_token (purpose=document.update) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "body": { "description": "Deprecated compatibility alias for typePayload.body when document type is markdown. \uBCF8\uBB38\uC774 \uC218\uC2ED KB \uC774\uC0C1\uC73C\uB85C \uD06C\uBA74 LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.update)` \uB97C \uD638\uCD9C\uD574 uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C \uC9C1\uC811 PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "title": { "description": "New title", "type": "string" }, "typePayload": { "description": "Type-specific payload for enabled document types. markdown: {body}; diagram: {diagramSource,sourceLanguage,edgeStyle,isModule,metadata}; openapi: {content}; html: {content} (a full HTML page).", "type": "object" }, "uuid": { "description": "Document UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4c3cfe4d25def672" },
912
+ { "mcpName": "update_folders", "commandPath": ["folders", "update"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "name": { "description": "New name", "type": "string" }, "uuid": { "description": "Folder UUID", "type": "string" } }, "required": ["uuid", "name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "43c58a96ee632b5f" },
913
+ { "mcpName": "update_projects", "commandPath": ["projects", "update"], "group": "generic", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "description": { "description": "New description", "type": "string" }, "name": { "description": "New name", "type": "string" }, "uuid": { "description": "Project UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "7d849ef0a830aa8c" },
914
+ { "mcpName": "update_workspaces", "commandPath": ["workspaces", "update"], "group": "workspace", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "description": { "description": "New description", "type": "string" }, "name": { "description": "New name", "type": "string" }, "uuid": { "description": "Workspace UUID", "type": "string" } }, "required": ["uuid"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "553275e6da799c5a" },
915
+ { "mcpName": "upload_attachments", "commandPath": ["tools", "run", "upload-attachments"], "group": "attachment", "description": "", "inputSchema": { "properties": { "items": { "description": "Attachments to register", "items": { "properties": { "contentType": { "description": "MIME type", "type": "string" }, "documentUuid": { "description": "Parent document UUID", "type": "string" }, "filename": { "description": "Original file name", "type": "string" }, "sizeBytes": { "description": "Size in bytes", "type": "integer" }, "storageKey": { "description": "Storage key returned by request_upload_token purpose=attachment.upload", "type": "string" } }, "required": ["documentUuid", "filename", "storageKey"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "binary-upload", "destructive": false, "sourceCatalog": "prod", "schemaHash": "deda4a56fbbe064f" }
783
916
  ];
784
917
 
785
918
  // src/core/catalog/generated.dev.ts
@@ -872,10 +1005,147 @@ var generatedTools2 = [
872
1005
  { "mcpName": "mcp__ainecto__upload_attachments", "commandPath": ["tools", "run", "upload-attachments"], "group": "attachment", "description": "", "inputSchema": { "properties": { "items": { "description": "Attachments to register", "items": { "properties": { "contentType": { "description": "MIME type", "type": "string" }, "documentUuid": { "description": "Parent document UUID", "type": "string" }, "filename": { "description": "Original file name", "type": "string" }, "sizeBytes": { "description": "Size in bytes", "type": "integer" }, "storageKey": { "description": "Storage key returned by request_upload_token purpose=attachment.upload", "type": "string" } }, "required": ["documentUuid", "filename", "storageKey"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "binary-upload", "destructive": false, "sourceCatalog": "dev", "schemaHash": "0622e590de3404dd" }
873
1006
  ];
874
1007
 
1008
+ // src/core/catalog/generator.ts
1009
+ import { createHash as createHash3 } from "crypto";
1010
+ var LEGACY_PREFIX = "mcp__ainecto__";
1011
+ function bareToolName(name) {
1012
+ return name.startsWith(LEGACY_PREFIX) ? name.slice(LEGACY_PREFIX.length) : name;
1013
+ }
1014
+ var DESTRUCTIVE_PREFIXES = [
1015
+ "delete_",
1016
+ "remove_",
1017
+ "archive_",
1018
+ "unarchive_",
1019
+ "restore_",
1020
+ "move_",
1021
+ "erd_delete_",
1022
+ "erd_remove_",
1023
+ "task_delete_",
1024
+ "testcase_delete_"
1025
+ ];
1026
+ function generateCatalog(tools, sourceCatalog) {
1027
+ return tools.map((tool) => normalizeTool(tool, sourceCatalog)).sort((a, b) => a.mcpName.localeCompare(b.mcpName));
1028
+ }
1029
+ function normalizeTool(tool, sourceCatalog) {
1030
+ if (!tool.name) {
1031
+ throw new Error("Unsupported tool name: empty.");
1032
+ }
1033
+ const stripped = bareToolName(tool.name);
1034
+ const group = inferGroup(stripped);
1035
+ const inputSchema = normalizeSchema(tool.inputSchema ?? { type: "object", properties: {} });
1036
+ const required = Array.isArray(inputSchema.required) ? inputSchema.required.filter((value) => typeof value === "string").sort() : [];
1037
+ const definition = {
1038
+ mcpName: tool.name,
1039
+ commandPath: generateCommandPath(stripped, group),
1040
+ group,
1041
+ description: tool.description ?? "",
1042
+ inputSchema,
1043
+ required,
1044
+ payloadMode: inferPayloadMode(stripped, inputSchema),
1045
+ destructive: inferDestructive(stripped, tool),
1046
+ sourceCatalog,
1047
+ schemaHash: ""
1048
+ };
1049
+ return { ...definition, schemaHash: computeSchemaHash(definition) };
1050
+ }
1051
+ function inferGroup(strippedName) {
1052
+ if (strippedName.startsWith("erd_")) return "erd";
1053
+ if (strippedName.startsWith("flow_")) return "flow";
1054
+ if (strippedName.startsWith("testcase_")) return "testcase";
1055
+ if (strippedName.startsWith("markdown_")) return "markdown";
1056
+ if (strippedName.includes("attachment") || strippedName.includes("upload")) return "attachment";
1057
+ if (strippedName.startsWith("task_")) return "task";
1058
+ if (strippedName.includes("workspace")) return "workspace";
1059
+ if (strippedName.includes("document")) return "document";
1060
+ return "generic";
1061
+ }
1062
+ function generateCommandPath(strippedName, group) {
1063
+ if (group !== "generic" && strippedName.startsWith(`${group}_`)) {
1064
+ return [group, snakeToKebab(strippedName.slice(group.length + 1))];
1065
+ }
1066
+ const [verb, ...rest] = strippedName.split("_");
1067
+ if (verb && rest.length > 0 && isVerb(verb)) {
1068
+ return [rest.join("-"), verb];
1069
+ }
1070
+ return ["tools", "run", snakeToKebab(strippedName)];
1071
+ }
1072
+ function inferPayloadMode(strippedName, inputSchema) {
1073
+ if (strippedName.includes("upload") || strippedName.includes("attachment_upload")) {
1074
+ return "binary-upload";
1075
+ }
1076
+ const properties = inputSchema.properties;
1077
+ if (!isRecord3(properties)) {
1078
+ return "flags";
1079
+ }
1080
+ const values = Object.values(properties);
1081
+ if (values.length === 0) {
1082
+ return "flags";
1083
+ }
1084
+ if (values.every(isScalarSchema)) {
1085
+ return "flags";
1086
+ }
1087
+ return "file-json";
1088
+ }
1089
+ function inferDestructive(strippedName, tool) {
1090
+ if (tool.annotations?.destructiveHint === true) {
1091
+ return true;
1092
+ }
1093
+ return DESTRUCTIVE_PREFIXES.some((prefix) => strippedName.startsWith(prefix));
1094
+ }
1095
+ function computeSchemaHash(tool) {
1096
+ return createHash3("sha256").update(stableStringify({
1097
+ name: tool.mcpName,
1098
+ description: tool.description,
1099
+ inputSchema: tool.inputSchema
1100
+ })).digest("hex").slice(0, 16);
1101
+ }
1102
+ function stableStringify(value) {
1103
+ if (Array.isArray(value)) {
1104
+ return `[${value.map(stableStringify).join(",")}]`;
1105
+ }
1106
+ if (isRecord3(value)) {
1107
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
1108
+ }
1109
+ return JSON.stringify(value);
1110
+ }
1111
+ function normalizeSchema(schema) {
1112
+ return JSON.parse(stableStringify(schema));
1113
+ }
1114
+ function isScalarSchema(value) {
1115
+ if (!isRecord3(value)) {
1116
+ return false;
1117
+ }
1118
+ const type = value.type;
1119
+ return type === "string" || type === "number" || type === "integer" || type === "boolean";
1120
+ }
1121
+ function isVerb(value) {
1122
+ return [
1123
+ "list",
1124
+ "get",
1125
+ "create",
1126
+ "update",
1127
+ "delete",
1128
+ "move",
1129
+ "restore",
1130
+ "archive",
1131
+ "unarchive",
1132
+ "search"
1133
+ ].includes(value);
1134
+ }
1135
+ function snakeToKebab(value) {
1136
+ return value.replaceAll("_", "-");
1137
+ }
1138
+ function isRecord3(value) {
1139
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1140
+ }
1141
+
875
1142
  // src/core/catalog/index.ts
876
1143
  function getGeneratedTools(env) {
877
1144
  return env === "dev" ? generatedTools2 : generatedTools;
878
1145
  }
1146
+ async function loadLiveTools(env, client) {
1147
+ return generateCatalog(await client.toolsList(), env);
1148
+ }
879
1149
 
880
1150
  // src/core/mcp/stdioBridge.ts
881
1151
  import { createInterface } from "readline";
@@ -905,7 +1175,7 @@ async function runStdioBridge(options) {
905
1175
  }
906
1176
  function parseJsonRpcRequest(line) {
907
1177
  const parsed = JSON.parse(line);
908
- if (!isRecord3(parsed) || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
1178
+ if (!isRecord4(parsed) || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
909
1179
  throw new Error("Invalid JSON-RPC request.");
910
1180
  }
911
1181
  return {
@@ -915,14 +1185,13 @@ function parseJsonRpcRequest(line) {
915
1185
  params: parsed.params
916
1186
  };
917
1187
  }
918
- function isRecord3(value) {
1188
+ function isRecord4(value) {
919
1189
  return typeof value === "object" && value !== null && !Array.isArray(value);
920
1190
  }
921
1191
 
922
1192
  // src/adapters/mcp/connector.ts
923
1193
  async function runConnector(options) {
924
- const tokenStore = new FileTokenStore();
925
- const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore, role: options.role });
1194
+ const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore: new FileTokenStore() });
926
1195
  const client = new McpRpcClient({
927
1196
  endpoint: options.endpoint,
928
1197
  tokenProvider: auth,
@@ -939,85 +1208,85 @@ async function runConnector(options) {
939
1208
  // src/core/catalog/enrichments.ts
940
1209
  var enrichments = [
941
1210
  {
942
- mcpName: "mcp__ainecto__list_projects",
1211
+ mcpName: "list_projects",
943
1212
  outputHint: "table",
944
1213
  tableColumns: ["uuid", "name", "projectKey", "workspaceUuid", "updatedAt"],
945
1214
  displayName: "List projects",
946
1215
  examples: ["ainecto projects list --env dev"]
947
1216
  },
948
1217
  {
949
- mcpName: "mcp__ainecto__list_workspaces",
1218
+ mcpName: "list_workspaces",
950
1219
  outputHint: "table",
951
1220
  tableColumns: ["uuid", "name", "updatedAt"],
952
1221
  displayName: "List workspaces"
953
1222
  },
954
1223
  {
955
- mcpName: "mcp__ainecto__list_folders",
1224
+ mcpName: "list_folders",
956
1225
  outputHint: "table",
957
1226
  tableColumns: ["uuid", "name", "projectUuid", "parentFolderUuid", "updatedAt"],
958
1227
  displayName: "List folders"
959
1228
  },
960
1229
  {
961
- mcpName: "mcp__ainecto__list_documents",
1230
+ mcpName: "list_documents",
962
1231
  outputHint: "table",
963
1232
  tableColumns: ["uuid", "title", "type", "projectUuid", "folderUuid", "updatedAt"],
964
1233
  displayName: "List documents"
965
1234
  },
966
1235
  {
967
- mcpName: "mcp__ainecto__search_documents",
1236
+ mcpName: "search_documents",
968
1237
  outputHint: "table",
969
1238
  tableColumns: ["uuid", "title", "type", "projectUuid", "updatedAt"],
970
1239
  displayName: "Search documents"
971
1240
  },
972
1241
  {
973
- mcpName: "mcp__ainecto__list_attachments",
1242
+ mcpName: "list_attachments",
974
1243
  outputHint: "table",
975
1244
  tableColumns: ["uuid", "filename", "contentType", "sizeBytes", "documentUuid"],
976
1245
  displayName: "List attachments"
977
1246
  },
978
1247
  {
979
- mcpName: "mcp__ainecto__erd_list_tables",
1248
+ mcpName: "erd_list_tables",
980
1249
  outputHint: "table",
981
1250
  tableColumns: ["uuid", "name", "schema_name", "display_order"],
982
1251
  displayName: "List ERD tables"
983
1252
  },
984
1253
  {
985
- mcpName: "mcp__ainecto__erd_list_refs",
1254
+ mcpName: "erd_list_refs",
986
1255
  outputHint: "table",
987
1256
  tableColumns: ["uuid", "name", "from_table_uuid", "to_table_uuid", "relationship"],
988
1257
  displayName: "List ERD refs"
989
1258
  },
990
1259
  {
991
- mcpName: "mcp__ainecto__erd_list_enums",
1260
+ mcpName: "erd_list_enums",
992
1261
  outputHint: "table",
993
1262
  tableColumns: ["uuid", "name", "display_order"],
994
1263
  displayName: "List ERD enums"
995
1264
  },
996
1265
  {
997
- mcpName: "mcp__ainecto__erd_list_table_groups",
1266
+ mcpName: "erd_list_table_groups",
998
1267
  outputHint: "table",
999
1268
  tableColumns: ["uuid", "name", "display_order"],
1000
1269
  displayName: "List ERD table groups"
1001
1270
  },
1002
1271
  {
1003
- mcpName: "mcp__ainecto__task_list_tasks",
1272
+ mcpName: "task_list_tasks",
1004
1273
  outputHint: "table",
1005
1274
  tableColumns: ["uuid", "title", "statusUuid", "assigneeId", "updatedAt"],
1006
1275
  displayName: "List tasks"
1007
1276
  },
1008
1277
  {
1009
- mcpName: "mcp__ainecto__task_list_statuses",
1278
+ mcpName: "task_list_statuses",
1010
1279
  outputHint: "table",
1011
1280
  tableColumns: ["uuid", "name", "displayOrder"],
1012
1281
  displayName: "List task statuses"
1013
1282
  },
1014
1283
  {
1015
- mcpName: "mcp__ainecto__enable_shares",
1284
+ mcpName: "enable_shares",
1016
1285
  aliases: ["shares enable"],
1017
1286
  displayName: "Enable shares"
1018
1287
  },
1019
1288
  {
1020
- mcpName: "mcp__ainecto__disable_shares",
1289
+ mcpName: "disable_shares",
1021
1290
  aliases: ["shares disable"],
1022
1291
  displayName: "Disable shares"
1023
1292
  }
@@ -1026,7 +1295,7 @@ var enrichments = [
1026
1295
  // src/core/output/table.ts
1027
1296
  function renderTable(data, options = {}) {
1028
1297
  const rows = extractRows(data);
1029
- if (!rows.length || !rows.every(isRecord4)) {
1298
+ if (!rows.length || !rows.every(isRecord5)) {
1030
1299
  return void 0;
1031
1300
  }
1032
1301
  const columns = options.columns?.length ? options.columns : inferColumns(rows);
@@ -1043,7 +1312,7 @@ function extractRows(data) {
1043
1312
  if (Array.isArray(data)) {
1044
1313
  return data;
1045
1314
  }
1046
- if (isRecord4(data)) {
1315
+ if (isRecord5(data)) {
1047
1316
  for (const key of ["items", "projects", "workspaces", "folders", "documents", "tasks", "attachments", "results", "data"]) {
1048
1317
  const value = data[key];
1049
1318
  if (Array.isArray(value)) {
@@ -1071,7 +1340,7 @@ function formatCell(value) {
1071
1340
  }
1072
1341
  return String(value);
1073
1342
  }
1074
- function isRecord4(value) {
1343
+ function isRecord5(value) {
1075
1344
  return typeof value === "object" && value !== null && !Array.isArray(value);
1076
1345
  }
1077
1346
 
@@ -1124,7 +1393,7 @@ function payloadToArgs(value) {
1124
1393
  if (value === void 0 || value === null) {
1125
1394
  return {};
1126
1395
  }
1127
- if (!isRecord5(value)) {
1396
+ if (!isRecord6(value)) {
1128
1397
  throw new CliCommandError("INVALID_PAYLOAD", "Generated command payload must be a JSON object.", { value });
1129
1398
  }
1130
1399
  return { ...value };
@@ -1228,12 +1497,12 @@ function coerceFlagValue(value, schema, key) {
1228
1497
  }
1229
1498
  function getSchemaProperties(schema) {
1230
1499
  const properties = schema.properties;
1231
- if (!isRecord5(properties)) {
1500
+ if (!isRecord6(properties)) {
1232
1501
  return {};
1233
1502
  }
1234
1503
  const result = {};
1235
1504
  for (const [key, value] of Object.entries(properties)) {
1236
- if (isRecord5(value)) {
1505
+ if (isRecord6(value)) {
1237
1506
  result[key] = value;
1238
1507
  }
1239
1508
  }
@@ -1248,7 +1517,7 @@ function requireValue(flag, value) {
1248
1517
  }
1249
1518
  return value;
1250
1519
  }
1251
- function isRecord5(value) {
1520
+ function isRecord6(value) {
1252
1521
  return typeof value === "object" && value !== null && !Array.isArray(value);
1253
1522
  }
1254
1523
 
@@ -1290,9 +1559,8 @@ async function promptConfirmation(io, message) {
1290
1559
  var ATTACHMENTS_UPLOAD_COMMAND_PATH = ["attachments", "upload"];
1291
1560
 
1292
1561
  // src/adapters/cli/generatedCommandRouter.ts
1293
- function matchGeneratedCommand(env, argv) {
1562
+ function matchGeneratedCommand(tools, argv) {
1294
1563
  const commandTokens = argv.filter((arg) => !arg.startsWith("-"));
1295
- const tools = getGeneratedTools(env);
1296
1564
  const entries = buildRouteEntries(tools, enrichments);
1297
1565
  const match = entries.filter((entry) => isPrefix(entry.path, commandTokens)).sort((a, b) => b.path.length - a.path.length)[0];
1298
1566
  if (!match) {
@@ -1308,7 +1576,7 @@ function matchGeneratedCommand(env, argv) {
1308
1576
  };
1309
1577
  }
1310
1578
  async function executeGeneratedCommand(options) {
1311
- const route = matchGeneratedCommand(options.env, options.argv);
1579
+ const route = matchGeneratedCommand(options.tools, options.argv);
1312
1580
  if (!route) {
1313
1581
  return 2;
1314
1582
  }
@@ -1471,7 +1739,7 @@ async function requestUploadToken(client, metadata, context) {
1471
1739
  requestArgs.ttlSeconds = context.ttlSeconds;
1472
1740
  }
1473
1741
  const response = unwrapMcpToolPayload(await client.toolsCall(context.requestToolName, requestArgs));
1474
- if (!isRecord6(response)) {
1742
+ if (!isRecord7(response)) {
1475
1743
  throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response was not an object.");
1476
1744
  }
1477
1745
  const token = response.token;
@@ -1541,7 +1809,7 @@ function parsePutResponse(text) {
1541
1809
  } catch {
1542
1810
  throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not valid JSON.");
1543
1811
  }
1544
- if (!isRecord6(parsed)) {
1812
+ if (!isRecord7(parsed)) {
1545
1813
  throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not an object.");
1546
1814
  }
1547
1815
  if (typeof parsed.storageKey !== "string") {
@@ -1574,10 +1842,10 @@ function validatePutResult(token, metadata, putResult) {
1574
1842
  }
1575
1843
  }
1576
1844
  function unwrapMcpToolPayload(value) {
1577
- if (!isRecord6(value) || !Array.isArray(value.content)) {
1845
+ if (!isRecord7(value) || !Array.isArray(value.content)) {
1578
1846
  return value;
1579
1847
  }
1580
- const text = value.content.map((item) => isRecord6(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
1848
+ const text = value.content.map((item) => isRecord7(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
1581
1849
  if (!text) {
1582
1850
  return value;
1583
1851
  }
@@ -1619,7 +1887,7 @@ var MIME_BY_EXTENSION = {
1619
1887
  ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1620
1888
  ".zip": "application/zip"
1621
1889
  };
1622
- function isRecord6(value) {
1890
+ function isRecord7(value) {
1623
1891
  return typeof value === "object" && value !== null && !Array.isArray(value);
1624
1892
  }
1625
1893
 
@@ -1744,12 +2012,13 @@ function renderAttachmentUploadResult(result, json) {
1744
2012
  }
1745
2013
 
1746
2014
  // src/adapters/cli/initCommand.ts
1747
- import { chmod as chmod2, lstat, mkdir as mkdir2, readFile as readFile4, realpath, rename as rename2, rm as rm2, rmdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
1748
- import { dirname as dirname2, join as join2, relative, resolve, sep } from "path";
2015
+ import { chmod as chmod2, mkdir as mkdir2, rename as rename2, rm as rm2, rmdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
2016
+ import { dirname as dirname3, join as join3, relative, resolve as resolve2, sep as sep2 } from "path";
1749
2017
 
1750
2018
  // src/core/harness/agentTargets.ts
1751
- var SERVER_NAME = "ai-erd";
1752
- var PACKAGE_NAME = "@ai-erd/mcp";
2019
+ import { basename as basename2 } from "path";
2020
+ var SERVER_NAME = "ainecto";
2021
+ var LEGACY_SERVER_NAME = "ai-erd";
1753
2022
  var AGENT_TARGETS = [
1754
2023
  { id: "claude-code", label: "Claude Code", path: ".mcp.json" },
1755
2024
  { id: "cursor", label: "Cursor", path: ".cursor/mcp.json" }
@@ -1765,7 +2034,7 @@ function buildServerEntry(input) {
1765
2034
  return { command: "npx", args };
1766
2035
  }
1767
2036
  function readRoleFromEntry(entry) {
1768
- if (!isRecord7(entry) || !Array.isArray(entry.args)) {
2037
+ if (!isRecord8(entry) || !Array.isArray(entry.args)) {
1769
2038
  return void 0;
1770
2039
  }
1771
2040
  const index = entry.args.indexOf("--role");
@@ -1778,16 +2047,44 @@ function readServerEntry(existing) {
1778
2047
  }
1779
2048
  try {
1780
2049
  const parsed = JSON.parse(existing);
1781
- return parsed?.mcpServers?.[SERVER_NAME];
2050
+ const servers = parsed?.mcpServers;
2051
+ return isRecord8(servers) ? servers[ourKeyIn(servers) ?? SERVER_NAME] : void 0;
1782
2052
  } catch {
1783
2053
  return void 0;
1784
2054
  }
1785
2055
  }
2056
+ function ourKeyIn(servers) {
2057
+ if (Object.prototype.hasOwnProperty.call(servers, SERVER_NAME)) {
2058
+ return SERVER_NAME;
2059
+ }
2060
+ return isOurBridge(servers[LEGACY_SERVER_NAME]) ? LEGACY_SERVER_NAME : void 0;
2061
+ }
2062
+ function legacyBridgeIn(servers) {
2063
+ return isOurBridge(servers[LEGACY_SERVER_NAME]);
2064
+ }
2065
+ function isOurBridge(entry) {
2066
+ if (!isRecord8(entry)) {
2067
+ return false;
2068
+ }
2069
+ const { command, args } = entry;
2070
+ const argList = Array.isArray(args) ? args.filter((arg) => typeof arg === "string") : [];
2071
+ const isOurPackage = (arg) => [PACKAGE_NAME, LEGACY_PACKAGE_NAME].some((name) => arg === name || arg.startsWith(`${name}@`));
2072
+ if (argList.some((arg) => isOurPackage(arg) || arg === "--role")) {
2073
+ return true;
2074
+ }
2075
+ const bin = typeof command === "string" ? basename2(command) : "";
2076
+ return OUR_BINS.includes(bin);
2077
+ }
2078
+ var OUR_BINS = ["ainecto", "ai-erd", "ai-erd-mcp"];
1786
2079
  function mergeServerEntry(existing, entry) {
1787
2080
  const root = parseConfigObject(existing);
1788
2081
  const servers = readServers(root);
1789
- const replaced = Object.prototype.hasOwnProperty.call(servers, SERVER_NAME);
1790
- const replacedEntry = replaced ? servers[SERVER_NAME] : void 0;
2082
+ const ourKey = ourKeyIn(servers);
2083
+ const replaced = ourKey !== void 0;
2084
+ const replacedEntry = ourKey === void 0 ? void 0 : servers[ourKey];
2085
+ if (legacyBridgeIn(servers)) {
2086
+ delete servers[LEGACY_SERVER_NAME];
2087
+ }
1791
2088
  servers[SERVER_NAME] = entry;
1792
2089
  return { content: stringify({ ...root, mcpServers: servers }), replaced, replacedEntry };
1793
2090
  }
@@ -1797,12 +2094,15 @@ function removeServerEntry(existing, restore) {
1797
2094
  }
1798
2095
  const root = parseConfigObject(existing);
1799
2096
  const servers = readServers(root);
1800
- const removed = Object.prototype.hasOwnProperty.call(servers, SERVER_NAME);
2097
+ const ourKey = ourKeyIn(servers);
2098
+ const removed = ourKey !== void 0;
1801
2099
  if (restore !== void 0) {
1802
- servers[SERVER_NAME] = restore.value;
2100
+ servers[ourKey ?? SERVER_NAME] = restore.value;
1803
2101
  return { content: stringify({ ...root, mcpServers: servers }), removed, emptied: false };
1804
2102
  }
1805
- delete servers[SERVER_NAME];
2103
+ if (ourKey !== void 0) {
2104
+ delete servers[ourKey];
2105
+ }
1806
2106
  const otherKeys = Object.keys(root).filter((key) => key !== "mcpServers");
1807
2107
  const emptied = Object.keys(servers).length === 0 && otherKeys.length === 0;
1808
2108
  return { content: stringify({ ...root, mcpServers: servers }), removed, emptied };
@@ -1832,7 +2132,7 @@ function readServers(root) {
1832
2132
  if (servers === void 0) {
1833
2133
  return {};
1834
2134
  }
1835
- if (!isRecord7(servers)) {
2135
+ if (!isRecord8(servers)) {
1836
2136
  throw new Error(
1837
2137
  'Existing MCP config has an "mcpServers" value that is not an object, so it was left untouched. Fix it by hand and run init again.'
1838
2138
  );
@@ -1851,7 +2151,7 @@ function parseConfigObject(existing) {
1851
2151
  `Existing MCP config is not valid JSON, so it was left untouched. Fix it by hand and run init again. (${error instanceof Error ? error.message : String(error)})`
1852
2152
  );
1853
2153
  }
1854
- if (!isRecord7(parsed)) {
2154
+ if (!isRecord8(parsed)) {
1855
2155
  throw new Error("Existing MCP config must be a JSON object; it was left untouched.");
1856
2156
  }
1857
2157
  return parsed;
@@ -1860,16 +2160,24 @@ function stringify(value) {
1860
2160
  return `${JSON.stringify(value, null, 2)}
1861
2161
  `;
1862
2162
  }
1863
- function isRecord7(value) {
2163
+ function isRecord8(value) {
1864
2164
  return typeof value === "object" && value !== null && !Array.isArray(value);
1865
2165
  }
1866
2166
 
1867
2167
  // src/core/harness/harnessDoc.ts
1868
- var MARKER_BEGIN = "<!-- ai-erd:begin -->";
1869
- var MARKER_END = "<!-- ai-erd:end -->";
1870
- var HARNESS_DOC_PATH = ".ai-erd/HARNESS.md";
1871
- var HARNESS_CONFIG_PATH = ".ai-erd/config.json";
1872
- var HARNESS_RECORD_PATH = ".ai-erd/init-record.json";
2168
+ var MARKER_BEGIN = "<!-- ainecto:begin -->";
2169
+ var MARKER_END = "<!-- ainecto:end -->";
2170
+ var LEGACY_MARKER_BEGIN = "<!-- ai-erd:begin -->";
2171
+ var LEGACY_MARKER_END = "<!-- ai-erd:end -->";
2172
+ var MARKERS = {
2173
+ begin: [MARKER_BEGIN, LEGACY_MARKER_BEGIN],
2174
+ end: [MARKER_END, LEGACY_MARKER_END]
2175
+ };
2176
+ var HARNESS_DIR = ".ainecto";
2177
+ var LEGACY_HARNESS_DIR = ".ai-erd";
2178
+ var HARNESS_DOC_PATH = `${HARNESS_DIR}/HARNESS.md`;
2179
+ var HARNESS_CONFIG_PATH = `${HARNESS_DIR}/config.json`;
2180
+ var HARNESS_RECORD_PATH = `${HARNESS_DIR}/init-record.json`;
1873
2181
  function renderHarnessDoc(input, template) {
1874
2182
  return fillTemplate(template ?? DEFAULT_HARNESS_DOC, {
1875
2183
  projectName: input.projectName,
@@ -1893,13 +2201,13 @@ function documentContractProblem(key, body) {
1893
2201
  }
1894
2202
  }
1895
2203
  if (key === AGENT_NOTE_KEY) {
1896
- return managedBlockProblem(key, body);
2204
+ return managedBlockProblem(key, withCurrentMarkers(body));
1897
2205
  }
1898
2206
  return void 0;
1899
2207
  }
1900
- var DEFAULT_HARNESS_DOC = `# AI-ERD Harness
2208
+ var DEFAULT_HARNESS_DOC = `# AiNecto Harness
1901
2209
 
1902
- This repository is wired to an AI-ERD project. The design source of truth lives in AI-ERD,
2210
+ This repository is wired to an AiNecto project. The design source of truth lives in AiNecto,
1903
2211
  not in this repository's files.
1904
2212
 
1905
2213
  - Project: **{{projectName}}** (\`{{projectUuid}}\`)
@@ -1908,9 +2216,11 @@ not in this repository's files.
1908
2216
  ## One session, one role
1909
2217
 
1910
2218
  A session picks exactly one role and keeps it for the whole session. The role comes from the
1911
- MCP server configuration \u2014 not from this file, and not from anything an agent can change at
1912
- runtime. The server exposes only the tools that role is allowed to use, so a tool that is out
1913
- of role is not merely discouraged: it is absent.
2219
+ MCP server configuration \u2014 not from this file \u2014 and it does not change during a session. The
2220
+ user picks the role; an agent applies a new one only when the user asks, and it takes effect in
2221
+ the next session. This is a working guardrail, not a security boundary. The server exposes only
2222
+ the tools that role is allowed to use, so a tool that is out of role is not merely discouraged:
2223
+ it is absent.
1914
2224
 
1915
2225
  | Role | Owns | Must not |
1916
2226
  | --- | --- | --- |
@@ -1937,7 +2247,7 @@ and which session should pick it up next.
1937
2247
 
1938
2248
  ## Not every change needs four sessions
1939
2249
 
1940
- A small change that fits the design already recorded in AI-ERD starts at Development. The rule
2250
+ A small change that fits the design already recorded in AiNecto starts at Development. The rule
1941
2251
  is not "always run four steps" \u2014 it is that when a step is needed, it does not happen inside
1942
2252
  another role's session.
1943
2253
 
@@ -1948,27 +2258,30 @@ Change the role in the MCP server configuration and start a new session:
1948
2258
  ${AGENT_TARGETS.map((target) => ` ${target.label.padEnd(12)} ${target.path}`).join("\n")}
1949
2259
  Codex global config \u2014 use a profile: codex -p <role>, or set ${ROLE_ENV_VAR}
1950
2260
 
1951
- Or run \`ai-erd init --role <design|development|test|validation>\` to rewrite them.
2261
+ Or run \`ainecto init --role <design|development|test|validation>\` to rewrite them.
1952
2262
 
1953
2263
  ---
1954
- Generated by \`ai-erd init\`. Remove everything it wrote with \`ai-erd init --undo\`.
2264
+ Generated by \`ainecto init\`. Remove everything it wrote with \`ainecto init --undo\`.
1955
2265
  `;
1956
2266
  function renderAgentNote(template) {
1957
- return fillTemplate(template ?? DEFAULT_AGENT_NOTE, {
2267
+ return fillTemplate(withCurrentMarkers(template ?? DEFAULT_AGENT_NOTE), {
1958
2268
  harnessDocPath: HARNESS_DOC_PATH,
1959
2269
  serverName: SERVER_NAME
1960
2270
  });
1961
2271
  }
2272
+ function withCurrentMarkers(template) {
2273
+ return template.split(LEGACY_MARKER_BEGIN).join(MARKER_BEGIN).split(LEGACY_MARKER_END).join(MARKER_END);
2274
+ }
1962
2275
  var DEFAULT_AGENT_NOTE = `${MARKER_BEGIN}
1963
- ## AI-ERD Harness
2276
+ ## AiNecto Harness
1964
2277
 
1965
- This repository uses AI-ERD as its design source of truth, and each session has one role
2278
+ This repository uses AiNecto as its design source of truth, and each session has one role
1966
2279
  (Design / Development / Test / Validation). Read \`{{harnessDocPath}}\` before doing work that
1967
2280
  touches requirements, the ERD, or architecture.
1968
2281
 
1969
- The role of this session comes from the \`{{serverName}}\` MCP server configuration, and from the
1970
- access token when you signed in with a role; the server states it in its \`initialize\`
1971
- instructions and exposes only that role's tools. If the work needs something outside the role,
2282
+ The role of this session comes from the \`{{serverName}}\` MCP server configuration that
2283
+ \`ainecto init\` wrote; the server states it in its \`initialize\` instructions and exposes only
2284
+ that role's tools. If the work needs something outside the role,
1972
2285
  stop and report which session is required.
1973
2286
  ${MARKER_END}`;
1974
2287
  function managedBlockProblem(key, body) {
@@ -2020,38 +2333,58 @@ function upsertMarkerBlock(existing, block) {
2020
2333
  return composed;
2021
2334
  }
2022
2335
  function assertComposedBlockIsOurs(composed, existing) {
2023
- if (ownedMarkerCount(composed, MARKER_BEGIN) === 1 && ownedMarkerCount(composed, MARKER_END) === 1) {
2336
+ if (ownedMarkerCount(composed, "begin") === 1 && ownedMarkerCount(composed, "end") === 1) {
2024
2337
  return;
2025
2338
  }
2026
2339
  const reason = hasUnclosedFence(existing) ? "this file ends inside an unclosed code fence, so the block would sit inside it" : "the block could not be found again once merged into this file";
2027
2340
  throw new Error(
2028
- `ai-erd could not place its block where --undo would find it again \u2014 ${reason}. The file was left untouched; fix that and run again.`
2341
+ `ainecto could not place its block where --undo would find it again \u2014 ${reason}. The file was left untouched; fix that and run again.`
2029
2342
  );
2030
2343
  }
2031
2344
  function assertNoDanglingMarker(content) {
2032
- const begins = ownedMarkerCount(content, MARKER_BEGIN);
2033
- const ends = ownedMarkerCount(content, MARKER_END);
2345
+ const begins = ownedMarkerCount(content, "begin");
2346
+ const ends = ownedMarkerCount(content, "end");
2034
2347
  if (begins !== ends || begins > 1) {
2035
2348
  throw new Error(
2036
- `ai-erd markers are unbalanced (${begins} begin, ${ends} end). The file was left untouched \u2014 remove the stray ${MARKER_BEGIN} / ${MARKER_END} by hand and run again.`
2349
+ `AiNecto markers are unbalanced (${begins} begin, ${ends} end). The file was left untouched \u2014 remove the stray ${MARKER_BEGIN} / ${MARKER_END} by hand and run again.`
2037
2350
  );
2038
2351
  }
2039
2352
  if (begins === 0) {
2040
2353
  return;
2041
2354
  }
2042
- if (ownedMarkerIndex(content, MARKER_END, 0) < ownedMarkerIndex(content, MARKER_BEGIN, 0)) {
2355
+ if (ownedMarker(content, "end", 0).at < ownedMarker(content, "begin", 0).at) {
2043
2356
  throw new Error(
2044
- `ai-erd markers are out of order (${MARKER_END} appears before ${MARKER_BEGIN}). The file was left untouched \u2014 fix the markers by hand and run again.`
2357
+ `AiNecto markers are out of order (${MARKER_END} appears before ${MARKER_BEGIN}). The file was left untouched \u2014 fix the markers by hand and run again.`
2045
2358
  );
2046
2359
  }
2047
2360
  }
2048
- function ownedMarkerCount(content, marker) {
2361
+ function ownedMarkerCount(content, kind) {
2049
2362
  let count = 0;
2050
- for (let at = ownedMarkerIndex(content, marker, 0); at >= 0; at = ownedMarkerIndex(content, marker, at + 1)) {
2363
+ for (let hit = ownedMarker(content, kind, 0); hit !== void 0; hit = ownedMarker(content, kind, hit.at + 1)) {
2051
2364
  count += 1;
2052
2365
  }
2053
2366
  return count;
2054
2367
  }
2368
+ function ownedMarker(content, kind, from) {
2369
+ let best;
2370
+ for (const marker of MARKERS[kind]) {
2371
+ const at = ownedMarkerIndex(content, marker, from);
2372
+ if (at >= 0 && (best === void 0 || at < best.at)) {
2373
+ best = { at, length: marker.length };
2374
+ }
2375
+ }
2376
+ return best;
2377
+ }
2378
+ function anyMarker(content, kind, from) {
2379
+ let best;
2380
+ for (const marker of MARKERS[kind]) {
2381
+ const at = content.indexOf(marker, from);
2382
+ if (at >= 0 && (best === void 0 || at < best.at)) {
2383
+ best = { at, length: marker.length };
2384
+ }
2385
+ }
2386
+ return best;
2387
+ }
2055
2388
  function isInsideFencedBlock(content, index) {
2056
2389
  let open;
2057
2390
  let offset = 0;
@@ -2116,24 +2449,24 @@ function markerEvidence(content) {
2116
2449
  let begins = 0;
2117
2450
  let ends = 0;
2118
2451
  while (true) {
2119
- const begin = rest.indexOf(MARKER_BEGIN, cursor);
2120
- const close = rest.indexOf(MARKER_END, cursor);
2121
- if (begin < 0 && close < 0) {
2452
+ const begin = anyMarker(rest, "begin", cursor);
2453
+ const close = anyMarker(rest, "end", cursor);
2454
+ if (begin === void 0 && close === void 0) {
2122
2455
  break;
2123
2456
  }
2124
- if (begin < 0 || close >= 0 && close < begin) {
2457
+ if (begin === void 0 || close !== void 0 && close.at < begin.at) {
2125
2458
  ends += 1;
2126
- cursor = close + MARKER_END.length;
2459
+ cursor = close.at + close.length;
2127
2460
  continue;
2128
2461
  }
2129
- const pairEnd = rest.indexOf(MARKER_END, begin + MARKER_BEGIN.length);
2130
- if (pairEnd < 0) {
2462
+ const pairEnd = anyMarker(rest, "end", begin.at + begin.length);
2463
+ if (pairEnd === void 0) {
2131
2464
  begins += 1;
2132
- cursor = begin + MARKER_BEGIN.length;
2465
+ cursor = begin.at + begin.length;
2133
2466
  continue;
2134
2467
  }
2135
- evidence.push(blockShape(rest.slice(begin, pairEnd + MARKER_END.length)));
2136
- cursor = pairEnd + MARKER_END.length;
2468
+ evidence.push(blockShape(rest.slice(begin.at, pairEnd.at + pairEnd.length)));
2469
+ cursor = pairEnd.at + pairEnd.length;
2137
2470
  }
2138
2471
  for (let i = 0; i < begins; i += 1) evidence.push(UNPAIRED_BEGIN);
2139
2472
  for (let i = 0; i < ends; i += 1) evidence.push(UNPAIRED_END);
@@ -2150,15 +2483,15 @@ function existingMarkerBlock(content) {
2150
2483
  return range ? content.slice(range.start, range.end) : void 0;
2151
2484
  }
2152
2485
  function markerRange(content) {
2153
- const start = ownedMarkerIndex(content, MARKER_BEGIN, 0);
2154
- if (start < 0) {
2486
+ const start = ownedMarker(content, "begin", 0);
2487
+ if (start === void 0) {
2155
2488
  return void 0;
2156
2489
  }
2157
- const endMarker = ownedMarkerIndex(content, MARKER_END, start);
2158
- if (endMarker < 0) {
2490
+ const endMarker = ownedMarker(content, "end", start.at);
2491
+ if (endMarker === void 0) {
2159
2492
  return void 0;
2160
2493
  }
2161
- return { start, end: endMarker + MARKER_END.length };
2494
+ return { start: start.at, end: endMarker.at + endMarker.length };
2162
2495
  }
2163
2496
  function ownedMarkerIndex(content, marker, from) {
2164
2497
  for (let at = content.indexOf(marker, from); at >= 0; at = content.indexOf(marker, at + 1)) {
@@ -2180,13 +2513,15 @@ function ownedMarkerIndex(content, marker, from) {
2180
2513
  }
2181
2514
 
2182
2515
  // src/core/harness/initRecord.ts
2183
- var RECORD_KIND = "ai-erd/init-record";
2516
+ var RECORD_KIND = "ainecto/init-record";
2517
+ var LEGACY_RECORD_KIND = "ai-erd/init-record";
2184
2518
  var RECORD_VERSION = 2;
2185
2519
  var ABSENT = { at: "absent" };
2186
2520
  function emptyRecord() {
2187
2521
  return { version: RECORD_VERSION, files: {}, createdDirectories: [] };
2188
2522
  }
2189
- function readRecord(content) {
2523
+ function readRecord(content, recordPath = HARNESS_RECORD_PATH) {
2524
+ const HARNESS_RECORD_PATH2 = recordPath;
2190
2525
  if (content === void 0) {
2191
2526
  return { state: "absent" };
2192
2527
  }
@@ -2194,26 +2529,26 @@ function readRecord(content) {
2194
2529
  try {
2195
2530
  parsed = JSON.parse(content);
2196
2531
  } catch {
2197
- return { state: "corrupt", reason: `${HARNESS_RECORD_PATH} is not valid JSON` };
2532
+ return { state: "corrupt", reason: `${HARNESS_RECORD_PATH2} is not valid JSON` };
2198
2533
  }
2199
2534
  if (!isObject(parsed)) {
2200
- return { state: "corrupt", reason: `${HARNESS_RECORD_PATH} is not a JSON object` };
2535
+ return { state: "corrupt", reason: `${HARNESS_RECORD_PATH2} is not a JSON object` };
2201
2536
  }
2202
- if (parsed.kind !== RECORD_KIND) {
2537
+ if (parsed.kind !== RECORD_KIND && parsed.kind !== LEGACY_RECORD_KIND) {
2203
2538
  return {
2204
2539
  state: "corrupt",
2205
- reason: `${HARNESS_RECORD_PATH} exists but is not an ai-erd record (kind=${JSON.stringify(parsed.kind)})`
2540
+ reason: `${HARNESS_RECORD_PATH2} exists but is not an AiNecto init record (kind=${JSON.stringify(parsed.kind)})`
2206
2541
  };
2207
2542
  }
2208
2543
  if (parsed.version !== RECORD_VERSION) {
2209
2544
  return {
2210
2545
  state: "legacy",
2211
- reason: `${HARNESS_RECORD_PATH} was written by another version of this CLI (record version ${JSON.stringify(parsed.version)}, this CLI reads ${RECORD_VERSION})`
2546
+ reason: `${HARNESS_RECORD_PATH2} was written by another version of this CLI (record version ${JSON.stringify(parsed.version)}, this CLI reads ${RECORD_VERSION})`
2212
2547
  };
2213
2548
  }
2214
2549
  const files = parsed.files;
2215
2550
  if (!isObject(files)) {
2216
- return { state: "corrupt", reason: `${HARNESS_RECORD_PATH} has no "files" object` };
2551
+ return { state: "corrupt", reason: `${HARNESS_RECORD_PATH2} has no "files" object` };
2217
2552
  }
2218
2553
  const out = {};
2219
2554
  for (const [path, raw] of Object.entries(files)) {
@@ -2221,14 +2556,14 @@ function readRecord(content) {
2221
2556
  if ("problem" in read) {
2222
2557
  return {
2223
2558
  state: "corrupt",
2224
- reason: `${HARNESS_RECORD_PATH} has an unusable entry for ${path} \u2014 it ${read.problem}`
2559
+ reason: `${HARNESS_RECORD_PATH2} has an unusable entry for ${path} \u2014 it ${read.problem}`
2225
2560
  };
2226
2561
  }
2227
2562
  out[path] = read.contract;
2228
2563
  }
2229
2564
  const directories = parsed.createdDirectories;
2230
2565
  if (directories !== void 0 && !isStringArray(directories)) {
2231
- return { state: "corrupt", reason: `${HARNESS_RECORD_PATH} has a malformed createdDirectories` };
2566
+ return { state: "corrupt", reason: `${HARNESS_RECORD_PATH2} has a malformed createdDirectories` };
2232
2567
  }
2233
2568
  return {
2234
2569
  state: "valid",
@@ -2274,10 +2609,10 @@ function legacyManagedRecord(configContent) {
2274
2609
  return true;
2275
2610
  }
2276
2611
  const lastWritten = managed.lastWritten;
2277
- return isObject(lastWritten) && Object.prototype.hasOwnProperty.call(lastWritten, HARNESS_CONFIG_PATH);
2612
+ return isObject(lastWritten) && [HARNESS_CONFIG_PATH, `${LEGACY_HARNESS_DIR}/config.json`].some((path) => Object.prototype.hasOwnProperty.call(lastWritten, path));
2278
2613
  }
2279
2614
  function legacyManagedNotice(configPath) {
2280
- return `${configPath} holds an init record written by an older version of this CLI (a "managed" field). This version keeps its record in ${HARNESS_RECORD_PATH} and cannot read that one, so your saved originals are still in there and nothing was changed. Undo with the version that wrote it (for example: npx @ai-erd/mcp@0.2 ai-erd init --undo), then run this version again.`;
2615
+ return `${configPath} holds an init record written by an older version of this CLI (a "managed" field). This version keeps its record in ${HARNESS_RECORD_PATH} and cannot read that one, so your saved originals are still in there and nothing was changed. Undo with the version that wrote it (for example: npx -y -p @ai-erd/mcp@0.2 ai-erd init --undo), then run this version again.`;
2281
2616
  }
2282
2617
  function serializeRecord(record) {
2283
2618
  return `${JSON.stringify({
@@ -2297,7 +2632,7 @@ function readContract(raw) {
2297
2632
  return { problem: `has an unknown scope (${JSON.stringify(raw.scope)})` };
2298
2633
  }
2299
2634
  if (typeof raw.createdByUs !== "boolean") {
2300
- return { problem: "does not say whether ai-erd created the file" };
2635
+ return { problem: "does not say whether ainecto created the file" };
2301
2636
  }
2302
2637
  const original = readOwned(raw.original);
2303
2638
  const written = readOwned(raw.written);
@@ -2305,7 +2640,7 @@ function readContract(raw) {
2305
2640
  return { problem: "has no readable record of what was there before" };
2306
2641
  }
2307
2642
  if (written === void 0) {
2308
- return { problem: "has no readable record of what ai-erd wrote" };
2643
+ return { problem: "has no readable record of what ainecto wrote" };
2309
2644
  }
2310
2645
  if (raw.fileFingerprint !== void 0 && typeof raw.fileFingerprint !== "string") {
2311
2646
  return { problem: "has a malformed fileFingerprint" };
@@ -2390,7 +2725,7 @@ function fieldsContractProblem(contract, ownedFields) {
2390
2725
  if (!Object.prototype.hasOwnProperty.call(body, key)) {
2391
2726
  return `says it wrote ${key}, but that field is not in the file body it recorded`;
2392
2727
  }
2393
- if (stableStringify(body[key]) !== stableStringify(writtenFields[key])) {
2728
+ if (stableStringify2(body[key]) !== stableStringify2(writtenFields[key])) {
2394
2729
  return `records a different value for ${key} than the file body it wrote`;
2395
2730
  }
2396
2731
  }
@@ -2418,13 +2753,13 @@ function readOwned(raw) {
2418
2753
  }
2419
2754
  return void 0;
2420
2755
  }
2421
- function stableStringify(value) {
2756
+ function stableStringify2(value) {
2422
2757
  if (Array.isArray(value)) {
2423
- return `[${value.map(stableStringify).join(",")}]`;
2758
+ return `[${value.map(stableStringify2).join(",")}]`;
2424
2759
  }
2425
2760
  if (value && typeof value === "object") {
2426
2761
  const entries = Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
2427
- return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
2762
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify2(item)}`).join(",")}}`;
2428
2763
  }
2429
2764
  return JSON.stringify(value) ?? "null";
2430
2765
  }
@@ -2436,7 +2771,188 @@ function isStringArray(value) {
2436
2771
  }
2437
2772
 
2438
2773
  // src/core/harness/initPlan.ts
2439
- import { createHash as createHash3 } from "crypto";
2774
+ import { createHash as createHash4 } from "crypto";
2775
+
2776
+ // src/core/harness/repositoryRole.ts
2777
+ import { lstat, readFile as readFile4, realpath } from "fs/promises";
2778
+ import { homedir as homedir2 } from "os";
2779
+ import { dirname as dirname2, join as join2, resolve, sep } from "path";
2780
+ function readConfigRole(path, content) {
2781
+ if (content === void 0 || !content.trim()) {
2782
+ return { kind: "absent" };
2783
+ }
2784
+ let servers;
2785
+ try {
2786
+ const parsed = JSON.parse(content);
2787
+ const raw = parsed?.mcpServers;
2788
+ servers = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
2789
+ } catch {
2790
+ return content.includes(SERVER_NAME) || content.includes(LEGACY_SERVER_NAME) ? { kind: "problem", reason: `${path} is not valid JSON` } : { kind: "absent" };
2791
+ }
2792
+ const roles = [];
2793
+ for (const key of [SERVER_NAME, LEGACY_SERVER_NAME]) {
2794
+ const entry = servers[key];
2795
+ if (!isOurBridge(entry)) {
2796
+ continue;
2797
+ }
2798
+ const role = readRoleFromEntry(entry);
2799
+ if (role === void 0) {
2800
+ return { kind: "problem", reason: `${path} has the "${key}" bridge without a valid --role` };
2801
+ }
2802
+ if (!roles.includes(role)) {
2803
+ roles.push(role);
2804
+ }
2805
+ }
2806
+ if (roles.length > 1) {
2807
+ return {
2808
+ kind: "problem",
2809
+ reason: `${path} has both the "${SERVER_NAME}" and the "${LEGACY_SERVER_NAME}" bridge with different roles (${roles.join(", ")})`
2810
+ };
2811
+ }
2812
+ return roles.length === 0 ? { kind: "absent" } : { kind: "role", role: roles[0] };
2813
+ }
2814
+ function rolesInFiles(files) {
2815
+ const roles = [];
2816
+ const problems = [];
2817
+ for (const target of AGENT_TARGETS) {
2818
+ const read = readConfigRole(target.path, files.get(target.path));
2819
+ if (read.kind === "problem") {
2820
+ problems.push(read.reason);
2821
+ } else if (read.kind === "role" && !roles.includes(read.role)) {
2822
+ roles.push(read.role);
2823
+ }
2824
+ }
2825
+ return { roles, problems, found: roles.length > 0 || problems.length > 0 };
2826
+ }
2827
+ async function findRepositoryRole(cwd, home = homedir2()) {
2828
+ const stopAt = await realOrResolved(home);
2829
+ for (let dir = await realOrResolved(cwd); ; dir = dirname2(dir)) {
2830
+ if (samePath(dir, stopAt)) {
2831
+ return { kind: "none" };
2832
+ }
2833
+ const files = /* @__PURE__ */ new Map();
2834
+ let readProblem;
2835
+ for (const target of AGENT_TARGETS) {
2836
+ try {
2837
+ files.set(target.path, await readConfigInside(dir, target.path));
2838
+ } catch (error) {
2839
+ readProblem = error instanceof Error ? error.message : String(error);
2840
+ }
2841
+ }
2842
+ if (readProblem !== void 0) {
2843
+ return { kind: "conflict", dir, reason: readProblem };
2844
+ }
2845
+ const result = rolesInFiles(files);
2846
+ if (result.found) {
2847
+ if (result.problems.length > 0) {
2848
+ return { kind: "conflict", dir, reason: result.problems.join("; ") };
2849
+ }
2850
+ if (result.roles.length > 1) {
2851
+ return {
2852
+ kind: "conflict",
2853
+ dir,
2854
+ reason: `agent configs disagree about the session role (${result.roles.join(", ")})`
2855
+ };
2856
+ }
2857
+ return { kind: "role", role: result.roles[0], dir };
2858
+ }
2859
+ if (dirname2(dir) === dir) {
2860
+ return { kind: "none" };
2861
+ }
2862
+ }
2863
+ }
2864
+ async function askTheUserToFixRole(dir, reason) {
2865
+ const gitRoot = await findGitRoot(dir);
2866
+ const roles = HARNESS_ROLES.join(", ");
2867
+ if (gitRoot !== void 0 && !samePath(await realOrResolved(gitRoot), await realOrResolved(dir))) {
2868
+ return `Cannot tell this folder's AI session role: ${reason} (in ${dir}, inside the git repository at ${gitRoot}). Ask the user how that AiNecto setting should look \u2014 \`ainecto init\` only sets up a repository root (${gitRoot}).`;
2869
+ }
2870
+ return `Cannot tell this repository's AI session role: ${reason} (in ${dir}). Ask the user which role this repository's AI sessions should have (${roles}), then run \`ainecto init --role <role>\` in ${dir}.`;
2871
+ }
2872
+ async function realOrResolved(path) {
2873
+ try {
2874
+ return await realpath(path);
2875
+ } catch {
2876
+ return resolve(path);
2877
+ }
2878
+ }
2879
+ function samePath(left, right) {
2880
+ return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
2881
+ }
2882
+ async function readConfigInside(dir, path) {
2883
+ let real;
2884
+ try {
2885
+ real = await realpath(join2(dir, path));
2886
+ } catch (error) {
2887
+ if (isNodeError2(error, "ENOENT") || isNodeError2(error, "ENOTDIR")) {
2888
+ return void 0;
2889
+ }
2890
+ throw error;
2891
+ }
2892
+ const realDir = await realOrResolved(dir);
2893
+ if (!samePath(real, realDir) && !real.startsWith(realDir + sep) && !(process.platform === "win32" && real.toLowerCase().startsWith((realDir + sep).toLowerCase()))) {
2894
+ throw new Error(`${path} points outside ${dir} (${real})`);
2895
+ }
2896
+ return readFile4(real, "utf8");
2897
+ }
2898
+ async function findGitRoot(cwd) {
2899
+ for (let at = resolve(cwd); ; at = dirname2(at)) {
2900
+ try {
2901
+ await lstat(join2(at, ".git"));
2902
+ return at;
2903
+ } catch {
2904
+ }
2905
+ if (dirname2(at) === at) {
2906
+ return void 0;
2907
+ }
2908
+ }
2909
+ }
2910
+ async function readManagedFile(root, path) {
2911
+ await assertInsideRepository(root, path);
2912
+ return readIfExists(join2(root, path));
2913
+ }
2914
+ async function readIfExists(absolutePath) {
2915
+ try {
2916
+ return await readFile4(absolutePath, "utf8");
2917
+ } catch (error) {
2918
+ if (isNodeError2(error, "ENOENT")) {
2919
+ return void 0;
2920
+ }
2921
+ throw error;
2922
+ }
2923
+ }
2924
+ async function assertInsideRepository(root, relativePath) {
2925
+ const absolutePath = join2(root, relativePath);
2926
+ const realRoot = await realpath(root);
2927
+ let realParent;
2928
+ try {
2929
+ realParent = await realpath(dirname2(absolutePath));
2930
+ } catch (error) {
2931
+ if (isNodeError2(error, "ENOENT")) {
2932
+ return;
2933
+ }
2934
+ throw error;
2935
+ }
2936
+ if (realParent !== realRoot && !realParent.startsWith(realRoot + sep)) {
2937
+ throw new Error(
2938
+ `${relativePath} resolves outside the repository (${realParent}); refusing to touch it.`
2939
+ );
2940
+ }
2941
+ try {
2942
+ if ((await lstat(absolutePath)).isSymbolicLink()) {
2943
+ throw new Error(`${relativePath} is a symbolic link; refusing to write through it.`);
2944
+ }
2945
+ } catch (error) {
2946
+ if (!isNodeError2(error, "ENOENT")) {
2947
+ throw error;
2948
+ }
2949
+ }
2950
+ }
2951
+ function isNodeError2(error, code) {
2952
+ return typeof error === "object" && error !== null && error.code === code;
2953
+ }
2954
+
2955
+ // src/core/harness/initPlan.ts
2440
2956
  var NOTE_TARGETS = [
2441
2957
  { path: "AGENTS.md", createIfMissing: true },
2442
2958
  { path: "CLAUDE.md", createIfMissing: false }
@@ -2464,10 +2980,10 @@ function sameOwned(left, right) {
2464
2980
  if (left.at === "text" && right.at === "text") {
2465
2981
  return left.text === right.text;
2466
2982
  }
2467
- return left.at === "json" && right.at === "json" && stableStringify(left.value) === stableStringify(right.value);
2983
+ return left.at === "json" && right.at === "json" && stableStringify2(left.value) === stableStringify2(right.value);
2468
2984
  }
2469
2985
  function contentFingerprint(content) {
2470
- return createHash3("sha256").update(content).digest("hex");
2986
+ return createHash4("sha256").update(content).digest("hex");
2471
2987
  }
2472
2988
  function serialize(body) {
2473
2989
  return `${JSON.stringify(body, null, 2)}
@@ -2478,7 +2994,7 @@ function fingerprintFor(path, content) {
2478
2994
  return contentFingerprint(content);
2479
2995
  }
2480
2996
  try {
2481
- return contentFingerprint(stableStringify(JSON.parse(content)));
2997
+ return contentFingerprint(stableStringify2(JSON.parse(content)));
2482
2998
  } catch {
2483
2999
  return contentFingerprint(content);
2484
3000
  }
@@ -2558,7 +3074,7 @@ function planInit(input) {
2558
3074
  return prior;
2559
3075
  }
2560
3076
  delete record.files[path];
2561
- notes.push(`${path}: it changed outside ai-erd, so the older backup was dropped and this is the new baseline.`);
3077
+ notes.push(`${path}: it changed outside ainecto, so the older backup was dropped and this is the new baseline.`);
2562
3078
  return void 0;
2563
3079
  };
2564
3080
  for (const target of AGENT_TARGETS) {
@@ -2569,7 +3085,7 @@ function planInit(input) {
2569
3085
  if (existing === merged.content) {
2570
3086
  continue;
2571
3087
  }
2572
- const before = previousRole(existing);
3088
+ const before = previousRole(target.path, existing);
2573
3089
  if (merged.replaced && before && before !== input.role) {
2574
3090
  notes.push(`${target.path}: role ${before} \u2192 ${input.role}`);
2575
3091
  }
@@ -2625,7 +3141,7 @@ function planInit(input) {
2625
3141
  }
2626
3142
  if (carried?.scope === "block" && unexplainedMarkers(existing, carried) > 0) {
2627
3143
  notes.push(
2628
- `${target.path}: this file has an ai-erd marker block that is not where ai-erd can find it again (inside a code fence, a quote, or edited). Left untouched and the earlier backup kept \u2014 move that block back to the top level, or remove it, then run again.`
3144
+ `${target.path}: this file has an ainecto marker block that is not where ainecto can find it again (inside a code fence, a quote, or edited). Left untouched and the earlier backup kept \u2014 move that block back to the top level, or remove it, then run again.`
2629
3145
  );
2630
3146
  continue;
2631
3147
  }
@@ -2635,7 +3151,7 @@ function planInit(input) {
2635
3151
  }
2636
3152
  const original = prior?.original ?? ownedText(blockNow);
2637
3153
  if (prior === void 0 && original.at !== "absent") {
2638
- notes.push(`${target.path}: an existing ai-erd block was saved and will be restored by --undo`);
3154
+ notes.push(`${target.path}: an existing ainecto block was saved and will be restored by --undo`);
2639
3155
  }
2640
3156
  record.files[target.path] = {
2641
3157
  scope: "block",
@@ -2672,11 +3188,11 @@ function planInit(input) {
2672
3188
  const currentValue = presentNow ? theirConfig[field] : void 0;
2673
3189
  const priorOriginal = priorConfig?.originalFields?.[field];
2674
3190
  const priorWritten = priorConfig?.writtenFields;
2675
- const untouchedByUser = priorWritten !== void 0 && hasField(priorWritten, field) && stableStringify(currentValue) === stableStringify(priorWritten[field]);
3191
+ const untouchedByUser = priorWritten !== void 0 && hasField(priorWritten, field) && stableStringify2(currentValue) === stableStringify2(priorWritten[field]);
2676
3192
  const next = untouchedByUser && priorOriginal !== void 0 ? priorOriginal : ownedJson(currentValue, presentNow);
2677
3193
  originalFields[field] = next;
2678
3194
  if (priorOriginal !== void 0 && !sameOwned(next, priorOriginal)) {
2679
- notes.push(`${HARNESS_CONFIG_PATH}: you changed ${field} \u2014 ai-erd now treats your value as the one to put back.`);
3195
+ notes.push(`${HARNESS_CONFIG_PATH}: you changed ${field} \u2014 ainecto now treats your value as the one to put back.`);
2680
3196
  }
2681
3197
  }
2682
3198
  const mergedConfig = serialize({ ...theirConfig ?? {}, ...ourFields });
@@ -2722,7 +3238,7 @@ function planUndo(input) {
2722
3238
  const notes = [];
2723
3239
  const tail = () => {
2724
3240
  notes.push(`Codex keeps its own global config \u2014 remove it with: ${codexRemoveCommand()}`);
2725
- notes.push(`If you set ${ROLE_ENV_VAR} anywhere, unset it too.`);
3241
+ notes.push(`If you set ${ROLE_ENV_VAR} (or the older ${LEGACY_ROLE_ENV_VAR}) anywhere, unset it too.`);
2726
3242
  };
2727
3243
  if (state.state === "absent") {
2728
3244
  notes.push(legacyManagedRecord(input.files.get(HARNESS_CONFIG_PATH)) ? legacyManagedNotice(HARNESS_CONFIG_PATH) : `No record of a previous init in ${HARNESS_RECORD_PATH} \u2014 no local file was changed.`);
@@ -2810,7 +3326,7 @@ function undoEntry(path, contract, existing) {
2810
3326
  if (sameOwned(currentOwned, contract.original)) {
2811
3327
  return {
2812
3328
  kind: "done",
2813
- note: contract.original.at === "absent" ? `${path}: the "${SERVER_NAME}" entry ai-erd added is already gone.` : `${path}: your original "${SERVER_NAME}" entry is already back in place.`
3329
+ note: contract.original.at === "absent" ? `${path}: the "${SERVER_NAME}" entry ainecto added is already gone.` : `${path}: your original "${SERVER_NAME}" entry is already back in place.`
2814
3330
  };
2815
3331
  }
2816
3332
  const goneButWeHoldTheOriginal = current === void 0 && contract.original.at !== "absent";
@@ -2840,13 +3356,13 @@ function undoBlock(path, contract, existing) {
2840
3356
  if (unexplainedMarkers(existing, contract) > 0) {
2841
3357
  return {
2842
3358
  kind: "unresolved",
2843
- note: `${path}: this file has an ai-erd marker block that --undo cannot account for (inside a code fence, a quote, or edited). Remove it by hand, then run --undo again.`
3359
+ note: `${path}: this file has an ainecto marker block that --undo cannot account for (inside a code fence, a quote, or edited). Remove it by hand, then run --undo again.`
2844
3360
  };
2845
3361
  }
2846
3362
  if (sameOwned(currentOwned, contract.original)) {
2847
3363
  return {
2848
3364
  kind: "done",
2849
- note: contract.original.at === "absent" ? `${path}: the ai-erd block is already gone.` : `${path}: the ai-erd block that was there before is already back in place.`
3365
+ note: contract.original.at === "absent" ? `${path}: the ainecto block is already gone.` : `${path}: the ainecto block that was there before is already back in place.`
2850
3366
  };
2851
3367
  }
2852
3368
  if (current === void 0 && contract.original.at === "text") {
@@ -2855,12 +3371,12 @@ function undoBlock(path, contract, existing) {
2855
3371
  ${contract.original.text}
2856
3372
  ` : `${contract.original.text}
2857
3373
  `;
2858
- return { kind: "write", content: body, note: `${path}: restored the ai-erd block that was there before.` };
3374
+ return { kind: "write", content: body, note: `${path}: restored the ainecto block that was there before.` };
2859
3375
  }
2860
3376
  if (!sameOwned(currentOwned, contract.written)) {
2861
3377
  return {
2862
3378
  kind: "unresolved",
2863
- note: `${path}: the ai-erd block was edited after init \u2014 left in place. Remove the block by hand if you meant to, then run --undo again.`
3379
+ note: `${path}: the ainecto block was edited after init \u2014 left in place. Remove the block by hand if you meant to, then run --undo again.`
2864
3380
  };
2865
3381
  }
2866
3382
  let result;
@@ -2872,7 +3388,7 @@ ${contract.original.text}
2872
3388
  if (!result.removed) {
2873
3389
  return {
2874
3390
  kind: "unresolved",
2875
- note: `${path}: the ai-erd block could not be removed \u2014 it is still in the file. Remove it by hand, then run --undo again.`
3391
+ note: `${path}: the ainecto block could not be removed \u2014 it is still in the file. Remove it by hand, then run --undo again.`
2876
3392
  };
2877
3393
  }
2878
3394
  if (result.emptied && contract.createdByUs) {
@@ -2881,7 +3397,7 @@ ${contract.original.text}
2881
3397
  return {
2882
3398
  kind: "write",
2883
3399
  content: result.content,
2884
- note: contract.original.at === "text" ? `${path}: restored the ai-erd block that was there before.` : void 0
3400
+ note: contract.original.at === "text" ? `${path}: restored the ainecto block that was there before.` : void 0
2885
3401
  };
2886
3402
  }
2887
3403
  function undoWhole(path, contract, existing) {
@@ -2911,14 +3427,14 @@ function undoFields(path, contract, existing) {
2911
3427
  if (parsed === void 0) {
2912
3428
  return {
2913
3429
  kind: "unresolved",
2914
- note: `${path} is not a JSON object any more, so ai-erd could not take its fields back out \u2014 the backup is kept. Fix the file and run --undo again.`
3430
+ note: `${path} is not a JSON object any more, so ainecto could not take its fields back out \u2014 the backup is kept. Fix the file and run --undo again.`
2915
3431
  };
2916
3432
  }
2917
3433
  const contractProblem = fieldsContractProblem(contract, OUR_CONFIG_FIELDS);
2918
3434
  if (contractProblem !== void 0) {
2919
3435
  return {
2920
3436
  kind: "unresolved",
2921
- note: `${path}: ai-erd's record of the fields it wrote ${contractProblem}, so its fields were left in place \u2014 the backup is kept. Restore that record, or remove the fields by hand.`
3437
+ note: `${path}: ainecto's record of the fields it wrote ${contractProblem}, so its fields were left in place \u2014 the backup is kept. Restore that record, or remove the fields by hand.`
2922
3438
  };
2923
3439
  }
2924
3440
  const written = contract.writtenFields;
@@ -2933,10 +3449,10 @@ function undoFields(path, contract, existing) {
2933
3449
  if (!hasField(written, field)) {
2934
3450
  return {
2935
3451
  kind: "unresolved",
2936
- note: `${path}: ai-erd has no record of what it wrote for ${field}, so its fields were left in place \u2014 the backup is kept. Restore that record, or remove the field by hand.`
3452
+ note: `${path}: ainecto has no record of what it wrote for ${field}, so its fields were left in place \u2014 the backup is kept. Restore that record, or remove the field by hand.`
2937
3453
  };
2938
3454
  }
2939
- const stillOurs = stableStringify(value) === stableStringify(written[field]);
3455
+ const stillOurs = stableStringify2(value) === stableStringify2(written[field]);
2940
3456
  if (!stillOurs) {
2941
3457
  next[field] = value;
2942
3458
  kept.push(field);
@@ -2946,7 +3462,7 @@ function undoFields(path, contract, existing) {
2946
3462
  if (original === void 0 || original.at !== "json" && original.at !== "absent") {
2947
3463
  return {
2948
3464
  kind: "unresolved",
2949
- note: `${path}: ai-erd cannot tell what was in ${field} before it wrote there, so the file was left as it is \u2014 the backup is kept. Remove the field by hand if you meant to.`
3465
+ note: `${path}: ainecto cannot tell what was in ${field} before it wrote there, so the file was left as it is \u2014 the backup is kept. Remove the field by hand if you meant to.`
2950
3466
  };
2951
3467
  }
2952
3468
  if (original.at === "json") {
@@ -2961,7 +3477,7 @@ function undoFields(path, contract, existing) {
2961
3477
  return body === existing ? { kind: "done", note } : {
2962
3478
  kind: "write",
2963
3479
  content: body,
2964
- note: note ?? `${path}: removed what ai-erd wrote and kept the rest.`
3480
+ note: note ?? `${path}: removed what ainecto wrote and kept the rest.`
2965
3481
  };
2966
3482
  }
2967
3483
  function withCreatedDirectories(current, directories) {
@@ -2981,27 +3497,98 @@ function parentsOf(paths) {
2981
3497
  }
2982
3498
  return [...directories].sort((left, right) => right.length - left.length);
2983
3499
  }
2984
- function detectRoles(files) {
2985
- const found = /* @__PURE__ */ new Map();
2986
- for (const target of AGENT_TARGETS) {
2987
- const existing = files.get(target.path);
2988
- if (existing === void 0) {
3500
+ function previousRole(path, existing) {
3501
+ const read = readConfigRole(path, existing);
3502
+ return read.kind === "role" ? read.role : void 0;
3503
+ }
3504
+
3505
+ // src/core/harness/legacyLayout.ts
3506
+ var LEGACY_PATHS = new Map(
3507
+ [HARNESS_DOC_PATH, HARNESS_CONFIG_PATH, HARNESS_RECORD_PATH].map((path) => [path, toLegacy(path)])
3508
+ );
3509
+ var LEGACY_RECORD_PATH = toLegacy(HARNESS_RECORD_PATH);
3510
+ var LEGACY_SNAPSHOT_PATHS = [...LEGACY_PATHS.values()];
3511
+ function adoptLegacyLayout(disk) {
3512
+ const files = /* @__PURE__ */ new Map();
3513
+ for (const [path, content] of disk) {
3514
+ if (!LEGACY_SNAPSHOT_PATHS.includes(path)) {
3515
+ files.set(path, content);
3516
+ }
3517
+ }
3518
+ const view = { files, moves: [], legacyDirCreatedByUs: false, notes: [] };
3519
+ if (!LEGACY_SNAPSHOT_PATHS.some((path) => disk.get(path) !== void 0)) {
3520
+ return view;
3521
+ }
3522
+ const legacyRecord = disk.get(LEGACY_RECORD_PATH);
3523
+ if (legacyRecord === void 0) {
3524
+ const legacyConfig = LEGACY_PATHS.get(HARNESS_CONFIG_PATH);
3525
+ if (disk.get(HARNESS_RECORD_PATH) === void 0 && legacyManagedRecord(disk.get(legacyConfig))) {
3526
+ return { ...view, refusal: legacyManagedNotice(legacyConfig) };
3527
+ }
3528
+ view.notes.push(
3529
+ `${LEGACY_HARNESS_DIR}/ was left in place \u2014 there is no ${LEGACY_RECORD_PATH} showing that an earlier version of this CLI wrote it. Remove it by hand if it is a leftover.`
3530
+ );
3531
+ return view;
3532
+ }
3533
+ const state = readRecord(legacyRecord, LEGACY_RECORD_PATH);
3534
+ if (state.state !== "valid") {
3535
+ return {
3536
+ ...view,
3537
+ refusal: state.state === "absent" ? `${LEGACY_RECORD_PATH} could not be read. Nothing was changed.` : `${state.reason}. Nothing was changed \u2014 move that file aside (or restore it) and run again.`
3538
+ };
3539
+ }
3540
+ const clash = [...LEGACY_PATHS].filter(([to, from]) => disk.get(to) !== void 0 && disk.get(from) !== void 0).map(([to]) => to);
3541
+ if (clash.length > 0) {
3542
+ return {
3543
+ ...view,
3544
+ refusal: `Both ${LEGACY_HARNESS_DIR}/ and ${HARNESS_DIR}/ hold AiNecto setup files (${clash.join(", ")}): an older version of this CLI (0.4.x) ran here after this one, or a move was interrupted. Nothing was changed. Undo the older setup with \`npx -y -p @ai-erd/mcp@0.4.6 ai-erd init --undo\` (or remove the ${LEGACY_HARNESS_DIR}/ leftovers by hand), then run again.`
3545
+ };
3546
+ }
3547
+ const record = state.record;
3548
+ const renamed = {
3549
+ ...record,
3550
+ files: Object.fromEntries(Object.entries(record.files).map(([path, contract]) => [toCurrent(path), contract])),
3551
+ // «하네스 폴더를 우리가 만들었다»는 같은 사실이다 — 이름만 옮긴다. 새 폴더를 이번에 만들면 적용이 또 적는다.
3552
+ createdDirectories: record.createdDirectories.map(toCurrent)
3553
+ };
3554
+ const moves = [];
3555
+ for (const [to, from] of LEGACY_PATHS) {
3556
+ const content = disk.get(from);
3557
+ if (content === void 0) {
2989
3558
  continue;
2990
3559
  }
2991
- found.set(target.path, previousRole(existing));
3560
+ files.set(to, to === HARNESS_RECORD_PATH ? serializeRecord(renamed) : content);
3561
+ moves.push({ from, to });
2992
3562
  }
2993
- return found;
3563
+ return {
3564
+ files,
3565
+ moves,
3566
+ legacyDirCreatedByUs: record.createdDirectories.includes(LEGACY_HARNESS_DIR),
3567
+ notes: [`${LEGACY_HARNESS_DIR}/ \u2192 ${HARNESS_DIR}/: moved what an earlier version of this CLI wrote (the folder was renamed).`]
3568
+ };
2994
3569
  }
2995
- function previousRole(existing) {
2996
- if (existing === void 0) {
2997
- return void 0;
2998
- }
2999
- try {
3000
- const parsed = JSON.parse(existing);
3001
- return readRoleFromEntry(parsed.mcpServers?.[SERVER_NAME]);
3002
- } catch {
3003
- return void 0;
3570
+ function withLegacyMoves(plan, view) {
3571
+ if (view.moves.length === 0) {
3572
+ return view.notes.length === 0 ? plan : { ...plan, notes: [...view.notes, ...plan.notes] };
3004
3573
  }
3574
+ const movedTo = new Set(view.moves.map((move) => move.to));
3575
+ const planned = /* @__PURE__ */ new Set([...plan.writes.map((write) => write.path), ...plan.deletes]);
3576
+ const carried = view.moves.filter((move) => !planned.has(move.to)).map((move) => ({ path: move.to, content: view.files.get(move.to), existed: false }));
3577
+ return {
3578
+ ...plan,
3579
+ // 새 경로는 디스크에 아직 없다 — «만든다»가 사실이다.
3580
+ writes: [...plan.writes.map((write) => movedTo.has(write.path) ? { ...write, existed: false } : write), ...carried],
3581
+ // 새 경로의 삭제는 디스크에 없는 파일이다 — 대신 옛 파일을 지운다.
3582
+ deletes: [...plan.deletes.filter((path) => !movedTo.has(path)), ...view.moves.map((move) => move.from)],
3583
+ notes: [...view.notes, ...plan.notes],
3584
+ removableDirectories: view.legacyDirCreatedByUs ? [...plan.removableDirectories ?? [], LEGACY_HARNESS_DIR] : plan.removableDirectories
3585
+ };
3586
+ }
3587
+ function toLegacy(path) {
3588
+ return `${LEGACY_HARNESS_DIR}${path.slice(HARNESS_DIR.length)}`;
3589
+ }
3590
+ function toCurrent(path) {
3591
+ return path === LEGACY_HARNESS_DIR || path.startsWith(`${LEGACY_HARNESS_DIR}/`) ? `${HARNESS_DIR}${path.slice(LEGACY_HARNESS_DIR.length)}` : path;
3005
3592
  }
3006
3593
 
3007
3594
  // src/core/harness/documentFetch.ts
@@ -3057,36 +3644,143 @@ function usableBodyProblem(key, entry) {
3057
3644
  return documentContractProblem(key, entry.body);
3058
3645
  }
3059
3646
 
3647
+ // src/adapters/cli/prompter.ts
3648
+ import { createInterface as createInterface3 } from "readline";
3649
+ var PromptCancelled = class extends Error {
3650
+ constructor() {
3651
+ super("Cancelled \u2014 no repository files were written. A sign-in you already completed stays saved.");
3652
+ this.name = "PromptCancelled";
3653
+ }
3654
+ };
3655
+ var AGENT_ENV_MARKERS = ["CLAUDECODE", "CI", "GEMINI_CLI", "CODEX_SANDBOX", "CURSOR_AGENT"];
3656
+ function agentEnvironment(env = process.env) {
3657
+ return AGENT_ENV_MARKERS.find((name) => {
3658
+ const value = env[name]?.trim().toLowerCase();
3659
+ return value !== void 0 && value !== "" && value !== "0" && value !== "false";
3660
+ });
3661
+ }
3662
+ function isInteractive(io, json, env = process.env) {
3663
+ return io.stdin?.isTTY === true && io.stdout?.isTTY === true && !json && agentEnvironment(env) === void 0;
3664
+ }
3665
+ function createReadlinePrompter(io) {
3666
+ let rl;
3667
+ let lines;
3668
+ const readLine = async (prompt) => {
3669
+ io.stderr.write(prompt);
3670
+ if (!rl) {
3671
+ rl = createInterface3({ input: io.stdin, terminal: false });
3672
+ lines = rl[Symbol.asyncIterator]();
3673
+ }
3674
+ const next = await lines.next();
3675
+ if (next.done) {
3676
+ io.stderr.write("\n");
3677
+ throw new PromptCancelled();
3678
+ }
3679
+ return next.value;
3680
+ };
3681
+ return {
3682
+ choose: (question, choices, defaultIndex) => chooseWith(readLine, (text) => io.stderr.write(text), question, choices, defaultIndex),
3683
+ text: async (question, defaultValue) => {
3684
+ const answer = (await readLine(`${question} [${defaultValue}]: `)).trim();
3685
+ return answer || defaultValue;
3686
+ },
3687
+ close: () => {
3688
+ rl?.close();
3689
+ }
3690
+ };
3691
+ }
3692
+ async function chooseWith(readLine, write, question, choices, defaultIndex) {
3693
+ if (choices.length === 0) {
3694
+ throw new Error(`Nothing to choose for: ${question}`);
3695
+ }
3696
+ write(`${question}
3697
+ `);
3698
+ choices.forEach((choice, index) => write(` [${index + 1}] ${choice.label}
3699
+ `));
3700
+ const hint = defaultIndex === void 0 ? "" : ` [${defaultIndex + 1}]`;
3701
+ for (; ; ) {
3702
+ const answer = (await readLine(`Enter a number (1-${choices.length})${hint}: `)).trim();
3703
+ if (answer === "" && defaultIndex !== void 0) {
3704
+ return choices[defaultIndex].value;
3705
+ }
3706
+ const picked = /^\d+$/.test(answer) ? Number(answer) : NaN;
3707
+ if (picked >= 1 && picked <= choices.length) {
3708
+ return choices[picked - 1].value;
3709
+ }
3710
+ write(answer === "" ? "Please choose one \u2014 there is no default here.\n" : `"${answer}" is not one of the numbers above.
3711
+ `);
3712
+ }
3713
+ }
3714
+
3060
3715
  // src/adapters/cli/initCommand.ts
3716
+ function singleRole(roles) {
3717
+ return roles.length === 1 ? roles[0] : void 0;
3718
+ }
3061
3719
  async function executeInitCommand(options) {
3720
+ try {
3721
+ return await runInit(options);
3722
+ } catch (error) {
3723
+ if (error instanceof PromptCancelled) {
3724
+ options.io.stderr.write(`${error.message}
3725
+ `);
3726
+ return 130;
3727
+ }
3728
+ throw error;
3729
+ } finally {
3730
+ options.prompter?.close();
3731
+ }
3732
+ }
3733
+ async function runInit(initialOptions) {
3734
+ let options = initialOptions;
3062
3735
  const args = parseInitArgs(options.argv);
3063
- const root = resolve(options.cwd);
3064
- const files = await readSnapshot(root);
3736
+ const root = resolve2(options.cwd);
3737
+ const rootNotes = await repositoryRootNotes(root);
3738
+ const disk = await readSnapshot(root);
3739
+ const layout = adoptLegacyLayout(disk);
3740
+ const files = layout.files;
3065
3741
  if (args.undo) {
3066
- const plan2 = planUndo({ files });
3067
- await applyPlan(root, plan2, args.dryRun, files, false);
3742
+ if (layout.refusal !== void 0) {
3743
+ options.io.stdout.write(renderSuccess(
3744
+ { action: args.dryRun ? "undo (dry-run)" : "undo", updated: [], removed: [], notes: [...rootNotes, layout.refusal] },
3745
+ { json: options.json }
3746
+ ));
3747
+ return 0;
3748
+ }
3749
+ const plan2 = withLegacyMoves(planUndo({ files }), layout);
3750
+ await applyPlan(root, plan2, args.dryRun, disk, false);
3068
3751
  options.io.stdout.write(renderSuccess(
3069
3752
  {
3070
3753
  action: args.dryRun ? "undo (dry-run)" : "undo",
3071
3754
  updated: plan2.writes.map((write) => write.path),
3072
3755
  removed: plan2.deletes,
3073
- notes: plan2.notes
3756
+ notes: [...rootNotes, ...plan2.notes]
3074
3757
  },
3075
3758
  { json: options.json }
3076
3759
  ));
3077
3760
  return 0;
3078
3761
  }
3079
- const role = options.role ?? currentRole(files);
3762
+ const refusal = layout.refusal ?? initRefusal(files);
3763
+ if (refusal !== void 0) {
3764
+ throw new Error(refusal);
3765
+ }
3766
+ const prompter = options.prompter;
3767
+ const role = options.role ?? (prompter ? await askRole(prompter, singleRole(rolesInFiles(files).roles)) : currentRole(files));
3768
+ const previousRole2 = (() => {
3769
+ try {
3770
+ return currentRole(files);
3771
+ } catch {
3772
+ return void 0;
3773
+ }
3774
+ })();
3080
3775
  if (!role) {
3081
3776
  throw new Error(
3082
- `--role is required on first run. Pick one of: ${HARNESS_ROLES.join(", ")}.`
3777
+ `No role given. Ask the user which role this repository's AI sessions should have (${HARNESS_ROLES.join(", ")}), then re-run with --role <role>.`
3083
3778
  );
3084
3779
  }
3085
- const refusal = initRefusal(files);
3086
- if (refusal !== void 0) {
3087
- throw new Error(refusal);
3088
- }
3089
- const project = await resolveProject(options, args, files, root);
3780
+ const connected = connectAs(options, role);
3781
+ options = connected;
3782
+ await ensureSignedIn(connected, role, args.dryRun);
3783
+ const project = await resolveProject(connected, args, files, root, role);
3090
3784
  if ("choices" in project) {
3091
3785
  options.io.stdout.write(renderSuccess(
3092
3786
  {
@@ -3100,10 +3794,12 @@ async function executeInitCommand(options) {
3100
3794
  const documents = args.dryRun ? { versions: {}, fallbacks: {}, unavailableReason: "skipped in --dry-run" } : await fetchHarnessDocuments({
3101
3795
  endpoint: options.endpoint,
3102
3796
  accessToken: await options.accessToken?.(),
3797
+ // ⛔이 이름은 바꾸지 않는다 — 서버가 문서의 클라이언트별 변형 키(HARNESS.DOC.AI_ERD_CLI 등)를 이것으로
3798
+ // 찾는다. 사람에게 안 보이는 서버 쪽 식별자다(역할 헤더·scope 와 같은 이유, 2026-10-11).
3103
3799
  client: "ai-erd-cli",
3104
3800
  fetchImpl: options.fetchImpl
3105
3801
  });
3106
- const plan = planInit({
3802
+ const plan = withLegacyMoves(planInit({
3107
3803
  role,
3108
3804
  env: options.env,
3109
3805
  projectName: project.name,
@@ -3112,11 +3808,15 @@ async function executeInitCommand(options) {
3112
3808
  cliVersion: options.cliVersion,
3113
3809
  files,
3114
3810
  documents
3115
- });
3811
+ }), layout);
3116
3812
  if (plan.refusal !== void 0) {
3117
3813
  throw new Error(plan.refusal);
3118
3814
  }
3119
- await applyPlan(root, plan, args.dryRun, files);
3815
+ await applyPlan(root, plan, args.dryRun, disk);
3816
+ if (!args.dryRun && previousRole2 !== void 0 && previousRole2 !== role) {
3817
+ options.io.stderr.write(`Role changes from ${previousRole2} to ${role}: \`ainecto\` commands now, MCP tools from a new session.
3818
+ `);
3819
+ }
3120
3820
  options.io.stdout.write(renderSuccess(
3121
3821
  {
3122
3822
  action: args.dryRun ? "init (dry-run)" : "init",
@@ -3124,42 +3824,45 @@ async function executeInitCommand(options) {
3124
3824
  project: { uuid: project.uuid, name: project.name },
3125
3825
  endpoint: options.endpoint,
3126
3826
  written: plan.writes.map((write) => `${write.existed ? "updated" : "created"} ${write.path}`),
3827
+ // 옛 폴더(.ai-erd/)를 옮길 때만 생긴다 — 지운 것을 말하지 않으면 사용자는 옛 파일이 어디 갔는지 모른다.
3828
+ ...plan.deletes.length > 0 ? { removed: plan.deletes } : {},
3127
3829
  codex: `Codex keeps a single global config, so init does not touch it. Run: ${plan.codexCommand}`,
3128
3830
  codexPerSession: plan.codexProfile,
3129
3831
  codexEnvFallback: `Without a profile, start the session with ${ROLE_ENV_VAR}=${role}.`,
3130
3832
  // ⚠조용히 기본값으로 떨어지지 않는다 — 서버에서 규칙을 고쳐도 안 바뀌는 이유를
3131
3833
  // 사용자가 영영 못 찾게 된다. ★요청 자체의 실패와 «문서별» 되돌림을 둘 다 적는다
3132
3834
  // (2026-09-23 독립 재리뷰 I5: HTTP 200 으로 온 빈 문서가 조용히 패키지 판이 됐다).
3133
- notes: [...plan.notes, ...fallbackNotes(documents)],
3835
+ notes: [...rootNotes, ...plan.notes, ...fallbackNotes(documents)],
3836
+ // ★「Enforce this on the server too: ainecto auth login …」 줄은 뺐다. init 이 이미 그 역할로
3837
+ // 로그인했으므로 참이 아니고, 남아 있으면 에이전트가 따라 쳐서 사람이 한 번 더 승인하게 된다
3838
+ // (2026-09-29 독립 리뷰 P1-1).
3839
+ // ★재시작 없이 이어간다(2026-09-30 사용자 결정). 셸의 `ainecto` 는 부를 때마다 저장소 역할을 읽어
3840
+ // 역할 헤더로 싣는다 — 서버가 요청마다 강제하므로 MCP 연결과 무관하게 이 세션에서 바로 맞다.
3841
+ // 재시작은 «그 역할의 MCP 도구»를 더할 뿐인 선택이다.
3134
3842
  next: [
3135
- "Restart the agent so it picks up the new MCP server.",
3136
- role === "design" ? "Ask it to change a table \u2014 a Design session may, and the change lands in AI-ERD." : `Ask it to change a table \u2014 a ${role} session will be told to stop.`,
3137
- `Enforce this on the server too: ${loginCommand(options, role)}`,
3138
- "Undo everything with: ai-erd init --undo"
3843
+ `Keep working in this session \u2014 no restart is needed. \`ainecto\` commands run from this repository use the ${role} role at once.`,
3844
+ `Read ${HARNESS_DOC_PATH} first, then \`ainecto tools list\` shows the role's tools and \`ainecto tools call <tool> '<json>'\` calls one.`,
3845
+ "Do not use MCP tools this session started with for this work \u2014 they keep the role (or no role) the session started with.",
3846
+ "Optional, to add the role's MCP tools: start a new session.",
3847
+ `Claude Code: exit and run \`claude -c\` in this folder, then approve the "${SERVER_NAME}" server from .mcp.json when asked.`,
3848
+ `Codex: save the profile above as $CODEX_HOME/${role}.config.toml and start with \`codex -p ${role}\`.`,
3849
+ role === "design" ? "To check the role, change a table through `ainecto` \u2014 Design may, and the change lands in AiNecto." : `To check the role, try changing a table through \`ainecto\` \u2014 ${role} is told to stop.`,
3850
+ `If the new Claude Code session still has no role, run \`claude mcp get ${SERVER_NAME}\`: a local-scope "${SERVER_NAME}" entry hides .mcp.json (remove it with \`claude mcp remove ${SERVER_NAME} -s local\`).`,
3851
+ // ★서버 이름이 바뀌었다(0.5.0). 예전에 같은 이름("ai-erd")이라 .mcp.json 이 덮던 사용자·Codex 전역 항목이
3852
+ // 이제는 «따로» 붙어, 그 세션이 역할 없는 서버를 하나 더 본다. 그 항목을 지우는 법을 같이 말한다.
3853
+ `If you added AiNecto earlier as "${LEGACY_SERVER_NAME}" (Claude Code user scope, or Codex), remove it \u2014 it no longer hides behind this "${SERVER_NAME}" entry: \`claude mcp remove ${LEGACY_SERVER_NAME} -s user\`, \`codex mcp remove ${LEGACY_SERVER_NAME}\`.`,
3854
+ "Undo everything with: ainecto init --undo"
3139
3855
  ]
3140
3856
  },
3141
3857
  { json: options.json }
3142
3858
  ));
3143
3859
  return 0;
3144
3860
  }
3145
- async function readRepositoryRole(cwd) {
3146
- const root = resolve(cwd);
3147
- const files = /* @__PURE__ */ new Map();
3148
- for (const target of AGENT_TARGETS) {
3149
- files.set(target.path, await readManagedFile(root, target.path));
3150
- }
3151
- try {
3152
- return currentRole(files);
3153
- } catch {
3154
- return void 0;
3155
- }
3156
- }
3157
3861
  function currentRole(files) {
3158
- const found = [...detectRoles(files).values()].filter((role) => role !== void 0);
3159
- const unique = [...new Set(found)];
3862
+ const unique = rolesInFiles(files).roles;
3160
3863
  if (unique.length > 1) {
3161
3864
  throw new Error(
3162
- `Agent configs disagree about the session role (${unique.join(", ")}). Re-run with an explicit --role to line them up.`
3865
+ `Agent configs in this repository disagree about the session role (${unique.join(", ")}). Ask the user which role this repository's AI sessions should have (${HARNESS_ROLES.join(", ")}), then re-run with --role <role> to line them up.`
3163
3866
  );
3164
3867
  }
3165
3868
  return unique[0];
@@ -3174,13 +3877,108 @@ function fallbackNotes(documents) {
3174
3877
  }
3175
3878
  return notes;
3176
3879
  }
3177
- async function resolveProject(options, args, files, root) {
3880
+ async function ensureSignedIn(options, role, dryRun) {
3881
+ if (options.login === void 0) {
3882
+ return;
3883
+ }
3884
+ if (await options.accessToken?.()) {
3885
+ return;
3886
+ }
3887
+ if (dryRun) {
3888
+ throw new Error(
3889
+ `No usable AiNecto sign-in on this machine, and --dry-run neither refreshes it nor opens a browser.
3890
+
3891
+ ${loginCommand(options)}
3892
+
3893
+ Then run init again.`
3894
+ );
3895
+ }
3896
+ await options.login();
3897
+ options.io.stderr.write("Signed in.\n");
3898
+ }
3899
+ async function repositoryRootNotes(cwd) {
3900
+ const gitRoot = await findGitRoot(cwd);
3901
+ if (gitRoot === void 0) {
3902
+ return [`${cwd} is not inside a git repository, so this folder was used as the repository root.`];
3903
+ }
3904
+ if (gitRoot !== cwd) {
3905
+ throw new Error(
3906
+ `This folder is inside the git repository at ${gitRoot}, not at its root. Agents read their MCP config from the repository root, so run init there:
3907
+
3908
+ cd ${gitRoot}
3909
+
3910
+ Nothing was changed.`
3911
+ );
3912
+ }
3913
+ return [];
3914
+ }
3915
+ function connectAs(options, role) {
3916
+ if (options.connect) {
3917
+ const connection = options.connect(role);
3918
+ return { ...options, ...connection };
3919
+ }
3920
+ if (!options.client) {
3921
+ throw new Error("init needs either a client or a way to connect once the role is known.");
3922
+ }
3923
+ return { ...options, client: options.client };
3924
+ }
3925
+ var ROLE_CHOICES = [
3926
+ { label: "Design requirements, ERD, boundaries, tasks", value: "design" },
3927
+ { label: "Development product code, following the approved design", value: "development" },
3928
+ { label: "Test test scenarios and test code", value: "test" },
3929
+ { label: "Validation judges the result; changes nothing", value: "validation" }
3930
+ ];
3931
+ async function askRole(prompter, current) {
3932
+ const index = current === void 0 ? void 0 : ROLE_CHOICES.findIndex((choice) => choice.value === current);
3933
+ return prompter.choose(
3934
+ current === void 0 ? "Which role should AI sessions in this repository have?" : `Which role should AI sessions in this repository have? (now: ${current})`,
3935
+ ROLE_CHOICES,
3936
+ index
3937
+ );
3938
+ }
3939
+ async function askProject(options, args, projects, bound, root, role, prompter) {
3940
+ const canCreate = role === "design" && !args.dryRun;
3941
+ const askNameAndCreate = async () => {
3942
+ const name = await prompter.text("New project name", args.projectName ?? basenameOf(root));
3943
+ return createProject(options, name);
3944
+ };
3945
+ if (projects.length === 0) {
3946
+ if (role !== "design") {
3947
+ throw new Error(noProjectForRole(options, role));
3948
+ }
3949
+ if (args.dryRun) {
3950
+ throw new Error(dryRunCannotCreate(args.projectName ?? basenameOf(root)));
3951
+ }
3952
+ return askNameAndCreate();
3953
+ }
3954
+ const choices = projects.map((project) => ({
3955
+ label: `${project.name} (${project.uuid})`,
3956
+ value: { project }
3957
+ }));
3958
+ if (canCreate) {
3959
+ choices.push({ label: "Create a new project", value: { create: true } });
3960
+ }
3961
+ const boundIndex = bound === void 0 ? -1 : projects.findIndex((project) => project.uuid === bound);
3962
+ const defaultIndex = boundIndex >= 0 ? boundIndex : projects.length === 1 ? 0 : void 0;
3963
+ const picked = await prompter.choose("Which AiNecto project should this repository use?", choices, defaultIndex);
3964
+ return "create" in picked ? askNameAndCreate() : picked.project;
3965
+ }
3966
+ function noProjectForRole(options, role) {
3967
+ return `No project found in your AiNecto account, and only a Design session can create one. Ask the user: create the project at ${new URL(options.endpoint).origin} or in a Design session, then run init again with --role ${role}.`;
3968
+ }
3969
+ function dryRunCannotCreate(name) {
3970
+ return `--dry-run cannot continue: there is no project to bind, and creating "${name}" would be a real change. Re-run without --dry-run, or pass --project <uuid>.`;
3971
+ }
3972
+ async function resolveProject(options, args, files, root, role) {
3178
3973
  const projects = await listProjects(options);
3179
3974
  if (args.projectUuid) {
3180
3975
  const found = projects.find((project) => project.uuid === args.projectUuid);
3181
3976
  return found ?? { uuid: args.projectUuid, name: args.projectName ?? args.projectUuid };
3182
3977
  }
3183
3978
  const bound = boundProjectUuid(files.get(HARNESS_CONFIG_PATH));
3979
+ if (options.prompter) {
3980
+ return askProject(options, args, projects, bound, root, role, options.prompter);
3981
+ }
3184
3982
  if (bound) {
3185
3983
  const found = projects.find((project) => project.uuid === bound);
3186
3984
  if (found) {
@@ -3193,16 +3991,17 @@ async function resolveProject(options, args, files, root) {
3193
3991
  if (projects.length > 1) {
3194
3992
  return { choices: projects };
3195
3993
  }
3994
+ if (role !== "design") {
3995
+ throw new Error(noProjectForRole(options, role));
3996
+ }
3196
3997
  const name = args.projectName ?? basenameOf(root);
3197
3998
  if (!args.yes) {
3198
3999
  throw new Error(
3199
- `No project found in your AI-ERD account. Re-run with --yes to create "${name}", or --project-name <name> --yes to pick the name.`
4000
+ `No project found in your AiNecto account. Re-run with --yes to create "${name}", or --project-name <name> --yes to pick the name.`
3200
4001
  );
3201
4002
  }
3202
4003
  if (args.dryRun) {
3203
- throw new Error(
3204
- `--dry-run cannot continue: there is no project to bind, and creating "${name}" would be a real change. Re-run without --dry-run, or pass --project <uuid>.`
3205
- );
4004
+ throw new Error(dryRunCannotCreate(name));
3206
4005
  }
3207
4006
  return createProject(options, name);
3208
4007
  }
@@ -3218,17 +4017,17 @@ async function callOrExplainSignIn(options, run) {
3218
4017
  throw error;
3219
4018
  }
3220
4019
  throw new Error(
3221
- `Not signed in for this role, so ${options.endpoint} refused the request (HTTP 401).
4020
+ `Not signed in, so ${options.endpoint} refused the request (HTTP 401).
3222
4021
 
3223
- ${loginCommand(options, options.role)}
4022
+ ${loginCommand(options)}
3224
4023
 
3225
- Then run init again. The role is carried by the access token, so each role signs in once.`
4024
+ Then run init again. Sign-in is one per machine; the role comes from this repository.`
3226
4025
  );
3227
4026
  }
3228
4027
  }
3229
- function loginCommand(options, role) {
4028
+ function loginCommand(options) {
3230
4029
  const target = isDefaultEndpointFor(options.env, options.endpoint) ? options.env === "prod" ? "" : ` --env ${options.env}` : ` --endpoint ${options.endpoint}`;
3231
- return `ai-erd auth login --role ${role ?? "<design|development|test|validation>"}${target}`;
4030
+ return `ainecto auth login${target}`;
3232
4031
  }
3233
4032
  function isUnauthorized(error) {
3234
4033
  return error instanceof McpRpcError && error.code === "MCP_HTTP_ERROR" && error.details?.status === 401;
@@ -3240,14 +4039,16 @@ async function createProject(options, name) {
3240
4039
  if (!first) {
3241
4040
  throw new Error("Project creation did not return a project uuid.");
3242
4041
  }
4042
+ options.io.stderr.write(`Created project ${first.name} (${first.uuid}).
4043
+ `);
3243
4044
  return first;
3244
4045
  }
3245
4046
  function unwrapToolJson(result) {
3246
- if (!isRecord8(result) || !Array.isArray(result.content)) {
4047
+ if (!isRecord9(result) || !Array.isArray(result.content)) {
3247
4048
  return result;
3248
4049
  }
3249
- const first = result.content.find((item) => isRecord8(item) && item.type === "text");
3250
- if (!isRecord8(first) || typeof first.text !== "string") {
4050
+ const first = result.content.find((item) => isRecord9(item) && item.type === "text");
4051
+ if (!isRecord9(first) || typeof first.text !== "string") {
3251
4052
  return result;
3252
4053
  }
3253
4054
  try {
@@ -3257,13 +4058,13 @@ function unwrapToolJson(result) {
3257
4058
  }
3258
4059
  }
3259
4060
  function extractProjects(value) {
3260
- const candidates = Array.isArray(value) ? value : isRecord8(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
4061
+ const candidates = Array.isArray(value) ? value : isRecord9(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
3261
4062
  const nested = value[key];
3262
4063
  return Array.isArray(nested) ? nested : [];
3263
4064
  }) : [];
3264
4065
  const projects = [];
3265
4066
  for (const candidate of candidates) {
3266
- if (isRecord8(candidate) && typeof candidate.uuid === "string") {
4067
+ if (isRecord9(candidate) && typeof candidate.uuid === "string") {
3267
4068
  projects.push({
3268
4069
  uuid: candidate.uuid,
3269
4070
  name: typeof candidate.name === "string" ? candidate.name : candidate.uuid
@@ -3285,52 +4086,11 @@ function boundProjectUuid(config) {
3285
4086
  }
3286
4087
  async function readSnapshot(root) {
3287
4088
  const snapshot = /* @__PURE__ */ new Map();
3288
- for (const path of MANAGED_PATHS) {
4089
+ for (const path of [...MANAGED_PATHS, ...LEGACY_SNAPSHOT_PATHS]) {
3289
4090
  snapshot.set(path, await readManagedFile(root, path));
3290
4091
  }
3291
4092
  return snapshot;
3292
4093
  }
3293
- async function readManagedFile(root, path) {
3294
- await assertInsideRepository(root, path);
3295
- return readIfExists(join2(root, path));
3296
- }
3297
- async function readIfExists(absolutePath) {
3298
- try {
3299
- return await readFile4(absolutePath, "utf8");
3300
- } catch (error) {
3301
- if (isRecord8(error) && error.code === "ENOENT") {
3302
- return void 0;
3303
- }
3304
- throw error;
3305
- }
3306
- }
3307
- async function assertInsideRepository(root, relativePath) {
3308
- const absolutePath = join2(root, relativePath);
3309
- const realRoot = await realpath(root);
3310
- let realParent;
3311
- try {
3312
- realParent = await realpath(dirname2(absolutePath));
3313
- } catch (error) {
3314
- if (isRecord8(error) && error.code === "ENOENT") {
3315
- return;
3316
- }
3317
- throw error;
3318
- }
3319
- if (realParent !== realRoot && !realParent.startsWith(realRoot + sep)) {
3320
- throw new Error(
3321
- `${relativePath} resolves outside the repository (${realParent}); refusing to touch it.`
3322
- );
3323
- }
3324
- try {
3325
- if ((await lstat(absolutePath)).isSymbolicLink()) {
3326
- throw new Error(`${relativePath} is a symbolic link; refusing to write through it.`);
3327
- }
3328
- } catch (error) {
3329
- if (!isRecord8(error) || error.code !== "ENOENT") {
3330
- throw error;
3331
- }
3332
- }
3333
- }
3334
4094
  async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3335
4095
  for (const write of plan.writes) {
3336
4096
  await assertInsideRepository(root, write.path);
@@ -3345,9 +4105,9 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3345
4105
  }
3346
4106
  const applied = [];
3347
4107
  try {
3348
- const isRecord9 = (path) => !recordFirst && path === HARNESS_RECORD_PATH;
3349
- const recordWrites = plan.writes.filter((write) => isRecord9(write.path));
3350
- const recordDeletes = plan.deletes.filter((path) => isRecord9(path));
4108
+ const isRecord10 = (path) => !recordFirst && (path === HARNESS_RECORD_PATH || path === LEGACY_RECORD_PATH);
4109
+ const recordWrites = plan.writes.filter((write) => isRecord10(write.path));
4110
+ const recordDeletes = plan.deletes.filter((path) => isRecord10(path));
3351
4111
  const createdDirectories = [...plan.record?.createdDirectories ?? []];
3352
4112
  const rememberDirectories = (directories) => {
3353
4113
  for (const directory of directories) {
@@ -3383,14 +4143,14 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3383
4143
  }
3384
4144
  for (const path of deletes) {
3385
4145
  await assertUnchangedSinceSnapshot(root, path, snapshot);
3386
- await rm2(join2(root, path), { force: true });
4146
+ await rm2(join3(root, path), { force: true });
3387
4147
  applied.push(path);
3388
4148
  }
3389
4149
  };
3390
4150
  try {
3391
4151
  await apply(
3392
- orderedWrites(plan.writes.filter((write) => !isRecord9(write.path)), recordFirst),
3393
- plan.deletes.filter((path) => !isRecord9(path))
4152
+ orderedWrites(plan.writes.filter((write) => !isRecord10(write.path)), recordFirst),
4153
+ plan.deletes.filter((path) => !isRecord10(path))
3394
4154
  );
3395
4155
  await recordDirectoriesOrExplain(false);
3396
4156
  await apply(recordWrites, recordDeletes);
@@ -3406,7 +4166,7 @@ Already applied before this failure: ${applied.join(", ") || "(nothing)"}`
3406
4166
  );
3407
4167
  }
3408
4168
  for (const directory of plan.removableDirectories ?? []) {
3409
- await rmdir(join2(root, directory)).catch(() => void 0);
4169
+ await rmdir(join3(root, directory)).catch(() => void 0);
3410
4170
  }
3411
4171
  }
3412
4172
  function orderedWrites(writes, recordFirst) {
@@ -3418,7 +4178,7 @@ async function assertUnchangedSinceSnapshot(root, relativePath, snapshot) {
3418
4178
  if (!snapshot.has(relativePath)) {
3419
4179
  return;
3420
4180
  }
3421
- const now = await readIfExists(join2(root, relativePath));
4181
+ const now = await readIfExists(join3(root, relativePath));
3422
4182
  if (now !== snapshot.get(relativePath)) {
3423
4183
  throw new Error(
3424
4184
  `${relativePath} changed while init was running; nothing was written. Run init again.`
@@ -3426,8 +4186,8 @@ async function assertUnchangedSinceSnapshot(root, relativePath, snapshot) {
3426
4186
  }
3427
4187
  }
3428
4188
  async function writeManagedFile(root, write, onDirectoriesCreated) {
3429
- const absolutePath = join2(root, write.path);
3430
- const targetDirectory = dirname2(absolutePath);
4189
+ const absolutePath = join3(root, write.path);
4190
+ const targetDirectory = dirname3(absolutePath);
3431
4191
  const firstCreated = await mkdir2(targetDirectory, { recursive: true });
3432
4192
  const created = createdChain(root, firstCreated, targetDirectory);
3433
4193
  onDirectoriesCreated?.(created);
@@ -3444,14 +4204,14 @@ function createdChain(root, firstCreated, targetDirectory) {
3444
4204
  if (firstCreated === void 0) {
3445
4205
  return [];
3446
4206
  }
3447
- const stop = resolve(firstCreated);
4207
+ const stop = resolve2(firstCreated);
3448
4208
  const made = [];
3449
- for (let at = resolve(targetDirectory); ; at = dirname2(at)) {
4209
+ for (let at = resolve2(targetDirectory); ; at = dirname3(at)) {
3450
4210
  const asRelative = relative(root, at);
3451
4211
  if (asRelative && !asRelative.startsWith("..")) {
3452
- made.push(asRelative.split(sep).join("/"));
4212
+ made.push(asRelative.split(sep2).join("/"));
3453
4213
  }
3454
- if (at === stop || dirname2(at) === at) {
4214
+ if (at === stop || dirname3(at) === at) {
3455
4215
  break;
3456
4216
  }
3457
4217
  }
@@ -3461,7 +4221,7 @@ async function fileMode(absolutePath) {
3461
4221
  try {
3462
4222
  return (await stat3(absolutePath)).mode & 4095;
3463
4223
  } catch (error) {
3464
- if (isRecord8(error) && error.code === "ENOENT") {
4224
+ if (isRecord9(error) && error.code === "ENOENT") {
3465
4225
  return void 0;
3466
4226
  }
3467
4227
  throw error;
@@ -3499,43 +4259,53 @@ function requireValue3(flag, value) {
3499
4259
  }
3500
4260
  function basenameOf(path) {
3501
4261
  const parts = path.split(/[\\/]/).filter(Boolean);
3502
- return parts[parts.length - 1] ?? "ai-erd-project";
4262
+ return parts[parts.length - 1] ?? "ainecto-project";
3503
4263
  }
3504
- function isRecord8(value) {
4264
+ function isRecord9(value) {
3505
4265
  return typeof value === "object" && value !== null && !Array.isArray(value);
3506
4266
  }
3507
4267
 
3508
- // src/core/version.ts
3509
- var CLI_VERSION = true ? "0.3.0" : "dev";
3510
- var CLI_USER_AGENT = `@ai-erd/mcp ${CLI_VERSION}`;
3511
-
3512
4268
  // src/adapters/cli/ainectoCli.ts
3513
4269
  async function runAinectoCli(argv, io) {
3514
- let parsed = { json: argv.includes("--json"), help: false, positionals: [] };
4270
+ let parsed = { json: argv.includes("--json"), help: false, version: false, positionals: [] };
3515
4271
  try {
3516
4272
  parsed = parseGlobalArgs(argv);
4273
+ if (parsed.version || parsed.positionals[0] === "version") {
4274
+ io.stdout.write(renderSuccess({ version: CLI_VERSION }, { json: parsed.json }));
4275
+ return 0;
4276
+ }
3517
4277
  if (parsed.help || parsed.positionals.length === 0) {
3518
4278
  io.stdout.write(helpText());
3519
4279
  return 0;
3520
4280
  }
3521
4281
  const resolved = resolveEndpoint({ env: parsed.env, endpoint: parsed.endpoint });
3522
- let sessionRole = resolveRole({ role: parsed.role }).role;
3523
- if (parsed.positionals[0] === "init" && sessionRole === void 0 && !argv.includes("--undo")) {
3524
- sessionRole = await readRepositoryRole(process.cwd());
3525
- }
4282
+ const explicitRole = () => resolveRole({ role: parsed.role });
3526
4283
  const tokenStore = new FileTokenStore();
3527
- const auth = new OAuthClient({ endpoint: resolved.endpoint, tokenStore, role: sessionRole });
3528
- const client = new McpRpcClient({
3529
- endpoint: resolved.endpoint,
3530
- tokenProvider: auth,
3531
- role: sessionRole
3532
- });
4284
+ const connect = (role) => {
4285
+ const auth = new OAuthClient({
4286
+ endpoint: resolved.endpoint,
4287
+ tokenStore,
4288
+ // ★init --dry-run 은 토큰을 갱신하지도 않는다 — 「아무것도 바꾸지 않는다」에 로그인 상태도 든다.
4289
+ // (init 의 플래그는 positional 로 흘러온다.)
4290
+ readOnly: parsed.positionals[0] === "init" && parsed.positionals.includes("--dry-run"),
4291
+ // ★URL 을 «브라우저를 열기 전에» 알린다. stdout 은 결과 전용이라 stderr 에 쓴다.
4292
+ // --json 이면 JSON 한 줄이다(설계 §21-3 (가)). 로그인이 역할과 무관해져 role 칸은 없다(0.4.2 §2-5).
4293
+ onAuthorizeUrl: (url) => {
4294
+ io.stderr.write(parsed.json ? `${JSON.stringify({ event: "login_url", url })}
4295
+ ` : `Opening your browser to sign in to AiNecto. If it does not open, visit:
4296
+ ${url}
4297
+ `);
4298
+ }
4299
+ });
4300
+ const client2 = new McpRpcClient({ endpoint: resolved.endpoint, tokenProvider: auth, role });
4301
+ return { auth, client: client2 };
4302
+ };
3533
4303
  const [domain, action, ...rest] = parsed.positionals;
3534
4304
  if (domain === "mcp") {
3535
4305
  await runConnector({
3536
4306
  endpoint: resolved.endpoint,
3537
- // `ai-erd mcp` 로 붙는 경로도 같은 역할을 쓴다 — bin/mcp.ts 와 같은 규칙.
3538
- role: sessionRole,
4307
+ // 브리지의 역할은 인자·환경변수에서만 온다 — bin/mcp.ts 와 같은 규칙. 저장소를 적용하지 않는다.
4308
+ role: explicitRole().role ?? null,
3539
4309
  input: io.stdin,
3540
4310
  output: io.stdout,
3541
4311
  errorOutput: io.stderr
@@ -3545,22 +4315,69 @@ async function runAinectoCli(argv, io) {
3545
4315
  if (domain === "init") {
3546
4316
  return await executeInitCommand({
3547
4317
  argv: parsed.positionals.slice(1),
3548
- role: sessionRole,
3549
- // 문서는 인증 후에 받는다 — 토큰이 없으면 패키지 기본값으로 가고, 그 사실을 보고한다.
3550
- // ★«필요할 때» 가져온다. --undo 는 로컬 작업이라 토큰을 건드릴 이유가 없다(S1).
3551
- accessToken: () => auth.getAccessToken().catch(() => void 0),
3552
- client,
4318
+ role: explicitRole().role,
4319
+ connect: (role) => {
4320
+ const { auth, client: client2 } = connect(role);
4321
+ return {
4322
+ client: client2,
4323
+ // 문서는 인증 후에 받는다 — 토큰이 없으면 패키지 기본값으로 가고, 그 사실을 보고한다.
4324
+ // ★«필요할 때» 가져온다. --undo 는 로컬 작업이라 토큰을 건드릴 이유가 없다(S1) — undo 는
4325
+ // connect 자체를 부르지 않는다.
4326
+ // ★갱신 실패는 «토큰 없음»(→ 로그인)이지만, AINECTO_TOKEN 을 못 쓰는 주소라는 거절은 삼키지 않는다 —
4327
+ // 삼키면 그 설정 오류가 브라우저 로그인으로 둔갑한다.
4328
+ accessToken: async () => {
4329
+ try {
4330
+ return await auth.getAccessToken();
4331
+ } catch (error) {
4332
+ if (process.env.AINECTO_TOKEN) {
4333
+ throw error;
4334
+ }
4335
+ return void 0;
4336
+ }
4337
+ },
4338
+ // 로그인한다 — `ainecto auth login` 과 같은 함수다. 서버마다 한 번이다(0.4.2).
4339
+ login: async () => {
4340
+ await auth.login();
4341
+ }
4342
+ };
4343
+ },
4344
+ // ★대화형일 때만 묻는다(판정은 isInteractive 한 곳). 에이전트·CI 는 TTY 가 아니다.
4345
+ prompter: isInteractive(io, parsed.json) ? createReadlinePrompter(io) : void 0,
3553
4346
  endpoint: resolved.endpoint,
3554
4347
  env: resolved.env,
3555
- cliVersion: `@ai-erd/mcp ${CLI_VERSION}`,
4348
+ cliVersion: `${PACKAGE_NAME} ${CLI_VERSION}`,
3556
4349
  cwd: process.cwd(),
3557
4350
  json: parsed.json,
3558
4351
  io
3559
4352
  });
3560
4353
  }
3561
4354
  if (domain === "auth") {
3562
- return await handleAuth(action, auth, resolved.endpoint, parsed.json, io);
4355
+ if (parsed.role !== void 0) {
4356
+ io.stderr.write("Sign-in is one per machine; --role is not used by auth.\n");
4357
+ }
4358
+ return await handleAuth(action, connect(null).auth, resolved.endpoint, parsed.json, io);
4359
+ }
4360
+ const env = parsed.env ?? "prod";
4361
+ if (domain === "tools" && action === "catalog") {
4362
+ return await printCatalog(parsed, io, resolved.endpoint, async () => {
4363
+ const explicit2 = explicitRole();
4364
+ return connect(await commandRole(explicit2, process.cwd())).client;
4365
+ });
4366
+ }
4367
+ let routeTools = getGeneratedTools(env);
4368
+ let remote = isAttachmentsUploadCommand(domain, action) || domain === "tools" && (action === "list" || action === "call") || matchGeneratedCommand(routeTools, parsed.positionals) !== void 0;
4369
+ if (!remote) {
4370
+ const live = await loadLiveTools(env, connect(null).client).catch(() => void 0);
4371
+ if (live && matchGeneratedCommand(live, parsed.positionals)) {
4372
+ routeTools = live;
4373
+ remote = true;
4374
+ }
3563
4375
  }
4376
+ if (!remote) {
4377
+ throw new Error(domain === "tools" ? "Expected tools list, tools call <mcpName>, or tools catalog." : `Unknown command "${domain}".`);
4378
+ }
4379
+ const explicit = explicitRole();
4380
+ const { client } = connect(await commandRole(explicit, process.cwd()));
3564
4381
  if (isAttachmentsUploadCommand(domain, action)) {
3565
4382
  return await executeAttachmentsUploadCommand({
3566
4383
  argv: rest,
@@ -3570,7 +4387,7 @@ async function runAinectoCli(argv, io) {
3570
4387
  });
3571
4388
  }
3572
4389
  const generatedResult = await executeGeneratedCommand({
3573
- env: parsed.env ?? "prod",
4390
+ tools: routeTools,
3574
4391
  argv: parsed.positionals,
3575
4392
  client,
3576
4393
  json: parsed.json,
@@ -3588,15 +4405,32 @@ async function runAinectoCli(argv, io) {
3588
4405
  return 1;
3589
4406
  }
3590
4407
  }
4408
+ async function commandRole(explicit, cwd) {
4409
+ const explicitRole = explicit.role ?? null;
4410
+ const found = await findRepositoryRole(cwd);
4411
+ if (found.kind === "conflict") {
4412
+ throw new Error(await askTheUserToFixRole(found.dir, found.reason));
4413
+ }
4414
+ if (found.kind === "none") {
4415
+ return explicitRole;
4416
+ }
4417
+ if (explicitRole !== null && explicitRole !== found.role) {
4418
+ const given = explicit.source === "env" ? `${explicit.envVar}=${explicitRole}` : `--role ${explicitRole}`;
4419
+ throw new Error(
4420
+ `This repository's AI sessions have the role ${found.role} (set in ${found.dir}). ${given} does not match \u2014 the role changes only with \`ainecto init --role <role>\`, after asking the user.`
4421
+ );
4422
+ }
4423
+ return found.role;
4424
+ }
3591
4425
  async function handleAuth(action, auth, endpoint, json, io) {
3592
4426
  if (action === "login") {
3593
4427
  await auth.login();
3594
- io.stdout.write(renderSuccess({ endpoint, authenticated: true }, { json }));
4428
+ io.stdout.write(renderSuccess({ ...await nextStep(), endpoint, authenticated: true }, { json }));
3595
4429
  return 0;
3596
4430
  }
3597
4431
  if (action === "status") {
3598
4432
  const token = await auth.getAccessToken();
3599
- io.stdout.write(renderSuccess({ endpoint, authenticated: Boolean(token) }, { json }));
4433
+ io.stdout.write(renderSuccess({ ...await nextStep(), endpoint, authenticated: Boolean(token) }, { json }));
3600
4434
  return 0;
3601
4435
  }
3602
4436
  if (action === "logout") {
@@ -3606,6 +4440,20 @@ async function handleAuth(action, auth, endpoint, json, io) {
3606
4440
  }
3607
4441
  throw new Error("Expected auth login, auth status, or auth logout.");
3608
4442
  }
4443
+ async function printCatalog(parsed, io, endpoint, liveClient) {
4444
+ const catalogEnv = parsed.env ?? "prod";
4445
+ try {
4446
+ const tools = await loadLiveTools(catalogEnv, await liveClient());
4447
+ io.stdout.write(renderSuccess(tools, { json: parsed.json }));
4448
+ } catch (error) {
4449
+ const reason = error instanceof Error ? error.message : String(error);
4450
+ io.stdout.write(renderSuccess(getGeneratedTools(catalogEnv), {
4451
+ json: parsed.json,
4452
+ warnings: [`Could not read the live tool list from ${endpoint} (${reason}). Showing the ${catalogEnv} catalog checked in when this CLI version was built \u2014 a snapshot of that server's tools/list that may miss newer document types and tools. Run \`ainecto auth login\`, then retry.`]
4453
+ }));
4454
+ }
4455
+ return 0;
4456
+ }
3609
4457
  async function handleTools(action, rest, parsed, client, io) {
3610
4458
  if (action === "list") {
3611
4459
  const tools = await client.toolsList();
@@ -3628,18 +4476,10 @@ async function handleTools(action, rest, parsed, client, io) {
3628
4476
  io.stdout.write(renderSuccess(result, { json: parsed.json, warnings: payload.warnings }));
3629
4477
  return isToolError(result) ? 1 : 0;
3630
4478
  }
3631
- if (action === "catalog") {
3632
- const catalogEnv = parsed.env ?? "prod";
3633
- io.stdout.write(renderSuccess(getGeneratedTools(parsed.env ?? "prod"), {
3634
- json: parsed.json,
3635
- warnings: [catalogEnv === "dev" ? "Local dev catalog is a checked-in live sync snapshot; rerun sync:tools --env dev to refresh it." : "Local prod catalog is seed fixture only until authenticated prod live sync updates generated catalogs."]
3636
- }));
3637
- return 0;
3638
- }
3639
4479
  throw new Error("Expected tools list, tools call <mcpName>, or tools catalog.");
3640
4480
  }
3641
4481
  function parseGlobalArgs(argv) {
3642
- const result = { json: false, help: false, positionals: [] };
4482
+ const result = { json: false, help: false, version: false, positionals: [] };
3643
4483
  for (let index = 0; index < argv.length; index += 1) {
3644
4484
  const arg = argv[index];
3645
4485
  if (!arg) {
@@ -3655,6 +4495,8 @@ function parseGlobalArgs(argv) {
3655
4495
  result.json = true;
3656
4496
  } else if (arg === "--help" || arg === "-h") {
3657
4497
  result.help = true;
4498
+ } else if (arg === "--version" || arg === "-v" || arg === "-V") {
4499
+ result.version = true;
3658
4500
  } else {
3659
4501
  result.positionals.push(arg);
3660
4502
  }
@@ -3686,13 +4528,25 @@ function requireValue4(flag, value) {
3686
4528
  }
3687
4529
  return value;
3688
4530
  }
4531
+ async function nextStep() {
4532
+ const found = await findRepositoryRole(process.cwd());
4533
+ return found.kind === "none" ? { setup: "incomplete", next: NEXT_STEP_INIT } : {};
4534
+ }
4535
+ var NEXT_STEP_INIT = [
4536
+ "AiNecto setup is not finished until `npx -y ainecto@latest init` runs in the repository.",
4537
+ "AI agents: tell the user this now, even if they only asked to install, update, or sign in. Then ask",
4538
+ "which role this repository's AI sessions should have \u2014 Design, Development, Test, or Validation \u2014",
4539
+ "never pick one yourself, and run `npx -y ainecto@latest init --role <role>` from the repository root.",
4540
+ "In a terminal: `npx -y ainecto@latest init` asks for the role and the project."
4541
+ ].join("\n");
3689
4542
  function helpText() {
3690
4543
  return [
3691
- "Ainecto CLI",
4544
+ "AiNecto CLI",
4545
+ "",
4546
+ NEXT_STEP_INIT,
3692
4547
  "",
3693
4548
  "Usage:",
3694
- " ainecto auth login --role <design|development|test|validation> [--env prod|dev] [--json]",
3695
- " ainecto auth status|logout [--role <role>] [--env prod|dev] [--json]",
4549
+ " ainecto auth login|status|logout [--env prod|dev] [--json] (one sign-in per machine)",
3696
4550
  " ainecto tools list [--env prod|dev] [--endpoint URL] [--json]",
3697
4551
  " ainecto tools call <mcpName> [-f payload.json|@-] [inline-json] [--json]",
3698
4552
  " ainecto attachments upload --document-uuid <uuid> <file...> [--json]",
@@ -3700,8 +4554,11 @@ function helpText() {
3700
4554
  " ainecto mcp [--env prod|dev] [--endpoint URL] [--role design|development|test|validation]",
3701
4555
  "",
3702
4556
  "Harness:",
3703
- " ai-erd init --role <design|development|test|validation> [--project <uuid>] [--dry-run]",
3704
- " ai-erd init --undo",
4557
+ " ainecto init [--role <design|development|test|validation>] [--project <uuid>] [--dry-run]",
4558
+ " In a terminal, asks for the role and project when they are not given.",
4559
+ " ainecto init --undo",
4560
+ "",
4561
+ "`ai-erd` (the older name) is the same command and still works.",
3705
4562
  ""
3706
4563
  ].join("\n");
3707
4564
  }