@qingflow-tech/qingflow-app-builder-mcp 1.0.19 → 1.0.21
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/cli/commands/builder.py +1 -1
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/formatters.py +160 -0
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.
|
|
6
|
+
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.21
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
12
|
+
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.21 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -20,7 +20,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
20
20
|
file_upload_local.add_argument("--bucket-type")
|
|
21
21
|
file_upload_local.add_argument("--path-id", type=int)
|
|
22
22
|
file_upload_local.add_argument("--file-related-url")
|
|
23
|
-
file_upload_local.set_defaults(handler=_handle_file_upload_local, format_hint="
|
|
23
|
+
file_upload_local.set_defaults(handler=_handle_file_upload_local, format_hint="file_upload_local")
|
|
24
24
|
|
|
25
25
|
feedback = builder_subparsers.add_parser("feedback", help="builder 侧反馈提交")
|
|
26
26
|
feedback_subparsers = feedback.add_subparsers(dest="builder_feedback_command", required=True)
|
|
@@ -11,7 +11,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
11
11
|
|
|
12
12
|
get_parser = chart_subparsers.add_parser("get", help="读取报表数据")
|
|
13
13
|
get_parser.add_argument("--chart-id", required=True)
|
|
14
|
-
get_parser.set_defaults(handler=_handle_get, format_hint="
|
|
14
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="chart_get")
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -10,11 +10,11 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
10
10
|
portal_subparsers = parser.add_subparsers(dest="portal_command", required=True)
|
|
11
11
|
|
|
12
12
|
list_parser = portal_subparsers.add_parser("list", help="列出当前用户可访问的门户")
|
|
13
|
-
list_parser.set_defaults(handler=_handle_list, format_hint="
|
|
13
|
+
list_parser.set_defaults(handler=_handle_list, format_hint="portal_list")
|
|
14
14
|
|
|
15
15
|
get_parser = portal_subparsers.add_parser("get", help="读取门户内容清单")
|
|
16
16
|
get_parser.add_argument("--dash-key", required=True)
|
|
17
|
-
get_parser.set_defaults(handler=_handle_get, format_hint="
|
|
17
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="portal_get")
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -11,7 +11,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
11
11
|
|
|
12
12
|
get_parser = view_subparsers.add_parser("get", help="读取视图资源描述")
|
|
13
13
|
get_parser.add_argument("--view-id", required=True)
|
|
14
|
-
get_parser.set_defaults(handler=_handle_get, format_hint="
|
|
14
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="view_get")
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -227,6 +227,120 @@ def _format_app_get(result: dict[str, Any]) -> str:
|
|
|
227
227
|
_append_warnings(lines, result.get("warnings"))
|
|
228
228
|
return "\n".join(lines) + "\n"
|
|
229
229
|
|
|
230
|
+
|
|
231
|
+
def _format_portal_list(result: dict[str, Any]) -> str:
|
|
232
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
233
|
+
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
234
|
+
rows: list[list[str]] = []
|
|
235
|
+
for item in items:
|
|
236
|
+
if not isinstance(item, dict):
|
|
237
|
+
continue
|
|
238
|
+
rows.append(
|
|
239
|
+
[
|
|
240
|
+
str(item.get("dash_key") or ""),
|
|
241
|
+
str(item.get("dash_name") or ""),
|
|
242
|
+
",".join(str(tag_id) for tag_id in item.get("package_tag_ids") or []),
|
|
243
|
+
]
|
|
244
|
+
)
|
|
245
|
+
text = _render_titled_table("Portals", ["dash_key", "name", "package_tag_ids"], rows).rstrip("\n")
|
|
246
|
+
lines = [text, f"Total: {data.get('total') if data.get('total') is not None else len(items)}"]
|
|
247
|
+
_append_warnings(lines, result.get("warnings"))
|
|
248
|
+
_append_verification(lines, result.get("verification"))
|
|
249
|
+
return "\n".join(lines) + "\n"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _format_portal_get(result: dict[str, Any]) -> str:
|
|
253
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
254
|
+
components = data.get("components") if isinstance(data.get("components"), list) else []
|
|
255
|
+
lines = [
|
|
256
|
+
f"Portal: {data.get('dash_name') or '-'}",
|
|
257
|
+
f"Dash Key: {data.get('dash_key') or '-'}",
|
|
258
|
+
f"Components: {data.get('component_count') if data.get('component_count') is not None else len(components)}",
|
|
259
|
+
]
|
|
260
|
+
package_tag_ids = data.get("package_tag_ids") if isinstance(data.get("package_tag_ids"), list) else []
|
|
261
|
+
if package_tag_ids:
|
|
262
|
+
lines.append("Package Tag IDs: " + ", ".join(str(item) for item in package_tag_ids))
|
|
263
|
+
if components:
|
|
264
|
+
lines.append("Component Refs:")
|
|
265
|
+
for item in components[:12]:
|
|
266
|
+
if not isinstance(item, dict):
|
|
267
|
+
continue
|
|
268
|
+
prefix = f"- {item.get('source_type') or 'unknown'}"
|
|
269
|
+
title = item.get("title")
|
|
270
|
+
chart_ref = item.get("chart_ref") if isinstance(item.get("chart_ref"), dict) else {}
|
|
271
|
+
view_ref = item.get("view_ref") if isinstance(item.get("view_ref"), dict) else {}
|
|
272
|
+
if chart_ref:
|
|
273
|
+
lines.append(f"{prefix}: {title or chart_ref.get('chart_name') or '-'} / chart_id={chart_ref.get('chart_id') or '-'}")
|
|
274
|
+
elif view_ref:
|
|
275
|
+
lines.append(
|
|
276
|
+
f"{prefix}: {title or view_ref.get('view_name') or '-'} / "
|
|
277
|
+
f"view_id={view_ref.get('view_id') or '-'} / app_key={view_ref.get('app_key') or '-'}"
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
lines.append(f"{prefix}: {title or '-'}")
|
|
281
|
+
if len(components) > 12:
|
|
282
|
+
lines.append(f"... {len(components) - 12} more")
|
|
283
|
+
_append_warnings(lines, result.get("warnings"))
|
|
284
|
+
_append_verification(lines, result.get("verification"))
|
|
285
|
+
return "\n".join(lines) + "\n"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _format_view_get(result: dict[str, Any]) -> str:
|
|
289
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
290
|
+
export_capability = data.get("export_capability") if isinstance(data.get("export_capability"), dict) else {}
|
|
291
|
+
visible_columns = data.get("visible_columns") if isinstance(data.get("visible_columns"), list) else []
|
|
292
|
+
lines = [
|
|
293
|
+
f"View: {data.get('view_name') or '-'}",
|
|
294
|
+
f"View ID: {data.get('view_id') or '-'}",
|
|
295
|
+
f"View Key: {data.get('view_key') or '-'}",
|
|
296
|
+
f"App Key: {data.get('app_key') or '-'}",
|
|
297
|
+
f"View Type: {data.get('view_type') or '-'}",
|
|
298
|
+
f"Analysis Supported: {data.get('analysis_supported')}",
|
|
299
|
+
]
|
|
300
|
+
if export_capability:
|
|
301
|
+
lines.append(
|
|
302
|
+
"Export: "
|
|
303
|
+
f"supported={export_capability.get('supported')} / "
|
|
304
|
+
f"tool={export_capability.get('tool') or '-'} / "
|
|
305
|
+
f"requires_app_key={export_capability.get('requires_app_key')}"
|
|
306
|
+
)
|
|
307
|
+
lines.append(f"Visible Columns: {len(visible_columns)}")
|
|
308
|
+
for column in visible_columns[:20]:
|
|
309
|
+
lines.append(f"- {column}")
|
|
310
|
+
if len(visible_columns) > 20:
|
|
311
|
+
lines.append(f"... {len(visible_columns) - 20} more")
|
|
312
|
+
_append_warnings(lines, result.get("warnings"))
|
|
313
|
+
_append_verification(lines, result.get("verification"))
|
|
314
|
+
return "\n".join(lines) + "\n"
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _format_chart_get(result: dict[str, Any]) -> str:
|
|
318
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
319
|
+
chart_data = data.get("data") if isinstance(data.get("data"), dict) else {}
|
|
320
|
+
config = data.get("config") if isinstance(data.get("config"), dict) else {}
|
|
321
|
+
lines = [
|
|
322
|
+
f"Chart: {data.get('chart_name') or '-'}",
|
|
323
|
+
f"Chart ID: {data.get('chart_id') or '-'}",
|
|
324
|
+
f"Chart Type: {data.get('chart_type') or '-'}",
|
|
325
|
+
f"Data Source: {data.get('data_source_type') or '-'} / {data.get('data_source_id') or '-'}",
|
|
326
|
+
f"Data Loaded: {bool(chart_data)}",
|
|
327
|
+
f"Config Loaded: {bool(config)}",
|
|
328
|
+
]
|
|
329
|
+
summary = chart_data.get("summary") if isinstance(chart_data.get("summary"), dict) else {}
|
|
330
|
+
rows = chart_data.get("rows") if isinstance(chart_data.get("rows"), list) else []
|
|
331
|
+
if summary:
|
|
332
|
+
lines.append("Summary:")
|
|
333
|
+
lines.extend(f"- {line}" for line in _dict_scalar_lines(summary))
|
|
334
|
+
if rows:
|
|
335
|
+
lines.append(f"Rows: {len(rows)}")
|
|
336
|
+
lines.append(json.dumps(rows[0], ensure_ascii=False))
|
|
337
|
+
elif config:
|
|
338
|
+
lines.append("Config Keys: " + ", ".join(str(key) for key in list(config.keys())[:12]))
|
|
339
|
+
_append_warnings(lines, result.get("warnings"))
|
|
340
|
+
_append_verification(lines, result.get("verification"))
|
|
341
|
+
return "\n".join(lines) + "\n"
|
|
342
|
+
|
|
343
|
+
|
|
230
344
|
def _format_record_list(result: dict[str, Any]) -> str:
|
|
231
345
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
232
346
|
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
@@ -926,6 +1040,47 @@ def _format_builder_summary(result: dict[str, Any]) -> str:
|
|
|
926
1040
|
return "\n".join(lines) + "\n"
|
|
927
1041
|
|
|
928
1042
|
|
|
1043
|
+
def _format_file_upload_local(result: dict[str, Any]) -> str:
|
|
1044
|
+
lines = [
|
|
1045
|
+
f"Upload Kind: {result.get('upload_kind') or result.get('requested_upload_kind') or '-'}",
|
|
1046
|
+
f"Effective Upload Kind: {result.get('effective_upload_kind') or result.get('upload_kind') or '-'}",
|
|
1047
|
+
]
|
|
1048
|
+
if "upload_fallback_applied" in result:
|
|
1049
|
+
lines.append(f"Fallback Applied: {result.get('upload_fallback_applied')}")
|
|
1050
|
+
if result.get("upload_fallback_reason"):
|
|
1051
|
+
lines.append(f"Fallback Reason: {result.get('upload_fallback_reason')}")
|
|
1052
|
+
if result.get("file_name"):
|
|
1053
|
+
lines.append(f"File: {result.get('file_name')}")
|
|
1054
|
+
if result.get("file_size") is not None:
|
|
1055
|
+
lines.append(f"Size: {result.get('file_size')}")
|
|
1056
|
+
if result.get("content_type"):
|
|
1057
|
+
lines.append(f"Content Type: {result.get('content_type')}")
|
|
1058
|
+
if result.get("upload_protocol"):
|
|
1059
|
+
lines.append(f"Protocol: {result.get('upload_protocol')}")
|
|
1060
|
+
if result.get("download_url"):
|
|
1061
|
+
lines.append(f"Download URL: {result.get('download_url')}")
|
|
1062
|
+
|
|
1063
|
+
attachment_value = result.get("attachment_value") if isinstance(result.get("attachment_value"), dict) else {}
|
|
1064
|
+
if attachment_value:
|
|
1065
|
+
lines.append("Attachment Value:")
|
|
1066
|
+
for key in ("value", "name", "otherInfo"):
|
|
1067
|
+
value = attachment_value.get(key)
|
|
1068
|
+
if value not in (None, ""):
|
|
1069
|
+
lines.append(f"- {key}: {value}")
|
|
1070
|
+
|
|
1071
|
+
comment_file_info = result.get("comment_file_info") if isinstance(result.get("comment_file_info"), dict) else {}
|
|
1072
|
+
if comment_file_info:
|
|
1073
|
+
lines.append("Comment File:")
|
|
1074
|
+
for key in ("url", "name", "uploadFileSize"):
|
|
1075
|
+
value = comment_file_info.get(key)
|
|
1076
|
+
if value not in (None, ""):
|
|
1077
|
+
lines.append(f"- {key}: {value}")
|
|
1078
|
+
|
|
1079
|
+
_append_warnings(lines, result.get("warnings"))
|
|
1080
|
+
_append_verification(lines, result.get("verification"))
|
|
1081
|
+
return "\n".join(lines) + "\n"
|
|
1082
|
+
|
|
1083
|
+
|
|
929
1084
|
def emit_json_result(result: dict[str, Any], *, stream: TextIO) -> None:
|
|
930
1085
|
json.dump(result, stream, ensure_ascii=False, indent=2)
|
|
931
1086
|
stream.write("\n")
|
|
@@ -1112,6 +1267,10 @@ _FORMATTERS = {
|
|
|
1112
1267
|
"workspace_select": _format_workspace_select,
|
|
1113
1268
|
"app_list": _format_app_items,
|
|
1114
1269
|
"app_get": _format_app_get,
|
|
1270
|
+
"portal_list": _format_portal_list,
|
|
1271
|
+
"portal_get": _format_portal_get,
|
|
1272
|
+
"view_get": _format_view_get,
|
|
1273
|
+
"chart_get": _format_chart_get,
|
|
1115
1274
|
"record_list": _format_record_list,
|
|
1116
1275
|
"record_access": _format_record_access,
|
|
1117
1276
|
"record_get": _format_record_get,
|
|
@@ -1135,4 +1294,5 @@ _FORMATTERS = {
|
|
|
1135
1294
|
"export_get": _format_export_get,
|
|
1136
1295
|
"export_direct": _format_export_direct,
|
|
1137
1296
|
"builder_summary": _format_builder_summary,
|
|
1297
|
+
"file_upload_local": _format_file_upload_local,
|
|
1138
1298
|
}
|