@qingflow-tech/qingflow-app-builder-mcp 1.0.36 → 1.0.37

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 @qingflow-tech/qingflow-app-builder-mcp@1.0.36
6
+ npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.37
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.36 qingflow-app-builder-mcp
12
+ npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.37 qingflow-app-builder-mcp
13
13
  ```
14
14
 
15
15
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qingflow-tech/qingflow-app-builder-mcp",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
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 = "1.0.36"
7
+ version = "1.0.37"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -9,7 +9,7 @@ import random
9
9
  import re
10
10
  import string
11
11
  import tempfile
12
- from typing import Any, cast
12
+ from typing import Any, NoReturn, cast
13
13
  from urllib.parse import quote_plus, unquote_plus
14
14
  from uuid import uuid4
15
15
 
@@ -14615,7 +14615,18 @@ def _build_qingbi_chart_field_lookup(
14615
14615
  field_lookup: dict[str, dict[str, Any]],
14616
14616
  ) -> dict[str, Any]:
14617
14617
  by_selector: dict[str, list[dict[str, Any]]] = {}
14618
- form_by_que_id = field_lookup.get("by_que_id") or {}
14618
+ form_by_que_id = dict(field_lookup.get("by_que_id") or {})
14619
+ if not form_by_que_id:
14620
+ for bucket_name in ("by_name", "by_field_id"):
14621
+ bucket = field_lookup.get(bucket_name) or {}
14622
+ if not isinstance(bucket, dict):
14623
+ continue
14624
+ for form_field in bucket.values():
14625
+ if not isinstance(form_field, dict):
14626
+ continue
14627
+ que_id = _coerce_any_int(form_field.get("que_id"))
14628
+ if que_id is not None:
14629
+ form_by_que_id.setdefault(que_id, form_field)
14619
14630
 
14620
14631
  def add_selector(key: Any, field: dict[str, Any]) -> None:
14621
14632
  normalized = str(key or "").strip()
@@ -15095,13 +15106,109 @@ def _build_public_chart_filter_matrix(
15095
15106
  "fieldName": qingbi_field.get("fieldName") or form_field.get("name") or field_id,
15096
15107
  "fieldType": qingbi_field.get("fieldType") or _qingbi_field_type_from_public_field(str(form_field.get("type") or "")),
15097
15108
  "judgeType": judge_map.get(operator, "equal"),
15098
- "judgeValues": values,
15109
+ "judgeValue": _build_qingbi_chart_filter_judge_value(
15110
+ operator=operator,
15111
+ values=values,
15112
+ form_field=form_field,
15113
+ chart_type=chart_type,
15114
+ ),
15115
+ "judgeValueDetailList": [],
15099
15116
  "matchType": 1,
15100
15117
  }
15101
15118
  )
15102
15119
  return [group] if group else []
15103
15120
 
15104
15121
 
15122
+ def _build_qingbi_chart_filter_judge_value(
15123
+ *,
15124
+ operator: str,
15125
+ values: list[Any],
15126
+ form_field: dict[str, Any],
15127
+ chart_type: str,
15128
+ ) -> str | None:
15129
+ if operator in {ViewFilterOperator.is_empty.value, ViewFilterOperator.not_empty.value}:
15130
+ return None
15131
+ if operator == ViewFilterOperator.in_.value:
15132
+ return "<&&>".join(
15133
+ _qingbi_chart_filter_value_to_text(value=value, form_field=form_field, chart_type=chart_type) for value in values
15134
+ )
15135
+ if not values:
15136
+ return ""
15137
+ return _qingbi_chart_filter_value_to_text(value=values[0], form_field=form_field, chart_type=chart_type)
15138
+
15139
+
15140
+ def _qingbi_chart_filter_value_to_text(*, value: Any, form_field: dict[str, Any], chart_type: str) -> str:
15141
+ option_details = [
15142
+ item
15143
+ for item in (form_field.get("option_details") or [])
15144
+ if isinstance(item, dict) and item.get("id") is not None and item.get("value") is not None
15145
+ ]
15146
+ if not option_details:
15147
+ if isinstance(value, dict):
15148
+ for key in ("value", "label", "name", "title"):
15149
+ raw = str(value.get(key) or "").strip()
15150
+ if raw:
15151
+ return raw
15152
+ if value.get("id") is not None:
15153
+ return str(value.get("id"))
15154
+ return _stringify_condition_value(value)
15155
+
15156
+ option_by_value = {str(item.get("value") or "").strip(): item for item in option_details if str(item.get("value") or "").strip()}
15157
+ option_by_id = {str(item.get("id")): item for item in option_details if item.get("id") is not None}
15158
+
15159
+ def resolve(raw: Any) -> str | None:
15160
+ text = _stringify_condition_value(raw).strip()
15161
+ if not text:
15162
+ return None
15163
+ matched = option_by_value.get(text) or option_by_id.get(text)
15164
+ if not matched:
15165
+ return None
15166
+ return str(matched.get("value") or "").strip()
15167
+
15168
+ def reject(raw: Any) -> NoReturn:
15169
+ allowed = [
15170
+ {"id": str(item.get("id")), "value": str(item.get("value"))}
15171
+ for item in option_details
15172
+ ]
15173
+ _raise_chart_rule(
15174
+ rule_code="CHART_FILTER_OPTION_VALUE_UNSUPPORTED",
15175
+ chart_type=chart_type,
15176
+ message="chart filter value is not in the field option list",
15177
+ expected="Use an existing option label or option id from the target field schema.",
15178
+ actual={
15179
+ "field_name": form_field.get("name"),
15180
+ "field_id": form_field.get("que_id"),
15181
+ "value": raw,
15182
+ "allowed_values": allowed,
15183
+ },
15184
+ offending_fields=[{"field_name": form_field.get("name"), "field_id": form_field.get("que_id"), "options": allowed}],
15185
+ next_action="Read schema first, then pass one of the allowed option labels or option ids.",
15186
+ )
15187
+
15188
+ if isinstance(value, dict):
15189
+ for key in ("value", "label", "name", "title"):
15190
+ if value.get(key) is not None:
15191
+ resolved = resolve(value.get(key))
15192
+ if resolved is not None:
15193
+ return resolved
15194
+ reject(value.get(key))
15195
+ if value.get("id") is not None:
15196
+ resolved = resolve(value.get("id"))
15197
+ if resolved is not None:
15198
+ return resolved
15199
+ reject(value.get("id"))
15200
+ reject(value)
15201
+ if isinstance(value, int):
15202
+ resolved = resolve(value)
15203
+ if resolved is not None:
15204
+ return resolved
15205
+ reject(value)
15206
+ resolved = resolve(value)
15207
+ if resolved is not None:
15208
+ return resolved
15209
+ reject(value)
15210
+
15211
+
15105
15212
  def _build_public_chart_config_payload(
15106
15213
  *,
15107
15214
  patch: ChartUpsertPatch,