@josephyan/qingflow-app-user-mcp 1.1.30 → 1.1.31
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-user-mcp@1.1.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@1.1.31
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@1.1.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@1.1.31 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -1092,6 +1092,7 @@ class ViewUpsertPatch(StrictModel):
|
|
|
1092
1092
|
columns: list[str] = Field(default_factory=list)
|
|
1093
1093
|
group_by: str | None = None
|
|
1094
1094
|
filters: list[ViewFilterRulePatch] = Field(default_factory=list)
|
|
1095
|
+
filter_groups: list[list[ViewFilterRulePatch]] = Field(default_factory=list, validation_alias=AliasChoices("filter_groups", "filterGroups", "or_filters", "orFilters"))
|
|
1095
1096
|
start_field: str | None = Field(default=None, validation_alias=AliasChoices("start_field", "startField"))
|
|
1096
1097
|
end_field: str | None = Field(default=None, validation_alias=AliasChoices("end_field", "endField"))
|
|
1097
1098
|
title_field: str | None = Field(default=None, validation_alias=AliasChoices("title_field", "titleField"))
|
|
@@ -1131,6 +1132,12 @@ class ViewUpsertPatch(StrictModel):
|
|
|
1131
1132
|
payload["filters"] = payload.pop("filter_rules")
|
|
1132
1133
|
if "filterRules" in payload and "filters" not in payload:
|
|
1133
1134
|
payload["filters"] = payload.pop("filterRules")
|
|
1135
|
+
if "filterGroups" in payload and "filter_groups" not in payload:
|
|
1136
|
+
payload["filter_groups"] = payload.pop("filterGroups")
|
|
1137
|
+
if "or_filters" in payload and "filter_groups" not in payload:
|
|
1138
|
+
payload["filter_groups"] = payload.pop("or_filters")
|
|
1139
|
+
if "orFilters" in payload and "filter_groups" not in payload:
|
|
1140
|
+
payload["filter_groups"] = payload.pop("orFilters")
|
|
1134
1141
|
if "queryConditions" in payload and "query_conditions" not in payload:
|
|
1135
1142
|
payload["query_conditions"] = payload.pop("queryConditions")
|
|
1136
1143
|
if "query_condition" in payload and "query_conditions" not in payload:
|
|
@@ -7181,7 +7181,11 @@ class AiBuilderFacade:
|
|
|
7181
7181
|
missing_gantt_fields = [name for name in (start_field, end_field, title_field) if name and name not in field_names]
|
|
7182
7182
|
if missing_gantt_fields:
|
|
7183
7183
|
blocking_issues.append({"error_code": "UNKNOWN_VIEW_FIELD", "view_name": patch.get("name"), "missing_fields": missing_gantt_fields})
|
|
7184
|
-
translated_filters, filter_issues = _build_view_filter_groups(
|
|
7184
|
+
translated_filters, filter_issues = _build_view_filter_groups(
|
|
7185
|
+
current_fields_by_name=current_fields_by_name,
|
|
7186
|
+
filters=patch.get("filters") or [],
|
|
7187
|
+
filter_groups=patch.get("filter_groups") or [],
|
|
7188
|
+
)
|
|
7185
7189
|
if filter_issues:
|
|
7186
7190
|
blocking_issues.extend(
|
|
7187
7191
|
{
|
|
@@ -8924,7 +8928,11 @@ class AiBuilderFacade:
|
|
|
8924
8928
|
translated_filters = None
|
|
8925
8929
|
filter_issues = []
|
|
8926
8930
|
else:
|
|
8927
|
-
translated_filters, filter_issues = _build_view_filter_groups(
|
|
8931
|
+
translated_filters, filter_issues = _build_view_filter_groups(
|
|
8932
|
+
current_fields_by_name=current_fields_by_name,
|
|
8933
|
+
filters=patch.filters,
|
|
8934
|
+
filter_groups=patch.filter_groups,
|
|
8935
|
+
)
|
|
8928
8936
|
if filter_issues:
|
|
8929
8937
|
first_issue = filter_issues[0]
|
|
8930
8938
|
return _failed(
|
|
@@ -24069,31 +24077,51 @@ def _build_view_filter_groups(
|
|
|
24069
24077
|
*,
|
|
24070
24078
|
current_fields_by_name: dict[str, dict[str, Any]],
|
|
24071
24079
|
filters: list[Any],
|
|
24080
|
+
filter_groups: list[list[Any]] | None = None,
|
|
24072
24081
|
) -> tuple[list[list[dict[str, Any]]], list[dict[str, Any]]]:
|
|
24073
|
-
translated_rules: list[dict[str, Any]] = []
|
|
24074
24082
|
issues: list[dict[str, Any]] = []
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
|
|
24080
|
-
field_name = str(raw_rule.get("field_name") or "").strip()
|
|
24081
|
-
field = current_fields_by_name.get(field_name)
|
|
24082
|
-
if field is None:
|
|
24083
|
+
raw_groups: list[list[Any]] = []
|
|
24084
|
+
if filters:
|
|
24085
|
+
raw_groups.append(list(filters))
|
|
24086
|
+
for raw_group in filter_groups or []:
|
|
24087
|
+
if not isinstance(raw_group, list):
|
|
24083
24088
|
issues.append(
|
|
24084
24089
|
{
|
|
24085
|
-
"error_code": "
|
|
24086
|
-
"
|
|
24087
|
-
"
|
|
24090
|
+
"error_code": "INVALID_VIEW_FILTER_GROUP",
|
|
24091
|
+
"reason_path": "filter_groups[]",
|
|
24092
|
+
"message": "filter_groups must be a list of filter rule lists",
|
|
24088
24093
|
}
|
|
24089
24094
|
)
|
|
24090
24095
|
continue
|
|
24091
|
-
|
|
24092
|
-
|
|
24093
|
-
|
|
24094
|
-
|
|
24095
|
-
translated_rules
|
|
24096
|
-
|
|
24096
|
+
raw_groups.append(raw_group)
|
|
24097
|
+
|
|
24098
|
+
translated_groups: list[list[dict[str, Any]]] = []
|
|
24099
|
+
for group_index, raw_group in enumerate(raw_groups):
|
|
24100
|
+
translated_rules: list[dict[str, Any]] = []
|
|
24101
|
+
for raw_rule in raw_group:
|
|
24102
|
+
if hasattr(raw_rule, "model_dump"):
|
|
24103
|
+
raw_rule = raw_rule.model_dump(mode="json")
|
|
24104
|
+
if not isinstance(raw_rule, dict):
|
|
24105
|
+
continue
|
|
24106
|
+
field_name = str(raw_rule.get("field_name") or "").strip()
|
|
24107
|
+
field = current_fields_by_name.get(field_name)
|
|
24108
|
+
if field is None:
|
|
24109
|
+
issues.append(
|
|
24110
|
+
{
|
|
24111
|
+
"error_code": "UNKNOWN_VIEW_FIELD",
|
|
24112
|
+
"missing_fields": [field_name] if field_name else [],
|
|
24113
|
+
"reason_path": f"filter_groups[{group_index}].field_name" if filter_groups else "filters[].field_name",
|
|
24114
|
+
}
|
|
24115
|
+
)
|
|
24116
|
+
continue
|
|
24117
|
+
translated_rule, issue = _translate_view_filter_rule(field=field, rule=raw_rule)
|
|
24118
|
+
if issue:
|
|
24119
|
+
issues.append(issue)
|
|
24120
|
+
continue
|
|
24121
|
+
translated_rules.append(translated_rule)
|
|
24122
|
+
if translated_rules:
|
|
24123
|
+
translated_groups.append(translated_rules)
|
|
24124
|
+
return translated_groups, issues
|
|
24097
24125
|
|
|
24098
24126
|
|
|
24099
24127
|
def _translate_view_filter_rule(*, field: dict[str, Any], rule: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any] | None]:
|
|
@@ -5763,6 +5763,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5763
5763
|
"upsert_views[].type",
|
|
5764
5764
|
"upsert_views[].columns",
|
|
5765
5765
|
"upsert_views[].filters",
|
|
5766
|
+
"upsert_views[].filter_groups",
|
|
5766
5767
|
"upsert_views[].buttons",
|
|
5767
5768
|
"upsert_views[].visibility",
|
|
5768
5769
|
"upsert_views[].query_conditions",
|
|
@@ -5781,6 +5782,9 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5781
5782
|
"kanban": "board",
|
|
5782
5783
|
"filter_rules": "filters",
|
|
5783
5784
|
"filterRules": "filters",
|
|
5785
|
+
"filterGroups": "filter_groups",
|
|
5786
|
+
"or_filters": "filter_groups",
|
|
5787
|
+
"orFilters": "filter_groups",
|
|
5784
5788
|
"queryConditions": "query_conditions",
|
|
5785
5789
|
"query_condition": "query_conditions",
|
|
5786
5790
|
"queryCondition": "query_conditions",
|
|
@@ -5807,7 +5811,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5807
5811
|
},
|
|
5808
5812
|
"execution_notes": [
|
|
5809
5813
|
"upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
|
|
5810
|
-
"filters are saved fixed filters that apply when the view opens;
|
|
5814
|
+
"filters are saved fixed filters that apply when the view opens; filters is one AND group, filter_groups is OR across groups with AND inside each group",
|
|
5815
|
+
"query_conditions configure the frontend query panel and only apply after a user enters query values",
|
|
5811
5816
|
"upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
|
|
5812
5817
|
"use patch_views for partial parameter replacement on existing views; the tool reads current config, merges patch_views[].set/unset, then submits the backend full-save payload internally",
|
|
5813
5818
|
"remove_views accepts a raw view_key or an exact unique view name; after DELETE the tool verifies deletion by single view_key readback, not by a full app view list",
|
|
@@ -5891,6 +5896,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5891
5896
|
"views[].type",
|
|
5892
5897
|
"views[].columns",
|
|
5893
5898
|
"views[].filters",
|
|
5899
|
+
"views[].filter_groups",
|
|
5894
5900
|
"views[].query_conditions",
|
|
5895
5901
|
"views[].visibility",
|
|
5896
5902
|
"views[].set",
|
|
@@ -5908,6 +5914,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5908
5914
|
"upsert_views[].type",
|
|
5909
5915
|
"upsert_views[].columns",
|
|
5910
5916
|
"upsert_views[].filters",
|
|
5917
|
+
"upsert_views[].filter_groups",
|
|
5911
5918
|
"upsert_views[].buttons",
|
|
5912
5919
|
"upsert_views[].visibility",
|
|
5913
5920
|
"upsert_views[].query_conditions",
|
|
@@ -5926,6 +5933,9 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5926
5933
|
"kanban": "board",
|
|
5927
5934
|
"filter_rules": "filters",
|
|
5928
5935
|
"filterRules": "filters",
|
|
5936
|
+
"filterGroups": "filter_groups",
|
|
5937
|
+
"or_filters": "filter_groups",
|
|
5938
|
+
"orFilters": "filter_groups",
|
|
5929
5939
|
"queryConditions": "query_conditions",
|
|
5930
5940
|
"query_condition": "query_conditions",
|
|
5931
5941
|
"queryCondition": "query_conditions",
|
|
@@ -5968,7 +5978,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
5968
5978
|
"views[].action_buttons are applied after the target view_key is resolved; action_buttons require publish=true because the button writer publishes button configuration",
|
|
5969
5979
|
"buttons omitted preserves existing button config; buttons=[] clears all buttons; buttons=[...] replaces the full button config",
|
|
5970
5980
|
"upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
|
|
5971
|
-
"filters are saved fixed filters that apply when the view opens;
|
|
5981
|
+
"filters are saved fixed filters that apply when the view opens; filters is one AND group, filter_groups is OR across groups with AND inside each group",
|
|
5982
|
+
"query_conditions configure the frontend query panel and only apply after a user enters query values",
|
|
5972
5983
|
"upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
|
|
5973
5984
|
"use patch_views for partial parameter replacement on existing views; the public update mode is patch even though the backend save is still a full view payload",
|
|
5974
5985
|
"remove_views accepts a raw view_key or an exact unique view name; after DELETE the tool verifies deletion by single view_key readback, not by a full app view list",
|