@qingflow-tech/qingflow-app-builder-mcp 1.0.30 → 1.0.32

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.30
6
+ npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.32
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.30 qingflow-app-builder-mcp
12
+ npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.32 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.30",
3
+ "version": "1.0.32",
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.30"
7
+ version = "1.0.32"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -99,6 +99,8 @@ from .models import (
99
99
  FlowNodePermissionsPatch,
100
100
  )
101
101
 
102
+ BUILDER_PORTAL_LIST_DETAIL_VERIFY_LIMIT = 50
103
+
102
104
 
103
105
  QUESTION_TYPE_TO_FIELD_TYPE: dict[int, str] = {
104
106
  2: FieldType.text.value,
@@ -5998,6 +6000,42 @@ class AiBuilderFacade:
5998
6000
  warnings: list[dict[str, Any]] = []
5999
6001
  items: list[dict[str, Any]] = []
6000
6002
  permission_verified = True
6003
+ if isinstance(raw_items, list) and len(raw_items) > BUILDER_PORTAL_LIST_DETAIL_VERIFY_LIMIT:
6004
+ normalized = _normalize_portal_list_items(raw_items)
6005
+ response = PortalListResponse(items=normalized, total=len(normalized))
6006
+ warnings.append(
6007
+ _warning(
6008
+ "PORTAL_PERMISSION_VERIFICATION_SKIPPED",
6009
+ "builder portal_list returned the discovery list without per-portal edit permission checks because the workspace has too many portals",
6010
+ total_available=len(raw_items),
6011
+ detail_verify_limit=BUILDER_PORTAL_LIST_DETAIL_VERIFY_LIMIT,
6012
+ )
6013
+ )
6014
+ return {
6015
+ "status": "success",
6016
+ "error_code": None,
6017
+ "recoverable": False,
6018
+ "message": "list builder portal discovery items",
6019
+ "normalized_args": {},
6020
+ "missing_fields": [],
6021
+ "allowed_values": {},
6022
+ "details": {
6023
+ "total_available": len(raw_items),
6024
+ "detail_verify_limit": BUILDER_PORTAL_LIST_DETAIL_VERIFY_LIMIT,
6025
+ "permission_filter_applied": False,
6026
+ },
6027
+ "request_id": None,
6028
+ "suggested_next_call": None,
6029
+ "noop": False,
6030
+ "warnings": warnings,
6031
+ "verification": {
6032
+ "portal_list_loaded": True,
6033
+ "portal_permissions_verified": False,
6034
+ "permission_filter_applied": False,
6035
+ },
6036
+ "verified": False,
6037
+ **response.model_dump(mode="json"),
6038
+ }
6001
6039
  for raw_item in raw_items if isinstance(raw_items, list) else []:
6002
6040
  if not isinstance(raw_item, dict):
6003
6041
  continue
@@ -162,9 +162,6 @@ def _handle_action(args: argparse.Namespace, context: CliContext) -> dict:
162
162
  return context.task.task_action_execute(
163
163
  profile=args.profile,
164
164
  task_id=args.task_id,
165
- app_key=args.app_key or "",
166
- record_id=args.record_id or "",
167
- workflow_node_id=int(args.workflow_node_id or 0),
168
165
  action=args.action,
169
166
  payload=payload,
170
167
  fields=fields,
@@ -225,9 +222,6 @@ def _run_task_workbench_task_loop(args: argparse.Namespace, context: CliContext)
225
222
  result = context.task.task_action_execute(
226
223
  profile=args.profile,
227
224
  task_id=args.task_id,
228
- app_key=args.app_key or "",
229
- record_id=args.record_id or "",
230
- workflow_node_id=int(args.workflow_node_id or 0),
231
225
  action=args.action,
232
226
  payload=payload_selection,
233
227
  fields={},
@@ -186,7 +186,7 @@ Use `record_code_block_run` when the user wants to execute a form code-block fie
186
186
  - `task_list` returns task-card summaries keyed by `task_id`.
187
187
  - For detail reads and actions, pass the exact `task_id` from `task_list.data.items[].task_id`.
188
188
  - `task_id` is not a row number, list index, record id, or workflow node id.
189
- - Do not reconstruct task actions from `app_key + record_id + workflow_node_id` unless the user explicitly provides that full locator for troubleshooting.
189
+ - `task_action_execute` only uses `task_id`; do not reconstruct or pass `app_key + record_id + workflow_node_id` for actions.
190
190
  - `task_workflow_log_get(task_id=...)` and `task_associated_report_detail_get(task_id=...)` are also supported for the current todo context.
191
191
  - Use `task_associated_report_detail_get` for associated view or report details.
192
192
  - Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, first choose an accessible view with `app_get`, then call `record_logs_get(app_key, record_id, view_id)` with that same explicit `view_id`.
@@ -191,7 +191,7 @@ Use export only when the user explicitly asks to export/download/generate an Exc
191
191
  - `task_list` returns task-card summaries keyed by `task_id`.
192
192
  - For detail reads and actions, pass the exact `task_id` from `task_list.data.items[].task_id`.
193
193
  - `task_id` is not a row number, list index, record id, or workflow node id.
194
- - Do not reconstruct task actions from `app_key + record_id + workflow_node_id` unless the user explicitly provides that full locator for troubleshooting.
194
+ - `task_action_execute` only uses `task_id`; do not reconstruct or pass `app_key + record_id + workflow_node_id` for actions.
195
195
  - `task_workflow_log_get(task_id=...)` and `task_associated_report_detail_get(task_id=...)` are also supported for the current todo context.
196
196
  - Use `task_associated_report_detail_get` for associated view or report details.
197
197
  - Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, first choose an accessible view with `app_get`, then call `record_logs_get(app_key, record_id, view_id)` with that same explicit `view_id`.
@@ -5343,6 +5343,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
5343
5343
  "returns builder-configurable portal list items only",
5344
5344
  "use this as the builder portal discovery path before portal_get",
5345
5345
  "results are compact list items, not raw dash payloads",
5346
+ "large workspaces may return an unfiltered discovery list with portal_permissions_verified=false instead of probing every portal detail",
5346
5347
  ],
5347
5348
  "minimal_example": {
5348
5349
  "profile": "default",
@@ -1055,7 +1055,7 @@ class ApprovalTools(ToolBase):
1055
1055
  if node_id is None:
1056
1056
  node_payload = dict(payload or {})
1057
1057
  node_id = self._extract_node_id(node_payload)
1058
- delegated = TaskContextTools(self.sessions, self.backend).task_action_execute(
1058
+ delegated = TaskContextTools(self.sessions, self.backend)._task_action_execute_with_locator(
1059
1059
  profile=profile,
1060
1060
  app_key=app_key,
1061
1061
  record_id=record_id,
@@ -126,9 +126,6 @@ class TaskContextTools(ToolBase):
126
126
  return self.task_action_execute(
127
127
  profile=profile,
128
128
  task_id=task_id,
129
- app_key="",
130
- record_id="",
131
- workflow_node_id=0,
132
129
  action=action,
133
130
  payload=payload or {},
134
131
  fields=fields or {},
@@ -317,9 +314,7 @@ class TaskContextTools(ToolBase):
317
314
  self,
318
315
  *,
319
316
  profile: str,
320
- app_key: str,
321
- record_id: Any,
322
- workflow_node_id: int,
317
+ task_id: Any,
323
318
  fields: dict[str, Any] | None = None,
324
319
  ) -> dict[str, Any]:
325
320
  """执行任务相关逻辑。"""
@@ -328,9 +323,7 @@ class TaskContextTools(ToolBase):
328
323
  raise_tool_error(QingflowApiError.config_error("fields is required and must be non-empty for task_save_only"))
329
324
  return self.task_action_execute(
330
325
  profile=profile,
331
- app_key=app_key,
332
- record_id=record_id,
333
- workflow_node_id=workflow_node_id,
326
+ task_id=task_id,
334
327
  action="save_only",
335
328
  payload={},
336
329
  fields=field_updates,
@@ -338,6 +331,34 @@ class TaskContextTools(ToolBase):
338
331
 
339
332
  @tool_cn_name("执行任务动作")
340
333
  def task_action_execute(
334
+ self,
335
+ *,
336
+ profile: str,
337
+ task_id: Any,
338
+ action: str,
339
+ payload: dict[str, Any],
340
+ fields: dict[str, Any] | None = None,
341
+ ) -> dict[str, Any]:
342
+ """执行任务相关逻辑。"""
343
+ if task_id in (None, ""):
344
+ raise_tool_error(
345
+ QingflowApiError.config_error(
346
+ "task_id is required for task_action_execute; get it from task_list.data.items[].task_id"
347
+ )
348
+ )
349
+
350
+ return self._task_action_execute_with_locator(
351
+ profile=profile,
352
+ task_id=task_id,
353
+ app_key="",
354
+ record_id="",
355
+ workflow_node_id=0,
356
+ action=action,
357
+ payload=payload,
358
+ fields=fields,
359
+ )
360
+
361
+ def _task_action_execute_with_locator(
341
362
  self,
342
363
  *,
343
364
  profile: str,
@@ -349,10 +370,8 @@ class TaskContextTools(ToolBase):
349
370
  payload: dict[str, Any],
350
371
  fields: dict[str, Any] | None = None,
351
372
  ) -> dict[str, Any]:
352
- """执行任务相关逻辑。"""
353
373
  if task_id in (None, ""):
354
374
  normalize_positive_id_int(record_id, field_name="record_id")
355
-
356
375
  normalized_action = (action or "").strip().lower()
357
376
  if normalized_action not in {"approve", "reject", "rollback", "transfer", "urge", "save_only"}:
358
377
  raise_tool_error(
@@ -492,7 +511,7 @@ class TaskContextTools(ToolBase):
492
511
  raise_tool_error(QingflowApiError.config_error(message))
493
512
  raise_tool_error(
494
513
  QingflowApiError.config_error(
495
- f"task action '{normalized_action}' is not currently available for app_key='{resolved_app_key}' record_id={record_id_text} workflow_node_id={resolved_workflow_node_id}"
514
+ f"task action '{normalized_action}' is not currently available for task_id='{task_id_text}'"
496
515
  )
497
516
  )
498
517
  feedback_required_for = capabilities.get("action_constraints", {}).get("feedback_required_for") or []