ainecto 0.6.0 → 0.8.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,34 +5,6 @@ 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
36
8
  var FileTokenStore = class {
37
9
  filePath;
38
10
  constructor(rootDir = join(homedir(), ".ainecto")) {
@@ -43,7 +15,6 @@ var FileTokenStore = class {
43
15
  return file[tokenKey(endpoint)];
44
16
  }
45
17
  async save(endpoint, token) {
46
- assertNoRoleScope(token);
47
18
  const file = await this.readFile();
48
19
  file[tokenKey(endpoint)] = { ...token, endpoint, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
49
20
  await this.writeFile(file);
@@ -88,13 +59,6 @@ var FileTokenStore = class {
88
59
  function tokenKey(endpoint) {
89
60
  return createHash("sha256").update(endpoint).digest("hex");
90
61
  }
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
- }
97
- }
98
62
  function isNodeError(error, code) {
99
63
  return error instanceof Error && "code" in error && error.code === code;
100
64
  }
@@ -176,6 +140,9 @@ function assertEnv(value) {
176
140
  }
177
141
  throw new Error(`Invalid --env value "${value}". Expected "prod" or "dev".`);
178
142
  }
143
+ function profileSettingsUrl(endpoint) {
144
+ return `${new URL(endpoint).origin}/home/settings`;
145
+ }
179
146
 
180
147
  // src/core/auth/clientRegistration.ts
181
148
  async function registerPublicClient(registrationEndpoint, redirectUri, fetchImpl = fetch) {
@@ -614,12 +581,28 @@ async function readAllStdin(stdin) {
614
581
  return text;
615
582
  }
616
583
 
584
+ // src/core/harness/legacyRole.ts
585
+ var LEGACY_ROLE_HEADER = "X-AiNecto-Role";
586
+ var LEGACY_ROLE_ENV_VAR = "AINECTO_ROLE";
587
+ var LEGACY_ROLE_NOTICE = "AiNecto session roles were removed; access follows your AiNecto permissions. Re-run `ainecto init` to tidy this repository.";
588
+ function legacyRoleInput(flag, envVars = process.env) {
589
+ const value = flag ?? envVars[LEGACY_ROLE_ENV_VAR];
590
+ return value !== void 0 && value.trim() ? value.trim() : void 0;
591
+ }
592
+
617
593
  // src/core/version.ts
618
- var CLI_VERSION = true ? "0.6.0" : "dev";
594
+ var UNBUNDLED_VERSION = "dev";
595
+ var CLI_VERSION = true ? "0.8.0" : UNBUNDLED_VERSION;
619
596
  var PACKAGE_NAME = "@ainecto/mcp";
620
597
  var CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;
598
+ function bridgePackageSpec(version = CLI_VERSION) {
599
+ return version === UNBUNDLED_VERSION ? PACKAGE_NAME : `${PACKAGE_NAME}@${version}`;
600
+ }
621
601
 
622
602
  // src/core/mcp/rpcClient.ts
603
+ var CHANNEL_HEADER = "X-AiNecto-Channel";
604
+ var AGENT_HEADER = "X-AiNecto-Agent";
605
+ var MAX_AGENT_NAME_LENGTH = 100;
623
606
  var McpRpcError = class extends Error {
624
607
  constructor(message, code, details) {
625
608
  super(message);
@@ -637,6 +620,14 @@ var McpRpcClient = class {
637
620
  options;
638
621
  fetchImpl;
639
622
  nextId = 1;
623
+ agent;
624
+ /**
625
+ * 브리지 전용 — 호스트 에이전트가 보낸 {@code initialize} 의 {@code clientInfo.name}(프로그램 고정 이름).
626
+ * 이후 모든 요청의 {@code X-AiNecto-Agent} 헤더로 싣는다. 직접 명령은 부르지 않는다(누가 셸을 돌렸는지 모른다).
627
+ */
628
+ useAgent(clientName) {
629
+ this.agent = agentHeaderValue(clientName);
630
+ }
640
631
  initialize() {
641
632
  return this.request("initialize", {
642
633
  protocolVersion: "2024-11-05",
@@ -706,8 +697,13 @@ var McpRpcClient = class {
706
697
  if (token) {
707
698
  headers.authorization = `Bearer ${token}`;
708
699
  }
709
- if (this.options.role) {
710
- headers[ROLE_HEADER.toLowerCase()] = this.options.role;
700
+ const legacyRole = printableHeaderValue(this.options.legacyRole);
701
+ if (legacyRole) {
702
+ headers[LEGACY_ROLE_HEADER.toLowerCase()] = legacyRole;
703
+ }
704
+ headers[CHANNEL_HEADER.toLowerCase()] = this.options.channel;
705
+ if (this.agent) {
706
+ headers[AGENT_HEADER.toLowerCase()] = this.agent;
711
707
  }
712
708
  return this.fetchImpl(this.options.endpoint, {
713
709
  method: "POST",
@@ -719,6 +715,16 @@ var McpRpcClient = class {
719
715
  function isToolError(result) {
720
716
  return isRecord2(result) && result.isError === true;
721
717
  }
718
+ function agentHeaderValue(clientName) {
719
+ return printableHeaderValue(clientName);
720
+ }
721
+ function printableHeaderValue(value) {
722
+ if (typeof value !== "string") {
723
+ return void 0;
724
+ }
725
+ const printable = value.replace(/[^\x20-\x7E]/g, "").trim().slice(0, MAX_AGENT_NAME_LENGTH).trim();
726
+ return printable || void 0;
727
+ }
722
728
  function isJsonRpcResponse(value) {
723
729
  return isRecord2(value) && value.jsonrpc === "2.0" && ("result" in value || "error" in value);
724
730
  }
@@ -816,7 +822,7 @@ function redactSecretString(value) {
816
822
 
817
823
  // src/core/catalog/generated.prod.ts
818
824
  var generatedTools = [
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" },
825
+ { "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. The thread's original author and earlier repliers get a notification email (except people mentioned in this reply, who get the mention email instead).", "minimum": 1, "type": "integer" } }, "required": ["body"], "type": "object" }, "minItems": 1, "type": "array" }, "mailLanguage": { "description": "Language of the comment notification email (mention or reply). Defaults to en.", "enum": ["en", "ko"], "type": "string" } }, "required": ["documentUuid", "items"], "type": "object" }, "required": ["documentUuid", "items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "b3ad621ef51c3003" },
820
826
  { "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
827
  { "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
828
  { "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" },
@@ -851,15 +857,19 @@ var generatedTools = [
851
857
  { "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
858
  { "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
859
  { "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" },
860
+ { "mcpName": "get_my_work_summary", "commandPath": ["my-work-summary", "get"], "group": "generic", "description": `One-call summary of what is waiting for the signed-in user in AiNecto \u2014 call it when the user asks things like "do I have anything to do?", "anything new?", "anything to review?" or "did anyone ask me something?". Returns notifications {unread (not yet read by the person, newest 100 counted; unreadCapped when there are more), unreadByKind, newForThisConnection (not yet acknowledged by this connection \u2014 the same number as the line at the end of tool results)}, mentionsToRead {items the person has not read that mention them, each with target title and url}, and todos {open, leaseExpired, inProgress, items: questions and requests waiting for the user's agent}. For details use list_notifications and list_agent_todos; to read the document and its comments use get_document with the target uuid. Read-only: nothing is marked read or acknowledged.`, "inputSchema": { "additionalProperties": false, "properties": { "projectUuid": { "description": "Older single-value form of projectUuids.", "type": "string" }, "projectUuids": { "description": "Only items in any of these projects. Use it when the user names a project or the project you are working in is clear. With workspaceUuids too, both must match. A workspace or project you cannot see matches nothing.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "workspaceUuids": { "description": "Only items in any of these workspaces.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "bc63904d51058f42" },
854
861
  { "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
862
  { "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
863
  { "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
864
  { "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" },
865
+ { "mcpName": "list_agent_todos", "commandPath": ["agent-todos", "list"], "group": "generic", "description": "List the to-dos that ask the signed-in user's agent to act now (for example a question that mentions the user). This is the current state, not a feed: a to-do you have not finished comes back on every call until it is completed, dismissed, or closed because the person did it themselves. Each item has todoId, kind, obligation, openedAt, summary, target (title and url), action and state. To work on one, claim it with update_agent_todos and complete it with a one-line note. Actions that affect other people (assigning, approving, inviting) need the user's confirmation first.", "inputSchema": { "additionalProperties": false, "properties": { "after": { "description": "todoId of the last item of the previous page.", "type": "string" }, "kinds": { "description": "Only these kinds.", "items": { "enum": ["COMMENT_MENTIONED", "COMMENT_REPLIED", "QUESTION_ANSWERED", "QUESTION_RESOLVED", "QUESTION_MENTIONED"], "type": "string" }, "type": "array", "uniqueItems": true }, "limit": { "description": "Items per call, default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Older single-value form of projectUuids.", "type": "string" }, "projectUuids": { "description": "Only to-dos in any of these projects. Use it when the user names a project or the project you are working in is clear. With workspaceUuids too, both must match. A workspace or project you cannot see matches nothing.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "state": { "description": "OPEN (default): to-dos you can take, including ones whose lease expired. MINE: to-dos currently leased (claimed) and not expired.", "items": { "enum": ["OPEN", "MINE"], "type": "string" }, "type": "array", "uniqueItems": true }, "workspaceUuids": { "description": "Only to-dos in any of these workspaces.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "9c228a3044a546cf" },
858
866
  { "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
867
  { "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
868
  { "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
869
  { "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" },
870
+ { "mcpName": "list_history", "commandPath": ["history", "list"], "group": "generic", "description": "List recent activity (who did what, people and agents) in a workspace, a project, or on one resource, newest first. Give at least one of workspaceUuid, projectUuid, or resourceType with resourceUuid; you need editor access or higher in that scope. This is a list to refresh, not a gap-free feed: to see what is new, read the first page again and skip ids you have already seen; to get each new notification exactly once use list_notifications. Each item has id, kind, at, lastAt, coalesceCount, actor, target (title and url only when you can read it now), changes and summary.", "inputSchema": { "additionalProperties": false, "properties": { "actorType": { "description": "Only people (USER) or only agents (AGENT).", "enum": ["USER", "AGENT"], "type": "string" }, "before": { "description": "nextCursor of the previous page (older items).", "type": "string" }, "kinds": { "description": "Only these kinds.", "items": { "enum": ["COMMENT_MENTIONED", "COMMENT_REPLIED", "QUESTION_ANSWERED", "QUESTION_RESOLVED", "QUESTION_MENTIONED"], "type": "string" }, "type": "array", "uniqueItems": true }, "limit": { "description": "Items per call, default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Activity in this project.", "type": "string" }, "resourceType": { "description": "With resourceUuid: activity on one resource.", "enum": ["DOCUMENT", "PROJECT"], "type": "string" }, "resourceUuid": { "description": "UUID of the resource named by resourceType.", "type": "string" }, "workspaceUuid": { "description": "Activity in this workspace.", "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "235832683071654b" },
862
871
  { "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" },
872
+ { "mcpName": "list_notifications", "commandPath": ["notifications", "list"], "group": "generic", "description": "Read new notifications for the signed-in user, oldest first and without gaps \u2014 for an agent that checks the inbox periodically. The server remembers, per connection and per filter (kinds, workspaceUuids, projectUuids, directOnly), how far you have acknowledged; a new filter starts from now. Each response carries ackToken: pass it as ack in your next call (with the same filter) after you have handled the batch \u2014 if you do not, the next call returns the same batch again (identify duplicates by id and seq; an item that comes back with a higher seq and a larger groupCount is an update of the same entry). Each item has id, seq, kind, at, summary (one line), actor, target (title and url only when you can read it now), action (what the user is asked to do) and humanRead. An item you can no longer read comes back as {id, seq, at, redacted: true}. This tool never marks anything read for the person; open to-dos are in list_agent_todos. Calls closer than 30 seconds apart (except a call that carries ack) return retryAfterSeconds instead of items.", "inputSchema": { "additionalProperties": false, "properties": { "ack": { "description": "The ackToken of the previous response. Send it with the same filter once you have handled (or safely stored) that batch; only then does the saved position move past it. Without it, the same batch is returned again.", "type": "string" }, "cursor": { "description": 'Where to read from. "saved" (default): after what this connection last acknowledged for this same filter. "latest": from now on (moves the saved position to now). "earliest": everything still kept (90 days). Or a nextCursor from a previous response: read after it without using or changing the saved position.', "type": "string" }, "directOnly": { "description": "Only notifications that point at you directly (mentions, questions to you).", "type": "boolean" }, "kinds": { "description": "Only these notification kinds.", "items": { "enum": ["COMMENT_MENTIONED", "COMMENT_REPLIED", "QUESTION_ANSWERED", "QUESTION_RESOLVED", "QUESTION_MENTIONED"], "type": "string" }, "type": "array", "uniqueItems": true }, "limit": { "description": "Items per call, default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Older single-value form of projectUuids.", "type": "string" }, "projectUuids": { "description": "Only notifications in any of these projects. Use it when the user names a project or the project you are working in is clear. With workspaceUuids too, both must match. A workspace or project you cannot see matches nothing.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "workspaceUuids": { "description": "Only notifications in any of these workspaces.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "0c2bb3fdc8291b03" },
863
873
  { "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
874
  { "mcpName": "list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "329250aecc06a298" },
865
875
  { "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" },
@@ -877,7 +887,8 @@ var generatedTools = [
877
887
  { "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
888
  { "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
889
  { "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" },
890
+ { "mcpName": "update_agent_todos", "commandPath": ["agent-todos", "update"], "group": "generic", "description": "Claim, renew, complete, release, or dismiss the signed-in user's agent to-dos. Only the session holding the current leaseToken may renew, release, or complete a claimed to-do \u2014 even another session of the same connection gets CLAIMED_BY_OTHER. Results are per item: {todoId, ok: true, state, leaseToken?, claimExpiresAt?} or {todoId, ok: false, error} with error one of CLAIMED_BY_OTHER, LEASE_LOST, ALREADY_DONE, CLOSED_BY_HUMAN, SUPERSEDED, ACCESS_LOST, NOT_FOUND, INVALID \u2014 on CLOSED_BY_HUMAN or SUPERSEDED stop working on it. Retrying the same complete or dismiss returns ok again. There is no tool to mark the person's notifications read.", "inputSchema": { "additionalProperties": false, "properties": { "items": { "description": "To-dos to update; each item is applied and reported on its own.", "items": { "additionalProperties": false, "properties": { "action": { "description": "claim: take it (returns leaseToken, valid 30 minutes). renew: extend your lease. complete: done (with leaseToken if you claimed it; without one only while it is still OPEN). release: give it back. dismiss: not a to-do.", "enum": ["claim", "renew", "complete", "release", "dismiss"], "type": "string" }, "leaseToken": { "description": "Required for renew and release, and for complete or dismiss of a to-do you claimed.", "type": "string" }, "note": { "description": "For complete or dismiss: one line on what you did (at most 500 characters, no document bodies). The person sees it in the inbox.", "maxLength": 500, "type": "string" }, "todoId": { "description": "todoId from list_agent_todos.", "type": "string" } }, "required": ["todoId", "action"], "type": "object" }, "maxItems": 50, "minItems": 1, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "da38f4f54f68dbce" },
891
+ { "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 comment notification email (mention or reply). Defaults to en.", "enum": ["en", "ko"], "type": "string" } }, "required": ["documentUuid", "items"], "type": "object" }, "required": ["documentUuid", "items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "prod", "schemaHash": "3c13611a49c95979" },
881
892
  { "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
893
  { "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
894
  { "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" },
@@ -887,92 +898,81 @@ var generatedTools = [
887
898
 
888
899
  // src/core/catalog/generated.dev.ts
889
900
  var generatedTools2 = [
890
- { "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, diagram, markdown, testcase, task.", "enum": ["erd", "diagram", "markdown", "testcase", "task"], "type": "string" }, "typePayload": { "description": "Type-specific payload for enabled document types. markdown: {body}; diagram: {diagramSource,sourceLanguage,edgeStyle,isModule,metadata}; testcase: {defaultEnvironment (max 50 chars), defaultTestAccount (max 100 chars)}; task: no typePayload; board and default statuses are seeded automatically.", "type": "object" } }, "required": ["projectUuid", "type", "title"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "6abbab093274fceb" },
891
- { "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": "dev", "schemaHash": "d423d3c265631664" },
892
- { "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": "dev", "schemaHash": "396c80c6cc2ba41a" },
893
- { "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": "dev", "schemaHash": "d663d461f9c9da65" },
894
- { "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": "dev", "schemaHash": "6e55e6511d732199" },
895
- { "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": "dev", "schemaHash": "5fbc7c142bb4a49d" },
896
- { "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": "dev", "schemaHash": "49fa7c0f823871b2" },
897
- { "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": "dev", "schemaHash": "e1a86ddaa9638298" },
898
- { "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": "dev", "schemaHash": "2aca644f7a105b06" },
899
- { "mcpName": "mcp__ainecto__diagram_apply_changes", "commandPath": ["tools", "run", "diagram-apply-changes"], "group": "generic", "description": "Required single-call mutation path for diagram node/edge/group create/update/delete changes. Put nodes, edges, and groups in one request and call this once. Do not look for type-specific diagram CUD tools; this tool is the only diagram CUD entry point.", "inputSchema": { "properties": { "description": { "description": "Reason for batch (audit)", "type": "string" }, "diagramUuid": { "description": "Diagram UUID", "type": "string" }, "edges": { "description": "Edge operations", "items": { "properties": { "action": { "description": "Operation kind", "enum": ["create", "update", "delete"], "type": "string" }, "bridgeType": { "description": "Bridge type", "type": "string" }, "fromUuid": { "description": "Source", "type": "string" }, "label": { "description": "Edge label", "type": "string" }, "lineStyle": { "description": "Line style", "type": "string" }, "strokeColor": { "description": "Stroke color", "type": "string" }, "strokeWidth": { "description": "Stroke width", "type": "integer" }, "toUuid": { "description": "Target", "type": "string" }, "uuid": { "description": "Required for update/delete", "type": "string" } }, "required": ["action"], "type": "object" }, "type": "array" }, "groups": { "description": "Group operations", "items": { "properties": { "action": { "description": "Operation kind", "enum": ["create", "update", "delete"], "type": "string" }, "collapsed": { "description": "Collapsed state", "type": "boolean" }, "color": { "description": "Group color", "type": "string" }, "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" } }, "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": "dev", "schemaHash": "1c87428fea4bb77a" },
900
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "b147277e74d3b0f7" },
901
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "4516fc00303ab8db" },
902
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "20f370ff4438c4b0" },
903
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "0ec4dd6d61584a98" },
904
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "fee200dd1433daf9" },
905
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "852a19cda307f863" },
906
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "a8c41752151ade3e" },
907
- { "mcpName": "mcp__ainecto__diagram_submit_llm_result", "commandPath": ["tools", "run", "diagram-submit-llm-result"], "group": "generic", "description": "", "inputSchema": { "properties": { "creditsCharged": { "description": "Deprecated; ignored. Credits are calculated server-side from tokens.", "type": "integer" }, "inputTokens": { "description": "Input token count", "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": "dev", "schemaHash": "374654baa87c1bb8" },
908
- { "mcpName": "mcp__ainecto__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": "dev", "schemaHash": "25537b63698bc33d" },
909
- { "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": "dev", "schemaHash": "a4b4358eff0faa97" },
910
- { "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": "dev", "schemaHash": "ebace643de07e826" },
911
- { "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": "dev", "schemaHash": "dd64d1f996060ab1" },
912
- { "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": "dev", "schemaHash": "d2e66548e3d60d30" },
913
- { "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": "dev", "schemaHash": "1e477b3fadb50c6b" },
914
- { "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": "dev", "schemaHash": "40bf1eb02e7a9730" },
915
- { "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": "dev", "schemaHash": "8edd40794f39f47a" },
916
- { "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": "dev", "schemaHash": "bcd417344639c0c9" },
917
- { "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": "dev", "schemaHash": "e0c2207905dbb068" },
918
- { "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": "dev", "schemaHash": "9ae2e04c310b57f2" },
919
- { "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": "dev", "schemaHash": "dc8811071df04ac2" },
920
- { "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": "dev", "schemaHash": "ca05b35b174fc209" },
921
- { "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": "dev", "schemaHash": "12bee70f6d254a5d" },
922
- { "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": "dev", "schemaHash": "4949c8a5bb425491" },
923
- { "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": "dev", "schemaHash": "b4ee9097f4b80df4" },
924
- { "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": "dev", "schemaHash": "ad81d04200d3d7f5" },
925
- { "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": "dev", "schemaHash": "ef7bae555463116e" },
926
- { "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": "dev", "schemaHash": "a75657bb2e5eb383" },
927
- { "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, diagram, markdown, testcase, task.", "enum": ["erd", "diagram", "markdown", "testcase", "task"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "e5d92431322ac0e3" },
928
- { "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": "dev", "schemaHash": "f3cde57fe87d375c" },
929
- { "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": "dev", "schemaHash": "9174d6965d7e53f5" },
930
- { "mcpName": "mcp__ainecto__list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "82f8586d68bfcd7d" },
931
- { "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": "dev", "schemaHash": "370e4b8b8ec18e18" },
932
- { "mcpName": "mcp__ainecto__list_workspaces", "commandPath": ["workspaces", "list"], "group": "workspace", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "7c32c74000696b2f" },
933
- { "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": "dev", "schemaHash": "f189514b3cccd1cc" },
934
- { "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": "dev", "schemaHash": "72114a2d2642ed92" },
935
- { "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": "dev", "schemaHash": "9758d6a262913a3f" },
936
- { "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": "dev", "schemaHash": "612a779b1fd176bb" },
937
- { "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, diagram, markdown, testcase, task. 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": "dev", "schemaHash": "cc5db0cc4a9d9603" },
938
- { "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": "dev", "schemaHash": "c4f448cc3b96c7a6" },
939
- { "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, diagram, markdown, testcase, task.", "enum": ["erd", "diagram", "markdown", "testcase", "task"], "type": "string" } }, "required": ["q"], "type": "object" }, "required": ["q"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "89efa02eab3c01a1" },
940
- { "mcpName": "mcp__ainecto__task_apply_changes", "commandPath": ["task", "apply-changes"], "group": "task", "description": "Task CUD entry point. Applies one atomic batch using operation objects shaped exactly as {op, uuid, data}. There is no partial success mode; any validation failure aborts the whole batch.", "inputSchema": { "examples": [{ "baseRevision": 1, "clientMutationId": "task-mutation-1", "documentUuid": "task-document-uuid", "operations": [{ "data": { "parentTaskUuid": null, "title": "Task title" }, "op": "create_task", "uuid": "client-task-uuid" }] }], "properties": { "baseRevision": { "description": "Current board revision from task_get_snapshot", "type": "integer" }, "clientMutationId": { "description": "Optional idempotency key. Visible ASCII, 1-128 chars; will become required in a later phase.", "maxLength": 128, "minLength": 1, "pattern": "^[\\x21-\\x7E]{1,128}$", "type": "string" }, "documentUuid": { "description": "Task document UUID", "type": "string" }, "operations": { "description": "Atomic batch. No partial success mode; validation failure aborts all operations.", "items": { "additionalProperties": false, "properties": { "data": { "description": "Operation payload. Keep all operation-specific fields inside data.", "type": "object" }, "op": { "description": "Operation type", "enum": ["create_task", "update_task", "archive_task", "restore_task", "move_task", "transition_task", "assign_task", "schedule_task", "set_progress"], "type": "string" }, "uuid": { "description": "Task UUID. For create_task this is the client-generated UUID.", "type": "string" } }, "required": ["op", "uuid", "data"], "type": "object" }, "type": "array" } }, "required": ["documentUuid", "baseRevision", "operations"], "type": "object" }, "required": ["baseRevision", "documentUuid", "operations"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "085eb6e8a3eae2c8" },
941
- { "mcpName": "mcp__ainecto__task_get_snapshot", "commandPath": ["task", "get-snapshot"], "group": "task", "description": "Get one Task document snapshot. Returns board revision, statuses, tasks, member summaries, and permissions. Use before mutations to obtain baseRevision.", "inputSchema": { "properties": { "documentUuid": { "description": "Task document UUID", "type": "string" }, "includeArchived": { "description": "Whether to include archived tasks", "type": "boolean" }, "limit": { "description": "Maximum tasks to return; defaults to server snapshot cap", "type": "integer" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "a11876f9b475338a" },
942
- { "mcpName": "mcp__ainecto__task_get_task", "commandPath": ["task", "get-task"], "group": "task", "description": "Get one task by UUID from a Task document.", "inputSchema": { "properties": { "documentUuid": { "description": "Task document UUID", "type": "string" }, "taskUuid": { "description": "Task UUID", "type": "string" } }, "required": ["documentUuid", "taskUuid"], "type": "object" }, "required": ["documentUuid", "taskUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "96b1ea530c583337" },
943
- { "mcpName": "mcp__ainecto__task_list_statuses", "commandPath": ["task", "list-statuses"], "group": "task", "description": "List statuses for a Task document. Status slug is intentionally not returned.", "inputSchema": { "properties": { "documentUuid": { "description": "Task document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "864ea509c54fe2e7" },
944
- { "mcpName": "mcp__ainecto__task_list_tasks", "commandPath": ["task", "list-tasks"], "group": "task", "description": "List tasks in a Task document with optional parent/status/assignee filters.", "inputSchema": { "properties": { "assigneeUserId": { "description": "Optional assignee user id filter", "type": "string" }, "documentUuid": { "description": "Task document UUID", "type": "string" }, "includeArchived": { "description": "Whether to include archived tasks", "type": "boolean" }, "limit": { "description": "Maximum tasks to return", "type": "integer" }, "parentTaskUuid": { "description": "Optional parent filter", "type": "string" }, "statusUuid": { "description": "Optional status filter", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "be8dc0a4ecd117a8" },
945
- { "mcpName": "mcp__ainecto__testcase_archive_scenarios", "commandPath": ["testcase", "archive-scenarios"], "group": "testcase", "description": "", "inputSchema": { "properties": { "ids": { "description": "Scenario ids", "items": { "type": "integer" }, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "a5a8768d14ebaed7" },
946
- { "mcpName": "mcp__ainecto__testcase_complete_runs", "commandPath": ["testcase", "complete-runs"], "group": "testcase", "description": "", "inputSchema": { "properties": { "ids": { "description": "Run ids", "items": { "type": "integer" }, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "84bc3dad482ac61a" },
947
- { "mcpName": "mcp__ainecto__testcase_create_runs", "commandPath": ["testcase", "create-runs"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Runs", "items": { "properties": { "buildVersion": { "description": "Build version", "type": "string" }, "description": { "description": "Description", "type": "string" }, "environment": { "description": "Environment", "type": "string" }, "name": { "description": "Run name", "type": "string" }, "projectId": { "description": "Project id", "type": "integer" }, "testAccount": { "description": "SUT test account", "type": "string" }, "testCaseIds": { "description": "Test case ids to attach", "items": { "type": "integer" }, "type": "array" } }, "required": ["projectId", "name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "6a08117c79f589a1" },
948
- { "mcpName": "mcp__ainecto__testcase_create_scenarios", "commandPath": ["testcase", "create-scenarios"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Scenarios", "items": { "properties": { "description": { "description": "Description", "type": "string" }, "documentUuid": { "description": "Parent testcase document UUID", "type": "string" }, "name": { "description": "Scenario name", "type": "string" }, "parentId": { "description": "Parent scenario id", "type": "integer" } }, "required": ["documentUuid", "name"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "4686fd0f078a65e8" },
949
- { "mcpName": "mcp__ainecto__testcase_create_testcases", "commandPath": ["testcase", "create-testcases"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Testcases", "items": { "properties": { "assigneeId": { "description": "Assignee user id", "type": "string" }, "expectedResult": { "description": "Expected result", "type": "string" }, "precondition": { "description": "Precondition", "type": "string" }, "priority": { "description": "Priority", "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"], "type": "string" }, "scenarioId": { "description": "Scenario id (required)", "type": "integer" }, "source": { "description": "Source", "enum": ["MANUAL", "AI", "IMPORT", "MCP"], "type": "string" }, "status": { "description": "Status", "enum": ["DRAFT", "READY", "DEPRECATED"], "type": "string" }, "tags": { "description": "Tags", "type": "string" }, "testAccount": { "description": "SUT test account", "type": "string" }, "title": { "description": "Title", "type": "string" }, "type": { "description": "Type", "enum": ["FUNCTIONAL", "REGRESSION", "SMOKE", "PERFORMANCE", "SECURITY", "E2E", "UNIT", "INTEGRATION"], "type": "string" } }, "required": ["scenarioId", "title"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "ae90d4749debacf0" },
950
- { "mcpName": "mcp__ainecto__testcase_delete_runs", "commandPath": ["testcase", "delete-runs"], "group": "testcase", "description": "", "inputSchema": { "properties": { "ids": { "description": "Run ids", "items": { "type": "integer" }, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "dev", "schemaHash": "1ba26baf794bde57" },
951
- { "mcpName": "mcp__ainecto__testcase_delete_testcases", "commandPath": ["testcase", "delete-testcases"], "group": "testcase", "description": "", "inputSchema": { "properties": { "ids": { "description": "Testcase ids", "items": { "type": "integer" }, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "dev", "schemaHash": "ab095f553bf46937" },
952
- { "mcpName": "mcp__ainecto__testcase_get_run", "commandPath": ["testcase", "get-run"], "group": "testcase", "description": "", "inputSchema": { "properties": { "id": { "description": "Run id", "type": "integer" } }, "required": ["id"], "type": "object" }, "required": ["id"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "713c83514cf07d68" },
953
- { "mcpName": "mcp__ainecto__testcase_get_testcase", "commandPath": ["testcase", "get-testcase"], "group": "testcase", "description": "", "inputSchema": { "properties": { "id": { "description": "Testcase id", "type": "integer" } }, "required": ["id"], "type": "object" }, "required": ["id"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "54e497a2e3c9978d" },
954
- { "mcpName": "mcp__ainecto__testcase_list_executions", "commandPath": ["testcase", "list-executions"], "group": "testcase", "description": "", "inputSchema": { "properties": { "runId": { "description": "Run id", "type": "integer" } }, "required": ["runId"], "type": "object" }, "required": ["runId"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "a5a2abcf9e43225e" },
955
- { "mcpName": "mcp__ainecto__testcase_list_runs", "commandPath": ["testcase", "list-runs"], "group": "testcase", "description": "", "inputSchema": { "properties": { "projectId": { "description": "Project id", "type": "integer" } }, "required": ["projectId"], "type": "object" }, "required": ["projectId"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "b7d5275b67bb11d9" },
956
- { "mcpName": "mcp__ainecto__testcase_list_scenarios", "commandPath": ["testcase", "list-scenarios"], "group": "testcase", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "Parent testcase document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "2486ade3c9cb0984" },
957
- { "mcpName": "mcp__ainecto__testcase_list_step_checks", "commandPath": ["testcase", "list-step-checks"], "group": "testcase", "description": "", "inputSchema": { "properties": { "caseId": { "description": "Testcase id", "type": "integer" }, "runId": { "description": "Run id", "type": "integer" } }, "required": ["runId", "caseId"], "type": "object" }, "required": ["caseId", "runId"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "f2d5b45c0d64cd5c" },
958
- { "mcpName": "mcp__ainecto__testcase_list_testcases", "commandPath": ["testcase", "list-testcases"], "group": "testcase", "description": "", "inputSchema": { "properties": { "documentUuid": { "description": "Parent testcase document UUID", "type": "string" } }, "required": ["documentUuid"], "type": "object" }, "required": ["documentUuid"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "ef40927392d5a684" },
959
- { "mcpName": "mcp__ainecto__testcase_record_executions", "commandPath": ["testcase", "record-executions"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Executions", "items": { "properties": { "comment": { "description": "Comment", "type": "string" }, "defectLinks": { "description": "Defect URLs", "type": "string" }, "durationSeconds": { "description": "Duration in seconds", "type": "integer" }, "result": { "description": "Result", "enum": ["PASSED", "FAILED", "BLOCKED", "SKIPPED", "UNTESTED", "IN_PROGRESS"], "type": "string" }, "runId": { "description": "Run id", "type": "integer" }, "testAccount": { "description": "SUT account", "type": "string" }, "testCaseId": { "description": "Testcase id", "type": "integer" } }, "required": ["runId", "testCaseId", "result"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "071a2075d00bff9b" },
960
- { "mcpName": "mcp__ainecto__testcase_remove_run_cases", "commandPath": ["testcase", "remove-run-cases"], "group": "testcase", "description": "", "inputSchema": { "properties": { "runId": { "description": "Run id", "type": "integer" }, "testCaseIds": { "description": "Testcase ids to remove", "items": { "type": "integer" }, "type": "array" } }, "required": ["runId", "testCaseIds"], "type": "object" }, "required": ["runId", "testCaseIds"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "7aef8a39afe88c5c" },
961
- { "mcpName": "mcp__ainecto__testcase_reorder_scenarios", "commandPath": ["testcase", "reorder-scenarios"], "group": "testcase", "description": "", "inputSchema": { "properties": { "ids": { "description": "Scenario ids in new order", "items": { "type": "integer" }, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "7faf43a0b53c2988" },
962
- { "mcpName": "mcp__ainecto__testcase_reorder_testcases", "commandPath": ["testcase", "reorder-testcases"], "group": "testcase", "description": "", "inputSchema": { "properties": { "ids": { "description": "Testcase ids in new order", "items": { "type": "integer" }, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "5f27d8b94aca5334" },
963
- { "mcpName": "mcp__ainecto__testcase_update_runs", "commandPath": ["testcase", "update-runs"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "buildVersion": { "description": "New build version", "type": "string" }, "description": { "description": "New description", "type": "string" }, "environment": { "description": "New environment", "type": "string" }, "id": { "description": "Run id", "type": "integer" }, "name": { "description": "New name", "type": "string" }, "status": { "description": "New status", "enum": ["DRAFT", "IN_PROGRESS", "COMPLETED", "CANCELLED"], "type": "string" }, "testAccount": { "description": "New SUT account", "type": "string" } }, "required": ["id"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "370074b6681d495f" },
964
- { "mcpName": "mcp__ainecto__testcase_update_scenarios", "commandPath": ["testcase", "update-scenarios"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "id": { "description": "Scenario id", "type": "integer" }, "name": { "description": "New name", "type": "string" }, "parentId": { "description": "New parent id", "type": "integer" } }, "required": ["id"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "929c8647c06c5fdd" },
965
- { "mcpName": "mcp__ainecto__testcase_update_testcases", "commandPath": ["testcase", "update-testcases"], "group": "testcase", "description": "", "inputSchema": { "properties": { "items": { "description": "Updates", "items": { "properties": { "assigneeId": { "description": "Assignee", "type": "string" }, "expectedResult": { "description": "New expected result", "type": "string" }, "id": { "description": "Testcase id", "type": "integer" }, "precondition": { "description": "New precondition", "type": "string" }, "priority": { "description": "Priority", "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"], "type": "string" }, "scenarioId": { "description": "New scenario id", "type": "integer" }, "status": { "description": "Status", "enum": ["DRAFT", "READY", "DEPRECATED"], "type": "string" }, "tags": { "description": "Tags", "type": "string" }, "testAccount": { "description": "SUT account", "type": "string" }, "title": { "description": "New title", "type": "string" }, "type": { "description": "Type", "enum": ["FUNCTIONAL", "REGRESSION", "SMOKE", "PERFORMANCE", "SECURITY", "E2E", "UNIT", "INTEGRATION"], "type": "string" } }, "required": ["id"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "b562a7b860f6036c" },
966
- { "mcpName": "mcp__ainecto__testcase_upsert_step_checks", "commandPath": ["testcase", "upsert-step-checks"], "group": "testcase", "description": "", "inputSchema": { "properties": { "caseId": { "description": "Testcase id", "type": "integer" }, "items": { "description": "Step checks", "items": { "properties": { "checked": { "description": "Checked state", "type": "boolean" }, "comment": { "description": "Per-step note", "type": "string" }, "stepId": { "description": "TestStep id", "type": "integer" } }, "required": ["stepId", "checked"], "type": "object" }, "type": "array" }, "runId": { "description": "Run id", "type": "integer" } }, "required": ["runId", "caseId", "items"], "type": "object" }, "required": ["caseId", "items", "runId"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "ac5768125a9fd15c" },
967
- { "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": "dev", "schemaHash": "4aae7df04fba8779" },
968
- { "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": "dev", "schemaHash": "e307cab0ebafd179" },
969
- { "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": "dev", "schemaHash": "15ea4a409d1549de" },
970
- { "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": "dev", "schemaHash": "641de5b64ec75ab2" },
971
- { "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}; diagram: {diagramSource,sourceLanguage,edgeStyle,isModule,metadata}; testcase: {defaultEnvironment (max 50 chars), defaultTestAccount (max 100 chars)}; task: no typePayload; board and default statuses are seeded automatically.", "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": "dev", "schemaHash": "778908eec43aba81" },
972
- { "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": "dev", "schemaHash": "7bef3b4702df1f00" },
973
- { "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": "dev", "schemaHash": "10cc0d84aeb2192b" },
974
- { "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": "dev", "schemaHash": "723e4f6d36ee9d02" },
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" }
901
+ { "mcpName": "create_comments", "commandPath": ["comments", "create"], "group": "generic", "description": "Add comments, questions, or replies to a document or a kanban card (cardUuid). Anyone who can read it 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": { "cardUuid": { "description": "Kanban card UUID the comments belong to (from kanban_list_cards). Anyone who can read the card's board may comment. Give this or documentUuid, not both.", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to. Give this or cardUuid, not both.", "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. The thread's original author and earlier repliers get a notification email (except people mentioned in this reply, who get the mention email instead).", "minimum": 1, "type": "integer" } }, "required": ["body"], "type": "object" }, "minItems": 1, "type": "array" }, "mailLanguage": { "description": "Language of the comment notification email (mention or reply). Defaults to en.", "enum": ["en", "ko"], "type": "string" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "d44acdedf00dfe76" },
902
+ { "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, diagram, markdown, openapi, html, file, kanban.", "enum": ["erd", "diagram", "markdown", "openapi", "html", "file", "kanban"], "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); kanban: no payload \u2014 creates an empty board; add cards with kanban_apply_changes.', "type": "object" } }, "required": ["projectUuid", "type", "title"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "65867cbf84bf78a8" },
903
+ { "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": "dev", "schemaHash": "30f45b887259609f" },
904
+ { "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": "dev", "schemaHash": "edf5cf688d7c039a" },
905
+ { "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": "dev", "schemaHash": "d9d1b0d561828b8e" },
906
+ { "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": "dev", "schemaHash": "b22c24bffdfe3d7d" },
907
+ { "mcpName": "delete_comments", "commandPath": ["comments", "delete"], "group": "generic", "description": "Delete your own comments on a document or a kanban card (cardUuid). 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": { "cardUuid": { "description": "Kanban card UUID the comments belong to (from kanban_list_cards). Anyone who can read the card's board may comment. Give this or documentUuid, not both.", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to. Give this or cardUuid, not both.", "type": "string" }, "ids": { "description": "Comment ids to delete (from mcp__ainecto__list_comments)", "items": { "minimum": 1, "type": "integer" }, "minItems": 1, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": true, "sourceCatalog": "dev", "schemaHash": "082e4b4d9ba97fe0" },
908
+ { "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": "dev", "schemaHash": "3a725ae70956c270" },
909
+ { "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": "dev", "schemaHash": "2254cde89c9e8f06" },
910
+ { "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": "dev", "schemaHash": "6521c33a28cb5465" },
911
+ { "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": "dev", "schemaHash": "3177868221137066" },
912
+ { "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": "dev", "schemaHash": "2c829103f86f4168" },
913
+ { "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": "dev", "schemaHash": "2c03c4f7daa69898" },
914
+ { "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": "dev", "schemaHash": "55c5aa27b8cf489d" },
915
+ { "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": "dev", "schemaHash": "646d553238328a9e" },
916
+ { "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": "dev", "schemaHash": "2143d616cf7822bf" },
917
+ { "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": "dev", "schemaHash": "4645085bde616381" },
918
+ { "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": "dev", "schemaHash": "8a7ca45dd34d1aef" },
919
+ { "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": "dev", "schemaHash": "d444066b5ca6eeb4" },
920
+ { "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": "dev", "schemaHash": "870ace32642a770a" },
921
+ { "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": "dev", "schemaHash": "9ad96e3dc5fd2a8f" },
922
+ { "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": "dev", "schemaHash": "bd1b310200b78420" },
923
+ { "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": "dev", "schemaHash": "18559d7c0af09d2c" },
924
+ { "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": "dev", "schemaHash": "87f5bcf5a253815a" },
925
+ { "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": "dev", "schemaHash": "651fd0d54e3635ff" },
926
+ { "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": "dev", "schemaHash": "f0d79d7a1996939c" },
927
+ { "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": "dev", "schemaHash": "5b47fe7d99e60d4d" },
928
+ { "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": "dev", "schemaHash": "0dc13648bf3fbf8f" },
929
+ { "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": "dev", "schemaHash": "3db246dfe8663370" },
930
+ { "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": "dev", "schemaHash": "4d1f602fa487a0e4" },
931
+ { "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": "dev", "schemaHash": "307d766c777bbe8c" },
932
+ { "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": "dev", "schemaHash": "775bff53bbc21c9e" },
933
+ { "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": "dev", "schemaHash": "34324dfc7ef461c8" },
934
+ { "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": "dev", "schemaHash": "1e0e7f1bf33b669c" },
935
+ { "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; kanban \u2192 board {viewer, columns: each column's first 20 cards + hasMore} (more cards and card details via kanban_list_cards); 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": "dev", "schemaHash": "5c8e9d305fc15c1f" },
936
+ { "mcpName": "get_my_work_summary", "commandPath": ["my-work-summary", "get"], "group": "generic", "description": `One-call summary of what is waiting for the signed-in user in AiNecto \u2014 call it when the user asks things like "do I have anything to do?", "anything new?", "anything to review?" or "did anyone ask me something?". Returns notifications {unread (not yet read by the person, newest 100 counted; unreadCapped when there are more), unreadByKind, newForThisConnection (not yet acknowledged by this connection \u2014 the same number as the line at the end of tool results)}, mentionsToRead {items the person has not read that mention them, each with target title and url}, and todos {open, leaseExpired, inProgress, items: questions and requests waiting for the user's agent} and cards {kanban cards assigned to the user on boards they can read now: assignedOpen, pendingAcceptance (assigned but not yet accepted or declined), inProgress, inReview, blocked, items: the first page}. For details use list_notifications, list_agent_todos and kanban_list_cards; to read the document and its comments use get_document with the target uuid. Read-only: nothing is marked read or acknowledged.`, "inputSchema": { "additionalProperties": false, "properties": { "projectUuid": { "description": "Older single-value form of projectUuids.", "type": "string" }, "projectUuids": { "description": "Only items in any of these projects. Use it when the user names a project or the project you are working in is clear. With workspaceUuids too, both must match. A workspace or project you cannot see matches nothing.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "workspaceUuids": { "description": "Only items in any of these workspaces.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "105cc4ba01f785d8" },
937
+ { "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": "dev", "schemaHash": "192403c3484ac774" },
938
+ { "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": "dev", "schemaHash": "4e308c1a8c7203d1" },
939
+ { "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": "dev", "schemaHash": "db6d1b4879d57c56" },
940
+ { "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": "dev", "schemaHash": "f29c86804eb91700" },
941
+ { "mcpName": "kanban_apply_changes", "commandPath": ["kanban", "apply-changes"], "group": "kanban", "description": "Create, edit, reorder and link cards on one kanban board in a single atomic call (at most 100 operations; if one fails nothing is applied and the error carries operationIndex and code). Creating and editing cards needs edit access to the board; the assignee may also check acceptance criteria and add result links (DELIVERABLE, REVIEW_RESULT, TEST_EVIDENCE). To change a card's column, assign it, accept, approve, block, watch and similar, use kanban_card_action. Results list each operation's card uuid, revision, status and sortKey.", "inputSchema": { "additionalProperties": false, "properties": { "boardUuid": { "description": "Kanban board (document) UUID.", "type": "string" }, "mailLanguage": { "description": "Language of the emails these operations may send (review requests to newly named reviewers). Defaults to en.", "enum": ["en", "ko"], "type": "string" }, "operations": { "description": "Operations, applied in order in one transaction. If any fails, none is applied and the error names operationIndex.", "items": { "additionalProperties": false, "properties": { "afterCardUuid": { "description": "Place after this card in the column (create, move).", "type": "string" }, "beforeCardUuid": { "description": "move: place before this card in the column.", "type": "string" }, "cardType": { "description": "Card type (default TASK).", "enum": ["TASK", "REVIEW", "BUG", "DECISION"], "type": "string" }, "cardUuid": { "description": "Target card (update, move, link, unlink). It must be on this board.", "type": "string" }, "clientMutationId": { "description": "create: idempotency key \u2014 retrying with the same value returns the same card instead of creating another.", "type": "string" }, "criteria": { "description": "Acceptance criteria. update replaces the whole list; the assignee may only flip done.", "items": { "additionalProperties": false, "properties": { "done": { "description": "Checked (default false).", "type": "boolean" }, "text": { "description": "What must be true.", "type": "string" } }, "required": ["text"], "type": "object" }, "type": "array" }, "description": { "description": "Markdown description. Keep it short \u2014 link longer text as a document instead.", "type": "string" }, "documentUuid": { "description": "AiNecto document to link (you must be able to read it). Give this or url.", "type": "string" }, "dueAt": { "description": "Due time, ISO-8601 instant such as 2026-10-31T09:00:00Z.", "type": "string" }, "expectedRevision": { "description": "The card revision you last read. If the card changed since, the whole call fails with CARD_CHANGED and the current card. Send it when people may edit the card too.", "minimum": 0, "type": "integer" }, "label": { "description": "Optional label for an external URL.", "type": "string" }, "linkId": { "description": "unlink: the linkId from the card's links.", "minimum": 1, "type": "integer" }, "linkRole": { "description": "Link role. The assignee may add DELIVERABLE, REVIEW_RESULT and TEST_EVIDENCE links; other roles need edit access to the board.", "enum": ["REQUIREMENT", "REFERENCE", "DELIVERABLE", "REVIEW_RESULT", "DECISION", "TEST_EVIDENCE"], "type": "string" }, "links": { "description": "create only: links to attach.", "items": { "additionalProperties": false, "properties": { "documentUuid": { "description": "AiNecto document to link (you must be able to read it). Give this or url.", "type": "string" }, "label": { "description": "Optional label for an external URL.", "type": "string" }, "linkRole": { "description": "Link role. The assignee may add DELIVERABLE, REVIEW_RESULT and TEST_EVIDENCE links; other roles need edit access to the board.", "enum": ["REQUIREMENT", "REFERENCE", "DELIVERABLE", "REVIEW_RESULT", "DECISION", "TEST_EVIDENCE"], "type": "string" }, "url": { "description": "External http(s) URL to link, e.g. a pull request. Give this or documentUuid.", "type": "string" } }, "required": ["linkRole"], "type": "object" }, "type": "array" }, "op": { "description": "create: a new card in To Do (you become its requester). update: change only the fields you send (send null to clear description, dueAt). move: reorder within the card's column. link / unlink: attach or remove a document or an external URL.", "enum": ["create", "link", "move", "unlink", "update"], "type": "string" }, "priority": { "description": "Priority (default MEDIUM).", "enum": ["LOW", "MEDIUM", "HIGH", "URGENT"], "type": "string" }, "responderIds": { "description": "update only: who should answer while the card is blocked.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "reviewerIds": { "description": "Reviewers (userIds from list_mention_candidates with cardUuid or the board's documentUuid). update replaces the whole list.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "title": { "description": "Card title (required for create).", "type": "string" }, "url": { "description": "External http(s) URL to link, e.g. a pull request. Give this or documentUuid.", "type": "string" }, "watcherIds": { "description": "Watchers who get activity notifications. update replaces the whole list.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "required": ["op"], "type": "object" }, "maxItems": 100, "minItems": 1, "type": "array" } }, "required": ["boardUuid", "operations"], "type": "object" }, "required": ["boardUuid", "operations"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "77de7bc105852b87" },
942
+ { "mcpName": "kanban_card_action", "commandPath": ["kanban", "card-action"], "group": "kanban", "description": "Do one workflow action on a kanban card, the same as the buttons in the web app. \u2605Before assign (to someone other than the signed-in user), approve or quick_done, show the user what will happen and get their explicit confirmation \u2014 these change other people's work and notify them. Actions: assign / unassign; accept / decline (only the assignee \u2014 accepting closes the assignment to-do); start (To Do \u2192 In Progress; anyone who can edit the board, or the assignee \u2014 not while an assignment is waiting to be accepted); request_review (\u2192 In Review); approve (\u2192 Done) / request_changes (\u2192 In Progress) by the requester or a reviewer; quick_done (To Do \u2192 Done); reopen (Done \u2192 In Progress); block / unblock; archive / unarchive; set_requester; watch / unwatch (yourself). Doing the action is what closes the matching card to-dos \u2014 update_agent_todos cannot complete them. Returns the card after the action (status, assignment state, allowedActions) and reasonCommentId when a reason was saved.", "inputSchema": { "additionalProperties": false, "properties": { "action": { "description": "The action. Which ones you may do now is the card's allowedActions in kanban_list_cards (with cardUuids).", "enum": ["assign", "unassign", "accept", "decline", "start", "request_review", "approve", "request_changes", "quick_done", "reopen", "block", "unblock", "archive", "unarchive", "set_requester", "watch", "unwatch"], "type": "string" }, "assigneeUserId": { "description": "assign: who gets the card \u2014 someone who can read the board (list_mention_candidates with cardUuid). Your own userId assigns it to you and accepts at once.", "type": "string" }, "cardUuid": { "description": "Card UUID (from kanban_list_cards).", "type": "string" }, "expectedRevision": { "description": "The card revision you last read. If the card changed since, the call fails with CARD_CHANGED and the current card. Send it when people may act on the card too.", "minimum": 0, "type": "integer" }, "mailLanguage": { "description": "Language of the email this action may send (assignment or review request). Defaults to en.", "enum": ["en", "ko"], "type": "string" }, "reason": { "description": "Saved as a comment on the card. Required when the action's allowedActions entry says reasonRequired (e.g. decline, reopen).", "type": "string" }, "requesterUserId": { "description": "set_requester: the new requester.", "type": "string" }, "responderIds": { "description": "block: who should answer (people who can read the board). Omit to keep the card's current responders.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "required": ["cardUuid", "action"], "type": "object" }, "required": ["action", "cardUuid"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "77da9c03fd1ce5f4" },
943
+ { "mcpName": "kanban_list_cards", "commandPath": ["kanban", "list-cards"], "group": "kanban", "description": "Read kanban cards. Three ways: cardUuids \u2192 those cards in full, including allowedActions (what you may do now with kanban_card_action) and links; boardUuid \u2192 the board's columns To Do, In Progress, In Review, Done (each with hasMore and nextAfter); neither \u2192 cards across all boards you can read where you are the assignee (or requester / watcher), most recently updated first. Each card has uuid, title, status, assignee and assignmentState (PENDING = not yet accepted), blocked, revision and allowedTransitions.", "inputSchema": { "additionalProperties": false, "properties": { "boardUuid": { "description": "Board (document) UUID: list its columns, first page each (or one column with status).", "type": "string" }, "cardUuids": { "description": "Read these cards in full (description, people, links, allowedActions). Use alone.", "items": { "type": "string" }, "maxItems": 50, "minItems": 1, "type": "array", "uniqueItems": true }, "cursor": { "description": "Next page: nextCursor (without boardUuid) or a column's nextAfter (with boardUuid and status).", "type": "string" }, "includeDone": { "description": "Without boardUuid: also recently completed cards.", "type": "boolean" }, "limit": { "description": "Cards per page (per column with boardUuid). Default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Without boardUuid: only boards in this project.", "type": "string" }, "relation": { "description": "Without boardUuid: cards across boards where you are the assignee (default), the requester or a watcher.", "enum": ["ASSIGNEE", "REQUESTER", "WATCHER"], "type": "string" }, "status": { "description": "With boardUuid: only this column.", "enum": ["TODO", "IN_PROGRESS", "IN_REVIEW", "DONE"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "99ed9634a628eb3f" },
944
+ { "mcpName": "list_agent_todos", "commandPath": ["agent-todos", "list"], "group": "generic", "description": "List the to-dos that ask the signed-in user's agent to act now (for example a question that mentions the user). This is the current state, not a feed: a to-do you have not finished comes back on every call until it is completed, dismissed, or closed because the person did it themselves. Each item has todoId, kind, obligation, openedAt, summary, target (title and url), action and state. To work on one, claim it with update_agent_todos and complete it with a one-line note. Actions that affect other people (assigning, approving, inviting) need the user's confirmation first.", "inputSchema": { "additionalProperties": false, "properties": { "after": { "description": "todoId of the last item of the previous page.", "type": "string" }, "kinds": { "description": "Only these kinds.", "items": { "enum": ["COMMENT_MENTIONED", "COMMENT_REPLIED", "QUESTION_ANSWERED", "QUESTION_RESOLVED", "QUESTION_MENTIONED", "CARD_CREATED", "CARD_UPDATED", "CARD_ASSIGNED", "CARD_UNASSIGNED", "CARD_ASSIGNMENT_ACCEPTED", "CARD_ASSIGNMENT_DECLINED", "CARD_STATUS_CHANGED", "CARD_REVIEW_REQUESTED", "CARD_CHANGES_REQUESTED", "CARD_APPROVED", "CARD_QUICK_DONE", "CARD_REOPENED", "CARD_BLOCKED", "CARD_UNBLOCKED", "CARD_ARCHIVED", "CARD_COMMENTED"], "type": "string" }, "type": "array", "uniqueItems": true }, "limit": { "description": "Items per call, default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Older single-value form of projectUuids.", "type": "string" }, "projectUuids": { "description": "Only to-dos in any of these projects. Use it when the user names a project or the project you are working in is clear. With workspaceUuids too, both must match. A workspace or project you cannot see matches nothing.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "state": { "description": "OPEN (default): to-dos you can take, including ones whose lease expired. MINE: to-dos currently leased (claimed) and not expired.", "items": { "enum": ["OPEN", "MINE"], "type": "string" }, "type": "array", "uniqueItems": true }, "workspaceUuids": { "description": "Only to-dos in any of these workspaces.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "ee1a536fc14ee727" },
945
+ { "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": "dev", "schemaHash": "fae7152d1e4a930b" },
946
+ { "mcpName": "list_comments", "commandPath": ["comments", "list"], "group": "generic", "description": "List the active comments on a document or a kanban card (cardUuid), 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 it can list its comments.", "inputSchema": { "additionalProperties": false, "properties": { "cardUuid": { "description": "Kanban card UUID the comments belong to (from kanban_list_cards). Anyone who can read the card's board may comment. Give this or documentUuid, not both.", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to. Give this or cardUuid, not both.", "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "21dbfc4740a62df5" },
947
+ { "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, diagram, markdown, openapi, html, file, kanban.", "enum": ["erd", "diagram", "markdown", "openapi", "html", "file", "kanban"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "0d7288c249fc715c" },
948
+ { "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": "dev", "schemaHash": "461dc6735ac92c3b" },
949
+ { "mcpName": "list_history", "commandPath": ["history", "list"], "group": "generic", "description": "List recent activity (who did what, people and agents) in a workspace, a project, or on one resource, newest first. Give at least one of workspaceUuid, projectUuid, or resourceType with resourceUuid; you need editor access or higher in that scope. This is a list to refresh, not a gap-free feed: to see what is new, read the first page again and skip ids you have already seen; to get each new notification exactly once use list_notifications. Each item has id, kind, at, lastAt, coalesceCount, actor, target (title and url only when you can read it now), changes and summary.", "inputSchema": { "additionalProperties": false, "properties": { "actorType": { "description": "Only people (USER) or only agents (AGENT).", "enum": ["USER", "AGENT"], "type": "string" }, "before": { "description": "nextCursor of the previous page (older items).", "type": "string" }, "kinds": { "description": "Only these kinds.", "items": { "enum": ["COMMENT_MENTIONED", "COMMENT_REPLIED", "QUESTION_ANSWERED", "QUESTION_RESOLVED", "QUESTION_MENTIONED", "CARD_CREATED", "CARD_UPDATED", "CARD_ASSIGNED", "CARD_UNASSIGNED", "CARD_ASSIGNMENT_ACCEPTED", "CARD_ASSIGNMENT_DECLINED", "CARD_STATUS_CHANGED", "CARD_REVIEW_REQUESTED", "CARD_CHANGES_REQUESTED", "CARD_APPROVED", "CARD_QUICK_DONE", "CARD_REOPENED", "CARD_BLOCKED", "CARD_UNBLOCKED", "CARD_ARCHIVED", "CARD_COMMENTED"], "type": "string" }, "type": "array", "uniqueItems": true }, "limit": { "description": "Items per call, default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Activity in this project.", "type": "string" }, "resourceType": { "description": "With resourceUuid: activity on one resource.", "enum": ["DOCUMENT", "PROJECT", "KANBAN_CARD"], "type": "string" }, "resourceUuid": { "description": "UUID of the resource named by resourceType.", "type": "string" }, "workspaceUuid": { "description": "Activity in this workspace.", "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "eef3e3be41cfb6b4" },
950
+ { "mcpName": "list_mention_candidates", "commandPath": ["mention-candidates", "list"], "group": "generic", "description": "List the people you can mention in comments on a document or a kanban card (cardUuid): userId and displayName. Only people who can read it are listed \u2014 for a card, the people who can read its board, which are also the people a card can be assigned to. 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": { "cardUuid": { "description": "Kanban card UUID the comments belong to (from kanban_list_cards). Anyone who can read the card's board may comment. Give this or documentUuid, not both.", "type": "string" }, "cursor": { "description": "nextCursor from the previous page (same parent and query).", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to. Give this or cardUuid, not both.", "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" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "e7585805859ccb73" },
951
+ { "mcpName": "list_notifications", "commandPath": ["notifications", "list"], "group": "generic", "description": "Read new notifications for the signed-in user, oldest first and without gaps \u2014 for an agent that checks the inbox periodically. The server remembers, per connection and per filter (kinds, workspaceUuids, projectUuids, directOnly), how far you have acknowledged; a new filter starts from now. Each response carries ackToken: pass it as ack in your next call (with the same filter) after you have handled the batch \u2014 if you do not, the next call returns the same batch again (identify duplicates by id and seq; an item that comes back with a higher seq and a larger groupCount is an update of the same entry). Each item has id, seq, kind, at, summary (one line), actor, target (title and url only when you can read it now), action (what the user is asked to do) and humanRead. An item you can no longer read comes back as {id, seq, at, redacted: true}. This tool never marks anything read for the person; open to-dos are in list_agent_todos. Calls closer than 30 seconds apart (except a call that carries ack) return retryAfterSeconds instead of items.", "inputSchema": { "additionalProperties": false, "properties": { "ack": { "description": "The ackToken of the previous response. Send it with the same filter once you have handled (or safely stored) that batch; only then does the saved position move past it. Without it, the same batch is returned again.", "type": "string" }, "cursor": { "description": 'Where to read from. "saved" (default): after what this connection last acknowledged for this same filter. "latest": from now on (moves the saved position to now). "earliest": everything still kept (90 days). Or a nextCursor from a previous response: read after it without using or changing the saved position.', "type": "string" }, "directOnly": { "description": "Only notifications that point at you directly (mentions, questions to you).", "type": "boolean" }, "kinds": { "description": "Only these notification kinds.", "items": { "enum": ["COMMENT_MENTIONED", "COMMENT_REPLIED", "QUESTION_ANSWERED", "QUESTION_RESOLVED", "QUESTION_MENTIONED", "CARD_CREATED", "CARD_UPDATED", "CARD_ASSIGNED", "CARD_UNASSIGNED", "CARD_ASSIGNMENT_ACCEPTED", "CARD_ASSIGNMENT_DECLINED", "CARD_STATUS_CHANGED", "CARD_REVIEW_REQUESTED", "CARD_CHANGES_REQUESTED", "CARD_APPROVED", "CARD_QUICK_DONE", "CARD_REOPENED", "CARD_BLOCKED", "CARD_UNBLOCKED", "CARD_ARCHIVED", "CARD_COMMENTED"], "type": "string" }, "type": "array", "uniqueItems": true }, "limit": { "description": "Items per call, default 20.", "maximum": 50, "minimum": 1, "type": "integer" }, "projectUuid": { "description": "Older single-value form of projectUuids.", "type": "string" }, "projectUuids": { "description": "Only notifications in any of these projects. Use it when the user names a project or the project you are working in is clear. With workspaceUuids too, both must match. A workspace or project you cannot see matches nothing.", "items": { "type": "string" }, "type": "array", "uniqueItems": true }, "workspaceUuids": { "description": "Only notifications in any of these workspaces.", "items": { "type": "string" }, "type": "array", "uniqueItems": true } }, "type": "object" }, "required": [], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "bedcd4375c7f8735" },
952
+ { "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": "dev", "schemaHash": "939bfcc088425771" },
953
+ { "mcpName": "list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "329250aecc06a298" },
954
+ { "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": "dev", "schemaHash": "f08f9faee3ba9107" },
955
+ { "mcpName": "list_workspaces", "commandPath": ["workspaces", "list"], "group": "workspace", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "39dba0089fdbdf6a" },
956
+ { "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": "dev", "schemaHash": "bf82401f055f29b0" },
957
+ { "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": "dev", "schemaHash": "1e4724ea42f25743" },
958
+ { "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": "dev", "schemaHash": "45899053743a0c1e" },
959
+ { "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": "dev", "schemaHash": "eff4458d2f35587a" },
960
+ { "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": "dev", "schemaHash": "abe0196da3c48ad5" },
961
+ { "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, diagram, markdown, openapi, html, file, kanban. 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": "dev", "schemaHash": "99a4b6c77dd2368b" },
962
+ { "mcpName": "resolve_comments", "commandPath": ["tools", "run", "resolve-comments"], "group": "generic", "description": "Mark questions on a document or a kanban card (cardUuid) as resolved. Only top-level comments with kind QUESTION can be resolved, and it needs edit access to the document or board (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": { "cardUuid": { "description": "Kanban card UUID the comments belong to (from kanban_list_cards). Anyone who can read the card's board may comment. Give this or documentUuid, not both.", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to. Give this or cardUuid, not both.", "type": "string" }, "ids": { "description": "Question ids to resolve (top-level comments with kind QUESTION)", "items": { "minimum": 1, "type": "integer" }, "minItems": 1, "type": "array" } }, "required": ["ids"], "type": "object" }, "required": ["ids"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "d996f083fd9e6acf" },
963
+ { "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": "dev", "schemaHash": "005b5b08a19fc605" },
964
+ { "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, diagram, markdown, openapi, html, file, kanban.", "enum": ["erd", "diagram", "markdown", "openapi", "html", "file", "kanban"], "type": "string" } }, "required": ["q"], "type": "object" }, "required": ["q"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "40322499fb85e525" },
965
+ { "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": "dev", "schemaHash": "f2810e71583a320f" },
966
+ { "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": "dev", "schemaHash": "2a24cc44696b7906" },
967
+ { "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": "dev", "schemaHash": "6925e444671afe19" },
968
+ { "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": "dev", "schemaHash": "8dafe9bc6378c3f2" },
969
+ { "mcpName": "update_agent_todos", "commandPath": ["agent-todos", "update"], "group": "generic", "description": "Claim, renew, complete, release, or dismiss the signed-in user's agent to-dos. Only the session holding the current leaseToken may renew, release, or complete a claimed to-do \u2014 even another session of the same connection gets CLAIMED_BY_OTHER. Results are per item: {todoId, ok: true, state, leaseToken?, claimExpiresAt?} or {todoId, ok: false, error} with error one of CLAIMED_BY_OTHER, LEASE_LOST, ALREADY_DONE, CLOSED_BY_HUMAN, SUPERSEDED, ACCESS_LOST, BUSINESS_ACTION_REQUIRED, NOT_FOUND, INVALID \u2014 on CLOSED_BY_HUMAN or SUPERSEDED stop working on it. BUSINESS_ACTION_REQUIRED: card to-dos (CARD_*) cannot be completed here; doing the card action closes them. Retrying the same complete or dismiss returns ok again. There is no tool to mark the person's notifications read.", "inputSchema": { "additionalProperties": false, "properties": { "items": { "description": "To-dos to update; each item is applied and reported on its own.", "items": { "additionalProperties": false, "properties": { "action": { "description": "claim: take it (returns leaseToken, valid 30 minutes). renew: extend your lease. complete: done (with leaseToken if you claimed it; without one only while it is still OPEN). release: give it back. dismiss: not a to-do.", "enum": ["claim", "renew", "complete", "release", "dismiss"], "type": "string" }, "leaseToken": { "description": "Required for renew and release, and for complete or dismiss of a to-do you claimed.", "type": "string" }, "note": { "description": "For complete or dismiss: one line on what you did (at most 500 characters, no document bodies). The person sees it in the inbox.", "maxLength": 500, "type": "string" }, "todoId": { "description": "todoId from list_agent_todos.", "type": "string" } }, "required": ["todoId", "action"], "type": "object" }, "maxItems": 50, "minItems": 1, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "d10fc0752ad55a12" },
970
+ { "mcpName": "update_comments", "commandPath": ["comments", "update"], "group": "generic", "description": "Edit your own comments on a document or a kanban card (cardUuid). Only the author can edit, and only while they can still read it. 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": { "cardUuid": { "description": "Kanban card UUID the comments belong to (from kanban_list_cards). Anyone who can read the card's board may comment. Give this or documentUuid, not both.", "type": "string" }, "documentUuid": { "description": "Document UUID the comments belong to. Give this or cardUuid, not both.", "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 comment notification email (mention or reply). Defaults to en.", "enum": ["en", "ko"], "type": "string" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "file-json", "destructive": false, "sourceCatalog": "dev", "schemaHash": "a5bc050079e34361" },
971
+ { "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); kanban: no body \u2014 only the title can be updated; cards are changed with kanban_apply_changes and kanban_card_action.", "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": "dev", "schemaHash": "47485db860973df3" },
972
+ { "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": "dev", "schemaHash": "43c58a96ee632b5f" },
973
+ { "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": "dev", "schemaHash": "7d849ef0a830aa8c" },
974
+ { "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": "dev", "schemaHash": "553275e6da799c5a" },
975
+ { "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": "dev", "schemaHash": "deda4a56fbbe064f" }
976
976
  ];
977
977
 
978
978
  // src/core/catalog/generator.ts
@@ -990,7 +990,6 @@ var DESTRUCTIVE_PREFIXES = [
990
990
  "move_",
991
991
  "erd_delete_",
992
992
  "erd_remove_",
993
- "task_delete_",
994
993
  "testcase_delete_"
995
994
  ];
996
995
  function generateCatalog(tools, sourceCatalog) {
@@ -1024,7 +1023,7 @@ function inferGroup(strippedName) {
1024
1023
  if (strippedName.startsWith("testcase_")) return "testcase";
1025
1024
  if (strippedName.startsWith("markdown_")) return "markdown";
1026
1025
  if (strippedName.includes("attachment") || strippedName.includes("upload")) return "attachment";
1027
- if (strippedName.startsWith("task_")) return "task";
1026
+ if (strippedName.startsWith("kanban_")) return "kanban";
1028
1027
  if (strippedName.includes("workspace")) return "workspace";
1029
1028
  if (strippedName.includes("document")) return "document";
1030
1029
  return "generic";
@@ -1127,6 +1126,9 @@ async function runStdioBridge(options) {
1127
1126
  }
1128
1127
  try {
1129
1128
  const request = parseJsonRpcRequest(line);
1129
+ if (request.method === "initialize") {
1130
+ options.client.useAgent(clientInfoName(request.params));
1131
+ }
1130
1132
  const response = await options.client.sendRaw(request);
1131
1133
  if (response && request.id !== void 0) {
1132
1134
  options.output.write(`${JSON.stringify(response)}
@@ -1155,17 +1157,27 @@ function parseJsonRpcRequest(line) {
1155
1157
  params: parsed.params
1156
1158
  };
1157
1159
  }
1160
+ function clientInfoName(params) {
1161
+ return isRecord4(params) && isRecord4(params.clientInfo) ? params.clientInfo.name : void 0;
1162
+ }
1158
1163
  function isRecord4(value) {
1159
1164
  return typeof value === "object" && value !== null && !Array.isArray(value);
1160
1165
  }
1161
1166
 
1162
1167
  // src/adapters/mcp/connector.ts
1163
1168
  async function runConnector(options) {
1169
+ const legacyRole = legacyRoleInput(options.legacyRoleFlag, options.envVars);
1170
+ if (legacyRole !== void 0) {
1171
+ options.errorOutput.write(`${LEGACY_ROLE_NOTICE}
1172
+ `);
1173
+ }
1164
1174
  const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore: new FileTokenStore() });
1165
1175
  const client = new McpRpcClient({
1166
1176
  endpoint: options.endpoint,
1167
1177
  tokenProvider: auth,
1168
- role: options.role
1178
+ legacyRole,
1179
+ // 에이전트의 MCP 연결 — 에이전트 이름은 브리지가 호스트의 initialize 에서 읽어 싣는다.
1180
+ channel: "mcp"
1169
1181
  });
1170
1182
  await runStdioBridge({
1171
1183
  input: options.input,
@@ -1238,18 +1250,6 @@ var enrichments = [
1238
1250
  tableColumns: ["uuid", "name", "display_order"],
1239
1251
  displayName: "List ERD table groups"
1240
1252
  },
1241
- {
1242
- mcpName: "task_list_tasks",
1243
- outputHint: "table",
1244
- tableColumns: ["uuid", "title", "statusUuid", "assigneeId", "updatedAt"],
1245
- displayName: "List tasks"
1246
- },
1247
- {
1248
- mcpName: "task_list_statuses",
1249
- outputHint: "table",
1250
- tableColumns: ["uuid", "name", "displayOrder"],
1251
- displayName: "List task statuses"
1252
- },
1253
1253
  {
1254
1254
  mcpName: "enable_shares",
1255
1255
  aliases: ["shares enable"],
@@ -1527,6 +1527,9 @@ async function promptConfirmation(io, message) {
1527
1527
 
1528
1528
  // src/adapters/cli/bespokeCommands.ts
1529
1529
  var ATTACHMENTS_UPLOAD_COMMAND_PATH = ["attachments", "upload"];
1530
+ var NOTIFICATIONS_POLL_COMMAND_PATH = ["notifications", "poll"];
1531
+ var NOTIFICATIONS_ACK_COMMAND_PATH = ["notifications", "ack"];
1532
+ var AGENT_TODOS_POLL_COMMAND_PATH = ["agent-todos", "poll"];
1530
1533
 
1531
1534
  // src/adapters/cli/generatedCommandRouter.ts
1532
1535
  function matchGeneratedCommand(tools, argv) {
@@ -1612,6 +1615,50 @@ function findRestStart(argv, commandPath) {
1612
1615
  // src/core/upload/attachmentUpload.ts
1613
1616
  import { readFile as readFile3, stat as stat2 } from "fs/promises";
1614
1617
  import { basename, extname } from "path";
1618
+
1619
+ // src/core/mcp/toolPayload.ts
1620
+ function unwrapMcpToolPayload(value) {
1621
+ if (!isRecord7(value) || !Array.isArray(value.content)) {
1622
+ return value;
1623
+ }
1624
+ const text = value.content.map((item) => isRecord7(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
1625
+ if (!text) {
1626
+ return value;
1627
+ }
1628
+ try {
1629
+ return JSON.parse(text);
1630
+ } catch {
1631
+ return value;
1632
+ }
1633
+ }
1634
+ function toolErrorText(value) {
1635
+ if (!isRecord7(value) || !Array.isArray(value.content)) {
1636
+ return "The tool returned an error.";
1637
+ }
1638
+ const text = value.content.map((item) => isRecord7(item) && typeof item.text === "string" ? item.text : "").filter(Boolean).join("\n");
1639
+ return text || "The tool returned an error.";
1640
+ }
1641
+ function isRecord7(value) {
1642
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1643
+ }
1644
+ function extractProjects(value) {
1645
+ const candidates = Array.isArray(value) ? value : isRecord7(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
1646
+ const nested = value[key];
1647
+ return Array.isArray(nested) ? nested : [];
1648
+ }) : [];
1649
+ const projects = [];
1650
+ for (const candidate of candidates) {
1651
+ if (isRecord7(candidate) && typeof candidate.uuid === "string") {
1652
+ projects.push({
1653
+ uuid: candidate.uuid,
1654
+ name: typeof candidate.name === "string" ? candidate.name : candidate.uuid
1655
+ });
1656
+ }
1657
+ }
1658
+ return projects;
1659
+ }
1660
+
1661
+ // src/core/upload/attachmentUpload.ts
1615
1662
  var AttachmentUploadError = class extends Error {
1616
1663
  constructor(code, message, details) {
1617
1664
  super(message);
@@ -1709,7 +1756,7 @@ async function requestUploadToken(client, metadata, context) {
1709
1756
  requestArgs.ttlSeconds = context.ttlSeconds;
1710
1757
  }
1711
1758
  const response = unwrapMcpToolPayload(await client.toolsCall(context.requestToolName, requestArgs));
1712
- if (!isRecord7(response)) {
1759
+ if (!isRecord8(response)) {
1713
1760
  throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response was not an object.");
1714
1761
  }
1715
1762
  const token = response.token;
@@ -1779,7 +1826,7 @@ function parsePutResponse(text) {
1779
1826
  } catch {
1780
1827
  throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not valid JSON.");
1781
1828
  }
1782
- if (!isRecord7(parsed)) {
1829
+ if (!isRecord8(parsed)) {
1783
1830
  throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not an object.");
1784
1831
  }
1785
1832
  if (typeof parsed.storageKey !== "string") {
@@ -1811,20 +1858,6 @@ function validatePutResult(token, metadata, putResult) {
1811
1858
  });
1812
1859
  }
1813
1860
  }
1814
- function unwrapMcpToolPayload(value) {
1815
- if (!isRecord7(value) || !Array.isArray(value.content)) {
1816
- return value;
1817
- }
1818
- const text = value.content.map((item) => isRecord7(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
1819
- if (!text) {
1820
- return value;
1821
- }
1822
- try {
1823
- return JSON.parse(text);
1824
- } catch {
1825
- return value;
1826
- }
1827
- }
1828
1861
  function emitProgress(context, stage, path, filename) {
1829
1862
  context.onProgress?.({
1830
1863
  stage,
@@ -1857,7 +1890,7 @@ var MIME_BY_EXTENSION = {
1857
1890
  ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1858
1891
  ".zip": "application/zip"
1859
1892
  };
1860
- function isRecord7(value) {
1893
+ function isRecord8(value) {
1861
1894
  return typeof value === "object" && value !== null && !Array.isArray(value);
1862
1895
  }
1863
1896
 
@@ -1981,9 +2014,256 @@ function renderAttachmentUploadResult(result, json) {
1981
2014
  return renderSuccess(lines.join("\n"), { json: false });
1982
2015
  }
1983
2016
 
2017
+ // src/adapters/cli/inboxPollCommands.ts
2018
+ import { mkdir as mkdir2, open, readFile as readFile4, rename as rename2 } from "fs/promises";
2019
+ import { dirname as dirname2 } from "path";
2020
+ var EXIT_NOTHING_NEW = 3;
2021
+ var LIST_NOTIFICATIONS = "mcp__ainecto__list_notifications";
2022
+ var LIST_AGENT_TODOS = "mcp__ainecto__list_agent_todos";
2023
+ function isInboxPollCommand(domain, action) {
2024
+ return [NOTIFICATIONS_POLL_COMMAND_PATH, NOTIFICATIONS_ACK_COMMAND_PATH, AGENT_TODOS_POLL_COMMAND_PATH].some(([d, a]) => d === domain && a === action);
2025
+ }
2026
+ var EXIT_USAGE = 2;
2027
+ var USAGE_ERRORS = /* @__PURE__ */ new Set(["UNKNOWN_FLAG", "FLAG_REQUIRES_VALUE", "MISSING_REQUIRED_FLAG", "INVALID_FLAG_VALUE"]);
2028
+ async function executeInboxPollCommand(options) {
2029
+ try {
2030
+ if (options.domain === "agent-todos") return await agentTodosPoll(options);
2031
+ if (options.action === "ack") return await notificationsAck(options);
2032
+ return await notificationsPoll(options);
2033
+ } catch (error) {
2034
+ if (error instanceof CliCommandError && USAGE_ERRORS.has(error.code)) {
2035
+ options.io.stderr.write(renderError(error, { json: options.json }));
2036
+ return EXIT_USAGE;
2037
+ }
2038
+ throw error;
2039
+ }
2040
+ }
2041
+ async function notificationsPoll(options) {
2042
+ const flags = parseFlags(options.argv, ["--cursor-file", "--kinds", "--workspace", "--project", "--limit"], ["--direct-only"]);
2043
+ const cursorPath = requireFlag(flags, "--cursor-file", "notifications poll");
2044
+ const filter = {
2045
+ kinds: flags.values["--kinds"]?.split(",").map((kind) => kind.trim()).filter(Boolean).sort(),
2046
+ ...scopeFilter(flags),
2047
+ directOnly: flags.switches.has("--direct-only")
2048
+ };
2049
+ if (filter.kinds?.length === 0) filter.kinds = void 0;
2050
+ const limit = flags.values["--limit"] === void 0 ? void 0 : positiveInt("--limit", flags.values["--limit"]);
2051
+ const saved = await readCursorFile(cursorPath);
2052
+ if (saved?.ackToken && saved.pendingItems?.length) {
2053
+ if (!sameFilter(saved.filter, filter)) {
2054
+ throw new CliCommandError(
2055
+ "PENDING_BATCH_OTHER_FILTER",
2056
+ `${cursorPath} holds an unacknowledged batch for a different filter. Run \`ainecto notifications ack --cursor-file ${cursorPath}\` after handling it, or use another cursor file for this filter.`,
2057
+ { cursorFile: cursorPath }
2058
+ );
2059
+ }
2060
+ options.io.stdout.write(renderSuccess({ items: saved.pendingItems, pending: true, stream: saved.stream }, { json: options.json }));
2061
+ return 0;
2062
+ }
2063
+ const result = await callTool(options.client, LIST_NOTIFICATIONS, {
2064
+ cursor: "saved",
2065
+ ...filter.kinds ? { kinds: filter.kinds } : {},
2066
+ ...scopeArgs(filter),
2067
+ ...filter.directOnly ? { directOnly: true } : {},
2068
+ ...limit !== void 0 ? { limit } : {}
2069
+ });
2070
+ const items = Array.isArray(result.items) ? result.items : [];
2071
+ const stream = typeof result.stream === "string" ? result.stream : void 0;
2072
+ if (typeof result.retryAfterSeconds === "number") {
2073
+ options.io.stderr.write(`Polled too soon; try again in ${result.retryAfterSeconds}s.
2074
+ `);
2075
+ return EXIT_NOTHING_NEW;
2076
+ }
2077
+ if (items.length === 0) {
2078
+ await writeCursorFile(cursorPath, { v: 1, filter, stream });
2079
+ if (options.json) options.io.stdout.write(renderSuccess({ items: [], stream }, { json: true }));
2080
+ return EXIT_NOTHING_NEW;
2081
+ }
2082
+ await writeCursorFile(cursorPath, {
2083
+ v: 1,
2084
+ filter,
2085
+ stream,
2086
+ ackToken: typeof result.ackToken === "string" ? result.ackToken : void 0,
2087
+ pendingItems: items
2088
+ });
2089
+ options.io.stdout.write(renderSuccess({ items, hasMore: result.hasMore === true, stream }, { json: options.json }));
2090
+ return 0;
2091
+ }
2092
+ async function notificationsAck(options) {
2093
+ const flags = parseFlags(options.argv, ["--cursor-file"], []);
2094
+ const cursorPath = requireFlag(flags, "--cursor-file", "notifications ack");
2095
+ const saved = await readCursorFile(cursorPath);
2096
+ if (!saved?.ackToken) {
2097
+ if (options.json) options.io.stdout.write(renderSuccess({ acked: false }, { json: true }));
2098
+ return EXIT_NOTHING_NEW;
2099
+ }
2100
+ await callTool(options.client, LIST_NOTIFICATIONS, {
2101
+ cursor: "saved",
2102
+ ack: saved.ackToken,
2103
+ ...saved.filter.kinds ? { kinds: saved.filter.kinds } : {},
2104
+ ...scopeArgs(saved.filter),
2105
+ ...saved.filter.directOnly ? { directOnly: true } : {},
2106
+ limit: 1
2107
+ });
2108
+ const acked = saved.pendingItems?.length ?? 0;
2109
+ await writeCursorFile(cursorPath, { v: 1, filter: saved.filter, stream: saved.stream });
2110
+ options.io.stdout.write(renderSuccess({ acked: true, items: acked }, { json: options.json }));
2111
+ return 0;
2112
+ }
2113
+ async function agentTodosPoll(options) {
2114
+ const flags = parseFlags(options.argv, ["--workspace", "--project", "--seen-file"], ["--only-new"]);
2115
+ const scope = scopeArgs(scopeFilter(flags));
2116
+ const onlyNew = flags.switches.has("--only-new");
2117
+ const seenPath = flags.values["--seen-file"];
2118
+ if (onlyNew && !seenPath) {
2119
+ throw new CliCommandError("MISSING_REQUIRED_FLAG", "--only-new needs --seen-file <path>.", { flag: "--seen-file" });
2120
+ }
2121
+ const items = [];
2122
+ let after;
2123
+ for (let page = 0; page < 20; page += 1) {
2124
+ const result = await callTool(options.client, LIST_AGENT_TODOS, { state: ["OPEN"], ...scope, limit: 50, ...after ? { after } : {} });
2125
+ const pageItems = Array.isArray(result.items) ? result.items.filter(isRecord9) : [];
2126
+ items.push(...pageItems);
2127
+ const last = pageItems[pageItems.length - 1];
2128
+ if (result.hasMore !== true || !last || typeof last.todoId !== "string") break;
2129
+ after = last.todoId;
2130
+ }
2131
+ let shown = items;
2132
+ if (onlyNew && seenPath) {
2133
+ const seen = new Set(await readSeenFile(seenPath));
2134
+ shown = items.filter((item) => typeof item.todoId === "string" && !seen.has(item.todoId));
2135
+ await writeJsonAtomic(seenPath, items.map((item) => item.todoId).filter((id) => typeof id === "string"));
2136
+ }
2137
+ if (shown.length === 0) {
2138
+ if (options.json) options.io.stdout.write(renderSuccess({ items: [] }, { json: true }));
2139
+ return EXIT_NOTHING_NEW;
2140
+ }
2141
+ options.io.stdout.write(renderSuccess({ items: shown }, { json: options.json }));
2142
+ return 0;
2143
+ }
2144
+ async function callTool(client, name, args) {
2145
+ const raw = await client.toolsCall(name, args);
2146
+ if (isToolError(raw)) {
2147
+ throw new CliCommandError("TOOL_ERROR", toolErrorText(raw), { tool: name });
2148
+ }
2149
+ const payload = unwrapMcpToolPayload(raw);
2150
+ if (!isRecord9(payload)) {
2151
+ throw new CliCommandError("MCP_PROTOCOL_ERROR", `${name} did not return an object.`, { tool: name });
2152
+ }
2153
+ return payload;
2154
+ }
2155
+ function scopeFilter(flags) {
2156
+ const workspaces = uuidSet(flags.lists["--workspace"]);
2157
+ const projects = uuidSet(flags.lists["--project"]);
2158
+ return {
2159
+ ...workspaces.length ? { workspaceUuids: workspaces } : {},
2160
+ ...projects.length ? { projectUuids: projects } : {}
2161
+ };
2162
+ }
2163
+ function uuidSet(values) {
2164
+ return [...new Set(values ?? [])].sort();
2165
+ }
2166
+ function scopeArgs(filter) {
2167
+ const workspaces = filter.workspaceUuids ?? [];
2168
+ const projects = filter.projectUuids ?? [];
2169
+ if (workspaces.length === 0 && projects.length === 1) return { projectUuid: projects[0] };
2170
+ return {
2171
+ ...workspaces.length ? { workspaceUuids: workspaces } : {},
2172
+ ...projects.length ? { projectUuids: projects } : {}
2173
+ };
2174
+ }
2175
+ function parseFlags(argv, valueFlags, switchFlags) {
2176
+ const values = {};
2177
+ const lists = {};
2178
+ const switches = /* @__PURE__ */ new Set();
2179
+ for (let index = 0; index < argv.length; index += 1) {
2180
+ const arg = argv[index];
2181
+ if (!arg) continue;
2182
+ if (valueFlags.includes(arg)) {
2183
+ const value = argv[index + 1];
2184
+ if (!value || value.startsWith("--")) {
2185
+ throw new CliCommandError("FLAG_REQUIRES_VALUE", `${arg} requires a value.`, { flag: arg });
2186
+ }
2187
+ values[arg] = value;
2188
+ (lists[arg] ??= []).push(value);
2189
+ index += 1;
2190
+ } else if (switchFlags.includes(arg)) {
2191
+ switches.add(arg);
2192
+ } else {
2193
+ throw new CliCommandError("UNKNOWN_FLAG", `Unknown argument ${arg}.`, { flag: arg });
2194
+ }
2195
+ }
2196
+ return { values, lists, switches };
2197
+ }
2198
+ function requireFlag(flags, flag, command) {
2199
+ const value = flags.values[flag];
2200
+ if (!value) throw new CliCommandError("MISSING_REQUIRED_FLAG", `${command} needs ${flag} <path>.`, { flag });
2201
+ return value;
2202
+ }
2203
+ function positiveInt(flag, value) {
2204
+ if (!value || !/^[1-9]\d*$/.test(value)) {
2205
+ throw new CliCommandError("INVALID_FLAG_VALUE", `${flag} must be a positive integer.`, { flag, value });
2206
+ }
2207
+ return Number.parseInt(value, 10);
2208
+ }
2209
+ function sameFilter(a, b) {
2210
+ return JSON.stringify(a.kinds ?? []) === JSON.stringify(b.kinds ?? []) && JSON.stringify(a.workspaceUuids ?? []) === JSON.stringify(b.workspaceUuids ?? []) && JSON.stringify(a.projectUuids ?? []) === JSON.stringify(b.projectUuids ?? []) && a.directOnly === b.directOnly;
2211
+ }
2212
+ function normalizeSavedFilter(saved) {
2213
+ const { projectUuid, ...rest } = saved;
2214
+ if (rest.projectUuids?.length || !projectUuid) return rest;
2215
+ return { ...rest, projectUuids: [projectUuid] };
2216
+ }
2217
+ async function readCursorFile(path) {
2218
+ const text = await readFile4(path, "utf8").catch((error) => {
2219
+ if (error.code === "ENOENT") return void 0;
2220
+ throw error;
2221
+ });
2222
+ if (text === void 0 || !text.trim()) return void 0;
2223
+ try {
2224
+ const parsed = JSON.parse(text);
2225
+ if (parsed?.v !== 1 || !isRecord9(parsed.filter)) throw new Error("shape");
2226
+ return { ...parsed, filter: normalizeSavedFilter(parsed.filter) };
2227
+ } catch {
2228
+ throw new CliCommandError("INVALID_CURSOR_FILE", `${path} is not a cursor file written by ainecto notifications poll.`, { cursorFile: path });
2229
+ }
2230
+ }
2231
+ async function writeCursorFile(path, value) {
2232
+ await writeJsonAtomic(path, value);
2233
+ }
2234
+ async function readSeenFile(path) {
2235
+ const text = await readFile4(path, "utf8").catch((error) => {
2236
+ if (error.code === "ENOENT") return void 0;
2237
+ throw error;
2238
+ });
2239
+ if (!text?.trim()) return [];
2240
+ try {
2241
+ const parsed = JSON.parse(text);
2242
+ return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
2243
+ } catch {
2244
+ throw new CliCommandError("INVALID_SEEN_FILE", `${path} is not a seen file written by ainecto agent-todos poll.`, { seenFile: path });
2245
+ }
2246
+ }
2247
+ async function writeJsonAtomic(path, value) {
2248
+ await mkdir2(dirname2(path), { recursive: true });
2249
+ const temp = `${path}.${process.pid}.tmp`;
2250
+ const handle = await open(temp, "w", 384);
2251
+ try {
2252
+ await handle.writeFile(`${JSON.stringify(value, null, 2)}
2253
+ `, "utf8");
2254
+ await handle.sync();
2255
+ } finally {
2256
+ await handle.close();
2257
+ }
2258
+ await rename2(temp, path);
2259
+ }
2260
+ function isRecord9(value) {
2261
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2262
+ }
2263
+
1984
2264
  // src/adapters/cli/initCommand.ts
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";
2265
+ import { chmod as chmod2, mkdir as mkdir3, rename as rename3, rm as rm2, rmdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
2266
+ import { dirname as dirname4, join as join3, relative, resolve as resolve2, sep as sep2 } from "path";
1987
2267
 
1988
2268
  // src/core/harness/agentTargets.ts
1989
2269
  import { basename as basename2 } from "path";
@@ -1992,8 +2272,8 @@ var AGENT_TARGETS = [
1992
2272
  { id: "claude-code", label: "Claude Code", path: ".mcp.json" },
1993
2273
  { id: "cursor", label: "Cursor", path: ".cursor/mcp.json" }
1994
2274
  ];
1995
- function buildServerEntry(input) {
1996
- const args = ["-y", PACKAGE_NAME, "--role", input.role];
2275
+ function buildServerEntry(input = {}) {
2276
+ const args = ["-y", input.packageSpec ?? bridgePackageSpec()];
1997
2277
  if (input.env && input.env !== "prod") {
1998
2278
  args.push("--env", input.env);
1999
2279
  }
@@ -2002,13 +2282,15 @@ function buildServerEntry(input) {
2002
2282
  }
2003
2283
  return { command: "npx", args };
2004
2284
  }
2005
- function readRoleFromEntry(entry) {
2006
- if (!isRecord8(entry) || !Array.isArray(entry.args)) {
2285
+ function entryCarriesRole(entry) {
2286
+ return isRecord10(entry) && Array.isArray(entry.args) && entry.args.includes("--role");
2287
+ }
2288
+ function pinnedBridgeVersion(entry) {
2289
+ if (!isRecord10(entry) || !Array.isArray(entry.args)) {
2007
2290
  return void 0;
2008
2291
  }
2009
- const index = entry.args.indexOf("--role");
2010
- const value = index >= 0 ? entry.args[index + 1] : void 0;
2011
- return typeof value === "string" && HARNESS_ROLES.includes(value) ? value : void 0;
2292
+ const pinned = entry.args.find((arg) => typeof arg === "string" && arg.startsWith(`${PACKAGE_NAME}@`));
2293
+ return pinned?.slice(PACKAGE_NAME.length + 1);
2012
2294
  }
2013
2295
  function readServerEntry(existing) {
2014
2296
  if (existing === void 0) {
@@ -2017,13 +2299,13 @@ function readServerEntry(existing) {
2017
2299
  try {
2018
2300
  const parsed = JSON.parse(existing);
2019
2301
  const servers = parsed?.mcpServers;
2020
- return isRecord8(servers) ? servers[SERVER_NAME] : void 0;
2302
+ return isRecord10(servers) ? servers[SERVER_NAME] : void 0;
2021
2303
  } catch {
2022
2304
  return void 0;
2023
2305
  }
2024
2306
  }
2025
2307
  function isOurBridge(entry) {
2026
- if (!isRecord8(entry)) {
2308
+ if (!isRecord10(entry)) {
2027
2309
  return false;
2028
2310
  }
2029
2311
  const { command, args } = entry;
@@ -2062,32 +2344,12 @@ function removeServerEntry(existing, restore) {
2062
2344
  const emptied = Object.keys(servers).length === 0 && otherKeys.length === 0;
2063
2345
  return { content: stringify({ ...root, mcpServers: servers }), removed, emptied };
2064
2346
  }
2065
- function codexAddCommand(input) {
2066
- const entry = buildServerEntry(input);
2067
- return `codex mcp add ${SERVER_NAME} -- ${entry.command} ${entry.args.join(" ")}`;
2068
- }
2069
- function codexProfileSetup(input) {
2070
- const entry = buildServerEntry(input);
2071
- return [
2072
- `Per-session roles in Codex use profiles. Put this in $CODEX_HOME/${input.role}.config.toml:`,
2073
- "",
2074
- ` [mcp_servers.${SERVER_NAME}]`,
2075
- ` command = "${entry.command}"`,
2076
- ` args = ${JSON.stringify(entry.args)}`,
2077
- "",
2078
- `Then start that session with: codex -p ${input.role}`,
2079
- "A profile entry with the same server name replaces the global one, so the session still sees exactly one."
2080
- ];
2081
- }
2082
- function codexRemoveCommand() {
2083
- return `codex mcp remove ${SERVER_NAME}`;
2084
- }
2085
2347
  function readServers(root) {
2086
2348
  const servers = root.mcpServers;
2087
2349
  if (servers === void 0) {
2088
2350
  return {};
2089
2351
  }
2090
- if (!isRecord8(servers)) {
2352
+ if (!isRecord10(servers)) {
2091
2353
  throw new Error(
2092
2354
  '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.'
2093
2355
  );
@@ -2106,7 +2368,7 @@ function parseConfigObject(existing) {
2106
2368
  `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)})`
2107
2369
  );
2108
2370
  }
2109
- if (!isRecord8(parsed)) {
2371
+ if (!isRecord10(parsed)) {
2110
2372
  throw new Error("Existing MCP config must be a JSON object; it was left untouched.");
2111
2373
  }
2112
2374
  return parsed;
@@ -2115,7 +2377,7 @@ function stringify(value) {
2115
2377
  return `${JSON.stringify(value, null, 2)}
2116
2378
  `;
2117
2379
  }
2118
- function isRecord8(value) {
2380
+ function isRecord10(value) {
2119
2381
  return typeof value === "object" && value !== null && !Array.isArray(value);
2120
2382
  }
2121
2383
 
@@ -2128,11 +2390,7 @@ var HARNESS_DOC_PATH = `${HARNESS_DIR}/HARNESS.md`;
2128
2390
  var HARNESS_CONFIG_PATH = `${HARNESS_DIR}/config.json`;
2129
2391
  var HARNESS_RECORD_PATH = `${HARNESS_DIR}/init-record.json`;
2130
2392
  function renderHarnessDoc(input, template) {
2131
- return fillTemplate(template ?? DEFAULT_HARNESS_DOC, {
2132
- projectName: input.projectName,
2133
- projectUuid: input.projectUuid,
2134
- endpoint: input.endpoint
2135
- });
2393
+ return fillTemplate(template ?? DEFAULT_HARNESS_DOC, { endpoint: input.endpoint });
2136
2394
  }
2137
2395
  function fillTemplate(template, values) {
2138
2396
  return template.replace(/\{\{([a-zA-Z0-9_]+)}}/g, (whole, name) => Object.prototype.hasOwnProperty.call(values, name) ? values[name] : whole);
@@ -2140,15 +2398,21 @@ function fillTemplate(template, values) {
2140
2398
  var DOC_KEY = "HARNESS.DOC";
2141
2399
  var AGENT_NOTE_KEY = "HARNESS.AGENT_NOTE";
2142
2400
  var REQUIRED_VARIABLES = {
2143
- [DOC_KEY]: ["projectName", "projectUuid", "endpoint"],
2401
+ [DOC_KEY]: ["endpoint"],
2144
2402
  [AGENT_NOTE_KEY]: ["harnessDocPath", "serverName"]
2145
2403
  };
2146
2404
  function documentContractProblem(key, body) {
2147
- for (const name of REQUIRED_VARIABLES[key] ?? []) {
2405
+ const known = REQUIRED_VARIABLES[key] ?? [];
2406
+ for (const name of known) {
2148
2407
  if (!body.includes(`{{${name}}}`)) {
2149
2408
  return `${key} is missing the required {{${name}}} placeholder`;
2150
2409
  }
2151
2410
  }
2411
+ for (const [, name] of body.matchAll(/\{\{([a-zA-Z0-9_]+)}}/g)) {
2412
+ if (!known.includes(name)) {
2413
+ return `${key} has a {{${name}}} placeholder this CLI does not fill`;
2414
+ }
2415
+ }
2152
2416
  if (key === AGENT_NOTE_KEY) {
2153
2417
  return managedBlockProblem(key, body);
2154
2418
  }
@@ -2156,58 +2420,32 @@ function documentContractProblem(key, body) {
2156
2420
  }
2157
2421
  var DEFAULT_HARNESS_DOC = `# AiNecto Harness
2158
2422
 
2159
- This repository is wired to an AiNecto project. The design source of truth lives in AiNecto,
2160
- not in this repository's files.
2423
+ This repository works with AiNecto. The design source of truth \u2014 requirements, the ERD,
2424
+ diagrams, documents, and task boards \u2014 lives in AiNecto, not in this repository's files.
2161
2425
 
2162
- - Project: **{{projectName}}** (\`{{projectUuid}}\`)
2163
2426
  - MCP endpoint: {{endpoint}}
2164
2427
 
2165
- ## One session, one role
2166
-
2167
- A session picks exactly one role and keeps it for the whole session. The role comes from the
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.
2173
-
2174
- | Role | Owns | Must not |
2175
- | --- | --- | --- |
2176
- | Design | Requirements, ERD, domain and module boundaries, dependency direction, tasks | Write product or test code |
2177
- | Development | Product code, following the approved design | Change requirements, ERD, architecture, or data ownership |
2178
- | Test | Test scenarios and test code, running them and reading results | Change product code to make a test pass; change the design |
2179
- | Validation | Judging the result against the design and the rules | Change anything; it reports PASS or FAIL with evidence |
2180
-
2181
- Every role can **read** the whole source of truth. Reading the design is how the other roles
2182
- do their job. A session that cannot do something reports it in the session, to you.
2183
-
2184
- ## When you are blocked
2428
+ ## What you can do
2185
2429
 
2186
- If the work needs something your role cannot do, **stop and say so**. Do not look for a way
2187
- around it: not by editing files the tools would have written, not by asking for the role to be
2188
- changed mid-session, not by deferring the decision into the implementation.
2430
+ Every AI session sees the same AiNecto tools. What a tool may change is decided by the signed-in
2431
+ user's AiNecto permissions on the workspace, project, or document it touches \u2014 the server refuses
2432
+ what the user may not do. A refusal is an answer: report it to the user instead of looking for a
2433
+ way around it.
2189
2434
 
2190
- The work needs a design change.
2191
- This is a Development Session, so I cannot make it.
2192
- Settle it in a Design Session, then continue here.
2435
+ Find the project and document for the task with the tools (\`list_projects\`, \`search_documents\`,
2436
+ \`get_document\`) \u2014 this repository is not bound to one project.
2193
2437
 
2194
- Validation reports \`PASS\`, or \`FAIL\` with the rule violated, where it happened, the evidence,
2195
- and which session should pick it up next.
2438
+ ## When a tool is missing
2196
2439
 
2197
- ## Not every change needs four sessions
2440
+ If a result names a tool that is not in your tool list, this session started before that tool
2441
+ was added. Start a new session, or call it from the repository with
2442
+ \`npx -y ainecto@latest tools call <tool> '<json>'\`.
2198
2443
 
2199
- A small change that fits the design already recorded in AiNecto starts at Development. The rule
2200
- is not "always run four steps" \u2014 it is that when a step is needed, it does not happen inside
2201
- another role's session.
2444
+ ## Commands
2202
2445
 
2203
- ## Switching roles
2204
-
2205
- Change the role in the MCP server configuration and start a new session:
2206
-
2207
- ${AGENT_TARGETS.map((target) => ` ${target.label.padEnd(12)} ${target.path}`).join("\n")}
2208
- Codex global config \u2014 use a profile: codex -p <role>, or set ${ROLE_ENV_VAR}
2209
-
2210
- Or run \`ainecto init --role <design|development|test|validation>\` to rewrite them.
2446
+ npx -y ainecto@latest setup connect this machine's coding agents (once per machine)
2447
+ npx -y ainecto@latest status who is signed in, and what is connected
2448
+ npx -y ainecto@latest doctor what is missing, and the command that fixes it
2211
2449
 
2212
2450
  ---
2213
2451
  Generated by \`ainecto init\`. Remove everything it wrote with \`ainecto init --undo\`.
@@ -2221,14 +2459,11 @@ function renderAgentNote(template) {
2221
2459
  var DEFAULT_AGENT_NOTE = `${MARKER_BEGIN}
2222
2460
  ## AiNecto Harness
2223
2461
 
2224
- This repository uses AiNecto as its design source of truth, and each session has one role
2225
- (Design / Development / Test / Validation). Read \`{{harnessDocPath}}\` before doing work that
2226
- touches requirements, the ERD, or architecture.
2462
+ This repository uses AiNecto as its design source of truth. Read \`{{harnessDocPath}}\` before doing
2463
+ work that touches requirements, the ERD, or architecture.
2227
2464
 
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,
2231
- stop and report which session is required.
2465
+ AiNecto tools come from the \`{{serverName}}\` MCP server. Every session sees the same tools; the
2466
+ signed-in user's AiNecto permissions decide what they may change, and the server refuses the rest.
2232
2467
  ${MARKER_END}`;
2233
2468
  function managedBlockProblem(key, body) {
2234
2469
  const trimmed = body.trim();
@@ -2254,14 +2489,14 @@ function managedBlockProblem(key, body) {
2254
2489
  return void 0;
2255
2490
  }
2256
2491
  function hasUnclosedFence(content) {
2257
- let open;
2492
+ let open2;
2258
2493
  for (const line of content.split("\n")) {
2259
2494
  const fence = fenceOf(line);
2260
2495
  if (!fence) continue;
2261
- if (!open) open = { char: fence.char, length: fence.length };
2262
- else if (fence.char === open.char && fence.length >= open.length && fence.info === "") open = void 0;
2496
+ if (!open2) open2 = { char: fence.char, length: fence.length };
2497
+ else if (fence.char === open2.char && fence.length >= open2.length && fence.info === "") open2 = void 0;
2263
2498
  }
2264
- return open !== void 0;
2499
+ return open2 !== void 0;
2265
2500
  }
2266
2501
  function canonicalManagedBlock(block) {
2267
2502
  return block.trim();
@@ -2321,24 +2556,24 @@ function hit(at, marker) {
2321
2556
  return at >= 0 ? { at, length: marker.length } : void 0;
2322
2557
  }
2323
2558
  function isInsideFencedBlock(content, index) {
2324
- let open;
2559
+ let open2;
2325
2560
  let offset = 0;
2326
2561
  for (const line of content.split("\n")) {
2327
2562
  const nextOffset = offset + line.length + 1;
2328
2563
  if (index < nextOffset) {
2329
- return open !== void 0;
2564
+ return open2 !== void 0;
2330
2565
  }
2331
2566
  const fence = fenceOf(line);
2332
2567
  if (fence) {
2333
- if (!open) {
2334
- open = { char: fence.char, length: fence.length };
2335
- } else if (fence.char === open.char && fence.length >= open.length && fence.info === "") {
2336
- open = void 0;
2568
+ if (!open2) {
2569
+ open2 = { char: fence.char, length: fence.length };
2570
+ } else if (fence.char === open2.char && fence.length >= open2.length && fence.info === "") {
2571
+ open2 = void 0;
2337
2572
  }
2338
2573
  }
2339
2574
  offset = nextOffset;
2340
2575
  }
2341
- return open !== void 0;
2576
+ return open2 !== void 0;
2342
2577
  }
2343
2578
  function fenceOf(line) {
2344
2579
  const match = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
@@ -2665,245 +2900,78 @@ function isStringArray(value) {
2665
2900
 
2666
2901
  // src/core/harness/initPlan.ts
2667
2902
  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" };
2903
+ var NOTE_TARGETS = [
2904
+ { path: "AGENTS.md", createIfMissing: true },
2905
+ { path: "CLAUDE.md", createIfMissing: false }
2906
+ ];
2907
+ var MANAGED_PATHS = [
2908
+ ...AGENT_TARGETS.map((target) => target.path),
2909
+ ...NOTE_TARGETS.map((target) => target.path),
2910
+ HARNESS_DOC_PATH,
2911
+ HARNESS_CONFIG_PATH,
2912
+ HARNESS_RECORD_PATH
2913
+ ];
2914
+ function ownedJson(value, present) {
2915
+ return present ? { at: "json", value } : ABSENT;
2916
+ }
2917
+ function ownedText(text) {
2918
+ return text === void 0 ? ABSENT : { at: "text", text };
2919
+ }
2920
+ function sameOwned(left, right) {
2921
+ if (left.at !== right.at) {
2922
+ return false;
2684
2923
  }
2685
- const entry = servers[SERVER_NAME];
2686
- if (!isOurBridge(entry)) {
2687
- return { kind: "absent" };
2924
+ if (left.at === "absent") {
2925
+ return true;
2688
2926
  }
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` };
2927
+ if (left.at === "text" && right.at === "text") {
2928
+ return left.text === right.text;
2692
2929
  }
2693
- return { kind: "role", role };
2930
+ return left.at === "json" && right.at === "json" && stableStringify2(left.value) === stableStringify2(right.value);
2694
2931
  }
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 };
2932
+ function contentFingerprint(content) {
2933
+ return createHash4("sha256").update(content).digest("hex");
2707
2934
  }
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
- }
2935
+ function serialize(body) {
2936
+ return `${JSON.stringify(body, null, 2)}
2937
+ `;
2744
2938
  }
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}).`;
2939
+ function fingerprintFor(path, content) {
2940
+ if (!path.endsWith(".json")) {
2941
+ return contentFingerprint(content);
2750
2942
  }
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
2943
  try {
2755
- return await realpath(path);
2944
+ return contentFingerprint(stableStringify2(JSON.parse(content)));
2756
2945
  } catch {
2757
- return resolve(path);
2946
+ return contentFingerprint(content);
2758
2947
  }
2759
2948
  }
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;
2949
+ function parseJsonObject(content) {
2765
2950
  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
2837
- var NOTE_TARGETS = [
2838
- { path: "AGENTS.md", createIfMissing: true },
2839
- { path: "CLAUDE.md", createIfMissing: false }
2840
- ];
2841
- var MANAGED_PATHS = [
2842
- ...AGENT_TARGETS.map((target) => target.path),
2843
- ...NOTE_TARGETS.map((target) => target.path),
2844
- HARNESS_DOC_PATH,
2845
- HARNESS_CONFIG_PATH,
2846
- HARNESS_RECORD_PATH
2847
- ];
2848
- function ownedJson(value, present) {
2849
- return present ? { at: "json", value } : ABSENT;
2850
- }
2851
- function ownedText(text) {
2852
- return text === void 0 ? ABSENT : { at: "text", text };
2853
- }
2854
- function sameOwned(left, right) {
2855
- if (left.at !== right.at) {
2856
- return false;
2857
- }
2858
- if (left.at === "absent") {
2859
- return true;
2860
- }
2861
- if (left.at === "text" && right.at === "text") {
2862
- return left.text === right.text;
2863
- }
2864
- return left.at === "json" && right.at === "json" && stableStringify2(left.value) === stableStringify2(right.value);
2865
- }
2866
- function contentFingerprint(content) {
2867
- return createHash4("sha256").update(content).digest("hex");
2868
- }
2869
- function serialize(body) {
2870
- return `${JSON.stringify(body, null, 2)}
2871
- `;
2872
- }
2873
- function fingerprintFor(path, content) {
2874
- if (!path.endsWith(".json")) {
2875
- return contentFingerprint(content);
2876
- }
2877
- try {
2878
- return contentFingerprint(stableStringify2(JSON.parse(content)));
2879
- } catch {
2880
- return contentFingerprint(content);
2881
- }
2882
- }
2883
- function parseJsonObject(content) {
2884
- try {
2885
- const parsed = JSON.parse(content);
2886
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
2887
- } catch {
2888
- return void 0;
2951
+ const parsed = JSON.parse(content);
2952
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
2953
+ } catch {
2954
+ return void 0;
2889
2955
  }
2890
2956
  }
2891
2957
  function configBodyOf(values) {
2892
2958
  return {
2893
- version: 1,
2894
- project: { uuid: values.projectUuid, name: values.projectName },
2959
+ version: 2,
2895
2960
  endpoint: values.endpoint,
2896
2961
  generatedBy: values.cliVersion,
2897
2962
  promptVersions: values.versions
2898
2963
  };
2899
2964
  }
2900
2965
  var OUR_CONFIG_FIELDS = Object.keys(configBodyOf({
2901
- projectUuid: "",
2902
- projectName: "",
2903
2966
  endpoint: "",
2904
2967
  cliVersion: "",
2905
2968
  versions: {}
2906
2969
  }));
2970
+ var RETIRED_CONFIG_FIELDS = ["project"];
2971
+ function ownedConfigFields(contract) {
2972
+ const written = contract?.writtenFields ?? {};
2973
+ return [...OUR_CONFIG_FIELDS, ...RETIRED_CONFIG_FIELDS.filter((field) => hasField(written, field))];
2974
+ }
2907
2975
  var hasField = (object, field) => Object.prototype.hasOwnProperty.call(object, field);
2908
2976
  function initRefusal(files, previous) {
2909
2977
  const state = previous ?? readRecord(files.get(HARNESS_RECORD_PATH));
@@ -2926,16 +2994,10 @@ function planInit(input) {
2926
2994
  const previous = input.previous ?? readRecord(input.files.get(HARNESS_RECORD_PATH));
2927
2995
  const refusal = initRefusal(input.files, previous);
2928
2996
  if (refusal !== void 0) {
2929
- return {
2930
- writes: [],
2931
- deletes: [],
2932
- notes: [],
2933
- codexCommand: codexAddCommand({ role: input.role, env: input.env, endpoint: input.endpoint }),
2934
- refusal
2935
- };
2997
+ return { writes: [], deletes: [], notes: [], refusal };
2936
2998
  }
2937
2999
  const carriedRecord = previous.state === "valid" ? previous.record : emptyRecord();
2938
- const entry = buildServerEntry({ role: input.role, env: input.env, endpoint: input.endpoint });
3000
+ const entry = buildServerEntry({ env: input.env, endpoint: input.endpoint, packageSpec: input.packageSpec });
2939
3001
  const writes = [];
2940
3002
  const notes = [];
2941
3003
  const record = {
@@ -2958,15 +3020,14 @@ function planInit(input) {
2958
3020
  for (const target of AGENT_TARGETS) {
2959
3021
  const existing = input.files.get(target.path);
2960
3022
  const currentEntry = existing === void 0 ? void 0 : readServerEntry(existing);
3023
+ if (!input.mcp && !isOurBridge(currentEntry)) {
3024
+ continue;
3025
+ }
2961
3026
  const prior = priorOf(target.path, ownedJson(currentEntry, currentEntry !== void 0));
2962
3027
  const merged = mergeServerEntry(existing, entry);
2963
3028
  if (existing === merged.content) {
2964
3029
  continue;
2965
3030
  }
2966
- const before = previousRole(target.path, existing);
2967
- if (merged.replaced && before && before !== input.role) {
2968
- notes.push(`${target.path}: role ${before} \u2192 ${input.role}`);
2969
- }
2970
3031
  const original = prior?.original ?? ownedJson(merged.replacedEntry, merged.replaced);
2971
3032
  if (prior === void 0 && original.at !== "absent") {
2972
3033
  notes.push(`${target.path}: your existing "${SERVER_NAME}" entry was saved and will be restored by --undo`);
@@ -2982,11 +3043,7 @@ function planInit(input) {
2982
3043
  }
2983
3044
  const existingDoc = input.files.get(HARNESS_DOC_PATH);
2984
3045
  const priorDoc = priorOf(HARNESS_DOC_PATH, ownedText(existingDoc));
2985
- const docContent = renderHarnessDoc({
2986
- projectName: input.projectName,
2987
- projectUuid: input.projectUuid,
2988
- endpoint: input.endpoint
2989
- }, input.documents?.doc);
3046
+ const docContent = renderHarnessDoc({ endpoint: input.endpoint }, input.documents?.doc);
2990
3047
  const docOriginal = priorDoc?.original ?? ownedText(existingDoc);
2991
3048
  if (priorDoc === void 0 && docOriginal.at !== "absent") {
2992
3049
  notes.push(`${HARNESS_DOC_PATH} already existed; its original was saved and --undo will restore it.`);
@@ -3048,8 +3105,6 @@ function planInit(input) {
3048
3105
  }
3049
3106
  const existingConfig = input.files.get(HARNESS_CONFIG_PATH);
3050
3107
  const ourFields = configBodyOf({
3051
- projectUuid: input.projectUuid,
3052
- projectName: input.projectName,
3053
3108
  endpoint: input.endpoint,
3054
3109
  cliVersion: input.cliVersion,
3055
3110
  // ★어느 판의 규칙으로 썼나. 이게 없으면 「무엇이 적혀 있었나」를 나중에 못 되짚는다.
@@ -3073,7 +3128,24 @@ function planInit(input) {
3073
3128
  notes.push(`${HARNESS_CONFIG_PATH}: you changed ${field} \u2014 ainecto now treats your value as the one to put back.`);
3074
3129
  }
3075
3130
  }
3076
- const mergedConfig = serialize({ ...theirConfig ?? {}, ...ourFields });
3131
+ const retained = { ...theirConfig ?? {} };
3132
+ for (const field of RETIRED_CONFIG_FIELDS) {
3133
+ const priorWritten = priorConfig?.writtenFields;
3134
+ if (priorWritten === void 0 || !hasField(priorWritten, field) || !hasField(retained, field)) {
3135
+ continue;
3136
+ }
3137
+ if (stableStringify2(retained[field]) !== stableStringify2(priorWritten[field])) {
3138
+ notes.push(`${HARNESS_CONFIG_PATH}: you changed ${field} \u2014 left as you set it (ainecto no longer writes it).`);
3139
+ continue;
3140
+ }
3141
+ const original = priorConfig?.originalFields?.[field];
3142
+ if (original?.at === "json") {
3143
+ retained[field] = original.value;
3144
+ } else {
3145
+ delete retained[field];
3146
+ }
3147
+ }
3148
+ const mergedConfig = serialize({ ...retained, ...ourFields });
3077
3149
  record.files[HARNESS_CONFIG_PATH] = {
3078
3150
  scope: "fields",
3079
3151
  createdByUs: priorConfig?.createdByUs ?? existingConfig === void 0,
@@ -3100,14 +3172,7 @@ function planInit(input) {
3100
3172
  existed: existingRecord !== void 0
3101
3173
  });
3102
3174
  }
3103
- return {
3104
- writes,
3105
- deletes: [],
3106
- codexCommand: codexAddCommand({ role: input.role, env: input.env, endpoint: input.endpoint }),
3107
- codexProfile: codexProfileSetup({ role: input.role, env: input.env, endpoint: input.endpoint }),
3108
- notes,
3109
- record
3110
- };
3175
+ return { writes, deletes: [], notes, record };
3111
3176
  }
3112
3177
  function planUndo(input) {
3113
3178
  const state = input.record ?? readRecord(input.files.get(HARNESS_RECORD_PATH));
@@ -3115,20 +3180,19 @@ function planUndo(input) {
3115
3180
  const deletes = [];
3116
3181
  const notes = [];
3117
3182
  const tail = () => {
3118
- notes.push(`Codex keeps its own global config \u2014 remove it with: ${codexRemoveCommand()}`);
3119
- notes.push(`If you set ${ROLE_ENV_VAR} anywhere, unset it too.`);
3183
+ notes.push("Agent connections that `ainecto setup` made on this machine are removed with `ainecto setup --undo`.");
3120
3184
  };
3121
3185
  if (state.state === "absent") {
3122
3186
  notes.push(`No record of a previous init in ${HARNESS_RECORD_PATH} \u2014 no local file was changed.`);
3123
3187
  tail();
3124
- return { writes, deletes, codexCommand: codexRemoveCommand(), notes };
3188
+ return { writes, deletes, notes };
3125
3189
  }
3126
3190
  if (state.state !== "valid") {
3127
3191
  notes.push(
3128
3192
  `${state.reason}. Nothing was changed \u2014 that file is kept because it may hold backups --undo still needs. Restore it, or finish the undo with the CLI version that wrote it.`
3129
3193
  );
3130
3194
  tail();
3131
- return { writes, deletes, codexCommand: codexRemoveCommand(), notes };
3195
+ return { writes, deletes, notes };
3132
3196
  }
3133
3197
  const record = state.record;
3134
3198
  const remaining = { ...record.files };
@@ -3162,7 +3226,7 @@ function planUndo(input) {
3162
3226
  }
3163
3227
  }
3164
3228
  const removableDirectories = parentsOf(deletes).filter((directory) => record.createdDirectories.includes(directory));
3165
- return { writes, deletes, codexCommand: codexRemoveCommand(), notes, removableDirectories };
3229
+ return { writes, deletes, notes, removableDirectories };
3166
3230
  }
3167
3231
  function unexplainedMarkers(existing, contract) {
3168
3232
  const recorded = [...contract.foreignBlocks ?? []];
@@ -3308,7 +3372,8 @@ function undoFields(path, contract, existing) {
3308
3372
  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.`
3309
3373
  };
3310
3374
  }
3311
- const contractProblem = fieldsContractProblem(contract, OUR_CONFIG_FIELDS);
3375
+ const owned = ownedConfigFields(contract);
3376
+ const contractProblem = fieldsContractProblem(contract, owned);
3312
3377
  if (contractProblem !== void 0) {
3313
3378
  return {
3314
3379
  kind: "unresolved",
@@ -3320,7 +3385,7 @@ function undoFields(path, contract, existing) {
3320
3385
  const next = {};
3321
3386
  const kept = [];
3322
3387
  for (const [field, value] of Object.entries(parsed)) {
3323
- if (!OUR_CONFIG_FIELDS.includes(field)) {
3388
+ if (!owned.includes(field)) {
3324
3389
  next[field] = value;
3325
3390
  continue;
3326
3391
  }
@@ -3375,14 +3440,8 @@ function parentsOf(paths) {
3375
3440
  }
3376
3441
  return [...directories].sort((left, right) => right.length - left.length);
3377
3442
  }
3378
- function previousRole(path, existing) {
3379
- const read = readConfigRole(path, existing);
3380
- return read.kind === "role" ? read.role : void 0;
3381
- }
3382
3443
 
3383
3444
  // src/core/harness/documentFetch.ts
3384
- var DOC_KEY2 = "HARNESS.DOC";
3385
- var AGENT_NOTE_KEY2 = "HARNESS.AGENT_NOTE";
3386
3445
  async function fetchHarnessDocuments(input) {
3387
3446
  const url = new URL(`${input.endpoint.replace(/\/$/, "")}/harness/documents`);
3388
3447
  assertTrustedEndpointUrl(url, "harness documents URL");
@@ -3405,7 +3464,7 @@ async function fetchHarnessDocuments(input) {
3405
3464
  const versions = {};
3406
3465
  const fallbacks = {};
3407
3466
  const bodies = {};
3408
- for (const key of [DOC_KEY2, AGENT_NOTE_KEY2]) {
3467
+ for (const key of [DOC_KEY, AGENT_NOTE_KEY]) {
3409
3468
  const entry = documents[key];
3410
3469
  const problem = usableBodyProblem(key, entry);
3411
3470
  if (problem) {
@@ -3417,7 +3476,7 @@ async function fetchHarnessDocuments(input) {
3417
3476
  versions[key] = entry.version;
3418
3477
  }
3419
3478
  }
3420
- return { doc: bodies[DOC_KEY2], agentNote: bodies[AGENT_NOTE_KEY2], versions, fallbacks };
3479
+ return { doc: bodies[DOC_KEY], agentNote: bodies[AGENT_NOTE_KEY], versions, fallbacks };
3421
3480
  } catch (error) {
3422
3481
  return {
3423
3482
  versions: {},
@@ -3433,95 +3492,75 @@ function usableBodyProblem(key, entry) {
3433
3492
  return documentContractProblem(key, entry.body);
3434
3493
  }
3435
3494
 
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";
3495
+ // src/core/harness/repositoryFiles.ts
3496
+ import { lstat, readFile as readFile5, realpath } from "fs/promises";
3497
+ import { dirname as dirname3, join as join2, resolve, sep } from "path";
3498
+ async function findGitRoot(cwd) {
3499
+ for (let at = resolve(cwd); ; at = dirname3(at)) {
3500
+ try {
3501
+ await lstat(join2(at, ".git"));
3502
+ return at;
3503
+ } catch {
3504
+ }
3505
+ if (dirname3(at) === at) {
3506
+ return void 0;
3507
+ }
3442
3508
  }
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
3509
  }
3451
- function isInteractive(io, json, env = process.env) {
3452
- return io.stdin?.isTTY === true && io.stdout?.isTTY === true && !json && agentEnvironment(env) === void 0;
3510
+ async function readManagedFile(root, path) {
3511
+ await assertInsideRepository(root, path);
3512
+ return readIfExists(join2(root, path));
3453
3513
  }
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();
3514
+ async function readIfExists(absolutePath) {
3515
+ try {
3516
+ return await readFile5(absolutePath, "utf8");
3517
+ } catch (error) {
3518
+ if (isNodeError2(error, "ENOENT")) {
3519
+ return void 0;
3478
3520
  }
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
- `);
3521
+ throw error;
3501
3522
  }
3502
3523
  }
3503
-
3504
- // src/adapters/cli/initCommand.ts
3505
- function singleRole(roles) {
3506
- return roles.length === 1 ? roles[0] : void 0;
3507
- }
3508
- async function executeInitCommand(options) {
3524
+ async function assertInsideRepository(root, relativePath) {
3525
+ const absolutePath = join2(root, relativePath);
3526
+ const realRoot = await realpath(root);
3527
+ let realParent;
3509
3528
  try {
3510
- return await runInit(options);
3529
+ realParent = await realpath(dirname3(absolutePath));
3511
3530
  } catch (error) {
3512
- if (error instanceof PromptCancelled) {
3513
- options.io.stderr.write(`${error.message}
3514
- `);
3515
- return 130;
3531
+ if (isNodeError2(error, "ENOENT")) {
3532
+ return;
3516
3533
  }
3517
3534
  throw error;
3518
- } finally {
3519
- options.prompter?.close();
3520
3535
  }
3536
+ if (realParent !== realRoot && !realParent.startsWith(realRoot + sep)) {
3537
+ throw new Error(
3538
+ `${relativePath} resolves outside the repository (${realParent}); refusing to touch it.`
3539
+ );
3540
+ }
3541
+ try {
3542
+ if ((await lstat(absolutePath)).isSymbolicLink()) {
3543
+ throw new Error(`${relativePath} is a symbolic link; refusing to write through it.`);
3544
+ }
3545
+ } catch (error) {
3546
+ if (!isNodeError2(error, "ENOENT")) {
3547
+ throw error;
3548
+ }
3549
+ }
3550
+ }
3551
+ function isNodeError2(error, code) {
3552
+ return typeof error === "object" && error !== null && error.code === code;
3521
3553
  }
3522
- async function runInit(initialOptions) {
3523
- let options = initialOptions;
3554
+
3555
+ // src/adapters/cli/initCommand.ts
3556
+ async function executeInitCommand(options) {
3524
3557
  const args = parseInitArgs(options.argv);
3558
+ if (args.ignored.length > 0) {
3559
+ options.io.stderr.write(
3560
+ `Ignored ${args.ignored.join(", ")}: init no longer asks for a role or a project \u2014 every session sees the same tools and your AiNecto permissions decide what they may change.
3561
+ `
3562
+ );
3563
+ }
3525
3564
  const root = resolve2(options.cwd);
3526
3565
  const rootNotes = await repositoryRootNotes(root);
3527
3566
  const files = await readSnapshot(root);
@@ -3543,47 +3582,18 @@ async function runInit(initialOptions) {
3543
3582
  if (refusal !== void 0) {
3544
3583
  throw new Error(refusal);
3545
3584
  }
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);
3564
- if ("choices" in project) {
3565
- options.io.stdout.write(renderSuccess(
3566
- {
3567
- message: "More than one project found. Re-run with --project <uuid>.",
3568
- projects: project.choices
3569
- },
3570
- { json: options.json }
3571
- ));
3572
- return 1;
3573
- }
3574
- const documents = args.dryRun ? { versions: {}, fallbacks: {}, unavailableReason: "skipped in --dry-run" } : await fetchHarnessDocuments({
3585
+ const signedIn = await ensureSignedIn(options, args.dryRun);
3586
+ const documents = signedIn ? await fetchHarnessDocuments({
3575
3587
  endpoint: options.endpoint,
3576
3588
  accessToken: await options.accessToken?.(),
3577
3589
  // 서버가 문서의 클라이언트별 변형 키를 이 이름으로 찾는다.
3578
3590
  client: "ainecto-cli",
3579
3591
  fetchImpl: options.fetchImpl
3580
- });
3592
+ }) : { versions: {}, fallbacks: {}, unavailableReason: args.dryRun ? "not signed in, and --dry-run does not sign in" : "not signed in" };
3581
3593
  const plan = planInit({
3582
- role,
3583
3594
  env: options.env,
3584
- projectName: project.name,
3585
- projectUuid: project.uuid,
3586
3595
  endpoint: options.endpoint,
3596
+ mcp: args.mcp,
3587
3597
  cliVersion: options.cliVersion,
3588
3598
  files,
3589
3599
  documents
@@ -3592,39 +3602,18 @@ async function runInit(initialOptions) {
3592
3602
  throw new Error(plan.refusal);
3593
3603
  }
3594
3604
  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
- }
3605
+ const mcpWritten = plan.writes.some((write) => AGENT_TARGETS.some((target) => target.path === write.path));
3599
3606
  options.io.stdout.write(renderSuccess(
3600
3607
  {
3601
3608
  action: args.dryRun ? "init (dry-run)" : "init",
3602
- role,
3603
- project: { uuid: project.uuid, name: project.name },
3604
3609
  endpoint: options.endpoint,
3605
3610
  written: plan.writes.map((write) => `${write.existed ? "updated" : "created"} ${write.path}`),
3606
- codex: `Codex keeps a single global config, so init does not touch it. Run: ${plan.codexCommand}`,
3607
- codexPerSession: plan.codexProfile,
3608
- codexEnvFallback: `Without a profile, start the session with ${ROLE_ENV_VAR}=${role}.`,
3609
- // ⚠조용히 기본값으로 떨어지지 않는다 — 서버에서 규칙을 고쳐도 안 바뀌는 이유를
3610
- // 사용자가 영영 못 찾게 된다. ★요청 자체의 실패와 «문서별» 되돌림을 둘 다 적는다
3611
- // (2026-09-23 독립 재리뷰 I5: HTTP 200 으로 온 빈 문서가 조용히 패키지 판이 됐다).
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 도구»를 더할 뿐인 선택이다.
3611
+ // ⚠조용히 기본값으로 떨어지지 않는다 — 서버에서 규칙을 고쳐도 안 바뀌는 이유를 사용자가 영영 못 찾는다.
3612
+ // ★요청 자체의 실패와 «문서별» 되돌림을 둘 다, 어느 «파일»이 그 글을 받는지까지 적는다.
3613
+ notes: [...rootNotes, ...plan.notes, ...fallbackNotes(documents, args.dryRun)],
3619
3614
  next: [
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\`).`,
3615
+ `Read ${HARNESS_DOC_PATH} first. Every session sees the same AiNecto tools; your AiNecto permissions decide what they may change.`,
3616
+ mcpWritten ? "Claude Code and Cursor pick up the repository's MCP config in a new session." : "To connect your coding agents on this machine, run `ainecto setup` once (or re-run init with --mcp to also write this repository's .mcp.json and .cursor/mcp.json).",
3628
3617
  "Undo everything with: ainecto init --undo"
3629
3618
  ]
3630
3619
  },
@@ -3632,43 +3621,29 @@ async function runInit(initialOptions) {
3632
3621
  ));
3633
3622
  return 0;
3634
3623
  }
3635
- function currentRole(files) {
3636
- const unique = rolesInFiles(files).roles;
3637
- if (unique.length > 1) {
3638
- throw new Error(
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.`
3640
- );
3641
- }
3642
- return unique[0];
3643
- }
3644
- function fallbackNotes(documents) {
3645
- const notes = [];
3646
- if (documents.unavailableReason) {
3647
- notes.push(`Harness rules came from this package, not the server (${documents.unavailableReason}).`);
3648
- }
3649
- for (const [key, reason] of Object.entries(documents.fallbacks)) {
3650
- notes.push(`${key} came from this package, not the server (${reason}).`);
3651
- }
3652
- return notes;
3624
+ function fallbackNotes(documents, dryRun) {
3625
+ const filesOf = {
3626
+ [DOC_KEY]: HARNESS_DOC_PATH,
3627
+ [AGENT_NOTE_KEY]: "AGENTS.md / CLAUDE.md"
3628
+ };
3629
+ const caveat = dryRun ? " \u2014 the real run may write the server's version instead" : "";
3630
+ const reasons = documents.unavailableReason ? { [DOC_KEY]: documents.unavailableReason, [AGENT_NOTE_KEY]: documents.unavailableReason } : documents.fallbacks;
3631
+ return Object.entries(reasons).map(([key, reason]) => `${filesOf[key] ?? key}: written from this package's default ${key}, not the server's (${reason})${caveat}.`);
3653
3632
  }
3654
- async function ensureSignedIn(options, role, dryRun) {
3655
- if (options.login === void 0) {
3656
- return;
3657
- }
3633
+ async function ensureSignedIn(options, dryRun) {
3658
3634
  if (await options.accessToken?.()) {
3659
- return;
3635
+ return true;
3660
3636
  }
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
- );
3637
+ if (dryRun || options.login === void 0) {
3638
+ if (dryRun) {
3639
+ options.io.stderr.write(`No usable AiNecto sign-in, so this plan uses the package's default rules. Sign in with: ${loginCommand(options)}
3640
+ `);
3641
+ }
3642
+ return false;
3669
3643
  }
3670
3644
  await options.login();
3671
3645
  options.io.stderr.write("Signed in.\n");
3646
+ return true;
3672
3647
  }
3673
3648
  async function repositoryRootNotes(cwd) {
3674
3649
  const gitRoot = await findGitRoot(cwd);
@@ -3686,202 +3661,34 @@ Nothing was changed.`
3686
3661
  }
3687
3662
  return [];
3688
3663
  }
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
- );
3664
+ function loginCommand(options) {
3665
+ const target = isDefaultEndpointFor(options.env, options.endpoint) ? options.env === "prod" ? "" : ` --env ${options.env}` : ` --endpoint ${options.endpoint}`;
3666
+ return `ainecto auth login${target}`;
3712
3667
  }
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 } });
3668
+ async function readSnapshot(root) {
3669
+ const snapshot = /* @__PURE__ */ new Map();
3670
+ for (const path of MANAGED_PATHS) {
3671
+ snapshot.set(path, await readManagedFile(root, path));
3734
3672
  }
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>.`;
3673
+ return snapshot;
3745
3674
  }
3746
- async function resolveProject(options, args, files, root, role) {
3747
- const projects = await listProjects(options);
3748
- if (args.projectUuid) {
3749
- const found = projects.find((project) => project.uuid === args.projectUuid);
3750
- return found ?? { uuid: args.projectUuid, name: args.projectName ?? args.projectUuid };
3751
- }
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
- }
3756
- if (bound) {
3757
- const found = projects.find((project) => project.uuid === bound);
3758
- if (found) {
3759
- return found;
3760
- }
3761
- }
3762
- if (projects.length === 1) {
3763
- return projects[0];
3764
- }
3765
- if (projects.length > 1) {
3766
- return { choices: projects };
3767
- }
3768
- if (role !== "design") {
3769
- throw new Error(noProjectForRole(options, role));
3675
+ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3676
+ for (const write of plan.writes) {
3677
+ await assertInsideRepository(root, write.path);
3678
+ await assertUnchangedSinceSnapshot(root, write.path, snapshot);
3770
3679
  }
3771
- const name = args.projectName ?? basenameOf(root);
3772
- if (!args.yes) {
3773
- throw new Error(
3774
- `No project found in your AiNecto account. Re-run with --yes to create "${name}", or --project-name <name> --yes to pick the name.`
3775
- );
3680
+ for (const path of plan.deletes) {
3681
+ await assertInsideRepository(root, path);
3682
+ await assertUnchangedSinceSnapshot(root, path, snapshot);
3776
3683
  }
3777
- if (args.dryRun) {
3778
- throw new Error(dryRunCannotCreate(name));
3684
+ if (dryRun) {
3685
+ return;
3779
3686
  }
3780
- return createProject(options, name);
3781
- }
3782
- async function listProjects(options) {
3783
- const result = await callOrExplainSignIn(options, () => options.client.toolsCall("list_projects", {}));
3784
- return extractProjects(unwrapToolJson(result));
3785
- }
3786
- async function callOrExplainSignIn(options, run) {
3687
+ const applied = [];
3787
3688
  try {
3788
- return await run();
3789
- } catch (error) {
3790
- if (!isUnauthorized(error)) {
3791
- throw error;
3792
- }
3793
- throw new Error(
3794
- `Not signed in, so ${options.endpoint} refused the request (HTTP 401).
3795
-
3796
- ${loginCommand(options)}
3797
-
3798
- Then run init again. Sign-in is one per machine; the role comes from this repository.`
3799
- );
3800
- }
3801
- }
3802
- function loginCommand(options) {
3803
- const target = isDefaultEndpointFor(options.env, options.endpoint) ? options.env === "prod" ? "" : ` --env ${options.env}` : ` --endpoint ${options.endpoint}`;
3804
- return `ainecto auth login${target}`;
3805
- }
3806
- function isUnauthorized(error) {
3807
- return error instanceof McpRpcError && error.code === "MCP_HTTP_ERROR" && error.details?.status === 401;
3808
- }
3809
- async function createProject(options, name) {
3810
- const result = await callOrExplainSignIn(options, () => options.client.toolsCall("create_projects", { items: [{ name }] }));
3811
- const created = extractProjects(unwrapToolJson(result));
3812
- const first = created[0];
3813
- if (!first) {
3814
- throw new Error("Project creation did not return a project uuid.");
3815
- }
3816
- options.io.stderr.write(`Created project ${first.name} (${first.uuid}).
3817
- `);
3818
- return first;
3819
- }
3820
- function unwrapToolJson(result) {
3821
- if (!isRecord9(result) || !Array.isArray(result.content)) {
3822
- return result;
3823
- }
3824
- const first = result.content.find((item) => isRecord9(item) && item.type === "text");
3825
- if (!isRecord9(first) || typeof first.text !== "string") {
3826
- return result;
3827
- }
3828
- try {
3829
- return JSON.parse(first.text);
3830
- } catch {
3831
- return first.text;
3832
- }
3833
- }
3834
- function extractProjects(value) {
3835
- const candidates = Array.isArray(value) ? value : isRecord9(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
3836
- const nested = value[key];
3837
- return Array.isArray(nested) ? nested : [];
3838
- }) : [];
3839
- const projects = [];
3840
- for (const candidate of candidates) {
3841
- if (isRecord9(candidate) && typeof candidate.uuid === "string") {
3842
- projects.push({
3843
- uuid: candidate.uuid,
3844
- name: typeof candidate.name === "string" ? candidate.name : candidate.uuid
3845
- });
3846
- }
3847
- }
3848
- return projects;
3849
- }
3850
- function boundProjectUuid(config) {
3851
- if (!config) {
3852
- return void 0;
3853
- }
3854
- try {
3855
- const parsed = JSON.parse(config);
3856
- return typeof parsed.project?.uuid === "string" ? parsed.project.uuid : void 0;
3857
- } catch {
3858
- return void 0;
3859
- }
3860
- }
3861
- async function readSnapshot(root) {
3862
- const snapshot = /* @__PURE__ */ new Map();
3863
- for (const path of MANAGED_PATHS) {
3864
- snapshot.set(path, await readManagedFile(root, path));
3865
- }
3866
- return snapshot;
3867
- }
3868
- async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3869
- for (const write of plan.writes) {
3870
- await assertInsideRepository(root, write.path);
3871
- await assertUnchangedSinceSnapshot(root, write.path, snapshot);
3872
- }
3873
- for (const path of plan.deletes) {
3874
- await assertInsideRepository(root, path);
3875
- await assertUnchangedSinceSnapshot(root, path, snapshot);
3876
- }
3877
- if (dryRun) {
3878
- return;
3879
- }
3880
- const applied = [];
3881
- try {
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));
3689
+ const isRecord12 = (path) => !recordFirst && path === HARNESS_RECORD_PATH;
3690
+ const recordWrites = plan.writes.filter((write) => isRecord12(write.path));
3691
+ const recordDeletes = plan.deletes.filter((path) => isRecord12(path));
3885
3692
  const createdDirectories = [...plan.record?.createdDirectories ?? []];
3886
3693
  const rememberDirectories = (directories) => {
3887
3694
  for (const directory of directories) {
@@ -3923,8 +3730,8 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
3923
3730
  };
3924
3731
  try {
3925
3732
  await apply(
3926
- orderedWrites(plan.writes.filter((write) => !isRecord10(write.path)), recordFirst),
3927
- plan.deletes.filter((path) => !isRecord10(path))
3733
+ orderedWrites(plan.writes.filter((write) => !isRecord12(write.path)), recordFirst),
3734
+ plan.deletes.filter((path) => !isRecord12(path))
3928
3735
  );
3929
3736
  await recordDirectoriesOrExplain(false);
3930
3737
  await apply(recordWrites, recordDeletes);
@@ -3961,8 +3768,8 @@ async function assertUnchangedSinceSnapshot(root, relativePath, snapshot) {
3961
3768
  }
3962
3769
  async function writeManagedFile(root, write, onDirectoriesCreated) {
3963
3770
  const absolutePath = join3(root, write.path);
3964
- const targetDirectory = dirname3(absolutePath);
3965
- const firstCreated = await mkdir2(targetDirectory, { recursive: true });
3771
+ const targetDirectory = dirname4(absolutePath);
3772
+ const firstCreated = await mkdir3(targetDirectory, { recursive: true });
3966
3773
  const created = createdChain(root, firstCreated, targetDirectory);
3967
3774
  onDirectoriesCreated?.(created);
3968
3775
  const previousMode = await fileMode(absolutePath);
@@ -3971,7 +3778,7 @@ async function writeManagedFile(root, write, onDirectoriesCreated) {
3971
3778
  if (previousMode !== void 0) {
3972
3779
  await chmod2(tmpPath, previousMode);
3973
3780
  }
3974
- await rename2(tmpPath, absolutePath);
3781
+ await rename3(tmpPath, absolutePath);
3975
3782
  return created;
3976
3783
  }
3977
3784
  function createdChain(root, firstCreated, targetDirectory) {
@@ -3980,12 +3787,12 @@ function createdChain(root, firstCreated, targetDirectory) {
3980
3787
  }
3981
3788
  const stop = resolve2(firstCreated);
3982
3789
  const made = [];
3983
- for (let at = resolve2(targetDirectory); ; at = dirname3(at)) {
3790
+ for (let at = resolve2(targetDirectory); ; at = dirname4(at)) {
3984
3791
  const asRelative = relative(root, at);
3985
3792
  if (asRelative && !asRelative.startsWith("..")) {
3986
3793
  made.push(asRelative.split(sep2).join("/"));
3987
3794
  }
3988
- if (at === stop || dirname3(at) === at) {
3795
+ if (at === stop || dirname4(at) === at) {
3989
3796
  break;
3990
3797
  }
3991
3798
  }
@@ -3995,29 +3802,33 @@ async function fileMode(absolutePath) {
3995
3802
  try {
3996
3803
  return (await stat3(absolutePath)).mode & 4095;
3997
3804
  } catch (error) {
3998
- if (isRecord9(error) && error.code === "ENOENT") {
3805
+ if (isRecord11(error) && error.code === "ENOENT") {
3999
3806
  return void 0;
4000
3807
  }
4001
3808
  throw error;
4002
3809
  }
4003
3810
  }
4004
3811
  function parseInitArgs(argv) {
4005
- const args = { dryRun: false, undo: false, yes: false };
3812
+ const args = { mcp: false, dryRun: false, undo: false, ignored: [] };
4006
3813
  for (let index = 0; index < argv.length; index += 1) {
4007
3814
  const arg = argv[index];
4008
3815
  if (!arg) {
4009
3816
  continue;
4010
3817
  }
4011
- if (arg === "--project") {
4012
- args.projectUuid = requireValue3(arg, argv[++index]);
4013
- } else if (arg === "--project-name") {
4014
- args.projectName = requireValue3(arg, argv[++index]);
3818
+ if (arg === "--mcp") {
3819
+ args.mcp = true;
4015
3820
  } else if (arg === "--dry-run") {
4016
3821
  args.dryRun = true;
4017
3822
  } else if (arg === "--undo") {
4018
3823
  args.undo = true;
3824
+ } else if (arg === "--project" || arg === "--project-name") {
3825
+ const value = argv[index + 1];
3826
+ if (value !== void 0 && !value.startsWith("--")) {
3827
+ index += 1;
3828
+ }
3829
+ args.ignored.push(arg);
4019
3830
  } else if (arg === "--yes" || arg === "-y") {
4020
- args.yes = true;
3831
+ args.ignored.push(arg);
4021
3832
  } else if (arg === "--json") {
4022
3833
  } else {
4023
3834
  throw new Error(`Unknown argument: ${arg}`);
@@ -4025,21 +3836,730 @@ function parseInitArgs(argv) {
4025
3836
  }
4026
3837
  return args;
4027
3838
  }
4028
- function requireValue3(flag, value) {
4029
- if (!value || value.startsWith("--")) {
4030
- throw new Error(`${flag} requires a value.`);
3839
+ function isRecord11(value) {
3840
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3841
+ }
3842
+
3843
+ // src/core/setup/accountSummary.ts
3844
+ async function readAccount(client) {
3845
+ const summary = {};
3846
+ try {
3847
+ await client.initialize();
3848
+ const self = unwrapMcpToolPayload(await client.toolsCall("identity_get_self", {}));
3849
+ if (typeof self?.userId === "string") summary.userId = self.userId;
3850
+ if (typeof self?.displayName === "string") summary.displayName = self.displayName;
3851
+ if ("jobProfile" in (self ?? {})) {
3852
+ const profile = self.jobProfile;
3853
+ summary.jobTitle = profile && typeof profile.title === "string" ? profile.title : null;
3854
+ }
3855
+ summary.projectCount = extractProjects(unwrapMcpToolPayload(await client.toolsCall("list_projects", {}))).length;
3856
+ } catch (error) {
3857
+ summary.problem = classify(error);
4031
3858
  }
4032
- return value;
3859
+ return summary;
4033
3860
  }
4034
- function basenameOf(path) {
4035
- const parts = path.split(/[\\/]/).filter(Boolean);
4036
- return parts[parts.length - 1] ?? "ainecto-project";
3861
+ function classify(error) {
3862
+ const message = error instanceof Error ? error.message : String(error);
3863
+ if (error instanceof McpRpcError && error.code === "MCP_HTTP_ERROR") {
3864
+ const status = error.details?.status;
3865
+ if (status === 401) return { kind: "unauthorized", message };
3866
+ if (status === 403) return { kind: "forbidden", message };
3867
+ return { kind: "other", message };
3868
+ }
3869
+ if (error instanceof McpRpcError) {
3870
+ return { kind: "other", message };
3871
+ }
3872
+ return { kind: "network", message };
4037
3873
  }
4038
- function isRecord9(value) {
4039
- return typeof value === "object" && value !== null && !Array.isArray(value);
3874
+
3875
+ // src/core/setup/agentConnections.ts
3876
+ import { chmod as chmod3, mkdir as mkdir4, readFile as readFile6, rename as rename4, stat as stat4, writeFile as writeFile3 } from "fs/promises";
3877
+ import { dirname as dirname5, join as join4 } from "path";
3878
+
3879
+ // src/core/setup/commandRunner.ts
3880
+ import { spawn as spawn2 } from "child_process";
3881
+ var COMMAND_TIMEOUT_MS = 9e4;
3882
+ function systemCommandRunner(options) {
3883
+ return (command, args) => new Promise((resolve3) => {
3884
+ let stdout = "";
3885
+ let stderr = "";
3886
+ let settled = false;
3887
+ const finish = (result) => {
3888
+ if (!settled) {
3889
+ settled = true;
3890
+ clearTimeout(timer);
3891
+ resolve3(result);
3892
+ }
3893
+ };
3894
+ const child = spawn2(command, [...args], {
3895
+ cwd: options.cwd,
3896
+ env: options.env ?? process.env,
3897
+ stdio: ["ignore", "pipe", "pipe"]
3898
+ });
3899
+ const timer = setTimeout(() => {
3900
+ child.kill();
3901
+ finish({ exitCode: null, stdout, stderr: `${stderr}
3902
+ ${command} timed out after ${COMMAND_TIMEOUT_MS / 1e3}s`, missing: false });
3903
+ }, COMMAND_TIMEOUT_MS);
3904
+ child.stdout.on("data", (chunk) => {
3905
+ stdout += String(chunk);
3906
+ });
3907
+ child.stderr.on("data", (chunk) => {
3908
+ stderr += String(chunk);
3909
+ });
3910
+ child.on("error", (error) => {
3911
+ finish({ exitCode: null, stdout, stderr: error.message, missing: error.code === "ENOENT" });
3912
+ });
3913
+ child.on("close", (code) => finish({ exitCode: code, stdout, stderr, missing: false }));
3914
+ });
3915
+ }
3916
+ function recordingRunner(runner, log) {
3917
+ return async (command, args) => {
3918
+ const result = await runner(command, args);
3919
+ const entry = { command: [command, ...args].join(" "), exitCode: result.exitCode };
3920
+ if (result.missing) {
3921
+ entry.missing = true;
3922
+ } else if (result.exitCode !== 0) {
3923
+ entry.error = lastLine(result.stderr) ?? lastLine(result.stdout);
3924
+ }
3925
+ log.push(entry);
3926
+ return result;
3927
+ };
3928
+ }
3929
+ function lastLine(text) {
3930
+ const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
3931
+ return lines[lines.length - 1];
3932
+ }
3933
+
3934
+ // src/core/setup/agentConnections.ts
3935
+ var AGENT_CLIENT_LABELS = {
3936
+ "claude-code": "Claude Code",
3937
+ cursor: "Cursor",
3938
+ codex: "Codex"
3939
+ };
3940
+ async function inspectAgents(context) {
3941
+ return [await inspectClaude(context), await inspectCursor(context), await inspectCodex(context)];
3942
+ }
3943
+ var NOT_FOUND = /No MCP server named/i;
3944
+ async function inspectClaude({ runner }) {
3945
+ const base = { client: "claude-code", ours: false, carriesRole: false };
3946
+ const result = await runner("claude", ["mcp", "get", SERVER_NAME]);
3947
+ if (result.missing) {
3948
+ return { ...base, installed: false };
3949
+ }
3950
+ if (result.exitCode !== 0) {
3951
+ return NOT_FOUND.test(result.stdout + result.stderr) ? { ...base, installed: true } : { ...base, installed: true, problem: lastLine(result.stderr) ?? `claude mcp get exited ${result.exitCode}` };
3952
+ }
3953
+ const field = (name) => result.stdout.match(new RegExp(`^\\s*${name}:\\s*(.*)$`, "m"))?.[1]?.trim();
3954
+ const scopeText = field("Scope") ?? "";
3955
+ const scope = /^user/i.test(scopeText) ? "user" : /^local/i.test(scopeText) ? "local" : /^project/i.test(scopeText) ? "project" : scopeText || void 0;
3956
+ const url = field("URL");
3957
+ const command = field("Command");
3958
+ const entry = url ? { url } : command ? { command, args: (field("Args") ?? "").split(/\s+/).filter(Boolean) } : void 0;
3959
+ return { ...base, installed: true, scope, ...describe(entry) };
3960
+ }
3961
+ async function inspectCursor({ home }) {
3962
+ const base = { client: "cursor", ours: false, carriesRole: false, scope: "global" };
3963
+ if (!await exists(join4(home, ".cursor"))) {
3964
+ return { ...base, installed: false };
3965
+ }
3966
+ let content;
3967
+ try {
3968
+ content = await readFile6(cursorConfigPath(home), "utf8");
3969
+ } catch (error) {
3970
+ if (!isNodeError3(error, "ENOENT")) {
3971
+ return { ...base, installed: true, problem: error instanceof Error ? error.message : String(error) };
3972
+ }
3973
+ }
3974
+ if (content !== void 0 && content.trim()) {
3975
+ try {
3976
+ JSON.parse(content);
3977
+ } catch {
3978
+ return { ...base, installed: true, problem: `${cursorConfigPath(home)} is not valid JSON` };
3979
+ }
3980
+ }
3981
+ const raw = readServerEntry(content);
3982
+ return { ...base, installed: true, ...describe(toExisting(raw)) };
3983
+ }
3984
+ async function inspectCodex({ runner }) {
3985
+ const base = { client: "codex", ours: false, carriesRole: false, scope: "global" };
3986
+ const result = await runner("codex", ["mcp", "get", SERVER_NAME, "--json"]);
3987
+ if (result.missing) {
3988
+ return { ...base, installed: false };
3989
+ }
3990
+ if (result.exitCode !== 0) {
3991
+ return NOT_FOUND.test(result.stdout + result.stderr) ? { ...base, installed: true } : { ...base, installed: true, problem: lastLine(result.stderr) ?? `codex mcp get exited ${result.exitCode}` };
3992
+ }
3993
+ try {
3994
+ const parsed = JSON.parse(result.stdout);
3995
+ const transport = parsed.transport ?? {};
3996
+ return { ...base, installed: true, ...describe(toExisting(transport)) };
3997
+ } catch {
3998
+ return { ...base, installed: true, problem: "codex mcp get --json did not return JSON" };
3999
+ }
4000
+ }
4001
+ async function connectAgent(state, desired, context, dryRun) {
4002
+ const outcome = (status, detail) => ({
4003
+ client: state.client,
4004
+ label: AGENT_CLIENT_LABELS[state.client],
4005
+ status,
4006
+ ...dryRun && (status === "connected" || status === "updated") ? { planned: true } : {},
4007
+ ...detail ? { detail } : {}
4008
+ });
4009
+ if (!state.installed) {
4010
+ return outcome("not installed");
4011
+ }
4012
+ if (state.problem) {
4013
+ return outcome("failed", state.problem);
4014
+ }
4015
+ if (state.entry && (!state.ours || state.client === "claude-code" && state.scope !== "user")) {
4016
+ return outcome("kept", keptDetail(state));
4017
+ }
4018
+ if (state.entry && sameEntry(state.entry, desired)) {
4019
+ return outcome("connected", "already connected");
4020
+ }
4021
+ const updating = state.entry !== void 0;
4022
+ const commands = writeCommands(state.client, desired, updating);
4023
+ if (dryRun) {
4024
+ return outcome(updating ? "updated" : "connected", commands.length > 0 ? `would run: ${commands.map((c) => c.join(" ")).join(" && ")}` : `would write ${cursorConfigPath(context.home)}`);
4025
+ }
4026
+ try {
4027
+ if (state.client === "cursor") {
4028
+ const path = cursorConfigPath(context.home);
4029
+ await writeKeepingMode(path, mergeServerEntry(await readIfExists2(path), desired).content);
4030
+ }
4031
+ for (const [command, ...args] of commands) {
4032
+ const result = await context.runner(command, args);
4033
+ if (result.exitCode !== 0) {
4034
+ return outcome("failed", `${[command, ...args].join(" ")}: ${lastLine(result.stderr) ?? lastLine(result.stdout) ?? `exit ${result.exitCode}`}`);
4035
+ }
4036
+ }
4037
+ } catch (error) {
4038
+ return outcome("failed", error instanceof Error ? error.message : String(error));
4039
+ }
4040
+ return outcome(updating ? "updated" : "connected");
4041
+ }
4042
+ async function disconnectAgent(state, context, dryRun) {
4043
+ const outcome = (status, detail) => ({
4044
+ client: state.client,
4045
+ label: AGENT_CLIENT_LABELS[state.client],
4046
+ status,
4047
+ ...dryRun && status === "removed" ? { planned: true } : {},
4048
+ ...detail ? { detail } : {}
4049
+ });
4050
+ if (!state.installed) {
4051
+ return outcome("not installed");
4052
+ }
4053
+ if (state.problem) {
4054
+ return outcome("failed", state.problem);
4055
+ }
4056
+ if (!state.entry) {
4057
+ return outcome("not connected");
4058
+ }
4059
+ if (!state.ours || state.client === "claude-code" && state.scope !== "user") {
4060
+ return outcome("kept", keptDetail(state));
4061
+ }
4062
+ if (dryRun) {
4063
+ return outcome("removed");
4064
+ }
4065
+ try {
4066
+ if (state.client === "cursor") {
4067
+ const path = cursorConfigPath(context.home);
4068
+ const removed = removeServerEntry(await readIfExists2(path));
4069
+ if (removed.content !== void 0) {
4070
+ await writeKeepingMode(path, removed.content);
4071
+ }
4072
+ } else {
4073
+ const [command, ...args] = removeCommand(state.client);
4074
+ const result = await context.runner(command, args);
4075
+ if (result.exitCode !== 0) {
4076
+ return outcome("failed", `${[command, ...args].join(" ")}: ${lastLine(result.stderr) ?? `exit ${result.exitCode}`}`);
4077
+ }
4078
+ }
4079
+ } catch (error) {
4080
+ return outcome("failed", error instanceof Error ? error.message : String(error));
4081
+ }
4082
+ return outcome("removed");
4083
+ }
4084
+ function desiredEntry(input) {
4085
+ return buildServerEntry({ env: input.env, endpoint: input.endpoint });
4086
+ }
4087
+ function cursorConfigPath(home) {
4088
+ return join4(home, ".cursor", "mcp.json");
4089
+ }
4090
+ function writeCommands(client, entry, updating) {
4091
+ const bridge = [entry.command, ...entry.args];
4092
+ if (client === "claude-code") {
4093
+ return [
4094
+ ...updating ? [["claude", "mcp", "remove", "-s", "user", SERVER_NAME]] : [],
4095
+ ["claude", "mcp", "add", "-s", "user", SERVER_NAME, "--", ...bridge]
4096
+ ];
4097
+ }
4098
+ if (client === "codex") {
4099
+ return [["codex", "mcp", "add", SERVER_NAME, "--", ...bridge]];
4100
+ }
4101
+ return [];
4102
+ }
4103
+ function removeCommand(client) {
4104
+ return client === "claude-code" ? ["claude", "mcp", "remove", "-s", "user", SERVER_NAME] : ["codex", "mcp", "remove", SERVER_NAME];
4105
+ }
4106
+ function keptDetail(state) {
4107
+ if (state.client === "claude-code" && state.ours && state.scope !== "user") {
4108
+ return `a ${state.scope}-scope "${SERVER_NAME}" entry takes precedence here \u2014 left as it is`;
4109
+ }
4110
+ const where = state.scope ? ` (${state.scope})` : "";
4111
+ return `someone else's "${SERVER_NAME}" entry${where} \u2014 left untouched`;
4112
+ }
4113
+ function describe(entry) {
4114
+ if (!entry) {
4115
+ return { ours: false, carriesRole: false };
4116
+ }
4117
+ if ("url" in entry) {
4118
+ return { entry, ours: false, carriesRole: false };
4119
+ }
4120
+ const pinnedVersion = pinnedBridgeVersion(entry);
4121
+ return {
4122
+ entry,
4123
+ ours: isOurBridge(entry),
4124
+ carriesRole: entryCarriesRole(entry),
4125
+ ...pinnedVersion ? { pinnedVersion } : {}
4126
+ };
4127
+ }
4128
+ function toExisting(raw) {
4129
+ if (typeof raw !== "object" || raw === null) {
4130
+ return void 0;
4131
+ }
4132
+ const { command, args, url } = raw;
4133
+ if (typeof url === "string" && url) {
4134
+ return { url };
4135
+ }
4136
+ if (typeof command === "string") {
4137
+ return { command, args: Array.isArray(args) ? args.filter((arg) => typeof arg === "string") : [] };
4138
+ }
4139
+ return void 0;
4140
+ }
4141
+ function sameEntry(entry, desired) {
4142
+ return "command" in entry && entry.command === desired.command && entry.args.length === desired.args.length && entry.args.every((arg, at) => arg === desired.args[at]);
4143
+ }
4144
+ async function writeKeepingMode(path, content) {
4145
+ await mkdir4(dirname5(path), { recursive: true });
4146
+ let mode;
4147
+ try {
4148
+ mode = (await stat4(path)).mode & 4095;
4149
+ } catch (error) {
4150
+ if (!isNodeError3(error, "ENOENT")) {
4151
+ throw error;
4152
+ }
4153
+ }
4154
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
4155
+ await writeFile3(tmp, content, { encoding: "utf8", mode: mode ?? 384 });
4156
+ if (mode !== void 0) {
4157
+ await chmod3(tmp, mode);
4158
+ }
4159
+ await rename4(tmp, path);
4160
+ }
4161
+ async function readIfExists2(path) {
4162
+ try {
4163
+ return await readFile6(path, "utf8");
4164
+ } catch (error) {
4165
+ if (isNodeError3(error, "ENOENT")) {
4166
+ return void 0;
4167
+ }
4168
+ throw error;
4169
+ }
4170
+ }
4171
+ async function exists(path) {
4172
+ try {
4173
+ await stat4(path);
4174
+ return true;
4175
+ } catch {
4176
+ return false;
4177
+ }
4178
+ }
4179
+ function isNodeError3(error, code) {
4180
+ return typeof error === "object" && error !== null && error.code === code;
4181
+ }
4182
+
4183
+ // src/adapters/cli/setupCommand.ts
4184
+ async function executeSetupCommand(options) {
4185
+ const args = parseSetupArgs(options.argv);
4186
+ const commands = [];
4187
+ const context = { runner: recordingRunner(options.runner, commands), home: options.home };
4188
+ if (args.undo) {
4189
+ const states = await inspectAgents(context);
4190
+ const agents2 = [];
4191
+ for (const state of states) {
4192
+ agents2.push(await disconnectAgent(state, context, args.dryRun));
4193
+ }
4194
+ const failed2 = agents2.some((agent) => agent.status === "failed");
4195
+ options.io.stdout.write(renderSuccess({
4196
+ action: args.dryRun ? "setup --undo (dry-run)" : "setup --undo",
4197
+ agents: agents2,
4198
+ commands,
4199
+ notes: ["Your sign-in is kept \u2014 sign out with `ainecto auth logout`."]
4200
+ }, { json: options.json }));
4201
+ return failed2 ? 1 : 0;
4202
+ }
4203
+ let signedIn = Boolean(await options.auth.getAccessToken());
4204
+ if (!signedIn && !args.dryRun) {
4205
+ await options.auth.login();
4206
+ options.io.stderr.write("Signed in.\n");
4207
+ signedIn = true;
4208
+ }
4209
+ const account = signedIn ? await readAccount(options.client) : void 0;
4210
+ const desired = desiredEntry({ env: options.env, endpoint: options.endpoint });
4211
+ const agents = [];
4212
+ for (const state of await inspectAgents(context)) {
4213
+ agents.push(await connectAgent(state, desired, context, args.dryRun));
4214
+ }
4215
+ const failed = agents.some((agent) => agent.status === "failed") || account?.problem !== void 0;
4216
+ options.io.stdout.write(renderSuccess({
4217
+ action: args.dryRun ? "setup (dry-run)" : "setup",
4218
+ setup: failed ? "partial" : "complete",
4219
+ endpoint: options.endpoint,
4220
+ account: accountView(account, signedIn, options.endpoint),
4221
+ agents,
4222
+ commands,
4223
+ next: nextSteps(account, agents, options.endpoint)
4224
+ }, { json: options.json }));
4225
+ return failed ? 1 : 0;
4226
+ }
4227
+ function accountView(account, signedIn, endpoint) {
4228
+ if (!signedIn) {
4229
+ return { signedIn: false, note: "sign-in required \u2014 `ainecto setup` (without --dry-run) opens the browser once" };
4230
+ }
4231
+ const view = { signedIn: true };
4232
+ if (account?.userId) view.userId = account.userId;
4233
+ if (account?.displayName) view.name = account.displayName;
4234
+ if (account?.jobTitle !== void 0) {
4235
+ view.jobTitle = account.jobTitle ?? `not set \u2014 set it at ${profileSettingsUrl(endpoint)}`;
4236
+ }
4237
+ if (account?.projectCount !== void 0) view.projects = account.projectCount;
4238
+ if (account?.problem) view.problem = `${account.problem.kind}: ${account.problem.message}`;
4239
+ return view;
4240
+ }
4241
+ function nextSteps(account, agents, endpoint) {
4242
+ const next = [];
4243
+ if (agents.some((agent) => agent.status === "connected" || agent.status === "updated")) {
4244
+ next.push("Start a new agent session to load the AiNecto tools. Every session sees the same tools; your AiNecto permissions decide what they may change.");
4245
+ }
4246
+ if (account?.jobTitle === null) {
4247
+ next.push(`Set your AiNecto role (your job) at ${profileSettingsUrl(endpoint)} \u2014 AI uses it to pitch explanations; it does not change permissions.`);
4248
+ }
4249
+ next.push("To add AiNecto's working rules to a repository, run `ainecto init` from its root.");
4250
+ next.push("`ainecto doctor` shows anything still missing.");
4251
+ return next;
4252
+ }
4253
+ function parseSetupArgs(argv) {
4254
+ const args = { dryRun: false, undo: false };
4255
+ for (const arg of argv) {
4256
+ if (arg === "--dry-run") {
4257
+ args.dryRun = true;
4258
+ } else if (arg === "--undo") {
4259
+ args.undo = true;
4260
+ } else if (arg === "--json") {
4261
+ } else {
4262
+ throw new Error(`Unknown argument: ${arg}`);
4263
+ }
4264
+ }
4265
+ return args;
4266
+ }
4267
+
4268
+ // src/core/setup/doctorChecks.ts
4269
+ function doctorChecks(status) {
4270
+ const checks = [];
4271
+ const settings = profileSettingsUrl(status.endpoint);
4272
+ if (status.token.state === "not-signed-in") {
4273
+ checks.push({ item: "sign-in", level: "fail", detail: `not signed in to ${status.endpoint}`, fix: "ainecto setup" });
4274
+ } else if (status.token.state === "refresh-failed") {
4275
+ checks.push({ item: "sign-in", level: "fail", detail: `sign-in expired and could not be renewed (${status.token.reason})`, fix: "ainecto setup" });
4276
+ } else {
4277
+ checks.push({ item: "sign-in", level: "ok", detail: status.token.state === "env" ? "AINECTO_TOKEN" : "signed in" });
4278
+ }
4279
+ const problem = status.account?.problem;
4280
+ if (problem?.kind === "network") {
4281
+ checks.push({ item: "server", level: "fail", detail: `cannot reach ${status.endpoint} (${problem.message})`, fix: "check the network, or the --endpoint you passed" });
4282
+ } else if (problem?.kind === "unauthorized") {
4283
+ checks.push({ item: "server", level: "fail", detail: `${status.endpoint} refused the sign-in (HTTP 401)`, fix: "ainecto auth login" });
4284
+ } else if (problem?.kind === "forbidden") {
4285
+ checks.push({ item: "server", level: "fail", detail: "the account's plan does not include MCP, or the account is not active (HTTP 403)", fix: `check your plan at ${settings}` });
4286
+ } else if (problem) {
4287
+ checks.push({ item: "server", level: "fail", detail: problem.message });
4288
+ } else if (status.account) {
4289
+ checks.push({ item: "server", level: "ok", detail: `${status.endpoint} answered; ${status.account.projectCount ?? 0} project(s) visible` });
4290
+ }
4291
+ for (const agent of status.agents) {
4292
+ checks.push(agentCheck(agent, status.cliVersion));
4293
+ }
4294
+ if (status.claudeHere?.entry && status.claudeHere.scope === "local") {
4295
+ const hereOursUpToDate = status.claudeHere.ours && !status.claudeHere.carriesRole;
4296
+ if (!hereOursUpToDate) {
4297
+ checks.push({
4298
+ item: "Claude Code (this folder)",
4299
+ level: "warn",
4300
+ detail: `a local-scope "${SERVER_NAME}" entry in this folder hides the user-scope connection`,
4301
+ fix: `claude mcp remove ${SERVER_NAME} -s local`
4302
+ });
4303
+ }
4304
+ }
4305
+ const repo = status.repository;
4306
+ if (repo?.problem) {
4307
+ checks.push({ item: "repository", level: "warn", detail: repo.problem });
4308
+ } else if (repo) {
4309
+ const leftovers = [
4310
+ ...repo.mcpFiles.filter((file) => file.ours && file.carriesRole).map((file) => `${file.path} passes the removed --role`),
4311
+ ...repo.mcpFiles.filter((file) => file.ours && olderThan(file.pinnedVersion, status.cliVersion)).map((file) => `${file.path} pins @ainecto/mcp@${file.pinnedVersion}`),
4312
+ ...repo.configHasProject ? [".ainecto/config.json still has the old project field"] : []
4313
+ ];
4314
+ if (leftovers.length > 0) {
4315
+ checks.push({ item: "repository", level: "warn", detail: leftovers.join("; "), fix: `ainecto init (in ${repo.root})` });
4316
+ } else {
4317
+ checks.push({
4318
+ item: "repository",
4319
+ level: "ok",
4320
+ detail: repo.harnessDoc ? `AiNecto rules are set up in ${repo.root}` : `no AiNecto rules in ${repo.root} (optional \u2014 \`ainecto init\` adds them)`
4321
+ });
4322
+ }
4323
+ }
4324
+ for (const name of status.legacyEnv) {
4325
+ checks.push({ item: name, level: "warn", detail: `${name} is set; roles were removed and it is ignored`, fix: `unset ${name} (it can be deleted)` });
4326
+ }
4327
+ if (status.legacyCodexProfiles.length > 0) {
4328
+ checks.push({
4329
+ item: "Codex role profiles",
4330
+ level: "warn",
4331
+ detail: `these Codex profiles still pass --role: ${status.legacyCodexProfiles.join(", ")}`,
4332
+ fix: "delete them \u2014 `ainecto setup` connects Codex for every session"
4333
+ });
4334
+ }
4335
+ if (status.globalCliVersion && olderThan(status.globalCliVersion, status.cliVersion)) {
4336
+ checks.push({
4337
+ item: "global ainecto",
4338
+ level: "warn",
4339
+ detail: `the ainecto on your PATH is ${status.globalCliVersion}, older than ${status.cliVersion}`,
4340
+ fix: "npm i -g ainecto@latest"
4341
+ });
4342
+ }
4343
+ if (status.account?.jobTitle === null) {
4344
+ checks.push({ item: "AiNecto job title", level: "warn", detail: "not set \u2014 AI uses it to pitch explanations; it does not change permissions", fix: `set it at ${settings}` });
4345
+ } else if (typeof status.account?.jobTitle === "string") {
4346
+ checks.push({ item: "AiNecto job title", level: "ok", detail: status.account.jobTitle });
4347
+ }
4348
+ return checks;
4349
+ }
4350
+ function agentCheck(agent, cliVersion) {
4351
+ const item = AGENT_CLIENT_LABELS[agent.client];
4352
+ if (!agent.installed) {
4353
+ return { item, level: "ok", detail: "not installed" };
4354
+ }
4355
+ if (agent.problem) {
4356
+ return { item, level: "fail", detail: agent.problem, fix: "ainecto setup" };
4357
+ }
4358
+ if (!agent.entry) {
4359
+ return { item, level: "warn", detail: "not connected to AiNecto", fix: "ainecto setup" };
4360
+ }
4361
+ if (!agent.ours) {
4362
+ return {
4363
+ item,
4364
+ level: "warn",
4365
+ detail: `someone else's "${SERVER_NAME}" entry${agent.scope ? ` (${agent.scope})` : ""} \u2014 AiNecto setup leaves it alone`,
4366
+ fix: agent.client === "claude-code" ? `claude mcp get ${SERVER_NAME}` : "keep it, or remove it and run `ainecto setup`"
4367
+ };
4368
+ }
4369
+ if (agent.client === "claude-code" && agent.scope !== "user") {
4370
+ return { item, level: "warn", detail: `connected in the ${agent.scope} scope, not for every project`, fix: "ainecto setup" };
4371
+ }
4372
+ if (agent.carriesRole) {
4373
+ return { item, level: "warn", detail: "connected, but still passes the removed --role", fix: "ainecto setup" };
4374
+ }
4375
+ if (olderThan(agent.pinnedVersion, cliVersion)) {
4376
+ return { item, level: "warn", detail: `connected with @ainecto/mcp@${agent.pinnedVersion}; ${cliVersion} is available`, fix: "ainecto setup" };
4377
+ }
4378
+ return { item, level: "ok", detail: agent.pinnedVersion ? `connected (@ainecto/mcp@${agent.pinnedVersion})` : "connected" };
4379
+ }
4380
+ function olderThan(a, b) {
4381
+ const left = parseVersion(a);
4382
+ const right = parseVersion(b);
4383
+ if (!left || !right) {
4384
+ return false;
4385
+ }
4386
+ for (let at = 0; at < 3; at += 1) {
4387
+ if (left[at] !== right[at]) {
4388
+ return left[at] < right[at];
4389
+ }
4390
+ }
4391
+ return false;
4392
+ }
4393
+ function parseVersion(value) {
4394
+ const match = value?.match(/^(\d+)\.(\d+)\.(\d+)/);
4395
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
4396
+ }
4397
+
4398
+ // src/core/setup/machineStatus.ts
4399
+ import { readdir, readFile as readFile7 } from "fs/promises";
4400
+ import { join as join5 } from "path";
4401
+ async function collectStatus(input) {
4402
+ const token = await tokenStatus(input);
4403
+ const usable = token.state === "env" || token.state === "signed-in";
4404
+ const status = {
4405
+ cliVersion: input.cliVersion,
4406
+ endpoint: input.endpoint,
4407
+ env: input.env,
4408
+ token,
4409
+ agents: await inspectAgents({ runner: input.runner, home: input.home }),
4410
+ legacyEnv: [LEGACY_ROLE_ENV_VAR, "AI_ERD_ROLE"].filter((name) => input.envVars[name]?.trim()),
4411
+ legacyCodexProfiles: await legacyCodexProfiles(input.envVars.CODEX_HOME ?? join5(input.home, ".codex"))
4412
+ };
4413
+ if (usable) {
4414
+ status.account = await readAccount(input.client);
4415
+ }
4416
+ if (input.hereRunner && input.cwd !== input.home) {
4417
+ status.claudeHere = await inspectClaude({ runner: input.hereRunner, home: input.home });
4418
+ }
4419
+ status.repository = await repositoryStatus(input.cwd);
4420
+ const globalVersion = await globalCliVersion(input.runner);
4421
+ if (globalVersion) {
4422
+ status.globalCliVersion = globalVersion;
4423
+ }
4424
+ return status;
4425
+ }
4426
+ async function tokenStatus(input) {
4427
+ if (input.envVars.AINECTO_TOKEN) {
4428
+ return { state: "env" };
4429
+ }
4430
+ const stored = await input.tokenStore.load(input.endpoint);
4431
+ const expiresAt = stored?.expiresAt ? new Date(stored.expiresAt).toISOString() : void 0;
4432
+ try {
4433
+ const token = await input.auth.getAccessToken();
4434
+ if (token) {
4435
+ return { state: "signed-in", ...expiresAt ? { expiresAt } : {} };
4436
+ }
4437
+ return stored ? { state: "refresh-failed", ...expiresAt ? { expiresAt } : {}, reason: "the stored sign-in expired and has no refresh token" } : { state: "not-signed-in" };
4438
+ } catch (error) {
4439
+ return {
4440
+ state: "refresh-failed",
4441
+ ...expiresAt ? { expiresAt } : {},
4442
+ reason: error instanceof Error ? error.message : String(error)
4443
+ };
4444
+ }
4445
+ }
4446
+ async function repositoryStatus(cwd) {
4447
+ const gitRoot = await findGitRoot(cwd);
4448
+ const root = gitRoot ?? cwd;
4449
+ const status = { root, git: gitRoot !== void 0, harnessDoc: false, configHasProject: false, mcpFiles: [] };
4450
+ try {
4451
+ status.harnessDoc = await readManagedFile(root, HARNESS_DOC_PATH) !== void 0;
4452
+ const config = parseObject(await readManagedFile(root, HARNESS_CONFIG_PATH));
4453
+ if (config) {
4454
+ if (typeof config.version === "number") status.configVersion = config.version;
4455
+ status.configHasProject = Object.prototype.hasOwnProperty.call(config, "project");
4456
+ const versions = config.promptVersions;
4457
+ if (typeof versions?.[DOC_KEY] === "string") status.harnessDocVersion = versions[DOC_KEY];
4458
+ }
4459
+ for (const target of AGENT_TARGETS) {
4460
+ const entry = readServerEntry(await readManagedFile(root, target.path));
4461
+ if (entry !== void 0) {
4462
+ const pinnedVersion = pinnedBridgeVersion(entry);
4463
+ status.mcpFiles.push({
4464
+ path: target.path,
4465
+ ours: isOurBridge(entry),
4466
+ carriesRole: entryCarriesRole(entry),
4467
+ ...pinnedVersion ? { pinnedVersion } : {}
4468
+ });
4469
+ }
4470
+ }
4471
+ } catch (error) {
4472
+ status.problem = error instanceof Error ? error.message : String(error);
4473
+ }
4474
+ return status;
4475
+ }
4476
+ async function legacyCodexProfiles(codexHome) {
4477
+ let names;
4478
+ try {
4479
+ names = await readdir(codexHome);
4480
+ } catch {
4481
+ return [];
4482
+ }
4483
+ const found = [];
4484
+ for (const name of names.filter((file) => file.endsWith(".config.toml"))) {
4485
+ const content = await readFile7(join5(codexHome, name), "utf8").catch(() => "");
4486
+ if (content.includes(PACKAGE_NAME) && content.includes("--role")) {
4487
+ found.push(join5(codexHome, name));
4488
+ }
4489
+ }
4490
+ return found;
4491
+ }
4492
+ async function globalCliVersion(runner) {
4493
+ const result = await runner("ainecto", ["--version", "--json"]);
4494
+ if (result.missing || result.exitCode !== 0) {
4495
+ return void 0;
4496
+ }
4497
+ try {
4498
+ const version = JSON.parse(result.stdout).data?.version;
4499
+ return typeof version === "string" ? version : void 0;
4500
+ } catch {
4501
+ return void 0;
4502
+ }
4503
+ }
4504
+ function parseObject(content) {
4505
+ if (content === void 0) {
4506
+ return void 0;
4507
+ }
4508
+ try {
4509
+ const parsed = JSON.parse(content);
4510
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
4511
+ } catch {
4512
+ return void 0;
4513
+ }
4514
+ }
4515
+
4516
+ // src/adapters/cli/statusCommand.ts
4517
+ async function executeStatusCommand(options) {
4518
+ assertNoArgs("status", options.argv);
4519
+ const status = await collectStatus(options.collect);
4520
+ options.io.stdout.write(renderSuccess(statusView(status), { json: options.json }));
4521
+ return 0;
4522
+ }
4523
+ async function executeDoctorCommand(options) {
4524
+ assertNoArgs("doctor", options.argv);
4525
+ const status = await collectStatus(options.collect);
4526
+ const checks = doctorChecks(status);
4527
+ const failed = checks.filter((check) => check.level === "fail").length;
4528
+ const warned = checks.filter((check) => check.level === "warn").length;
4529
+ options.io.stdout.write(renderSuccess({
4530
+ summary: failed > 0 ? `${failed} problem(s) to fix` : warned > 0 ? `working \u2014 ${warned} suggestion(s)` : "all good",
4531
+ checks
4532
+ }, { json: options.json }));
4533
+ return failed > 0 ? 1 : 0;
4534
+ }
4535
+ function statusView(status) {
4536
+ const signedIn = status.token.state === "env" || status.token.state === "signed-in";
4537
+ return {
4538
+ cli: status.cliVersion,
4539
+ endpoint: status.endpoint,
4540
+ env: status.env,
4541
+ signIn: status.token,
4542
+ account: accountView(status.account, signedIn, status.endpoint),
4543
+ agents: status.agents.map((agent) => ({
4544
+ client: AGENT_CLIENT_LABELS[agent.client],
4545
+ state: !agent.installed ? "not installed" : agent.problem ? `unknown (${agent.problem})` : !agent.entry ? "not connected" : agent.ours ? `connected${agent.scope ? ` (${agent.scope})` : ""}` : `someone else's entry${agent.scope ? ` (${agent.scope})` : ""}`,
4546
+ ...agent.pinnedVersion ? { bridge: `@ainecto/mcp@${agent.pinnedVersion}` } : {},
4547
+ ...agent.carriesRole ? { legacyRole: true } : {}
4548
+ })),
4549
+ ...status.claudeHere?.entry ? { claudeCodeHere: { scope: status.claudeHere.scope, ours: status.claudeHere.ours } } : {},
4550
+ repository: status.repository,
4551
+ ...status.globalCliVersion ? { globalAinecto: status.globalCliVersion } : {}
4552
+ };
4553
+ }
4554
+ function assertNoArgs(command, argv) {
4555
+ const unknown = argv.filter((arg) => arg !== "--json");
4556
+ if (unknown.length > 0) {
4557
+ throw new Error(`Unknown argument for ${command}: ${unknown[0]}`);
4558
+ }
4040
4559
  }
4041
4560
 
4042
4561
  // src/adapters/cli/ainectoCli.ts
4562
+ import { homedir as homedir2 } from "os";
4043
4563
  async function runAinectoCli(argv, io) {
4044
4564
  let parsed = { json: argv.includes("--json"), help: false, version: false, positionals: [] };
4045
4565
  try {
@@ -4053,15 +4573,21 @@ async function runAinectoCli(argv, io) {
4053
4573
  return 0;
4054
4574
  }
4055
4575
  const resolved = resolveEndpoint({ env: parsed.env, endpoint: parsed.endpoint });
4056
- const explicitRole = () => resolveRole({ role: parsed.role });
4576
+ const [domain, action, ...rest] = parsed.positionals;
4577
+ const legacyRole = legacyRoleInput(parsed.legacyRole);
4578
+ if (legacyRole !== void 0 && domain !== "mcp") {
4579
+ io.stderr.write(parsed.json ? `${JSON.stringify({ event: "legacy_role_ignored", message: LEGACY_ROLE_NOTICE })}
4580
+ ` : `${LEGACY_ROLE_NOTICE}
4581
+ `);
4582
+ }
4057
4583
  const tokenStore = new FileTokenStore();
4058
- const connect = (role) => {
4584
+ const connect = () => {
4059
4585
  const auth = new OAuthClient({
4060
4586
  endpoint: resolved.endpoint,
4061
4587
  tokenStore,
4062
- // ★init --dry-run 은 토큰을 갱신하지도 않는다 — 「아무것도 바꾸지 않는다」에 로그인 상태도 든다.
4063
- // (init 의 플래그는 positional 로 흘러온다.)
4064
- readOnly: parsed.positionals[0] === "init" && parsed.positionals.includes("--dry-run"),
4588
+ // ★init·setup --dry-run 은 토큰을 갱신하지도 않는다 — 「아무것도 바꾸지 않는다」에 로그인 상태도 든다.
4589
+ // (하위 명령의 플래그는 positional 로 흘러온다.)
4590
+ readOnly: (domain === "init" || domain === "setup") && parsed.positionals.includes("--dry-run"),
4065
4591
  // ★URL 을 «브라우저를 열기 전에» 알린다. stdout 은 결과 전용이라 stderr 에 쓴다.
4066
4592
  // --json 이면 JSON 한 줄이다(설계 §21-3 (가)). 로그인이 역할과 무관해져 role 칸은 없다(0.4.2 §2-5).
4067
4593
  onAuthorizeUrl: (url) => {
@@ -4071,15 +4597,13 @@ async function runAinectoCli(argv, io) {
4071
4597
  `);
4072
4598
  }
4073
4599
  });
4074
- const client2 = new McpRpcClient({ endpoint: resolved.endpoint, tokenProvider: auth, role });
4600
+ const client2 = new McpRpcClient({ endpoint: resolved.endpoint, tokenProvider: auth, legacyRole, channel: "cli" });
4075
4601
  return { auth, client: client2 };
4076
4602
  };
4077
- const [domain, action, ...rest] = parsed.positionals;
4078
4603
  if (domain === "mcp") {
4079
4604
  await runConnector({
4080
4605
  endpoint: resolved.endpoint,
4081
- // 브리지의 역할은 인자·환경변수에서만 온다 — bin/mcp.ts 와 같은 규칙. 저장소를 적용하지 않는다.
4082
- role: explicitRole().role ?? null,
4606
+ legacyRoleFlag: parsed.legacyRole,
4083
4607
  input: io.stdin,
4084
4608
  output: io.stdout,
4085
4609
  errorOutput: io.stderr
@@ -4087,36 +4611,27 @@ async function runAinectoCli(argv, io) {
4087
4611
  return 0;
4088
4612
  }
4089
4613
  if (domain === "init") {
4614
+ const { auth } = connect();
4090
4615
  return await executeInitCommand({
4091
4616
  argv: parsed.positionals.slice(1),
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();
4617
+ // 문서는 인증 후에 받는다 — 토큰이 없으면 패키지 기본값으로 가고, 그 사실을 보고한다.
4618
+ // ★«필요할 때» 가져온다. --undo 는 로컬 작업이라 토큰을 건드리지 않는다(S1).
4619
+ // ★갱신 실패는 «토큰 없음»(→ 로그인)이지만, AINECTO_TOKEN 을 못 쓰는 주소라는 거절은 삼키지 않는다 —
4620
+ // 삼키면 그 설정 오류가 브라우저 로그인으로 둔갑한다.
4621
+ accessToken: async () => {
4622
+ try {
4623
+ return await auth.getAccessToken();
4624
+ } catch (error) {
4625
+ if (process.env.AINECTO_TOKEN) {
4626
+ throw error;
4115
4627
  }
4116
- };
4628
+ return void 0;
4629
+ }
4630
+ },
4631
+ // 로그인한다 — `ainecto auth login` 과 같은 함수다. 서버마다 한 번이다.
4632
+ login: async () => {
4633
+ await auth.login();
4117
4634
  },
4118
- // ★대화형일 때만 묻는다(판정은 isInteractive 한 곳). 에이전트·CI 는 TTY 가 아니다.
4119
- prompter: isInteractive(io, parsed.json) ? createReadlinePrompter(io) : void 0,
4120
4635
  endpoint: resolved.endpoint,
4121
4636
  env: resolved.env,
4122
4637
  cliVersion: `${PACKAGE_NAME} ${CLI_VERSION}`,
@@ -4125,23 +4640,54 @@ async function runAinectoCli(argv, io) {
4125
4640
  io
4126
4641
  });
4127
4642
  }
4643
+ if (domain === "setup") {
4644
+ const { auth, client: client2 } = connect();
4645
+ return await executeSetupCommand({
4646
+ argv: parsed.positionals.slice(1),
4647
+ endpoint: resolved.endpoint,
4648
+ env: resolved.env,
4649
+ auth,
4650
+ client: client2,
4651
+ // ★외부 CLI 는 홈 폴더에서 돈다 — 지금 저장소의 local·project 범위 항목이 사용자 범위 판정에 끼지 않게.
4652
+ runner: systemCommandRunner({ cwd: homedir2() }),
4653
+ home: homedir2(),
4654
+ json: parsed.json,
4655
+ io
4656
+ });
4657
+ }
4658
+ if (domain === "status" || domain === "doctor") {
4659
+ const { auth, client: client2 } = connect();
4660
+ const run = domain === "status" ? executeStatusCommand : executeDoctorCommand;
4661
+ return await run({
4662
+ argv: parsed.positionals.slice(1),
4663
+ collect: {
4664
+ endpoint: resolved.endpoint,
4665
+ env: resolved.env,
4666
+ cliVersion: CLI_VERSION,
4667
+ tokenStore,
4668
+ auth,
4669
+ client: client2,
4670
+ runner: systemCommandRunner({ cwd: homedir2() }),
4671
+ hereRunner: systemCommandRunner({ cwd: process.cwd() }),
4672
+ home: homedir2(),
4673
+ cwd: process.cwd(),
4674
+ envVars: process.env
4675
+ },
4676
+ json: parsed.json,
4677
+ io
4678
+ });
4679
+ }
4128
4680
  if (domain === "auth") {
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);
4681
+ return await handleAuth(action, connect().auth, resolved.endpoint, parsed.json, io);
4133
4682
  }
4134
4683
  const env = parsed.env ?? "prod";
4135
4684
  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
- });
4685
+ return await printCatalog(parsed, io, resolved.endpoint, async () => connect().client);
4140
4686
  }
4141
4687
  let routeTools = getGeneratedTools(env);
4142
- let remote = isAttachmentsUploadCommand(domain, action) || domain === "tools" && (action === "list" || action === "call") || matchGeneratedCommand(routeTools, parsed.positionals) !== void 0;
4688
+ let remote = isAttachmentsUploadCommand(domain, action) || isInboxPollCommand(domain, action) || domain === "tools" && (action === "list" || action === "call") || matchGeneratedCommand(routeTools, parsed.positionals) !== void 0;
4143
4689
  if (!remote) {
4144
- const live = await loadLiveTools(env, connect(null).client).catch(() => void 0);
4690
+ const live = await loadLiveTools(env, connect().client).catch(() => void 0);
4145
4691
  if (live && matchGeneratedCommand(live, parsed.positionals)) {
4146
4692
  routeTools = live;
4147
4693
  remote = true;
@@ -4150,8 +4696,7 @@ async function runAinectoCli(argv, io) {
4150
4696
  if (!remote) {
4151
4697
  throw new Error(domain === "tools" ? "Expected tools list, tools call <mcpName>, or tools catalog." : `Unknown command "${domain}".`);
4152
4698
  }
4153
- const explicit = explicitRole();
4154
- const { client } = connect(await commandRole(explicit, process.cwd()));
4699
+ const { client } = connect();
4155
4700
  if (isAttachmentsUploadCommand(domain, action)) {
4156
4701
  return await executeAttachmentsUploadCommand({
4157
4702
  argv: rest,
@@ -4160,6 +4705,9 @@ async function runAinectoCli(argv, io) {
4160
4705
  io
4161
4706
  });
4162
4707
  }
4708
+ if (isInboxPollCommand(domain, action)) {
4709
+ return await executeInboxPollCommand({ domain, action, argv: rest, client, json: parsed.json, io });
4710
+ }
4163
4711
  const generatedResult = await executeGeneratedCommand({
4164
4712
  tools: routeTools,
4165
4713
  argv: parsed.positionals,
@@ -4179,23 +4727,6 @@ async function runAinectoCli(argv, io) {
4179
4727
  return 1;
4180
4728
  }
4181
4729
  }
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
- }
4199
4730
  async function handleAuth(action, auth, endpoint, json, io) {
4200
4731
  if (action === "login") {
4201
4732
  await auth.login();
@@ -4260,11 +4791,15 @@ function parseGlobalArgs(argv) {
4260
4791
  continue;
4261
4792
  }
4262
4793
  if (arg === "--env") {
4263
- result.env = assertEnv(requireValue4(arg, argv[++index]));
4794
+ result.env = assertEnv(requireValue3(arg, argv[++index]));
4264
4795
  } else if (arg === "--endpoint") {
4265
- result.endpoint = requireValue4(arg, argv[++index]);
4796
+ result.endpoint = requireValue3(arg, argv[++index]);
4266
4797
  } else if (arg === "--role") {
4267
- result.role = assertRole(requireValue4(arg, argv[++index]));
4798
+ const value = argv[index + 1];
4799
+ if (value !== void 0 && !value.startsWith("--")) {
4800
+ result.legacyRole = value;
4801
+ index += 1;
4802
+ }
4268
4803
  } else if (arg === "--json") {
4269
4804
  result.json = true;
4270
4805
  } else if (arg === "--help" || arg === "-h") {
@@ -4286,7 +4821,7 @@ function parsePayloadArgs(argv) {
4286
4821
  continue;
4287
4822
  }
4288
4823
  if (arg === "--file" || arg === "-f") {
4289
- result.file = requireValue4(arg, argv[++index]);
4824
+ result.file = requireValue3(arg, argv[++index]);
4290
4825
  } else {
4291
4826
  inline.push(arg);
4292
4827
  }
@@ -4296,40 +4831,48 @@ function parsePayloadArgs(argv) {
4296
4831
  }
4297
4832
  return result;
4298
4833
  }
4299
- function requireValue4(flag, value) {
4834
+ function requireValue3(flag, value) {
4300
4835
  if (!value || value.startsWith("--")) {
4301
4836
  throw new Error(`${flag} requires a value.`);
4302
4837
  }
4303
4838
  return value;
4304
4839
  }
4305
4840
  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."
4841
+ return { next: NEXT_STEP };
4842
+ }
4843
+ var NEXT_STEP = [
4844
+ "Connect your coding agents to AiNecto once per machine: `npx -y ainecto@latest setup`.",
4845
+ "AI agents: tell the user this now, even if they only asked to install, update, or sign in.",
4846
+ "To add AiNecto's working rules to a repository, run `npx -y ainecto@latest init` from its root.",
4847
+ "`npx -y ainecto@latest doctor` shows what is still missing and the command that fixes it."
4315
4848
  ].join("\n");
4316
4849
  function helpText() {
4317
4850
  return [
4318
4851
  "AiNecto CLI",
4319
4852
  "",
4320
- NEXT_STEP_INIT,
4853
+ NEXT_STEP,
4321
4854
  "",
4322
4855
  "Usage:",
4323
4856
  " ainecto auth login|status|logout [--env prod|dev] [--json] (one sign-in per machine)",
4324
4857
  " ainecto tools list [--env prod|dev] [--endpoint URL] [--json]",
4325
4858
  " ainecto tools call <mcpName> [-f payload.json|@-] [inline-json] [--json]",
4326
4859
  " ainecto attachments upload --document-uuid <uuid> <file...> [--json]",
4860
+ " ainecto notifications poll --cursor-file <path> [--kinds A,B] [--workspace <uuid>]... [--project <uuid>]... [--direct-only] [--limit N] [--json]",
4861
+ " ainecto notifications ack --cursor-file <path> (after the agent handled the polled batch)",
4862
+ " ainecto agent-todos poll [--workspace <uuid>]... [--project <uuid>]... [--only-new --seen-file <path>] [--json]",
4863
+ " --workspace and --project may repeat: any of the given workspaces AND any of the given projects",
4864
+ " poll exit codes: 0 new items \xB7 3 nothing new \xB7 1 error \xB7 2 usage error",
4327
4865
  " ainecto <generated-command> [flags] [-f payload.json] [--yes] [--json]",
4328
- " ainecto mcp [--env prod|dev] [--endpoint URL] [--role design|development|test|validation]",
4866
+ " ainecto mcp [--env prod|dev] [--endpoint URL]",
4867
+ "",
4868
+ "Setup (once per machine):",
4869
+ " ainecto setup [--env prod|dev] [--endpoint URL] [--dry-run] [--json] sign in and connect Claude Code, Cursor, and Codex",
4870
+ " ainecto setup --undo remove those connections (sign-in stays)",
4871
+ " ainecto status [--json] who is signed in, and what is connected (read-only)",
4872
+ " ainecto doctor [--json] status + what is missing and the command that fixes it",
4329
4873
  "",
4330
4874
  "Harness:",
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.",
4875
+ " ainecto init [--mcp] [--dry-run] [--json] AiNecto rules for this repository (--mcp also writes .mcp.json and .cursor/mcp.json)",
4333
4876
  " ainecto init --undo",
4334
4877
  ""
4335
4878
  ].join("\n");