@rex0220/kintone-sql-tools 1.4.0 → 1.9.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/dist-cli/ksql.js +25 -6
- package/dist-mcp/ksql-mcp.js +120 -77
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +2 -2
package/dist-cli/ksql.js
CHANGED
|
@@ -4367,9 +4367,9 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
4367
4367
|
}
|
|
4368
4368
|
for (const s of analysis.statements) {
|
|
4369
4369
|
if (!s.isDml || s.tempTablesReferenced.length === 0) continue;
|
|
4370
|
-
if (s.statementType === "INSERT_SELECT"
|
|
4370
|
+
if (s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT") continue;
|
|
4371
4371
|
throw new BatchAnalysisError(
|
|
4372
|
-
|
|
4372
|
+
`ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
|
|
4373
4373
|
s.index
|
|
4374
4374
|
);
|
|
4375
4375
|
}
|
|
@@ -4405,8 +4405,18 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
4405
4405
|
}
|
|
4406
4406
|
try {
|
|
4407
4407
|
const remaining = deadline !== null ? deadline - Date.now() : null;
|
|
4408
|
+
const userConfirm = options.confirm;
|
|
4409
|
+
const stmtOptions = userConfirm ? {
|
|
4410
|
+
...options,
|
|
4411
|
+
confirm: (count, operation) => userConfirm(count, operation, {
|
|
4412
|
+
statementIndex: i,
|
|
4413
|
+
statementCount: statements.length,
|
|
4414
|
+
statementType: info.statementType,
|
|
4415
|
+
targetAppId: info.targetAppId
|
|
4416
|
+
})
|
|
4417
|
+
} : options;
|
|
4408
4418
|
const outcome = await runWithDeadline(
|
|
4409
|
-
executeBatchStatement(statements[i], info, countedClient,
|
|
4419
|
+
executeBatchStatement(statements[i], info, countedClient, stmtOptions, cacheContext, tempTables),
|
|
4410
4420
|
remaining
|
|
4411
4421
|
);
|
|
4412
4422
|
results.push({ ...base, status: "success", ...outcome });
|
|
@@ -4457,6 +4467,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
4457
4467
|
if (stmt.type === "INSERT_SELECT") {
|
|
4458
4468
|
return { result: await executeInsertSelect(stmt, client, options, cacheContext, tempTables) };
|
|
4459
4469
|
}
|
|
4470
|
+
if (stmt.type === "UPSERT_SELECT") {
|
|
4471
|
+
return { result: await executeUpsertSelect(stmt, client, options, cacheContext, tempTables) };
|
|
4472
|
+
}
|
|
4460
4473
|
throw new Error(`ArgumentError: temp table references in ${stmt.type} are not supported yet.`);
|
|
4461
4474
|
}
|
|
4462
4475
|
return { result: await executeParsedStatement(stmt, client, options, cacheContext) };
|
|
@@ -5778,8 +5791,8 @@ function evalOrderKeyForRow(key, row) {
|
|
|
5778
5791
|
return evalStringFunc(key.expr, row);
|
|
5779
5792
|
}
|
|
5780
5793
|
}
|
|
5781
|
-
async function executeUpsertSelect(stmt, client, options, cacheContext) {
|
|
5782
|
-
const selectResult = await executeSelect(stmt.select, client, options, cacheContext);
|
|
5794
|
+
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
5795
|
+
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
5783
5796
|
const { rows, columns } = selectResult;
|
|
5784
5797
|
if (columns.length !== stmt.fields.length) {
|
|
5785
5798
|
throw new Error(
|
|
@@ -5988,12 +6001,18 @@ function buildPlanForBatchQuery(query, info) {
|
|
|
5988
6001
|
lines.push(
|
|
5989
6002
|
`INSERT INTO APP${query.appId} ... SELECT\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30BD\u30FC\u30B9\u3002\u5B9F\u884C\u6642\u306B\u4EF6\u6570\u78BA\u5B9A \u2192 dmlMaxRows \u9069\u7528\uFF09`
|
|
5990
6003
|
);
|
|
6004
|
+
} else if (query.type === "UPSERT_SELECT") {
|
|
6005
|
+
lines.push(
|
|
6006
|
+
`UPSERT INTO APP${query.appId} ... SELECT\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30BD\u30FC\u30B9\u3002\u7167\u5408\u5F8C\u306B insert + update \u5408\u8A08\u78BA\u5B9A \u2192 dmlMaxRows \u9069\u7528\uFF09`
|
|
6007
|
+
);
|
|
5991
6008
|
}
|
|
5992
6009
|
lines.push(" mode: FULL_SCAN\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u53C2\u7167\uFF09");
|
|
5993
6010
|
lines.push(
|
|
5994
6011
|
` temp: ${info.tempTablesReferenced.join(", ")}\uFF08\u30A4\u30F3\u30E1\u30E2\u30EA\u8D70\u67FB\u3002\u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u884C\u6570\u4E0D\u660E\uFF09`
|
|
5995
6012
|
);
|
|
5996
|
-
const apps = info.appIds.filter(
|
|
6013
|
+
const apps = info.appIds.filter(
|
|
6014
|
+
(a) => query.type !== "INSERT_SELECT" && query.type !== "UPSERT_SELECT" || a !== query.appId
|
|
6015
|
+
);
|
|
5997
6016
|
if (apps.length > 0) {
|
|
5998
6017
|
lines.push(` app: ${apps.map((a) => `APP${a}`).join(", ")}`);
|
|
5999
6018
|
}
|
package/dist-mcp/ksql-mcp.js
CHANGED
|
@@ -35271,9 +35271,9 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
35271
35271
|
}
|
|
35272
35272
|
for (const s of analysis.statements) {
|
|
35273
35273
|
if (!s.isDml || s.tempTablesReferenced.length === 0) continue;
|
|
35274
|
-
if (s.statementType === "INSERT_SELECT"
|
|
35274
|
+
if (s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT") continue;
|
|
35275
35275
|
throw new BatchAnalysisError(
|
|
35276
|
-
|
|
35276
|
+
`ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
|
|
35277
35277
|
s.index
|
|
35278
35278
|
);
|
|
35279
35279
|
}
|
|
@@ -35309,8 +35309,18 @@ async function executeBatch(sql, client, options = {}) {
|
|
|
35309
35309
|
}
|
|
35310
35310
|
try {
|
|
35311
35311
|
const remaining = deadline !== null ? deadline - Date.now() : null;
|
|
35312
|
+
const userConfirm = options.confirm;
|
|
35313
|
+
const stmtOptions = userConfirm ? {
|
|
35314
|
+
...options,
|
|
35315
|
+
confirm: (count, operation) => userConfirm(count, operation, {
|
|
35316
|
+
statementIndex: i,
|
|
35317
|
+
statementCount: statements.length,
|
|
35318
|
+
statementType: info.statementType,
|
|
35319
|
+
targetAppId: info.targetAppId
|
|
35320
|
+
})
|
|
35321
|
+
} : options;
|
|
35312
35322
|
const outcome = await runWithDeadline(
|
|
35313
|
-
executeBatchStatement(statements[i], info, countedClient,
|
|
35323
|
+
executeBatchStatement(statements[i], info, countedClient, stmtOptions, cacheContext, tempTables),
|
|
35314
35324
|
remaining
|
|
35315
35325
|
);
|
|
35316
35326
|
results.push({ ...base, status: "success", ...outcome });
|
|
@@ -35361,6 +35371,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
35361
35371
|
if (stmt.type === "INSERT_SELECT") {
|
|
35362
35372
|
return { result: await executeInsertSelect(stmt, client, options, cacheContext, tempTables) };
|
|
35363
35373
|
}
|
|
35374
|
+
if (stmt.type === "UPSERT_SELECT") {
|
|
35375
|
+
return { result: await executeUpsertSelect(stmt, client, options, cacheContext, tempTables) };
|
|
35376
|
+
}
|
|
35364
35377
|
throw new Error(`ArgumentError: temp table references in ${stmt.type} are not supported yet.`);
|
|
35365
35378
|
}
|
|
35366
35379
|
return { result: await executeParsedStatement(stmt, client, options, cacheContext) };
|
|
@@ -36682,8 +36695,8 @@ function evalOrderKeyForRow(key, row) {
|
|
|
36682
36695
|
return evalStringFunc(key.expr, row);
|
|
36683
36696
|
}
|
|
36684
36697
|
}
|
|
36685
|
-
async function executeUpsertSelect(stmt, client, options, cacheContext) {
|
|
36686
|
-
const selectResult = await executeSelect(stmt.select, client, options, cacheContext);
|
|
36698
|
+
async function executeUpsertSelect(stmt, client, options, cacheContext, cteCache) {
|
|
36699
|
+
const selectResult = cteCache !== void 0 && cteCache.size > 0 ? await executeQueryWithCte(stmt.select, client, options, cteCache, cacheContext) : await executeSelect(stmt.select, client, options, cacheContext);
|
|
36687
36700
|
const { rows, columns } = selectResult;
|
|
36688
36701
|
if (columns.length !== stmt.fields.length) {
|
|
36689
36702
|
throw new Error(
|
|
@@ -36892,12 +36905,18 @@ function buildPlanForBatchQuery(query, info) {
|
|
|
36892
36905
|
lines.push(
|
|
36893
36906
|
`INSERT INTO APP${query.appId} ... SELECT\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30BD\u30FC\u30B9\u3002\u5B9F\u884C\u6642\u306B\u4EF6\u6570\u78BA\u5B9A \u2192 dmlMaxRows \u9069\u7528\uFF09`
|
|
36894
36907
|
);
|
|
36908
|
+
} else if (query.type === "UPSERT_SELECT") {
|
|
36909
|
+
lines.push(
|
|
36910
|
+
`UPSERT INTO APP${query.appId} ... SELECT\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u30BD\u30FC\u30B9\u3002\u7167\u5408\u5F8C\u306B insert + update \u5408\u8A08\u78BA\u5B9A \u2192 dmlMaxRows \u9069\u7528\uFF09`
|
|
36911
|
+
);
|
|
36895
36912
|
}
|
|
36896
36913
|
lines.push(" mode: FULL_SCAN\uFF08\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u53C2\u7167\uFF09");
|
|
36897
36914
|
lines.push(
|
|
36898
36915
|
` temp: ${info.tempTablesReferenced.join(", ")}\uFF08\u30A4\u30F3\u30E1\u30E2\u30EA\u8D70\u67FB\u3002\u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u884C\u6570\u4E0D\u660E\uFF09`
|
|
36899
36916
|
);
|
|
36900
|
-
const apps = info.appIds.filter(
|
|
36917
|
+
const apps = info.appIds.filter(
|
|
36918
|
+
(a) => query.type !== "INSERT_SELECT" && query.type !== "UPSERT_SELECT" || a !== query.appId
|
|
36919
|
+
);
|
|
36901
36920
|
if (apps.length > 0) {
|
|
36902
36921
|
lines.push(` app: ${apps.map((a) => `APP${a}`).join(", ")}`);
|
|
36903
36922
|
}
|
|
@@ -38271,6 +38290,24 @@ function requireDmlApproval(input, toolName, suffix = "") {
|
|
|
38271
38290
|
}
|
|
38272
38291
|
return Number(input.dmlMaxRows);
|
|
38273
38292
|
}
|
|
38293
|
+
function containsSelectBasedDml(statements) {
|
|
38294
|
+
return statements.some(
|
|
38295
|
+
(s) => s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT"
|
|
38296
|
+
);
|
|
38297
|
+
}
|
|
38298
|
+
function resolveMutateRuntimeMaxRecords(statements, dmlMaxRows) {
|
|
38299
|
+
return containsSelectBasedDml(statements) ? void 0 : dmlMaxRows + 1;
|
|
38300
|
+
}
|
|
38301
|
+
var READ_LIMIT_MESSAGE_FRAGMENT = "\u53D6\u5F97\u4EF6\u6570\u304C\u4E0A\u9650";
|
|
38302
|
+
var SELECT_BASED_DML_READ_LIMIT_HINT = "SELECT-based DML \u306E\u30BD\u30FC\u30B9\u8AAD\u307F\u53D6\u308A\u4E0A\u9650\u306F dmlMaxRows \u3067\u306F\u306A\u304F maxRecords \u89E3\u6C7A\u5024(KSQL_MAX_RECORDS / profile \u306E query.maxRecords\u3001\u65E2\u5B9A 500)\u3067\u5236\u5FA1\u3055\u308C\u307E\u3059\u3002dmlMaxRows \u306F\u5F71\u97FF\u884C\u6570\u30AC\u30FC\u30C9\u3067\u3059\u3002";
|
|
38303
|
+
function appendSelectBasedDmlReadLimitHint(err) {
|
|
38304
|
+
if (err instanceof Error && err.message.includes(READ_LIMIT_MESSAGE_FRAGMENT)) {
|
|
38305
|
+
const hinted = new Error(`${err.message} ${SELECT_BASED_DML_READ_LIMIT_HINT}`);
|
|
38306
|
+
hinted.name = err.name;
|
|
38307
|
+
return hinted;
|
|
38308
|
+
}
|
|
38309
|
+
return err;
|
|
38310
|
+
}
|
|
38274
38311
|
function toToolResult(payload, isError = false) {
|
|
38275
38312
|
return {
|
|
38276
38313
|
content: [
|
|
@@ -38428,7 +38465,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38428
38465
|
}
|
|
38429
38466
|
return toSelectPayload(result);
|
|
38430
38467
|
}
|
|
38431
|
-
async function mutateBatch(input, validation,
|
|
38468
|
+
async function mutateBatch(input, validation, dmlMaxRows) {
|
|
38432
38469
|
if (!validation.containsDml) {
|
|
38433
38470
|
throw new Error("ArgumentError: batch contains no DML statements. Use ksql_query.");
|
|
38434
38471
|
}
|
|
@@ -38436,20 +38473,12 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38436
38473
|
for (const s of validation.statements) {
|
|
38437
38474
|
if (!s.isDml) continue;
|
|
38438
38475
|
const at = ` (statement ${s.index})`;
|
|
38439
|
-
if (s.statementType === "INSERT_SELECT" && !s.tempOnlySource) {
|
|
38440
|
-
throw new Error(
|
|
38441
|
-
`ArgumentError: INSERT_SELECT in a batch must select from temp tables only.${at}`
|
|
38442
|
-
);
|
|
38443
|
-
}
|
|
38444
|
-
if (s.statementType === "UPSERT_SELECT") {
|
|
38445
|
-
throw new Error(`ArgumentError: ${s.statementType} is not supported by ksql_mutate yet.${at}`);
|
|
38446
|
-
}
|
|
38447
38476
|
if ((s.statementType === "UPDATE" || s.statementType === "DELETE") && !s.hasWhere) {
|
|
38448
38477
|
throw new Error(`ArgumentError: ${s.statementType} without WHERE is blocked by ksql_mutate.${at}`);
|
|
38449
38478
|
}
|
|
38450
|
-
if (s.insertValuesCount !== null && s.insertValuesCount >
|
|
38479
|
+
if (s.insertValuesCount !== null && s.insertValuesCount > dmlMaxRows) {
|
|
38451
38480
|
throw new Error(
|
|
38452
|
-
`ArgumentError: INSERT rows (${s.insertValuesCount}) exceed dmlMaxRows (${
|
|
38481
|
+
`ArgumentError: INSERT rows (${s.insertValuesCount}) exceed dmlMaxRows (${dmlMaxRows}).${at}`
|
|
38453
38482
|
);
|
|
38454
38483
|
}
|
|
38455
38484
|
staticInsertTotal += s.insertValuesCount ?? 0;
|
|
@@ -38460,10 +38489,13 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38460
38489
|
`ArgumentError: batch INSERT rows (${staticInsertTotal}) exceed dmlTotalMaxRows (${dmlTotalMaxRows}).`
|
|
38461
38490
|
);
|
|
38462
38491
|
}
|
|
38492
|
+
const selectBasedDml = containsSelectBasedDml(validation.statements);
|
|
38463
38493
|
const runtime = await createRuntime(serverOptions, {
|
|
38464
38494
|
sql: input.sql,
|
|
38465
38495
|
profile: input.profile,
|
|
38466
|
-
|
|
38496
|
+
// SELECT-based DML を含む場合は dmlMaxRows で読み取りを絞らない(案A。
|
|
38497
|
+
// resolveMutateRuntimeMaxRecords の doc コメント参照)
|
|
38498
|
+
maxRecords: resolveMutateRuntimeMaxRecords(validation.statements, dmlMaxRows),
|
|
38467
38499
|
fetchParallel: input.fetchParallel,
|
|
38468
38500
|
onLimit: DEFAULT_ON_LIMIT,
|
|
38469
38501
|
timeout: input.timeout
|
|
@@ -38477,8 +38509,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38477
38509
|
// 合計タイムアウト(解決済みの runtime.timeout。per-request と同値)
|
|
38478
38510
|
timeoutMs: runtime.timeout,
|
|
38479
38511
|
confirm: async (count, operation) => {
|
|
38480
|
-
if (count >
|
|
38481
|
-
throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${
|
|
38512
|
+
if (count > dmlMaxRows) {
|
|
38513
|
+
throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
38482
38514
|
}
|
|
38483
38515
|
totalAffected += count;
|
|
38484
38516
|
if (dmlTotalMaxRows !== void 0 && totalAffected > dmlTotalMaxRows) {
|
|
@@ -38489,46 +38521,61 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38489
38521
|
return true;
|
|
38490
38522
|
}
|
|
38491
38523
|
});
|
|
38492
|
-
|
|
38524
|
+
const payload = toBatchQueryPayload(batchResult);
|
|
38525
|
+
if (selectBasedDml) {
|
|
38526
|
+
for (const entry of payload.statements) {
|
|
38527
|
+
if (entry.type !== "INSERT_SELECT" && entry.type !== "UPSERT_SELECT") continue;
|
|
38528
|
+
const error51 = entry.error;
|
|
38529
|
+
if (typeof error51?.message !== "string") continue;
|
|
38530
|
+
if (!error51.message.includes(READ_LIMIT_MESSAGE_FRAGMENT)) continue;
|
|
38531
|
+
entry.error = { ...error51, message: `${error51.message} ${SELECT_BASED_DML_READ_LIMIT_HINT}` };
|
|
38532
|
+
}
|
|
38533
|
+
}
|
|
38534
|
+
return payload;
|
|
38493
38535
|
}
|
|
38494
38536
|
async function mutate(input) {
|
|
38495
|
-
const
|
|
38537
|
+
const dmlMaxRows = requireDmlApproval(input, "ksql_mutate");
|
|
38496
38538
|
const validation = await validate(input);
|
|
38497
38539
|
if (validation.batch) {
|
|
38498
|
-
return mutateBatch(input, validation,
|
|
38540
|
+
return mutateBatch(input, validation, dmlMaxRows);
|
|
38499
38541
|
}
|
|
38500
38542
|
if (!validation.isDml) {
|
|
38501
38543
|
throw new Error(`ArgumentError: ${validation.statementType} is not allowed by ksql_mutate. Use ksql_query.`);
|
|
38502
38544
|
}
|
|
38503
|
-
if (validation.statementType === "INSERT_SELECT" || validation.statementType === "UPSERT_SELECT") {
|
|
38504
|
-
throw new Error(`ArgumentError: ${validation.statementType} is not supported by ksql_mutate yet.`);
|
|
38505
|
-
}
|
|
38506
38545
|
if ((validation.statementType === "UPDATE" || validation.statementType === "DELETE") && !validation.hasWhere) {
|
|
38507
38546
|
throw new Error(`ArgumentError: ${validation.statementType} without WHERE is blocked by ksql_mutate.`);
|
|
38508
38547
|
}
|
|
38509
|
-
if (validation.insertValuesCount !== null && validation.insertValuesCount >
|
|
38510
|
-
throw new Error(`ArgumentError: INSERT rows (${validation.insertValuesCount}) exceed dmlMaxRows (${
|
|
38548
|
+
if (validation.insertValuesCount !== null && validation.insertValuesCount > dmlMaxRows) {
|
|
38549
|
+
throw new Error(`ArgumentError: INSERT rows (${validation.insertValuesCount}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
38511
38550
|
}
|
|
38551
|
+
const selectBasedDml = containsSelectBasedDml(validation.statements);
|
|
38512
38552
|
const runtime = await createRuntime(serverOptions, {
|
|
38513
38553
|
sql: input.sql,
|
|
38514
38554
|
profile: input.profile,
|
|
38515
|
-
|
|
38555
|
+
// SELECT-based DML は dmlMaxRows で読み取りを絞らない(案A。
|
|
38556
|
+
// resolveMutateRuntimeMaxRecords の doc コメント参照)
|
|
38557
|
+
maxRecords: resolveMutateRuntimeMaxRecords(validation.statements, dmlMaxRows),
|
|
38516
38558
|
fetchParallel: input.fetchParallel,
|
|
38517
38559
|
onLimit: DEFAULT_ON_LIMIT,
|
|
38518
38560
|
timeout: input.timeout
|
|
38519
38561
|
});
|
|
38520
|
-
|
|
38521
|
-
|
|
38522
|
-
|
|
38523
|
-
|
|
38524
|
-
|
|
38525
|
-
|
|
38526
|
-
|
|
38527
|
-
|
|
38562
|
+
let result;
|
|
38563
|
+
try {
|
|
38564
|
+
result = await executeSql(runtime.sql, runtime.client, {
|
|
38565
|
+
maxRecords: runtime.maxRecords,
|
|
38566
|
+
fetchParallel: runtime.fetchParallel,
|
|
38567
|
+
onLimitReached: runtime.onLimit,
|
|
38568
|
+
cacheContext: runtime.cacheContext,
|
|
38569
|
+
confirm: async (count, operation) => {
|
|
38570
|
+
if (count > dmlMaxRows) {
|
|
38571
|
+
throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows}).`);
|
|
38572
|
+
}
|
|
38573
|
+
return true;
|
|
38528
38574
|
}
|
|
38529
|
-
|
|
38530
|
-
|
|
38531
|
-
|
|
38575
|
+
});
|
|
38576
|
+
} catch (err) {
|
|
38577
|
+
throw selectBasedDml ? appendSelectBasedDmlReadLimitHint(err) : err;
|
|
38578
|
+
}
|
|
38532
38579
|
if (result.type === "SELECT") {
|
|
38533
38580
|
throw new Error(`ArgumentError: ksql_mutate returned unexpected result type ${result.type}.`);
|
|
38534
38581
|
}
|
|
@@ -38631,13 +38678,13 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38631
38678
|
result: result2
|
|
38632
38679
|
};
|
|
38633
38680
|
}
|
|
38634
|
-
const
|
|
38681
|
+
const dmlMaxRows = requireDmlApproval(input, "ksql_run_saved_query", "for DML saved queries");
|
|
38635
38682
|
const result = await mutate({
|
|
38636
38683
|
sql: saved.sql,
|
|
38637
38684
|
profile: profile2,
|
|
38638
38685
|
allowDml: true,
|
|
38639
38686
|
confirmText: "yes",
|
|
38640
|
-
dmlMaxRows
|
|
38687
|
+
dmlMaxRows,
|
|
38641
38688
|
fetchParallel: input.fetchParallel,
|
|
38642
38689
|
timeout: input.timeout
|
|
38643
38690
|
});
|
|
@@ -38685,48 +38732,43 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
|
|
|
38685
38732
|
}
|
|
38686
38733
|
|
|
38687
38734
|
// src/mcp/schemas.ts
|
|
38688
|
-
var profile = external_exports.string().min(1).optional();
|
|
38689
|
-
var maxRecords = external_exports.number().int().positive().optional();
|
|
38690
|
-
var fetchParallel = external_exports.number().int().min(1).max(10).optional();
|
|
38691
|
-
var onLimit = external_exports.enum(["error", "truncate"]).optional();
|
|
38692
|
-
var timeout = external_exports.number().int().positive().optional();
|
|
38693
|
-
var
|
|
38694
|
-
var
|
|
38695
|
-
var savedQueryTags = external_exports.array(external_exports.string().min(1)).optional();
|
|
38735
|
+
var profile = external_exports.string().min(1).describe("kintone connection profile name from ksql.config.json (default: the server's default profile).").optional();
|
|
38736
|
+
var maxRecords = external_exports.number().int().positive().describe("Maximum records fetched per SELECT (default 500).").optional();
|
|
38737
|
+
var fetchParallel = external_exports.number().int().min(1).max(10).describe("Number of parallel kintone record-fetch requests (1-10).").optional();
|
|
38738
|
+
var onLimit = external_exports.enum(["error", "truncate"]).describe("Behavior when maxRecords is exceeded: 'error' rejects, 'truncate' returns the first maxRecords rows (default 'error').").optional();
|
|
38739
|
+
var timeout = external_exports.number().int().positive().describe("Request timeout in milliseconds. For multi-statement batches this also acts as the total batch deadline.").optional();
|
|
38740
|
+
var savedQueryName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).describe("Saved query name (alphanumeric, '_' and '-', up to 64 chars).");
|
|
38741
|
+
var savedQueryTags = external_exports.array(external_exports.string().min(1)).describe("Tags for organizing saved queries.").optional();
|
|
38696
38742
|
var validateInputSchema = external_exports.object({
|
|
38697
|
-
sql: external_exports.string().min(1),
|
|
38743
|
+
sql: external_exports.string().min(1).describe("kSQL text to validate. May contain multiple ;-separated statements (batch) and temp tables (#name)."),
|
|
38698
38744
|
profile
|
|
38699
38745
|
});
|
|
38700
38746
|
var explainInputSchema = external_exports.object({
|
|
38701
|
-
sql: external_exports.string().min(1),
|
|
38747
|
+
sql: external_exports.string().min(1).describe("kSQL text to explain. May contain multiple ;-separated statements (batch) and temp tables (#name)."),
|
|
38702
38748
|
profile
|
|
38703
38749
|
});
|
|
38704
38750
|
var queryInputSchema = external_exports.object({
|
|
38705
|
-
sql: external_exports.string().min(1),
|
|
38751
|
+
sql: external_exports.string().min(1).describe("Read-only kSQL text. May contain multiple ;-separated statements (batch) with temp tables, e.g. CREATE TEMP TABLE #t AS SELECT ...; SELECT ... FROM #t;"),
|
|
38706
38752
|
profile,
|
|
38707
38753
|
maxRecords,
|
|
38708
38754
|
fetchParallel,
|
|
38709
38755
|
onLimit,
|
|
38710
38756
|
timeout,
|
|
38711
|
-
|
|
38712
|
-
|
|
38713
|
-
/** バッチ(複文)専用: 返却する結果セットの合計行数上限(既定なし) */
|
|
38714
|
-
maxTotalRecords: external_exports.number().int().positive().optional()
|
|
38757
|
+
continueOnError: external_exports.boolean().describe("Batch (multi-statement) only: keep executing subsequent statements after a runtime error (default false = fail-fast).").optional(),
|
|
38758
|
+
maxTotalRecords: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total rows returned across all result sets (default: unlimited).").optional()
|
|
38715
38759
|
});
|
|
38716
38760
|
var mutateInputSchema = external_exports.object({
|
|
38717
|
-
sql: external_exports.string().min(1),
|
|
38761
|
+
sql: external_exports.string().min(1).describe("DML kSQL text. May contain multiple ;-separated statements (batch) with temp tables, e.g. CREATE TEMP TABLE #t AS SELECT ...; INSERT INTO APPx (...) SELECT ... FROM #t;"),
|
|
38718
38762
|
profile,
|
|
38719
|
-
allowDml: external_exports.literal(true),
|
|
38720
|
-
confirmText: external_exports.literal("yes"),
|
|
38721
|
-
dmlMaxRows,
|
|
38763
|
+
allowDml: external_exports.literal(true).describe("Must be true to acknowledge that this call writes to kintone."),
|
|
38764
|
+
confirmText: external_exports.literal("yes").describe('Must be the literal string "yes" to confirm execution.'),
|
|
38765
|
+
dmlMaxRows: external_exports.number().int().positive().describe("Per-statement cap on affected rows. The call fails before writing if any statement would exceed it; for UPSERT it counts inserts + updates. It does NOT limit source reads of INSERT/UPSERT ... SELECT: those follow the runtime maxRecords resolution (KSQL_MAX_RECORDS / profile query.maxRecords, default 500; temp tables hold at most 10000 rows), so choose it by intended write count only."),
|
|
38722
38766
|
fetchParallel,
|
|
38723
38767
|
timeout,
|
|
38724
|
-
|
|
38725
|
-
* なお DML バッチに continueOnError は存在しない(常に fail-fast) */
|
|
38726
|
-
dmlTotalMaxRows: dmlMaxRows.optional()
|
|
38768
|
+
dmlTotalMaxRows: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total affected rows across the whole batch (default: per-statement dmlMaxRows only). DML batches always run fail-fast.").optional()
|
|
38727
38769
|
});
|
|
38728
38770
|
var describeAppInputSchema = external_exports.object({
|
|
38729
|
-
app: external_exports.number().int().positive(),
|
|
38771
|
+
app: external_exports.number().int().positive().describe("kintone app ID to describe."),
|
|
38730
38772
|
profile,
|
|
38731
38773
|
maxRecords,
|
|
38732
38774
|
fetchParallel,
|
|
@@ -38743,12 +38785,12 @@ var showAppsInputSchema = external_exports.object({
|
|
|
38743
38785
|
var listQueriesInputSchema = external_exports.object({});
|
|
38744
38786
|
var saveQueryInputSchema = external_exports.object({
|
|
38745
38787
|
name: savedQueryName,
|
|
38746
|
-
title: external_exports.string().min(1).optional(),
|
|
38747
|
-
description: external_exports.string().min(1).optional(),
|
|
38748
|
-
sql: external_exports.string().min(1),
|
|
38749
|
-
defaultProfile: external_exports.string().min(1),
|
|
38750
|
-
readOnly: external_exports.boolean(),
|
|
38751
|
-
allowProfileOverride: external_exports.boolean().optional(),
|
|
38788
|
+
title: external_exports.string().min(1).describe("Human-readable title.").optional(),
|
|
38789
|
+
description: external_exports.string().min(1).describe("What the query does and when to use it.").optional(),
|
|
38790
|
+
sql: external_exports.string().min(1).describe("kSQL text to save (single statement only)."),
|
|
38791
|
+
defaultProfile: external_exports.string().min(1).describe("Profile the saved query runs against by default."),
|
|
38792
|
+
readOnly: external_exports.boolean().describe("true for read-only queries; false marks the saved query as DML (requires mutate safety inputs at run time)."),
|
|
38793
|
+
allowProfileOverride: external_exports.boolean().describe("Allow overriding the profile at run time (default false).").optional(),
|
|
38752
38794
|
tags: savedQueryTags
|
|
38753
38795
|
});
|
|
38754
38796
|
var savedQueryNameInputSchema = external_exports.object({
|
|
@@ -38761,9 +38803,9 @@ var runSavedQueryInputSchema = external_exports.object({
|
|
|
38761
38803
|
fetchParallel,
|
|
38762
38804
|
onLimit,
|
|
38763
38805
|
timeout,
|
|
38764
|
-
allowDml: external_exports.literal(true).optional(),
|
|
38765
|
-
confirmText: external_exports.literal("yes").optional(),
|
|
38766
|
-
dmlMaxRows:
|
|
38806
|
+
allowDml: external_exports.literal(true).describe("Required for DML saved queries: must be true to acknowledge writes.").optional(),
|
|
38807
|
+
confirmText: external_exports.literal("yes").describe('Required for DML saved queries: must be the literal string "yes".').optional(),
|
|
38808
|
+
dmlMaxRows: external_exports.number().int().positive().describe("Required for DML saved queries: per-statement cap on affected rows; for UPSERT it counts inserts + updates. It does NOT limit source reads of INSERT/UPSERT ... SELECT: those follow the runtime maxRecords resolution (KSQL_MAX_RECORDS / profile query.maxRecords, default 500; temp tables hold at most 10000 rows). Note: this tool's maxRecords / onLimit inputs apply to read-only saved queries only.").optional()
|
|
38767
38809
|
});
|
|
38768
38810
|
var validateInputShape = validateInputSchema.shape;
|
|
38769
38811
|
var explainInputShape = explainInputSchema.shape;
|
|
@@ -38812,10 +38854,11 @@ Options:
|
|
|
38812
38854
|
-h, --help Show help
|
|
38813
38855
|
`);
|
|
38814
38856
|
}
|
|
38857
|
+
var SERVER_VERSION = true ? "1.9.0" : "0.0.0-dev";
|
|
38815
38858
|
function createServer(args) {
|
|
38816
38859
|
const server = new McpServer({
|
|
38817
38860
|
name: "ksql-mcp",
|
|
38818
|
-
version:
|
|
38861
|
+
version: SERVER_VERSION
|
|
38819
38862
|
});
|
|
38820
38863
|
const tools = createKsqlMcpTools({
|
|
38821
38864
|
configPath: args.configPath,
|
|
@@ -38833,12 +38876,12 @@ function createServer(args) {
|
|
|
38833
38876
|
}, tools.explainTool);
|
|
38834
38877
|
server.registerTool("ksql_query", {
|
|
38835
38878
|
title: "Run read-only kSQL",
|
|
38836
|
-
description: "Execute read-only kSQL
|
|
38879
|
+
description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE. Supports multi-statement batches with temp tables (CREATE TEMP TABLE #t AS SELECT ...; SELECT ... FROM #t;). DML is rejected.",
|
|
38837
38880
|
inputSchema: queryInputShape
|
|
38838
38881
|
}, tools.queryTool);
|
|
38839
38882
|
server.registerTool("ksql_mutate", {
|
|
38840
38883
|
title: "Run mutating kSQL",
|
|
38841
|
-
description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls.
|
|
38884
|
+
description: "Execute DML kSQL with explicit allowDml, confirmText, and dmlMaxRows safety controls. Supports multi-statement DML batches with temp tables. INSERT/UPSERT INTO app ... SELECT supports app sources, temp tables, or joins of both. For UPSERT, dmlMaxRows counts inserts + updates. dmlMaxRows caps affected rows only, not source reads: the source SELECT reads up to the runtime maxRecords (KSQL_MAX_RECORDS / profile query.maxRecords, default 500); temp tables hold at most 10000 rows.",
|
|
38842
38885
|
inputSchema: mutateInputShape
|
|
38843
38886
|
}, tools.mutateTool);
|
|
38844
38887
|
server.registerTool("ksql_describe_app", {
|
package/dist-mcpb/ksql-mcp.mcpb
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rex0220/kintone-sql-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "kintone SQL plugin and CLI tools (ksql)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"prepack": "npm run build:cli && npm run build:mcp && npm run build:mcpb",
|
|
35
35
|
"start": "run-p develop upload",
|
|
36
36
|
"develop": "node build.mjs --watch",
|
|
37
|
-
"upload": "
|
|
37
|
+
"upload": "node scripts/upload-plugin.cjs"
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
40
40
|
"dist-cli/",
|