@qingflow-tech/qingflow-app-builder-mcp 1.0.1 → 1.0.3

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.
Files changed (42) hide show
  1. package/README.md +2 -2
  2. package/docs/local-agent-install.md +9 -3
  3. package/npm/lib/runtime.mjs +10 -3
  4. package/package.json +1 -1
  5. package/pyproject.toml +1 -1
  6. package/skills/qingflow-app-builder/SKILL.md +88 -184
  7. package/skills/qingflow-app-builder/references/create-app.md +15 -34
  8. package/skills/qingflow-app-builder/references/gotchas.md +3 -3
  9. package/skills/qingflow-app-builder/references/solution-playbooks.md +1 -2
  10. package/skills/qingflow-app-builder/references/tool-selection.md +9 -10
  11. package/src/qingflow_mcp/__init__.py +33 -1
  12. package/src/qingflow_mcp/builder_facade/models.py +14 -4
  13. package/src/qingflow_mcp/builder_facade/service.py +1582 -124
  14. package/src/qingflow_mcp/cli/commands/auth.py +69 -1
  15. package/src/qingflow_mcp/cli/commands/builder.py +4 -3
  16. package/src/qingflow_mcp/cli/commands/record.py +5 -5
  17. package/src/qingflow_mcp/cli/commands/task.py +74 -22
  18. package/src/qingflow_mcp/cli/commands/workspace.py +22 -0
  19. package/src/qingflow_mcp/cli/formatters.py +287 -48
  20. package/src/qingflow_mcp/cli/main.py +6 -1
  21. package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
  22. package/src/qingflow_mcp/config.py +8 -0
  23. package/src/qingflow_mcp/errors.py +2 -2
  24. package/src/qingflow_mcp/id_utils.py +49 -0
  25. package/src/qingflow_mcp/public_surface.py +11 -1
  26. package/src/qingflow_mcp/response_trim.py +380 -9
  27. package/src/qingflow_mcp/server.py +4 -0
  28. package/src/qingflow_mcp/server_app_builder.py +11 -1
  29. package/src/qingflow_mcp/server_app_user.py +24 -0
  30. package/src/qingflow_mcp/session_store.py +69 -15
  31. package/src/qingflow_mcp/solution/compiler/form_compiler.py +2 -2
  32. package/src/qingflow_mcp/solution/executor.py +2 -2
  33. package/src/qingflow_mcp/tools/ai_builder_tools.py +48 -18
  34. package/src/qingflow_mcp/tools/app_tools.py +1 -0
  35. package/src/qingflow_mcp/tools/auth_tools.py +271 -12
  36. package/src/qingflow_mcp/tools/base.py +6 -2
  37. package/src/qingflow_mcp/tools/code_block_tools.py +2 -2
  38. package/src/qingflow_mcp/tools/import_tools.py +36 -2
  39. package/src/qingflow_mcp/tools/record_tools.py +410 -156
  40. package/src/qingflow_mcp/tools/resource_read_tools.py +114 -32
  41. package/src/qingflow_mcp/tools/task_context_tools.py +899 -141
  42. package/src/qingflow_mcp/tools/workspace_tools.py +141 -0
@@ -91,7 +91,7 @@ class CodeBlockTools(RecordTools):
91
91
  def record_code_block_run(
92
92
  profile: str = DEFAULT_PROFILE,
93
93
  app_key: str = "",
94
- record_id: int = 0,
94
+ record_id: str = "",
95
95
  code_block_field: str = "",
96
96
  role: int = 1,
97
97
  workflow_node_id: int | None = None,
@@ -197,7 +197,7 @@ class CodeBlockTools(RecordTools):
197
197
  *,
198
198
  profile: str,
199
199
  app_key: str,
200
- record_id: int,
200
+ record_id: int | str,
201
201
  code_block_field: str,
202
202
  role: int = 1,
203
203
  workflow_node_id: int | None = None,
@@ -783,6 +783,8 @@ class ImportTools(ToolBase):
783
783
  error_code="CONFIG_ERROR",
784
784
  message="record_import_status_get accepts import_id or process_id_str, but not both at the same time",
785
785
  extra={
786
+ "import_id": normalized_import_id,
787
+ "process_id_str": normalized_process_id,
786
788
  "details": {
787
789
  "fix_hint": "Use only one of `import_id` or `process_id_str`. You may pass `app_key` as an optional routing hint for direct method compatibility.",
788
790
  }
@@ -793,6 +795,8 @@ class ImportTools(ToolBase):
793
795
  error_code="CONFIG_ERROR",
794
796
  message="record_import_status_get requires at least one selector: process_id_str, import_id, or app_key",
795
797
  extra={
798
+ "import_id": normalized_import_id,
799
+ "process_id_str": normalized_process_id,
796
800
  "details": {
797
801
  "fix_hint": "Use `process_id_str` or `import_id` for a known import, or use only `app_key` to inspect the latest import in that app.",
798
802
  }
@@ -806,6 +810,9 @@ class ImportTools(ToolBase):
806
810
  if local_job is None and normalized_process_id:
807
811
  matches = [item for item in self._job_store.list() if _normalize_optional_text(item.get("process_id_str")) == normalized_process_id]
808
812
  local_job = matches[0] if len(matches) == 1 else None
813
+ effective_process_id = normalized_process_id
814
+ if effective_process_id is None and isinstance(local_job, dict):
815
+ effective_process_id = _normalize_optional_text(local_job.get("process_id_str"))
809
816
  resolved_app_key = normalized_app_key
810
817
  if not resolved_app_key and isinstance(local_job, dict):
811
818
  resolved_app_key = str(local_job.get("app_key") or "").strip()
@@ -814,6 +821,8 @@ class ImportTools(ToolBase):
814
821
  error_code="CONFIG_ERROR",
815
822
  message="record_import_status_get could not determine app_key from the provided selector",
816
823
  extra={
824
+ "import_id": normalized_import_id,
825
+ "process_id_str": effective_process_id,
817
826
  "details": {
818
827
  "fix_hint": "Use the original `app_key`, or call import status with the latest-import mode: only `app_key`.",
819
828
  }
@@ -832,13 +841,18 @@ class ImportTools(ToolBase):
832
841
  matched_record, matched_by = _match_import_record(
833
842
  records,
834
843
  local_job=local_job,
835
- process_id_str=normalized_process_id,
844
+ import_id=normalized_import_id,
845
+ process_id_str=effective_process_id,
836
846
  )
837
847
  if matched_record is None:
838
848
  return self._failed_status_result(
839
849
  error_code="IMPORT_STATUS_AMBIGUOUS",
840
850
  message="could not uniquely resolve an import record from the provided identifiers",
841
- extra={"matched_by": matched_by},
851
+ extra={
852
+ "import_id": normalized_import_id,
853
+ "process_id_str": effective_process_id,
854
+ "matched_by": matched_by,
855
+ },
842
856
  )
843
857
  normalized_process = _normalize_optional_text(
844
858
  matched_record.get("processIdStr") or matched_record.get("processId") or matched_record.get("process_id_str")
@@ -2118,6 +2132,7 @@ def _match_import_record(
2118
2132
  records: list[JSONObject],
2119
2133
  *,
2120
2134
  local_job: dict[str, Any] | None,
2135
+ import_id: str | None,
2121
2136
  process_id_str: str | None,
2122
2137
  ) -> tuple[JSONObject | None, str | None]:
2123
2138
  if process_id_str:
@@ -2130,6 +2145,16 @@ def _match_import_record(
2130
2145
  return exact[0], "process_id_str"
2131
2146
  if len(exact) > 1:
2132
2147
  return None, "process_id_str"
2148
+ if import_id:
2149
+ exact = [
2150
+ item
2151
+ for item in records
2152
+ if import_id in _extract_import_record_ids(item)
2153
+ ]
2154
+ if len(exact) == 1:
2155
+ return exact[0], "import_id"
2156
+ if len(exact) > 1:
2157
+ return None, "import_id"
2133
2158
  if isinstance(local_job, dict):
2134
2159
  source_file_name = _normalize_optional_text(local_job.get("source_file_name"))
2135
2160
  started_at = _parse_utc(local_job.get("started_at"))
@@ -2160,6 +2185,15 @@ def _match_import_record(
2160
2185
  return None, None
2161
2186
 
2162
2187
 
2188
+ def _extract_import_record_ids(record: JSONObject) -> set[str]:
2189
+ identifiers: set[str] = set()
2190
+ for key in ("importId", "import_id", "dataImportId", "data_import_id"):
2191
+ normalized = _normalize_optional_text(record.get(key))
2192
+ if normalized:
2193
+ identifiers.add(normalized)
2194
+ return identifiers
2195
+
2196
+
2163
2197
  def _parse_utc(value: Any) -> datetime | None:
2164
2198
  text = _normalize_optional_text(value)
2165
2199
  if text is None: