@ainecto/mcp 0.3.0 → 0.6.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,52 @@ 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 = "ainecto:role:";
12
+ var ROLE_HEADER = "X-AiNecto-Role";
13
+ var ROLE_ENV_VAR = "AINECTO_ROLE";
14
+ function resolveRole(input = {}) {
15
+ if (input.role !== void 0) {
16
+ return { role: assertRole(input.role), source: "flag" };
17
+ }
18
+ const envVars = input.envVars ?? process.env;
19
+ const fromEnv = envVars[ROLE_ENV_VAR];
20
+ if (fromEnv && fromEnv.trim()) {
21
+ return { role: assertRole(fromEnv), source: "env" };
22
+ }
23
+ return { source: "none" };
24
+ }
25
+ function assertRole(value) {
26
+ const normalized = value.trim().toLowerCase();
27
+ if (HARNESS_ROLES.includes(normalized)) {
28
+ return normalized;
29
+ }
30
+ throw new Error(
31
+ `Invalid role "${value}". Expected one of: ${HARNESS_ROLES.join(", ")}.`
32
+ );
33
+ }
34
+
35
+ // src/core/auth/tokenStore.ts
8
36
  var FileTokenStore = class {
9
37
  filePath;
10
38
  constructor(rootDir = join(homedir(), ".ainecto")) {
11
39
  this.filePath = join(rootDir, "tokens.json");
12
40
  }
13
- async load(endpoint, role) {
41
+ async load(endpoint) {
14
42
  const file = await this.readFile();
15
- return file[tokenKey(endpoint, role)];
43
+ return file[tokenKey(endpoint)];
16
44
  }
17
- async save(endpoint, token, role) {
45
+ async save(endpoint, token) {
46
+ assertNoRoleScope(token);
18
47
  const file = await this.readFile();
19
- file[tokenKey(endpoint, role)] = { ...token, endpoint, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
48
+ file[tokenKey(endpoint)] = { ...token, endpoint, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
20
49
  await this.writeFile(file);
21
50
  }
22
- async delete(endpoint, role) {
51
+ async delete(endpoint) {
23
52
  const file = await this.readFile();
24
- delete file[tokenKey(endpoint, role)];
53
+ delete file[tokenKey(endpoint)];
25
54
  await this.writeFile(file);
26
55
  }
27
56
  async readFile() {
@@ -56,9 +85,15 @@ var FileTokenStore = class {
56
85
  await chmodIfPermissive(this.filePath, 384);
57
86
  }
58
87
  };
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");
88
+ function tokenKey(endpoint) {
89
+ return createHash("sha256").update(endpoint).digest("hex");
90
+ }
91
+ function assertNoRoleScope(token) {
92
+ if (token.scope?.split(/\s+/).some((scope) => scope.startsWith(ROLE_SCOPE_PREFIX))) {
93
+ throw new Error(
94
+ "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."
95
+ );
96
+ }
62
97
  }
63
98
  function isNodeError(error, code) {
64
99
  return error instanceof Error && "code" in error && error.code === code;
@@ -85,13 +120,9 @@ import { URLSearchParams } from "url";
85
120
 
86
121
  // src/core/config/endpoints.ts
87
122
  var DEFAULT_ENDPOINTS = {
88
- prod: "https://ai-erd.com/mcp",
89
- dev: "https://dev.ai-erd.com/mcp"
123
+ prod: "https://ainecto.com/mcp",
124
+ dev: "https://dev.ainecto.com/mcp"
90
125
  };
91
- var LEGACY_DEFAULT_ENDPOINTS = [
92
- "https://ainecto.com/mcp",
93
- "https://dev.ainecto.com/mcp"
94
- ];
95
126
  function resolveEndpoint(input = {}) {
96
127
  const envVars = input.envVars ?? process.env;
97
128
  const env = input.env ?? "prod";
@@ -114,7 +145,7 @@ function isDefaultEndpointFor(env, endpoint) {
114
145
  }
115
146
  function isDefaultEndpoint(endpoint) {
116
147
  const normalized = normalizeEndpoint(endpoint);
117
- return Object.values(DEFAULT_ENDPOINTS).includes(normalized) || LEGACY_DEFAULT_ENDPOINTS.includes(normalized);
148
+ return Object.values(DEFAULT_ENDPOINTS).includes(normalized);
118
149
  }
119
150
  function assertTrustedEndpointUrl(value, label = "URL") {
120
151
  const url = typeof value === "string" ? new URL(value) : value;
@@ -154,7 +185,7 @@ async function registerPublicClient(registrationEndpoint, redirectUri, fetchImpl
154
185
  method: "POST",
155
186
  headers: { "content-type": "application/json", accept: "application/json" },
156
187
  body: JSON.stringify({
157
- client_name: "Ainecto CLI",
188
+ client_name: "AiNecto CLI",
158
189
  redirect_uris: [redirectUri],
159
190
  token_endpoint_auth_method: "none",
160
191
  grant_types: ["authorization_code", "refresh_token"],
@@ -177,32 +208,16 @@ function isRecord(value) {
177
208
  return typeof value === "object" && value !== null && !Array.isArray(value);
178
209
  }
179
210
 
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
211
  // src/core/auth/oauth.ts
212
+ var DEFAULT_LOGIN_TIMEOUT_MS = 3e5;
213
+ var OAuthLoginError = class extends Error {
214
+ constructor(code, message) {
215
+ super(message);
216
+ this.code = code;
217
+ this.name = "OAuthLoginError";
218
+ }
219
+ code;
220
+ };
206
221
  var OAuthClient = class {
207
222
  constructor(options) {
208
223
  this.options = options;
@@ -220,21 +235,27 @@ var OAuthClient = class {
220
235
  assertEnvTokenEndpointAllowed(this.options.endpoint, this.envVars);
221
236
  return envToken;
222
237
  }
223
- const stored = await this.options.tokenStore.load(this.options.endpoint, this.options.role);
238
+ const stored = await this.options.tokenStore.load(this.options.endpoint);
224
239
  if (!stored) {
225
240
  return void 0;
226
241
  }
227
242
  if (!shouldRefresh(stored)) {
228
243
  return stored.accessToken;
229
244
  }
230
- return this.refreshStoredToken(stored);
245
+ if (this.options.readOnly) {
246
+ return isExpired(stored) ? void 0 : stored.accessToken;
247
+ }
248
+ if (!stored.refreshToken) {
249
+ return isExpired(stored) ? void 0 : stored.accessToken;
250
+ }
251
+ return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });
231
252
  }
232
253
  async refreshAfterUnauthorized() {
233
- const stored = await this.options.tokenStore.load(this.options.endpoint, this.options.role);
234
- if (!stored?.refreshToken) {
254
+ const stored = await this.options.tokenStore.load(this.options.endpoint);
255
+ if (this.options.readOnly || !stored?.refreshToken) {
235
256
  return void 0;
236
257
  }
237
- return this.refreshStoredToken(stored);
258
+ return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });
238
259
  }
239
260
  async login() {
240
261
  const pkce = generatePkcePair();
@@ -251,12 +272,14 @@ var OAuthClient = class {
251
272
  authorizeUrl.searchParams.set("code_challenge_method", "S256");
252
273
  authorizeUrl.searchParams.set("resource", this.options.endpoint);
253
274
  authorizeUrl.searchParams.set("state", state);
254
- if (this.options.role) {
255
- authorizeUrl.searchParams.set("scope", `mcp ${ROLE_SCOPE_PREFIX}${this.options.role}`);
256
- }
257
275
  assertTrustedEndpointUrl(authorizeUrl, "OAuth authorization URL");
258
- await this.openBrowserImpl(authorizeUrl.toString());
259
- const code = await loopback.waitForCode();
276
+ const url = authorizeUrl.toString();
277
+ this.options.onAuthorizeUrl?.(url);
278
+ const browserFailure = this.openBrowserImpl(url).then(() => new Promise(() => void 0));
279
+ const code = await withTimeout(
280
+ Promise.race([loopback.waitForCode(), browserFailure]),
281
+ this.options.loginTimeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS
282
+ );
260
283
  const token = await exchangeToken(metadata.tokenEndpoint, {
261
284
  grant_type: "authorization_code",
262
285
  code,
@@ -267,19 +290,17 @@ var OAuthClient = class {
267
290
  ...registration.clientSecret ? { client_secret: registration.clientSecret } : {}
268
291
  }, this.fetchImpl);
269
292
  const stored = toStoredToken(this.options.endpoint, token, metadata, registration);
270
- await this.options.tokenStore.save(this.options.endpoint, stored, this.options.role);
293
+ await this.options.tokenStore.save(this.options.endpoint, stored);
271
294
  return stored;
272
295
  } finally {
273
296
  await loopback.close();
274
297
  }
275
298
  }
276
299
  async logout() {
277
- await this.options.tokenStore.delete(this.options.endpoint, this.options.role);
300
+ await this.options.tokenStore.delete(this.options.endpoint);
278
301
  }
302
+ /** 부르는 쪽이 refresh token 이 있음을 이미 확인했다 — 타입이 그것을 요구한다. */
279
303
  async refreshStoredToken(stored) {
280
- if (!stored.refreshToken) {
281
- return stored.accessToken;
282
- }
283
304
  const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);
284
305
  const token = await exchangeToken(metadata.tokenEndpoint, {
285
306
  grant_type: "refresh_token",
@@ -289,7 +310,7 @@ var OAuthClient = class {
289
310
  resource: this.options.endpoint
290
311
  }, this.fetchImpl);
291
312
  const next = toStoredToken(this.options.endpoint, token, metadata, stored);
292
- await this.options.tokenStore.save(this.options.endpoint, next, this.options.role);
313
+ await this.options.tokenStore.save(this.options.endpoint, next);
293
314
  return next.accessToken;
294
315
  }
295
316
  };
@@ -301,6 +322,27 @@ function generatePkcePair() {
301
322
  function generateOAuthState() {
302
323
  return randomBytes(32).toString("base64url");
303
324
  }
325
+ function isExpired(token, now = Date.now()) {
326
+ return token.expiresAt !== void 0 && token.expiresAt <= now;
327
+ }
328
+ async function withTimeout(work, timeoutMs) {
329
+ let timer;
330
+ const timeout = new Promise((_, reject) => {
331
+ timer = setTimeout(() => {
332
+ const minutes = timeoutMs / 6e4;
333
+ const span = Number.isInteger(minutes) ? `${minutes} minute${minutes === 1 ? "" : "s"}` : `${timeoutMs} ms`;
334
+ reject(new OAuthLoginError(
335
+ "LOGIN_TIMEOUT",
336
+ `Sign-in was not completed within ${span}. Nothing was saved \u2014 run the same command again.`
337
+ ));
338
+ }, timeoutMs);
339
+ });
340
+ try {
341
+ return await Promise.race([work, timeout]);
342
+ } finally {
343
+ clearTimeout(timer);
344
+ }
345
+ }
304
346
  function shouldRefresh(token, now = Date.now()) {
305
347
  if (!token.expiresAt) {
306
348
  return false;
@@ -393,11 +435,11 @@ function toStoredToken(endpoint, token, metadata, previous) {
393
435
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
394
436
  };
395
437
  }
396
- async function createLoopbackReceiver(expectedState) {
438
+ async function createLoopbackReceiver(expectedState, host = "127.0.0.1") {
397
439
  let resolveCode;
398
440
  let rejectCode;
399
- const codePromise = new Promise((resolve2, reject) => {
400
- resolveCode = resolve2;
441
+ const codePromise = new Promise((resolve3, reject) => {
442
+ resolveCode = resolve3;
401
443
  rejectCode = reject;
402
444
  });
403
445
  codePromise.catch(() => void 0);
@@ -408,13 +450,13 @@ async function createLoopbackReceiver(expectedState) {
408
450
  const state = url.searchParams.get("state");
409
451
  if (error) {
410
452
  res.writeHead(400, { "content-type": "text/plain" });
411
- res.end("Ainecto CLI authorization failed. You can close this tab.");
453
+ res.end("AiNecto CLI authorization failed. You can close this tab.");
412
454
  rejectCode(new Error(`OAuth authorization failed: ${error}`));
413
455
  return;
414
456
  }
415
457
  if (!state || state !== expectedState) {
416
458
  res.writeHead(400, { "content-type": "text/plain" });
417
- res.end("Ainecto CLI authorization state mismatch. You can close this tab.");
459
+ res.end("AiNecto CLI authorization state mismatch. You can close this tab.");
418
460
  rejectCode(new Error("OAuth state mismatch."));
419
461
  return;
420
462
  }
@@ -425,10 +467,16 @@ async function createLoopbackReceiver(expectedState) {
425
467
  return;
426
468
  }
427
469
  res.writeHead(200, { "content-type": "text/plain" });
428
- res.end("Ainecto CLI authorization complete. You can close this tab.");
470
+ res.end("AiNecto CLI authorization complete. You can close this tab.");
429
471
  resolveCode(code);
430
472
  });
431
- await new Promise((resolve2) => server.listen(0, "127.0.0.1", resolve2));
473
+ await new Promise((resolve3, reject) => {
474
+ server.once("error", (error) => reject(new OAuthLoginError(
475
+ "CALLBACK_UNAVAILABLE",
476
+ `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.`
477
+ )));
478
+ server.listen(0, host, () => resolve3());
479
+ });
432
480
  const address = server.address();
433
481
  if (!address || typeof address === "string") {
434
482
  throw new Error("Loopback OAuth server did not bind to a TCP port.");
@@ -436,7 +484,10 @@ async function createLoopbackReceiver(expectedState) {
436
484
  return {
437
485
  redirectUri: `http://127.0.0.1:${address.port}/callback`,
438
486
  waitForCode: () => codePromise,
439
- close: () => new Promise((resolve2, reject) => server.close((error) => error ? reject(error) : resolve2()))
487
+ close: () => new Promise((resolve3, reject) => {
488
+ server.closeAllConnections?.();
489
+ server.close((error) => error ? reject(error) : resolve3());
490
+ })
440
491
  };
441
492
  }
442
493
  function parseStringArray(value) {
@@ -460,10 +511,39 @@ function getBrowserOpenCommand(url, platform = process.platform) {
460
511
  }
461
512
  return { command: "xdg-open", args: [url] };
462
513
  }
463
- async function openBrowser(url) {
464
- const { command, args } = getBrowserOpenCommand(url);
465
- const child = spawn(command, args, { stdio: "ignore", detached: true });
466
- child.unref();
514
+ function launchBrowser(url, deps = {}) {
515
+ const platform = deps.platform ?? process.platform;
516
+ const { command, args } = getBrowserOpenCommand(url, platform);
517
+ const spawnImpl = deps.spawnImpl ?? spawn;
518
+ return new Promise((resolve3, reject) => {
519
+ const unavailable = (reason) => new OAuthLoginError(
520
+ "BROWSER_UNAVAILABLE",
521
+ `Could not open a browser on this machine (${command}: ${reason}). Sign-in needs a browser here.`
522
+ );
523
+ let child;
524
+ try {
525
+ child = spawnImpl(command, args, { stdio: "ignore", detached: true });
526
+ } catch (error) {
527
+ reject(unavailable(error instanceof Error ? error.message : String(error)));
528
+ return;
529
+ }
530
+ child.once("error", (error) => reject(unavailable(error.message)));
531
+ if (platform === "win32") {
532
+ child.once("spawn", () => resolve3());
533
+ } else {
534
+ child.once("exit", (code, signal) => {
535
+ if (code === 0) {
536
+ resolve3();
537
+ } else {
538
+ reject(unavailable(code === null ? `stopped by ${signal ?? "a signal"}` : `exited with code ${code}`));
539
+ }
540
+ });
541
+ }
542
+ child.unref();
543
+ });
544
+ }
545
+ function openBrowser(url) {
546
+ return launchBrowser(url);
467
547
  }
468
548
  function assertEnvTokenEndpointAllowed(endpoint, envVars) {
469
549
  if (envVars.AINECTO_ALLOW_CUSTOM_ENDPOINT_TOKEN === "1") {
@@ -534,6 +614,11 @@ async function readAllStdin(stdin) {
534
614
  return text;
535
615
  }
536
616
 
617
+ // src/core/version.ts
618
+ var CLI_VERSION = true ? "0.6.0" : "dev";
619
+ var PACKAGE_NAME = "@ainecto/mcp";
620
+ var CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;
621
+
537
622
  // src/core/mcp/rpcClient.ts
538
623
  var McpRpcError = class extends Error {
539
624
  constructor(message, code, details) {
@@ -556,7 +641,7 @@ var McpRpcClient = class {
556
641
  return this.request("initialize", {
557
642
  protocolVersion: "2024-11-05",
558
643
  capabilities: {},
559
- clientInfo: { name: "@ainecto/mcp", version: "0.1.0" }
644
+ clientInfo: { name: PACKAGE_NAME, version: CLI_VERSION }
560
645
  });
561
646
  }
562
647
  async toolsList() {
@@ -731,55 +816,73 @@ function redactSecretString(value) {
731
816
 
732
817
  // src/core/catalog/generated.prod.ts
733
818
  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" }
819
+ { "mcpName": "create_comments", "commandPath": ["comments", "create"], "group": "generic", "description": "Add comments, questions, or replies to a document. Anyone who can read the document may comment and reply. Set parentCommentId to reply to a top-level comment. To mention people, get their userId and displayName from list_mention_candidates, write @<displayName> in the body, and list their userIds in mentionUserIds (clientMutationId is then required). Items are applied in order and each one commits on its own; results report each item (ok, or status and error).", "inputSchema": { "additionalProperties": false, "properties": { "documentUuid": { "description": "Document UUID the comments belong to", "type": "string" }, "items": { "description": "Comments to add, applied in order", "items": { "additionalProperties": false, "properties": { "body": { "description": "Comment text (plain text, 1..10000 characters). To mention someone, include @<displayName> exactly as returned by mcp__ainecto__list_mention_candidates.", "maxLength": 1e4, "minLength": 1, "type": "string" }, "clientMutationId": { "description": "Idempotency key (visible ASCII, at most 128 characters). Required when mentionUserIds is not empty; reuse the same value when retrying the same item so the mention email is not sent twice.", "maxLength": 128, "type": "string" }, "kind": { "description": "Top-level comments only: COMMENT (default) or QUESTION. A QUESTION can later be resolved with mcp__ainecto__resolve_comments.", "enum": ["COMMENT", "QUESTION"], "type": "string" }, "mentionUserIds": { "description": "userIds to mention (from mcp__ainecto__list_mention_candidates). Each one's @<displayName> must appear in body; newly mentioned people get a notification email.", "items": { "type": "string" }, "maxItems": 20, "type": "array", "uniqueItems": true }, "parentCommentId": { "description": "Reply to this top-level comment id (from mcp__ainecto__list_comments). Replies are one level deep: replying to a reply is rejected.", "minimum": 1, "type": "integer" } }, "required": ["body"], "type": "object" }, "minItems": 1, "type": "array" }, "mailLanguage": { "description": "Language of the mention notification email. Defaults to en.", "enum": ["en", "ko"], "type": "string" } }, "required": ["documentUuid", "items"], "type": "object" }, "required": ["documentUuid", "items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "a673b909d16835ef" },
820
+ { "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, file.", "enum": ["erd", "markdown", "diagram", "openapi", "html", "file"], "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); file: no JSON payload \u2014 bytes go through request_upload_token(purpose=document.create, scopeJson={projectUuid,folderUuid?,type:"file",title,filename}) then one curl PUT (images\xB7PDF\xB7mp4/webm\xB7audio\xB7text/code\xB7CSV only).', "type": "object" } }, "required": ["projectUuid", "type", "title"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "aa868b0e2821006a" },
821
+ { "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" },
822
+ { "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" },
823
+ { "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" },
824
+ { "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" },
825
+ { "mcpName": "delete_comments", "commandPath": ["comments", "delete"], "group": "generic", "description": "Delete your own comments on a document. Only the author can delete. A resolved question and a top-level comment that still has replies cannot be deleted. Each id is applied on its own; results report each id.", "inputSchema": { "additionalProperties": false, "properties": { "documentUuid": { "description": "Document UUID the comments belong to", "type": "string" }, "ids": { "description": "Comment ids to delete (from mcp__ainecto__list_comments)", "items": { "minimum": 1, "type": "integer" }, "minItems": 1, "type": "array" } }, "required": ["documentUuid", "ids"], "type": "object" }, "required": ["documentUuid", "ids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "prod", "schemaHash": "17b9658b5a47f866" },
826
+ { "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" },
827
+ { "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" },
828
+ { "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" },
829
+ { "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" },
830
+ { "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": "Omit (null) for a plain node-to-node edge. Set only when a group is an endpoint: GROUP_TO_GROUP|NODE_TO_GROUP|GROUP_TO_NODE", "enum": ["GROUP_TO_GROUP", "NODE_TO_GROUP", "GROUP_TO_NODE"], "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": "2c829103f86f4168" },
831
+ { "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" },
832
+ { "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" },
833
+ { "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" },
834
+ { "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" },
835
+ { "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" },
836
+ { "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" },
837
+ { "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" },
838
+ { "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" },
839
+ { "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" },
840
+ { "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" },
841
+ { "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" },
842
+ { "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" },
843
+ { "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" },
844
+ { "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" },
845
+ { "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" },
846
+ { "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" },
847
+ { "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" },
848
+ { "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" },
849
+ { "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" },
850
+ { "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" },
851
+ { "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" },
852
+ { "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" },
853
+ { "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); file \u2192 file {filename, contentType, sizeBytes, viewerKind} + content for text/CSV (first 64 KB); 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": "2efef592c2d6dd7a" },
854
+ { "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" },
855
+ { "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" },
856
+ { "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" },
857
+ { "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" },
858
+ { "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" },
859
+ { "mcpName": "list_comments", "commandPath": ["comments", "list"], "group": "generic", "description": "List the active comments on a document, oldest first, including replies (a reply has parentCommentId set; replies are one level deep). Each comment has id, kind (COMMENT or QUESTION), body, author, mentions (userId, label and position in the body), and for questions questionState (UNANSWERED, ANSWERED, RESOLVED) and resolvedAt. Anyone who can read the document can list its comments.", "inputSchema": { "additionalProperties": false, "properties": { "documentUuid": { "description": "Document UUID the comments belong to", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "4c60115a00f61404" },
860
+ { "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, file.", "enum": ["erd", "markdown", "diagram", "openapi", "html", "file"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "2171496b44dc2abf" },
861
+ { "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" },
862
+ { "mcpName": "list_mention_candidates", "commandPath": ["mention-candidates", "list"], "group": "generic", "description": "List the people you can mention in comments on a document: userId and displayName. Only people who can read the document are listed. To mention someone, write @<displayName> exactly in the comment body and pass their userId in mentionUserIds of create_comments or update_comments. Page with nextCursor while hasMore is true.", "inputSchema": { "additionalProperties": false, "properties": { "cursor": { "description": "nextCursor from the previous page (same documentUuid and query).", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to", "type": "string" }, "limit": { "description": "Page size, 1..50. Defaults to 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "query": { "description": "Name filter, 0..100 characters. Omit or leave empty for the first page of everyone.", "maxLength": 100, "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "896a5a6ff357268b" },
863
+ { "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" },
864
+ { "mcpName": "list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "329250aecc06a298" },
865
+ { "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" },
866
+ { "mcpName": "list_workspaces", "commandPath": ["workspaces", "list"], "group": "workspace", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "39dba0089fdbdf6a" },
867
+ { "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" },
868
+ { "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" },
869
+ { "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" },
870
+ { "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" },
871
+ { "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" },
872
+ { "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, file. document.update/document.append \uB294 documentUuid \uB97C \uD3EC\uD568. type=file \uC740 filename \uB3C4 \uD3EC\uD568(\uD655\uC7A5\uC790\uB85C \uD615\uC2DD \uD310\uC815) \u2014 PUT \uC758 Content-Type \uC740 \uD30C\uC77C MIME, \uBCF8\uBB38\uC740 \uD30C\uC77C \uBC14\uC774\uD2B8. attachment.upload: {documentUuid, filename}; storageKey \uB294 \uC11C\uBC84\uAC00 \uBC1C\uAE09 scope \uC5D0 \uB123\uB294\uB2E4.", "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": "fe1984b3979abf86" },
873
+ { "mcpName": "resolve_comments", "commandPath": ["tools", "run", "resolve-comments"], "group": "generic", "description": "Mark questions on a document as resolved. Only top-level comments with kind QUESTION can be resolved, and it needs edit access to the document (readers can comment but cannot resolve). Resolving cannot be undone. Each id is applied on its own; results report each id.", "inputSchema": { "additionalProperties": false, "properties": { "documentUuid": { "description": "Document UUID the comments belong to", "type": "string" }, "ids": { "description": "Question ids to resolve (top-level comments with kind QUESTION)", "items": { "minimum": 1, "type": "integer" }, "minItems": 1, "type": "array" } }, "required": ["documentUuid", "ids"], "type": "object" }, "required": ["documentUuid", "ids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "7b32b72b3a0d64ba" },
874
+ { "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" },
875
+ { "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, file.", "enum": ["erd", "markdown", "diagram", "openapi", "html", "file"], "type": "string" } }, "required": ["q"], "type": "object" }, "required": ["q"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "e82abba1358754e8" },
876
+ { "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" },
877
+ { "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" },
878
+ { "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" },
879
+ { "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" },
880
+ { "mcpName": "update_comments", "commandPath": ["comments", "update"], "group": "generic", "description": "Edit your own comments on a document. Only the author can edit, and only while they can still read the document. A resolved question and a top-level comment that already has replies are locked. Items are applied in order and each one commits on its own; results report each item.", "inputSchema": { "additionalProperties": false, "properties": { "documentUuid": { "description": "Document UUID the comments belong to", "type": "string" }, "items": { "description": "Edits, applied in order", "items": { "additionalProperties": false, "properties": { "body": { "description": "New comment text (replaces the old text).", "maxLength": 1e4, "minLength": 1, "type": "string" }, "clientMutationId": { "description": "Idempotency key (visible ASCII, at most 128 characters). Required when mentionUserIds is not empty; reuse the same value when retrying the same item so the mention email is not sent twice.", "maxLength": 128, "type": "string" }, "id": { "description": "Comment id (from mcp__ainecto__list_comments)", "minimum": 1, "type": "integer" }, "mentionUserIds": { "description": "The complete set of userIds mentioned after the edit; omit to remove all mentions. Each one's @<displayName> must appear in body. Only people who were not mentioned before get a notification email.", "items": { "type": "string" }, "maxItems": 20, "type": "array", "uniqueItems": true } }, "required": ["id", "body"], "type": "object" }, "minItems": 1, "type": "array" }, "mailLanguage": { "description": "Language of the mention notification email. Defaults to en.", "enum": ["en", "ko"], "type": "string" } }, "required": ["documentUuid", "items"], "type": "object" }, "required": ["documentUuid", "items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "fa37fedfdebd8d54" },
881
+ { "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); file: {file:{filename,contentType,sizeBytes,viewerKind}} (+content for text/CSV, first 64 KB).", "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": "c2088f9ef5b49c6c" },
882
+ { "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" },
883
+ { "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" },
884
+ { "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" },
885
+ { "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
886
  ];
784
887
 
785
888
  // src/core/catalog/generated.dev.ts
@@ -872,10 +975,147 @@ var generatedTools2 = [
872
975
  { "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
976
  ];
874
977
 
978
+ // src/core/catalog/generator.ts
979
+ import { createHash as createHash3 } from "crypto";
980
+ var LEGACY_PREFIX = "mcp__ainecto__";
981
+ function bareToolName(name) {
982
+ return name.startsWith(LEGACY_PREFIX) ? name.slice(LEGACY_PREFIX.length) : name;
983
+ }
984
+ var DESTRUCTIVE_PREFIXES = [
985
+ "delete_",
986
+ "remove_",
987
+ "archive_",
988
+ "unarchive_",
989
+ "restore_",
990
+ "move_",
991
+ "erd_delete_",
992
+ "erd_remove_",
993
+ "task_delete_",
994
+ "testcase_delete_"
995
+ ];
996
+ function generateCatalog(tools, sourceCatalog) {
997
+ return tools.map((tool) => normalizeTool(tool, sourceCatalog)).sort((a, b) => a.mcpName.localeCompare(b.mcpName));
998
+ }
999
+ function normalizeTool(tool, sourceCatalog) {
1000
+ if (!tool.name) {
1001
+ throw new Error("Unsupported tool name: empty.");
1002
+ }
1003
+ const stripped = bareToolName(tool.name);
1004
+ const group = inferGroup(stripped);
1005
+ const inputSchema = normalizeSchema(tool.inputSchema ?? { type: "object", properties: {} });
1006
+ const required = Array.isArray(inputSchema.required) ? inputSchema.required.filter((value) => typeof value === "string").sort() : [];
1007
+ const definition = {
1008
+ mcpName: tool.name,
1009
+ commandPath: generateCommandPath(stripped, group),
1010
+ group,
1011
+ description: tool.description ?? "",
1012
+ inputSchema,
1013
+ required,
1014
+ payloadMode: inferPayloadMode(stripped, inputSchema),
1015
+ destructive: inferDestructive(stripped, tool),
1016
+ sourceCatalog,
1017
+ schemaHash: ""
1018
+ };
1019
+ return { ...definition, schemaHash: computeSchemaHash(definition) };
1020
+ }
1021
+ function inferGroup(strippedName) {
1022
+ if (strippedName.startsWith("erd_")) return "erd";
1023
+ if (strippedName.startsWith("flow_")) return "flow";
1024
+ if (strippedName.startsWith("testcase_")) return "testcase";
1025
+ if (strippedName.startsWith("markdown_")) return "markdown";
1026
+ if (strippedName.includes("attachment") || strippedName.includes("upload")) return "attachment";
1027
+ if (strippedName.startsWith("task_")) return "task";
1028
+ if (strippedName.includes("workspace")) return "workspace";
1029
+ if (strippedName.includes("document")) return "document";
1030
+ return "generic";
1031
+ }
1032
+ function generateCommandPath(strippedName, group) {
1033
+ if (group !== "generic" && strippedName.startsWith(`${group}_`)) {
1034
+ return [group, snakeToKebab(strippedName.slice(group.length + 1))];
1035
+ }
1036
+ const [verb, ...rest] = strippedName.split("_");
1037
+ if (verb && rest.length > 0 && isVerb(verb)) {
1038
+ return [rest.join("-"), verb];
1039
+ }
1040
+ return ["tools", "run", snakeToKebab(strippedName)];
1041
+ }
1042
+ function inferPayloadMode(strippedName, inputSchema) {
1043
+ if (strippedName.includes("upload") || strippedName.includes("attachment_upload")) {
1044
+ return "binary-upload";
1045
+ }
1046
+ const properties = inputSchema.properties;
1047
+ if (!isRecord3(properties)) {
1048
+ return "flags";
1049
+ }
1050
+ const values = Object.values(properties);
1051
+ if (values.length === 0) {
1052
+ return "flags";
1053
+ }
1054
+ if (values.every(isScalarSchema)) {
1055
+ return "flags";
1056
+ }
1057
+ return "file-json";
1058
+ }
1059
+ function inferDestructive(strippedName, tool) {
1060
+ if (tool.annotations?.destructiveHint === true) {
1061
+ return true;
1062
+ }
1063
+ return DESTRUCTIVE_PREFIXES.some((prefix) => strippedName.startsWith(prefix));
1064
+ }
1065
+ function computeSchemaHash(tool) {
1066
+ return createHash3("sha256").update(stableStringify({
1067
+ name: tool.mcpName,
1068
+ description: tool.description,
1069
+ inputSchema: tool.inputSchema
1070
+ })).digest("hex").slice(0, 16);
1071
+ }
1072
+ function stableStringify(value) {
1073
+ if (Array.isArray(value)) {
1074
+ return `[${value.map(stableStringify).join(",")}]`;
1075
+ }
1076
+ if (isRecord3(value)) {
1077
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
1078
+ }
1079
+ return JSON.stringify(value);
1080
+ }
1081
+ function normalizeSchema(schema) {
1082
+ return JSON.parse(stableStringify(schema));
1083
+ }
1084
+ function isScalarSchema(value) {
1085
+ if (!isRecord3(value)) {
1086
+ return false;
1087
+ }
1088
+ const type = value.type;
1089
+ return type === "string" || type === "number" || type === "integer" || type === "boolean";
1090
+ }
1091
+ function isVerb(value) {
1092
+ return [
1093
+ "list",
1094
+ "get",
1095
+ "create",
1096
+ "update",
1097
+ "delete",
1098
+ "move",
1099
+ "restore",
1100
+ "archive",
1101
+ "unarchive",
1102
+ "search"
1103
+ ].includes(value);
1104
+ }
1105
+ function snakeToKebab(value) {
1106
+ return value.replaceAll("_", "-");
1107
+ }
1108
+ function isRecord3(value) {
1109
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1110
+ }
1111
+
875
1112
  // src/core/catalog/index.ts
876
1113
  function getGeneratedTools(env) {
877
1114
  return env === "dev" ? generatedTools2 : generatedTools;
878
1115
  }
1116
+ async function loadLiveTools(env, client) {
1117
+ return generateCatalog(await client.toolsList(), env);
1118
+ }
879
1119
 
880
1120
  // src/core/mcp/stdioBridge.ts
881
1121
  import { createInterface } from "readline";
@@ -905,7 +1145,7 @@ async function runStdioBridge(options) {
905
1145
  }
906
1146
  function parseJsonRpcRequest(line) {
907
1147
  const parsed = JSON.parse(line);
908
- if (!isRecord3(parsed) || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
1148
+ if (!isRecord4(parsed) || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
909
1149
  throw new Error("Invalid JSON-RPC request.");
910
1150
  }
911
1151
  return {
@@ -915,14 +1155,13 @@ function parseJsonRpcRequest(line) {
915
1155
  params: parsed.params
916
1156
  };
917
1157
  }
918
- function isRecord3(value) {
1158
+ function isRecord4(value) {
919
1159
  return typeof value === "object" && value !== null && !Array.isArray(value);
920
1160
  }
921
1161
 
922
1162
  // src/adapters/mcp/connector.ts
923
1163
  async function runConnector(options) {
924
- const tokenStore = new FileTokenStore();
925
- const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore, role: options.role });
1164
+ const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore: new FileTokenStore() });
926
1165
  const client = new McpRpcClient({
927
1166
  endpoint: options.endpoint,
928
1167
  tokenProvider: auth,
@@ -939,85 +1178,85 @@ async function runConnector(options) {
939
1178
  // src/core/catalog/enrichments.ts
940
1179
  var enrichments = [
941
1180
  {
942
- mcpName: "mcp__ainecto__list_projects",
1181
+ mcpName: "list_projects",
943
1182
  outputHint: "table",
944
1183
  tableColumns: ["uuid", "name", "projectKey", "workspaceUuid", "updatedAt"],
945
1184
  displayName: "List projects",
946
1185
  examples: ["ainecto projects list --env dev"]
947
1186
  },
948
1187
  {
949
- mcpName: "mcp__ainecto__list_workspaces",
1188
+ mcpName: "list_workspaces",
950
1189
  outputHint: "table",
951
1190
  tableColumns: ["uuid", "name", "updatedAt"],
952
1191
  displayName: "List workspaces"
953
1192
  },
954
1193
  {
955
- mcpName: "mcp__ainecto__list_folders",
1194
+ mcpName: "list_folders",
956
1195
  outputHint: "table",
957
1196
  tableColumns: ["uuid", "name", "projectUuid", "parentFolderUuid", "updatedAt"],
958
1197
  displayName: "List folders"
959
1198
  },
960
1199
  {
961
- mcpName: "mcp__ainecto__list_documents",
1200
+ mcpName: "list_documents",
962
1201
  outputHint: "table",
963
1202
  tableColumns: ["uuid", "title", "type", "projectUuid", "folderUuid", "updatedAt"],
964
1203
  displayName: "List documents"
965
1204
  },
966
1205
  {
967
- mcpName: "mcp__ainecto__search_documents",
1206
+ mcpName: "search_documents",
968
1207
  outputHint: "table",
969
1208
  tableColumns: ["uuid", "title", "type", "projectUuid", "updatedAt"],
970
1209
  displayName: "Search documents"
971
1210
  },
972
1211
  {
973
- mcpName: "mcp__ainecto__list_attachments",
1212
+ mcpName: "list_attachments",
974
1213
  outputHint: "table",
975
1214
  tableColumns: ["uuid", "filename", "contentType", "sizeBytes", "documentUuid"],
976
1215
  displayName: "List attachments"
977
1216
  },
978
1217
  {
979
- mcpName: "mcp__ainecto__erd_list_tables",
1218
+ mcpName: "erd_list_tables",
980
1219
  outputHint: "table",
981
1220
  tableColumns: ["uuid", "name", "schema_name", "display_order"],
982
1221
  displayName: "List ERD tables"
983
1222
  },
984
1223
  {
985
- mcpName: "mcp__ainecto__erd_list_refs",
1224
+ mcpName: "erd_list_refs",
986
1225
  outputHint: "table",
987
1226
  tableColumns: ["uuid", "name", "from_table_uuid", "to_table_uuid", "relationship"],
988
1227
  displayName: "List ERD refs"
989
1228
  },
990
1229
  {
991
- mcpName: "mcp__ainecto__erd_list_enums",
1230
+ mcpName: "erd_list_enums",
992
1231
  outputHint: "table",
993
1232
  tableColumns: ["uuid", "name", "display_order"],
994
1233
  displayName: "List ERD enums"
995
1234
  },
996
1235
  {
997
- mcpName: "mcp__ainecto__erd_list_table_groups",
1236
+ mcpName: "erd_list_table_groups",
998
1237
  outputHint: "table",
999
1238
  tableColumns: ["uuid", "name", "display_order"],
1000
1239
  displayName: "List ERD table groups"
1001
1240
  },
1002
1241
  {
1003
- mcpName: "mcp__ainecto__task_list_tasks",
1242
+ mcpName: "task_list_tasks",
1004
1243
  outputHint: "table",
1005
1244
  tableColumns: ["uuid", "title", "statusUuid", "assigneeId", "updatedAt"],
1006
1245
  displayName: "List tasks"
1007
1246
  },
1008
1247
  {
1009
- mcpName: "mcp__ainecto__task_list_statuses",
1248
+ mcpName: "task_list_statuses",
1010
1249
  outputHint: "table",
1011
1250
  tableColumns: ["uuid", "name", "displayOrder"],
1012
1251
  displayName: "List task statuses"
1013
1252
  },
1014
1253
  {
1015
- mcpName: "mcp__ainecto__enable_shares",
1254
+ mcpName: "enable_shares",
1016
1255
  aliases: ["shares enable"],
1017
1256
  displayName: "Enable shares"
1018
1257
  },
1019
1258
  {
1020
- mcpName: "mcp__ainecto__disable_shares",
1259
+ mcpName: "disable_shares",
1021
1260
  aliases: ["shares disable"],
1022
1261
  displayName: "Disable shares"
1023
1262
  }
@@ -1026,7 +1265,7 @@ var enrichments = [
1026
1265
  // src/core/output/table.ts
1027
1266
  function renderTable(data, options = {}) {
1028
1267
  const rows = extractRows(data);
1029
- if (!rows.length || !rows.every(isRecord4)) {
1268
+ if (!rows.length || !rows.every(isRecord5)) {
1030
1269
  return void 0;
1031
1270
  }
1032
1271
  const columns = options.columns?.length ? options.columns : inferColumns(rows);
@@ -1043,7 +1282,7 @@ function extractRows(data) {
1043
1282
  if (Array.isArray(data)) {
1044
1283
  return data;
1045
1284
  }
1046
- if (isRecord4(data)) {
1285
+ if (isRecord5(data)) {
1047
1286
  for (const key of ["items", "projects", "workspaces", "folders", "documents", "tasks", "attachments", "results", "data"]) {
1048
1287
  const value = data[key];
1049
1288
  if (Array.isArray(value)) {
@@ -1071,7 +1310,7 @@ function formatCell(value) {
1071
1310
  }
1072
1311
  return String(value);
1073
1312
  }
1074
- function isRecord4(value) {
1313
+ function isRecord5(value) {
1075
1314
  return typeof value === "object" && value !== null && !Array.isArray(value);
1076
1315
  }
1077
1316
 
@@ -1124,7 +1363,7 @@ function payloadToArgs(value) {
1124
1363
  if (value === void 0 || value === null) {
1125
1364
  return {};
1126
1365
  }
1127
- if (!isRecord5(value)) {
1366
+ if (!isRecord6(value)) {
1128
1367
  throw new CliCommandError("INVALID_PAYLOAD", "Generated command payload must be a JSON object.", { value });
1129
1368
  }
1130
1369
  return { ...value };
@@ -1228,12 +1467,12 @@ function coerceFlagValue(value, schema, key) {
1228
1467
  }
1229
1468
  function getSchemaProperties(schema) {
1230
1469
  const properties = schema.properties;
1231
- if (!isRecord5(properties)) {
1470
+ if (!isRecord6(properties)) {
1232
1471
  return {};
1233
1472
  }
1234
1473
  const result = {};
1235
1474
  for (const [key, value] of Object.entries(properties)) {
1236
- if (isRecord5(value)) {
1475
+ if (isRecord6(value)) {
1237
1476
  result[key] = value;
1238
1477
  }
1239
1478
  }
@@ -1248,7 +1487,7 @@ function requireValue(flag, value) {
1248
1487
  }
1249
1488
  return value;
1250
1489
  }
1251
- function isRecord5(value) {
1490
+ function isRecord6(value) {
1252
1491
  return typeof value === "object" && value !== null && !Array.isArray(value);
1253
1492
  }
1254
1493
 
@@ -1290,9 +1529,8 @@ async function promptConfirmation(io, message) {
1290
1529
  var ATTACHMENTS_UPLOAD_COMMAND_PATH = ["attachments", "upload"];
1291
1530
 
1292
1531
  // src/adapters/cli/generatedCommandRouter.ts
1293
- function matchGeneratedCommand(env, argv) {
1532
+ function matchGeneratedCommand(tools, argv) {
1294
1533
  const commandTokens = argv.filter((arg) => !arg.startsWith("-"));
1295
- const tools = getGeneratedTools(env);
1296
1534
  const entries = buildRouteEntries(tools, enrichments);
1297
1535
  const match = entries.filter((entry) => isPrefix(entry.path, commandTokens)).sort((a, b) => b.path.length - a.path.length)[0];
1298
1536
  if (!match) {
@@ -1308,7 +1546,7 @@ function matchGeneratedCommand(env, argv) {
1308
1546
  };
1309
1547
  }
1310
1548
  async function executeGeneratedCommand(options) {
1311
- const route = matchGeneratedCommand(options.env, options.argv);
1549
+ const route = matchGeneratedCommand(options.tools, options.argv);
1312
1550
  if (!route) {
1313
1551
  return 2;
1314
1552
  }
@@ -1471,7 +1709,7 @@ async function requestUploadToken(client, metadata, context) {
1471
1709
  requestArgs.ttlSeconds = context.ttlSeconds;
1472
1710
  }
1473
1711
  const response = unwrapMcpToolPayload(await client.toolsCall(context.requestToolName, requestArgs));
1474
- if (!isRecord6(response)) {
1712
+ if (!isRecord7(response)) {
1475
1713
  throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response was not an object.");
1476
1714
  }
1477
1715
  const token = response.token;
@@ -1541,7 +1779,7 @@ function parsePutResponse(text) {
1541
1779
  } catch {
1542
1780
  throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not valid JSON.");
1543
1781
  }
1544
- if (!isRecord6(parsed)) {
1782
+ if (!isRecord7(parsed)) {
1545
1783
  throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not an object.");
1546
1784
  }
1547
1785
  if (typeof parsed.storageKey !== "string") {
@@ -1574,10 +1812,10 @@ function validatePutResult(token, metadata, putResult) {
1574
1812
  }
1575
1813
  }
1576
1814
  function unwrapMcpToolPayload(value) {
1577
- if (!isRecord6(value) || !Array.isArray(value.content)) {
1815
+ if (!isRecord7(value) || !Array.isArray(value.content)) {
1578
1816
  return value;
1579
1817
  }
1580
- const text = value.content.map((item) => isRecord6(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
1818
+ const text = value.content.map((item) => isRecord7(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
1581
1819
  if (!text) {
1582
1820
  return value;
1583
1821
  }
@@ -1619,7 +1857,7 @@ var MIME_BY_EXTENSION = {
1619
1857
  ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1620
1858
  ".zip": "application/zip"
1621
1859
  };
1622
- function isRecord6(value) {
1860
+ function isRecord7(value) {
1623
1861
  return typeof value === "object" && value !== null && !Array.isArray(value);
1624
1862
  }
1625
1863
 
@@ -1744,12 +1982,12 @@ function renderAttachmentUploadResult(result, json) {
1744
1982
  }
1745
1983
 
1746
1984
  // 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";
1985
+ import { chmod as chmod2, mkdir as mkdir2, rename as rename2, rm as rm2, rmdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
1986
+ import { dirname as dirname3, join as join3, relative, resolve as resolve2, sep as sep2 } from "path";
1749
1987
 
1750
1988
  // src/core/harness/agentTargets.ts
1751
- var SERVER_NAME = "ai-erd";
1752
- var PACKAGE_NAME = "@ai-erd/mcp";
1989
+ import { basename as basename2 } from "path";
1990
+ var SERVER_NAME = "ainecto";
1753
1991
  var AGENT_TARGETS = [
1754
1992
  { id: "claude-code", label: "Claude Code", path: ".mcp.json" },
1755
1993
  { id: "cursor", label: "Cursor", path: ".cursor/mcp.json" }
@@ -1765,7 +2003,7 @@ function buildServerEntry(input) {
1765
2003
  return { command: "npx", args };
1766
2004
  }
1767
2005
  function readRoleFromEntry(entry) {
1768
- if (!isRecord7(entry) || !Array.isArray(entry.args)) {
2006
+ if (!isRecord8(entry) || !Array.isArray(entry.args)) {
1769
2007
  return void 0;
1770
2008
  }
1771
2009
  const index = entry.args.indexOf("--role");
@@ -1778,11 +2016,26 @@ function readServerEntry(existing) {
1778
2016
  }
1779
2017
  try {
1780
2018
  const parsed = JSON.parse(existing);
1781
- return parsed?.mcpServers?.[SERVER_NAME];
2019
+ const servers = parsed?.mcpServers;
2020
+ return isRecord8(servers) ? servers[SERVER_NAME] : void 0;
1782
2021
  } catch {
1783
2022
  return void 0;
1784
2023
  }
1785
2024
  }
2025
+ function isOurBridge(entry) {
2026
+ if (!isRecord8(entry)) {
2027
+ return false;
2028
+ }
2029
+ const { command, args } = entry;
2030
+ const argList = Array.isArray(args) ? args.filter((arg) => typeof arg === "string") : [];
2031
+ const isOurPackage = (arg) => arg === PACKAGE_NAME || arg.startsWith(`${PACKAGE_NAME}@`);
2032
+ if (argList.some((arg) => isOurPackage(arg) || arg === "--role")) {
2033
+ return true;
2034
+ }
2035
+ const bin = typeof command === "string" ? basename2(command) : "";
2036
+ return OUR_BINS.includes(bin);
2037
+ }
2038
+ var OUR_BINS = ["ainecto"];
1786
2039
  function mergeServerEntry(existing, entry) {
1787
2040
  const root = parseConfigObject(existing);
1788
2041
  const servers = readServers(root);
@@ -1802,7 +2055,9 @@ function removeServerEntry(existing, restore) {
1802
2055
  servers[SERVER_NAME] = restore.value;
1803
2056
  return { content: stringify({ ...root, mcpServers: servers }), removed, emptied: false };
1804
2057
  }
1805
- delete servers[SERVER_NAME];
2058
+ if (removed) {
2059
+ delete servers[SERVER_NAME];
2060
+ }
1806
2061
  const otherKeys = Object.keys(root).filter((key) => key !== "mcpServers");
1807
2062
  const emptied = Object.keys(servers).length === 0 && otherKeys.length === 0;
1808
2063
  return { content: stringify({ ...root, mcpServers: servers }), removed, emptied };
@@ -1832,7 +2087,7 @@ function readServers(root) {
1832
2087
  if (servers === void 0) {
1833
2088
  return {};
1834
2089
  }
1835
- if (!isRecord7(servers)) {
2090
+ if (!isRecord8(servers)) {
1836
2091
  throw new Error(
1837
2092
  '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
2093
  );
@@ -1851,7 +2106,7 @@ function parseConfigObject(existing) {
1851
2106
  `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
2107
  );
1853
2108
  }
1854
- if (!isRecord7(parsed)) {
2109
+ if (!isRecord8(parsed)) {
1855
2110
  throw new Error("Existing MCP config must be a JSON object; it was left untouched.");
1856
2111
  }
1857
2112
  return parsed;
@@ -1860,16 +2115,18 @@ function stringify(value) {
1860
2115
  return `${JSON.stringify(value, null, 2)}
1861
2116
  `;
1862
2117
  }
1863
- function isRecord7(value) {
2118
+ function isRecord8(value) {
1864
2119
  return typeof value === "object" && value !== null && !Array.isArray(value);
1865
2120
  }
1866
2121
 
1867
2122
  // 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";
2123
+ var MARKER_BEGIN = "<!-- ainecto:begin -->";
2124
+ var MARKER_END = "<!-- ainecto:end -->";
2125
+ var MARKERS = { begin: MARKER_BEGIN, end: MARKER_END };
2126
+ var HARNESS_DIR = ".ainecto";
2127
+ var HARNESS_DOC_PATH = `${HARNESS_DIR}/HARNESS.md`;
2128
+ var HARNESS_CONFIG_PATH = `${HARNESS_DIR}/config.json`;
2129
+ var HARNESS_RECORD_PATH = `${HARNESS_DIR}/init-record.json`;
1873
2130
  function renderHarnessDoc(input, template) {
1874
2131
  return fillTemplate(template ?? DEFAULT_HARNESS_DOC, {
1875
2132
  projectName: input.projectName,
@@ -1897,9 +2154,9 @@ function documentContractProblem(key, body) {
1897
2154
  }
1898
2155
  return void 0;
1899
2156
  }
1900
- var DEFAULT_HARNESS_DOC = `# AI-ERD Harness
2157
+ var DEFAULT_HARNESS_DOC = `# AiNecto Harness
1901
2158
 
1902
- This repository is wired to an AI-ERD project. The design source of truth lives in AI-ERD,
2159
+ This repository is wired to an AiNecto project. The design source of truth lives in AiNecto,
1903
2160
  not in this repository's files.
1904
2161
 
1905
2162
  - Project: **{{projectName}}** (\`{{projectUuid}}\`)
@@ -1908,9 +2165,11 @@ not in this repository's files.
1908
2165
  ## One session, one role
1909
2166
 
1910
2167
  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.
2168
+ MCP server configuration \u2014 not from this file \u2014 and it does not change during a session. The
2169
+ user picks the role; an agent applies a new one only when the user asks, and it takes effect in
2170
+ the next session. This is a working guardrail, not a security boundary. The server exposes only
2171
+ the tools that role is allowed to use, so a tool that is out of role is not merely discouraged:
2172
+ it is absent.
1914
2173
 
1915
2174
  | Role | Owns | Must not |
1916
2175
  | --- | --- | --- |
@@ -1937,7 +2196,7 @@ and which session should pick it up next.
1937
2196
 
1938
2197
  ## Not every change needs four sessions
1939
2198
 
1940
- A small change that fits the design already recorded in AI-ERD starts at Development. The rule
2199
+ A small change that fits the design already recorded in AiNecto starts at Development. The rule
1941
2200
  is not "always run four steps" \u2014 it is that when a step is needed, it does not happen inside
1942
2201
  another role's session.
1943
2202
 
@@ -1948,10 +2207,10 @@ Change the role in the MCP server configuration and start a new session:
1948
2207
  ${AGENT_TARGETS.map((target) => ` ${target.label.padEnd(12)} ${target.path}`).join("\n")}
1949
2208
  Codex global config \u2014 use a profile: codex -p <role>, or set ${ROLE_ENV_VAR}
1950
2209
 
1951
- Or run \`ai-erd init --role <design|development|test|validation>\` to rewrite them.
2210
+ Or run \`ainecto init --role <design|development|test|validation>\` to rewrite them.
1952
2211
 
1953
2212
  ---
1954
- Generated by \`ai-erd init\`. Remove everything it wrote with \`ai-erd init --undo\`.
2213
+ Generated by \`ainecto init\`. Remove everything it wrote with \`ainecto init --undo\`.
1955
2214
  `;
1956
2215
  function renderAgentNote(template) {
1957
2216
  return fillTemplate(template ?? DEFAULT_AGENT_NOTE, {
@@ -1960,15 +2219,15 @@ function renderAgentNote(template) {
1960
2219
  });
1961
2220
  }
1962
2221
  var DEFAULT_AGENT_NOTE = `${MARKER_BEGIN}
1963
- ## AI-ERD Harness
2222
+ ## AiNecto Harness
1964
2223
 
1965
- This repository uses AI-ERD as its design source of truth, and each session has one role
2224
+ This repository uses AiNecto as its design source of truth, and each session has one role
1966
2225
  (Design / Development / Test / Validation). Read \`{{harnessDocPath}}\` before doing work that
1967
2226
  touches requirements, the ERD, or architecture.
1968
2227
 
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,
2228
+ The role of this session comes from the \`{{serverName}}\` MCP server configuration that
2229
+ \`ainecto init\` wrote; the server states it in its \`initialize\` instructions and exposes only
2230
+ that role's tools. If the work needs something outside the role,
1972
2231
  stop and report which session is required.
1973
2232
  ${MARKER_END}`;
1974
2233
  function managedBlockProblem(key, body) {
@@ -2020,38 +2279,47 @@ function upsertMarkerBlock(existing, block) {
2020
2279
  return composed;
2021
2280
  }
2022
2281
  function assertComposedBlockIsOurs(composed, existing) {
2023
- if (ownedMarkerCount(composed, MARKER_BEGIN) === 1 && ownedMarkerCount(composed, MARKER_END) === 1) {
2282
+ if (ownedMarkerCount(composed, "begin") === 1 && ownedMarkerCount(composed, "end") === 1) {
2024
2283
  return;
2025
2284
  }
2026
2285
  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
2286
  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.`
2287
+ `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
2288
  );
2030
2289
  }
2031
2290
  function assertNoDanglingMarker(content) {
2032
- const begins = ownedMarkerCount(content, MARKER_BEGIN);
2033
- const ends = ownedMarkerCount(content, MARKER_END);
2291
+ const begins = ownedMarkerCount(content, "begin");
2292
+ const ends = ownedMarkerCount(content, "end");
2034
2293
  if (begins !== ends || begins > 1) {
2035
2294
  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.`
2295
+ `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
2296
  );
2038
2297
  }
2039
2298
  if (begins === 0) {
2040
2299
  return;
2041
2300
  }
2042
- if (ownedMarkerIndex(content, MARKER_END, 0) < ownedMarkerIndex(content, MARKER_BEGIN, 0)) {
2301
+ if (ownedMarker(content, "end", 0).at < ownedMarker(content, "begin", 0).at) {
2043
2302
  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.`
2303
+ `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
2304
  );
2046
2305
  }
2047
2306
  }
2048
- function ownedMarkerCount(content, marker) {
2307
+ function ownedMarkerCount(content, kind) {
2049
2308
  let count = 0;
2050
- for (let at = ownedMarkerIndex(content, marker, 0); at >= 0; at = ownedMarkerIndex(content, marker, at + 1)) {
2309
+ for (let hit2 = ownedMarker(content, kind, 0); hit2 !== void 0; hit2 = ownedMarker(content, kind, hit2.at + 1)) {
2051
2310
  count += 1;
2052
2311
  }
2053
2312
  return count;
2054
2313
  }
2314
+ function ownedMarker(content, kind, from) {
2315
+ return hit(ownedMarkerIndex(content, MARKERS[kind], from), MARKERS[kind]);
2316
+ }
2317
+ function anyMarker(content, kind, from) {
2318
+ return hit(content.indexOf(MARKERS[kind], from), MARKERS[kind]);
2319
+ }
2320
+ function hit(at, marker) {
2321
+ return at >= 0 ? { at, length: marker.length } : void 0;
2322
+ }
2055
2323
  function isInsideFencedBlock(content, index) {
2056
2324
  let open;
2057
2325
  let offset = 0;
@@ -2116,24 +2384,24 @@ function markerEvidence(content) {
2116
2384
  let begins = 0;
2117
2385
  let ends = 0;
2118
2386
  while (true) {
2119
- const begin = rest.indexOf(MARKER_BEGIN, cursor);
2120
- const close = rest.indexOf(MARKER_END, cursor);
2121
- if (begin < 0 && close < 0) {
2387
+ const begin = anyMarker(rest, "begin", cursor);
2388
+ const close = anyMarker(rest, "end", cursor);
2389
+ if (begin === void 0 && close === void 0) {
2122
2390
  break;
2123
2391
  }
2124
- if (begin < 0 || close >= 0 && close < begin) {
2392
+ if (begin === void 0 || close !== void 0 && close.at < begin.at) {
2125
2393
  ends += 1;
2126
- cursor = close + MARKER_END.length;
2394
+ cursor = close.at + close.length;
2127
2395
  continue;
2128
2396
  }
2129
- const pairEnd = rest.indexOf(MARKER_END, begin + MARKER_BEGIN.length);
2130
- if (pairEnd < 0) {
2397
+ const pairEnd = anyMarker(rest, "end", begin.at + begin.length);
2398
+ if (pairEnd === void 0) {
2131
2399
  begins += 1;
2132
- cursor = begin + MARKER_BEGIN.length;
2400
+ cursor = begin.at + begin.length;
2133
2401
  continue;
2134
2402
  }
2135
- evidence.push(blockShape(rest.slice(begin, pairEnd + MARKER_END.length)));
2136
- cursor = pairEnd + MARKER_END.length;
2403
+ evidence.push(blockShape(rest.slice(begin.at, pairEnd.at + pairEnd.length)));
2404
+ cursor = pairEnd.at + pairEnd.length;
2137
2405
  }
2138
2406
  for (let i = 0; i < begins; i += 1) evidence.push(UNPAIRED_BEGIN);
2139
2407
  for (let i = 0; i < ends; i += 1) evidence.push(UNPAIRED_END);
@@ -2150,15 +2418,15 @@ function existingMarkerBlock(content) {
2150
2418
  return range ? content.slice(range.start, range.end) : void 0;
2151
2419
  }
2152
2420
  function markerRange(content) {
2153
- const start = ownedMarkerIndex(content, MARKER_BEGIN, 0);
2154
- if (start < 0) {
2421
+ const start = ownedMarker(content, "begin", 0);
2422
+ if (start === void 0) {
2155
2423
  return void 0;
2156
2424
  }
2157
- const endMarker = ownedMarkerIndex(content, MARKER_END, start);
2158
- if (endMarker < 0) {
2425
+ const endMarker = ownedMarker(content, "end", start.at);
2426
+ if (endMarker === void 0) {
2159
2427
  return void 0;
2160
2428
  }
2161
- return { start, end: endMarker + MARKER_END.length };
2429
+ return { start: start.at, end: endMarker.at + endMarker.length };
2162
2430
  }
2163
2431
  function ownedMarkerIndex(content, marker, from) {
2164
2432
  for (let at = content.indexOf(marker, from); at >= 0; at = content.indexOf(marker, at + 1)) {
@@ -2180,7 +2448,7 @@ function ownedMarkerIndex(content, marker, from) {
2180
2448
  }
2181
2449
 
2182
2450
  // src/core/harness/initRecord.ts
2183
- var RECORD_KIND = "ai-erd/init-record";
2451
+ var RECORD_KIND = "ainecto/init-record";
2184
2452
  var RECORD_VERSION = 2;
2185
2453
  var ABSENT = { at: "absent" };
2186
2454
  function emptyRecord() {
@@ -2202,7 +2470,7 @@ function readRecord(content) {
2202
2470
  if (parsed.kind !== RECORD_KIND) {
2203
2471
  return {
2204
2472
  state: "corrupt",
2205
- reason: `${HARNESS_RECORD_PATH} exists but is not an ai-erd record (kind=${JSON.stringify(parsed.kind)})`
2473
+ reason: `${HARNESS_RECORD_PATH} exists but is not an AiNecto init record (kind=${JSON.stringify(parsed.kind)})`
2206
2474
  };
2207
2475
  }
2208
2476
  if (parsed.version !== RECORD_VERSION) {
@@ -2239,46 +2507,6 @@ function readRecord(content) {
2239
2507
  }
2240
2508
  };
2241
2509
  }
2242
- var LEGACY_MANAGED_KEYS = [
2243
- "created",
2244
- "originals",
2245
- "lastWritten",
2246
- "lastWrittenFragment",
2247
- "lastWrittenBody",
2248
- "replacedEntries",
2249
- "replacedBlocks",
2250
- "blockAdded",
2251
- "foreignMarkers"
2252
- ];
2253
- function legacyManagedRecord(configContent) {
2254
- if (configContent === void 0) {
2255
- return false;
2256
- }
2257
- let parsed;
2258
- try {
2259
- parsed = JSON.parse(configContent);
2260
- } catch {
2261
- return false;
2262
- }
2263
- if (!isObject(parsed) || !isObject(parsed.managed)) {
2264
- return false;
2265
- }
2266
- const managed = parsed.managed;
2267
- const hasLegacyShape = LEGACY_MANAGED_KEYS.some(
2268
- (key) => Object.prototype.hasOwnProperty.call(managed, key)
2269
- );
2270
- if (!hasLegacyShape) {
2271
- return false;
2272
- }
2273
- if (typeof parsed.generatedBy === "string") {
2274
- return true;
2275
- }
2276
- const lastWritten = managed.lastWritten;
2277
- return isObject(lastWritten) && Object.prototype.hasOwnProperty.call(lastWritten, HARNESS_CONFIG_PATH);
2278
- }
2279
- 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.`;
2281
- }
2282
2510
  function serializeRecord(record) {
2283
2511
  return `${JSON.stringify({
2284
2512
  kind: RECORD_KIND,
@@ -2297,7 +2525,7 @@ function readContract(raw) {
2297
2525
  return { problem: `has an unknown scope (${JSON.stringify(raw.scope)})` };
2298
2526
  }
2299
2527
  if (typeof raw.createdByUs !== "boolean") {
2300
- return { problem: "does not say whether ai-erd created the file" };
2528
+ return { problem: "does not say whether ainecto created the file" };
2301
2529
  }
2302
2530
  const original = readOwned(raw.original);
2303
2531
  const written = readOwned(raw.written);
@@ -2305,7 +2533,7 @@ function readContract(raw) {
2305
2533
  return { problem: "has no readable record of what was there before" };
2306
2534
  }
2307
2535
  if (written === void 0) {
2308
- return { problem: "has no readable record of what ai-erd wrote" };
2536
+ return { problem: "has no readable record of what ainecto wrote" };
2309
2537
  }
2310
2538
  if (raw.fileFingerprint !== void 0 && typeof raw.fileFingerprint !== "string") {
2311
2539
  return { problem: "has a malformed fileFingerprint" };
@@ -2390,7 +2618,7 @@ function fieldsContractProblem(contract, ownedFields) {
2390
2618
  if (!Object.prototype.hasOwnProperty.call(body, key)) {
2391
2619
  return `says it wrote ${key}, but that field is not in the file body it recorded`;
2392
2620
  }
2393
- if (stableStringify(body[key]) !== stableStringify(writtenFields[key])) {
2621
+ if (stableStringify2(body[key]) !== stableStringify2(writtenFields[key])) {
2394
2622
  return `records a different value for ${key} than the file body it wrote`;
2395
2623
  }
2396
2624
  }
@@ -2418,13 +2646,13 @@ function readOwned(raw) {
2418
2646
  }
2419
2647
  return void 0;
2420
2648
  }
2421
- function stableStringify(value) {
2649
+ function stableStringify2(value) {
2422
2650
  if (Array.isArray(value)) {
2423
- return `[${value.map(stableStringify).join(",")}]`;
2651
+ return `[${value.map(stableStringify2).join(",")}]`;
2424
2652
  }
2425
2653
  if (value && typeof value === "object") {
2426
2654
  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(",")}}`;
2655
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify2(item)}`).join(",")}}`;
2428
2656
  }
2429
2657
  return JSON.stringify(value) ?? "null";
2430
2658
  }
@@ -2436,7 +2664,176 @@ function isStringArray(value) {
2436
2664
  }
2437
2665
 
2438
2666
  // src/core/harness/initPlan.ts
2439
- import { createHash as createHash3 } from "crypto";
2667
+ import { createHash as createHash4 } from "crypto";
2668
+
2669
+ // src/core/harness/repositoryRole.ts
2670
+ import { lstat, readFile as readFile4, realpath } from "fs/promises";
2671
+ import { homedir as homedir2 } from "os";
2672
+ import { dirname as dirname2, join as join2, resolve, sep } from "path";
2673
+ function readConfigRole(path, content) {
2674
+ if (content === void 0 || !content.trim()) {
2675
+ return { kind: "absent" };
2676
+ }
2677
+ let servers;
2678
+ try {
2679
+ const parsed = JSON.parse(content);
2680
+ const raw = parsed?.mcpServers;
2681
+ servers = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
2682
+ } catch {
2683
+ return content.includes(SERVER_NAME) ? { kind: "problem", reason: `${path} is not valid JSON` } : { kind: "absent" };
2684
+ }
2685
+ const entry = servers[SERVER_NAME];
2686
+ if (!isOurBridge(entry)) {
2687
+ return { kind: "absent" };
2688
+ }
2689
+ const role = readRoleFromEntry(entry);
2690
+ if (role === void 0) {
2691
+ return { kind: "problem", reason: `${path} has the "${SERVER_NAME}" bridge without a valid --role` };
2692
+ }
2693
+ return { kind: "role", role };
2694
+ }
2695
+ function rolesInFiles(files) {
2696
+ const roles = [];
2697
+ const problems = [];
2698
+ for (const target of AGENT_TARGETS) {
2699
+ const read = readConfigRole(target.path, files.get(target.path));
2700
+ if (read.kind === "problem") {
2701
+ problems.push(read.reason);
2702
+ } else if (read.kind === "role" && !roles.includes(read.role)) {
2703
+ roles.push(read.role);
2704
+ }
2705
+ }
2706
+ return { roles, problems, found: roles.length > 0 || problems.length > 0 };
2707
+ }
2708
+ async function findRepositoryRole(cwd, home = homedir2()) {
2709
+ const stopAt = await realOrResolved(home);
2710
+ for (let dir = await realOrResolved(cwd); ; dir = dirname2(dir)) {
2711
+ if (samePath(dir, stopAt)) {
2712
+ return { kind: "none" };
2713
+ }
2714
+ const files = /* @__PURE__ */ new Map();
2715
+ let readProblem;
2716
+ for (const target of AGENT_TARGETS) {
2717
+ try {
2718
+ files.set(target.path, await readConfigInside(dir, target.path));
2719
+ } catch (error) {
2720
+ readProblem = error instanceof Error ? error.message : String(error);
2721
+ }
2722
+ }
2723
+ if (readProblem !== void 0) {
2724
+ return { kind: "conflict", dir, reason: readProblem };
2725
+ }
2726
+ const result = rolesInFiles(files);
2727
+ if (result.found) {
2728
+ if (result.problems.length > 0) {
2729
+ return { kind: "conflict", dir, reason: result.problems.join("; ") };
2730
+ }
2731
+ if (result.roles.length > 1) {
2732
+ return {
2733
+ kind: "conflict",
2734
+ dir,
2735
+ reason: `agent configs disagree about the session role (${result.roles.join(", ")})`
2736
+ };
2737
+ }
2738
+ return { kind: "role", role: result.roles[0], dir };
2739
+ }
2740
+ if (dirname2(dir) === dir) {
2741
+ return { kind: "none" };
2742
+ }
2743
+ }
2744
+ }
2745
+ async function askTheUserToFixRole(dir, reason) {
2746
+ const gitRoot = await findGitRoot(dir);
2747
+ const roles = HARNESS_ROLES.join(", ");
2748
+ if (gitRoot !== void 0 && !samePath(await realOrResolved(gitRoot), await realOrResolved(dir))) {
2749
+ 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}).`;
2750
+ }
2751
+ 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}.`;
2752
+ }
2753
+ async function realOrResolved(path) {
2754
+ try {
2755
+ return await realpath(path);
2756
+ } catch {
2757
+ return resolve(path);
2758
+ }
2759
+ }
2760
+ function samePath(left, right) {
2761
+ return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
2762
+ }
2763
+ async function readConfigInside(dir, path) {
2764
+ let real;
2765
+ try {
2766
+ real = await realpath(join2(dir, path));
2767
+ } catch (error) {
2768
+ if (isNodeError2(error, "ENOENT") || isNodeError2(error, "ENOTDIR")) {
2769
+ return void 0;
2770
+ }
2771
+ throw error;
2772
+ }
2773
+ const realDir = await realOrResolved(dir);
2774
+ if (!samePath(real, realDir) && !real.startsWith(realDir + sep) && !(process.platform === "win32" && real.toLowerCase().startsWith((realDir + sep).toLowerCase()))) {
2775
+ throw new Error(`${path} points outside ${dir} (${real})`);
2776
+ }
2777
+ return readFile4(real, "utf8");
2778
+ }
2779
+ async function findGitRoot(cwd) {
2780
+ for (let at = resolve(cwd); ; at = dirname2(at)) {
2781
+ try {
2782
+ await lstat(join2(at, ".git"));
2783
+ return at;
2784
+ } catch {
2785
+ }
2786
+ if (dirname2(at) === at) {
2787
+ return void 0;
2788
+ }
2789
+ }
2790
+ }
2791
+ async function readManagedFile(root, path) {
2792
+ await assertInsideRepository(root, path);
2793
+ return readIfExists(join2(root, path));
2794
+ }
2795
+ async function readIfExists(absolutePath) {
2796
+ try {
2797
+ return await readFile4(absolutePath, "utf8");
2798
+ } catch (error) {
2799
+ if (isNodeError2(error, "ENOENT")) {
2800
+ return void 0;
2801
+ }
2802
+ throw error;
2803
+ }
2804
+ }
2805
+ async function assertInsideRepository(root, relativePath) {
2806
+ const absolutePath = join2(root, relativePath);
2807
+ const realRoot = await realpath(root);
2808
+ let realParent;
2809
+ try {
2810
+ realParent = await realpath(dirname2(absolutePath));
2811
+ } catch (error) {
2812
+ if (isNodeError2(error, "ENOENT")) {
2813
+ return;
2814
+ }
2815
+ throw error;
2816
+ }
2817
+ if (realParent !== realRoot && !realParent.startsWith(realRoot + sep)) {
2818
+ throw new Error(
2819
+ `${relativePath} resolves outside the repository (${realParent}); refusing to touch it.`
2820
+ );
2821
+ }
2822
+ try {
2823
+ if ((await lstat(absolutePath)).isSymbolicLink()) {
2824
+ throw new Error(`${relativePath} is a symbolic link; refusing to write through it.`);
2825
+ }
2826
+ } catch (error) {
2827
+ if (!isNodeError2(error, "ENOENT")) {
2828
+ throw error;
2829
+ }
2830
+ }
2831
+ }
2832
+ function isNodeError2(error, code) {
2833
+ return typeof error === "object" && error !== null && error.code === code;
2834
+ }
2835
+
2836
+ // src/core/harness/initPlan.ts
2440
2837
  var NOTE_TARGETS = [
2441
2838
  { path: "AGENTS.md", createIfMissing: true },
2442
2839
  { path: "CLAUDE.md", createIfMissing: false }
@@ -2464,10 +2861,10 @@ function sameOwned(left, right) {
2464
2861
  if (left.at === "text" && right.at === "text") {
2465
2862
  return left.text === right.text;
2466
2863
  }
2467
- return left.at === "json" && right.at === "json" && stableStringify(left.value) === stableStringify(right.value);
2864
+ return left.at === "json" && right.at === "json" && stableStringify2(left.value) === stableStringify2(right.value);
2468
2865
  }
2469
2866
  function contentFingerprint(content) {
2470
- return createHash3("sha256").update(content).digest("hex");
2867
+ return createHash4("sha256").update(content).digest("hex");
2471
2868
  }
2472
2869
  function serialize(body) {
2473
2870
  return `${JSON.stringify(body, null, 2)}
@@ -2478,7 +2875,7 @@ function fingerprintFor(path, content) {
2478
2875
  return contentFingerprint(content);
2479
2876
  }
2480
2877
  try {
2481
- return contentFingerprint(stableStringify(JSON.parse(content)));
2878
+ return contentFingerprint(stableStringify2(JSON.parse(content)));
2482
2879
  } catch {
2483
2880
  return contentFingerprint(content);
2484
2881
  }
@@ -2513,9 +2910,6 @@ function initRefusal(files, previous) {
2513
2910
  if (state.state === "corrupt" || state.state === "legacy") {
2514
2911
  return `${state.reason}. Nothing was changed \u2014 move that file aside (or restore it) and run again.`;
2515
2912
  }
2516
- if (state.state === "absent" && legacyManagedRecord(files.get(HARNESS_CONFIG_PATH))) {
2517
- return legacyManagedNotice(HARNESS_CONFIG_PATH);
2518
- }
2519
2913
  const carried = state.state === "valid" ? state.record : emptyRecord();
2520
2914
  for (const [path, contract] of Object.entries(carried.files)) {
2521
2915
  if (contract.scope !== "fields") {
@@ -2558,7 +2952,7 @@ function planInit(input) {
2558
2952
  return prior;
2559
2953
  }
2560
2954
  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.`);
2955
+ notes.push(`${path}: it changed outside ainecto, so the older backup was dropped and this is the new baseline.`);
2562
2956
  return void 0;
2563
2957
  };
2564
2958
  for (const target of AGENT_TARGETS) {
@@ -2569,7 +2963,7 @@ function planInit(input) {
2569
2963
  if (existing === merged.content) {
2570
2964
  continue;
2571
2965
  }
2572
- const before = previousRole(existing);
2966
+ const before = previousRole(target.path, existing);
2573
2967
  if (merged.replaced && before && before !== input.role) {
2574
2968
  notes.push(`${target.path}: role ${before} \u2192 ${input.role}`);
2575
2969
  }
@@ -2625,7 +3019,7 @@ function planInit(input) {
2625
3019
  }
2626
3020
  if (carried?.scope === "block" && unexplainedMarkers(existing, carried) > 0) {
2627
3021
  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.`
3022
+ `${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
3023
  );
2630
3024
  continue;
2631
3025
  }
@@ -2635,7 +3029,7 @@ function planInit(input) {
2635
3029
  }
2636
3030
  const original = prior?.original ?? ownedText(blockNow);
2637
3031
  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`);
3032
+ notes.push(`${target.path}: an existing ainecto block was saved and will be restored by --undo`);
2639
3033
  }
2640
3034
  record.files[target.path] = {
2641
3035
  scope: "block",
@@ -2672,11 +3066,11 @@ function planInit(input) {
2672
3066
  const currentValue = presentNow ? theirConfig[field] : void 0;
2673
3067
  const priorOriginal = priorConfig?.originalFields?.[field];
2674
3068
  const priorWritten = priorConfig?.writtenFields;
2675
- const untouchedByUser = priorWritten !== void 0 && hasField(priorWritten, field) && stableStringify(currentValue) === stableStringify(priorWritten[field]);
3069
+ const untouchedByUser = priorWritten !== void 0 && hasField(priorWritten, field) && stableStringify2(currentValue) === stableStringify2(priorWritten[field]);
2676
3070
  const next = untouchedByUser && priorOriginal !== void 0 ? priorOriginal : ownedJson(currentValue, presentNow);
2677
3071
  originalFields[field] = next;
2678
3072
  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.`);
3073
+ notes.push(`${HARNESS_CONFIG_PATH}: you changed ${field} \u2014 ainecto now treats your value as the one to put back.`);
2680
3074
  }
2681
3075
  }
2682
3076
  const mergedConfig = serialize({ ...theirConfig ?? {}, ...ourFields });
@@ -2725,7 +3119,7 @@ function planUndo(input) {
2725
3119
  notes.push(`If you set ${ROLE_ENV_VAR} anywhere, unset it too.`);
2726
3120
  };
2727
3121
  if (state.state === "absent") {
2728
- 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.`);
3122
+ notes.push(`No record of a previous init in ${HARNESS_RECORD_PATH} \u2014 no local file was changed.`);
2729
3123
  tail();
2730
3124
  return { writes, deletes, codexCommand: codexRemoveCommand(), notes };
2731
3125
  }
@@ -2810,7 +3204,7 @@ function undoEntry(path, contract, existing) {
2810
3204
  if (sameOwned(currentOwned, contract.original)) {
2811
3205
  return {
2812
3206
  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.`
3207
+ 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
3208
  };
2815
3209
  }
2816
3210
  const goneButWeHoldTheOriginal = current === void 0 && contract.original.at !== "absent";
@@ -2840,13 +3234,13 @@ function undoBlock(path, contract, existing) {
2840
3234
  if (unexplainedMarkers(existing, contract) > 0) {
2841
3235
  return {
2842
3236
  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.`
3237
+ 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
3238
  };
2845
3239
  }
2846
3240
  if (sameOwned(currentOwned, contract.original)) {
2847
3241
  return {
2848
3242
  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.`
3243
+ 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
3244
  };
2851
3245
  }
2852
3246
  if (current === void 0 && contract.original.at === "text") {
@@ -2855,12 +3249,12 @@ function undoBlock(path, contract, existing) {
2855
3249
  ${contract.original.text}
2856
3250
  ` : `${contract.original.text}
2857
3251
  `;
2858
- return { kind: "write", content: body, note: `${path}: restored the ai-erd block that was there before.` };
3252
+ return { kind: "write", content: body, note: `${path}: restored the ainecto block that was there before.` };
2859
3253
  }
2860
3254
  if (!sameOwned(currentOwned, contract.written)) {
2861
3255
  return {
2862
3256
  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.`
3257
+ 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
3258
  };
2865
3259
  }
2866
3260
  let result;
@@ -2872,7 +3266,7 @@ ${contract.original.text}
2872
3266
  if (!result.removed) {
2873
3267
  return {
2874
3268
  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.`
3269
+ 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
3270
  };
2877
3271
  }
2878
3272
  if (result.emptied && contract.createdByUs) {
@@ -2881,7 +3275,7 @@ ${contract.original.text}
2881
3275
  return {
2882
3276
  kind: "write",
2883
3277
  content: result.content,
2884
- note: contract.original.at === "text" ? `${path}: restored the ai-erd block that was there before.` : void 0
3278
+ note: contract.original.at === "text" ? `${path}: restored the ainecto block that was there before.` : void 0
2885
3279
  };
2886
3280
  }
2887
3281
  function undoWhole(path, contract, existing) {
@@ -2911,14 +3305,14 @@ function undoFields(path, contract, existing) {
2911
3305
  if (parsed === void 0) {
2912
3306
  return {
2913
3307
  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.`
3308
+ 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
3309
  };
2916
3310
  }
2917
3311
  const contractProblem = fieldsContractProblem(contract, OUR_CONFIG_FIELDS);
2918
3312
  if (contractProblem !== void 0) {
2919
3313
  return {
2920
3314
  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.`
3315
+ 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
3316
  };
2923
3317
  }
2924
3318
  const written = contract.writtenFields;
@@ -2933,10 +3327,10 @@ function undoFields(path, contract, existing) {
2933
3327
  if (!hasField(written, field)) {
2934
3328
  return {
2935
3329
  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.`
3330
+ 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
3331
  };
2938
3332
  }
2939
- const stillOurs = stableStringify(value) === stableStringify(written[field]);
3333
+ const stillOurs = stableStringify2(value) === stableStringify2(written[field]);
2940
3334
  if (!stillOurs) {
2941
3335
  next[field] = value;
2942
3336
  kept.push(field);
@@ -2946,7 +3340,7 @@ function undoFields(path, contract, existing) {
2946
3340
  if (original === void 0 || original.at !== "json" && original.at !== "absent") {
2947
3341
  return {
2948
3342
  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.`
3343
+ 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
3344
  };
2951
3345
  }
2952
3346
  if (original.at === "json") {
@@ -2961,7 +3355,7 @@ function undoFields(path, contract, existing) {
2961
3355
  return body === existing ? { kind: "done", note } : {
2962
3356
  kind: "write",
2963
3357
  content: body,
2964
- note: note ?? `${path}: removed what ai-erd wrote and kept the rest.`
3358
+ note: note ?? `${path}: removed what ainecto wrote and kept the rest.`
2965
3359
  };
2966
3360
  }
2967
3361
  function withCreatedDirectories(current, directories) {
@@ -2981,27 +3375,9 @@ function parentsOf(paths) {
2981
3375
  }
2982
3376
  return [...directories].sort((left, right) => right.length - left.length);
2983
3377
  }
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) {
2989
- continue;
2990
- }
2991
- found.set(target.path, previousRole(existing));
2992
- }
2993
- return found;
2994
- }
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;
3004
- }
3378
+ function previousRole(path, existing) {
3379
+ const read = readConfigRole(path, existing);
3380
+ return read.kind === "role" ? read.role : void 0;
3005
3381
  }
3006
3382
 
3007
3383
  // src/core/harness/documentFetch.ts
@@ -3057,10 +3433,97 @@ function usableBodyProblem(key, entry) {
3057
3433
  return documentContractProblem(key, entry.body);
3058
3434
  }
3059
3435
 
3436
+ // src/adapters/cli/prompter.ts
3437
+ import { createInterface as createInterface3 } from "readline";
3438
+ var PromptCancelled = class extends Error {
3439
+ constructor() {
3440
+ super("Cancelled \u2014 no repository files were written. A sign-in you already completed stays saved.");
3441
+ this.name = "PromptCancelled";
3442
+ }
3443
+ };
3444
+ var AGENT_ENV_MARKERS = ["CLAUDECODE", "CI", "GEMINI_CLI", "CODEX_SANDBOX", "CURSOR_AGENT"];
3445
+ function agentEnvironment(env = process.env) {
3446
+ return AGENT_ENV_MARKERS.find((name) => {
3447
+ const value = env[name]?.trim().toLowerCase();
3448
+ return value !== void 0 && value !== "" && value !== "0" && value !== "false";
3449
+ });
3450
+ }
3451
+ function isInteractive(io, json, env = process.env) {
3452
+ return io.stdin?.isTTY === true && io.stdout?.isTTY === true && !json && agentEnvironment(env) === void 0;
3453
+ }
3454
+ function createReadlinePrompter(io) {
3455
+ let rl;
3456
+ let lines;
3457
+ const readLine = async (prompt) => {
3458
+ io.stderr.write(prompt);
3459
+ if (!rl) {
3460
+ rl = createInterface3({ input: io.stdin, terminal: false });
3461
+ lines = rl[Symbol.asyncIterator]();
3462
+ }
3463
+ const next = await lines.next();
3464
+ if (next.done) {
3465
+ io.stderr.write("\n");
3466
+ throw new PromptCancelled();
3467
+ }
3468
+ return next.value;
3469
+ };
3470
+ return {
3471
+ choose: (question, choices, defaultIndex) => chooseWith(readLine, (text) => io.stderr.write(text), question, choices, defaultIndex),
3472
+ text: async (question, defaultValue) => {
3473
+ const answer = (await readLine(`${question} [${defaultValue}]: `)).trim();
3474
+ return answer || defaultValue;
3475
+ },
3476
+ close: () => {
3477
+ rl?.close();
3478
+ }
3479
+ };
3480
+ }
3481
+ async function chooseWith(readLine, write, question, choices, defaultIndex) {
3482
+ if (choices.length === 0) {
3483
+ throw new Error(`Nothing to choose for: ${question}`);
3484
+ }
3485
+ write(`${question}
3486
+ `);
3487
+ choices.forEach((choice, index) => write(` [${index + 1}] ${choice.label}
3488
+ `));
3489
+ const hint = defaultIndex === void 0 ? "" : ` [${defaultIndex + 1}]`;
3490
+ for (; ; ) {
3491
+ const answer = (await readLine(`Enter a number (1-${choices.length})${hint}: `)).trim();
3492
+ if (answer === "" && defaultIndex !== void 0) {
3493
+ return choices[defaultIndex].value;
3494
+ }
3495
+ const picked = /^\d+$/.test(answer) ? Number(answer) : NaN;
3496
+ if (picked >= 1 && picked <= choices.length) {
3497
+ return choices[picked - 1].value;
3498
+ }
3499
+ write(answer === "" ? "Please choose one \u2014 there is no default here.\n" : `"${answer}" is not one of the numbers above.
3500
+ `);
3501
+ }
3502
+ }
3503
+
3060
3504
  // src/adapters/cli/initCommand.ts
3505
+ function singleRole(roles) {
3506
+ return roles.length === 1 ? roles[0] : void 0;
3507
+ }
3061
3508
  async function executeInitCommand(options) {
3509
+ try {
3510
+ return await runInit(options);
3511
+ } catch (error) {
3512
+ if (error instanceof PromptCancelled) {
3513
+ options.io.stderr.write(`${error.message}
3514
+ `);
3515
+ return 130;
3516
+ }
3517
+ throw error;
3518
+ } finally {
3519
+ options.prompter?.close();
3520
+ }
3521
+ }
3522
+ async function runInit(initialOptions) {
3523
+ let options = initialOptions;
3062
3524
  const args = parseInitArgs(options.argv);
3063
- const root = resolve(options.cwd);
3525
+ const root = resolve2(options.cwd);
3526
+ const rootNotes = await repositoryRootNotes(root);
3064
3527
  const files = await readSnapshot(root);
3065
3528
  if (args.undo) {
3066
3529
  const plan2 = planUndo({ files });
@@ -3070,23 +3533,34 @@ async function executeInitCommand(options) {
3070
3533
  action: args.dryRun ? "undo (dry-run)" : "undo",
3071
3534
  updated: plan2.writes.map((write) => write.path),
3072
3535
  removed: plan2.deletes,
3073
- notes: plan2.notes
3536
+ notes: [...rootNotes, ...plan2.notes]
3074
3537
  },
3075
3538
  { json: options.json }
3076
3539
  ));
3077
3540
  return 0;
3078
3541
  }
3079
- const role = options.role ?? currentRole(files);
3080
- if (!role) {
3081
- throw new Error(
3082
- `--role is required on first run. Pick one of: ${HARNESS_ROLES.join(", ")}.`
3083
- );
3084
- }
3085
3542
  const refusal = initRefusal(files);
3086
3543
  if (refusal !== void 0) {
3087
3544
  throw new Error(refusal);
3088
3545
  }
3089
- const project = await resolveProject(options, args, files, root);
3546
+ const prompter = options.prompter;
3547
+ const role = options.role ?? (prompter ? await askRole(prompter, singleRole(rolesInFiles(files).roles)) : currentRole(files));
3548
+ const previousRole2 = (() => {
3549
+ try {
3550
+ return currentRole(files);
3551
+ } catch {
3552
+ return void 0;
3553
+ }
3554
+ })();
3555
+ if (!role) {
3556
+ throw new Error(
3557
+ `No role given. Ask the user which role this repository's AI sessions should have (${HARNESS_ROLES.join(", ")}), then re-run with --role <role>.`
3558
+ );
3559
+ }
3560
+ const connected = connectAs(options, role);
3561
+ options = connected;
3562
+ await ensureSignedIn(connected, role, args.dryRun);
3563
+ const project = await resolveProject(connected, args, files, root, role);
3090
3564
  if ("choices" in project) {
3091
3565
  options.io.stdout.write(renderSuccess(
3092
3566
  {
@@ -3100,7 +3574,8 @@ async function executeInitCommand(options) {
3100
3574
  const documents = args.dryRun ? { versions: {}, fallbacks: {}, unavailableReason: "skipped in --dry-run" } : await fetchHarnessDocuments({
3101
3575
  endpoint: options.endpoint,
3102
3576
  accessToken: await options.accessToken?.(),
3103
- client: "ai-erd-cli",
3577
+ // 서버가 문서의 클라이언트별 변형 키를 이 이름으로 찾는다.
3578
+ client: "ainecto-cli",
3104
3579
  fetchImpl: options.fetchImpl
3105
3580
  });
3106
3581
  const plan = planInit({
@@ -3117,6 +3592,10 @@ async function executeInitCommand(options) {
3117
3592
  throw new Error(plan.refusal);
3118
3593
  }
3119
3594
  await applyPlan(root, plan, args.dryRun, files);
3595
+ if (!args.dryRun && previousRole2 !== void 0 && previousRole2 !== role) {
3596
+ options.io.stderr.write(`Role changes from ${previousRole2} to ${role}: \`ainecto\` commands now, MCP tools from a new session.
3597
+ `);
3598
+ }
3120
3599
  options.io.stdout.write(renderSuccess(
3121
3600
  {
3122
3601
  action: args.dryRun ? "init (dry-run)" : "init",
@@ -3130,36 +3609,34 @@ async function executeInitCommand(options) {
3130
3609
  // ⚠조용히 기본값으로 떨어지지 않는다 — 서버에서 규칙을 고쳐도 안 바뀌는 이유를
3131
3610
  // 사용자가 영영 못 찾게 된다. ★요청 자체의 실패와 «문서별» 되돌림을 둘 다 적는다
3132
3611
  // (2026-09-23 독립 재리뷰 I5: HTTP 200 으로 온 빈 문서가 조용히 패키지 판이 됐다).
3133
- notes: [...plan.notes, ...fallbackNotes(documents)],
3612
+ notes: [...rootNotes, ...plan.notes, ...fallbackNotes(documents)],
3613
+ // ★「Enforce this on the server too: ainecto auth login …」 줄은 뺐다. init 이 이미 그 역할로
3614
+ // 로그인했으므로 참이 아니고, 남아 있으면 에이전트가 따라 쳐서 사람이 한 번 더 승인하게 된다
3615
+ // (2026-09-29 독립 리뷰 P1-1).
3616
+ // ★재시작 없이 이어간다(2026-09-30 사용자 결정). 셸의 `ainecto` 는 부를 때마다 저장소 역할을 읽어
3617
+ // 역할 헤더로 싣는다 — 서버가 요청마다 강제하므로 MCP 연결과 무관하게 이 세션에서 바로 맞다.
3618
+ // 재시작은 «그 역할의 MCP 도구»를 더할 뿐인 선택이다.
3134
3619
  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"
3620
+ `Keep working in this session \u2014 no restart is needed. \`ainecto\` commands run from this repository use the ${role} role at once.`,
3621
+ `Read ${HARNESS_DOC_PATH} first, then \`ainecto tools list\` shows the role's tools and \`ainecto tools call <tool> '<json>'\` calls one.`,
3622
+ "Do not use MCP tools this session started with for this work \u2014 they keep the role (or no role) the session started with.",
3623
+ "Optional, to add the role's MCP tools: start a new session.",
3624
+ `Claude Code: exit and run \`claude -c\` in this folder, then approve the "${SERVER_NAME}" server from .mcp.json when asked.`,
3625
+ `Codex: save the profile above as $CODEX_HOME/${role}.config.toml and start with \`codex -p ${role}\`.`,
3626
+ 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.`,
3627
+ `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\`).`,
3628
+ "Undo everything with: ainecto init --undo"
3139
3629
  ]
3140
3630
  },
3141
3631
  { json: options.json }
3142
3632
  ));
3143
3633
  return 0;
3144
3634
  }
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
3635
  function currentRole(files) {
3158
- const found = [...detectRoles(files).values()].filter((role) => role !== void 0);
3159
- const unique = [...new Set(found)];
3636
+ const unique = rolesInFiles(files).roles;
3160
3637
  if (unique.length > 1) {
3161
3638
  throw new Error(
3162
- `Agent configs disagree about the session role (${unique.join(", ")}). Re-run with an explicit --role to line them up.`
3639
+ `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
3640
  );
3164
3641
  }
3165
3642
  return unique[0];
@@ -3174,13 +3651,108 @@ function fallbackNotes(documents) {
3174
3651
  }
3175
3652
  return notes;
3176
3653
  }
3177
- async function resolveProject(options, args, files, root) {
3654
+ async function ensureSignedIn(options, role, dryRun) {
3655
+ if (options.login === void 0) {
3656
+ return;
3657
+ }
3658
+ if (await options.accessToken?.()) {
3659
+ return;
3660
+ }
3661
+ if (dryRun) {
3662
+ throw new Error(
3663
+ `No usable AiNecto sign-in on this machine, and --dry-run neither refreshes it nor opens a browser.
3664
+
3665
+ ${loginCommand(options)}
3666
+
3667
+ Then run init again.`
3668
+ );
3669
+ }
3670
+ await options.login();
3671
+ options.io.stderr.write("Signed in.\n");
3672
+ }
3673
+ async function repositoryRootNotes(cwd) {
3674
+ const gitRoot = await findGitRoot(cwd);
3675
+ if (gitRoot === void 0) {
3676
+ return [`${cwd} is not inside a git repository, so this folder was used as the repository root.`];
3677
+ }
3678
+ if (gitRoot !== cwd) {
3679
+ throw new Error(
3680
+ `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:
3681
+
3682
+ cd ${gitRoot}
3683
+
3684
+ Nothing was changed.`
3685
+ );
3686
+ }
3687
+ return [];
3688
+ }
3689
+ function connectAs(options, role) {
3690
+ if (options.connect) {
3691
+ const connection = options.connect(role);
3692
+ return { ...options, ...connection };
3693
+ }
3694
+ if (!options.client) {
3695
+ throw new Error("init needs either a client or a way to connect once the role is known.");
3696
+ }
3697
+ return { ...options, client: options.client };
3698
+ }
3699
+ var ROLE_CHOICES = [
3700
+ { label: "Design requirements, ERD, boundaries, tasks", value: "design" },
3701
+ { label: "Development product code, following the approved design", value: "development" },
3702
+ { label: "Test test scenarios and test code", value: "test" },
3703
+ { label: "Validation judges the result; changes nothing", value: "validation" }
3704
+ ];
3705
+ async function askRole(prompter, current) {
3706
+ const index = current === void 0 ? void 0 : ROLE_CHOICES.findIndex((choice) => choice.value === current);
3707
+ return prompter.choose(
3708
+ current === void 0 ? "Which role should AI sessions in this repository have?" : `Which role should AI sessions in this repository have? (now: ${current})`,
3709
+ ROLE_CHOICES,
3710
+ index
3711
+ );
3712
+ }
3713
+ async function askProject(options, args, projects, bound, root, role, prompter) {
3714
+ const canCreate = role === "design" && !args.dryRun;
3715
+ const askNameAndCreate = async () => {
3716
+ const name = await prompter.text("New project name", args.projectName ?? basenameOf(root));
3717
+ return createProject(options, name);
3718
+ };
3719
+ if (projects.length === 0) {
3720
+ if (role !== "design") {
3721
+ throw new Error(noProjectForRole(options, role));
3722
+ }
3723
+ if (args.dryRun) {
3724
+ throw new Error(dryRunCannotCreate(args.projectName ?? basenameOf(root)));
3725
+ }
3726
+ return askNameAndCreate();
3727
+ }
3728
+ const choices = projects.map((project) => ({
3729
+ label: `${project.name} (${project.uuid})`,
3730
+ value: { project }
3731
+ }));
3732
+ if (canCreate) {
3733
+ choices.push({ label: "Create a new project", value: { create: true } });
3734
+ }
3735
+ const boundIndex = bound === void 0 ? -1 : projects.findIndex((project) => project.uuid === bound);
3736
+ const defaultIndex = boundIndex >= 0 ? boundIndex : projects.length === 1 ? 0 : void 0;
3737
+ const picked = await prompter.choose("Which AiNecto project should this repository use?", choices, defaultIndex);
3738
+ return "create" in picked ? askNameAndCreate() : picked.project;
3739
+ }
3740
+ function noProjectForRole(options, role) {
3741
+ 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}.`;
3742
+ }
3743
+ function dryRunCannotCreate(name) {
3744
+ 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>.`;
3745
+ }
3746
+ async function resolveProject(options, args, files, root, role) {
3178
3747
  const projects = await listProjects(options);
3179
3748
  if (args.projectUuid) {
3180
3749
  const found = projects.find((project) => project.uuid === args.projectUuid);
3181
3750
  return found ?? { uuid: args.projectUuid, name: args.projectName ?? args.projectUuid };
3182
3751
  }
3183
3752
  const bound = boundProjectUuid(files.get(HARNESS_CONFIG_PATH));
3753
+ if (options.prompter) {
3754
+ return askProject(options, args, projects, bound, root, role, options.prompter);
3755
+ }
3184
3756
  if (bound) {
3185
3757
  const found = projects.find((project) => project.uuid === bound);
3186
3758
  if (found) {
@@ -3193,16 +3765,17 @@ async function resolveProject(options, args, files, root) {
3193
3765
  if (projects.length > 1) {
3194
3766
  return { choices: projects };
3195
3767
  }
3768
+ if (role !== "design") {
3769
+ throw new Error(noProjectForRole(options, role));
3770
+ }
3196
3771
  const name = args.projectName ?? basenameOf(root);
3197
3772
  if (!args.yes) {
3198
3773
  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.`
3774
+ `No project found in your AiNecto account. Re-run with --yes to create "${name}", or --project-name <name> --yes to pick the name.`
3200
3775
  );
3201
3776
  }
3202
3777
  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
- );
3778
+ throw new Error(dryRunCannotCreate(name));
3206
3779
  }
3207
3780
  return createProject(options, name);
3208
3781
  }
@@ -3218,17 +3791,17 @@ async function callOrExplainSignIn(options, run) {
3218
3791
  throw error;
3219
3792
  }
3220
3793
  throw new Error(
3221
- `Not signed in for this role, so ${options.endpoint} refused the request (HTTP 401).
3794
+ `Not signed in, so ${options.endpoint} refused the request (HTTP 401).
3222
3795
 
3223
- ${loginCommand(options, options.role)}
3796
+ ${loginCommand(options)}
3224
3797
 
3225
- Then run init again. The role is carried by the access token, so each role signs in once.`
3798
+ Then run init again. Sign-in is one per machine; the role comes from this repository.`
3226
3799
  );
3227
3800
  }
3228
3801
  }
3229
- function loginCommand(options, role) {
3802
+ function loginCommand(options) {
3230
3803
  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}`;
3804
+ return `ainecto auth login${target}`;
3232
3805
  }
3233
3806
  function isUnauthorized(error) {
3234
3807
  return error instanceof McpRpcError && error.code === "MCP_HTTP_ERROR" && error.details?.status === 401;
@@ -3240,14 +3813,16 @@ async function createProject(options, name) {
3240
3813
  if (!first) {
3241
3814
  throw new Error("Project creation did not return a project uuid.");
3242
3815
  }
3816
+ options.io.stderr.write(`Created project ${first.name} (${first.uuid}).
3817
+ `);
3243
3818
  return first;
3244
3819
  }
3245
3820
  function unwrapToolJson(result) {
3246
- if (!isRecord8(result) || !Array.isArray(result.content)) {
3821
+ if (!isRecord9(result) || !Array.isArray(result.content)) {
3247
3822
  return result;
3248
3823
  }
3249
- const first = result.content.find((item) => isRecord8(item) && item.type === "text");
3250
- if (!isRecord8(first) || typeof first.text !== "string") {
3824
+ const first = result.content.find((item) => isRecord9(item) && item.type === "text");
3825
+ if (!isRecord9(first) || typeof first.text !== "string") {
3251
3826
  return result;
3252
3827
  }
3253
3828
  try {
@@ -3257,13 +3832,13 @@ function unwrapToolJson(result) {
3257
3832
  }
3258
3833
  }
3259
3834
  function extractProjects(value) {
3260
- const candidates = Array.isArray(value) ? value : isRecord8(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
3835
+ const candidates = Array.isArray(value) ? value : isRecord9(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
3261
3836
  const nested = value[key];
3262
3837
  return Array.isArray(nested) ? nested : [];
3263
3838
  }) : [];
3264
3839
  const projects = [];
3265
3840
  for (const candidate of candidates) {
3266
- if (isRecord8(candidate) && typeof candidate.uuid === "string") {
3841
+ if (isRecord9(candidate) && typeof candidate.uuid === "string") {
3267
3842
  projects.push({
3268
3843
  uuid: candidate.uuid,
3269
3844
  name: typeof candidate.name === "string" ? candidate.name : candidate.uuid
@@ -3290,47 +3865,6 @@ async function readSnapshot(root) {
3290
3865
  }
3291
3866
  return snapshot;
3292
3867
  }
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
3868
  async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3335
3869
  for (const write of plan.writes) {
3336
3870
  await assertInsideRepository(root, write.path);
@@ -3345,9 +3879,9 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3345
3879
  }
3346
3880
  const applied = [];
3347
3881
  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));
3882
+ const isRecord10 = (path) => !recordFirst && path === HARNESS_RECORD_PATH;
3883
+ const recordWrites = plan.writes.filter((write) => isRecord10(write.path));
3884
+ const recordDeletes = plan.deletes.filter((path) => isRecord10(path));
3351
3885
  const createdDirectories = [...plan.record?.createdDirectories ?? []];
3352
3886
  const rememberDirectories = (directories) => {
3353
3887
  for (const directory of directories) {
@@ -3383,14 +3917,14 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3383
3917
  }
3384
3918
  for (const path of deletes) {
3385
3919
  await assertUnchangedSinceSnapshot(root, path, snapshot);
3386
- await rm2(join2(root, path), { force: true });
3920
+ await rm2(join3(root, path), { force: true });
3387
3921
  applied.push(path);
3388
3922
  }
3389
3923
  };
3390
3924
  try {
3391
3925
  await apply(
3392
- orderedWrites(plan.writes.filter((write) => !isRecord9(write.path)), recordFirst),
3393
- plan.deletes.filter((path) => !isRecord9(path))
3926
+ orderedWrites(plan.writes.filter((write) => !isRecord10(write.path)), recordFirst),
3927
+ plan.deletes.filter((path) => !isRecord10(path))
3394
3928
  );
3395
3929
  await recordDirectoriesOrExplain(false);
3396
3930
  await apply(recordWrites, recordDeletes);
@@ -3406,7 +3940,7 @@ Already applied before this failure: ${applied.join(", ") || "(nothing)"}`
3406
3940
  );
3407
3941
  }
3408
3942
  for (const directory of plan.removableDirectories ?? []) {
3409
- await rmdir(join2(root, directory)).catch(() => void 0);
3943
+ await rmdir(join3(root, directory)).catch(() => void 0);
3410
3944
  }
3411
3945
  }
3412
3946
  function orderedWrites(writes, recordFirst) {
@@ -3418,7 +3952,7 @@ async function assertUnchangedSinceSnapshot(root, relativePath, snapshot) {
3418
3952
  if (!snapshot.has(relativePath)) {
3419
3953
  return;
3420
3954
  }
3421
- const now = await readIfExists(join2(root, relativePath));
3955
+ const now = await readIfExists(join3(root, relativePath));
3422
3956
  if (now !== snapshot.get(relativePath)) {
3423
3957
  throw new Error(
3424
3958
  `${relativePath} changed while init was running; nothing was written. Run init again.`
@@ -3426,8 +3960,8 @@ async function assertUnchangedSinceSnapshot(root, relativePath, snapshot) {
3426
3960
  }
3427
3961
  }
3428
3962
  async function writeManagedFile(root, write, onDirectoriesCreated) {
3429
- const absolutePath = join2(root, write.path);
3430
- const targetDirectory = dirname2(absolutePath);
3963
+ const absolutePath = join3(root, write.path);
3964
+ const targetDirectory = dirname3(absolutePath);
3431
3965
  const firstCreated = await mkdir2(targetDirectory, { recursive: true });
3432
3966
  const created = createdChain(root, firstCreated, targetDirectory);
3433
3967
  onDirectoriesCreated?.(created);
@@ -3444,14 +3978,14 @@ function createdChain(root, firstCreated, targetDirectory) {
3444
3978
  if (firstCreated === void 0) {
3445
3979
  return [];
3446
3980
  }
3447
- const stop = resolve(firstCreated);
3981
+ const stop = resolve2(firstCreated);
3448
3982
  const made = [];
3449
- for (let at = resolve(targetDirectory); ; at = dirname2(at)) {
3983
+ for (let at = resolve2(targetDirectory); ; at = dirname3(at)) {
3450
3984
  const asRelative = relative(root, at);
3451
3985
  if (asRelative && !asRelative.startsWith("..")) {
3452
- made.push(asRelative.split(sep).join("/"));
3986
+ made.push(asRelative.split(sep2).join("/"));
3453
3987
  }
3454
- if (at === stop || dirname2(at) === at) {
3988
+ if (at === stop || dirname3(at) === at) {
3455
3989
  break;
3456
3990
  }
3457
3991
  }
@@ -3461,7 +3995,7 @@ async function fileMode(absolutePath) {
3461
3995
  try {
3462
3996
  return (await stat3(absolutePath)).mode & 4095;
3463
3997
  } catch (error) {
3464
- if (isRecord8(error) && error.code === "ENOENT") {
3998
+ if (isRecord9(error) && error.code === "ENOENT") {
3465
3999
  return void 0;
3466
4000
  }
3467
4001
  throw error;
@@ -3499,43 +4033,53 @@ function requireValue3(flag, value) {
3499
4033
  }
3500
4034
  function basenameOf(path) {
3501
4035
  const parts = path.split(/[\\/]/).filter(Boolean);
3502
- return parts[parts.length - 1] ?? "ai-erd-project";
4036
+ return parts[parts.length - 1] ?? "ainecto-project";
3503
4037
  }
3504
- function isRecord8(value) {
4038
+ function isRecord9(value) {
3505
4039
  return typeof value === "object" && value !== null && !Array.isArray(value);
3506
4040
  }
3507
4041
 
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
4042
  // src/adapters/cli/ainectoCli.ts
3513
4043
  async function runAinectoCli(argv, io) {
3514
- let parsed = { json: argv.includes("--json"), help: false, positionals: [] };
4044
+ let parsed = { json: argv.includes("--json"), help: false, version: false, positionals: [] };
3515
4045
  try {
3516
4046
  parsed = parseGlobalArgs(argv);
4047
+ if (parsed.version || parsed.positionals[0] === "version") {
4048
+ io.stdout.write(renderSuccess({ version: CLI_VERSION }, { json: parsed.json }));
4049
+ return 0;
4050
+ }
3517
4051
  if (parsed.help || parsed.positionals.length === 0) {
3518
4052
  io.stdout.write(helpText());
3519
4053
  return 0;
3520
4054
  }
3521
4055
  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
- }
4056
+ const explicitRole = () => resolveRole({ role: parsed.role });
3526
4057
  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
- });
4058
+ const connect = (role) => {
4059
+ const auth = new OAuthClient({
4060
+ endpoint: resolved.endpoint,
4061
+ tokenStore,
4062
+ // ★init --dry-run 은 토큰을 갱신하지도 않는다 — 「아무것도 바꾸지 않는다」에 로그인 상태도 든다.
4063
+ // (init 의 플래그는 positional 로 흘러온다.)
4064
+ readOnly: parsed.positionals[0] === "init" && parsed.positionals.includes("--dry-run"),
4065
+ // ★URL 을 «브라우저를 열기 전에» 알린다. stdout 은 결과 전용이라 stderr 에 쓴다.
4066
+ // --json 이면 JSON 한 줄이다(설계 §21-3 (가)). 로그인이 역할과 무관해져 role 칸은 없다(0.4.2 §2-5).
4067
+ onAuthorizeUrl: (url) => {
4068
+ io.stderr.write(parsed.json ? `${JSON.stringify({ event: "login_url", url })}
4069
+ ` : `Opening your browser to sign in to AiNecto. If it does not open, visit:
4070
+ ${url}
4071
+ `);
4072
+ }
4073
+ });
4074
+ const client2 = new McpRpcClient({ endpoint: resolved.endpoint, tokenProvider: auth, role });
4075
+ return { auth, client: client2 };
4076
+ };
3533
4077
  const [domain, action, ...rest] = parsed.positionals;
3534
4078
  if (domain === "mcp") {
3535
4079
  await runConnector({
3536
4080
  endpoint: resolved.endpoint,
3537
- // `ai-erd mcp` 로 붙는 경로도 같은 역할을 쓴다 — bin/mcp.ts 와 같은 규칙.
3538
- role: sessionRole,
4081
+ // 브리지의 역할은 인자·환경변수에서만 온다 — bin/mcp.ts 와 같은 규칙. 저장소를 적용하지 않는다.
4082
+ role: explicitRole().role ?? null,
3539
4083
  input: io.stdin,
3540
4084
  output: io.stdout,
3541
4085
  errorOutput: io.stderr
@@ -3545,22 +4089,69 @@ async function runAinectoCli(argv, io) {
3545
4089
  if (domain === "init") {
3546
4090
  return await executeInitCommand({
3547
4091
  argv: parsed.positionals.slice(1),
3548
- role: sessionRole,
3549
- // 문서는 인증 후에 받는다 — 토큰이 없으면 패키지 기본값으로 가고, 그 사실을 보고한다.
3550
- // ★«필요할 때» 가져온다. --undo 는 로컬 작업이라 토큰을 건드릴 이유가 없다(S1).
3551
- accessToken: () => auth.getAccessToken().catch(() => void 0),
3552
- client,
4092
+ role: explicitRole().role,
4093
+ connect: (role) => {
4094
+ const { auth, client: client2 } = connect(role);
4095
+ return {
4096
+ client: client2,
4097
+ // 문서는 인증 후에 받는다 — 토큰이 없으면 패키지 기본값으로 가고, 그 사실을 보고한다.
4098
+ // ★«필요할 때» 가져온다. --undo 는 로컬 작업이라 토큰을 건드릴 이유가 없다(S1) — undo 는
4099
+ // connect 자체를 부르지 않는다.
4100
+ // ★갱신 실패는 «토큰 없음»(→ 로그인)이지만, AINECTO_TOKEN 을 못 쓰는 주소라는 거절은 삼키지 않는다 —
4101
+ // 삼키면 그 설정 오류가 브라우저 로그인으로 둔갑한다.
4102
+ accessToken: async () => {
4103
+ try {
4104
+ return await auth.getAccessToken();
4105
+ } catch (error) {
4106
+ if (process.env.AINECTO_TOKEN) {
4107
+ throw error;
4108
+ }
4109
+ return void 0;
4110
+ }
4111
+ },
4112
+ // 로그인한다 — `ainecto auth login` 과 같은 함수다. 서버마다 한 번이다(0.4.2).
4113
+ login: async () => {
4114
+ await auth.login();
4115
+ }
4116
+ };
4117
+ },
4118
+ // ★대화형일 때만 묻는다(판정은 isInteractive 한 곳). 에이전트·CI 는 TTY 가 아니다.
4119
+ prompter: isInteractive(io, parsed.json) ? createReadlinePrompter(io) : void 0,
3553
4120
  endpoint: resolved.endpoint,
3554
4121
  env: resolved.env,
3555
- cliVersion: `@ai-erd/mcp ${CLI_VERSION}`,
4122
+ cliVersion: `${PACKAGE_NAME} ${CLI_VERSION}`,
3556
4123
  cwd: process.cwd(),
3557
4124
  json: parsed.json,
3558
4125
  io
3559
4126
  });
3560
4127
  }
3561
4128
  if (domain === "auth") {
3562
- return await handleAuth(action, auth, resolved.endpoint, parsed.json, io);
4129
+ if (parsed.role !== void 0) {
4130
+ io.stderr.write("Sign-in is one per machine; --role is not used by auth.\n");
4131
+ }
4132
+ return await handleAuth(action, connect(null).auth, resolved.endpoint, parsed.json, io);
4133
+ }
4134
+ const env = parsed.env ?? "prod";
4135
+ if (domain === "tools" && action === "catalog") {
4136
+ return await printCatalog(parsed, io, resolved.endpoint, async () => {
4137
+ const explicit2 = explicitRole();
4138
+ return connect(await commandRole(explicit2, process.cwd())).client;
4139
+ });
4140
+ }
4141
+ let routeTools = getGeneratedTools(env);
4142
+ let remote = isAttachmentsUploadCommand(domain, action) || domain === "tools" && (action === "list" || action === "call") || matchGeneratedCommand(routeTools, parsed.positionals) !== void 0;
4143
+ if (!remote) {
4144
+ const live = await loadLiveTools(env, connect(null).client).catch(() => void 0);
4145
+ if (live && matchGeneratedCommand(live, parsed.positionals)) {
4146
+ routeTools = live;
4147
+ remote = true;
4148
+ }
4149
+ }
4150
+ if (!remote) {
4151
+ throw new Error(domain === "tools" ? "Expected tools list, tools call <mcpName>, or tools catalog." : `Unknown command "${domain}".`);
3563
4152
  }
4153
+ const explicit = explicitRole();
4154
+ const { client } = connect(await commandRole(explicit, process.cwd()));
3564
4155
  if (isAttachmentsUploadCommand(domain, action)) {
3565
4156
  return await executeAttachmentsUploadCommand({
3566
4157
  argv: rest,
@@ -3570,7 +4161,7 @@ async function runAinectoCli(argv, io) {
3570
4161
  });
3571
4162
  }
3572
4163
  const generatedResult = await executeGeneratedCommand({
3573
- env: parsed.env ?? "prod",
4164
+ tools: routeTools,
3574
4165
  argv: parsed.positionals,
3575
4166
  client,
3576
4167
  json: parsed.json,
@@ -3588,15 +4179,32 @@ async function runAinectoCli(argv, io) {
3588
4179
  return 1;
3589
4180
  }
3590
4181
  }
4182
+ async function commandRole(explicit, cwd) {
4183
+ const explicitRole = explicit.role ?? null;
4184
+ const found = await findRepositoryRole(cwd);
4185
+ if (found.kind === "conflict") {
4186
+ throw new Error(await askTheUserToFixRole(found.dir, found.reason));
4187
+ }
4188
+ if (found.kind === "none") {
4189
+ return explicitRole;
4190
+ }
4191
+ if (explicitRole !== null && explicitRole !== found.role) {
4192
+ const given = explicit.source === "env" ? `${ROLE_ENV_VAR}=${explicitRole}` : `--role ${explicitRole}`;
4193
+ throw new Error(
4194
+ `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.`
4195
+ );
4196
+ }
4197
+ return found.role;
4198
+ }
3591
4199
  async function handleAuth(action, auth, endpoint, json, io) {
3592
4200
  if (action === "login") {
3593
4201
  await auth.login();
3594
- io.stdout.write(renderSuccess({ endpoint, authenticated: true }, { json }));
4202
+ io.stdout.write(renderSuccess({ ...await nextStep(), endpoint, authenticated: true }, { json }));
3595
4203
  return 0;
3596
4204
  }
3597
4205
  if (action === "status") {
3598
4206
  const token = await auth.getAccessToken();
3599
- io.stdout.write(renderSuccess({ endpoint, authenticated: Boolean(token) }, { json }));
4207
+ io.stdout.write(renderSuccess({ ...await nextStep(), endpoint, authenticated: Boolean(token) }, { json }));
3600
4208
  return 0;
3601
4209
  }
3602
4210
  if (action === "logout") {
@@ -3606,6 +4214,20 @@ async function handleAuth(action, auth, endpoint, json, io) {
3606
4214
  }
3607
4215
  throw new Error("Expected auth login, auth status, or auth logout.");
3608
4216
  }
4217
+ async function printCatalog(parsed, io, endpoint, liveClient) {
4218
+ const catalogEnv = parsed.env ?? "prod";
4219
+ try {
4220
+ const tools = await loadLiveTools(catalogEnv, await liveClient());
4221
+ io.stdout.write(renderSuccess(tools, { json: parsed.json }));
4222
+ } catch (error) {
4223
+ const reason = error instanceof Error ? error.message : String(error);
4224
+ io.stdout.write(renderSuccess(getGeneratedTools(catalogEnv), {
4225
+ json: parsed.json,
4226
+ 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.`]
4227
+ }));
4228
+ }
4229
+ return 0;
4230
+ }
3609
4231
  async function handleTools(action, rest, parsed, client, io) {
3610
4232
  if (action === "list") {
3611
4233
  const tools = await client.toolsList();
@@ -3628,18 +4250,10 @@ async function handleTools(action, rest, parsed, client, io) {
3628
4250
  io.stdout.write(renderSuccess(result, { json: parsed.json, warnings: payload.warnings }));
3629
4251
  return isToolError(result) ? 1 : 0;
3630
4252
  }
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
4253
  throw new Error("Expected tools list, tools call <mcpName>, or tools catalog.");
3640
4254
  }
3641
4255
  function parseGlobalArgs(argv) {
3642
- const result = { json: false, help: false, positionals: [] };
4256
+ const result = { json: false, help: false, version: false, positionals: [] };
3643
4257
  for (let index = 0; index < argv.length; index += 1) {
3644
4258
  const arg = argv[index];
3645
4259
  if (!arg) {
@@ -3655,6 +4269,8 @@ function parseGlobalArgs(argv) {
3655
4269
  result.json = true;
3656
4270
  } else if (arg === "--help" || arg === "-h") {
3657
4271
  result.help = true;
4272
+ } else if (arg === "--version" || arg === "-v" || arg === "-V") {
4273
+ result.version = true;
3658
4274
  } else {
3659
4275
  result.positionals.push(arg);
3660
4276
  }
@@ -3686,13 +4302,25 @@ function requireValue4(flag, value) {
3686
4302
  }
3687
4303
  return value;
3688
4304
  }
4305
+ async function nextStep() {
4306
+ const found = await findRepositoryRole(process.cwd());
4307
+ return found.kind === "none" ? { setup: "incomplete", next: NEXT_STEP_INIT } : {};
4308
+ }
4309
+ var NEXT_STEP_INIT = [
4310
+ "AiNecto setup is not finished until `npx -y ainecto@latest init` runs in the repository.",
4311
+ "AI agents: tell the user this now, even if they only asked to install, update, or sign in. Then ask",
4312
+ "which role this repository's AI sessions should have \u2014 Design, Development, Test, or Validation \u2014",
4313
+ "never pick one yourself, and run `npx -y ainecto@latest init --role <role>` from the repository root.",
4314
+ "In a terminal: `npx -y ainecto@latest init` asks for the role and the project."
4315
+ ].join("\n");
3689
4316
  function helpText() {
3690
4317
  return [
3691
- "Ainecto CLI",
4318
+ "AiNecto CLI",
4319
+ "",
4320
+ NEXT_STEP_INIT,
3692
4321
  "",
3693
4322
  "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]",
4323
+ " ainecto auth login|status|logout [--env prod|dev] [--json] (one sign-in per machine)",
3696
4324
  " ainecto tools list [--env prod|dev] [--endpoint URL] [--json]",
3697
4325
  " ainecto tools call <mcpName> [-f payload.json|@-] [inline-json] [--json]",
3698
4326
  " ainecto attachments upload --document-uuid <uuid> <file...> [--json]",
@@ -3700,8 +4328,9 @@ function helpText() {
3700
4328
  " ainecto mcp [--env prod|dev] [--endpoint URL] [--role design|development|test|validation]",
3701
4329
  "",
3702
4330
  "Harness:",
3703
- " ai-erd init --role <design|development|test|validation> [--project <uuid>] [--dry-run]",
3704
- " ai-erd init --undo",
4331
+ " ainecto init [--role <design|development|test|validation>] [--project <uuid>] [--dry-run]",
4332
+ " In a terminal, asks for the role and project when they are not given.",
4333
+ " ainecto init --undo",
3705
4334
  ""
3706
4335
  ].join("\n");
3707
4336
  }