@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 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" && s.tempOnlySource) continue;
4370
+ if (s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT") continue;
4371
4371
  throw new BatchAnalysisError(
4372
- s.statementType === "INSERT_SELECT" ? `ArgumentError: INSERT_SELECT in a batch must select from temp tables only. (statement ${s.index})` : `ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
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, options, cacheContext, tempTables),
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((a) => query.type !== "INSERT_SELECT" || a !== query.appId);
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
  }
@@ -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" && s.tempOnlySource) continue;
35274
+ if (s.statementType === "INSERT_SELECT" || s.statementType === "UPSERT_SELECT") continue;
35275
35275
  throw new BatchAnalysisError(
35276
- s.statementType === "INSERT_SELECT" ? `ArgumentError: INSERT_SELECT in a batch must select from temp tables only. (statement ${s.index})` : `ArgumentError: temp table references in ${s.statementType} are not supported yet.`,
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, options, cacheContext, tempTables),
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((a) => query.type !== "INSERT_SELECT" || a !== query.appId);
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, dmlMaxRows2) {
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 > dmlMaxRows2) {
38479
+ if (s.insertValuesCount !== null && s.insertValuesCount > dmlMaxRows) {
38451
38480
  throw new Error(
38452
- `ArgumentError: INSERT rows (${s.insertValuesCount}) exceed dmlMaxRows (${dmlMaxRows2}).${at}`
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
- maxRecords: dmlMaxRows2 + 1,
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 > dmlMaxRows2) {
38481
- throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows2}).`);
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
- return toBatchQueryPayload(batchResult);
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 dmlMaxRows2 = requireDmlApproval(input, "ksql_mutate");
38537
+ const dmlMaxRows = requireDmlApproval(input, "ksql_mutate");
38496
38538
  const validation = await validate(input);
38497
38539
  if (validation.batch) {
38498
- return mutateBatch(input, validation, dmlMaxRows2);
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 > dmlMaxRows2) {
38510
- throw new Error(`ArgumentError: INSERT rows (${validation.insertValuesCount}) exceed dmlMaxRows (${dmlMaxRows2}).`);
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
- maxRecords: dmlMaxRows2 + 1,
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
- const result = await executeSql(runtime.sql, runtime.client, {
38521
- maxRecords: runtime.maxRecords,
38522
- fetchParallel: runtime.fetchParallel,
38523
- onLimitReached: runtime.onLimit,
38524
- cacheContext: runtime.cacheContext,
38525
- confirm: async (count, operation) => {
38526
- if (count > dmlMaxRows2) {
38527
- throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows2}).`);
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
- return true;
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 dmlMaxRows2 = requireDmlApproval(input, "ksql_run_saved_query", "for DML saved queries");
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: dmlMaxRows2,
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 dmlMaxRows = external_exports.number().int().positive();
38694
- var savedQueryName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/);
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
- /** バッチ(複文)専用: 実行時エラー後も後続文を実行する(既定 false = fail-fast) */
38712
- continueOnError: external_exports.boolean().optional(),
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
- /** バッチ(複文)専用: バッチ合計の影響行数上限(既定なし = 文ごとの dmlMaxRows のみ)
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: dmlMaxRows.optional()
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: "1.0.0"
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 statements such as SELECT, WITH, UNION, EXPLAIN, SHOW APPS, and DESCRIBE. DML is rejected.",
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. INSERT_SELECT and UPSERT_SELECT are rejected.",
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", {
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rex0220/kintone-sql-tools",
3
- "version": "1.4.0",
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": "rex0220-plugin-uploader -f dist/ksql-plugin-v1.3.0.zip --watch"
37
+ "upload": "node scripts/upload-plugin.cjs"
38
38
  },
39
39
  "files": [
40
40
  "dist-cli/",