@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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-user/SKILL.md +183 -113
- package/skills/qingflow-app-user/references/data-gotchas.md +20 -30
- package/skills/qingflow-app-user/references/environments.md +1 -1
- package/skills/qingflow-app-user/references/record-patterns.md +80 -66
- package/skills/qingflow-app-user/references/workflow-usage.md +10 -8
- package/skills/qingflow-record-analysis/SKILL.md +4 -4
- package/skills/qingflow-record-analysis/agents/openai.yaml +1 -1
- package/skills/qingflow-record-analysis/references/analysis-gotchas.md +2 -2
- package/skills/qingflow-record-analysis/references/analysis-patterns.md +2 -2
- package/skills/qingflow-record-analysis/references/confidence-reporting.md +2 -2
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/server.py +6 -6
- package/src/qingflow_mcp/server_app_user.py +8 -183
- package/src/qingflow_mcp/tools/approval_tools.py +357 -75
- package/src/qingflow_mcp/tools/directory_tools.py +158 -28
- package/src/qingflow_mcp/tools/record_tools.py +619 -120
- package/src/qingflow_mcp/tools/task_tools.py +376 -225
|
@@ -35,39 +35,39 @@ class DirectoryTools(ToolBase):
|
|
|
35
35
|
def directory_list_internal_users(
|
|
36
36
|
profile: str = DEFAULT_PROFILE,
|
|
37
37
|
keyword: str | None = None,
|
|
38
|
-
|
|
38
|
+
department_id: int | None = None,
|
|
39
39
|
role_id: int | None = None,
|
|
40
40
|
page_num: int = 1,
|
|
41
41
|
page_size: int = 20,
|
|
42
|
-
|
|
42
|
+
include_disabled: bool = False,
|
|
43
43
|
) -> dict[str, Any]:
|
|
44
44
|
return self.directory_list_internal_users(
|
|
45
45
|
profile=profile,
|
|
46
46
|
keyword=keyword,
|
|
47
|
-
dept_id=
|
|
47
|
+
dept_id=department_id,
|
|
48
48
|
role_id=role_id,
|
|
49
49
|
page_num=page_num,
|
|
50
50
|
page_size=page_size,
|
|
51
|
-
contain_disable=
|
|
51
|
+
contain_disable=include_disabled,
|
|
52
52
|
)
|
|
53
53
|
|
|
54
54
|
@mcp.tool()
|
|
55
55
|
def directory_list_all_internal_users(
|
|
56
56
|
profile: str = DEFAULT_PROFILE,
|
|
57
57
|
keyword: str | None = None,
|
|
58
|
-
|
|
58
|
+
department_id: int | None = None,
|
|
59
59
|
role_id: int | None = None,
|
|
60
60
|
page_size: int = 200,
|
|
61
|
-
|
|
61
|
+
include_disabled: bool = False,
|
|
62
62
|
max_pages: int = 100,
|
|
63
63
|
) -> dict[str, Any]:
|
|
64
64
|
return self.directory_list_all_internal_users(
|
|
65
65
|
profile=profile,
|
|
66
66
|
keyword=keyword,
|
|
67
|
-
dept_id=
|
|
67
|
+
dept_id=department_id,
|
|
68
68
|
role_id=role_id,
|
|
69
69
|
page_size=page_size,
|
|
70
|
-
contain_disable=
|
|
70
|
+
contain_disable=include_disabled,
|
|
71
71
|
max_pages=max_pages,
|
|
72
72
|
)
|
|
73
73
|
|
|
@@ -88,13 +88,13 @@ class DirectoryTools(ToolBase):
|
|
|
88
88
|
@mcp.tool()
|
|
89
89
|
def directory_list_all_departments(
|
|
90
90
|
profile: str = DEFAULT_PROFILE,
|
|
91
|
-
|
|
91
|
+
parent_department_id: int | None = None,
|
|
92
92
|
max_depth: int = 20,
|
|
93
93
|
max_items: int = 2000,
|
|
94
94
|
) -> dict[str, Any]:
|
|
95
95
|
return self.directory_list_all_departments(
|
|
96
96
|
profile=profile,
|
|
97
|
-
parent_dept_id=
|
|
97
|
+
parent_dept_id=parent_department_id,
|
|
98
98
|
max_depth=max_depth,
|
|
99
99
|
max_items=max_items,
|
|
100
100
|
)
|
|
@@ -102,9 +102,9 @@ class DirectoryTools(ToolBase):
|
|
|
102
102
|
@mcp.tool()
|
|
103
103
|
def directory_list_sub_departments(
|
|
104
104
|
profile: str = DEFAULT_PROFILE,
|
|
105
|
-
|
|
105
|
+
parent_department_id: int | None = None,
|
|
106
106
|
) -> dict[str, Any]:
|
|
107
|
-
return self.directory_list_sub_departments(profile=profile, parent_dept_id=
|
|
107
|
+
return self.directory_list_sub_departments(profile=profile, parent_dept_id=parent_department_id)
|
|
108
108
|
|
|
109
109
|
@mcp.tool()
|
|
110
110
|
def directory_list_external_members(
|
|
@@ -150,9 +150,27 @@ class DirectoryTools(ToolBase):
|
|
|
150
150
|
"pageSize": page_size,
|
|
151
151
|
},
|
|
152
152
|
)
|
|
153
|
-
return {
|
|
153
|
+
return {
|
|
154
|
+
"profile": profile,
|
|
155
|
+
"ws_id": session_profile.selected_ws_id,
|
|
156
|
+
"request_route": self._request_route_payload(context),
|
|
157
|
+
"result": result,
|
|
158
|
+
}
|
|
154
159
|
|
|
155
|
-
|
|
160
|
+
raw = self._run(profile, runner)
|
|
161
|
+
items = [item for item in _directory_items(raw.get("result")) if isinstance(item, dict)]
|
|
162
|
+
return self._public_directory_response(
|
|
163
|
+
raw,
|
|
164
|
+
items=items,
|
|
165
|
+
pagination={
|
|
166
|
+
"page": page_num,
|
|
167
|
+
"page_size": page_size,
|
|
168
|
+
"returned_items": len(items),
|
|
169
|
+
"reported_total": _coerce_int(_payload_value(raw.get("result"), "total")),
|
|
170
|
+
"page_amount": _coerce_int(_payload_value(raw.get("result"), "pageAmount")),
|
|
171
|
+
},
|
|
172
|
+
selection={"query": query, "scopes": normalized_scopes},
|
|
173
|
+
)
|
|
156
174
|
|
|
157
175
|
def directory_list_internal_users(
|
|
158
176
|
self,
|
|
@@ -178,9 +196,27 @@ class DirectoryTools(ToolBase):
|
|
|
178
196
|
if role_id is not None:
|
|
179
197
|
params["roleId"] = role_id
|
|
180
198
|
result = self.backend.request("GET", context, "/contact", params=params)
|
|
181
|
-
return {
|
|
199
|
+
return {
|
|
200
|
+
"profile": profile,
|
|
201
|
+
"ws_id": session_profile.selected_ws_id,
|
|
202
|
+
"request_route": self._request_route_payload(context),
|
|
203
|
+
"result": result,
|
|
204
|
+
}
|
|
182
205
|
|
|
183
|
-
|
|
206
|
+
raw = self._run(profile, runner)
|
|
207
|
+
items = [item for item in _directory_items(raw.get("result")) if isinstance(item, dict)]
|
|
208
|
+
return self._public_directory_response(
|
|
209
|
+
raw,
|
|
210
|
+
items=items,
|
|
211
|
+
pagination={
|
|
212
|
+
"page": page_num,
|
|
213
|
+
"page_size": page_size,
|
|
214
|
+
"returned_items": len(items),
|
|
215
|
+
"reported_total": _coerce_int(_payload_value(raw.get("result"), "total")),
|
|
216
|
+
"page_amount": _coerce_int(_payload_value(raw.get("result"), "pageAmount")),
|
|
217
|
+
},
|
|
218
|
+
selection={"keyword": keyword, "department_id": dept_id, "role_id": role_id, "include_disabled": contain_disable},
|
|
219
|
+
)
|
|
184
220
|
|
|
185
221
|
def directory_list_all_internal_users(
|
|
186
222
|
self,
|
|
@@ -237,6 +273,7 @@ class DirectoryTools(ToolBase):
|
|
|
237
273
|
return {
|
|
238
274
|
"profile": profile,
|
|
239
275
|
"ws_id": session_profile.selected_ws_id,
|
|
276
|
+
"request_route": self._request_route_payload(context),
|
|
240
277
|
"items": items,
|
|
241
278
|
"pagination": {
|
|
242
279
|
"page_size": page_size,
|
|
@@ -250,7 +287,13 @@ class DirectoryTools(ToolBase):
|
|
|
250
287
|
},
|
|
251
288
|
}
|
|
252
289
|
|
|
253
|
-
|
|
290
|
+
raw = self._run(profile, runner)
|
|
291
|
+
return self._public_directory_response(
|
|
292
|
+
raw,
|
|
293
|
+
items=[item for item in raw.get("items", []) if isinstance(item, dict)],
|
|
294
|
+
pagination=raw.get("pagination", {}),
|
|
295
|
+
selection={"keyword": keyword, "department_id": dept_id, "role_id": role_id, "include_disabled": contain_disable},
|
|
296
|
+
)
|
|
254
297
|
|
|
255
298
|
def directory_list_internal_departments(
|
|
256
299
|
self,
|
|
@@ -270,9 +313,27 @@ class DirectoryTools(ToolBase):
|
|
|
270
313
|
"/contact/deptByPage",
|
|
271
314
|
params={"keyword": keyword, "pageNum": page_num, "pageSize": page_size},
|
|
272
315
|
)
|
|
273
|
-
return {
|
|
316
|
+
return {
|
|
317
|
+
"profile": profile,
|
|
318
|
+
"ws_id": session_profile.selected_ws_id,
|
|
319
|
+
"request_route": self._request_route_payload(context),
|
|
320
|
+
"page": result,
|
|
321
|
+
}
|
|
274
322
|
|
|
275
|
-
|
|
323
|
+
raw = self._run(profile, runner)
|
|
324
|
+
items = [item for item in _directory_items(raw.get("page")) if isinstance(item, dict)]
|
|
325
|
+
return self._public_directory_response(
|
|
326
|
+
raw,
|
|
327
|
+
items=items,
|
|
328
|
+
pagination={
|
|
329
|
+
"page": page_num,
|
|
330
|
+
"page_size": page_size,
|
|
331
|
+
"returned_items": len(items),
|
|
332
|
+
"reported_total": _coerce_int(_payload_value(raw.get("page"), "total")),
|
|
333
|
+
"page_amount": _coerce_int(_payload_value(raw.get("page"), "pageAmount")),
|
|
334
|
+
},
|
|
335
|
+
selection={"keyword": keyword},
|
|
336
|
+
)
|
|
276
337
|
|
|
277
338
|
def directory_list_all_departments(
|
|
278
339
|
self,
|
|
@@ -304,18 +365,25 @@ class DirectoryTools(ToolBase):
|
|
|
304
365
|
return {
|
|
305
366
|
"profile": profile,
|
|
306
367
|
"ws_id": session_profile.selected_ws_id,
|
|
368
|
+
"request_route": self._request_route_payload(context),
|
|
307
369
|
"items": items,
|
|
308
|
-
"
|
|
309
|
-
"
|
|
370
|
+
"pagination": {
|
|
371
|
+
"root_parent_department_id": parent_dept_id,
|
|
310
372
|
"returned_items": len(items),
|
|
311
|
-
"
|
|
312
|
-
"truncated": truncated,
|
|
373
|
+
"fetched_pages": deepest_depth + 1 if items else 0,
|
|
313
374
|
"is_complete": not truncated,
|
|
375
|
+
"has_more": truncated,
|
|
314
376
|
"max_items": max_items,
|
|
315
377
|
},
|
|
316
378
|
}
|
|
317
379
|
|
|
318
|
-
|
|
380
|
+
raw = self._run(profile, runner)
|
|
381
|
+
return self._public_directory_response(
|
|
382
|
+
raw,
|
|
383
|
+
items=[item for item in raw.get("items", []) if isinstance(item, dict)],
|
|
384
|
+
pagination=raw.get("pagination", {}),
|
|
385
|
+
selection={"parent_department_id": parent_dept_id, "max_depth": max_depth, "max_items": max_items},
|
|
386
|
+
)
|
|
319
387
|
|
|
320
388
|
def directory_list_sub_departments(self, *, profile: str, parent_dept_id: int | None) -> dict[str, Any]:
|
|
321
389
|
def runner(session_profile, context):
|
|
@@ -323,9 +391,21 @@ class DirectoryTools(ToolBase):
|
|
|
323
391
|
if parent_dept_id is not None:
|
|
324
392
|
params["parentDeptId"] = parent_dept_id
|
|
325
393
|
result = self.backend.request("GET", context, "/contact/subDeptList", params=params)
|
|
326
|
-
return {
|
|
394
|
+
return {
|
|
395
|
+
"profile": profile,
|
|
396
|
+
"ws_id": session_profile.selected_ws_id,
|
|
397
|
+
"request_route": self._request_route_payload(context),
|
|
398
|
+
"items": result,
|
|
399
|
+
}
|
|
327
400
|
|
|
328
|
-
|
|
401
|
+
raw = self._run(profile, runner)
|
|
402
|
+
items = [item for item in raw.get("items", []) if isinstance(item, dict)]
|
|
403
|
+
return self._public_directory_response(
|
|
404
|
+
raw,
|
|
405
|
+
items=items,
|
|
406
|
+
pagination={"returned_items": len(items)},
|
|
407
|
+
selection={"parent_department_id": parent_dept_id},
|
|
408
|
+
)
|
|
329
409
|
|
|
330
410
|
def directory_list_external_members(
|
|
331
411
|
self,
|
|
@@ -349,9 +429,59 @@ class DirectoryTools(ToolBase):
|
|
|
349
429
|
if keyword:
|
|
350
430
|
params["keyword"] = keyword
|
|
351
431
|
result = self.backend.request("GET", context, "/external/member/pageList", params=params)
|
|
352
|
-
return {
|
|
432
|
+
return {
|
|
433
|
+
"profile": profile,
|
|
434
|
+
"ws_id": session_profile.selected_ws_id,
|
|
435
|
+
"request_route": self._request_route_payload(context),
|
|
436
|
+
"page": result,
|
|
437
|
+
"simple": simple,
|
|
438
|
+
}
|
|
353
439
|
|
|
354
|
-
|
|
440
|
+
raw = self._run(profile, runner)
|
|
441
|
+
items = [item for item in _directory_items(raw.get("page")) if isinstance(item, dict)]
|
|
442
|
+
return self._public_directory_response(
|
|
443
|
+
raw,
|
|
444
|
+
items=items,
|
|
445
|
+
pagination={
|
|
446
|
+
"page": page_num,
|
|
447
|
+
"page_size": page_size,
|
|
448
|
+
"returned_items": len(items),
|
|
449
|
+
"reported_total": _coerce_int(_payload_value(raw.get("page"), "total")),
|
|
450
|
+
"page_amount": _coerce_int(_payload_value(raw.get("page"), "pageAmount")),
|
|
451
|
+
},
|
|
452
|
+
selection={"keyword": keyword, "simple": simple},
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
def _request_route_payload(self, context) -> dict[str, Any]: # type: ignore[no-untyped-def]
|
|
456
|
+
describe_route = getattr(self.backend, "describe_route", None)
|
|
457
|
+
if callable(describe_route):
|
|
458
|
+
payload = describe_route(context)
|
|
459
|
+
if isinstance(payload, dict):
|
|
460
|
+
return payload
|
|
461
|
+
return {
|
|
462
|
+
"base_url": getattr(context, "base_url", None),
|
|
463
|
+
"qf_version": getattr(context, "qf_version", None),
|
|
464
|
+
"qf_version_source": getattr(context, "qf_version_source", None) or ("context" if getattr(context, "qf_version", None) else "unknown"),
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
def _public_directory_response(
|
|
468
|
+
self,
|
|
469
|
+
raw: dict[str, Any],
|
|
470
|
+
*,
|
|
471
|
+
items: list[dict[str, Any]],
|
|
472
|
+
pagination: dict[str, Any],
|
|
473
|
+
selection: dict[str, Any],
|
|
474
|
+
) -> dict[str, Any]:
|
|
475
|
+
response = dict(raw)
|
|
476
|
+
response["ok"] = bool(raw.get("ok", True))
|
|
477
|
+
response["warnings"] = []
|
|
478
|
+
response["output_profile"] = "normal"
|
|
479
|
+
response["data"] = {
|
|
480
|
+
"items": items,
|
|
481
|
+
"pagination": pagination,
|
|
482
|
+
"selection": selection,
|
|
483
|
+
}
|
|
484
|
+
return response
|
|
355
485
|
|
|
356
486
|
def _walk_department_tree(
|
|
357
487
|
self,
|