mcp-ticketer 0.3.1__py3-none-any.whl → 0.3.2__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 (37) hide show
  1. mcp_ticketer/__version__.py +1 -1
  2. mcp_ticketer/adapters/aitrackdown.py +12 -15
  3. mcp_ticketer/adapters/github.py +7 -4
  4. mcp_ticketer/adapters/jira.py +23 -22
  5. mcp_ticketer/adapters/linear/__init__.py +1 -1
  6. mcp_ticketer/adapters/linear/adapter.py +88 -89
  7. mcp_ticketer/adapters/linear/client.py +71 -52
  8. mcp_ticketer/adapters/linear/mappers.py +88 -68
  9. mcp_ticketer/adapters/linear/queries.py +28 -7
  10. mcp_ticketer/adapters/linear/types.py +57 -50
  11. mcp_ticketer/adapters/linear.py +2 -2
  12. mcp_ticketer/cli/adapter_diagnostics.py +86 -51
  13. mcp_ticketer/cli/diagnostics.py +165 -72
  14. mcp_ticketer/cli/linear_commands.py +156 -113
  15. mcp_ticketer/cli/main.py +153 -82
  16. mcp_ticketer/cli/simple_health.py +73 -45
  17. mcp_ticketer/cli/utils.py +15 -10
  18. mcp_ticketer/core/config.py +23 -19
  19. mcp_ticketer/core/env_discovery.py +5 -4
  20. mcp_ticketer/core/env_loader.py +109 -86
  21. mcp_ticketer/core/exceptions.py +20 -18
  22. mcp_ticketer/core/models.py +9 -0
  23. mcp_ticketer/core/project_config.py +1 -1
  24. mcp_ticketer/mcp/server.py +294 -139
  25. mcp_ticketer/queue/health_monitor.py +152 -121
  26. mcp_ticketer/queue/manager.py +11 -4
  27. mcp_ticketer/queue/queue.py +15 -3
  28. mcp_ticketer/queue/run_worker.py +1 -1
  29. mcp_ticketer/queue/ticket_registry.py +190 -132
  30. mcp_ticketer/queue/worker.py +54 -25
  31. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/METADATA +1 -1
  32. mcp_ticketer-0.3.2.dist-info/RECORD +59 -0
  33. mcp_ticketer-0.3.1.dist-info/RECORD +0 -59
  34. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/WHEEL +0 -0
  35. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/entry_points.txt +0 -0
  36. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/licenses/LICENSE +0 -0
  37. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/top_level.txt +0 -0
@@ -238,7 +238,9 @@ WORKFLOW_STATES_QUERY = """
238
238
  }
239
239
  """
240
240
 
241
- CREATE_ISSUE_MUTATION = ALL_FRAGMENTS + """
241
+ CREATE_ISSUE_MUTATION = (
242
+ ALL_FRAGMENTS
243
+ + """
242
244
  mutation CreateIssue($input: IssueCreateInput!) {
243
245
  issueCreate(input: $input) {
244
246
  success
@@ -248,8 +250,11 @@ CREATE_ISSUE_MUTATION = ALL_FRAGMENTS + """
248
250
  }
249
251
  }
250
252
  """
253
+ )
251
254
 
252
- UPDATE_ISSUE_MUTATION = ALL_FRAGMENTS + """
255
+ UPDATE_ISSUE_MUTATION = (
256
+ ALL_FRAGMENTS
257
+ + """
253
258
  mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {
254
259
  issueUpdate(id: $id, input: $input) {
255
260
  success
@@ -259,8 +264,11 @@ UPDATE_ISSUE_MUTATION = ALL_FRAGMENTS + """
259
264
  }
260
265
  }
261
266
  """
267
+ )
262
268
 
263
- LIST_ISSUES_QUERY = ISSUE_LIST_FRAGMENTS + """
269
+ LIST_ISSUES_QUERY = (
270
+ ISSUE_LIST_FRAGMENTS
271
+ + """
264
272
  query ListIssues($filter: IssueFilter, $first: Int!) {
265
273
  issues(
266
274
  filter: $filter
@@ -277,8 +285,11 @@ LIST_ISSUES_QUERY = ISSUE_LIST_FRAGMENTS + """
277
285
  }
278
286
  }
279
287
  """
288
+ )
280
289
 
281
- SEARCH_ISSUES_QUERY = ISSUE_LIST_FRAGMENTS + """
290
+ SEARCH_ISSUES_QUERY = (
291
+ ISSUE_LIST_FRAGMENTS
292
+ + """
282
293
  query SearchIssues($filter: IssueFilter, $first: Int!) {
283
294
  issues(
284
295
  filter: $filter
@@ -291,6 +302,7 @@ SEARCH_ISSUES_QUERY = ISSUE_LIST_FRAGMENTS + """
291
302
  }
292
303
  }
293
304
  """
305
+ )
294
306
 
295
307
  GET_CYCLES_QUERY = """
296
308
  query GetCycles($filter: CycleFilter) {
@@ -336,7 +348,9 @@ SEARCH_ISSUE_BY_IDENTIFIER_QUERY = """
336
348
  }
337
349
  """
338
350
 
339
- LIST_PROJECTS_QUERY = PROJECT_FRAGMENT + """
351
+ LIST_PROJECTS_QUERY = (
352
+ PROJECT_FRAGMENT
353
+ + """
340
354
  query ListProjects($filter: ProjectFilter, $first: Int!) {
341
355
  projects(filter: $filter, first: $first, orderBy: updatedAt) {
342
356
  nodes {
@@ -345,8 +359,11 @@ LIST_PROJECTS_QUERY = PROJECT_FRAGMENT + """
345
359
  }
346
360
  }
347
361
  """
362
+ )
348
363
 
349
- CREATE_SUB_ISSUE_MUTATION = ALL_FRAGMENTS + """
364
+ CREATE_SUB_ISSUE_MUTATION = (
365
+ ALL_FRAGMENTS
366
+ + """
350
367
  mutation CreateSubIssue($input: IssueCreateInput!) {
351
368
  issueCreate(input: $input) {
352
369
  success
@@ -356,11 +373,15 @@ CREATE_SUB_ISSUE_MUTATION = ALL_FRAGMENTS + """
356
373
  }
357
374
  }
358
375
  """
376
+ )
359
377
 
360
- GET_CURRENT_USER_QUERY = USER_FRAGMENT + """
378
+ GET_CURRENT_USER_QUERY = (
379
+ USER_FRAGMENT
380
+ + """
361
381
  query GetCurrentUser {
362
382
  viewer {
363
383
  ...UserFields
364
384
  }
365
385
  }
366
386
  """
387
+ )
@@ -10,39 +10,39 @@ from mcp_ticketer.core.models import Priority, TicketState
10
10
 
11
11
  class LinearPriorityMapping:
12
12
  """Mapping between universal Priority and Linear priority values."""
13
-
13
+
14
14
  # Linear uses numeric priorities: 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low
15
15
  TO_LINEAR: Dict[Priority, int] = {
16
16
  Priority.CRITICAL: 1, # Urgent
17
- Priority.HIGH: 2, # High
18
- Priority.MEDIUM: 3, # Medium
19
- Priority.LOW: 4, # Low
17
+ Priority.HIGH: 2, # High
18
+ Priority.MEDIUM: 3, # Medium
19
+ Priority.LOW: 4, # Low
20
20
  }
21
-
21
+
22
22
  FROM_LINEAR: Dict[int, Priority] = {
23
- 0: Priority.LOW, # No priority -> Low
23
+ 0: Priority.LOW, # No priority -> Low
24
24
  1: Priority.CRITICAL, # Urgent -> Critical
25
- 2: Priority.HIGH, # High -> High
26
- 3: Priority.MEDIUM, # Medium -> Medium
27
- 4: Priority.LOW, # Low -> Low
25
+ 2: Priority.HIGH, # High -> High
26
+ 3: Priority.MEDIUM, # Medium -> Medium
27
+ 4: Priority.LOW, # Low -> Low
28
28
  }
29
29
 
30
30
 
31
31
  class LinearStateMapping:
32
32
  """Mapping between universal TicketState and Linear workflow state types."""
33
-
33
+
34
34
  # Linear workflow state types
35
35
  TO_LINEAR: Dict[TicketState, str] = {
36
36
  TicketState.OPEN: "unstarted",
37
37
  TicketState.IN_PROGRESS: "started",
38
38
  TicketState.READY: "unstarted", # No direct equivalent, use unstarted
39
- TicketState.TESTED: "started", # No direct equivalent, use started
39
+ TicketState.TESTED: "started", # No direct equivalent, use started
40
40
  TicketState.DONE: "completed",
41
41
  TicketState.CLOSED: "canceled",
42
42
  TicketState.WAITING: "unstarted",
43
43
  TicketState.BLOCKED: "unstarted",
44
44
  }
45
-
45
+
46
46
  FROM_LINEAR: Dict[str, TicketState] = {
47
47
  "backlog": TicketState.OPEN,
48
48
  "unstarted": TicketState.OPEN,
@@ -54,7 +54,7 @@ class LinearStateMapping:
54
54
 
55
55
  class LinearWorkflowStateType(Enum):
56
56
  """Linear workflow state types."""
57
-
57
+
58
58
  BACKLOG = "backlog"
59
59
  UNSTARTED = "unstarted"
60
60
  STARTED = "started"
@@ -64,7 +64,7 @@ class LinearWorkflowStateType(Enum):
64
64
 
65
65
  class LinearProjectState(Enum):
66
66
  """Linear project states."""
67
-
67
+
68
68
  PLANNED = "planned"
69
69
  STARTED = "started"
70
70
  COMPLETED = "completed"
@@ -74,7 +74,7 @@ class LinearProjectState(Enum):
74
74
 
75
75
  class LinearIssueRelationType(Enum):
76
76
  """Linear issue relation types."""
77
-
77
+
78
78
  BLOCKS = "blocks"
79
79
  BLOCKED_BY = "blockedBy"
80
80
  DUPLICATE = "duplicate"
@@ -84,55 +84,59 @@ class LinearIssueRelationType(Enum):
84
84
 
85
85
  class LinearCommentType(Enum):
86
86
  """Linear comment types."""
87
-
87
+
88
88
  COMMENT = "comment"
89
89
  SYSTEM = "system"
90
90
 
91
91
 
92
92
  def get_linear_priority(priority: Priority) -> int:
93
93
  """Convert universal Priority to Linear priority value.
94
-
94
+
95
95
  Args:
96
96
  priority: Universal priority enum
97
-
97
+
98
98
  Returns:
99
99
  Linear priority integer (0-4)
100
+
100
101
  """
101
102
  return LinearPriorityMapping.TO_LINEAR.get(priority, 3) # Default to Medium
102
103
 
103
104
 
104
105
  def get_universal_priority(linear_priority: int) -> Priority:
105
106
  """Convert Linear priority value to universal Priority.
106
-
107
+
107
108
  Args:
108
109
  linear_priority: Linear priority integer (0-4)
109
-
110
+
110
111
  Returns:
111
112
  Universal priority enum
113
+
112
114
  """
113
115
  return LinearPriorityMapping.FROM_LINEAR.get(linear_priority, Priority.MEDIUM)
114
116
 
115
117
 
116
118
  def get_linear_state_type(state: TicketState) -> str:
117
119
  """Convert universal TicketState to Linear workflow state type.
118
-
120
+
119
121
  Args:
120
122
  state: Universal ticket state enum
121
-
123
+
122
124
  Returns:
123
125
  Linear workflow state type string
126
+
124
127
  """
125
128
  return LinearStateMapping.TO_LINEAR.get(state, "unstarted")
126
129
 
127
130
 
128
131
  def get_universal_state(linear_state_type: str) -> TicketState:
129
132
  """Convert Linear workflow state type to universal TicketState.
130
-
133
+
131
134
  Args:
132
135
  linear_state_type: Linear workflow state type string
133
-
136
+
134
137
  Returns:
135
138
  Universal ticket state enum
139
+
136
140
  """
137
141
  return LinearStateMapping.FROM_LINEAR.get(linear_state_type, TicketState.OPEN)
138
142
 
@@ -150,7 +154,7 @@ def build_issue_filter(
150
154
  include_archived: bool = False,
151
155
  ) -> Dict[str, Any]:
152
156
  """Build a Linear issue filter from parameters.
153
-
157
+
154
158
  Args:
155
159
  state: Filter by ticket state
156
160
  assignee_id: Filter by assignee Linear user ID
@@ -162,38 +166,39 @@ def build_issue_filter(
162
166
  updated_after: Filter by update date (ISO string)
163
167
  due_before: Filter by due date (ISO string)
164
168
  include_archived: Whether to include archived issues
165
-
169
+
166
170
  Returns:
167
171
  Linear GraphQL filter object
172
+
168
173
  """
169
174
  issue_filter: Dict[str, Any] = {}
170
-
175
+
171
176
  # Team filter (required for most operations)
172
177
  if team_id:
173
178
  issue_filter["team"] = {"id": {"eq": team_id}}
174
-
179
+
175
180
  # State filter
176
181
  if state:
177
182
  state_type = get_linear_state_type(state)
178
183
  issue_filter["state"] = {"type": {"eq": state_type}}
179
-
184
+
180
185
  # Assignee filter
181
186
  if assignee_id:
182
187
  issue_filter["assignee"] = {"id": {"eq": assignee_id}}
183
-
188
+
184
189
  # Priority filter
185
190
  if priority:
186
191
  linear_priority = get_linear_priority(priority)
187
192
  issue_filter["priority"] = {"eq": linear_priority}
188
-
193
+
189
194
  # Project filter
190
195
  if project_id:
191
196
  issue_filter["project"] = {"id": {"eq": project_id}}
192
-
197
+
193
198
  # Labels filter
194
199
  if labels:
195
200
  issue_filter["labels"] = {"some": {"name": {"in": labels}}}
196
-
201
+
197
202
  # Date filters
198
203
  if created_after:
199
204
  issue_filter["createdAt"] = {"gte": created_after}
@@ -201,11 +206,11 @@ def build_issue_filter(
201
206
  issue_filter["updatedAt"] = {"gte": updated_after}
202
207
  if due_before:
203
208
  issue_filter["dueDate"] = {"lte": due_before}
204
-
209
+
205
210
  # Archived filter
206
211
  if not include_archived:
207
212
  issue_filter["archivedAt"] = {"null": True}
208
-
213
+
209
214
  return issue_filter
210
215
 
211
216
 
@@ -215,63 +220,65 @@ def build_project_filter(
215
220
  include_completed: bool = True,
216
221
  ) -> Dict[str, Any]:
217
222
  """Build a Linear project filter from parameters.
218
-
223
+
219
224
  Args:
220
225
  state: Filter by project state
221
226
  team_id: Filter by team ID
222
227
  include_completed: Whether to include completed projects
223
-
228
+
224
229
  Returns:
225
230
  Linear GraphQL filter object
231
+
226
232
  """
227
233
  project_filter: Dict[str, Any] = {}
228
-
234
+
229
235
  # Team filter
230
236
  if team_id:
231
237
  project_filter["teams"] = {"some": {"id": {"eq": team_id}}}
232
-
238
+
233
239
  # State filter
234
240
  if state:
235
241
  project_filter["state"] = {"eq": state}
236
242
  elif not include_completed:
237
243
  # Exclude completed projects by default
238
244
  project_filter["state"] = {"neq": "completed"}
239
-
245
+
240
246
  return project_filter
241
247
 
242
248
 
243
249
  def extract_linear_metadata(issue_data: Dict[str, Any]) -> Dict[str, Any]:
244
250
  """Extract Linear-specific metadata from issue data.
245
-
251
+
246
252
  Args:
247
253
  issue_data: Raw Linear issue data from GraphQL
248
-
254
+
249
255
  Returns:
250
256
  Dictionary of Linear-specific metadata
257
+
251
258
  """
252
259
  metadata = {}
253
-
260
+
254
261
  # Extract Linear-specific fields
255
262
  if "dueDate" in issue_data and issue_data["dueDate"]:
256
263
  metadata["due_date"] = issue_data["dueDate"]
257
-
264
+
258
265
  if "cycle" in issue_data and issue_data["cycle"]:
259
266
  metadata["cycle_id"] = issue_data["cycle"]["id"]
260
267
  metadata["cycle_name"] = issue_data["cycle"]["name"]
261
-
268
+
262
269
  if "estimate" in issue_data and issue_data["estimate"]:
263
270
  metadata["estimate"] = issue_data["estimate"]
264
-
271
+
265
272
  if "branchName" in issue_data and issue_data["branchName"]:
266
273
  metadata["branch_name"] = issue_data["branchName"]
267
-
274
+
268
275
  if "url" in issue_data:
269
276
  metadata["linear_url"] = issue_data["url"]
270
-
277
+
271
278
  if "slaBreachesAt" in issue_data and issue_data["slaBreachesAt"]:
272
279
  metadata["sla_breaches_at"] = issue_data["slaBreachesAt"]
273
-
280
+
274
281
  if "customerTicketCount" in issue_data:
275
282
  metadata["customer_ticket_count"] = issue_data["customerTicketCount"]
276
-
283
+
277
284
  return metadata
@@ -8,8 +8,8 @@ For new code, import directly from the linear package:
8
8
  from mcp_ticketer.adapters.linear import LinearAdapter
9
9
  """
10
10
 
11
- # Import the refactored adapter
12
- from .linear import LinearAdapter
11
+ # Import the refactored adapter from the modular structure
12
+ from .linear.adapter import LinearAdapter
13
13
 
14
14
  # Re-export for backward compatibility
15
15
  __all__ = ["LinearAdapter"]