@josephyan/qingflow-app-user-mcp 0.2.0-beta.20 → 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.
@@ -18,14 +18,19 @@ class ApprovalTools(ToolBase):
18
18
 
19
19
  def register(self, mcp: FastMCP) -> None:
20
20
  @mcp.tool()
21
- def record_comment_add(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
22
- return self.record_comment_add(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
21
+ def record_comment_write(
22
+ profile: str = DEFAULT_PROFILE,
23
+ app_key: str = "",
24
+ record_id: int = 0,
25
+ payload: dict[str, Any] | None = None,
26
+ ) -> dict[str, Any]:
27
+ return self.record_comment_write(profile=profile, app_key=app_key, record_id=record_id, payload=payload or {})
23
28
 
24
29
  @mcp.tool()
25
30
  def record_comment_list(
26
31
  profile: str = DEFAULT_PROFILE,
27
32
  app_key: str = "",
28
- apply_id: int = 0,
33
+ record_id: int = 0,
29
34
  page_size: int = 20,
30
35
  list_type: int | None = None,
31
36
  page_num: int | None = 1,
@@ -33,26 +38,26 @@ class ApprovalTools(ToolBase):
33
38
  return self.record_comment_list(
34
39
  profile=profile,
35
40
  app_key=app_key,
36
- apply_id=apply_id,
41
+ apply_id=record_id,
37
42
  page_size=page_size,
38
43
  list_type=list_type,
39
44
  page_num=page_num,
40
45
  )
41
46
 
42
47
  @mcp.tool()
43
- def record_comment_mention_candidates(
48
+ def record_comment_mentions(
44
49
  profile: str = DEFAULT_PROFILE,
45
50
  app_key: str = "",
46
- apply_id: int = 0,
51
+ record_id: int = 0,
47
52
  page_size: int = 20,
48
53
  page_num: int = 1,
49
54
  list_type: int | None = None,
50
55
  keyword: str | None = None,
51
56
  ) -> dict[str, Any]:
52
- return self.record_comment_mention_candidates(
57
+ return self.record_comment_mentions(
53
58
  profile=profile,
54
59
  app_key=app_key,
55
- apply_id=apply_id,
60
+ record_id=record_id,
56
61
  page_size=page_size,
57
62
  page_num=page_num,
58
63
  list_type=list_type,
@@ -60,84 +65,180 @@ class ApprovalTools(ToolBase):
60
65
  )
61
66
 
62
67
  @mcp.tool()
63
- def record_comment_mark_read(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0) -> dict[str, Any]:
64
- return self.record_comment_mark_read(profile=profile, app_key=app_key, apply_id=apply_id)
65
-
66
- @mcp.tool()
67
- def record_comment_stats(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0) -> dict[str, Any]:
68
- return self.record_comment_stats(profile=profile, app_key=app_key, apply_id=apply_id)
68
+ def record_comment_mark_read(profile: str = DEFAULT_PROFILE, app_key: str = "", record_id: int = 0) -> dict[str, Any]:
69
+ return self.record_comment_mark_read(profile=profile, app_key=app_key, apply_id=record_id)
69
70
 
70
- @mcp.tool(description=self._high_risk_tool_description(operation="approve", target="workflow record"))
71
- def record_approve(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
72
- return self.record_approve(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
73
-
74
- @mcp.tool(description=self._high_risk_tool_description(operation="reject", target="workflow record"))
75
- def record_reject(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
76
- return self.record_reject(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
77
-
78
- @mcp.tool()
79
- def record_rollback_candidates(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, audit_node_id: int = 0) -> dict[str, Any]:
80
- return self.record_rollback_candidates(profile=profile, app_key=app_key, apply_id=apply_id, audit_node_id=audit_node_id)
81
-
82
- @mcp.tool()
83
- def record_rollback(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
84
- return self.record_rollback(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
71
+ @mcp.tool(description=self._high_risk_tool_description(operation="approve", target="workflow task"))
72
+ def task_approve(profile: str = DEFAULT_PROFILE, app_key: str = "", record_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
73
+ return self.task_approve(profile=profile, app_key=app_key, record_id=record_id, payload=payload or {})
85
74
 
86
- @mcp.tool()
87
- def record_transfer(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
88
- return self.record_transfer(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
75
+ @mcp.tool(description=self._high_risk_tool_description(operation="reject", target="workflow task"))
76
+ def task_reject(profile: str = DEFAULT_PROFILE, app_key: str = "", record_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
77
+ return self.task_reject(profile=profile, app_key=app_key, record_id=record_id, payload=payload or {})
89
78
 
90
79
  @mcp.tool()
91
- def record_transfer_candidates(
80
+ def task_rollback_candidates(
92
81
  profile: str = DEFAULT_PROFILE,
93
82
  app_key: str = "",
94
- apply_id: int = 0,
95
- page_size: int = 20,
96
- page_num: int = 1,
97
- audit_node_id: int = 0,
98
- keyword: str | None = None,
83
+ record_id: int = 0,
84
+ workflow_node_id: int = 0,
99
85
  ) -> dict[str, Any]:
100
- return self.record_transfer_candidates(
101
- profile=profile,
102
- app_key=app_key,
103
- apply_id=apply_id,
104
- page_size=page_size,
105
- page_num=page_num,
106
- audit_node_id=audit_node_id,
107
- keyword=keyword,
108
- )
86
+ return self.task_rollback_candidates(profile=profile, app_key=app_key, record_id=record_id, workflow_node_id=workflow_node_id)
109
87
 
110
88
  @mcp.tool()
111
- def record_reassign_get(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0) -> dict[str, Any]:
112
- return self.record_reassign_get(profile=profile, app_key=app_key, apply_id=apply_id)
89
+ def task_rollback(profile: str = DEFAULT_PROFILE, app_key: str = "", record_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
90
+ return self.task_rollback(profile=profile, app_key=app_key, record_id=record_id, payload=payload or {})
113
91
 
114
92
  @mcp.tool()
115
- def record_reassign(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
116
- return self.record_reassign(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
93
+ def task_transfer(profile: str = DEFAULT_PROFILE, app_key: str = "", record_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
94
+ return self.task_transfer(profile=profile, app_key=app_key, record_id=record_id, payload=payload or {})
117
95
 
118
96
  @mcp.tool()
119
- def record_countersign_candidates(
97
+ def task_transfer_candidates(
120
98
  profile: str = DEFAULT_PROFILE,
121
99
  app_key: str = "",
122
- apply_id: int = 0,
100
+ record_id: int = 0,
123
101
  page_size: int = 20,
124
102
  page_num: int = 1,
125
- audit_node_id: int = 0,
126
- search_key: str | None = None,
103
+ workflow_node_id: int = 0,
104
+ keyword: str | None = None,
127
105
  ) -> dict[str, Any]:
128
- return self.record_countersign_candidates(
106
+ return self.task_transfer_candidates(
129
107
  profile=profile,
130
108
  app_key=app_key,
131
- apply_id=apply_id,
109
+ record_id=record_id,
132
110
  page_size=page_size,
133
111
  page_num=page_num,
134
- audit_node_id=audit_node_id,
135
- search_key=search_key,
112
+ workflow_node_id=workflow_node_id,
113
+ keyword=keyword,
136
114
  )
137
115
 
138
- @mcp.tool()
139
- def record_countersign(profile: str = DEFAULT_PROFILE, app_key: str = "", apply_id: int = 0, payload: dict[str, Any] | None = None) -> dict[str, Any]:
140
- return self.record_countersign(profile=profile, app_key=app_key, apply_id=apply_id, payload=payload or {})
116
+ def record_comment_write(self, *, profile: str, app_key: str, record_id: int, payload: dict[str, Any]) -> dict[str, Any]:
117
+ raw = self.record_comment_add(profile=profile, app_key=app_key, apply_id=record_id, payload=payload)
118
+ return self._public_action_response(
119
+ raw,
120
+ action="record_comment_write",
121
+ resource={"app_key": app_key, "record_id": record_id},
122
+ selection={},
123
+ )
124
+
125
+ def record_comment_mentions(
126
+ self,
127
+ *,
128
+ profile: str,
129
+ app_key: str,
130
+ record_id: int,
131
+ page_size: int = 20,
132
+ page_num: int = 1,
133
+ list_type: int | None = None,
134
+ keyword: str | None = None,
135
+ ) -> dict[str, Any]:
136
+ raw = self.record_comment_mention_candidates(
137
+ profile=profile,
138
+ app_key=app_key,
139
+ apply_id=record_id,
140
+ page_size=page_size,
141
+ page_num=page_num,
142
+ list_type=list_type,
143
+ keyword=keyword,
144
+ )
145
+ items = _approval_page_items(raw.get("page"))
146
+ return self._public_page_response(
147
+ raw,
148
+ items=items,
149
+ pagination={
150
+ "page": page_num,
151
+ "page_size": page_size,
152
+ "returned_items": len(items),
153
+ "page_amount": _approval_page_amount(raw.get("page")),
154
+ "reported_total": _approval_page_total(raw.get("page")),
155
+ },
156
+ selection={"app_key": app_key, "record_id": record_id, "list_type": list_type, "keyword": keyword},
157
+ )
158
+
159
+ def task_approve(self, *, profile: str, app_key: str, record_id: int, payload: dict[str, Any]) -> dict[str, Any]:
160
+ raw = self.record_approve(profile=profile, app_key=app_key, apply_id=record_id, payload=payload)
161
+ return self._public_action_response(
162
+ raw,
163
+ action="task_approve",
164
+ resource={"app_key": app_key, "record_id": record_id},
165
+ selection={},
166
+ human_review=True,
167
+ )
168
+
169
+ def task_reject(self, *, profile: str, app_key: str, record_id: int, payload: dict[str, Any]) -> dict[str, Any]:
170
+ raw = self.record_reject(profile=profile, app_key=app_key, apply_id=record_id, payload=payload)
171
+ return self._public_action_response(
172
+ raw,
173
+ action="task_reject",
174
+ resource={"app_key": app_key, "record_id": record_id},
175
+ selection={},
176
+ human_review=True,
177
+ )
178
+
179
+ def task_rollback_candidates(self, *, profile: str, app_key: str, record_id: int, workflow_node_id: int) -> dict[str, Any]:
180
+ raw = self.record_rollback_candidates(profile=profile, app_key=app_key, apply_id=record_id, audit_node_id=workflow_node_id)
181
+ items = _approval_page_items(raw.get("result"))
182
+ return self._public_page_response(
183
+ raw,
184
+ items=items,
185
+ pagination={"returned_items": len(items)},
186
+ selection={"app_key": app_key, "record_id": record_id, "workflow_node_id": workflow_node_id},
187
+ )
188
+
189
+ def task_rollback(self, *, profile: str, app_key: str, record_id: int, payload: dict[str, Any]) -> dict[str, Any]:
190
+ raw = self.record_rollback(profile=profile, app_key=app_key, apply_id=record_id, payload=payload)
191
+ return self._public_action_response(
192
+ raw,
193
+ action="task_rollback",
194
+ resource={"app_key": app_key, "record_id": record_id},
195
+ selection={},
196
+ human_review=True,
197
+ )
198
+
199
+ def task_transfer(self, *, profile: str, app_key: str, record_id: int, payload: dict[str, Any]) -> dict[str, Any]:
200
+ raw = self.record_transfer(profile=profile, app_key=app_key, apply_id=record_id, payload=payload)
201
+ return self._public_action_response(
202
+ raw,
203
+ action="task_transfer",
204
+ resource={"app_key": app_key, "record_id": record_id},
205
+ selection={},
206
+ human_review=True,
207
+ )
208
+
209
+ def task_transfer_candidates(
210
+ self,
211
+ *,
212
+ profile: str,
213
+ app_key: str,
214
+ record_id: int,
215
+ page_size: int = 20,
216
+ page_num: int = 1,
217
+ workflow_node_id: int = 0,
218
+ keyword: str | None = None,
219
+ ) -> dict[str, Any]:
220
+ raw = self.record_transfer_candidates(
221
+ profile=profile,
222
+ app_key=app_key,
223
+ apply_id=record_id,
224
+ page_size=page_size,
225
+ page_num=page_num,
226
+ audit_node_id=workflow_node_id,
227
+ keyword=keyword,
228
+ )
229
+ items = _approval_page_items(raw.get("page"))
230
+ return self._public_page_response(
231
+ raw,
232
+ items=items,
233
+ pagination={
234
+ "page": page_num,
235
+ "page_size": page_size,
236
+ "returned_items": len(items),
237
+ "page_amount": _approval_page_amount(raw.get("page")),
238
+ "reported_total": _approval_page_total(raw.get("page")),
239
+ },
240
+ selection={"app_key": app_key, "record_id": record_id, "workflow_node_id": workflow_node_id, "keyword": keyword},
241
+ )
141
242
 
142
243
  def record_comment_add(self, *, profile: str, app_key: str, apply_id: int, payload: dict[str, Any]) -> dict[str, Any]:
143
244
  self._require_app_and_apply(app_key, apply_id)
@@ -145,7 +246,14 @@ class ApprovalTools(ToolBase):
145
246
 
146
247
  def runner(session_profile, context):
147
248
  result = self.backend.request("POST", context, f"/app/{app_key}/apply/{apply_id}/comment", json_body=payload)
148
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
249
+ return {
250
+ "profile": profile,
251
+ "ws_id": session_profile.selected_ws_id,
252
+ "app_key": app_key,
253
+ "apply_id": apply_id,
254
+ "result": result,
255
+ "request_route": self._request_route_payload(context),
256
+ }
149
257
 
150
258
  return self._run(profile, runner)
151
259
 
@@ -167,9 +275,23 @@ class ApprovalTools(ToolBase):
167
275
  "list_type": list_type,
168
276
  "list_type_label": get_record_list_type_label(list_type),
169
277
  "page": result,
278
+ "request_route": self._request_route_payload(context),
170
279
  }
171
280
 
172
- return self._run(profile, runner)
281
+ raw = self._run(profile, runner)
282
+ items = _approval_page_items(raw.get("page"))
283
+ return self._public_page_response(
284
+ raw,
285
+ items=items,
286
+ pagination={
287
+ "page": page_num,
288
+ "page_size": page_size,
289
+ "returned_items": len(items),
290
+ "page_amount": _approval_page_amount(raw.get("page")),
291
+ "reported_total": _approval_page_total(raw.get("page")),
292
+ },
293
+ selection={"app_key": app_key, "record_id": apply_id, "list_type": list_type},
294
+ )
173
295
 
174
296
  def record_comment_mention_candidates(
175
297
  self,
@@ -199,6 +321,7 @@ class ApprovalTools(ToolBase):
199
321
  "list_type": list_type,
200
322
  "list_type_label": get_record_list_type_label(list_type),
201
323
  "page": result,
324
+ "request_route": self._request_route_payload(context),
202
325
  }
203
326
 
204
327
  return self._run(profile, runner)
@@ -208,16 +331,36 @@ class ApprovalTools(ToolBase):
208
331
 
209
332
  def runner(session_profile, context):
210
333
  result = self.backend.request("POST", context, f"/app/{app_key}/apply/{apply_id}/comment/read")
211
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
334
+ return {
335
+ "profile": profile,
336
+ "ws_id": session_profile.selected_ws_id,
337
+ "app_key": app_key,
338
+ "apply_id": apply_id,
339
+ "result": result,
340
+ "request_route": self._request_route_payload(context),
341
+ }
212
342
 
213
- return self._run(profile, runner)
343
+ raw = self._run(profile, runner)
344
+ return self._public_action_response(
345
+ raw,
346
+ action="record_comment_mark_read",
347
+ resource={"app_key": app_key, "record_id": apply_id},
348
+ selection={},
349
+ )
214
350
 
215
351
  def record_comment_stats(self, *, profile: str, app_key: str, apply_id: int) -> dict[str, Any]:
216
352
  self._require_app_and_apply(app_key, apply_id)
217
353
 
218
354
  def runner(session_profile, context):
219
355
  result = self.backend.request("GET", context, f"/app/{app_key}/apply/{apply_id}/comment/statistic")
220
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
356
+ return {
357
+ "profile": profile,
358
+ "ws_id": session_profile.selected_ws_id,
359
+ "app_key": app_key,
360
+ "apply_id": apply_id,
361
+ "result": result,
362
+ "request_route": self._request_route_payload(context),
363
+ }
221
364
 
222
365
  return self._run(profile, runner)
223
366
 
@@ -273,7 +416,14 @@ class ApprovalTools(ToolBase):
273
416
  f"/app/{app_key}/apply/{apply_id}/revertNode",
274
417
  params={"auditNodeId": audit_node_id},
275
418
  )
276
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
419
+ return {
420
+ "profile": profile,
421
+ "ws_id": session_profile.selected_ws_id,
422
+ "app_key": app_key,
423
+ "apply_id": apply_id,
424
+ "result": result,
425
+ "request_route": self._request_route_payload(context),
426
+ }
277
427
 
278
428
  return self._run(profile, runner)
279
429
 
@@ -284,7 +434,14 @@ class ApprovalTools(ToolBase):
284
434
 
285
435
  def runner(session_profile, context):
286
436
  result = self.backend.request("POST", context, f"/app/{app_key}/apply/{apply_id}/rollback", json_body=body)
287
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
437
+ return {
438
+ "profile": profile,
439
+ "ws_id": session_profile.selected_ws_id,
440
+ "app_key": app_key,
441
+ "apply_id": apply_id,
442
+ "result": result,
443
+ "request_route": self._request_route_payload(context),
444
+ }
288
445
 
289
446
  return self._run(profile, runner)
290
447
 
@@ -295,7 +452,14 @@ class ApprovalTools(ToolBase):
295
452
 
296
453
  def runner(session_profile, context):
297
454
  result = self.backend.request("POST", context, f"/app/{app_key}/apply/{apply_id}/transfer", json_body=body)
298
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
455
+ return {
456
+ "profile": profile,
457
+ "ws_id": session_profile.selected_ws_id,
458
+ "app_key": app_key,
459
+ "apply_id": apply_id,
460
+ "result": result,
461
+ "request_route": self._request_route_payload(context),
462
+ }
299
463
 
300
464
  return self._run(profile, runner)
301
465
 
@@ -319,7 +483,14 @@ class ApprovalTools(ToolBase):
319
483
  if keyword:
320
484
  params["keyword"] = keyword
321
485
  result = self.backend.request("GET", context, f"/app/{app_key}/apply/{apply_id}/transfer/member", params=params)
322
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "page": result}
486
+ return {
487
+ "profile": profile,
488
+ "ws_id": session_profile.selected_ws_id,
489
+ "app_key": app_key,
490
+ "apply_id": apply_id,
491
+ "page": result,
492
+ "request_route": self._request_route_payload(context),
493
+ }
323
494
 
324
495
  return self._run(profile, runner)
325
496
 
@@ -328,7 +499,14 @@ class ApprovalTools(ToolBase):
328
499
 
329
500
  def runner(session_profile, context):
330
501
  result = self.backend.request("GET", context, f"/app/{app_key}/apply/{apply_id}/reassign")
331
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
502
+ return {
503
+ "profile": profile,
504
+ "ws_id": session_profile.selected_ws_id,
505
+ "app_key": app_key,
506
+ "apply_id": apply_id,
507
+ "result": result,
508
+ "request_route": self._request_route_payload(context),
509
+ }
332
510
 
333
511
  return self._run(profile, runner)
334
512
 
@@ -338,7 +516,14 @@ class ApprovalTools(ToolBase):
338
516
 
339
517
  def runner(session_profile, context):
340
518
  result = self.backend.request("POST", context, f"/app/{app_key}/apply/{apply_id}/reassign", json_body=body)
341
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
519
+ return {
520
+ "profile": profile,
521
+ "ws_id": session_profile.selected_ws_id,
522
+ "app_key": app_key,
523
+ "apply_id": apply_id,
524
+ "result": result,
525
+ "request_route": self._request_route_payload(context),
526
+ }
342
527
 
343
528
  return self._run(profile, runner)
344
529
 
@@ -362,7 +547,14 @@ class ApprovalTools(ToolBase):
362
547
  if search_key:
363
548
  params["searchKey"] = search_key
364
549
  result = self.backend.request("GET", context, f"/app/{app_key}/apply/{apply_id}/countersign/member", params=params)
365
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "page": result}
550
+ return {
551
+ "profile": profile,
552
+ "ws_id": session_profile.selected_ws_id,
553
+ "app_key": app_key,
554
+ "apply_id": apply_id,
555
+ "page": result,
556
+ "request_route": self._request_route_payload(context),
557
+ }
366
558
 
367
559
  return self._run(profile, runner)
368
560
 
@@ -373,10 +565,29 @@ class ApprovalTools(ToolBase):
373
565
 
374
566
  def runner(session_profile, context):
375
567
  result = self.backend.request("POST", context, f"/app/{app_key}/apply/{apply_id}/countersign", json_body=body)
376
- return {"profile": profile, "ws_id": session_profile.selected_ws_id, "app_key": app_key, "apply_id": apply_id, "result": result}
568
+ return {
569
+ "profile": profile,
570
+ "ws_id": session_profile.selected_ws_id,
571
+ "app_key": app_key,
572
+ "apply_id": apply_id,
573
+ "result": result,
574
+ "request_route": self._request_route_payload(context),
575
+ }
377
576
 
378
577
  return self._run(profile, runner)
379
578
 
579
+ def _request_route_payload(self, context) -> JSONObject: # type: ignore[no-untyped-def]
580
+ describe_route = getattr(self.backend, "describe_route", None)
581
+ if callable(describe_route):
582
+ payload = describe_route(context)
583
+ if isinstance(payload, dict):
584
+ return payload
585
+ return {
586
+ "base_url": getattr(context, "base_url", None),
587
+ "qf_version": getattr(context, "qf_version", None),
588
+ "qf_version_source": getattr(context, "qf_version_source", None) or ("context" if getattr(context, "qf_version", None) else "unknown"),
589
+ }
590
+
380
591
  def _require_app_and_apply(self, app_key: str, apply_id: int) -> None:
381
592
  if not app_key:
382
593
  raise_tool_error(QingflowApiError.config_error("app_key is required"))
@@ -485,6 +696,47 @@ class ApprovalTools(ToolBase):
485
696
  if payload.get("handSignImageUrl"):
486
697
  raise_tool_error(QingflowApiError.not_supported("NOT_SUPPORTED_IN_V1: handSignImageUrl is not supported"))
487
698
 
699
+ def _public_page_response(
700
+ self,
701
+ raw: dict[str, Any],
702
+ *,
703
+ items: list[dict[str, Any]],
704
+ pagination: dict[str, Any],
705
+ selection: dict[str, Any],
706
+ ) -> dict[str, Any]:
707
+ response = dict(raw)
708
+ response["ok"] = bool(raw.get("ok", True))
709
+ response["warnings"] = []
710
+ response["output_profile"] = "normal"
711
+ response["data"] = {
712
+ "items": items,
713
+ "pagination": pagination,
714
+ "selection": selection,
715
+ }
716
+ return response
717
+
718
+ def _public_action_response(
719
+ self,
720
+ raw: dict[str, Any],
721
+ *,
722
+ action: str,
723
+ resource: dict[str, Any],
724
+ selection: dict[str, Any],
725
+ human_review: bool = False,
726
+ ) -> dict[str, Any]:
727
+ response = dict(raw)
728
+ response["ok"] = bool(raw.get("ok", True))
729
+ response["warnings"] = []
730
+ response["output_profile"] = "normal"
731
+ response["data"] = {
732
+ "action": action,
733
+ "resource": resource,
734
+ "selection": selection,
735
+ "result": raw.get("result"),
736
+ "human_review": human_review,
737
+ }
738
+ return response
739
+
488
740
  def _request_route_payload(self, context) -> JSONObject: # type: ignore[no-untyped-def]
489
741
  describe_route = getattr(self.backend, "describe_route", None)
490
742
  if callable(describe_route):
@@ -496,3 +748,33 @@ class ApprovalTools(ToolBase):
496
748
  "qf_version": context.qf_version,
497
749
  "qf_version_source": getattr(context, "qf_version_source", None) or ("context" if getattr(context, "qf_version", None) else "unknown"),
498
750
  }
751
+
752
+
753
+ def _approval_page_items(payload: Any) -> list[dict[str, Any]]:
754
+ if isinstance(payload, list):
755
+ return [item for item in payload if isinstance(item, dict)]
756
+ if not isinstance(payload, dict):
757
+ return []
758
+ for key in ("list", "items", "rows", "result"):
759
+ value = payload.get(key)
760
+ if isinstance(value, list):
761
+ return [item for item in value if isinstance(item, dict)]
762
+ for container_key in ("data", "page"):
763
+ nested = payload.get(container_key)
764
+ if isinstance(nested, dict):
765
+ nested_items = _approval_page_items(nested)
766
+ if nested_items:
767
+ return nested_items
768
+ return []
769
+
770
+
771
+ def _approval_page_amount(payload: Any) -> Any:
772
+ if isinstance(payload, dict):
773
+ return payload.get("pageAmount", payload.get("page_amount"))
774
+ return None
775
+
776
+
777
+ def _approval_page_total(payload: Any) -> Any:
778
+ if isinstance(payload, dict):
779
+ return payload.get("total", payload.get("count"))
780
+ return None