@ainecto/mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -0
- package/dist/bin/ainecto.js +1691 -0
- package/dist/bin/ainecto.js.map +1 -0
- package/dist/bin/mcp.js +586 -0
- package/dist/bin/mcp.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,1691 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/auth/tokenStore.ts
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
import { mkdir, readFile, rename, rm, stat, writeFile, chmod } from "fs/promises";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { dirname, join } from "path";
|
|
8
|
+
var FileTokenStore = class {
|
|
9
|
+
filePath;
|
|
10
|
+
constructor(rootDir = join(homedir(), ".ainecto")) {
|
|
11
|
+
this.filePath = join(rootDir, "tokens.json");
|
|
12
|
+
}
|
|
13
|
+
async load(endpoint) {
|
|
14
|
+
const file = await this.readFile();
|
|
15
|
+
return file[tokenKey(endpoint)];
|
|
16
|
+
}
|
|
17
|
+
async save(endpoint, token) {
|
|
18
|
+
const file = await this.readFile();
|
|
19
|
+
file[tokenKey(endpoint)] = { ...token, endpoint, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20
|
+
await this.writeFile(file);
|
|
21
|
+
}
|
|
22
|
+
async delete(endpoint) {
|
|
23
|
+
const file = await this.readFile();
|
|
24
|
+
delete file[tokenKey(endpoint)];
|
|
25
|
+
await this.writeFile(file);
|
|
26
|
+
}
|
|
27
|
+
async readFile() {
|
|
28
|
+
try {
|
|
29
|
+
const raw = await readFile(this.filePath, "utf8");
|
|
30
|
+
const parsed = JSON.parse(raw);
|
|
31
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
32
|
+
throw new Error("Token file must contain an object.");
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (isNodeError(error, "ENOENT")) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async writeFile(file) {
|
|
43
|
+
await mkdir(dirname(this.filePath), { recursive: true, mode: 448 });
|
|
44
|
+
await chmod(dirname(this.filePath), 448);
|
|
45
|
+
const tmpPath = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
46
|
+
await writeFile(tmpPath, `${JSON.stringify(file, null, 2)}
|
|
47
|
+
`, { mode: 384 });
|
|
48
|
+
await chmod(tmpPath, 384);
|
|
49
|
+
await rename(tmpPath, this.filePath);
|
|
50
|
+
await chmod(this.filePath, 384);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function tokenKey(endpoint) {
|
|
54
|
+
return createHash("sha256").update(endpoint).digest("hex");
|
|
55
|
+
}
|
|
56
|
+
function isNodeError(error, code) {
|
|
57
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/core/auth/oauth.ts
|
|
61
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
62
|
+
import { createServer } from "http";
|
|
63
|
+
import { spawn } from "child_process";
|
|
64
|
+
import { URLSearchParams } from "url";
|
|
65
|
+
|
|
66
|
+
// src/core/auth/clientRegistration.ts
|
|
67
|
+
async function registerPublicClient(registrationEndpoint, redirectUri, fetchImpl = fetch) {
|
|
68
|
+
const response = await fetchImpl(registrationEndpoint, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
client_name: "Ainecto CLI",
|
|
73
|
+
redirect_uris: [redirectUri],
|
|
74
|
+
token_endpoint_auth_method: "none",
|
|
75
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
76
|
+
response_types: ["code"]
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`OAuth client registration failed with HTTP ${response.status}.`);
|
|
81
|
+
}
|
|
82
|
+
const body = await response.json();
|
|
83
|
+
if (!isRecord(body) || typeof body.client_id !== "string") {
|
|
84
|
+
throw new Error("OAuth client registration response did not include client_id.");
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
clientId: body.client_id,
|
|
88
|
+
clientSecret: typeof body.client_secret === "string" ? body.client_secret : void 0
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function isRecord(value) {
|
|
92
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/core/auth/oauth.ts
|
|
96
|
+
var OAuthClient = class {
|
|
97
|
+
constructor(options) {
|
|
98
|
+
this.options = options;
|
|
99
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
100
|
+
this.envVars = options.envVars ?? process.env;
|
|
101
|
+
this.openBrowserImpl = options.openBrowser ?? openBrowser;
|
|
102
|
+
}
|
|
103
|
+
options;
|
|
104
|
+
fetchImpl;
|
|
105
|
+
envVars;
|
|
106
|
+
openBrowserImpl;
|
|
107
|
+
async getAccessToken() {
|
|
108
|
+
const envToken = this.envVars.AINECTO_TOKEN;
|
|
109
|
+
if (envToken) {
|
|
110
|
+
return envToken;
|
|
111
|
+
}
|
|
112
|
+
const stored = await this.options.tokenStore.load(this.options.endpoint);
|
|
113
|
+
if (!stored) {
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
if (!shouldRefresh(stored)) {
|
|
117
|
+
return stored.accessToken;
|
|
118
|
+
}
|
|
119
|
+
return this.refreshStoredToken(stored);
|
|
120
|
+
}
|
|
121
|
+
async refreshAfterUnauthorized() {
|
|
122
|
+
const stored = await this.options.tokenStore.load(this.options.endpoint);
|
|
123
|
+
if (!stored?.refreshToken) {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
return this.refreshStoredToken(stored);
|
|
127
|
+
}
|
|
128
|
+
async login() {
|
|
129
|
+
const pkce = generatePkcePair();
|
|
130
|
+
const state = generateOAuthState();
|
|
131
|
+
const loopback = await createLoopbackReceiver(state);
|
|
132
|
+
try {
|
|
133
|
+
const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);
|
|
134
|
+
const registration = metadata.registrationEndpoint ? await registerPublicClient(metadata.registrationEndpoint, loopback.redirectUri, this.fetchImpl) : { clientId: "ainecto-cli" };
|
|
135
|
+
const authorizeUrl = new URL(metadata.authorizationEndpoint);
|
|
136
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
137
|
+
authorizeUrl.searchParams.set("client_id", registration.clientId);
|
|
138
|
+
authorizeUrl.searchParams.set("redirect_uri", loopback.redirectUri);
|
|
139
|
+
authorizeUrl.searchParams.set("code_challenge", pkce.codeChallenge);
|
|
140
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
141
|
+
authorizeUrl.searchParams.set("resource", this.options.endpoint);
|
|
142
|
+
authorizeUrl.searchParams.set("state", state);
|
|
143
|
+
await this.openBrowserImpl(authorizeUrl.toString());
|
|
144
|
+
const code = await loopback.waitForCode();
|
|
145
|
+
const token = await exchangeToken(metadata.tokenEndpoint, {
|
|
146
|
+
grant_type: "authorization_code",
|
|
147
|
+
code,
|
|
148
|
+
redirect_uri: loopback.redirectUri,
|
|
149
|
+
client_id: registration.clientId,
|
|
150
|
+
code_verifier: pkce.codeVerifier,
|
|
151
|
+
resource: this.options.endpoint,
|
|
152
|
+
...registration.clientSecret ? { client_secret: registration.clientSecret } : {}
|
|
153
|
+
}, this.fetchImpl);
|
|
154
|
+
const stored = toStoredToken(this.options.endpoint, token, metadata, registration);
|
|
155
|
+
await this.options.tokenStore.save(this.options.endpoint, stored);
|
|
156
|
+
return stored;
|
|
157
|
+
} finally {
|
|
158
|
+
await loopback.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async logout() {
|
|
162
|
+
await this.options.tokenStore.delete(this.options.endpoint);
|
|
163
|
+
}
|
|
164
|
+
async refreshStoredToken(stored) {
|
|
165
|
+
if (!stored.refreshToken) {
|
|
166
|
+
return stored.accessToken;
|
|
167
|
+
}
|
|
168
|
+
const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);
|
|
169
|
+
const token = await exchangeToken(metadata.tokenEndpoint, {
|
|
170
|
+
grant_type: "refresh_token",
|
|
171
|
+
refresh_token: stored.refreshToken,
|
|
172
|
+
client_id: stored.clientId ?? "ainecto-cli",
|
|
173
|
+
...stored.clientSecret ? { client_secret: stored.clientSecret } : {},
|
|
174
|
+
resource: this.options.endpoint
|
|
175
|
+
}, this.fetchImpl);
|
|
176
|
+
const next = toStoredToken(this.options.endpoint, token, metadata, stored);
|
|
177
|
+
await this.options.tokenStore.save(this.options.endpoint, next);
|
|
178
|
+
return next.accessToken;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
function generatePkcePair() {
|
|
182
|
+
const codeVerifier = randomBytes(32).toString("base64url");
|
|
183
|
+
const codeChallenge = createHash2("sha256").update(codeVerifier).digest("base64url");
|
|
184
|
+
return { codeVerifier, codeChallenge };
|
|
185
|
+
}
|
|
186
|
+
function generateOAuthState() {
|
|
187
|
+
return randomBytes(32).toString("base64url");
|
|
188
|
+
}
|
|
189
|
+
function shouldRefresh(token, now = Date.now()) {
|
|
190
|
+
if (!token.expiresAt) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
return token.expiresAt - now < 6e4;
|
|
194
|
+
}
|
|
195
|
+
async function discoverOAuthMetadata(endpoint, fetchImpl = fetch) {
|
|
196
|
+
const resourceMetadataUrl = await discoverResourceMetadataUrl(endpoint, fetchImpl);
|
|
197
|
+
const resourceMetadata = await fetchJson(resourceMetadataUrl, fetchImpl);
|
|
198
|
+
const authorizationServers = resourceMetadata.authorization_servers;
|
|
199
|
+
if (!Array.isArray(authorizationServers) || typeof authorizationServers[0] !== "string") {
|
|
200
|
+
throw new Error("OAuth protected resource metadata did not include authorization_servers.");
|
|
201
|
+
}
|
|
202
|
+
const authServer = authorizationServers[0];
|
|
203
|
+
const metadataUrl = authServer.includes("/.well-known/") ? authServer : `${authServer.replace(/\/$/, "")}/.well-known/oauth-authorization-server`;
|
|
204
|
+
const metadata = await fetchJson(metadataUrl, fetchImpl);
|
|
205
|
+
if (typeof metadata.authorization_endpoint !== "string" || typeof metadata.token_endpoint !== "string") {
|
|
206
|
+
throw new Error("OAuth authorization server metadata is missing authorization_endpoint or token_endpoint.");
|
|
207
|
+
}
|
|
208
|
+
const codeChallengeMethodsSupported = parseStringArray(metadata.code_challenge_methods_supported);
|
|
209
|
+
if (!codeChallengeMethodsSupported.includes("S256")) {
|
|
210
|
+
throw new Error("OAuth authorization server metadata must support PKCE S256.");
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
authorizationEndpoint: metadata.authorization_endpoint,
|
|
214
|
+
tokenEndpoint: metadata.token_endpoint,
|
|
215
|
+
registrationEndpoint: typeof metadata.registration_endpoint === "string" ? metadata.registration_endpoint : void 0,
|
|
216
|
+
issuer: typeof metadata.issuer === "string" ? metadata.issuer : void 0,
|
|
217
|
+
codeChallengeMethodsSupported
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
async function discoverResourceMetadataUrl(endpoint, fetchImpl) {
|
|
221
|
+
const challengeUrl = new URL(endpoint);
|
|
222
|
+
const response = await fetchImpl(challengeUrl, { method: "GET" });
|
|
223
|
+
const challenge = response.headers.get("www-authenticate");
|
|
224
|
+
const fromChallenge = challenge ? parseBearerChallengeParam(challenge, "resource_metadata") : void 0;
|
|
225
|
+
if (fromChallenge) {
|
|
226
|
+
return fromChallenge;
|
|
227
|
+
}
|
|
228
|
+
const origin = new URL(endpoint).origin;
|
|
229
|
+
return `${origin}/.well-known/oauth-protected-resource`;
|
|
230
|
+
}
|
|
231
|
+
function parseBearerChallengeParam(header, name) {
|
|
232
|
+
const pattern = new RegExp(`${name}="([^"]+)"`);
|
|
233
|
+
return pattern.exec(header)?.[1];
|
|
234
|
+
}
|
|
235
|
+
async function exchangeToken(tokenEndpoint, params, fetchImpl) {
|
|
236
|
+
const response = await fetchImpl(tokenEndpoint, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
239
|
+
body: new URLSearchParams(params)
|
|
240
|
+
});
|
|
241
|
+
if (!response.ok) {
|
|
242
|
+
throw new Error(`OAuth token exchange failed with HTTP ${response.status}.`);
|
|
243
|
+
}
|
|
244
|
+
return response.json();
|
|
245
|
+
}
|
|
246
|
+
function toStoredToken(endpoint, token, metadata, previous) {
|
|
247
|
+
if (typeof token.access_token !== "string") {
|
|
248
|
+
throw new Error("OAuth token response did not include access_token.");
|
|
249
|
+
}
|
|
250
|
+
const expiresIn = typeof token.expires_in === "number" ? token.expires_in : void 0;
|
|
251
|
+
return {
|
|
252
|
+
endpoint,
|
|
253
|
+
accessToken: token.access_token,
|
|
254
|
+
refreshToken: typeof token.refresh_token === "string" ? token.refresh_token : previous?.refreshToken,
|
|
255
|
+
tokenType: typeof token.token_type === "string" ? token.token_type : previous?.tokenType,
|
|
256
|
+
expiresAt: expiresIn ? Date.now() + expiresIn * 1e3 : previous?.expiresAt,
|
|
257
|
+
scope: typeof token.scope === "string" ? token.scope : previous?.scope,
|
|
258
|
+
clientId: previous?.clientId ?? "ainecto-cli",
|
|
259
|
+
clientSecret: previous?.clientSecret,
|
|
260
|
+
authorizationServer: metadata.issuer,
|
|
261
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async function createLoopbackReceiver(expectedState) {
|
|
265
|
+
let resolveCode;
|
|
266
|
+
let rejectCode;
|
|
267
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
268
|
+
resolveCode = resolve;
|
|
269
|
+
rejectCode = reject;
|
|
270
|
+
});
|
|
271
|
+
codePromise.catch(() => void 0);
|
|
272
|
+
const server = createServer((req, res) => {
|
|
273
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
274
|
+
const error = url.searchParams.get("error");
|
|
275
|
+
const code = url.searchParams.get("code");
|
|
276
|
+
const state = url.searchParams.get("state");
|
|
277
|
+
if (error) {
|
|
278
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
279
|
+
res.end("Ainecto CLI authorization failed. You can close this tab.");
|
|
280
|
+
rejectCode(new Error(`OAuth authorization failed: ${error}`));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (!state || state !== expectedState) {
|
|
284
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
285
|
+
res.end("Ainecto CLI authorization state mismatch. You can close this tab.");
|
|
286
|
+
rejectCode(new Error("OAuth state mismatch."));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (!code) {
|
|
290
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
291
|
+
res.end("Missing OAuth code.");
|
|
292
|
+
rejectCode(new Error("OAuth redirect did not include a code."));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
res.writeHead(200, { "content-type": "text/plain" });
|
|
296
|
+
res.end("Ainecto CLI authorization complete. You can close this tab.");
|
|
297
|
+
resolveCode(code);
|
|
298
|
+
});
|
|
299
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
300
|
+
const address = server.address();
|
|
301
|
+
if (!address || typeof address === "string") {
|
|
302
|
+
throw new Error("Loopback OAuth server did not bind to a TCP port.");
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
redirectUri: `http://127.0.0.1:${address.port}/callback`,
|
|
306
|
+
waitForCode: () => codePromise,
|
|
307
|
+
close: () => new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()))
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function parseStringArray(value) {
|
|
311
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
312
|
+
}
|
|
313
|
+
async function fetchJson(url, fetchImpl) {
|
|
314
|
+
const response = await fetchImpl(url);
|
|
315
|
+
if (!response.ok) {
|
|
316
|
+
throw new Error(`Failed to fetch OAuth metadata from ${url}: HTTP ${response.status}.`);
|
|
317
|
+
}
|
|
318
|
+
return response.json();
|
|
319
|
+
}
|
|
320
|
+
async function openBrowser(url) {
|
|
321
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
322
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
323
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
324
|
+
child.unref();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/core/config/endpoints.ts
|
|
328
|
+
var DEFAULT_ENDPOINTS = {
|
|
329
|
+
prod: "https://ainecto.com/mcp",
|
|
330
|
+
dev: "https://dev.ainecto.com/mcp"
|
|
331
|
+
};
|
|
332
|
+
function resolveEndpoint(input = {}) {
|
|
333
|
+
const envVars = input.envVars ?? process.env;
|
|
334
|
+
const env = input.env ?? "prod";
|
|
335
|
+
if (input.endpoint) {
|
|
336
|
+
return { env, endpoint: normalizeEndpoint(input.endpoint), source: "flag" };
|
|
337
|
+
}
|
|
338
|
+
if (envVars.AINECTO_MCP_ENDPOINT) {
|
|
339
|
+
return { env, endpoint: normalizeEndpoint(envVars.AINECTO_MCP_ENDPOINT), source: "env" };
|
|
340
|
+
}
|
|
341
|
+
return { env, endpoint: DEFAULT_ENDPOINTS[env], source: "default" };
|
|
342
|
+
}
|
|
343
|
+
function normalizeEndpoint(endpoint) {
|
|
344
|
+
const url = new URL(endpoint);
|
|
345
|
+
url.hash = "";
|
|
346
|
+
return url.toString().replace(/\/$/, "");
|
|
347
|
+
}
|
|
348
|
+
function assertEnv(value) {
|
|
349
|
+
if (value === void 0) {
|
|
350
|
+
return void 0;
|
|
351
|
+
}
|
|
352
|
+
if (value === "prod" || value === "dev") {
|
|
353
|
+
return value;
|
|
354
|
+
}
|
|
355
|
+
throw new Error(`Invalid --env value "${value}". Expected "prod" or "dev".`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/core/input/payloadSource.ts
|
|
359
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
360
|
+
async function parsePayloadSource(input) {
|
|
361
|
+
const warnings = [];
|
|
362
|
+
const stdinIsTTY = input.stdinIsTTY ?? input.stdin?.isTTY ?? true;
|
|
363
|
+
const hasInline = input.inline !== void 0 && input.inline !== "";
|
|
364
|
+
const hasStdin = input.stdinText !== void 0 || !stdinIsTTY;
|
|
365
|
+
if (input.file) {
|
|
366
|
+
if (hasInline || hasStdin) {
|
|
367
|
+
warnings.push("Multiple payload sources were provided; --file takes precedence.");
|
|
368
|
+
}
|
|
369
|
+
const text = await readFileOrStdin(input.file, input);
|
|
370
|
+
return { value: parseJson(text), source: input.file === "-" || input.file === "@-" ? "stdin" : "file", warnings };
|
|
371
|
+
}
|
|
372
|
+
if (hasStdin) {
|
|
373
|
+
if (hasInline) {
|
|
374
|
+
warnings.push("Multiple payload sources were provided; stdin takes precedence.");
|
|
375
|
+
}
|
|
376
|
+
const text = input.stdinText ?? await readAllStdin(input.stdin);
|
|
377
|
+
if (!text.trim()) {
|
|
378
|
+
return { value: {}, source: "empty", warnings };
|
|
379
|
+
}
|
|
380
|
+
return { value: parseJson(text), source: "stdin", warnings };
|
|
381
|
+
}
|
|
382
|
+
if (hasInline) {
|
|
383
|
+
const inline = input.inline === "@-" ? await readAllStdin(input.stdin) : input.inline;
|
|
384
|
+
return { value: parseJson(inline), source: "inline", warnings };
|
|
385
|
+
}
|
|
386
|
+
return { value: {}, source: "empty", warnings };
|
|
387
|
+
}
|
|
388
|
+
function parseJson(text) {
|
|
389
|
+
if (text === void 0 || !text.trim()) {
|
|
390
|
+
return {};
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
return JSON.parse(text);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
396
|
+
throw new Error(`Invalid JSON payload: ${detail}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async function readFileOrStdin(file, input) {
|
|
400
|
+
if (file === "-" || file === "@-") {
|
|
401
|
+
return input.stdinText ?? await readAllStdin(input.stdin);
|
|
402
|
+
}
|
|
403
|
+
return readFile2(file, "utf8");
|
|
404
|
+
}
|
|
405
|
+
async function readAllStdin(stdin) {
|
|
406
|
+
if (!stdin) {
|
|
407
|
+
return "";
|
|
408
|
+
}
|
|
409
|
+
stdin.setEncoding("utf8");
|
|
410
|
+
let text = "";
|
|
411
|
+
for await (const chunk of stdin) {
|
|
412
|
+
text += chunk;
|
|
413
|
+
}
|
|
414
|
+
return text;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/core/mcp/rpcClient.ts
|
|
418
|
+
var McpRpcError = class extends Error {
|
|
419
|
+
constructor(message, code, details) {
|
|
420
|
+
super(message);
|
|
421
|
+
this.code = code;
|
|
422
|
+
this.details = details;
|
|
423
|
+
}
|
|
424
|
+
code;
|
|
425
|
+
details;
|
|
426
|
+
};
|
|
427
|
+
var McpRpcClient = class {
|
|
428
|
+
constructor(options) {
|
|
429
|
+
this.options = options;
|
|
430
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
431
|
+
}
|
|
432
|
+
options;
|
|
433
|
+
fetchImpl;
|
|
434
|
+
nextId = 1;
|
|
435
|
+
initialize() {
|
|
436
|
+
return this.request("initialize", {
|
|
437
|
+
protocolVersion: "2024-11-05",
|
|
438
|
+
capabilities: {},
|
|
439
|
+
clientInfo: { name: "@ainecto/mcp", version: "0.1.0" }
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
async toolsList() {
|
|
443
|
+
const result = await this.request("tools/list", {});
|
|
444
|
+
if (!isRecord2(result) || !Array.isArray(result.tools)) {
|
|
445
|
+
throw new McpRpcError("tools/list response did not include a tools array.", "MCP_PROTOCOL_ERROR", result);
|
|
446
|
+
}
|
|
447
|
+
return result.tools;
|
|
448
|
+
}
|
|
449
|
+
async toolsCall(name, args) {
|
|
450
|
+
return this.request("tools/call", { name, arguments: args ?? {} });
|
|
451
|
+
}
|
|
452
|
+
async request(method, params) {
|
|
453
|
+
const response = await this.sendRaw({
|
|
454
|
+
jsonrpc: "2.0",
|
|
455
|
+
id: this.nextId++,
|
|
456
|
+
method,
|
|
457
|
+
params
|
|
458
|
+
});
|
|
459
|
+
if (!response) {
|
|
460
|
+
return void 0;
|
|
461
|
+
}
|
|
462
|
+
if ("error" in response) {
|
|
463
|
+
throw new McpRpcError(response.error.message, "MCP_ERROR", response.error);
|
|
464
|
+
}
|
|
465
|
+
return response.result;
|
|
466
|
+
}
|
|
467
|
+
async sendRaw(request) {
|
|
468
|
+
const token = await this.options.tokenProvider?.getAccessToken();
|
|
469
|
+
let response = await this.postJson(request, token);
|
|
470
|
+
if (response.status === 401 && this.options.tokenProvider) {
|
|
471
|
+
const refreshed = await this.options.tokenProvider.refreshAfterUnauthorized();
|
|
472
|
+
if (refreshed) {
|
|
473
|
+
response = await this.postJson(request, refreshed);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (response.status === 204 || response.status === 202) {
|
|
477
|
+
return void 0;
|
|
478
|
+
}
|
|
479
|
+
const text = await response.text();
|
|
480
|
+
if (!response.ok) {
|
|
481
|
+
throw new McpRpcError(`MCP HTTP request failed with HTTP ${response.status}.`, "MCP_HTTP_ERROR", {
|
|
482
|
+
status: response.status,
|
|
483
|
+
body: safeBodyPreview(text)
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
if (!text.trim()) {
|
|
487
|
+
return void 0;
|
|
488
|
+
}
|
|
489
|
+
const parsed = JSON.parse(text);
|
|
490
|
+
if (!isJsonRpcResponse(parsed)) {
|
|
491
|
+
throw new McpRpcError("MCP response was not a JSON-RPC response.", "MCP_PROTOCOL_ERROR", parsed);
|
|
492
|
+
}
|
|
493
|
+
return parsed;
|
|
494
|
+
}
|
|
495
|
+
postJson(request, token) {
|
|
496
|
+
const headers = {
|
|
497
|
+
"content-type": "application/json",
|
|
498
|
+
accept: "application/json"
|
|
499
|
+
};
|
|
500
|
+
if (token) {
|
|
501
|
+
headers.authorization = `Bearer ${token}`;
|
|
502
|
+
}
|
|
503
|
+
return this.fetchImpl(this.options.endpoint, {
|
|
504
|
+
method: "POST",
|
|
505
|
+
headers,
|
|
506
|
+
body: JSON.stringify(request)
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
function isJsonRpcResponse(value) {
|
|
511
|
+
return isRecord2(value) && value.jsonrpc === "2.0" && ("result" in value || "error" in value);
|
|
512
|
+
}
|
|
513
|
+
function isRecord2(value) {
|
|
514
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
515
|
+
}
|
|
516
|
+
function safeBodyPreview(body) {
|
|
517
|
+
return body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/core/output/render.ts
|
|
521
|
+
function renderSuccess(data, options = {}) {
|
|
522
|
+
if (options.json) {
|
|
523
|
+
const shape = { ok: true, data };
|
|
524
|
+
if (options.warnings?.length) {
|
|
525
|
+
shape.warnings = options.warnings;
|
|
526
|
+
}
|
|
527
|
+
return `${JSON.stringify(shape, null, 2)}
|
|
528
|
+
`;
|
|
529
|
+
}
|
|
530
|
+
const warnings = options.warnings?.length ? `${options.warnings.map((warning) => `Warning: ${warning}`).join("\n")}
|
|
531
|
+
` : "";
|
|
532
|
+
return `${warnings}${renderHuman(data)}
|
|
533
|
+
`;
|
|
534
|
+
}
|
|
535
|
+
function renderError(error, options = {}) {
|
|
536
|
+
const shape = toErrorShape(error);
|
|
537
|
+
if (options.json) {
|
|
538
|
+
return `${JSON.stringify(shape, null, 2)}
|
|
539
|
+
`;
|
|
540
|
+
}
|
|
541
|
+
return `Error: ${shape.error.message}
|
|
542
|
+
`;
|
|
543
|
+
}
|
|
544
|
+
function toErrorShape(error) {
|
|
545
|
+
if (error instanceof Error) {
|
|
546
|
+
const maybe = error;
|
|
547
|
+
return {
|
|
548
|
+
ok: false,
|
|
549
|
+
error: {
|
|
550
|
+
code: maybe.code ?? "CLI_ERROR",
|
|
551
|
+
message: error.message,
|
|
552
|
+
details: maybe.details
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
return { ok: false, error: { code: "CLI_ERROR", message: String(error) } };
|
|
557
|
+
}
|
|
558
|
+
function renderHuman(data) {
|
|
559
|
+
if (Array.isArray(data)) {
|
|
560
|
+
return data.map((item) => renderHuman(item)).join("\n");
|
|
561
|
+
}
|
|
562
|
+
if (typeof data === "string") {
|
|
563
|
+
return data;
|
|
564
|
+
}
|
|
565
|
+
return JSON.stringify(data, null, 2);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// src/core/catalog/generated.prod.ts
|
|
569
|
+
var generatedTools = [
|
|
570
|
+
{ "mcpName": "mcp__ainecto__list_projects", "commandPath": ["projects", "list"], "group": "generic", "description": "List projects visible to the authenticated user.", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "prod", "schemaHash": "15947335f3573335" }
|
|
571
|
+
];
|
|
572
|
+
|
|
573
|
+
// src/core/catalog/generated.dev.ts
|
|
574
|
+
var generatedTools2 = [
|
|
575
|
+
{ "mcpName": "mcp__ainecto__create_documents", "commandPath": ["documents", "create"], "group": "document", "description": "Create documents. If a document body is tens of KB or more, prefer request_upload_token (purpose=document.create) and upload the body with curl.", "inputSchema": { "properties": { "items": { "description": "Documents", "items": { "properties": { "body": { "description": "Deprecated compatibility alias for typePayload.body when document type is markdown. \uBCF8\uBB38\uC774 \uC218\uC2ED KB \uC774\uC0C1\uC73C\uB85C \uD06C\uBA74 LLM \uD1A0\uD070 \uC808\uC57D\uC744 \uC704\uD574 `request_upload_token (purpose=document.create)` \uB97C \uD638\uCD9C\uD574 uploadUrl \uC744 \uBC1B\uACE0 curl \uB85C \uC9C1\uC811 PUT \uC73C\uB85C \uBCF4\uB0BC \uAC83.", "type": "string" }, "folderUuid": { "description": "Optional folder UUID", "type": "string" }, "projectUuid": { "description": "Project UUID", "type": "string" }, "title": { "description": "Document title", "type": "string" }, "type": { "description": "Document type. Enabled values: erd, markdown, testcase, task.", "enum": ["erd", "markdown", "testcase", "task"], "type": "string" }, "typePayload": { "description": "Type-specific payload for enabled document types. markdown: {body}; 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": "82748a9e6d632c2d" },
|
|
576
|
+
{ "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" },
|
|
577
|
+
{ "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" },
|
|
578
|
+
{ "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" },
|
|
579
|
+
{ "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" },
|
|
580
|
+
{ "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" },
|
|
581
|
+
{ "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" },
|
|
582
|
+
{ "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" },
|
|
583
|
+
{ "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" },
|
|
584
|
+
{ "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" },
|
|
585
|
+
{ "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" },
|
|
586
|
+
{ "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" },
|
|
587
|
+
{ "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" },
|
|
588
|
+
{ "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" },
|
|
589
|
+
{ "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" },
|
|
590
|
+
{ "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" },
|
|
591
|
+
{ "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" },
|
|
592
|
+
{ "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" },
|
|
593
|
+
{ "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" },
|
|
594
|
+
{ "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" },
|
|
595
|
+
{ "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" },
|
|
596
|
+
{ "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" },
|
|
597
|
+
{ "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" },
|
|
598
|
+
{ "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" },
|
|
599
|
+
{ "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" },
|
|
600
|
+
{ "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" },
|
|
601
|
+
{ "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" },
|
|
602
|
+
{ "mcpName": "mcp__ainecto__list_documents", "commandPath": ["documents", "list"], "group": "document", "description": "", "inputSchema": { "properties": { "folderUuid": { "description": "Optional folder UUID", "type": "string" }, "limit": { "description": "Result cap when q is set (default 50)", "type": "integer" }, "projectUuid": { "description": "Project UUID", "type": "string" }, "q": { "description": "Optional full-text query. When provided, results come from the search index instead of folder listing.", "type": "string" }, "type": { "description": "Optional document type filter. Enabled values: erd, markdown, testcase, task.", "enum": ["erd", "markdown", "testcase", "task"], "type": "string" } }, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "dd179f8859509f8a" },
|
|
603
|
+
{ "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" },
|
|
604
|
+
{ "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" },
|
|
605
|
+
{ "mcpName": "mcp__ainecto__list_trash", "commandPath": ["trash", "list"], "group": "generic", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "82f8586d68bfcd7d" },
|
|
606
|
+
{ "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" },
|
|
607
|
+
{ "mcpName": "mcp__ainecto__list_workspaces", "commandPath": ["workspaces", "list"], "group": "workspace", "description": "", "inputSchema": { "properties": {}, "type": "object" }, "required": [], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "7c32c74000696b2f" },
|
|
608
|
+
{ "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" },
|
|
609
|
+
{ "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" },
|
|
610
|
+
{ "mcpName": "mcp__ainecto__move_documents", "commandPath": ["documents", "move"], "group": "document", "description": "", "inputSchema": { "properties": { "items": { "description": "Moves", "items": { "properties": { "newFolderUuid": { "description": "New folder UUID \u2014 null/empty for root", "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": "2ad5f10cc6313569" },
|
|
611
|
+
{ "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" },
|
|
612
|
+
{ "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. \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"], "type": "string" }, "scopeJson": { "description": "\uC120\uD0DD \u2014 JSON scope. document.create \uB294 projectUuid, type, title \uC744 \uD3EC\uD568\uD558\uACE0 enabled type values \uB294 erd, markdown, testcase, task. document.update/document.append \uB294 documentUuid \uB97C \uD3EC\uD568.", "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": "ea6f645a8da9cddf" },
|
|
613
|
+
{ "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" },
|
|
614
|
+
{ "mcpName": "mcp__ainecto__search_documents", "commandPath": ["documents", "search"], "group": "document", "description": "", "inputSchema": { "properties": { "limit": { "description": "\uACB0\uACFC \uAC1C\uC218 (\uAE30\uBCF8 20, \uCD5C\uB300 50)", "type": "integer" }, "offset": { "description": "\uD398\uC774\uC9C0\uB124\uC774\uC158 offset (\uAE30\uBCF8 0)", "type": "integer" }, "projectUuid": { "description": "\uD2B9\uC815 \uD504\uB85C\uC81D\uD2B8\uB85C \uD55C\uC815 (\uC120\uD0DD)", "type": "string" }, "q": { "description": "\uAC80\uC0C9 \uD0A4\uC6CC\uB4DC", "type": "string" }, "type": { "description": "Optional document type filter. Enabled values: erd, markdown, testcase, task.", "enum": ["erd", "markdown", "testcase", "task"], "type": "string" } }, "required": ["q"], "type": "object" }, "required": ["q"], "payloadMode": "flags", "destructive": false, "sourceCatalog": "dev", "schemaHash": "0110ec6ceba8993d" },
|
|
615
|
+
{ "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" },
|
|
616
|
+
{ "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" },
|
|
617
|
+
{ "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" },
|
|
618
|
+
{ "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" },
|
|
619
|
+
{ "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" },
|
|
620
|
+
{ "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" },
|
|
621
|
+
{ "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" },
|
|
622
|
+
{ "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" },
|
|
623
|
+
{ "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" },
|
|
624
|
+
{ "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" },
|
|
625
|
+
{ "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" },
|
|
626
|
+
{ "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" },
|
|
627
|
+
{ "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" },
|
|
628
|
+
{ "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" },
|
|
629
|
+
{ "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" },
|
|
630
|
+
{ "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" },
|
|
631
|
+
{ "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" },
|
|
632
|
+
{ "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" },
|
|
633
|
+
{ "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" },
|
|
634
|
+
{ "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" },
|
|
635
|
+
{ "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" },
|
|
636
|
+
{ "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" },
|
|
637
|
+
{ "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" },
|
|
638
|
+
{ "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" },
|
|
639
|
+
{ "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" },
|
|
640
|
+
{ "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" },
|
|
641
|
+
{ "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" },
|
|
642
|
+
{ "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" },
|
|
643
|
+
{ "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" },
|
|
644
|
+
{ "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" },
|
|
645
|
+
{ "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" },
|
|
646
|
+
{ "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}; 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": "7f0d7c8c17676b82" },
|
|
647
|
+
{ "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" },
|
|
648
|
+
{ "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" },
|
|
649
|
+
{ "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" },
|
|
650
|
+
{ "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)", "type": "string" } }, "required": ["documentUuid", "filename", "storageKey"], "type": "object" }, "type": "array" } }, "required": ["items"], "type": "object" }, "required": ["items"], "payloadMode": "binary-upload", "destructive": false, "sourceCatalog": "dev", "schemaHash": "e5e8a2f1fd708938" }
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
// src/core/catalog/index.ts
|
|
654
|
+
function getGeneratedTools(env) {
|
|
655
|
+
return env === "dev" ? generatedTools2 : generatedTools;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/core/mcp/stdioBridge.ts
|
|
659
|
+
import { createInterface } from "readline";
|
|
660
|
+
async function runStdioBridge(options) {
|
|
661
|
+
const lines = createInterface({ input: options.input, crlfDelay: Infinity });
|
|
662
|
+
for await (const line of lines) {
|
|
663
|
+
if (!line.trim()) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
const request = parseJsonRpcRequest(line);
|
|
668
|
+
const response = await options.client.sendRaw(request);
|
|
669
|
+
if (response && request.id !== void 0) {
|
|
670
|
+
options.output.write(`${JSON.stringify(response)}
|
|
671
|
+
`);
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
675
|
+
options.output.write(`${JSON.stringify({
|
|
676
|
+
jsonrpc: "2.0",
|
|
677
|
+
id: null,
|
|
678
|
+
error: { code: -32603, message }
|
|
679
|
+
})}
|
|
680
|
+
`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function parseJsonRpcRequest(line) {
|
|
685
|
+
const parsed = JSON.parse(line);
|
|
686
|
+
if (!isRecord3(parsed) || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
|
|
687
|
+
throw new Error("Invalid JSON-RPC request.");
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
jsonrpc: "2.0",
|
|
691
|
+
id: typeof parsed.id === "string" || typeof parsed.id === "number" || parsed.id === null ? parsed.id : void 0,
|
|
692
|
+
method: parsed.method,
|
|
693
|
+
params: parsed.params
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function isRecord3(value) {
|
|
697
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// src/adapters/mcp/connector.ts
|
|
701
|
+
async function runConnector(options) {
|
|
702
|
+
const tokenStore = new FileTokenStore();
|
|
703
|
+
const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore });
|
|
704
|
+
const client = new McpRpcClient({ endpoint: options.endpoint, tokenProvider: auth });
|
|
705
|
+
await runStdioBridge({
|
|
706
|
+
input: options.input,
|
|
707
|
+
output: options.output,
|
|
708
|
+
errorOutput: options.errorOutput,
|
|
709
|
+
client
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// src/core/catalog/enrichments.ts
|
|
714
|
+
var enrichments = [
|
|
715
|
+
{
|
|
716
|
+
mcpName: "mcp__ainecto__list_projects",
|
|
717
|
+
outputHint: "table",
|
|
718
|
+
tableColumns: ["uuid", "name", "projectKey", "workspaceUuid", "updatedAt"],
|
|
719
|
+
displayName: "List projects",
|
|
720
|
+
examples: ["ainecto projects list --env dev"]
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
mcpName: "mcp__ainecto__list_workspaces",
|
|
724
|
+
outputHint: "table",
|
|
725
|
+
tableColumns: ["uuid", "name", "updatedAt"],
|
|
726
|
+
displayName: "List workspaces"
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
mcpName: "mcp__ainecto__list_folders",
|
|
730
|
+
outputHint: "table",
|
|
731
|
+
tableColumns: ["uuid", "name", "projectUuid", "parentFolderUuid", "updatedAt"],
|
|
732
|
+
displayName: "List folders"
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
mcpName: "mcp__ainecto__list_documents",
|
|
736
|
+
outputHint: "table",
|
|
737
|
+
tableColumns: ["uuid", "title", "type", "projectUuid", "folderUuid", "updatedAt"],
|
|
738
|
+
displayName: "List documents"
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
mcpName: "mcp__ainecto__search_documents",
|
|
742
|
+
outputHint: "table",
|
|
743
|
+
tableColumns: ["uuid", "title", "type", "projectUuid", "updatedAt"],
|
|
744
|
+
displayName: "Search documents"
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
mcpName: "mcp__ainecto__list_attachments",
|
|
748
|
+
outputHint: "table",
|
|
749
|
+
tableColumns: ["uuid", "filename", "contentType", "sizeBytes", "documentUuid"],
|
|
750
|
+
displayName: "List attachments"
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
mcpName: "mcp__ainecto__erd_list_tables",
|
|
754
|
+
outputHint: "table",
|
|
755
|
+
tableColumns: ["uuid", "name", "schema_name", "display_order"],
|
|
756
|
+
displayName: "List ERD tables"
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
mcpName: "mcp__ainecto__erd_list_refs",
|
|
760
|
+
outputHint: "table",
|
|
761
|
+
tableColumns: ["uuid", "name", "from_table_uuid", "to_table_uuid", "relationship"],
|
|
762
|
+
displayName: "List ERD refs"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
mcpName: "mcp__ainecto__erd_list_enums",
|
|
766
|
+
outputHint: "table",
|
|
767
|
+
tableColumns: ["uuid", "name", "display_order"],
|
|
768
|
+
displayName: "List ERD enums"
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
mcpName: "mcp__ainecto__erd_list_table_groups",
|
|
772
|
+
outputHint: "table",
|
|
773
|
+
tableColumns: ["uuid", "name", "display_order"],
|
|
774
|
+
displayName: "List ERD table groups"
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
mcpName: "mcp__ainecto__task_list_tasks",
|
|
778
|
+
outputHint: "table",
|
|
779
|
+
tableColumns: ["uuid", "title", "statusUuid", "assigneeId", "updatedAt"],
|
|
780
|
+
displayName: "List tasks"
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
mcpName: "mcp__ainecto__task_list_statuses",
|
|
784
|
+
outputHint: "table",
|
|
785
|
+
tableColumns: ["uuid", "name", "displayOrder"],
|
|
786
|
+
displayName: "List task statuses"
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
mcpName: "mcp__ainecto__enable_shares",
|
|
790
|
+
aliases: ["shares enable"],
|
|
791
|
+
displayName: "Enable shares"
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
mcpName: "mcp__ainecto__disable_shares",
|
|
795
|
+
aliases: ["shares disable"],
|
|
796
|
+
displayName: "Disable shares"
|
|
797
|
+
}
|
|
798
|
+
];
|
|
799
|
+
|
|
800
|
+
// src/core/output/table.ts
|
|
801
|
+
function renderTable(data, options = {}) {
|
|
802
|
+
const rows = extractRows(data);
|
|
803
|
+
if (!rows.length || !rows.every(isRecord4)) {
|
|
804
|
+
return void 0;
|
|
805
|
+
}
|
|
806
|
+
const columns = options.columns?.length ? options.columns : inferColumns(rows);
|
|
807
|
+
if (!columns.length) {
|
|
808
|
+
return void 0;
|
|
809
|
+
}
|
|
810
|
+
const widths = columns.map((column) => Math.max(column.length, ...rows.map((row) => formatCell(row[column]).length)));
|
|
811
|
+
const header = columns.map((column, index) => column.padEnd(widths[index] ?? column.length)).join(" ");
|
|
812
|
+
const divider = widths.map((width) => "-".repeat(width)).join(" ");
|
|
813
|
+
const body = rows.map((row) => columns.map((column, index) => formatCell(row[column]).padEnd(widths[index] ?? column.length)).join(" "));
|
|
814
|
+
return [header, divider, ...body].join("\n");
|
|
815
|
+
}
|
|
816
|
+
function extractRows(data) {
|
|
817
|
+
if (Array.isArray(data)) {
|
|
818
|
+
return data;
|
|
819
|
+
}
|
|
820
|
+
if (isRecord4(data)) {
|
|
821
|
+
for (const key of ["items", "projects", "workspaces", "folders", "documents", "tasks", "attachments", "results", "data"]) {
|
|
822
|
+
const value = data[key];
|
|
823
|
+
if (Array.isArray(value)) {
|
|
824
|
+
return value;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return [];
|
|
829
|
+
}
|
|
830
|
+
function inferColumns(rows) {
|
|
831
|
+
const preferred = ["uuid", "id", "name", "title", "status", "type", "projectUuid", "documentUuid", "createdAt", "updatedAt"];
|
|
832
|
+
const keys = new Set(rows.flatMap((row) => Object.keys(row)));
|
|
833
|
+
const columns = preferred.filter((key) => keys.has(key));
|
|
834
|
+
if (columns.length > 0) {
|
|
835
|
+
return columns.slice(0, 6);
|
|
836
|
+
}
|
|
837
|
+
return [...keys].slice(0, 6);
|
|
838
|
+
}
|
|
839
|
+
function formatCell(value) {
|
|
840
|
+
if (value === null || value === void 0) {
|
|
841
|
+
return "";
|
|
842
|
+
}
|
|
843
|
+
if (typeof value === "object") {
|
|
844
|
+
return JSON.stringify(value);
|
|
845
|
+
}
|
|
846
|
+
return String(value);
|
|
847
|
+
}
|
|
848
|
+
function isRecord4(value) {
|
|
849
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/adapters/cli/errors.ts
|
|
853
|
+
var CliCommandError = class extends Error {
|
|
854
|
+
constructor(code, message, details) {
|
|
855
|
+
super(message);
|
|
856
|
+
this.code = code;
|
|
857
|
+
this.details = details;
|
|
858
|
+
}
|
|
859
|
+
code;
|
|
860
|
+
details;
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// src/adapters/cli/flagParser.ts
|
|
864
|
+
async function parseGeneratedCommandArgs(tool, argv, stdin) {
|
|
865
|
+
const parsed = parseArgv(argv);
|
|
866
|
+
const payload = await parsePayload(tool, parsed, stdin);
|
|
867
|
+
const args = payloadToArgs(payload.value);
|
|
868
|
+
const properties = getSchemaProperties(tool.inputSchema);
|
|
869
|
+
for (const [key, value] of Object.entries(parsed.flags)) {
|
|
870
|
+
if (key === "yes") {
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
const schemaKey = resolveSchemaKey(key, properties);
|
|
874
|
+
if (!schemaKey) {
|
|
875
|
+
throw new CliCommandError("UNKNOWN_FLAG", `Unknown flag --${key} for ${tool.commandPath.join(" ")}.`, {
|
|
876
|
+
flag: key,
|
|
877
|
+
mcpName: tool.mcpName
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
args[schemaKey] = coerceFlagValue(value, properties[schemaKey], schemaKey);
|
|
881
|
+
}
|
|
882
|
+
for (const required of tool.required) {
|
|
883
|
+
if (args[required] === void 0 || args[required] === "") {
|
|
884
|
+
throw new CliCommandError("MISSING_REQUIRED_FLAG", `Missing required argument "${required}" for ${tool.commandPath.join(" ")}.`, {
|
|
885
|
+
required,
|
|
886
|
+
flag: `--${camelToKebab(required)}`,
|
|
887
|
+
mcpName: tool.mcpName
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
args,
|
|
893
|
+
yes: parsed.yes,
|
|
894
|
+
warnings: payload.warnings
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
function payloadToArgs(value) {
|
|
898
|
+
if (value === void 0 || value === null) {
|
|
899
|
+
return {};
|
|
900
|
+
}
|
|
901
|
+
if (!isRecord5(value)) {
|
|
902
|
+
throw new CliCommandError("INVALID_PAYLOAD", "Generated command payload must be a JSON object.", { value });
|
|
903
|
+
}
|
|
904
|
+
return { ...value };
|
|
905
|
+
}
|
|
906
|
+
function parseArgv(argv) {
|
|
907
|
+
const flags = {};
|
|
908
|
+
const inline = [];
|
|
909
|
+
let file;
|
|
910
|
+
let yes = false;
|
|
911
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
912
|
+
const arg = argv[index];
|
|
913
|
+
if (!arg) {
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (arg === "--file" || arg === "-f") {
|
|
917
|
+
file = requireValue(arg, argv[++index]);
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
if (arg === "--yes" || arg === "-y") {
|
|
921
|
+
yes = true;
|
|
922
|
+
flags.yes = true;
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
if (arg.startsWith("--")) {
|
|
926
|
+
const withoutPrefix = arg.slice(2);
|
|
927
|
+
const eqIndex = withoutPrefix.indexOf("=");
|
|
928
|
+
if (eqIndex >= 0) {
|
|
929
|
+
const key = withoutPrefix.slice(0, eqIndex);
|
|
930
|
+
flags[key] = withoutPrefix.slice(eqIndex + 1);
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
const next = argv[index + 1];
|
|
934
|
+
if (!next || next.startsWith("--")) {
|
|
935
|
+
flags[withoutPrefix] = true;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
flags[withoutPrefix] = next;
|
|
939
|
+
index += 1;
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
inline.push(arg);
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
flags,
|
|
946
|
+
yes,
|
|
947
|
+
file,
|
|
948
|
+
inline: inline.length > 0 ? inline.join(" ") : void 0
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
async function parsePayload(tool, parsed, stdin) {
|
|
952
|
+
if (tool.payloadMode === "file-json" || parsed.file || parsed.inline) {
|
|
953
|
+
return parsePayloadSource({
|
|
954
|
+
file: parsed.file,
|
|
955
|
+
inline: parsed.inline,
|
|
956
|
+
stdin
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
return { value: {}, source: "empty", warnings: [] };
|
|
960
|
+
}
|
|
961
|
+
function resolveSchemaKey(flag, properties) {
|
|
962
|
+
if (properties[flag]) {
|
|
963
|
+
return flag;
|
|
964
|
+
}
|
|
965
|
+
const normalized = flag.toLowerCase();
|
|
966
|
+
return Object.keys(properties).find((key) => camelToKebab(key) === normalized);
|
|
967
|
+
}
|
|
968
|
+
function coerceFlagValue(value, schema, key) {
|
|
969
|
+
const type = schema?.type;
|
|
970
|
+
if (type === "boolean") {
|
|
971
|
+
if (typeof value === "boolean") {
|
|
972
|
+
return value;
|
|
973
|
+
}
|
|
974
|
+
if (value === "true") return true;
|
|
975
|
+
if (value === "false") return false;
|
|
976
|
+
throw new CliCommandError("INVALID_FLAG_VALUE", `Flag "${key}" must be a boolean.`, { key, value });
|
|
977
|
+
}
|
|
978
|
+
if (type === "string") {
|
|
979
|
+
if (typeof value === "boolean") {
|
|
980
|
+
throw new CliCommandError("FLAG_REQUIRES_VALUE", `--${camelToKebab(key)} requires a value.`, { key });
|
|
981
|
+
}
|
|
982
|
+
return value;
|
|
983
|
+
}
|
|
984
|
+
if (type === "integer") {
|
|
985
|
+
if (typeof value === "boolean" || !/^-?\d+$/.test(value)) {
|
|
986
|
+
const code = typeof value === "boolean" ? "FLAG_REQUIRES_VALUE" : "INVALID_FLAG_VALUE";
|
|
987
|
+
throw new CliCommandError(code, `Flag "${key}" must be an integer.`, { key, value });
|
|
988
|
+
}
|
|
989
|
+
return Number.parseInt(value, 10);
|
|
990
|
+
}
|
|
991
|
+
if (type === "number") {
|
|
992
|
+
if (typeof value === "boolean" || Number.isNaN(Number(value))) {
|
|
993
|
+
const code = typeof value === "boolean" ? "FLAG_REQUIRES_VALUE" : "INVALID_FLAG_VALUE";
|
|
994
|
+
throw new CliCommandError(code, `Flag "${key}" must be a number.`, { key, value });
|
|
995
|
+
}
|
|
996
|
+
return Number(value);
|
|
997
|
+
}
|
|
998
|
+
throw new CliCommandError("UNSUPPORTED_FLAG_PAYLOAD", `Flag "${key}" is not a scalar field; provide it through --file, stdin, or inline JSON.`, {
|
|
999
|
+
key,
|
|
1000
|
+
type
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
function getSchemaProperties(schema) {
|
|
1004
|
+
const properties = schema.properties;
|
|
1005
|
+
if (!isRecord5(properties)) {
|
|
1006
|
+
return {};
|
|
1007
|
+
}
|
|
1008
|
+
const result = {};
|
|
1009
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
1010
|
+
if (isRecord5(value)) {
|
|
1011
|
+
result[key] = value;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return result;
|
|
1015
|
+
}
|
|
1016
|
+
function camelToKebab(value) {
|
|
1017
|
+
return value.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`).toLowerCase();
|
|
1018
|
+
}
|
|
1019
|
+
function requireValue(flag, value) {
|
|
1020
|
+
if (!value || value.startsWith("--")) {
|
|
1021
|
+
throw new CliCommandError("FLAG_REQUIRES_VALUE", `${flag} requires a value.`, { flag });
|
|
1022
|
+
}
|
|
1023
|
+
return value;
|
|
1024
|
+
}
|
|
1025
|
+
function isRecord5(value) {
|
|
1026
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// src/adapters/cli/destructiveConfirmation.ts
|
|
1030
|
+
import { createInterface as createInterface2 } from "readline";
|
|
1031
|
+
async function confirmDestructiveCommand(options) {
|
|
1032
|
+
if (!options.tool.destructive) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
if (options.yes) {
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
if (options.json) {
|
|
1039
|
+
throw new CliCommandError("CONFIRMATION_REQUIRED", `Destructive command ${options.tool.commandPath.join(" ")} requires --yes in --json mode.`, {
|
|
1040
|
+
mcpName: options.tool.mcpName
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
const answer = await promptConfirmation(options.io, `Run destructive command ${options.tool.commandPath.join(" ")}? Type "yes" to continue: `);
|
|
1044
|
+
if (answer.trim().toLowerCase() !== "yes") {
|
|
1045
|
+
throw new CliCommandError("CONFIRMATION_DECLINED", "Destructive command was not confirmed.", {
|
|
1046
|
+
mcpName: options.tool.mcpName
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
async function promptConfirmation(io, message) {
|
|
1051
|
+
io.stderr.write(message);
|
|
1052
|
+
const rl = createInterface2({ input: io.stdin, terminal: false });
|
|
1053
|
+
try {
|
|
1054
|
+
for await (const line of rl) {
|
|
1055
|
+
return line;
|
|
1056
|
+
}
|
|
1057
|
+
return "";
|
|
1058
|
+
} finally {
|
|
1059
|
+
rl.close();
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/adapters/cli/bespokeCommands.ts
|
|
1064
|
+
var ATTACHMENTS_UPLOAD_COMMAND_PATH = ["attachments", "upload"];
|
|
1065
|
+
|
|
1066
|
+
// src/adapters/cli/generatedCommandRouter.ts
|
|
1067
|
+
function matchGeneratedCommand(env, argv) {
|
|
1068
|
+
const commandTokens = argv.filter((arg) => !arg.startsWith("-"));
|
|
1069
|
+
const tools = getGeneratedTools(env);
|
|
1070
|
+
const entries = buildRouteEntries(tools, enrichments);
|
|
1071
|
+
const match = entries.filter((entry) => isPrefix(entry.path, commandTokens)).sort((a, b) => b.path.length - a.path.length)[0];
|
|
1072
|
+
if (!match) {
|
|
1073
|
+
return void 0;
|
|
1074
|
+
}
|
|
1075
|
+
const restStart = findRestStart(argv, match.path);
|
|
1076
|
+
return {
|
|
1077
|
+
tool: match.tool,
|
|
1078
|
+
matchedPath: match.path,
|
|
1079
|
+
rest: argv.slice(restStart),
|
|
1080
|
+
presentation: match.presentation,
|
|
1081
|
+
rawContractWarning: match.tool.payloadMode === "binary-upload" ? "This generated command uses the raw MCP argument contract only; local file upload semantics are not implemented here." : void 0
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
async function executeGeneratedCommand(options) {
|
|
1085
|
+
const route = matchGeneratedCommand(options.env, options.argv);
|
|
1086
|
+
if (!route) {
|
|
1087
|
+
return 2;
|
|
1088
|
+
}
|
|
1089
|
+
const parsed = await parseGeneratedCommandArgs(route.tool, route.rest, options.io.stdin);
|
|
1090
|
+
await confirmDestructiveCommand({
|
|
1091
|
+
tool: route.tool,
|
|
1092
|
+
yes: parsed.yes,
|
|
1093
|
+
json: options.json,
|
|
1094
|
+
io: options.io
|
|
1095
|
+
});
|
|
1096
|
+
const result = await options.client.toolsCall(route.tool.mcpName, parsed.args);
|
|
1097
|
+
const warnings = [...parsed.warnings, ...route.rawContractWarning ? [route.rawContractWarning] : []];
|
|
1098
|
+
options.io.stdout.write(renderGeneratedResult(result, {
|
|
1099
|
+
json: options.json,
|
|
1100
|
+
warnings,
|
|
1101
|
+
presentation: route.presentation
|
|
1102
|
+
}));
|
|
1103
|
+
return 0;
|
|
1104
|
+
}
|
|
1105
|
+
function renderGeneratedResult(result, options) {
|
|
1106
|
+
if (!options.json && options.presentation?.outputHint === "table") {
|
|
1107
|
+
const table = renderTable(result, { columns: options.presentation.tableColumns });
|
|
1108
|
+
if (table) {
|
|
1109
|
+
const warnings = options.warnings.length ? `${options.warnings.map((warning) => `Warning: ${warning}`).join("\n")}
|
|
1110
|
+
` : "";
|
|
1111
|
+
return `${warnings}${table}
|
|
1112
|
+
`;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return renderSuccess(result, { json: options.json, warnings: options.warnings });
|
|
1116
|
+
}
|
|
1117
|
+
function buildRouteEntries(tools, presentations) {
|
|
1118
|
+
const byName = new Map(presentations.map((presentation) => [presentation.mcpName, presentation]));
|
|
1119
|
+
const entries = [];
|
|
1120
|
+
for (const tool of tools) {
|
|
1121
|
+
const presentation = byName.get(tool.mcpName);
|
|
1122
|
+
entries.push({ path: tool.commandPath, tool, presentation });
|
|
1123
|
+
for (const alias of presentation?.aliases ?? []) {
|
|
1124
|
+
entries.push({ path: alias.split(/\s+/).filter(Boolean), tool, presentation });
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return entries;
|
|
1128
|
+
}
|
|
1129
|
+
function isPrefix(prefix, values) {
|
|
1130
|
+
return prefix.every((value, index) => values[index] === value);
|
|
1131
|
+
}
|
|
1132
|
+
function findRestStart(argv, commandPath) {
|
|
1133
|
+
let matched = 0;
|
|
1134
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1135
|
+
if (!argv[index]?.startsWith("-")) {
|
|
1136
|
+
if (argv[index] !== commandPath[matched]) {
|
|
1137
|
+
return index;
|
|
1138
|
+
}
|
|
1139
|
+
matched += 1;
|
|
1140
|
+
if (matched === commandPath.length) {
|
|
1141
|
+
return index + 1;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return argv.length;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/core/upload/attachmentUpload.ts
|
|
1149
|
+
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
1150
|
+
import { basename, extname } from "path";
|
|
1151
|
+
var AttachmentUploadError = class extends Error {
|
|
1152
|
+
constructor(code, message, details) {
|
|
1153
|
+
super(message);
|
|
1154
|
+
this.code = code;
|
|
1155
|
+
this.details = details;
|
|
1156
|
+
}
|
|
1157
|
+
code;
|
|
1158
|
+
details;
|
|
1159
|
+
};
|
|
1160
|
+
async function uploadAttachments(client, input) {
|
|
1161
|
+
if (!input.documentUuid.trim()) {
|
|
1162
|
+
throw new AttachmentUploadError("MISSING_REQUIRED_FLAG", 'Missing required argument "documentUuid".', {
|
|
1163
|
+
flag: "--document-uuid"
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
if (input.files.length === 0) {
|
|
1167
|
+
throw new AttachmentUploadError("MISSING_FILE", "Expected at least one file to upload.");
|
|
1168
|
+
}
|
|
1169
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
1170
|
+
const uploaded = [];
|
|
1171
|
+
for (let index = 0; index < input.files.length; index += 1) {
|
|
1172
|
+
const file = input.files[index];
|
|
1173
|
+
if (!file) {
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
uploaded.push(await uploadSingleAttachment(client, file, {
|
|
1177
|
+
documentUuid: input.documentUuid,
|
|
1178
|
+
maxBytes: input.maxBytes,
|
|
1179
|
+
ttlSeconds: input.ttlSeconds,
|
|
1180
|
+
requestToolName: input.requestToolName ?? "mcp__ainecto__request_upload_token",
|
|
1181
|
+
commitToolName: input.commitToolName ?? "mcp__ainecto__upload_attachments",
|
|
1182
|
+
fetchImpl,
|
|
1183
|
+
onProgress: input.onProgress,
|
|
1184
|
+
index,
|
|
1185
|
+
total: input.files.length
|
|
1186
|
+
}));
|
|
1187
|
+
}
|
|
1188
|
+
return { count: uploaded.length, uploaded };
|
|
1189
|
+
}
|
|
1190
|
+
async function uploadSingleAttachment(client, file, context) {
|
|
1191
|
+
const metadata = await readFileMetadata(file);
|
|
1192
|
+
emitProgress(context, "token", file.path, metadata.filename);
|
|
1193
|
+
const uploadToken = await requestUploadToken(client, metadata, context);
|
|
1194
|
+
emitProgress(context, "put", file.path, metadata.filename);
|
|
1195
|
+
const putResult = await putAttachmentBytes(uploadToken, metadata, context.fetchImpl);
|
|
1196
|
+
validatePutResult(uploadToken, metadata, putResult);
|
|
1197
|
+
const item = {
|
|
1198
|
+
documentUuid: context.documentUuid,
|
|
1199
|
+
filename: metadata.filename,
|
|
1200
|
+
storageKey: uploadToken.storageKey,
|
|
1201
|
+
contentType: putResult.contentType,
|
|
1202
|
+
sizeBytes: putResult.sizeBytes
|
|
1203
|
+
};
|
|
1204
|
+
emitProgress(context, "register", file.path, metadata.filename);
|
|
1205
|
+
const registerResult = await client.toolsCall(context.commitToolName, { items: [item] });
|
|
1206
|
+
emitProgress(context, "done", file.path, metadata.filename);
|
|
1207
|
+
return {
|
|
1208
|
+
path: file.path,
|
|
1209
|
+
documentUuid: context.documentUuid,
|
|
1210
|
+
filename: metadata.filename,
|
|
1211
|
+
storageKey: uploadToken.storageKey,
|
|
1212
|
+
contentType: putResult.contentType,
|
|
1213
|
+
sizeBytes: putResult.sizeBytes,
|
|
1214
|
+
registerResult
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
async function readFileMetadata(file) {
|
|
1218
|
+
const stats = await stat2(file.path);
|
|
1219
|
+
if (!stats.isFile()) {
|
|
1220
|
+
throw new AttachmentUploadError("INVALID_FILE", `Attachment path is not a file: ${file.path}`, { path: file.path });
|
|
1221
|
+
}
|
|
1222
|
+
const bytes = await readFile3(file.path);
|
|
1223
|
+
return {
|
|
1224
|
+
bytes,
|
|
1225
|
+
filename: file.filename ?? basename(file.path),
|
|
1226
|
+
contentType: file.contentType ?? detectContentType(file.path),
|
|
1227
|
+
sizeBytes: bytes.byteLength
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
async function requestUploadToken(client, metadata, context) {
|
|
1231
|
+
const scopeJson = JSON.stringify({
|
|
1232
|
+
documentUuid: context.documentUuid,
|
|
1233
|
+
filename: metadata.filename,
|
|
1234
|
+
contentType: metadata.contentType,
|
|
1235
|
+
sizeBytes: metadata.sizeBytes
|
|
1236
|
+
});
|
|
1237
|
+
const requestArgs = {
|
|
1238
|
+
purpose: "attachment.upload",
|
|
1239
|
+
scopeJson
|
|
1240
|
+
};
|
|
1241
|
+
if (context.maxBytes !== void 0) {
|
|
1242
|
+
requestArgs.maxBytes = context.maxBytes;
|
|
1243
|
+
}
|
|
1244
|
+
if (context.ttlSeconds !== void 0) {
|
|
1245
|
+
requestArgs.ttlSeconds = context.ttlSeconds;
|
|
1246
|
+
}
|
|
1247
|
+
const response = unwrapMcpToolPayload(await client.toolsCall(context.requestToolName, requestArgs));
|
|
1248
|
+
if (!isRecord6(response)) {
|
|
1249
|
+
throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response was not an object.");
|
|
1250
|
+
}
|
|
1251
|
+
const token = response.token;
|
|
1252
|
+
const uploadUrl = response.uploadUrl;
|
|
1253
|
+
const storageKey = response.storageKey;
|
|
1254
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
1255
|
+
throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response did not include token.");
|
|
1256
|
+
}
|
|
1257
|
+
if (typeof uploadUrl !== "string" || uploadUrl.length === 0) {
|
|
1258
|
+
throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response did not include uploadUrl.");
|
|
1259
|
+
}
|
|
1260
|
+
if (typeof storageKey !== "string" || storageKey.length === 0) {
|
|
1261
|
+
throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response did not include storageKey.");
|
|
1262
|
+
}
|
|
1263
|
+
return { token, uploadUrl, storageKey };
|
|
1264
|
+
}
|
|
1265
|
+
async function putAttachmentBytes(token, metadata, fetchImpl) {
|
|
1266
|
+
const maxAttempts = 3;
|
|
1267
|
+
let lastRetryableError;
|
|
1268
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1269
|
+
try {
|
|
1270
|
+
const response = await fetchImpl(token.uploadUrl, {
|
|
1271
|
+
method: "PUT",
|
|
1272
|
+
headers: {
|
|
1273
|
+
authorization: `Bearer ${token.token}`,
|
|
1274
|
+
"content-type": metadata.contentType
|
|
1275
|
+
},
|
|
1276
|
+
body: new Uint8Array(metadata.bytes)
|
|
1277
|
+
});
|
|
1278
|
+
if (shouldRetryResponse(response.status) && attempt < maxAttempts) {
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
if (!response.ok) {
|
|
1282
|
+
throw uploadHttpError(response.status);
|
|
1283
|
+
}
|
|
1284
|
+
return parsePutResponse(await response.text());
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
if (error instanceof AttachmentUploadError) {
|
|
1287
|
+
throw error;
|
|
1288
|
+
}
|
|
1289
|
+
lastRetryableError = error;
|
|
1290
|
+
if (attempt === maxAttempts) {
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_FAILED", "Attachment upload failed.", {
|
|
1296
|
+
cause: lastRetryableError instanceof Error ? lastRetryableError.message : String(lastRetryableError)
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
function uploadHttpError(status) {
|
|
1300
|
+
if (status === 409) {
|
|
1301
|
+
return new AttachmentUploadError("ATTACHMENT_UPLOAD_CONFLICT", "Attachment upload failed with HTTP 409 Conflict.", { status });
|
|
1302
|
+
}
|
|
1303
|
+
if (status === 401 || status === 403) {
|
|
1304
|
+
return new AttachmentUploadError("ATTACHMENT_UPLOAD_TOKEN_EXPIRED", `Attachment upload token was rejected with HTTP ${status}.`, { status });
|
|
1305
|
+
}
|
|
1306
|
+
return new AttachmentUploadError("ATTACHMENT_UPLOAD_HTTP_ERROR", `Attachment upload failed with HTTP ${status}.`, { status });
|
|
1307
|
+
}
|
|
1308
|
+
function shouldRetryResponse(status) {
|
|
1309
|
+
return status === 429 || status >= 500;
|
|
1310
|
+
}
|
|
1311
|
+
function parsePutResponse(text) {
|
|
1312
|
+
let parsed;
|
|
1313
|
+
try {
|
|
1314
|
+
parsed = JSON.parse(text);
|
|
1315
|
+
} catch {
|
|
1316
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not valid JSON.");
|
|
1317
|
+
}
|
|
1318
|
+
if (!isRecord6(parsed)) {
|
|
1319
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not an object.");
|
|
1320
|
+
}
|
|
1321
|
+
if (typeof parsed.storageKey !== "string") {
|
|
1322
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response did not include storageKey.");
|
|
1323
|
+
}
|
|
1324
|
+
if (typeof parsed.sizeBytes !== "number" || !Number.isFinite(parsed.sizeBytes)) {
|
|
1325
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response did not include numeric sizeBytes.");
|
|
1326
|
+
}
|
|
1327
|
+
if (typeof parsed.contentType !== "string") {
|
|
1328
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response did not include contentType.");
|
|
1329
|
+
}
|
|
1330
|
+
return {
|
|
1331
|
+
storageKey: parsed.storageKey,
|
|
1332
|
+
sizeBytes: parsed.sizeBytes,
|
|
1333
|
+
contentType: parsed.contentType
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
function validatePutResult(token, metadata, putResult) {
|
|
1337
|
+
if (putResult.storageKey !== token.storageKey) {
|
|
1338
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_STORAGE_KEY_MISMATCH", "Attachment PUT response storageKey did not match the upload token.", {
|
|
1339
|
+
expected: token.storageKey,
|
|
1340
|
+
actual: putResult.storageKey
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
if (putResult.sizeBytes !== metadata.sizeBytes) {
|
|
1344
|
+
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_SIZE_MISMATCH", "Attachment PUT response sizeBytes did not match the local file size.", {
|
|
1345
|
+
expected: metadata.sizeBytes,
|
|
1346
|
+
actual: putResult.sizeBytes
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
function unwrapMcpToolPayload(value) {
|
|
1351
|
+
if (!isRecord6(value) || !Array.isArray(value.content)) {
|
|
1352
|
+
return value;
|
|
1353
|
+
}
|
|
1354
|
+
const text = value.content.map((item) => isRecord6(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
|
|
1355
|
+
if (!text) {
|
|
1356
|
+
return value;
|
|
1357
|
+
}
|
|
1358
|
+
try {
|
|
1359
|
+
return JSON.parse(text);
|
|
1360
|
+
} catch {
|
|
1361
|
+
return value;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
function emitProgress(context, stage, path, filename) {
|
|
1365
|
+
context.onProgress?.({
|
|
1366
|
+
stage,
|
|
1367
|
+
path,
|
|
1368
|
+
filename,
|
|
1369
|
+
index: context.index,
|
|
1370
|
+
total: context.total
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function detectContentType(path) {
|
|
1374
|
+
const extension = extname(path).toLowerCase();
|
|
1375
|
+
return MIME_BY_EXTENSION[extension] ?? "application/octet-stream";
|
|
1376
|
+
}
|
|
1377
|
+
var MIME_BY_EXTENSION = {
|
|
1378
|
+
".avif": "image/avif",
|
|
1379
|
+
".csv": "text/csv",
|
|
1380
|
+
".gif": "image/gif",
|
|
1381
|
+
".htm": "text/html",
|
|
1382
|
+
".html": "text/html",
|
|
1383
|
+
".jpeg": "image/jpeg",
|
|
1384
|
+
".jpg": "image/jpeg",
|
|
1385
|
+
".json": "application/json",
|
|
1386
|
+
".md": "text/markdown",
|
|
1387
|
+
".pdf": "application/pdf",
|
|
1388
|
+
".png": "image/png",
|
|
1389
|
+
".svg": "image/svg+xml",
|
|
1390
|
+
".txt": "text/plain",
|
|
1391
|
+
".webp": "image/webp",
|
|
1392
|
+
".xls": "application/vnd.ms-excel",
|
|
1393
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1394
|
+
".zip": "application/zip"
|
|
1395
|
+
};
|
|
1396
|
+
function isRecord6(value) {
|
|
1397
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// src/adapters/cli/attachmentsUploadCommand.ts
|
|
1401
|
+
function isAttachmentsUploadCommand(domain, action) {
|
|
1402
|
+
return domain === ATTACHMENTS_UPLOAD_COMMAND_PATH[0] && action === ATTACHMENTS_UPLOAD_COMMAND_PATH[1];
|
|
1403
|
+
}
|
|
1404
|
+
async function executeAttachmentsUploadCommand(options) {
|
|
1405
|
+
const parsed = parseAttachmentsUploadArgs(options.argv);
|
|
1406
|
+
const result = await uploadAttachments(options.client, {
|
|
1407
|
+
documentUuid: parsed.documentUuid,
|
|
1408
|
+
files: parsed.files.map((path) => ({
|
|
1409
|
+
path,
|
|
1410
|
+
filename: parsed.filename,
|
|
1411
|
+
contentType: parsed.contentType
|
|
1412
|
+
})),
|
|
1413
|
+
maxBytes: parsed.maxBytes,
|
|
1414
|
+
ttlSeconds: parsed.ttlSeconds,
|
|
1415
|
+
onProgress: options.json ? void 0 : (progress) => {
|
|
1416
|
+
renderProgress(progress, options.io.stderr);
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
options.io.stdout.write(renderAttachmentUploadResult(result, options.json));
|
|
1420
|
+
return 0;
|
|
1421
|
+
}
|
|
1422
|
+
function parseAttachmentsUploadArgs(argv) {
|
|
1423
|
+
const files = [];
|
|
1424
|
+
let documentUuid;
|
|
1425
|
+
let filename;
|
|
1426
|
+
let contentType;
|
|
1427
|
+
let maxBytes;
|
|
1428
|
+
let ttlSeconds;
|
|
1429
|
+
let readFilesOnly = false;
|
|
1430
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1431
|
+
const arg = argv[index];
|
|
1432
|
+
if (!arg) {
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1435
|
+
if (readFilesOnly) {
|
|
1436
|
+
files.push(arg);
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
if (arg === "--") {
|
|
1440
|
+
readFilesOnly = true;
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
if (arg === "--document-uuid" || arg === "--documentUuid") {
|
|
1444
|
+
documentUuid = requireValue2(arg, argv[++index]);
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
if (arg === "--filename" || arg === "--name") {
|
|
1448
|
+
filename = requireValue2(arg, argv[++index]);
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
if (arg === "--content-type" || arg === "--contentType") {
|
|
1452
|
+
contentType = requireValue2(arg, argv[++index]);
|
|
1453
|
+
continue;
|
|
1454
|
+
}
|
|
1455
|
+
if (arg === "--max-bytes" || arg === "--maxBytes") {
|
|
1456
|
+
maxBytes = parsePositiveInteger(arg, requireValue2(arg, argv[++index]));
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
if (arg === "--ttl-seconds" || arg === "--ttlSeconds") {
|
|
1460
|
+
ttlSeconds = parsePositiveInteger(arg, requireValue2(arg, argv[++index]));
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
if (arg.startsWith("--")) {
|
|
1464
|
+
throw new CliCommandError("UNKNOWN_FLAG", `Unknown flag ${arg} for attachments upload.`, { flag: arg });
|
|
1465
|
+
}
|
|
1466
|
+
files.push(arg);
|
|
1467
|
+
}
|
|
1468
|
+
if (!documentUuid) {
|
|
1469
|
+
throw new CliCommandError("MISSING_REQUIRED_FLAG", 'Missing required argument "documentUuid" for attachments upload.', {
|
|
1470
|
+
flag: "--document-uuid"
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
if (files.length === 0) {
|
|
1474
|
+
throw new CliCommandError("MISSING_FILE", "Expected at least one file to upload.");
|
|
1475
|
+
}
|
|
1476
|
+
if (filename && files.length !== 1) {
|
|
1477
|
+
throw new CliCommandError("INVALID_FLAG_VALUE", "--filename/--name can only be used with exactly one file.", {
|
|
1478
|
+
flag: "--filename",
|
|
1479
|
+
files: files.length
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
return {
|
|
1483
|
+
documentUuid,
|
|
1484
|
+
files,
|
|
1485
|
+
filename,
|
|
1486
|
+
contentType,
|
|
1487
|
+
maxBytes,
|
|
1488
|
+
ttlSeconds
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
function parsePositiveInteger(flag, value) {
|
|
1492
|
+
if (!/^[1-9]\d*$/.test(value)) {
|
|
1493
|
+
throw new CliCommandError("INVALID_FLAG_VALUE", `${flag} must be a positive integer.`, { flag, value });
|
|
1494
|
+
}
|
|
1495
|
+
return Number.parseInt(value, 10);
|
|
1496
|
+
}
|
|
1497
|
+
function requireValue2(flag, value) {
|
|
1498
|
+
if (!value || value.startsWith("--")) {
|
|
1499
|
+
throw new CliCommandError("FLAG_REQUIRES_VALUE", `${flag} requires a value.`, { flag });
|
|
1500
|
+
}
|
|
1501
|
+
return value;
|
|
1502
|
+
}
|
|
1503
|
+
function renderProgress(progress, stderr) {
|
|
1504
|
+
if (progress.stage === "token") {
|
|
1505
|
+
stderr.write(`Uploading ${progress.filename} (${progress.index + 1}/${progress.total})...
|
|
1506
|
+
`);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function renderAttachmentUploadResult(result, json) {
|
|
1510
|
+
if (json) {
|
|
1511
|
+
return renderSuccess(result, { json });
|
|
1512
|
+
}
|
|
1513
|
+
const lines = [
|
|
1514
|
+
`Uploaded ${result.count} attachment${result.count === 1 ? "" : "s"}.`,
|
|
1515
|
+
...result.uploaded.map((file) => `- ${file.filename} (${file.sizeBytes} bytes)`)
|
|
1516
|
+
];
|
|
1517
|
+
return renderSuccess(lines.join("\n"), { json: false });
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// src/adapters/cli/ainectoCli.ts
|
|
1521
|
+
async function runAinectoCli(argv, io) {
|
|
1522
|
+
let parsed = { json: argv.includes("--json"), help: false, positionals: [] };
|
|
1523
|
+
try {
|
|
1524
|
+
parsed = parseGlobalArgs(argv);
|
|
1525
|
+
if (parsed.help || parsed.positionals.length === 0) {
|
|
1526
|
+
io.stdout.write(helpText());
|
|
1527
|
+
return 0;
|
|
1528
|
+
}
|
|
1529
|
+
const resolved = resolveEndpoint({ env: parsed.env, endpoint: parsed.endpoint });
|
|
1530
|
+
const tokenStore = new FileTokenStore();
|
|
1531
|
+
const auth = new OAuthClient({ endpoint: resolved.endpoint, tokenStore });
|
|
1532
|
+
const client = new McpRpcClient({ endpoint: resolved.endpoint, tokenProvider: auth });
|
|
1533
|
+
const [domain, action, ...rest] = parsed.positionals;
|
|
1534
|
+
if (domain === "mcp") {
|
|
1535
|
+
await runConnector({
|
|
1536
|
+
endpoint: resolved.endpoint,
|
|
1537
|
+
input: io.stdin,
|
|
1538
|
+
output: io.stdout,
|
|
1539
|
+
errorOutput: io.stderr
|
|
1540
|
+
});
|
|
1541
|
+
return 0;
|
|
1542
|
+
}
|
|
1543
|
+
if (domain === "auth") {
|
|
1544
|
+
return handleAuth(action, auth, resolved.endpoint, parsed.json, io);
|
|
1545
|
+
}
|
|
1546
|
+
if (isAttachmentsUploadCommand(domain, action)) {
|
|
1547
|
+
return await executeAttachmentsUploadCommand({
|
|
1548
|
+
argv: rest,
|
|
1549
|
+
client,
|
|
1550
|
+
json: parsed.json,
|
|
1551
|
+
io
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
const generatedResult = await executeGeneratedCommand({
|
|
1555
|
+
env: parsed.env ?? "prod",
|
|
1556
|
+
argv: parsed.positionals,
|
|
1557
|
+
client,
|
|
1558
|
+
json: parsed.json,
|
|
1559
|
+
io
|
|
1560
|
+
});
|
|
1561
|
+
if (generatedResult !== 2) {
|
|
1562
|
+
return generatedResult;
|
|
1563
|
+
}
|
|
1564
|
+
if (domain === "tools") {
|
|
1565
|
+
return handleTools(action, rest, parsed, client, io);
|
|
1566
|
+
}
|
|
1567
|
+
throw new Error(`Unknown command "${domain}".`);
|
|
1568
|
+
} catch (error) {
|
|
1569
|
+
io.stderr.write(renderError(error, { json: parsed.json }));
|
|
1570
|
+
return 1;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
async function handleAuth(action, auth, endpoint, json, io) {
|
|
1574
|
+
if (action === "login") {
|
|
1575
|
+
await auth.login();
|
|
1576
|
+
io.stdout.write(renderSuccess({ endpoint, authenticated: true }, { json }));
|
|
1577
|
+
return 0;
|
|
1578
|
+
}
|
|
1579
|
+
if (action === "status") {
|
|
1580
|
+
const token = await auth.getAccessToken();
|
|
1581
|
+
io.stdout.write(renderSuccess({ endpoint, authenticated: Boolean(token) }, { json }));
|
|
1582
|
+
return 0;
|
|
1583
|
+
}
|
|
1584
|
+
if (action === "logout") {
|
|
1585
|
+
await auth.logout();
|
|
1586
|
+
io.stdout.write(renderSuccess({ endpoint, authenticated: false }, { json }));
|
|
1587
|
+
return 0;
|
|
1588
|
+
}
|
|
1589
|
+
throw new Error("Expected auth login, auth status, or auth logout.");
|
|
1590
|
+
}
|
|
1591
|
+
async function handleTools(action, rest, parsed, client, io) {
|
|
1592
|
+
if (action === "list") {
|
|
1593
|
+
const tools = await client.toolsList();
|
|
1594
|
+
io.stdout.write(renderSuccess(tools, { json: parsed.json }));
|
|
1595
|
+
return 0;
|
|
1596
|
+
}
|
|
1597
|
+
if (action === "call") {
|
|
1598
|
+
const toolName = rest[0];
|
|
1599
|
+
if (!toolName) {
|
|
1600
|
+
throw new Error("Expected MCP tool name after tools call.");
|
|
1601
|
+
}
|
|
1602
|
+
const toolArgs = rest.slice(1);
|
|
1603
|
+
const payloadOptions = parsePayloadArgs(toolArgs);
|
|
1604
|
+
const payload = await parsePayloadSource({
|
|
1605
|
+
file: payloadOptions.file,
|
|
1606
|
+
inline: payloadOptions.inline,
|
|
1607
|
+
stdin: io.stdin
|
|
1608
|
+
});
|
|
1609
|
+
const result = await client.toolsCall(toolName, payload.value);
|
|
1610
|
+
io.stdout.write(renderSuccess(result, { json: parsed.json, warnings: payload.warnings }));
|
|
1611
|
+
return 0;
|
|
1612
|
+
}
|
|
1613
|
+
if (action === "catalog") {
|
|
1614
|
+
const catalogEnv = parsed.env ?? "prod";
|
|
1615
|
+
io.stdout.write(renderSuccess(getGeneratedTools(parsed.env ?? "prod"), {
|
|
1616
|
+
json: parsed.json,
|
|
1617
|
+
warnings: [catalogEnv === "dev" ? "Local dev catalog is a checked-in live sync snapshot; rerun sync:tools --env dev to refresh it." : "Local prod catalog is seed fixture only until authenticated prod live sync updates generated catalogs."]
|
|
1618
|
+
}));
|
|
1619
|
+
return 0;
|
|
1620
|
+
}
|
|
1621
|
+
throw new Error("Expected tools list, tools call <mcpName>, or tools catalog.");
|
|
1622
|
+
}
|
|
1623
|
+
function parseGlobalArgs(argv) {
|
|
1624
|
+
const result = { json: false, help: false, positionals: [] };
|
|
1625
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1626
|
+
const arg = argv[index];
|
|
1627
|
+
if (!arg) {
|
|
1628
|
+
continue;
|
|
1629
|
+
}
|
|
1630
|
+
if (arg === "--env") {
|
|
1631
|
+
result.env = assertEnv(requireValue3(arg, argv[++index]));
|
|
1632
|
+
} else if (arg === "--endpoint") {
|
|
1633
|
+
result.endpoint = requireValue3(arg, argv[++index]);
|
|
1634
|
+
} else if (arg === "--json") {
|
|
1635
|
+
result.json = true;
|
|
1636
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
1637
|
+
result.help = true;
|
|
1638
|
+
} else {
|
|
1639
|
+
result.positionals.push(arg);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
return result;
|
|
1643
|
+
}
|
|
1644
|
+
function parsePayloadArgs(argv) {
|
|
1645
|
+
const result = {};
|
|
1646
|
+
const inline = [];
|
|
1647
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1648
|
+
const arg = argv[index];
|
|
1649
|
+
if (!arg) {
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
if (arg === "--file" || arg === "-f") {
|
|
1653
|
+
result.file = requireValue3(arg, argv[++index]);
|
|
1654
|
+
} else {
|
|
1655
|
+
inline.push(arg);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
if (inline.length > 0) {
|
|
1659
|
+
result.inline = inline.join(" ");
|
|
1660
|
+
}
|
|
1661
|
+
return result;
|
|
1662
|
+
}
|
|
1663
|
+
function requireValue3(flag, value) {
|
|
1664
|
+
if (!value || value.startsWith("--")) {
|
|
1665
|
+
throw new Error(`${flag} requires a value.`);
|
|
1666
|
+
}
|
|
1667
|
+
return value;
|
|
1668
|
+
}
|
|
1669
|
+
function helpText() {
|
|
1670
|
+
return [
|
|
1671
|
+
"Ainecto CLI",
|
|
1672
|
+
"",
|
|
1673
|
+
"Usage:",
|
|
1674
|
+
" ainecto auth login|status|logout [--env prod|dev] [--endpoint URL] [--json]",
|
|
1675
|
+
" ainecto tools list [--env prod|dev] [--endpoint URL] [--json]",
|
|
1676
|
+
" ainecto tools call <mcpName> [-f payload.json|@-] [inline-json] [--json]",
|
|
1677
|
+
" ainecto attachments upload --document-uuid <uuid> <file...> [--json]",
|
|
1678
|
+
" ainecto <generated-command> [flags] [-f payload.json] [--yes] [--json]",
|
|
1679
|
+
" ainecto mcp [--env prod|dev] [--endpoint URL]",
|
|
1680
|
+
""
|
|
1681
|
+
].join("\n");
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// src/bin/ainecto.ts
|
|
1685
|
+
var exitCode = await runAinectoCli(process.argv.slice(2), {
|
|
1686
|
+
stdin: process.stdin,
|
|
1687
|
+
stdout: process.stdout,
|
|
1688
|
+
stderr: process.stderr
|
|
1689
|
+
});
|
|
1690
|
+
process.exitCode = exitCode;
|
|
1691
|
+
//# sourceMappingURL=ainecto.js.map
|