@josephyan/qingflow-app-user-mcp 0.2.0-beta.2 → 0.2.0-beta.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.
Files changed (32) hide show
  1. package/README.md +12 -2
  2. package/npm/lib/runtime.mjs +37 -0
  3. package/npm/scripts/postinstall.mjs +5 -1
  4. package/package.json +3 -2
  5. package/pyproject.toml +1 -1
  6. package/skills/qingflow-app-user/SKILL.md +230 -0
  7. package/skills/qingflow-app-user/agents/openai.yaml +4 -0
  8. package/skills/qingflow-app-user/references/data-gotchas.md +49 -0
  9. package/skills/qingflow-app-user/references/environments.md +63 -0
  10. package/skills/qingflow-app-user/references/record-patterns.md +110 -0
  11. package/skills/qingflow-app-user/references/workflow-usage.md +26 -0
  12. package/skills/qingflow-record-analysis/SKILL.md +253 -0
  13. package/skills/qingflow-record-analysis/agents/openai.yaml +4 -0
  14. package/skills/qingflow-record-analysis/references/analysis-gotchas.md +141 -0
  15. package/skills/qingflow-record-analysis/references/analysis-patterns.md +113 -0
  16. package/skills/qingflow-record-analysis/references/confidence-reporting.md +92 -0
  17. package/src/qingflow_mcp/__init__.py +1 -1
  18. package/src/qingflow_mcp/builder_facade/models.py +294 -1
  19. package/src/qingflow_mcp/builder_facade/service.py +2727 -235
  20. package/src/qingflow_mcp/server.py +7 -5
  21. package/src/qingflow_mcp/server_app_builder.py +80 -4
  22. package/src/qingflow_mcp/server_app_user.py +8 -182
  23. package/src/qingflow_mcp/solution/compiler/form_compiler.py +1 -1
  24. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +21 -2
  25. package/src/qingflow_mcp/solution/executor.py +34 -7
  26. package/src/qingflow_mcp/tools/ai_builder_tools.py +1038 -30
  27. package/src/qingflow_mcp/tools/app_tools.py +1 -2
  28. package/src/qingflow_mcp/tools/approval_tools.py +357 -75
  29. package/src/qingflow_mcp/tools/directory_tools.py +158 -28
  30. package/src/qingflow_mcp/tools/record_tools.py +1954 -973
  31. package/src/qingflow_mcp/tools/task_tools.py +376 -225
  32. package/src/qingflow_mcp/tools/workflow_tools.py +78 -4
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from mcp.server.fastmcp import FastMCP
4
4
 
5
+ from ..backend_client import BackendRequestContext
5
6
  from ..config import DEFAULT_PROFILE
6
7
  from ..errors import QingflowApiError, raise_tool_error
7
8
  from ..json_types import JSONObject, JSONValue
@@ -70,13 +71,28 @@ class WorkflowTools(ToolBase):
70
71
  def workflow_add_node(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
71
72
  self._require_app_key(app_key)
72
73
  body = self._require_dict(payload)
73
- return self._request(profile, "POST", f"/app/{app_key}/auditNodes", app_key=app_key, json_body=body)
74
+ return self._request_with_post_fallbacks(
75
+ profile=profile,
76
+ app_key=app_key,
77
+ path=f"/app/{app_key}/auditNodes",
78
+ json_body=body,
79
+ alternate_paths=[f"/app/{app_key}/auditNode"],
80
+ )
74
81
 
75
82
  def workflow_update_node(self, *, profile: str, app_key: str, audit_node_id: int, payload: JSONObject) -> JSONObject:
76
83
  self._require_app_key(app_key)
77
84
  self._require_positive("audit_node_id", audit_node_id)
78
85
  body = self._require_dict(payload)
79
- return self._request(profile, "POST", f"/app/{app_key}/auditNodes/{audit_node_id}", app_key=app_key, audit_node_id=audit_node_id, json_body=body, risk_operation="update", risk_target="workflow node configuration")
86
+ return self._request_with_post_fallbacks(
87
+ profile=profile,
88
+ app_key=app_key,
89
+ path=f"/app/{app_key}/auditNodes/{audit_node_id}",
90
+ json_body=body,
91
+ alternate_paths=[f"/app/{app_key}/auditNode/{audit_node_id}"],
92
+ risk_operation="update",
93
+ risk_target="workflow node configuration",
94
+ audit_node_id=audit_node_id,
95
+ )
80
96
 
81
97
  def workflow_delete_node(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
82
98
  self._require_app_key(app_key)
@@ -110,12 +126,26 @@ class WorkflowTools(ToolBase):
110
126
  def workflow_update_global_settings(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
111
127
  self._require_app_key(app_key)
112
128
  body = self._require_dict(payload)
113
- return self._request(profile, "POST", f"/app/{app_key}/workflow/global/setting", app_key=app_key, json_body=body, risk_operation="update", risk_target="workflow global settings")
129
+ return self._request_with_post_fallbacks(
130
+ profile=profile,
131
+ app_key=app_key,
132
+ path=f"/app/{app_key}/workflow/global/setting",
133
+ json_body=body,
134
+ alternate_paths=[],
135
+ risk_operation="update",
136
+ risk_target="workflow global settings",
137
+ )
114
138
 
115
139
  def workflow_publish(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
116
140
  self._require_app_key(app_key)
117
141
  body = self._require_dict(payload)
118
- return self._request(profile, "POST", f"/app/{app_key}/publish", app_key=app_key, json_body=body)
142
+ return self._request_with_post_fallbacks(
143
+ profile=profile,
144
+ app_key=app_key,
145
+ path=f"/app/{app_key}/publish",
146
+ json_body=body,
147
+ alternate_paths=[],
148
+ )
119
149
 
120
150
  def workflow_get_future_nodes(self, *, profile: str, app_key: str, apply_id: int) -> JSONObject:
121
151
  self._require_app_key(app_key)
@@ -229,6 +259,50 @@ class WorkflowTools(ToolBase):
229
259
 
230
260
  return self._run(profile, runner)
231
261
 
262
+ def _request_with_post_fallbacks(
263
+ self,
264
+ *,
265
+ profile: str,
266
+ app_key: str,
267
+ path: str,
268
+ json_body: JSONObject,
269
+ alternate_paths: list[str],
270
+ risk_operation: str | None = None,
271
+ risk_target: str | None = None,
272
+ **extra: JSONValue,
273
+ ) -> JSONObject:
274
+ def runner(session_profile, context):
275
+ attempted_contexts = [context]
276
+ if context.qf_version is not None:
277
+ attempted_contexts.append(
278
+ BackendRequestContext(
279
+ base_url=context.base_url,
280
+ token=context.token,
281
+ ws_id=context.ws_id,
282
+ qf_version=None,
283
+ qf_version_source="workflow_retry_without_qf_version",
284
+ )
285
+ )
286
+ paths = [path, *alternate_paths]
287
+ last_error: QingflowApiError | None = None
288
+ for call_context in attempted_contexts:
289
+ for candidate_path in paths:
290
+ try:
291
+ result = self.backend.request("POST", call_context, candidate_path, json_body=json_body)
292
+ response: JSONObject = {"profile": profile, "ws_id": session_profile.selected_ws_id, "result": result}
293
+ response.update(extra)
294
+ if risk_operation and risk_target:
295
+ return self._attach_human_review_notice(response, operation=risk_operation, target=risk_target)
296
+ return response
297
+ except QingflowApiError as error:
298
+ last_error = error
299
+ if error.http_status != 404:
300
+ raise
301
+ assert last_error is not None
302
+ raise last_error
303
+
304
+ return self._run(profile, runner)
305
+
232
306
  def _require_app_key(self, app_key: str) -> None:
233
307
  if not app_key:
234
308
  raise_tool_error(QingflowApiError.config_error("app_key is required"))