superproductivity-mcp 1.2.6.dev6__tar.gz → 1.2.6.dev8__tar.gz

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 (26) hide show
  1. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/PKG-INFO +1 -1
  2. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/plugin/manifest.json +1 -1
  3. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/plugin/plugin.js +33 -33
  4. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/pyproject.toml +1 -1
  5. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.github/workflows/build.yml +0 -0
  6. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.github/workflows/publish-dev.yml +0 -0
  7. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.github/workflows/publish.yml +0 -0
  8. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.github/workflows/release.yml +0 -0
  9. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.github/workflows/version-gate.yml +0 -0
  10. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.gitignore +0 -0
  11. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.mcp.json.example +0 -0
  12. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/.mise.toml +0 -0
  13. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/LICENSE.txt +0 -0
  14. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/README.md +0 -0
  15. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/build-plugin.sh +0 -0
  16. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/docs/superpowers/plans/2026-04-29-packaging-distribution.md +0 -0
  17. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/docs/superpowers/plans/2026-04-30-gitflow-ci.md +0 -0
  18. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/docs/superpowers/specs/2026-04-29-gitflow-ci-design.md +0 -0
  19. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/docs/superpowers/specs/2026-04-29-packaging-distribution-design.md +0 -0
  20. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/plugin/index.html +0 -0
  21. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/src/superproductivity_mcp/__init__.py +0 -0
  22. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/src/superproductivity_mcp/__main__.py +0 -0
  23. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/src/superproductivity_mcp/server.py +0 -0
  24. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/tests/__init__.py +0 -0
  25. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/tests/test_mcp_logic.py +0 -0
  26. {superproductivity_mcp-1.2.6.dev6 → superproductivity_mcp-1.2.6.dev8}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: superproductivity-mcp
3
- Version: 1.2.6.dev6
3
+ Version: 1.2.6.dev8
4
4
  Summary: Super Productivity MCP server for Claude Desktop integration
5
5
  Project-URL: Homepage, https://github.com/ben-elliot-nice/superproductivity-mcp
6
6
  License-File: LICENSE.txt
@@ -2,7 +2,7 @@
2
2
  "id": "sp-mcp-bridge",
3
3
  "name": "MCP Bridge",
4
4
  "description": "Bridge between Super Productivity and MCP (Model Context Protocol) servers for Claude Desktop integration",
5
- "version": "1.2.6.dev6",
5
+ "version": "1.2.6.dev8",
6
6
  "manifestVersion": 1,
7
7
  "author": "Super Productivity Team",
8
8
  "homepage": "https://github.com/johannesjo/super-productivity",
@@ -485,32 +485,20 @@ class MCPBridgePlugin {
485
485
 
486
486
  case 'addTask':
487
487
  if (command.data.parentId) {
488
- // SP's addTask double-writes when parentId is supplied at creation time.
489
- // Strategy: create WITHOUT parentId, then set parentId via updateTask (hides
490
- // child from root), then add to parent's subTaskIds (produces nested display).
491
488
  const { parentId, title, ...rest } = command.data;
492
- // Strip SP scheduling/tag syntax from title at creation if present, to prevent
493
- // SP from parsing @project/#tag/+parent during addTask. Restore via update.
489
+ // Strip SP scheduling/tag syntax (@, #, +) from title at creation to prevent
490
+ // SP parsing it during addTask. Restore via a separate updateTask on title only.
494
491
  const hasSyntax = /[@#+]/.test(title);
495
492
  const createTitle = hasSyntax
496
493
  ? title.replace(/@\w+/g, '').replace(/#\w+/g, '').replace(/\+\w+/g, '').trim()
497
494
  : title;
498
- // Create without parentId to avoid SP's addTask double-write bug.
499
495
  await this.log(`Creating subtask under ${parentId}`, 'debug');
500
- const subtaskId = await PluginAPI.addTask({ ...rest, title: createTitle });
501
- // Step 1: restore title syntax if stripped, and set parentId via updateTask.
502
- // Setting parentId via updateTask (not addTask) avoids the double-write bug
503
- // while still hiding the child from SP's root list.
504
- const updatePayload = hasSyntax ? { parentId, title } : { parentId };
505
- await PluginAPI.updateTask(subtaskId, updatePayload);
506
- // Step 2: add child to parent's subTaskIds so SP nests it correctly.
507
- const allTasks = await PluginAPI.getTasks();
508
- const parentTask = allTasks.find(t => t.id === parentId);
509
- if (parentTask) {
510
- const existing = parentTask.subTaskIds || [];
511
- if (!existing.includes(subtaskId)) {
512
- await PluginAPI.updateTask(parentId, { subTaskIds: [...existing, subtaskId] });
513
- }
496
+ // Pass parentId to addTask directly SP creates a true subtask at creation
497
+ // time and handles hierarchy correctly. updateTask({ parentId }) creates a
498
+ // root-level cross-reference label instead of a true nested subtask.
499
+ const subtaskId = await PluginAPI.addTask({ ...rest, title: createTitle, parentId });
500
+ if (hasSyntax) {
501
+ await PluginAPI.updateTask(subtaskId, { title });
514
502
  }
515
503
  result = subtaskId;
516
504
  } else {
@@ -537,26 +525,38 @@ class MCPBridgePlugin {
537
525
  const allTasks = await PluginAPI.getTasks();
538
526
  const taskMap = new Map(allTasks.map(t => [t.id, t]));
539
527
  const fixes = [];
528
+ const errors = [];
529
+
530
+ // Build a map of parentId → [childIds that have parentId set but are missing from parent's subTaskIds]
531
+ // Root cause: concurrent create_tasks calls race on subTaskIds updates — last write wins,
532
+ // leaving orphaned children with parentId set but not in parent's subTaskIds array.
533
+ const missingFromParent = new Map(); // parentId → Set of childIds to add
540
534
  for (const task of allTasks) {
541
- // Fix 1: child is in parent's subTaskIds but has no parentId set
542
- for (const childId of (task.subTaskIds || [])) {
543
- const child = taskMap.get(childId);
544
- if (child && !child.parentId) {
545
- await PluginAPI.updateTask(childId, { parentId: task.id });
546
- fixes.push(`set parentId on "${child.title}" → parent "${task.title}"`);
547
- }
548
- }
549
- // Fix 2: child has parentId set but is missing from parent's subTaskIds
550
535
  if (task.parentId) {
551
536
  const parent = taskMap.get(task.parentId);
552
537
  if (parent && !(parent.subTaskIds || []).includes(task.id)) {
553
- const updated = [...(parent.subTaskIds || []), task.id];
554
- await PluginAPI.updateTask(task.parentId, { subTaskIds: updated });
555
- fixes.push(`added "${task.title}" to subTaskIds of parent "${parent.title}"`);
538
+ if (!missingFromParent.has(task.parentId)) {
539
+ missingFromParent.set(task.parentId, new Set());
540
+ }
541
+ missingFromParent.get(task.parentId).add(task.id);
556
542
  }
557
543
  }
558
544
  }
559
- result = { fixed: fixes.length, fixes };
545
+
546
+ // For each parent, do ONE update with ALL missing children at once to avoid race conditions
547
+ for (const [parentId, missingIds] of missingFromParent) {
548
+ const parent = taskMap.get(parentId);
549
+ try {
550
+ const updated = [...(parent.subTaskIds || []), ...missingIds];
551
+ await PluginAPI.updateTask(parentId, { subTaskIds: updated });
552
+ const childTitles = [...missingIds].map(id => taskMap.get(id)?.title || id);
553
+ fixes.push(`added ${missingIds.size} missing children to "${parent.title}": ${childTitles.join(', ')}`);
554
+ } catch (e) {
555
+ errors.push(`failed to fix parent "${parent?.title}": ${e.message}`);
556
+ }
557
+ }
558
+
559
+ result = { fixed: fixes.length, fixes, errors };
560
560
  break;
561
561
  }
562
562
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superproductivity-mcp"
3
- version = "1.2.6.dev6"
3
+ version = "1.2.6.dev8"
4
4
  description = "Super Productivity MCP server for Claude Desktop integration"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"