@dypai-ai/mcp 1.5.12 → 1.5.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dypai-ai/mcp",
3
- "version": "1.5.12",
3
+ "version": "1.5.13",
4
4
  "description": "DYPAI MCP Server — AI agent toolkit for building and deploying full-stack apps",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -1121,7 +1121,7 @@ async function handleRequest(msg) {
1121
1121
  return makeResponse(id, {
1122
1122
  protocolVersion: "2024-11-05",
1123
1123
  capabilities: { tools: {} },
1124
- serverInfo: { name: "dypai", version: "1.5.12" },
1124
+ serverInfo: { name: "dypai", version: "1.5.13" },
1125
1125
  instructions: SERVER_INSTRUCTIONS,
1126
1126
  })
1127
1127
  }
@@ -397,6 +397,46 @@ function isWordAt(sql, index, word) {
397
397
  return !/[A-Za-z0-9_]/.test(before) && !/[A-Za-z0-9_]/.test(after)
398
398
  }
399
399
 
400
+ function skipDoubleQuotedIdentifier(sql, index) {
401
+ if (sql[index] !== "\"") return index
402
+ for (let i = index + 1; i < sql.length; i++) {
403
+ if (sql[i] !== "\"") continue
404
+ if (sql[i + 1] === "\"") {
405
+ i++
406
+ continue
407
+ }
408
+ return i
409
+ }
410
+ return sql.length - 1
411
+ }
412
+
413
+ function skipSqlSpaces(sql, index) {
414
+ let i = index
415
+ while (i < sql.length && /\s/.test(sql[i])) i++
416
+ return i
417
+ }
418
+
419
+ function findMatchingParen(sql, openIndex) {
420
+ if (sql[openIndex] !== "(") return -1
421
+ let depth = 0
422
+ for (let i = openIndex; i < sql.length; i++) {
423
+ const ch = sql[i]
424
+ if (ch === "\"") {
425
+ i = skipDoubleQuotedIdentifier(sql, i)
426
+ continue
427
+ }
428
+ if (ch === "(") {
429
+ depth++
430
+ continue
431
+ }
432
+ if (ch === ")") {
433
+ depth--
434
+ if (depth === 0) return i
435
+ }
436
+ }
437
+ return -1
438
+ }
439
+
400
440
  function findOuterSelectList(sqlText) {
401
441
  const sql = stripSqlForInference(sqlText)
402
442
  if (!sql) return null
@@ -404,6 +444,10 @@ function findOuterSelectList(sqlText) {
404
444
  let selectStart = -1
405
445
  for (let i = 0; i < sql.length; i++) {
406
446
  const ch = sql[i]
447
+ if (ch === "\"") {
448
+ i = skipDoubleQuotedIdentifier(sql, i)
449
+ continue
450
+ }
407
451
  if (ch === "(") depth++
408
452
  else if (ch === ")") depth = Math.max(0, depth - 1)
409
453
 
@@ -417,15 +461,183 @@ function findOuterSelectList(sqlText) {
417
461
  return sql.slice(selectStart, i).trim()
418
462
  }
419
463
  }
464
+ if (selectStart >= 0) return sql.slice(selectStart).trim()
420
465
  return null
421
466
  }
422
467
 
468
+ function hasTopLevelLimitOne(sqlText) {
469
+ const sql = stripSqlForInference(sqlText)
470
+ let depth = 0
471
+ for (let i = 0; i < sql.length; i++) {
472
+ const ch = sql[i]
473
+ if (ch === "\"") {
474
+ i = skipDoubleQuotedIdentifier(sql, i)
475
+ continue
476
+ }
477
+ if (ch === "(") {
478
+ depth++
479
+ continue
480
+ }
481
+ if (ch === ")") {
482
+ depth = Math.max(0, depth - 1)
483
+ continue
484
+ }
485
+ if (depth !== 0 || !isWordAt(sql, i, "limit")) continue
486
+
487
+ const tail = sql.slice(skipSqlSpaces(sql, i + "limit".length))
488
+ return /^1(?:\D|$)/.test(tail)
489
+ }
490
+ return false
491
+ }
492
+
493
+ function hasTopLevelFetchOne(sqlText) {
494
+ const sql = stripSqlForInference(sqlText)
495
+ let depth = 0
496
+ for (let i = 0; i < sql.length; i++) {
497
+ const ch = sql[i]
498
+ if (ch === "\"") {
499
+ i = skipDoubleQuotedIdentifier(sql, i)
500
+ continue
501
+ }
502
+ if (ch === "(") {
503
+ depth++
504
+ continue
505
+ }
506
+ if (ch === ")") {
507
+ depth = Math.max(0, depth - 1)
508
+ continue
509
+ }
510
+ if (depth !== 0 || !isWordAt(sql, i, "fetch")) continue
511
+
512
+ let cursor = skipSqlSpaces(sql, i + "fetch".length)
513
+ if (isWordAt(sql, cursor, "first")) cursor += "first".length
514
+ else if (isWordAt(sql, cursor, "next")) cursor += "next".length
515
+ else continue
516
+ cursor = skipSqlSpaces(sql, cursor)
517
+ return /^1(?:\D|$)/.test(sql.slice(cursor))
518
+ }
519
+ return false
520
+ }
521
+
522
+ function hasTopLevelGroupBy(sqlText) {
523
+ const sql = stripSqlForInference(sqlText)
524
+ let depth = 0
525
+ for (let i = 0; i < sql.length; i++) {
526
+ const ch = sql[i]
527
+ if (ch === "\"") {
528
+ i = skipDoubleQuotedIdentifier(sql, i)
529
+ continue
530
+ }
531
+ if (ch === "(") {
532
+ depth++
533
+ continue
534
+ }
535
+ if (ch === ")") {
536
+ depth = Math.max(0, depth - 1)
537
+ continue
538
+ }
539
+ if (depth !== 0 || !isWordAt(sql, i, "group")) continue
540
+
541
+ const restStart = i + "group".length
542
+ const rest = sql.slice(restStart)
543
+ const byOffset = rest.search(/\S/)
544
+ if (byOffset >= 0 && isWordAt(sql, restStart + byOffset, "by")) return true
545
+ }
546
+ return false
547
+ }
548
+
549
+ function hasTopLevelSetOperation(sqlText) {
550
+ const sql = stripSqlForInference(sqlText)
551
+ let depth = 0
552
+ for (let i = 0; i < sql.length; i++) {
553
+ const ch = sql[i]
554
+ if (ch === "\"") {
555
+ i = skipDoubleQuotedIdentifier(sql, i)
556
+ continue
557
+ }
558
+ if (ch === "(") {
559
+ depth++
560
+ continue
561
+ }
562
+ if (ch === ")") {
563
+ depth = Math.max(0, depth - 1)
564
+ continue
565
+ }
566
+ if (depth !== 0) continue
567
+ if (isWordAt(sql, i, "union") || isWordAt(sql, i, "intersect") || isWordAt(sql, i, "except")) {
568
+ return true
569
+ }
570
+ }
571
+ return false
572
+ }
573
+
574
+ const AGGREGATE_FUNCTIONS = ["count", "sum", "avg", "min", "max", "json_agg", "jsonb_agg", "array_agg", "string_agg"]
575
+
576
+ function isWindowAggregateCall(sql, openIndex) {
577
+ const closeIndex = findMatchingParen(sql, openIndex)
578
+ if (closeIndex < 0) return false
579
+
580
+ let cursor = skipSqlSpaces(sql, closeIndex + 1)
581
+ if (isWordAt(sql, cursor, "filter")) {
582
+ cursor = skipSqlSpaces(sql, cursor + "filter".length)
583
+ if (sql[cursor] === "(") {
584
+ const filterClose = findMatchingParen(sql, cursor)
585
+ if (filterClose >= 0) cursor = skipSqlSpaces(sql, filterClose + 1)
586
+ }
587
+ }
588
+
589
+ return isWordAt(sql, cursor, "over")
590
+ }
591
+
592
+ function hasOuterAggregateFunction(selectList) {
593
+ const sql = String(selectList || "")
594
+ let depth = 0
595
+ const activeSubqueryDepths = []
596
+
597
+ const insideSubquery = () => activeSubqueryDepths.some(d => d <= depth)
598
+
599
+ for (let i = 0; i < sql.length; i++) {
600
+ const ch = sql[i]
601
+ if (ch === "\"") {
602
+ i = skipDoubleQuotedIdentifier(sql, i)
603
+ continue
604
+ }
605
+ if (ch === "(") {
606
+ depth++
607
+ continue
608
+ }
609
+ if (ch === ")") {
610
+ activeSubqueryDepths.splice(0, activeSubqueryDepths.length, ...activeSubqueryDepths.filter(d => d < depth))
611
+ depth = Math.max(0, depth - 1)
612
+ continue
613
+ }
614
+
615
+ if (depth > 0 && isWordAt(sql, i, "select")) {
616
+ activeSubqueryDepths.push(depth)
617
+ i += "select".length - 1
618
+ continue
619
+ }
620
+
621
+ for (const fn of AGGREGATE_FUNCTIONS) {
622
+ if (!isWordAt(sql, i, fn)) continue
623
+ const openIndex = skipSqlSpaces(sql, i + fn.length)
624
+ if (sql[openIndex] === "(" && !insideSubquery() && !isWindowAggregateCall(sql, openIndex)) return true
625
+ }
626
+ }
627
+
628
+ return false
629
+ }
630
+
423
631
  function splitTopLevelComma(list) {
424
632
  const parts = []
425
633
  let depth = 0
426
634
  let start = 0
427
635
  for (let i = 0; i < list.length; i++) {
428
636
  const ch = list[i]
637
+ if (ch === "\"") {
638
+ i = skipDoubleQuotedIdentifier(list, i)
639
+ continue
640
+ }
429
641
  if (ch === "(") depth++
430
642
  else if (ch === ")") depth = Math.max(0, depth - 1)
431
643
  else if (ch === "," && depth === 0) {
@@ -478,10 +690,12 @@ function inferSelectOutputColumns(sqlText) {
478
690
  function inferSqlCardinality(sqlText) {
479
691
  const sql = stripSqlForInference(sqlText)
480
692
  if (!sql) return null
481
- if (/\blimit\s+1(?:\D|$)/i.test(sql)) return "single"
693
+ if (hasTopLevelLimitOne(sql) || hasTopLevelFetchOne(sql)) return "single"
482
694
  const startsLikeRead = /^(select|with)\b/i.test(sql)
483
- const hasAggregate = /\b(count|sum|avg|min|max|json_agg|jsonb_agg|array_agg|string_agg)\s*\(/i.test(sql)
484
- const hasGroupBy = /\bgroup\s+by\b/i.test(sql)
695
+ if (startsLikeRead && hasTopLevelSetOperation(sql)) return "many"
696
+ const outerSelectList = findOuterSelectList(sql)
697
+ const hasAggregate = outerSelectList ? hasOuterAggregateFunction(outerSelectList) : false
698
+ const hasGroupBy = hasTopLevelGroupBy(sql)
485
699
  if (startsLikeRead && hasAggregate && !hasGroupBy) return "single"
486
700
  return "many"
487
701
  }