mcp-ticketer 0.12.0__py3-none-any.whl → 2.2.13__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 (129) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +1 -1
  3. mcp_ticketer/_version_scm.py +1 -0
  4. mcp_ticketer/adapters/aitrackdown.py +507 -6
  5. mcp_ticketer/adapters/asana/adapter.py +229 -0
  6. mcp_ticketer/adapters/asana/mappers.py +14 -0
  7. mcp_ticketer/adapters/github/__init__.py +26 -0
  8. mcp_ticketer/adapters/github/adapter.py +3229 -0
  9. mcp_ticketer/adapters/github/client.py +335 -0
  10. mcp_ticketer/adapters/github/mappers.py +797 -0
  11. mcp_ticketer/adapters/github/queries.py +692 -0
  12. mcp_ticketer/adapters/github/types.py +460 -0
  13. mcp_ticketer/adapters/hybrid.py +47 -5
  14. mcp_ticketer/adapters/jira/__init__.py +35 -0
  15. mcp_ticketer/adapters/jira/adapter.py +1351 -0
  16. mcp_ticketer/adapters/jira/client.py +271 -0
  17. mcp_ticketer/adapters/jira/mappers.py +246 -0
  18. mcp_ticketer/adapters/jira/queries.py +216 -0
  19. mcp_ticketer/adapters/jira/types.py +304 -0
  20. mcp_ticketer/adapters/linear/adapter.py +2730 -139
  21. mcp_ticketer/adapters/linear/client.py +175 -3
  22. mcp_ticketer/adapters/linear/mappers.py +203 -8
  23. mcp_ticketer/adapters/linear/queries.py +280 -3
  24. mcp_ticketer/adapters/linear/types.py +120 -4
  25. mcp_ticketer/analysis/__init__.py +56 -0
  26. mcp_ticketer/analysis/dependency_graph.py +255 -0
  27. mcp_ticketer/analysis/health_assessment.py +304 -0
  28. mcp_ticketer/analysis/orphaned.py +218 -0
  29. mcp_ticketer/analysis/project_status.py +594 -0
  30. mcp_ticketer/analysis/similarity.py +224 -0
  31. mcp_ticketer/analysis/staleness.py +266 -0
  32. mcp_ticketer/automation/__init__.py +11 -0
  33. mcp_ticketer/automation/project_updates.py +378 -0
  34. mcp_ticketer/cli/adapter_diagnostics.py +3 -1
  35. mcp_ticketer/cli/auggie_configure.py +17 -5
  36. mcp_ticketer/cli/codex_configure.py +97 -61
  37. mcp_ticketer/cli/configure.py +1288 -105
  38. mcp_ticketer/cli/cursor_configure.py +314 -0
  39. mcp_ticketer/cli/diagnostics.py +13 -12
  40. mcp_ticketer/cli/discover.py +5 -0
  41. mcp_ticketer/cli/gemini_configure.py +17 -5
  42. mcp_ticketer/cli/init_command.py +880 -0
  43. mcp_ticketer/cli/install_mcp_server.py +418 -0
  44. mcp_ticketer/cli/instruction_commands.py +6 -0
  45. mcp_ticketer/cli/main.py +267 -3175
  46. mcp_ticketer/cli/mcp_configure.py +821 -119
  47. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  48. mcp_ticketer/cli/platform_detection.py +77 -12
  49. mcp_ticketer/cli/platform_installer.py +545 -0
  50. mcp_ticketer/cli/project_update_commands.py +350 -0
  51. mcp_ticketer/cli/setup_command.py +795 -0
  52. mcp_ticketer/cli/simple_health.py +12 -10
  53. mcp_ticketer/cli/ticket_commands.py +705 -103
  54. mcp_ticketer/cli/utils.py +113 -0
  55. mcp_ticketer/core/__init__.py +56 -6
  56. mcp_ticketer/core/adapter.py +533 -2
  57. mcp_ticketer/core/config.py +21 -21
  58. mcp_ticketer/core/exceptions.py +7 -1
  59. mcp_ticketer/core/label_manager.py +732 -0
  60. mcp_ticketer/core/mappers.py +31 -19
  61. mcp_ticketer/core/milestone_manager.py +252 -0
  62. mcp_ticketer/core/models.py +480 -0
  63. mcp_ticketer/core/onepassword_secrets.py +1 -1
  64. mcp_ticketer/core/priority_matcher.py +463 -0
  65. mcp_ticketer/core/project_config.py +132 -14
  66. mcp_ticketer/core/project_utils.py +281 -0
  67. mcp_ticketer/core/project_validator.py +376 -0
  68. mcp_ticketer/core/session_state.py +176 -0
  69. mcp_ticketer/core/state_matcher.py +625 -0
  70. mcp_ticketer/core/url_parser.py +425 -0
  71. mcp_ticketer/core/validators.py +69 -0
  72. mcp_ticketer/mcp/server/__main__.py +2 -1
  73. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  74. mcp_ticketer/mcp/server/main.py +106 -25
  75. mcp_ticketer/mcp/server/routing.py +723 -0
  76. mcp_ticketer/mcp/server/server_sdk.py +58 -0
  77. mcp_ticketer/mcp/server/tools/__init__.py +33 -11
  78. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  79. mcp_ticketer/mcp/server/tools/attachment_tools.py +5 -5
  80. mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
  81. mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
  82. mcp_ticketer/mcp/server/tools/config_tools.py +1391 -145
  83. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  84. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
  85. mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
  86. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  87. mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
  88. mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
  89. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  90. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  91. mcp_ticketer/mcp/server/tools/search_tools.py +209 -97
  92. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  93. mcp_ticketer/mcp/server/tools/ticket_tools.py +1107 -124
  94. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
  95. mcp_ticketer/queue/queue.py +68 -0
  96. mcp_ticketer/queue/worker.py +1 -1
  97. mcp_ticketer/utils/__init__.py +5 -0
  98. mcp_ticketer/utils/token_utils.py +246 -0
  99. mcp_ticketer-2.2.13.dist-info/METADATA +1396 -0
  100. mcp_ticketer-2.2.13.dist-info/RECORD +158 -0
  101. mcp_ticketer-2.2.13.dist-info/top_level.txt +2 -0
  102. py_mcp_installer/examples/phase3_demo.py +178 -0
  103. py_mcp_installer/scripts/manage_version.py +54 -0
  104. py_mcp_installer/setup.py +6 -0
  105. py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
  106. py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
  107. py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
  108. py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
  109. py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
  110. py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
  111. py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
  112. py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
  113. py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
  114. py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
  115. py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
  116. py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
  117. py_mcp_installer/src/py_mcp_installer/types.py +222 -0
  118. py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
  119. py_mcp_installer/tests/__init__.py +0 -0
  120. py_mcp_installer/tests/platforms/__init__.py +0 -0
  121. py_mcp_installer/tests/test_platform_detector.py +17 -0
  122. mcp_ticketer/adapters/github.py +0 -1574
  123. mcp_ticketer/adapters/jira.py +0 -1258
  124. mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
  125. mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
  126. mcp_ticketer-0.12.0.dist-info/top_level.txt +0 -1
  127. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/WHEEL +0 -0
  128. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/entry_points.txt +0 -0
  129. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.2.13.dist-info}/licenses/LICENSE +0 -0
@@ -6,6 +6,7 @@ import builtins
6
6
  import logging
7
7
  import mimetypes
8
8
  import os
9
+ from datetime import datetime
9
10
  from pathlib import Path
10
11
  from typing import Any
11
12
 
@@ -59,6 +60,7 @@ class AsanaAdapter(BaseAdapter[Task]):
59
60
  """Initialize Asana adapter.
60
61
 
61
62
  Args:
63
+ ----
62
64
  config: Configuration with:
63
65
  - api_key: Asana Personal Access Token (or ASANA_PAT env var)
64
66
  - workspace: Asana workspace name (optional, for resolution)
@@ -68,6 +70,7 @@ class AsanaAdapter(BaseAdapter[Task]):
68
70
  - max_retries: Maximum retry attempts (default: 3)
69
71
 
70
72
  Raises:
73
+ ------
71
74
  ValueError: If required configuration is missing
72
75
 
73
76
  """
@@ -120,6 +123,7 @@ class AsanaAdapter(BaseAdapter[Task]):
120
123
  """Validate Asana API credentials.
121
124
 
122
125
  Returns:
126
+ -------
123
127
  Tuple of (is_valid, error_message)
124
128
 
125
129
  """
@@ -244,9 +248,11 @@ class AsanaAdapter(BaseAdapter[Task]):
244
248
  """Load custom fields configured for a specific project.
245
249
 
246
250
  Args:
251
+ ----
247
252
  project_gid: Project GID to load custom fields for
248
253
 
249
254
  Returns:
255
+ -------
250
256
  Dictionary mapping field name (lowercase) to field data
251
257
 
252
258
  """
@@ -277,9 +283,11 @@ class AsanaAdapter(BaseAdapter[Task]):
277
283
  """Get custom fields for a project, loading if not cached.
278
284
 
279
285
  Args:
286
+ ----
280
287
  project_gid: Project GID
281
288
 
282
289
  Returns:
290
+ -------
283
291
  Dictionary mapping field name (lowercase) to field data
284
292
 
285
293
  """
@@ -295,10 +303,12 @@ class AsanaAdapter(BaseAdapter[Task]):
295
303
  """Map TicketState to Asana Status custom field option.
296
304
 
297
305
  Args:
306
+ ----
298
307
  state: The TicketState to map
299
308
  status_field: The Status custom field data with enum_options
300
309
 
301
310
  Returns:
311
+ -------
302
312
  Matching enum option or None
303
313
 
304
314
  """
@@ -339,6 +349,7 @@ class AsanaAdapter(BaseAdapter[Task]):
339
349
  We return a mapping to "true"/"false" strings for compatibility.
340
350
 
341
351
  Returns:
352
+ -------
342
353
  Dictionary mapping TicketState to completion status string
343
354
 
344
355
  """
@@ -357,9 +368,11 @@ class AsanaAdapter(BaseAdapter[Task]):
357
368
  """Resolve project identifier (name or GID) to GID.
358
369
 
359
370
  Args:
371
+ ----
360
372
  project_identifier: Project name or GID
361
373
 
362
374
  Returns:
375
+ -------
363
376
  Project GID or None if not found
364
377
 
365
378
  """
@@ -392,9 +405,11 @@ class AsanaAdapter(BaseAdapter[Task]):
392
405
  """Resolve user identifier (email, name, or GID) to GID.
393
406
 
394
407
  Args:
408
+ ----
395
409
  user_identifier: User email, name, or GID
396
410
 
397
411
  Returns:
412
+ -------
398
413
  User GID or None if not found
399
414
 
400
415
  """
@@ -432,12 +447,15 @@ class AsanaAdapter(BaseAdapter[Task]):
432
447
  """Create a new Asana project or task.
433
448
 
434
449
  Args:
450
+ ----
435
451
  ticket: Epic or Task to create
436
452
 
437
453
  Returns:
454
+ -------
438
455
  Created ticket with ID populated
439
456
 
440
457
  Raises:
458
+ ------
441
459
  ValueError: If creation fails
442
460
 
443
461
  """
@@ -460,9 +478,11 @@ class AsanaAdapter(BaseAdapter[Task]):
460
478
  """Create an Asana project from an Epic.
461
479
 
462
480
  Args:
481
+ ----
463
482
  epic: Epic to create
464
483
 
465
484
  Returns:
485
+ -------
466
486
  Created epic with Asana metadata
467
487
 
468
488
  """
@@ -491,9 +511,11 @@ class AsanaAdapter(BaseAdapter[Task]):
491
511
  subtask (child of another task) when task.parent_issue is provided.
492
512
 
493
513
  Args:
514
+ ----
494
515
  task: Task to create
495
516
 
496
517
  Returns:
518
+ -------
497
519
  Created task with Asana metadata
498
520
 
499
521
  """
@@ -564,6 +586,7 @@ class AsanaAdapter(BaseAdapter[Task]):
564
586
  """Add tags to an Asana task.
565
587
 
566
588
  Args:
589
+ ----
567
590
  task_gid: Task GID
568
591
  tags: List of tag names to add
569
592
 
@@ -602,9 +625,11 @@ class AsanaAdapter(BaseAdapter[Task]):
602
625
  """Read an Asana task by GID.
603
626
 
604
627
  Args:
628
+ ----
605
629
  ticket_id: Asana task GID
606
630
 
607
631
  Returns:
632
+ -------
608
633
  Task if found, None otherwise
609
634
 
610
635
  """
@@ -632,10 +657,12 @@ class AsanaAdapter(BaseAdapter[Task]):
632
657
  """Update an Asana task.
633
658
 
634
659
  Args:
660
+ ----
635
661
  ticket_id: Task GID
636
662
  updates: Dictionary of fields to update
637
663
 
638
664
  Returns:
665
+ -------
639
666
  Updated task or None if not found
640
667
 
641
668
  """
@@ -776,9 +803,11 @@ class AsanaAdapter(BaseAdapter[Task]):
776
803
  """Delete an Asana task.
777
804
 
778
805
  Args:
806
+ ----
779
807
  ticket_id: Task GID
780
808
 
781
809
  Returns:
810
+ -------
782
811
  True if successfully deleted
783
812
 
784
813
  """
@@ -795,11 +824,13 @@ class AsanaAdapter(BaseAdapter[Task]):
795
824
  """List Asana tasks with optional filtering.
796
825
 
797
826
  Args:
827
+ ----
798
828
  limit: Maximum number of tasks to return
799
829
  offset: Number of tasks to skip (Note: Asana uses offset tokens)
800
830
  filters: Optional filters (state, assignee, project, etc.)
801
831
 
802
832
  Returns:
833
+ -------
803
834
  List of tasks matching the criteria
804
835
 
805
836
  """
@@ -896,9 +927,11 @@ class AsanaAdapter(BaseAdapter[Task]):
896
927
  """Search Asana tasks using filters.
897
928
 
898
929
  Args:
930
+ ----
899
931
  query: Search query with filters
900
932
 
901
933
  Returns:
934
+ -------
902
935
  List of tasks matching the search criteria
903
936
 
904
937
  """
@@ -936,10 +969,12 @@ class AsanaAdapter(BaseAdapter[Task]):
936
969
  """Transition task to new state.
937
970
 
938
971
  Args:
972
+ ----
939
973
  ticket_id: Task GID
940
974
  target_state: Target state
941
975
 
942
976
  Returns:
977
+ -------
943
978
  Updated task or None if failed
944
979
 
945
980
  """
@@ -949,12 +984,15 @@ class AsanaAdapter(BaseAdapter[Task]):
949
984
  """Add a comment to an Asana task (as a story).
950
985
 
951
986
  Args:
987
+ ----
952
988
  comment: Comment to add
953
989
 
954
990
  Returns:
991
+ -------
955
992
  Created comment with ID
956
993
 
957
994
  Raises:
995
+ ------
958
996
  ValueError: If comment creation fails
959
997
 
960
998
  """
@@ -984,11 +1022,13 @@ class AsanaAdapter(BaseAdapter[Task]):
984
1022
  Filters stories to only return comment type (not system events).
985
1023
 
986
1024
  Args:
1025
+ ----
987
1026
  ticket_id: Task GID
988
1027
  limit: Maximum number of comments to return
989
1028
  offset: Number of comments to skip
990
1029
 
991
1030
  Returns:
1031
+ -------
992
1032
  List of comments for the task
993
1033
 
994
1034
  """
@@ -1019,11 +1059,13 @@ class AsanaAdapter(BaseAdapter[Task]):
1019
1059
  """Create an Asana project (Epic).
1020
1060
 
1021
1061
  Args:
1062
+ ----
1022
1063
  title: Epic title
1023
1064
  description: Epic description
1024
1065
  **kwargs: Additional fields
1025
1066
 
1026
1067
  Returns:
1068
+ -------
1027
1069
  Created epic or None if failed
1028
1070
 
1029
1071
  """
@@ -1041,9 +1083,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1041
1083
  """Get an Asana project (Epic) by GID.
1042
1084
 
1043
1085
  Args:
1086
+ ----
1044
1087
  epic_id: Project GID
1045
1088
 
1046
1089
  Returns:
1090
+ -------
1047
1091
  Epic if found, None otherwise
1048
1092
 
1049
1093
  """
@@ -1065,10 +1109,12 @@ class AsanaAdapter(BaseAdapter[Task]):
1065
1109
  """Update an Asana project (Epic).
1066
1110
 
1067
1111
  Args:
1112
+ ----
1068
1113
  epic_id: Project GID
1069
1114
  updates: Dictionary of fields to update
1070
1115
 
1071
1116
  Returns:
1117
+ -------
1072
1118
  Updated epic or None if failed
1073
1119
 
1074
1120
  """
@@ -1108,9 +1154,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1108
1154
  """List all Asana projects (Epics).
1109
1155
 
1110
1156
  Args:
1157
+ ----
1111
1158
  **kwargs: Optional filter parameters
1112
1159
 
1113
1160
  Returns:
1161
+ -------
1114
1162
  List of epics
1115
1163
 
1116
1164
  """
@@ -1143,13 +1191,65 @@ class AsanaAdapter(BaseAdapter[Task]):
1143
1191
  logger.error(f"Failed to list projects: {e}")
1144
1192
  return []
1145
1193
 
1194
+ async def delete_epic(self, epic_id: str) -> bool:
1195
+ """Delete an Asana project (Epic).
1196
+
1197
+ Args:
1198
+ ----
1199
+ epic_id: Project GID to delete
1200
+
1201
+ Returns:
1202
+ -------
1203
+ True if successfully deleted, False otherwise
1204
+
1205
+ Raises:
1206
+ ------
1207
+ ValueError: If credentials are invalid or GID format is invalid
1208
+
1209
+ """
1210
+ # Validate credentials
1211
+ is_valid, error_message = self.validate_credentials()
1212
+ if not is_valid:
1213
+ raise ValueError(error_message)
1214
+
1215
+ # Validate GID format (should be numeric)
1216
+ if not epic_id or not epic_id.isdigit():
1217
+ raise ValueError(
1218
+ f"Invalid project GID '{epic_id}'. Asana project GIDs must be numeric."
1219
+ )
1220
+
1221
+ try:
1222
+ # Delete project using REST API
1223
+ await self.client.delete(f"/projects/{epic_id}")
1224
+ logger.info(f"Successfully deleted project {epic_id}")
1225
+ return True
1226
+
1227
+ except Exception as e:
1228
+ # Check if it's a 404 (not found) - return False
1229
+ if "404" in str(e) or "Not Found" in str(e):
1230
+ logger.warning(f"Project {epic_id} not found")
1231
+ return False
1232
+
1233
+ # Check for permissions errors
1234
+ if "403" in str(e) or "Forbidden" in str(e):
1235
+ logger.error(f"Permission denied to delete project {epic_id}")
1236
+ raise ValueError(
1237
+ f"Permission denied: You don't have permission to delete project {epic_id}"
1238
+ ) from e
1239
+
1240
+ # Other errors - log and raise
1241
+ logger.error(f"Failed to delete project {epic_id}: {e}")
1242
+ raise ValueError(f"Failed to delete project: {e}") from e
1243
+
1146
1244
  async def list_issues_by_epic(self, epic_id: str) -> builtins.list[Task]:
1147
1245
  """List all tasks in a project (Epic).
1148
1246
 
1149
1247
  Args:
1248
+ ----
1150
1249
  epic_id: Project GID
1151
1250
 
1152
1251
  Returns:
1252
+ -------
1153
1253
  List of tasks in the project
1154
1254
 
1155
1255
  """
@@ -1161,9 +1261,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1161
1261
  """List all subtasks of a task (Issue).
1162
1262
 
1163
1263
  Args:
1264
+ ----
1164
1265
  issue_id: Parent task GID
1165
1266
 
1166
1267
  Returns:
1268
+ -------
1167
1269
  List of subtasks
1168
1270
 
1169
1271
  """
@@ -1198,14 +1300,17 @@ class AsanaAdapter(BaseAdapter[Task]):
1198
1300
  """Attach a file to an Asana task.
1199
1301
 
1200
1302
  Args:
1303
+ ----
1201
1304
  ticket_id: Task GID
1202
1305
  file_path: Local file path to upload
1203
1306
  description: Optional attachment description (not used by Asana)
1204
1307
 
1205
1308
  Returns:
1309
+ -------
1206
1310
  Created Attachment with metadata
1207
1311
 
1208
1312
  Raises:
1313
+ ------
1209
1314
  FileNotFoundError: If file doesn't exist
1210
1315
  ValueError: If upload fails
1211
1316
 
@@ -1255,9 +1360,11 @@ class AsanaAdapter(BaseAdapter[Task]):
1255
1360
  """Get all attachments for an Asana task.
1256
1361
 
1257
1362
  Args:
1363
+ ----
1258
1364
  ticket_id: Task GID
1259
1365
 
1260
1366
  Returns:
1367
+ -------
1261
1368
  List of attachments
1262
1369
 
1263
1370
  """
@@ -1285,10 +1392,12 @@ class AsanaAdapter(BaseAdapter[Task]):
1285
1392
  """Delete an attachment from an Asana task.
1286
1393
 
1287
1394
  Args:
1395
+ ----
1288
1396
  ticket_id: Task GID (not used, kept for interface compatibility)
1289
1397
  attachment_id: Attachment GID
1290
1398
 
1291
1399
  Returns:
1400
+ -------
1292
1401
  True if deleted successfully
1293
1402
 
1294
1403
  """
@@ -1303,6 +1412,126 @@ class AsanaAdapter(BaseAdapter[Task]):
1303
1412
  """Close adapter and cleanup resources."""
1304
1413
  await self.client.close()
1305
1414
 
1415
+ # Milestone Methods (Not yet implemented)
1416
+
1417
+ async def milestone_create(
1418
+ self,
1419
+ name: str,
1420
+ target_date: datetime | None = None,
1421
+ labels: list[str] | None = None,
1422
+ description: str = "",
1423
+ project_id: str | None = None,
1424
+ ) -> Any:
1425
+ """Create milestone - not yet implemented for Asana.
1426
+
1427
+ Args:
1428
+ ----
1429
+ name: Milestone name
1430
+ target_date: Target completion date
1431
+ labels: Labels that define this milestone
1432
+ description: Milestone description
1433
+ project_id: Associated project ID
1434
+
1435
+ Raises:
1436
+ ------
1437
+ NotImplementedError: Milestone support coming in v2.1.0
1438
+
1439
+ """
1440
+ raise NotImplementedError("Milestone support for Asana coming in v2.1.0")
1441
+
1442
+ async def milestone_get(self, milestone_id: str) -> Any:
1443
+ """Get milestone - not yet implemented for Asana.
1444
+
1445
+ Args:
1446
+ ----
1447
+ milestone_id: Milestone identifier
1448
+
1449
+ Raises:
1450
+ ------
1451
+ NotImplementedError: Milestone support coming in v2.1.0
1452
+
1453
+ """
1454
+ raise NotImplementedError("Milestone support for Asana coming in v2.1.0")
1455
+
1456
+ async def milestone_list(
1457
+ self,
1458
+ project_id: str | None = None,
1459
+ state: str | None = None,
1460
+ ) -> list[Any]:
1461
+ """List milestones - not yet implemented for Asana.
1462
+
1463
+ Args:
1464
+ ----
1465
+ project_id: Filter by project
1466
+ state: Filter by state
1467
+
1468
+ Raises:
1469
+ ------
1470
+ NotImplementedError: Milestone support coming in v2.1.0
1471
+
1472
+ """
1473
+ raise NotImplementedError("Milestone support for Asana coming in v2.1.0")
1474
+
1475
+ async def milestone_update(
1476
+ self,
1477
+ milestone_id: str,
1478
+ name: str | None = None,
1479
+ target_date: datetime | None = None,
1480
+ state: str | None = None,
1481
+ labels: list[str] | None = None,
1482
+ description: str | None = None,
1483
+ ) -> Any:
1484
+ """Update milestone - not yet implemented for Asana.
1485
+
1486
+ Args:
1487
+ ----
1488
+ milestone_id: Milestone identifier
1489
+ name: New name
1490
+ target_date: New target date
1491
+ state: New state
1492
+ labels: New labels
1493
+ description: New description
1494
+
1495
+ Raises:
1496
+ ------
1497
+ NotImplementedError: Milestone support coming in v2.1.0
1498
+
1499
+ """
1500
+ raise NotImplementedError("Milestone support for Asana coming in v2.1.0")
1501
+
1502
+ async def milestone_delete(self, milestone_id: str) -> bool:
1503
+ """Delete milestone - not yet implemented for Asana.
1504
+
1505
+ Args:
1506
+ ----
1507
+ milestone_id: Milestone identifier
1508
+
1509
+ Raises:
1510
+ ------
1511
+ NotImplementedError: Milestone support coming in v2.1.0
1512
+
1513
+ """
1514
+ raise NotImplementedError("Milestone support for Asana coming in v2.1.0")
1515
+
1516
+ async def milestone_get_issues(
1517
+ self,
1518
+ milestone_id: str,
1519
+ state: str | None = None,
1520
+ ) -> list[Any]:
1521
+ """Get milestone issues - not yet implemented for Asana.
1522
+
1523
+ Args:
1524
+ ----
1525
+ milestone_id: Milestone identifier
1526
+ state: Filter by issue state
1527
+
1528
+ Raises:
1529
+ ------
1530
+ NotImplementedError: Milestone support coming in v2.1.0
1531
+
1532
+ """
1533
+ raise NotImplementedError("Milestone support for Asana coming in v2.1.0")
1534
+
1306
1535
 
1307
1536
  # Register the adapter
1308
1537
  AdapterRegistry.register("asana", AsanaAdapter)
@@ -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
  """
@@ -0,0 +1,26 @@
1
+ """GitHub adapter for MCP Ticketer.
2
+
3
+ This module provides integration with GitHub's REST and GraphQL APIs for universal ticket management.
4
+ The adapter is split into multiple modules for better organization:
5
+
6
+ - adapter.py: Main GitHubAdapter class with core functionality
7
+ - queries.py: GraphQL queries and fragments
8
+ - types.py: GitHub-specific types and mappings
9
+ - client.py: HTTP/GraphQL client management
10
+ - mappers.py: Data transformation between GitHub and universal models
11
+
12
+ Usage:
13
+ from mcp_ticketer.adapters.github import GitHubAdapter
14
+
15
+ config = {
16
+ "token": "your_github_token",
17
+ "owner": "repository_owner",
18
+ "repo": "repository_name"
19
+ }
20
+ adapter = GitHubAdapter(config)
21
+ """
22
+
23
+ from .adapter import GitHubAdapter
24
+ from .types import GitHubStateMapping
25
+
26
+ __all__ = ["GitHubAdapter", "GitHubStateMapping"]