@josephyan/qingflow-app-user-mcp 1.1.18 → 1.1.19

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 @josephyan/qingflow-app-user-mcp@1.1.18
6
+ npm install @josephyan/qingflow-app-user-mcp@1.1.19
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-app-user-mcp@1.1.18 qingflow-app-user-mcp
12
+ npx -y -p @josephyan/qingflow-app-user-mcp@1.1.19 qingflow-app-user-mcp
13
13
  ```
14
14
 
15
15
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-app-user-mcp",
3
- "version": "1.1.18",
3
+ "version": "1.1.19",
4
4
  "description": "Operational end-user MCP for Qingflow records, tasks, comments, and directory 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.1.18"
7
+ version = "1.1.19"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -3155,7 +3155,7 @@ class AiBuilderFacade:
3155
3155
  return expanded, issues, results
3156
3156
 
3157
3157
  def app_custom_buttons_apply(self, *, profile: str, request: CustomButtonsApplyRequest) -> JSONObject:
3158
- normalized_args = request.model_dump(mode="json")
3158
+ normalized_args = request.model_dump(mode="json", exclude_none=True, exclude_defaults=True)
3159
3159
  app_key = request.app_key
3160
3160
  permission_outcomes: list[PermissionCheckOutcome] = []
3161
3161
  button_write_intent = bool(request.upsert_buttons or request.patch_buttons or request.remove_buttons)
@@ -3239,7 +3239,10 @@ class AiBuilderFacade:
3239
3239
  )
3240
3240
  )
3241
3241
  upsert_buttons.extend(expanded_buttons)
3242
- normalized_args["upsert_buttons"] = [patch.model_dump(mode="json") for patch in upsert_buttons]
3242
+ normalized_args["upsert_buttons"] = [
3243
+ patch.model_dump(mode="json", exclude_none=True, exclude_defaults=True)
3244
+ for patch in upsert_buttons
3245
+ ]
3243
3246
  normalized_args["patch_results"] = patch_results
3244
3247
 
3245
3248
  compiled_add_data_configs, add_data_issues = self._compile_custom_button_semantic_add_data_configs(
@@ -10261,8 +10264,14 @@ class AiBuilderFacade:
10261
10264
  return CustomButtonsApplyRequest.model_validate(
10262
10265
  {
10263
10266
  "app_key": app_key,
10264
- "upsert_buttons": [item.model_dump(mode="json") for item in upsert_buttons],
10265
- "view_configs": [item.model_dump(mode="json") for item in view_configs],
10267
+ "upsert_buttons": [
10268
+ item.model_dump(mode="json", exclude_none=True, exclude_defaults=True)
10269
+ for item in upsert_buttons
10270
+ ],
10271
+ "view_configs": [
10272
+ item.model_dump(mode="json", exclude_none=True, exclude_defaults=True)
10273
+ for item in view_configs
10274
+ ],
10266
10275
  }
10267
10276
  ), []
10268
10277
  except Exception as error:
@@ -220,9 +220,10 @@ class AiBuilderTools(ToolBase):
220
220
  wrapped["error"] = error
221
221
  app_results.append(wrapped)
222
222
 
223
+ partial = sum(1 for item in app_results if str(item.get("status") or "") == "partial_success")
223
224
  succeeded = sum(1 for item in app_results if str(item.get("status") or "") in {"success", "partial_success"})
224
225
  failed = len(app_results) - succeeded
225
- status = "success" if failed == 0 else "failed" if succeeded == 0 else "partial_success"
226
+ status = "failed" if succeeded == 0 else "partial_success" if failed > 0 or partial > 0 else "success"
226
227
  write_executed = any(
227
228
  isinstance(item.get("result"), dict) and bool(item["result"].get("write_executed"))
228
229
  for item in app_results
@@ -230,7 +231,7 @@ class AiBuilderTools(ToolBase):
230
231
  payload: JSONObject = {
231
232
  "status": status,
232
233
  "error_code": None if status == "success" else f"{tool_name.upper()}_BATCH_PARTIAL" if succeeded else f"{tool_name.upper()}_BATCH_FAILED",
233
- "recoverable": failed > 0,
234
+ "recoverable": failed > 0 or partial > 0,
234
235
  "message": (
235
236
  f"applied {tool_name} to {succeeded}/{len(app_results)} apps"
236
237
  if status != "success"
@@ -240,12 +241,13 @@ class AiBuilderTools(ToolBase):
240
241
  "apps": app_results,
241
242
  "errors": errors,
242
243
  "verification": {
243
- "batch_verified": failed == 0,
244
+ "batch_verified": failed == 0 and partial == 0,
244
245
  "succeeded": succeeded,
246
+ "partial": partial,
245
247
  "failed": failed,
246
248
  },
247
249
  "write_executed": write_executed,
248
- "write_succeeded": write_executed and failed == 0,
250
+ "write_succeeded": write_executed and failed == 0 and partial == 0,
249
251
  "safe_to_retry": not write_executed,
250
252
  }
251
253
  return _attach_builder_apply_envelope(tool_name, payload)
@@ -295,18 +297,20 @@ class AiBuilderTools(ToolBase):
295
297
  app_item["error"] = error
296
298
  errors.append(error)
297
299
  apps.append(app_item)
300
+ partial = sum(1 for item in apps if str(item.get("status") or "") == "partial_success")
298
301
  succeeded = sum(1 for item in apps if str(item.get("status") or "") in {"success", "partial_success"})
299
302
  failed = len(apps) - succeeded
303
+ status = "failed" if succeeded == 0 else "partial_success" if failed > 0 or partial > 0 else "success"
300
304
  return {
301
- "status": "success" if failed == 0 else "failed" if succeeded == 0 else "partial_success",
302
- "error_code": None if failed == 0 else f"{tool_name.upper()}_BATCH_PARTIAL" if succeeded else f"{tool_name.upper()}_BATCH_FAILED",
303
- "recoverable": failed > 0,
304
- "message": f"read {tool_name} for {succeeded}/{len(apps)} apps" if failed else f"read {tool_name} for {succeeded} apps",
305
+ "status": status,
306
+ "error_code": None if status == "success" else f"{tool_name.upper()}_BATCH_PARTIAL" if succeeded else f"{tool_name.upper()}_BATCH_FAILED",
307
+ "recoverable": failed > 0 or partial > 0,
308
+ "message": f"read {tool_name} for {succeeded}/{len(apps)} apps" if status != "success" else f"read {tool_name} for {succeeded} apps",
305
309
  "normalized_args": {"app_keys": normalized_keys},
306
310
  "app_keys": normalized_keys,
307
311
  "apps": apps,
308
312
  "errors": errors,
309
- "verification": {"batch_verified": failed == 0, "succeeded": succeeded, "failed": failed},
313
+ "verification": {"batch_verified": failed == 0 and partial == 0, "succeeded": succeeded, "partial": partial, "failed": failed},
310
314
  }
311
315
 
312
316
  def register(self, mcp) -> None: