@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 CHANGED
@@ -3,13 +3,13 @@
3
3
  Install:
4
4
 
5
5
  ```bash
6
- npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.19
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.19 qingflow-app-builder-mcp
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qingflow-tech/qingflow-app-builder-mcp",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
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.19"
7
+ version = "1.0.21"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -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="generic")
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="generic")
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="generic")
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="generic")
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="generic")
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
  }