mcp-ticketer 0.12.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

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.

Potentially problematic release.


This version of mcp-ticketer might be problematic. Click here for more details.

Files changed (87) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +1 -1
  3. mcp_ticketer/adapters/aitrackdown.py +385 -6
  4. mcp_ticketer/adapters/asana/adapter.py +108 -0
  5. mcp_ticketer/adapters/asana/mappers.py +14 -0
  6. mcp_ticketer/adapters/github.py +525 -11
  7. mcp_ticketer/adapters/hybrid.py +47 -5
  8. mcp_ticketer/adapters/jira.py +521 -0
  9. mcp_ticketer/adapters/linear/adapter.py +1784 -101
  10. mcp_ticketer/adapters/linear/client.py +85 -3
  11. mcp_ticketer/adapters/linear/mappers.py +96 -8
  12. mcp_ticketer/adapters/linear/queries.py +168 -1
  13. mcp_ticketer/adapters/linear/types.py +80 -4
  14. mcp_ticketer/analysis/__init__.py +56 -0
  15. mcp_ticketer/analysis/dependency_graph.py +255 -0
  16. mcp_ticketer/analysis/health_assessment.py +304 -0
  17. mcp_ticketer/analysis/orphaned.py +218 -0
  18. mcp_ticketer/analysis/project_status.py +594 -0
  19. mcp_ticketer/analysis/similarity.py +224 -0
  20. mcp_ticketer/analysis/staleness.py +266 -0
  21. mcp_ticketer/automation/__init__.py +11 -0
  22. mcp_ticketer/automation/project_updates.py +378 -0
  23. mcp_ticketer/cli/adapter_diagnostics.py +3 -1
  24. mcp_ticketer/cli/auggie_configure.py +17 -5
  25. mcp_ticketer/cli/codex_configure.py +97 -61
  26. mcp_ticketer/cli/configure.py +851 -103
  27. mcp_ticketer/cli/cursor_configure.py +314 -0
  28. mcp_ticketer/cli/diagnostics.py +13 -12
  29. mcp_ticketer/cli/discover.py +5 -0
  30. mcp_ticketer/cli/gemini_configure.py +17 -5
  31. mcp_ticketer/cli/init_command.py +880 -0
  32. mcp_ticketer/cli/instruction_commands.py +6 -0
  33. mcp_ticketer/cli/main.py +233 -3151
  34. mcp_ticketer/cli/mcp_configure.py +672 -98
  35. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  36. mcp_ticketer/cli/platform_detection.py +77 -12
  37. mcp_ticketer/cli/platform_installer.py +536 -0
  38. mcp_ticketer/cli/project_update_commands.py +350 -0
  39. mcp_ticketer/cli/setup_command.py +639 -0
  40. mcp_ticketer/cli/simple_health.py +12 -10
  41. mcp_ticketer/cli/ticket_commands.py +264 -24
  42. mcp_ticketer/core/__init__.py +28 -6
  43. mcp_ticketer/core/adapter.py +166 -1
  44. mcp_ticketer/core/config.py +21 -21
  45. mcp_ticketer/core/exceptions.py +7 -1
  46. mcp_ticketer/core/label_manager.py +732 -0
  47. mcp_ticketer/core/mappers.py +31 -19
  48. mcp_ticketer/core/models.py +135 -0
  49. mcp_ticketer/core/onepassword_secrets.py +1 -1
  50. mcp_ticketer/core/priority_matcher.py +463 -0
  51. mcp_ticketer/core/project_config.py +132 -14
  52. mcp_ticketer/core/session_state.py +171 -0
  53. mcp_ticketer/core/state_matcher.py +592 -0
  54. mcp_ticketer/core/url_parser.py +425 -0
  55. mcp_ticketer/core/validators.py +69 -0
  56. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  57. mcp_ticketer/mcp/server/main.py +106 -25
  58. mcp_ticketer/mcp/server/routing.py +655 -0
  59. mcp_ticketer/mcp/server/server_sdk.py +58 -0
  60. mcp_ticketer/mcp/server/tools/__init__.py +31 -12
  61. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  62. mcp_ticketer/mcp/server/tools/attachment_tools.py +6 -8
  63. mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
  64. mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
  65. mcp_ticketer/mcp/server/tools/config_tools.py +1184 -136
  66. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  67. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
  68. mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
  69. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  70. mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
  71. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  72. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  73. mcp_ticketer/mcp/server/tools/search_tools.py +180 -97
  74. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  75. mcp_ticketer/mcp/server/tools/ticket_tools.py +1070 -123
  76. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
  77. mcp_ticketer/queue/worker.py +1 -1
  78. mcp_ticketer/utils/__init__.py +5 -0
  79. mcp_ticketer/utils/token_utils.py +246 -0
  80. mcp_ticketer-2.0.1.dist-info/METADATA +1366 -0
  81. mcp_ticketer-2.0.1.dist-info/RECORD +122 -0
  82. mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
  83. mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
  84. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/WHEEL +0 -0
  85. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/entry_points.txt +0 -0
  86. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/licenses/LICENSE +0 -0
  87. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/top_level.txt +0 -0
@@ -59,6 +59,7 @@ class AsanaAdapter(BaseAdapter[Task]):
59
59
  """Initialize Asana adapter.
60
60
 
61
61
  Args:
62
+ ----
62
63
  config: Configuration with:
63
64
  - api_key: Asana Personal Access Token (or ASANA_PAT env var)
64
65
  - workspace: Asana workspace name (optional, for resolution)
@@ -68,6 +69,7 @@ class AsanaAdapter(BaseAdapter[Task]):
68
69
  - max_retries: Maximum retry attempts (default: 3)
69
70
 
70
71
  Raises:
72
+ ------
71
73
  ValueError: If required configuration is missing
72
74
 
73
75
  """
@@ -120,6 +122,7 @@ class AsanaAdapter(BaseAdapter[Task]):
120
122
  """Validate Asana API credentials.
121
123
 
122
124
  Returns:
125
+ -------
123
126
  Tuple of (is_valid, error_message)
124
127
 
125
128
  """
@@ -244,9 +247,11 @@ class AsanaAdapter(BaseAdapter[Task]):
244
247
  """Load custom fields configured for a specific project.
245
248
 
246
249
  Args:
250
+ ----
247
251
  project_gid: Project GID to load custom fields for
248
252
 
249
253
  Returns:
254
+ -------
250
255
  Dictionary mapping field name (lowercase) to field data
251
256
 
252
257
  """
@@ -277,9 +282,11 @@ class AsanaAdapter(BaseAdapter[Task]):
277
282
  """Get custom fields for a project, loading if not cached.
278
283
 
279
284
  Args:
285
+ ----
280
286
  project_gid: Project GID
281
287
 
282
288
  Returns:
289
+ -------
283
290
  Dictionary mapping field name (lowercase) to field data
284
291
 
285
292
  """
@@ -295,10 +302,12 @@ class AsanaAdapter(BaseAdapter[Task]):
295
302
  """Map TicketState to Asana Status custom field option.
296
303
 
297
304
  Args:
305
+ ----
298
306
  state: The TicketState to map
299
307
  status_field: The Status custom field data with enum_options
300
308
 
301
309
  Returns:
310
+ -------
302
311
  Matching enum option or None
303
312
 
304
313
  """
@@ -339,6 +348,7 @@ class AsanaAdapter(BaseAdapter[Task]):
339
348
  We return a mapping to "true"/"false" strings for compatibility.
340
349
 
341
350
  Returns:
351
+ -------
342
352
  Dictionary mapping TicketState to completion status string
343
353
 
344
354
  """
@@ -357,9 +367,11 @@ class AsanaAdapter(BaseAdapter[Task]):
357
367
  """Resolve project identifier (name or GID) to GID.
358
368
 
359
369
  Args:
370
+ ----
360
371
  project_identifier: Project name or GID
361
372
 
362
373
  Returns:
374
+ -------
363
375
  Project GID or None if not found
364
376
 
365
377
  """
@@ -392,9 +404,11 @@ class AsanaAdapter(BaseAdapter[Task]):
392
404
  """Resolve user identifier (email, name, or GID) to GID.
393
405
 
394
406
  Args:
407
+ ----
395
408
  user_identifier: User email, name, or GID
396
409
 
397
410
  Returns:
411
+ -------
398
412
  User GID or None if not found
399
413
 
400
414
  """
@@ -432,12 +446,15 @@ class AsanaAdapter(BaseAdapter[Task]):
432
446
  """Create a new Asana project or task.
433
447
 
434
448
  Args:
449
+ ----
435
450
  ticket: Epic or Task to create
436
451
 
437
452
  Returns:
453
+ -------
438
454
  Created ticket with ID populated
439
455
 
440
456
  Raises:
457
+ ------
441
458
  ValueError: If creation fails
442
459
 
443
460
  """
@@ -460,9 +477,11 @@ class AsanaAdapter(BaseAdapter[Task]):
460
477
  """Create an Asana project from an Epic.
461
478
 
462
479
  Args:
480
+ ----
463
481
  epic: Epic to create
464
482
 
465
483
  Returns:
484
+ -------
466
485
  Created epic with Asana metadata
467
486
 
468
487
  """
@@ -491,9 +510,11 @@ class AsanaAdapter(BaseAdapter[Task]):
491
510
  subtask (child of another task) when task.parent_issue is provided.
492
511
 
493
512
  Args:
513
+ ----
494
514
  task: Task to create
495
515
 
496
516
  Returns:
517
+ -------
497
518
  Created task with Asana metadata
498
519
 
499
520
  """
@@ -564,6 +585,7 @@ class AsanaAdapter(BaseAdapter[Task]):
564
585
  """Add tags to an Asana task.
565
586
 
566
587
  Args:
588
+ ----
567
589
  task_gid: Task GID
568
590
  tags: List of tag names to add
569
591
 
@@ -602,9 +624,11 @@ class AsanaAdapter(BaseAdapter[Task]):
602
624
  """Read an Asana task by GID.
603
625
 
604
626
  Args:
627
+ ----
605
628
  ticket_id: Asana task GID
606
629
 
607
630
  Returns:
631
+ -------
608
632
  Task if found, None otherwise
609
633
 
610
634
  """
@@ -632,10 +656,12 @@ class AsanaAdapter(BaseAdapter[Task]):
632
656
  """Update an Asana task.
633
657
 
634
658
  Args:
659
+ ----
635
660
  ticket_id: Task GID
636
661
  updates: Dictionary of fields to update
637
662
 
638
663
  Returns:
664
+ -------
639
665
  Updated task or None if not found
640
666
 
641
667
  """
@@ -776,9 +802,11 @@ class AsanaAdapter(BaseAdapter[Task]):
776
802
  """Delete an Asana task.
777
803
 
778
804
  Args:
805
+ ----
779
806
  ticket_id: Task GID
780
807
 
781
808
  Returns:
809
+ -------
782
810
  True if successfully deleted
783
811
 
784
812
  """
@@ -795,11 +823,13 @@ class AsanaAdapter(BaseAdapter[Task]):
795
823
  """List Asana tasks with optional filtering.
796
824
 
797
825
  Args:
826
+ ----
798
827
  limit: Maximum number of tasks to return
799
828
  offset: Number of tasks to skip (Note: Asana uses offset tokens)
800
829
  filters: Optional filters (state, assignee, project, etc.)
801
830
 
802
831
  Returns:
832
+ -------
803
833
  List of tasks matching the criteria
804
834
 
805
835
  """
@@ -896,9 +926,11 @@ class AsanaAdapter(BaseAdapter[Task]):
896
926
  """Search Asana tasks using filters.
897
927
 
898
928
  Args:
929
+ ----
899
930
  query: Search query with filters
900
931
 
901
932
  Returns:
933
+ -------
902
934
  List of tasks matching the search criteria
903
935
 
904
936
  """
@@ -936,10 +968,12 @@ class AsanaAdapter(BaseAdapter[Task]):
936
968
  """Transition task to new state.
937
969
 
938
970
  Args:
971
+ ----
939
972
  ticket_id: Task GID
940
973
  target_state: Target state
941
974
 
942
975
  Returns:
976
+ -------
943
977
  Updated task or None if failed
944
978
 
945
979
  """
@@ -949,12 +983,15 @@ class AsanaAdapter(BaseAdapter[Task]):
949
983
  """Add a comment to an Asana task (as a story).
950
984
 
951
985
  Args:
986
+ ----
952
987
  comment: Comment to add
953
988
 
954
989
  Returns:
990
+ -------
955
991
  Created comment with ID
956
992
 
957
993
  Raises:
994
+ ------
958
995
  ValueError: If comment creation fails
959
996
 
960
997
  """
@@ -984,11 +1021,13 @@ class AsanaAdapter(BaseAdapter[Task]):
984
1021
  Filters stories to only return comment type (not system events).
985
1022
 
986
1023
  Args:
1024
+ ----
987
1025
  ticket_id: Task GID
988
1026
  limit: Maximum number of comments to return
989
1027
  offset: Number of comments to skip
990
1028
 
991
1029
  Returns:
1030
+ -------
992
1031
  List of comments for the task
993
1032
 
994
1033
  """
@@ -1019,11 +1058,13 @@ class AsanaAdapter(BaseAdapter[Task]):
1019
1058
  """Create an Asana project (Epic).
1020
1059
 
1021
1060
  Args:
1061
+ ----
1022
1062
  title: Epic title
1023
1063
  description: Epic description
1024
1064
  **kwargs: Additional fields
1025
1065
 
1026
1066
  Returns:
1067
+ -------
1027
1068
  Created epic or None if failed
1028
1069
 
1029
1070
  """
@@ -1041,9 +1082,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1041
1082
  """Get an Asana project (Epic) by GID.
1042
1083
 
1043
1084
  Args:
1085
+ ----
1044
1086
  epic_id: Project GID
1045
1087
 
1046
1088
  Returns:
1089
+ -------
1047
1090
  Epic if found, None otherwise
1048
1091
 
1049
1092
  """
@@ -1065,10 +1108,12 @@ class AsanaAdapter(BaseAdapter[Task]):
1065
1108
  """Update an Asana project (Epic).
1066
1109
 
1067
1110
  Args:
1111
+ ----
1068
1112
  epic_id: Project GID
1069
1113
  updates: Dictionary of fields to update
1070
1114
 
1071
1115
  Returns:
1116
+ -------
1072
1117
  Updated epic or None if failed
1073
1118
 
1074
1119
  """
@@ -1108,9 +1153,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1108
1153
  """List all Asana projects (Epics).
1109
1154
 
1110
1155
  Args:
1156
+ ----
1111
1157
  **kwargs: Optional filter parameters
1112
1158
 
1113
1159
  Returns:
1160
+ -------
1114
1161
  List of epics
1115
1162
 
1116
1163
  """
@@ -1143,13 +1190,65 @@ class AsanaAdapter(BaseAdapter[Task]):
1143
1190
  logger.error(f"Failed to list projects: {e}")
1144
1191
  return []
1145
1192
 
1193
+ async def delete_epic(self, epic_id: str) -> bool:
1194
+ """Delete an Asana project (Epic).
1195
+
1196
+ Args:
1197
+ ----
1198
+ epic_id: Project GID to delete
1199
+
1200
+ Returns:
1201
+ -------
1202
+ True if successfully deleted, False otherwise
1203
+
1204
+ Raises:
1205
+ ------
1206
+ ValueError: If credentials are invalid or GID format is invalid
1207
+
1208
+ """
1209
+ # Validate credentials
1210
+ is_valid, error_message = self.validate_credentials()
1211
+ if not is_valid:
1212
+ raise ValueError(error_message)
1213
+
1214
+ # Validate GID format (should be numeric)
1215
+ if not epic_id or not epic_id.isdigit():
1216
+ raise ValueError(
1217
+ f"Invalid project GID '{epic_id}'. Asana project GIDs must be numeric."
1218
+ )
1219
+
1220
+ try:
1221
+ # Delete project using REST API
1222
+ await self.client.delete(f"/projects/{epic_id}")
1223
+ logger.info(f"Successfully deleted project {epic_id}")
1224
+ return True
1225
+
1226
+ except Exception as e:
1227
+ # Check if it's a 404 (not found) - return False
1228
+ if "404" in str(e) or "Not Found" in str(e):
1229
+ logger.warning(f"Project {epic_id} not found")
1230
+ return False
1231
+
1232
+ # Check for permissions errors
1233
+ if "403" in str(e) or "Forbidden" in str(e):
1234
+ logger.error(f"Permission denied to delete project {epic_id}")
1235
+ raise ValueError(
1236
+ f"Permission denied: You don't have permission to delete project {epic_id}"
1237
+ ) from e
1238
+
1239
+ # Other errors - log and raise
1240
+ logger.error(f"Failed to delete project {epic_id}: {e}")
1241
+ raise ValueError(f"Failed to delete project: {e}") from e
1242
+
1146
1243
  async def list_issues_by_epic(self, epic_id: str) -> builtins.list[Task]:
1147
1244
  """List all tasks in a project (Epic).
1148
1245
 
1149
1246
  Args:
1247
+ ----
1150
1248
  epic_id: Project GID
1151
1249
 
1152
1250
  Returns:
1251
+ -------
1153
1252
  List of tasks in the project
1154
1253
 
1155
1254
  """
@@ -1161,9 +1260,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1161
1260
  """List all subtasks of a task (Issue).
1162
1261
 
1163
1262
  Args:
1263
+ ----
1164
1264
  issue_id: Parent task GID
1165
1265
 
1166
1266
  Returns:
1267
+ -------
1167
1268
  List of subtasks
1168
1269
 
1169
1270
  """
@@ -1198,14 +1299,17 @@ class AsanaAdapter(BaseAdapter[Task]):
1198
1299
  """Attach a file to an Asana task.
1199
1300
 
1200
1301
  Args:
1302
+ ----
1201
1303
  ticket_id: Task GID
1202
1304
  file_path: Local file path to upload
1203
1305
  description: Optional attachment description (not used by Asana)
1204
1306
 
1205
1307
  Returns:
1308
+ -------
1206
1309
  Created Attachment with metadata
1207
1310
 
1208
1311
  Raises:
1312
+ ------
1209
1313
  FileNotFoundError: If file doesn't exist
1210
1314
  ValueError: If upload fails
1211
1315
 
@@ -1255,9 +1359,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1255
1359
  """Get all attachments for an Asana task.
1256
1360
 
1257
1361
  Args:
1362
+ ----
1258
1363
  ticket_id: Task GID
1259
1364
 
1260
1365
  Returns:
1366
+ -------
1261
1367
  List of attachments
1262
1368
 
1263
1369
  """
@@ -1285,10 +1391,12 @@ class AsanaAdapter(BaseAdapter[Task]):
1285
1391
  """Delete an attachment from an Asana task.
1286
1392
 
1287
1393
  Args:
1394
+ ----
1288
1395
  ticket_id: Task GID (not used, kept for interface compatibility)
1289
1396
  attachment_id: Attachment GID
1290
1397
 
1291
1398
  Returns:
1399
+ -------
1292
1400
  True if deleted successfully
1293
1401
 
1294
1402
  """
@@ -22,9 +22,11 @@ def parse_asana_datetime(date_str: str | None) -> datetime | None:
22
22
  """Parse Asana datetime string to datetime object.
23
23
 
24
24
  Args:
25
+ ----
25
26
  date_str: ISO 8601 datetime string or None
26
27
 
27
28
  Returns:
29
+ -------
28
30
  Parsed datetime or None
29
31
 
30
32
  """
@@ -43,9 +45,11 @@ def map_asana_project_to_epic(project: dict[str, Any]) -> Epic:
43
45
  """Map Asana project to Epic.
44
46
 
45
47
  Args:
48
+ ----
46
49
  project: Asana project data
47
50
 
48
51
  Returns:
52
+ -------
49
53
  Epic model instance
50
54
 
51
55
  """
@@ -91,9 +95,11 @@ def map_asana_task_to_task(task: dict[str, Any]) -> Task:
91
95
  - No parent task → ISSUE (standard task)
92
96
 
93
97
  Args:
98
+ ----
94
99
  task: Asana task data
95
100
 
96
101
  Returns:
102
+ -------
97
103
  Task model instance
98
104
 
99
105
  """
@@ -181,11 +187,13 @@ def map_epic_to_asana_project(
181
187
  """Map Epic to Asana project create/update data.
182
188
 
183
189
  Args:
190
+ ----
184
191
  epic: Epic model instance
185
192
  workspace_gid: Asana workspace GID
186
193
  team_gid: Asana team GID (optional, required for organization workspaces)
187
194
 
188
195
  Returns:
196
+ -------
189
197
  Asana project data for create/update
190
198
 
191
199
  """
@@ -216,11 +224,13 @@ def map_task_to_asana_task(
216
224
  """Map Task to Asana task create/update data.
217
225
 
218
226
  Args:
227
+ ----
219
228
  task: Task model instance
220
229
  workspace_gid: Asana workspace GID
221
230
  project_gids: List of project GIDs to add task to (optional)
222
231
 
223
232
  Returns:
233
+ -------
224
234
  Asana task data for create/update
225
235
 
226
236
  """
@@ -262,10 +272,12 @@ def map_asana_story_to_comment(story: dict[str, Any], task_gid: str) -> Comment
262
272
  Only maps stories of type 'comment'. Other story types (system events) are filtered out.
263
273
 
264
274
  Args:
275
+ ----
265
276
  story: Asana story data
266
277
  task_gid: Parent task GID
267
278
 
268
279
  Returns:
280
+ -------
269
281
  Comment model instance or None if not a comment type
270
282
 
271
283
  """
@@ -300,10 +312,12 @@ def map_asana_attachment_to_attachment(
300
312
  IMPORTANT: Use permanent_url for reliable access, not download_url which expires.
301
313
 
302
314
  Args:
315
+ ----
303
316
  attachment: Asana attachment data
304
317
  task_gid: Parent task GID
305
318
 
306
319
  Returns:
320
+ -------
307
321
  Attachment model instance
308
322
 
309
323
  """