@josephyan/qingflow-app-builder-mcp 0.2.0-beta.1015 → 0.2.0-beta.1016
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 +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/cli/formatters.py +7 -0
- package/src/qingflow_mcp/response_trim.py +0 -1
- package/src/qingflow_mcp/tools/export_tools.py +15 -0
- package/src/qingflow_mcp/tools/import_tools.py +42 -2
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.1016
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.1016 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -407,6 +407,13 @@ def _format_import_status(result: dict[str, Any]) -> str:
|
|
|
407
407
|
f"Failed Rows: {result.get('failed') or 0}",
|
|
408
408
|
f"Progress: {result.get('progress') or '-'}",
|
|
409
409
|
]
|
|
410
|
+
if result.get("process_status") not in (None, ""):
|
|
411
|
+
lines.append(f"Process Status: {result.get('process_status')}")
|
|
412
|
+
error_file_urls = result.get("error_file_urls") if isinstance(result.get("error_file_urls"), list) else []
|
|
413
|
+
if error_file_urls:
|
|
414
|
+
lines.append("Error Files:")
|
|
415
|
+
for url in error_file_urls:
|
|
416
|
+
lines.append(f"- {url}")
|
|
410
417
|
_append_warnings(lines, result.get("warnings"))
|
|
411
418
|
_append_verification(lines, result.get("verification"))
|
|
412
419
|
return "\n".join(lines) + "\n"
|
|
@@ -192,6 +192,8 @@ class ExportTools(ToolBase):
|
|
|
192
192
|
filter_bean = self._build_export_filter_bean(
|
|
193
193
|
resolved_view,
|
|
194
194
|
selected_record_ids=effective_record_ids,
|
|
195
|
+
order_by=normalized_order_by,
|
|
196
|
+
row_scope=row_scope,
|
|
195
197
|
include_workflow_log=include_workflow_log,
|
|
196
198
|
)
|
|
197
199
|
started_at = _utc_now().replace(microsecond=0).isoformat()
|
|
@@ -732,6 +734,8 @@ class ExportTools(ToolBase):
|
|
|
732
734
|
resolved_view: AccessibleViewRoute,
|
|
733
735
|
*,
|
|
734
736
|
selected_record_ids: list[int],
|
|
737
|
+
order_by: list[JSONObject],
|
|
738
|
+
row_scope: str,
|
|
735
739
|
include_workflow_log: bool,
|
|
736
740
|
) -> JSONObject:
|
|
737
741
|
filter_payload: JSONObject = {}
|
|
@@ -744,6 +748,17 @@ class ExportTools(ToolBase):
|
|
|
744
748
|
filter_payload["type"] = DEFAULT_RECORD_LIST_TYPE
|
|
745
749
|
if selected_record_ids:
|
|
746
750
|
filter_payload["applyIds"] = selected_record_ids
|
|
751
|
+
if row_scope == "queried" and order_by:
|
|
752
|
+
normalized_sorts = [
|
|
753
|
+
{
|
|
754
|
+
"queId": field_id,
|
|
755
|
+
"isAscend": str(item.get("direction") or "asc").strip().lower() != "desc",
|
|
756
|
+
}
|
|
757
|
+
for item in order_by
|
|
758
|
+
if isinstance(item, dict) and (field_id := _coerce_int(item.get("field_id"))) is not None
|
|
759
|
+
]
|
|
760
|
+
if normalized_sorts:
|
|
761
|
+
filter_payload["sorts"] = normalized_sorts
|
|
747
762
|
return {
|
|
748
763
|
"filter": filter_payload,
|
|
749
764
|
# Backend export code later auto-unboxes this field to primitive boolean.
|
|
@@ -37,6 +37,13 @@ SAFE_REPAIRS = {
|
|
|
37
37
|
"normalize_url_cells",
|
|
38
38
|
}
|
|
39
39
|
EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
|
|
40
|
+
IMPORT_STATUS_BY_PROCESS_STATUS = {
|
|
41
|
+
1: "queued",
|
|
42
|
+
2: "running",
|
|
43
|
+
3: "succeeded",
|
|
44
|
+
4: "failed",
|
|
45
|
+
5: "partially_failed",
|
|
46
|
+
}
|
|
40
47
|
|
|
41
48
|
|
|
42
49
|
class ImportTools(ToolBase):
|
|
@@ -866,13 +873,26 @@ class ImportTools(ToolBase):
|
|
|
866
873
|
"process_id_str": normalized_process,
|
|
867
874
|
},
|
|
868
875
|
)
|
|
876
|
+
raw_process_status = matched_record.get("processStatus")
|
|
869
877
|
total_rows = _coerce_int(matched_record.get("totalNumber") or matched_record.get("total_rows"))
|
|
870
878
|
success_rows = _coerce_int(matched_record.get("successNum") or matched_record.get("success_rows"))
|
|
871
879
|
failed_rows = _coerce_int(matched_record.get("errorNum") or matched_record.get("failed_rows"))
|
|
872
880
|
progress = _coerce_int(matched_record.get("importPercentage") or matched_record.get("progress"))
|
|
881
|
+
normalized_status = _normalize_import_status(raw_process_status)
|
|
882
|
+
warnings: list[dict[str, str]] = []
|
|
883
|
+
if normalized_status in {"succeeded", "failed", "partially_failed"} and all(
|
|
884
|
+
value is None for value in (total_rows, success_rows, failed_rows)
|
|
885
|
+
):
|
|
886
|
+
warnings.append(
|
|
887
|
+
{
|
|
888
|
+
"code": "IMPORT_STATUS_COUNTERS_MISSING",
|
|
889
|
+
"message": "backend import history returned a terminal process status without row counters",
|
|
890
|
+
}
|
|
891
|
+
)
|
|
873
892
|
return {
|
|
874
893
|
"ok": True,
|
|
875
|
-
"status":
|
|
894
|
+
"status": normalized_status,
|
|
895
|
+
"process_status": _coerce_int(raw_process_status),
|
|
876
896
|
"app_key": resolved_app_key,
|
|
877
897
|
"import_id": normalized_import_id or (local_job.get("import_id") if isinstance(local_job, dict) else None),
|
|
878
898
|
"process_id_str": normalized_process,
|
|
@@ -885,7 +905,7 @@ class ImportTools(ToolBase):
|
|
|
885
905
|
"error_file_urls": _normalize_error_file_urls(matched_record.get("errorFileUrls")),
|
|
886
906
|
"operate_time": matched_record.get("operateTime"),
|
|
887
907
|
"operate_user": matched_record.get("operateUser"),
|
|
888
|
-
"warnings":
|
|
908
|
+
"warnings": warnings,
|
|
889
909
|
"verification": {
|
|
890
910
|
"status_lookup_completed": True,
|
|
891
911
|
"matched_by": matched_by,
|
|
@@ -2217,6 +2237,26 @@ def _coerce_int(value: Any) -> int | None:
|
|
|
2217
2237
|
return None
|
|
2218
2238
|
|
|
2219
2239
|
|
|
2240
|
+
def _normalize_import_status(value: Any) -> str:
|
|
2241
|
+
status_code = _coerce_int(value)
|
|
2242
|
+
if status_code is not None:
|
|
2243
|
+
return IMPORT_STATUS_BY_PROCESS_STATUS.get(status_code, "unknown")
|
|
2244
|
+
text = str(value or "").strip().lower()
|
|
2245
|
+
if text in {"queued", "running", "succeeded", "failed", "partially_failed", "unknown"}:
|
|
2246
|
+
return text
|
|
2247
|
+
if text in {"line_up", "lineup"}:
|
|
2248
|
+
return "queued"
|
|
2249
|
+
if text in {"execute", "executing", "processing"}:
|
|
2250
|
+
return "running"
|
|
2251
|
+
if text in {"success", "completed"}:
|
|
2252
|
+
return "succeeded"
|
|
2253
|
+
if text in {"partly_fail", "partial_fail", "partially_fail", "partial_failed"}:
|
|
2254
|
+
return "partially_failed"
|
|
2255
|
+
if text in {"fail", "error"}:
|
|
2256
|
+
return "failed"
|
|
2257
|
+
return "unknown"
|
|
2258
|
+
|
|
2259
|
+
|
|
2220
2260
|
def _normalize_error_file_urls(value: Any) -> list[str]:
|
|
2221
2261
|
if isinstance(value, list):
|
|
2222
2262
|
return [str(item).strip() for item in value if str(item).strip()]
|