@josephyan/qingflow-app-builder-mcp 0.2.0-beta.27 → 0.2.0-beta.28

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.27
6
+ npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.28
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.27 qingflow-app-builder-mcp
12
+ npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.28 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.27",
3
+ "version": "0.2.0-beta.28",
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.0b27"
7
+ version = "0.2.0b28"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,4 +2,4 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.2.0b27"
5
+ __version__ = "0.2.0b28"
@@ -53,6 +53,7 @@ Always call `record_schema_get` before `record_list`, `record_get`, `record_writ
53
53
 
54
54
  - Hidden fields are omitted.
55
55
  - Missing fields mean the field is not visible in the current permission scope.
56
+ - Read `fields` and `suggested_*` from the top level of the schema response.
56
57
 
57
58
  ## Analytics Path
58
59
 
@@ -79,6 +80,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
79
80
 
80
81
  `record_schema_get -> record_list / record_get / record_write`
81
82
 
83
+ - For `columns`, prefer `[{{field_id}}]`; bare integer field ids remain supported for compatibility.
84
+
82
85
  `record_write` uses SQL-like JSON clauses:
83
86
 
84
87
  - `insert` -> `values`
@@ -41,6 +41,7 @@ Always call `record_schema_get` before `record_list`, `record_get`, `record_writ
41
41
 
42
42
  - Hidden fields are omitted.
43
43
  - Missing fields mean the field is not visible in the current permission scope.
44
+ - Read `fields` and `suggested_*` from the top level of the schema response.
44
45
 
45
46
  ## Analytics Path
46
47
 
@@ -67,6 +68,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
67
68
 
68
69
  `record_schema_get -> record_list / record_get / record_write`
69
70
 
71
+ - For `columns`, prefer `[{{field_id}}]`; bare integer field ids remain supported for compatibility.
72
+
70
73
  `record_write` uses SQL-like JSON clauses:
71
74
 
72
75
  - `insert` -> `values`
@@ -269,7 +269,7 @@ class RecordTools(ToolBase):
269
269
  def record_list(
270
270
  profile: str = DEFAULT_PROFILE,
271
271
  app_key: str = "",
272
- columns: list[int] | None = None,
272
+ columns: list[JSONObject | int] | None = None,
273
273
  where: list[JSONObject] | None = None,
274
274
  order_by: list[JSONObject] | None = None,
275
275
  limit: int = 50,
@@ -296,7 +296,7 @@ class RecordTools(ToolBase):
296
296
  profile: str = DEFAULT_PROFILE,
297
297
  app_key: str = "",
298
298
  record_id: int = 0,
299
- columns: list[int] | None = None,
299
+ columns: list[JSONObject | int] | None = None,
300
300
  workflow_node_id: int | None = None,
301
301
  output_profile: str = "normal",
302
302
  ) -> JSONObject:
@@ -389,23 +389,21 @@ class RecordTools(ToolBase):
389
389
  "ok": True,
390
390
  "status": "success",
391
391
  "request_route": self._request_route_payload(context),
392
- "data": {
393
- "app_key": app_key,
394
- "schema_scope": "applicant_node",
395
- "workflow_node": {
396
- "workflow_node_id": applicant_node.workflow_node_id,
397
- "name": applicant_node.name,
398
- "type": applicant_node.type,
399
- },
400
- "view_resolution": _view_selection_payload(view_selection),
401
- "fields": fields,
402
- "suggested_dimensions": suggested_dimensions,
403
- "suggested_metrics": suggested_metrics,
404
- "suggested_time_fields": suggested_time_fields,
392
+ "app_key": app_key,
393
+ "schema_scope": "applicant_node",
394
+ "workflow_node": {
395
+ "workflow_node_id": applicant_node.workflow_node_id,
396
+ "name": applicant_node.name,
397
+ "type": applicant_node.type,
405
398
  },
399
+ "view_resolution": _view_selection_payload(view_selection),
400
+ "fields": fields,
401
+ "suggested_dimensions": suggested_dimensions,
402
+ "suggested_metrics": suggested_metrics,
403
+ "suggested_time_fields": suggested_time_fields,
406
404
  }
407
405
  if output_profile == "verbose":
408
- response["data"]["field_count"] = len(fields)
406
+ response["field_count"] = len(fields)
409
407
  return response
410
408
 
411
409
  return self._run_record_tool(profile, runner)
@@ -605,7 +603,7 @@ class RecordTools(ToolBase):
605
603
  *,
606
604
  profile: str,
607
605
  app_key: str,
608
- columns: list[int],
606
+ columns: list[JSONObject | int],
609
607
  where: list[JSONObject],
610
608
  order_by: list[JSONObject],
611
609
  limit: int,
@@ -617,10 +615,9 @@ class RecordTools(ToolBase):
617
615
  normalized_output_profile = self._normalize_public_output_profile(output_profile)
618
616
  if not app_key:
619
617
  raise_tool_error(QingflowApiError.config_error("app_key is required"))
620
- if not columns:
618
+ normalized_columns = _normalize_public_column_selectors(columns)
619
+ if not normalized_columns:
621
620
  raise_tool_error(QingflowApiError.config_error("columns is required"))
622
- if any(not isinstance(item, int) or item < 0 for item in columns):
623
- raise_tool_error(QingflowApiError.config_error("columns must be a list of field_id integers"))
624
621
  if limit <= 0:
625
622
  raise_tool_error(QingflowApiError.config_error("limit must be positive"))
626
623
  if page <= 0:
@@ -640,8 +637,8 @@ class RecordTools(ToolBase):
640
637
  filters=self._normalize_record_list_where(where),
641
638
  sorts=self._normalize_record_list_order_by(order_by),
642
639
  max_rows=limit,
643
- max_columns=len(columns),
644
- select_columns=columns,
640
+ max_columns=len(normalized_columns),
641
+ select_columns=normalized_columns,
645
642
  amount_column=None,
646
643
  time_range={},
647
644
  stat_policy={},
@@ -674,7 +671,7 @@ class RecordTools(ToolBase):
674
671
  "result_amount": pagination.get("result_amount"),
675
672
  },
676
673
  "selection": {
677
- "columns": columns,
674
+ "columns": [_column_selector_payload(field_id) for field_id in normalized_columns],
678
675
  "view": cast(JSONObject, raw["data"]).get("view"),
679
676
  },
680
677
  },
@@ -695,17 +692,16 @@ class RecordTools(ToolBase):
695
692
  profile: str,
696
693
  app_key: str,
697
694
  record_id: int,
698
- columns: list[int],
695
+ columns: list[JSONObject | int],
699
696
  workflow_node_id: int | None,
700
697
  output_profile: str,
701
698
  ) -> JSONObject:
702
699
  normalized_output_profile = self._normalize_public_output_profile(output_profile)
703
700
  if record_id <= 0:
704
701
  raise_tool_error(QingflowApiError.config_error("record_id must be positive"))
705
- if columns and any(not isinstance(item, int) or item < 0 for item in columns):
706
- raise_tool_error(QingflowApiError.config_error("columns must be a list of field_id integers"))
702
+ normalized_columns = _normalize_public_column_selectors(columns)
707
703
 
708
- if columns:
704
+ if normalized_columns:
709
705
  raw = self.record_query(
710
706
  profile=profile,
711
707
  query_mode="record",
@@ -720,8 +716,8 @@ class RecordTools(ToolBase):
720
716
  filters=[],
721
717
  sorts=[],
722
718
  max_rows=1,
723
- max_columns=len(columns),
724
- select_columns=columns,
719
+ max_columns=len(normalized_columns),
720
+ select_columns=normalized_columns,
725
721
  amount_column=None,
726
722
  time_range={},
727
723
  stat_policy={},
@@ -742,7 +738,7 @@ class RecordTools(ToolBase):
742
738
  "record_id": record_id,
743
739
  "record": record_data.get("row"),
744
740
  "selection": {
745
- "columns": columns,
741
+ "columns": [_column_selector_payload(field_id) for field_id in normalized_columns],
746
742
  "workflow_node_id": workflow_node_id,
747
743
  },
748
744
  },
@@ -5262,6 +5258,28 @@ def _extract_sort_selector(item: JSONObject) -> JSONValue:
5262
5258
  return None
5263
5259
 
5264
5260
 
5261
+ def _normalize_public_column_selectors(columns: list[JSONObject | int]) -> list[int]:
5262
+ normalized: list[int] = []
5263
+ for item in columns:
5264
+ field_id: int | None = None
5265
+ if isinstance(item, int):
5266
+ field_id = item
5267
+ elif isinstance(item, dict):
5268
+ field_id = _coerce_count(item.get("field_id", item.get("fieldId")))
5269
+ if field_id is None or field_id < 0:
5270
+ raise_tool_error(
5271
+ QingflowApiError.config_error(
5272
+ "columns must be a list of field_id integers or {field_id} objects"
5273
+ )
5274
+ )
5275
+ normalized.append(field_id)
5276
+ return normalized
5277
+
5278
+
5279
+ def _column_selector_payload(field_id: int) -> JSONObject:
5280
+ return {"field_id": field_id}
5281
+
5282
+
5265
5283
  def _resolve_sort_ascend(item: JSONObject) -> bool:
5266
5284
  if "isAscend" in item:
5267
5285
  return bool(item["isAscend"])