@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 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.1015
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.1015 qingflow-app-builder-mcp
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-app-builder-mcp",
3
- "version": "0.2.0-beta.1015",
3
+ "version": "0.2.0-beta.1016",
4
4
  "description": "Builder MCP for Qingflow app/package/system design and staged solution workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qingflow-mcp"
7
- version = "0.2.0b1015"
7
+ version = "0.2.0b1016"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
 
6
6
  __all__ = ["__version__"]
7
7
 
8
- _FALLBACK_VERSION = "0.2.0b1015"
8
+ _FALLBACK_VERSION = "0.2.0b1016"
9
9
 
10
10
 
11
11
  def _resolve_local_pyproject_version() -> str | None:
@@ -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"
@@ -666,7 +666,6 @@ def _trim_import_status_payload(payload: JSONObject) -> None:
666
666
  "total_rows",
667
667
  "success_rows",
668
668
  "failed_rows",
669
- "error_file_urls",
670
669
  "operate_time",
671
670
  "operate_user",
672
671
  "verification",
@@ -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": _normalize_optional_text(matched_record.get("processStatus")) or "unknown",
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()]