@josephyan/qingflow-app-builder-mcp 0.2.0-beta.49 → 0.2.0-beta.50
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/builder_facade/models.py +61 -5
- package/src/qingflow_mcp/builder_facade/service.py +525 -20
- package/src/qingflow_mcp/tools/ai_builder_tools.py +51 -0
- package/src/qingflow_mcp/tools/app_tools.py +36 -0
- package/src/qingflow_mcp/tools/code_block_tools.py +18 -3
- package/src/qingflow_mcp/tools/record_tools.py +735 -90
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.50
|
|
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.50 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -28,6 +28,11 @@ class PublicFieldType(str, Enum):
|
|
|
28
28
|
subtable = "subtable"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class PublicRelationMode(str, Enum):
|
|
32
|
+
single = "single"
|
|
33
|
+
multiple = "multiple"
|
|
34
|
+
|
|
35
|
+
|
|
31
36
|
FIELD_TYPE_ALIASES: dict[str, PublicFieldType] = {
|
|
32
37
|
"textarea": PublicFieldType.long_text,
|
|
33
38
|
"amount": PublicFieldType.amount,
|
|
@@ -268,6 +273,7 @@ class FieldPatch(StrictModel):
|
|
|
268
273
|
target_app_key: str | None = None
|
|
269
274
|
display_field: FieldSelector | None = None
|
|
270
275
|
visible_fields: list[FieldSelector] = Field(default_factory=list)
|
|
276
|
+
relation_mode: PublicRelationMode | None = Field(default=None, validation_alias=AliasChoices("relation_mode", "relationMode", "selection_mode", "selectionMode"))
|
|
271
277
|
subfields: list["FieldPatch"] = Field(default_factory=list)
|
|
272
278
|
|
|
273
279
|
@model_validator(mode="after")
|
|
@@ -276,8 +282,8 @@ class FieldPatch(StrictModel):
|
|
|
276
282
|
raise ValueError("relation field requires target_app_key")
|
|
277
283
|
if self.type != PublicFieldType.relation and self.target_app_key:
|
|
278
284
|
raise ValueError("target_app_key is only allowed for relation fields")
|
|
279
|
-
if self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields):
|
|
280
|
-
raise ValueError("display_field and
|
|
285
|
+
if self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields or self.relation_mode is not None):
|
|
286
|
+
raise ValueError("display_field, visible_fields, and relation_mode are only allowed for relation fields")
|
|
281
287
|
if self.type == PublicFieldType.subtable and not self.subfields:
|
|
282
288
|
raise ValueError("subtable field requires subfields")
|
|
283
289
|
if self.type != PublicFieldType.subtable and self.subfields:
|
|
@@ -299,14 +305,15 @@ class FieldMutation(StrictModel):
|
|
|
299
305
|
target_app_key: str | None = None
|
|
300
306
|
display_field: FieldSelector | None = None
|
|
301
307
|
visible_fields: list[FieldSelector] | None = None
|
|
308
|
+
relation_mode: PublicRelationMode | None = Field(default=None, validation_alias=AliasChoices("relation_mode", "relationMode", "selection_mode", "selectionMode"))
|
|
302
309
|
subfields: list[FieldPatch] | None = None
|
|
303
310
|
|
|
304
311
|
@model_validator(mode="after")
|
|
305
312
|
def validate_shape(self) -> "FieldMutation":
|
|
306
313
|
if self.type == PublicFieldType.relation and not self.target_app_key:
|
|
307
314
|
raise ValueError("relation field requires target_app_key")
|
|
308
|
-
if self.type is not None and self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields):
|
|
309
|
-
raise ValueError("display_field and
|
|
315
|
+
if self.type is not None and self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields or self.relation_mode is not None):
|
|
316
|
+
raise ValueError("display_field, visible_fields, and relation_mode are only allowed for relation fields")
|
|
310
317
|
if self.type == PublicFieldType.subtable and not self.subfields:
|
|
311
318
|
raise ValueError("subtable field requires subfields")
|
|
312
319
|
return self
|
|
@@ -915,11 +922,30 @@ def _normalize_field_payload(value: Any) -> Any:
|
|
|
915
922
|
normalized_from_id = FIELD_TYPE_ID_ALIASES.get(raw_type)
|
|
916
923
|
if normalized_from_id is not None:
|
|
917
924
|
payload["type"] = normalized_from_id.value
|
|
918
|
-
return payload
|
|
919
925
|
if isinstance(raw_type, str):
|
|
920
926
|
normalized = FIELD_TYPE_ALIASES.get(raw_type.strip().lower())
|
|
921
927
|
if normalized is not None:
|
|
922
928
|
payload["type"] = normalized.value
|
|
929
|
+
normalized_relation_mode = _normalize_public_relation_mode(
|
|
930
|
+
payload.get("relation_mode", payload.get("relationMode", payload.get("selection_mode", payload.get("selectionMode"))))
|
|
931
|
+
)
|
|
932
|
+
if normalized_relation_mode is None:
|
|
933
|
+
for alias_key in ("optional_data_num", "optionalDataNum", "multiple", "allow_multiple"):
|
|
934
|
+
if alias_key in payload:
|
|
935
|
+
normalized_relation_mode = _normalize_public_relation_mode(payload.get(alias_key))
|
|
936
|
+
break
|
|
937
|
+
if normalized_relation_mode is not None:
|
|
938
|
+
payload["relation_mode"] = normalized_relation_mode
|
|
939
|
+
for alias_key in (
|
|
940
|
+
"relationMode",
|
|
941
|
+
"selection_mode",
|
|
942
|
+
"selectionMode",
|
|
943
|
+
"optional_data_num",
|
|
944
|
+
"optionalDataNum",
|
|
945
|
+
"multiple",
|
|
946
|
+
"allow_multiple",
|
|
947
|
+
):
|
|
948
|
+
payload.pop(alias_key, None)
|
|
923
949
|
return payload
|
|
924
950
|
|
|
925
951
|
|
|
@@ -927,3 +953,33 @@ def _slugify_title(title: str) -> str:
|
|
|
927
953
|
normalized = "".join(ch.lower() if ch.isalnum() else "_" for ch in str(title or ""))
|
|
928
954
|
collapsed = "_".join(part for part in normalized.split("_") if part)
|
|
929
955
|
return collapsed or "section"
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
def _normalize_public_relation_mode(value: Any) -> str | None:
|
|
959
|
+
if value is None:
|
|
960
|
+
return None
|
|
961
|
+
if isinstance(value, bool):
|
|
962
|
+
return PublicRelationMode.multiple.value if value else PublicRelationMode.single.value
|
|
963
|
+
if isinstance(value, int):
|
|
964
|
+
if value == 0:
|
|
965
|
+
return PublicRelationMode.multiple.value
|
|
966
|
+
if value == 1:
|
|
967
|
+
return PublicRelationMode.single.value
|
|
968
|
+
return None
|
|
969
|
+
if isinstance(value, str):
|
|
970
|
+
normalized = value.strip().lower()
|
|
971
|
+
aliases = {
|
|
972
|
+
"single": PublicRelationMode.single.value,
|
|
973
|
+
"single_select": PublicRelationMode.single.value,
|
|
974
|
+
"single-select": PublicRelationMode.single.value,
|
|
975
|
+
"one": PublicRelationMode.single.value,
|
|
976
|
+
"1": PublicRelationMode.single.value,
|
|
977
|
+
"multiple": PublicRelationMode.multiple.value,
|
|
978
|
+
"multi": PublicRelationMode.multiple.value,
|
|
979
|
+
"multi_select": PublicRelationMode.multiple.value,
|
|
980
|
+
"multi-select": PublicRelationMode.multiple.value,
|
|
981
|
+
"many": PublicRelationMode.multiple.value,
|
|
982
|
+
"0": PublicRelationMode.multiple.value,
|
|
983
|
+
}
|
|
984
|
+
return aliases.get(normalized, normalized or None)
|
|
985
|
+
return None
|