qingflow-mcp 0.3.16 → 0.3.17
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/server.js +114 -59
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -65,7 +65,7 @@ const REQUEST_TIMEOUT_MS = toPositiveInt(process.env.QINGFLOW_REQUEST_TIMEOUT_MS
|
|
|
65
65
|
const EXECUTION_BUDGET_MS = toPositiveInt(process.env.QINGFLOW_EXECUTION_BUDGET_MS) ?? 20000;
|
|
66
66
|
const WAIT_RESULT_DEFAULT_TIMEOUT_MS = toPositiveInt(process.env.QINGFLOW_WAIT_RESULT_TIMEOUT_MS) ?? 5000;
|
|
67
67
|
const WAIT_RESULT_POLL_INTERVAL_MS = toPositiveInt(process.env.QINGFLOW_WAIT_RESULT_POLL_INTERVAL_MS) ?? 500;
|
|
68
|
-
const SERVER_VERSION = "0.3.
|
|
68
|
+
const SERVER_VERSION = "0.3.17";
|
|
69
69
|
const accessToken = process.env.QINGFLOW_ACCESS_TOKEN;
|
|
70
70
|
const baseUrl = process.env.QINGFLOW_BASE_URL;
|
|
71
71
|
if (!accessToken) {
|
|
@@ -517,12 +517,23 @@ const createInputSchema = z
|
|
|
517
517
|
const createSuccessOutputSchema = z.object({
|
|
518
518
|
ok: z.literal(true),
|
|
519
519
|
data: z.object({
|
|
520
|
+
status: z.enum(["completed", "pending", "timeout", "failed"]),
|
|
520
521
|
request_id: z.string().nullable(),
|
|
521
522
|
apply_id: z.union([z.string(), z.number(), z.null()]),
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
523
|
+
resource: z
|
|
524
|
+
.object({
|
|
525
|
+
type: z.literal("record"),
|
|
526
|
+
apply_id: z.union([z.string(), z.number()])
|
|
527
|
+
})
|
|
528
|
+
.nullable(),
|
|
529
|
+
next_action: z
|
|
530
|
+
.object({
|
|
531
|
+
tool: z.string(),
|
|
532
|
+
arguments: z.record(z.unknown()),
|
|
533
|
+
reason: z.string().optional()
|
|
534
|
+
})
|
|
535
|
+
.nullable(),
|
|
536
|
+
raw: z.object({ operation_result: z.unknown() }).nullable()
|
|
526
537
|
}),
|
|
527
538
|
meta: apiMetaSchema
|
|
528
539
|
});
|
|
@@ -555,12 +566,23 @@ const updateInputSchema = z
|
|
|
555
566
|
const updateSuccessOutputSchema = z.object({
|
|
556
567
|
ok: z.literal(true),
|
|
557
568
|
data: z.object({
|
|
569
|
+
status: z.enum(["completed", "pending", "timeout", "failed"]),
|
|
558
570
|
request_id: z.string().nullable(),
|
|
559
|
-
apply_id: z.union([z.string(), z.number(), z.null()])
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
571
|
+
apply_id: z.union([z.string(), z.number(), z.null()]),
|
|
572
|
+
resource: z
|
|
573
|
+
.object({
|
|
574
|
+
type: z.literal("record"),
|
|
575
|
+
apply_id: z.union([z.string(), z.number()])
|
|
576
|
+
})
|
|
577
|
+
.nullable(),
|
|
578
|
+
next_action: z
|
|
579
|
+
.object({
|
|
580
|
+
tool: z.string(),
|
|
581
|
+
arguments: z.record(z.unknown()),
|
|
582
|
+
reason: z.string().optional()
|
|
583
|
+
})
|
|
584
|
+
.nullable(),
|
|
585
|
+
raw: z.object({ operation_result: z.unknown() }).nullable()
|
|
564
586
|
}),
|
|
565
587
|
meta: apiMetaSchema
|
|
566
588
|
});
|
|
@@ -1578,30 +1600,36 @@ server.registerTool("qf_record_create", {
|
|
|
1578
1600
|
const immediateApplyId = result?.applyId ?? null;
|
|
1579
1601
|
const shouldWaitForResult = (parsedArgs.wait_result ?? false) && requestId !== null && immediateApplyId === null;
|
|
1580
1602
|
let finalApplyId = immediateApplyId;
|
|
1581
|
-
let
|
|
1582
|
-
let
|
|
1583
|
-
let operationResult = null;
|
|
1603
|
+
let waitStatus = immediateApplyId !== null ? "completed" : "pending";
|
|
1604
|
+
let rawOperationResult = null;
|
|
1584
1605
|
if (shouldWaitForResult) {
|
|
1585
1606
|
const waited = await waitForOperationResolution({
|
|
1586
1607
|
requestId: requestId,
|
|
1587
1608
|
timeoutMs: parsedArgs.wait_timeout_ms ?? WAIT_RESULT_DEFAULT_TIMEOUT_MS
|
|
1588
1609
|
});
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
operationResult = waited.operationResult;
|
|
1610
|
+
waitStatus = waited.status;
|
|
1611
|
+
rawOperationResult = waited.operationResult;
|
|
1592
1612
|
finalApplyId = waited.applyId;
|
|
1593
1613
|
}
|
|
1614
|
+
const createResource = finalApplyId !== null ? { type: "record", apply_id: finalApplyId } : null;
|
|
1615
|
+
const createNextAction = waitStatus === "pending" || waitStatus === "timeout"
|
|
1616
|
+
? {
|
|
1617
|
+
tool: "qf_operation_get",
|
|
1618
|
+
arguments: { request_id: requestId },
|
|
1619
|
+
reason: waitStatus === "timeout"
|
|
1620
|
+
? "Operation timed out; poll again to check completion."
|
|
1621
|
+
: "Operation is async; poll to check completion."
|
|
1622
|
+
}
|
|
1623
|
+
: null;
|
|
1594
1624
|
return okResult({
|
|
1595
1625
|
ok: true,
|
|
1596
1626
|
data: {
|
|
1627
|
+
status: waitStatus,
|
|
1597
1628
|
request_id: requestId,
|
|
1598
1629
|
apply_id: finalApplyId,
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
async_hint: isResolved
|
|
1603
|
-
? "Record created and resolved."
|
|
1604
|
-
: "Use qf_operation_get with request_id to fetch the result."
|
|
1630
|
+
resource: createResource,
|
|
1631
|
+
next_action: createNextAction,
|
|
1632
|
+
raw: rawOperationResult !== null ? { operation_result: rawOperationResult } : null
|
|
1605
1633
|
},
|
|
1606
1634
|
meta: buildMeta(response)
|
|
1607
1635
|
}, `Create request sent for app ${parsedArgs.app_key}`);
|
|
@@ -1624,7 +1652,11 @@ server.registerTool("qf_record_update", {
|
|
|
1624
1652
|
const parsedArgs = updateInputSchema.parse(args);
|
|
1625
1653
|
const requiresForm = needsFormResolution(parsedArgs.fields);
|
|
1626
1654
|
if (requiresForm && !parsedArgs.app_key) {
|
|
1627
|
-
throw
|
|
1655
|
+
throw missingRequiredFieldError({
|
|
1656
|
+
field: "app_key",
|
|
1657
|
+
tool: "qf_record_update",
|
|
1658
|
+
fixHint: "Provide app_key when fields uses title-based keys, or switch fields to numeric que_id."
|
|
1659
|
+
});
|
|
1628
1660
|
}
|
|
1629
1661
|
const form = requiresForm && parsedArgs.app_key
|
|
1630
1662
|
? await getFormCached(parsedArgs.app_key, parsedArgs.user_id, Boolean(parsedArgs.force_refresh_form))
|
|
@@ -1639,31 +1671,45 @@ server.registerTool("qf_record_update", {
|
|
|
1639
1671
|
const result = asObject(response.result);
|
|
1640
1672
|
const updateRequestId = asNullableString(result?.requestId);
|
|
1641
1673
|
const shouldWaitForUpdate = (parsedArgs.wait_result ?? false) && updateRequestId !== null;
|
|
1642
|
-
let
|
|
1643
|
-
let
|
|
1644
|
-
let
|
|
1645
|
-
let updateApplyId = null;
|
|
1674
|
+
let updateStatus = "pending";
|
|
1675
|
+
let updateRawOperationResult = null;
|
|
1676
|
+
let updateApplyId = parsedArgs.apply_id;
|
|
1646
1677
|
if (shouldWaitForUpdate) {
|
|
1647
1678
|
const waited = await waitForOperationResolution({
|
|
1648
1679
|
requestId: updateRequestId,
|
|
1649
1680
|
timeoutMs: parsedArgs.wait_timeout_ms ?? WAIT_RESULT_DEFAULT_TIMEOUT_MS
|
|
1650
1681
|
});
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1682
|
+
updateStatus = waited.status;
|
|
1683
|
+
updateRawOperationResult = waited.operationResult;
|
|
1684
|
+
// For updates, the apply_id is already known from input; keep it unless operation returned a different one
|
|
1685
|
+
if (waited.applyId !== null) {
|
|
1686
|
+
updateApplyId = waited.applyId;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
else if (updateRequestId === null) {
|
|
1690
|
+
// No async operation — synchronous completion
|
|
1691
|
+
updateStatus = "completed";
|
|
1655
1692
|
}
|
|
1693
|
+
// else: wait_result=false but has requestId — submitted, not polled, stays "pending"
|
|
1694
|
+
const updateResource = updateApplyId !== null ? { type: "record", apply_id: updateApplyId } : null;
|
|
1695
|
+
const updateNextAction = updateStatus === "pending" || updateStatus === "timeout"
|
|
1696
|
+
? {
|
|
1697
|
+
tool: "qf_operation_get",
|
|
1698
|
+
arguments: { request_id: updateRequestId },
|
|
1699
|
+
reason: updateStatus === "timeout"
|
|
1700
|
+
? "Operation timed out; poll again to check completion."
|
|
1701
|
+
: "Operation is async; poll to check completion."
|
|
1702
|
+
}
|
|
1703
|
+
: null;
|
|
1656
1704
|
return okResult({
|
|
1657
1705
|
ok: true,
|
|
1658
1706
|
data: {
|
|
1707
|
+
status: updateStatus,
|
|
1659
1708
|
request_id: updateRequestId,
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
async_hint: updateIsResolved
|
|
1665
|
-
? "Record updated and resolved."
|
|
1666
|
-
: "Use qf_operation_get with request_id to fetch the update result."
|
|
1709
|
+
apply_id: updateApplyId,
|
|
1710
|
+
resource: updateResource,
|
|
1711
|
+
next_action: updateNextAction,
|
|
1712
|
+
raw: updateRawOperationResult !== null ? { operation_result: updateRawOperationResult } : null
|
|
1667
1713
|
},
|
|
1668
1714
|
meta: buildMeta(response)
|
|
1669
1715
|
}, `Update request sent for apply ${String(parsedArgs.apply_id)}`);
|
|
@@ -3362,7 +3408,7 @@ function inferPlanMissingRequired(tool, args) {
|
|
|
3362
3408
|
missing.push("select_columns");
|
|
3363
3409
|
}
|
|
3364
3410
|
}
|
|
3365
|
-
else {
|
|
3411
|
+
else if (queryMode === "list") {
|
|
3366
3412
|
if (!hasAppKey) {
|
|
3367
3413
|
missing.push("app_key");
|
|
3368
3414
|
}
|
|
@@ -3370,6 +3416,9 @@ function inferPlanMissingRequired(tool, args) {
|
|
|
3370
3416
|
missing.push("select_columns");
|
|
3371
3417
|
}
|
|
3372
3418
|
}
|
|
3419
|
+
else if (!hasAppKey) {
|
|
3420
|
+
missing.push("app_key");
|
|
3421
|
+
}
|
|
3373
3422
|
}
|
|
3374
3423
|
return missing;
|
|
3375
3424
|
}
|
|
@@ -4516,12 +4565,12 @@ async function executeRecordsExport(format, args) {
|
|
|
4516
4565
|
app_key: args.app_key,
|
|
4517
4566
|
selected_columns: selectedColumnsForRows,
|
|
4518
4567
|
filters: echoFilters(effectiveFilters),
|
|
4519
|
-
time_range:
|
|
4568
|
+
time_range: timeRangeResolution.mapping
|
|
4520
4569
|
? {
|
|
4521
|
-
column: String(args.time_range.
|
|
4522
|
-
from:
|
|
4523
|
-
to:
|
|
4524
|
-
timezone:
|
|
4570
|
+
column: String(args.time_range?.column ?? timeRangeResolution.mapping.requested ?? ""),
|
|
4571
|
+
from: timeRangeResolution.time_range?.from ?? null,
|
|
4572
|
+
to: timeRangeResolution.time_range?.to ?? null,
|
|
4573
|
+
timezone: timeRangeResolution.time_range?.timezone ?? null
|
|
4525
4574
|
}
|
|
4526
4575
|
: null
|
|
4527
4576
|
}, sourcePages);
|
|
@@ -4547,7 +4596,6 @@ async function executeRecordsExport(format, args) {
|
|
|
4547
4596
|
? {
|
|
4548
4597
|
completeness,
|
|
4549
4598
|
evidence,
|
|
4550
|
-
resolved_mappings: resolvedMappings,
|
|
4551
4599
|
execution: {
|
|
4552
4600
|
scanned_pages: fetchedPages,
|
|
4553
4601
|
requested_pages: requestedPages,
|
|
@@ -4562,6 +4610,7 @@ async function executeRecordsExport(format, args) {
|
|
|
4562
4610
|
? {
|
|
4563
4611
|
completeness,
|
|
4564
4612
|
evidence,
|
|
4613
|
+
resolved_mappings: resolvedMappings,
|
|
4565
4614
|
error_code: null,
|
|
4566
4615
|
fix_hint: null
|
|
4567
4616
|
}
|
|
@@ -6441,6 +6490,13 @@ function isPendingOperationStatus(status) {
|
|
|
6441
6490
|
return ["PENDING", "PROCESSING", "RUNNING", "IN_PROGRESS", "QUEUED"].includes(status);
|
|
6442
6491
|
}
|
|
6443
6492
|
function extractOperationApplyId(operationResult) {
|
|
6493
|
+
// Handle case where operationResult itself is a numeric string or number (the apply_id directly)
|
|
6494
|
+
if (typeof operationResult === "string" && /^\d+$/.test(operationResult.trim())) {
|
|
6495
|
+
return operationResult.trim();
|
|
6496
|
+
}
|
|
6497
|
+
if (typeof operationResult === "number" && Number.isFinite(operationResult)) {
|
|
6498
|
+
return operationResult;
|
|
6499
|
+
}
|
|
6444
6500
|
const obj = asObject(operationResult);
|
|
6445
6501
|
return obj?.applyId ?? obj?.apply_id ?? null;
|
|
6446
6502
|
}
|
|
@@ -6450,15 +6506,14 @@ async function waitForOperationResolution(params) {
|
|
|
6450
6506
|
while (Date.now() <= deadline) {
|
|
6451
6507
|
const response = await client.getOperation(params.requestId);
|
|
6452
6508
|
lastResult = response.result;
|
|
6453
|
-
const
|
|
6509
|
+
const opStatus = extractOperationStatus(lastResult);
|
|
6454
6510
|
const applyId = extractOperationApplyId(lastResult);
|
|
6455
|
-
if (
|
|
6456
|
-
return {
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
};
|
|
6511
|
+
if (applyId !== null) {
|
|
6512
|
+
return { status: "completed", operationResult: lastResult, applyId };
|
|
6513
|
+
}
|
|
6514
|
+
if (opStatus && !isPendingOperationStatus(opStatus)) {
|
|
6515
|
+
// Non-pending status but no apply_id — treat as failed
|
|
6516
|
+
return { status: "failed", operationResult: lastResult, applyId: null };
|
|
6462
6517
|
}
|
|
6463
6518
|
const remaining = deadline - Date.now();
|
|
6464
6519
|
if (remaining <= 0) {
|
|
@@ -6466,12 +6521,12 @@ async function waitForOperationResolution(params) {
|
|
|
6466
6521
|
}
|
|
6467
6522
|
await delay(Math.min(WAIT_RESULT_POLL_INTERVAL_MS, remaining));
|
|
6468
6523
|
}
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
operationResult: lastResult,
|
|
6473
|
-
|
|
6474
|
-
};
|
|
6524
|
+
// Timed out — check if last result has apply_id anyway (edge case)
|
|
6525
|
+
const finalApplyId = extractOperationApplyId(lastResult);
|
|
6526
|
+
if (finalApplyId !== null) {
|
|
6527
|
+
return { status: "completed", operationResult: lastResult, applyId: finalApplyId };
|
|
6528
|
+
}
|
|
6529
|
+
return { status: "timeout", operationResult: lastResult, applyId: null };
|
|
6475
6530
|
}
|
|
6476
6531
|
function resolveListItemLimit(params) {
|
|
6477
6532
|
if (params.total <= 0) {
|