@josephyan/qingflow-app-user-mcp 0.2.0-beta.46 → 0.2.0-beta.47

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@0.2.0-beta.46
6
+ npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.47
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.46 qingflow-app-user-mcp
12
+ npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.47 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": "0.2.0-beta.46",
3
+ "version": "0.2.0-beta.47",
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 = "0.2.0b46"
7
+ version = "0.2.0b47"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,4 +2,4 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.2.0b46"
5
+ __version__ = "0.2.0b47"
@@ -1132,11 +1132,35 @@ def _analyze_headers(
1132
1132
  issues: list[JSONObject] = []
1133
1133
  repair_suggestions: list[str] = []
1134
1134
  if missing:
1135
- issues.append(_issue("MISSING_COLUMNS", f"Missing expected columns: {', '.join(missing)}", severity="error"))
1135
+ issues.append(
1136
+ _issue(
1137
+ "MISSING_COLUMNS",
1138
+ f"Missing expected columns: {', '.join(missing)}",
1139
+ severity="error",
1140
+ repairable=True,
1141
+ repair_code="normalize_headers",
1142
+ )
1143
+ )
1136
1144
  if extra:
1137
- issues.append(_issue("EXTRA_COLUMNS", f"Unexpected columns: {', '.join(extra)}", severity="error"))
1145
+ issues.append(
1146
+ _issue(
1147
+ "EXTRA_COLUMNS",
1148
+ f"Unexpected columns: {', '.join(extra)}",
1149
+ severity="error",
1150
+ repairable=True,
1151
+ repair_code="normalize_headers",
1152
+ )
1153
+ )
1138
1154
  if duplicates:
1139
- issues.append(_issue("DUPLICATE_COLUMNS", f"Duplicate columns: {', '.join(sorted(set(duplicates)))}", severity="error"))
1155
+ issues.append(
1156
+ _issue(
1157
+ "DUPLICATE_COLUMNS",
1158
+ f"Duplicate columns: {', '.join(sorted(set(duplicates)))}",
1159
+ severity="error",
1160
+ repairable=True,
1161
+ repair_code="normalize_headers",
1162
+ )
1163
+ )
1140
1164
  normalized_changes = []
1141
1165
  for text in actual_headers:
1142
1166
  if not text:
@@ -1144,7 +1168,7 @@ def _analyze_headers(
1144
1168
  canonical = allowed_by_key.get(_normalize_header_key(text))
1145
1169
  if canonical and canonical != text:
1146
1170
  normalized_changes.append((text, canonical))
1147
- if normalized_changes:
1171
+ if missing or extra or duplicates or normalized_changes:
1148
1172
  repair_suggestions.append("normalize_headers")
1149
1173
  return {"issues": issues, "repair_suggestions": repair_suggestions}
1150
1174
 
@@ -1194,7 +1218,8 @@ def _sheet_header_map(sheet) -> dict[str, int]: # type: ignore[no-untyped-def]
1194
1218
  def _repair_headers(sheet, expected_columns: list[JSONObject]) -> bool: # type: ignore[no-untyped-def]
1195
1219
  changed = False
1196
1220
  expected_by_key = {_normalize_header_key(item["title"]): item["title"] for item in expected_columns}
1197
- for cell in next(sheet.iter_rows(min_row=1, max_row=1), []):
1221
+ header_cells = list(next(sheet.iter_rows(min_row=1, max_row=1), []))
1222
+ for cell in header_cells:
1198
1223
  text = _normalize_optional_text(cell.value)
1199
1224
  if text is None:
1200
1225
  continue
@@ -1202,6 +1227,19 @@ def _repair_headers(sheet, expected_columns: list[JSONObject]) -> bool: # type:
1202
1227
  if canonical and canonical != text:
1203
1228
  cell.value = canonical
1204
1229
  changed = True
1230
+ if changed:
1231
+ return True
1232
+
1233
+ # Fallback for template-based files where headers were edited into non-canonical
1234
+ # values but column order is still intact. Keep any extra trailing system columns.
1235
+ for index, column in enumerate(expected_columns, start=1):
1236
+ if index > len(header_cells):
1237
+ break
1238
+ expected_title = str(column["title"]).strip()
1239
+ current_title = _normalize_optional_text(header_cells[index - 1].value)
1240
+ if current_title != expected_title:
1241
+ header_cells[index - 1].value = expected_title
1242
+ changed = True
1205
1243
  return changed
1206
1244
 
1207
1245