mcp-ticketer 0.3.5__py3-none-any.whl → 0.3.7__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.

@@ -1,6 +1,6 @@
1
1
  """Version information for mcp-ticketer package."""
2
2
 
3
- __version__ = "0.3.5"
3
+ __version__ = "0.3.7"
4
4
  __version_info__ = tuple(int(part) for part in __version__.split("."))
5
5
 
6
6
  # Package metadata
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
6
+ import logging
5
7
  import os
6
8
  from typing import Any
7
9
 
@@ -205,7 +207,7 @@ class LinearAdapter(BaseAdapter[Task]):
205
207
  )
206
208
 
207
209
  workflow_states = {}
208
- for state in result["workflowStates"]["nodes"]:
210
+ for state in result["team"]["states"]["nodes"]:
209
211
  state_type = state["type"].lower()
210
212
  if state_type not in workflow_states:
211
213
  workflow_states[state_type] = state
@@ -218,12 +220,14 @@ class LinearAdapter(BaseAdapter[Task]):
218
220
  raise ValueError(f"Failed to load workflow states: {e}")
219
221
 
220
222
  async def _load_team_labels(self, team_id: str) -> None:
221
- """Load and cache labels for the team.
223
+ """Load and cache labels for the team with retry logic.
222
224
 
223
225
  Args:
224
226
  team_id: Linear team ID
225
227
 
226
228
  """
229
+ logger = logging.getLogger(__name__)
230
+
227
231
  query = """
228
232
  query GetTeamLabels($teamId: String!) {
229
233
  team(id: $teamId) {
@@ -239,15 +243,32 @@ class LinearAdapter(BaseAdapter[Task]):
239
243
  }
240
244
  """
241
245
 
242
- try:
243
- result = await self.client.execute_query(query, {"teamId": team_id})
244
- self._labels_cache = result["team"]["labels"]["nodes"]
245
- except Exception:
246
- # Log error but don't fail - labels are optional
247
- self._labels_cache = []
246
+ max_retries = 3
247
+ for attempt in range(max_retries):
248
+ try:
249
+ result = await self.client.execute_query(query, {"teamId": team_id})
250
+ labels = result.get("team", {}).get("labels", {}).get("nodes", [])
251
+ self._labels_cache = labels
252
+ logger.info(f"Loaded {len(labels)} labels for team {team_id}")
253
+ return # Success
254
+
255
+ except Exception as e:
256
+ if attempt < max_retries - 1:
257
+ wait_time = 2**attempt
258
+ logger.warning(
259
+ f"Failed to load labels (attempt {attempt + 1}/{max_retries}): {e}. "
260
+ f"Retrying in {wait_time}s..."
261
+ )
262
+ await asyncio.sleep(wait_time)
263
+ else:
264
+ logger.error(
265
+ f"Failed to load team labels after {max_retries} attempts: {e}",
266
+ exc_info=True,
267
+ )
268
+ self._labels_cache = [] # Explicitly empty on failure
248
269
 
249
270
  async def _resolve_label_ids(self, label_names: list[str]) -> list[str]:
250
- """Resolve label names to Linear label IDs.
271
+ """Resolve label names to Linear label IDs with proper None vs empty list handling.
251
272
 
252
273
  Args:
253
274
  label_names: List of label names
@@ -256,16 +277,25 @@ class LinearAdapter(BaseAdapter[Task]):
256
277
  List of Linear label IDs that exist
257
278
 
258
279
  """
259
- import logging
260
-
261
280
  logger = logging.getLogger(__name__)
262
281
 
263
- if not self._labels_cache:
282
+ # None = not loaded yet, [] = loaded but empty or failed
283
+ if self._labels_cache is None:
264
284
  team_id = await self._ensure_team_id()
265
285
  await self._load_team_labels(team_id)
266
286
 
287
+ if self._labels_cache is None:
288
+ # Still None after load attempt - should not happen
289
+ logger.error(
290
+ "Label cache is None after load attempt. Tags will be skipped."
291
+ )
292
+ return []
293
+
267
294
  if not self._labels_cache:
268
- logger.warning("No labels found in team cache")
295
+ # Empty list - either no labels in team or load failed
296
+ logger.warning(
297
+ f"Team has no labels available. Cannot resolve tags: {label_names}"
298
+ )
269
299
  return []
270
300
 
271
301
  # Create name -> ID mapping (case-insensitive)
@@ -835,7 +865,7 @@ class LinearAdapter(BaseAdapter[Task]):
835
865
 
836
866
  comment_input = {
837
867
  "issueId": linear_id,
838
- "body": comment.body,
868
+ "body": comment.content,
839
869
  }
840
870
 
841
871
  result = await self.client.execute_mutation(
@@ -226,13 +226,15 @@ ISSUE_LIST_FRAGMENTS = (
226
226
 
227
227
  WORKFLOW_STATES_QUERY = """
228
228
  query WorkflowStates($teamId: String!) {
229
- workflowStates(filter: { team: { id: { eq: $teamId } } }) {
230
- nodes {
231
- id
232
- name
233
- type
234
- position
235
- color
229
+ team(id: $teamId) {
230
+ states {
231
+ nodes {
232
+ id
233
+ name
234
+ type
235
+ position
236
+ color
237
+ }
236
238
  }
237
239
  }
238
240
  }
@@ -861,6 +861,44 @@ class MCPTicketServer:
861
861
  "required": ["title"],
862
862
  },
863
863
  },
864
+ {
865
+ "name": "ticket_comment",
866
+ "description": "Add or list comments on a ticket",
867
+ "inputSchema": {
868
+ "type": "object",
869
+ "properties": {
870
+ "operation": {
871
+ "type": "string",
872
+ "enum": ["add", "list"],
873
+ "description": "Operation to perform: 'add' to create a comment, 'list' to retrieve comments",
874
+ "default": "add",
875
+ },
876
+ "ticket_id": {
877
+ "type": "string",
878
+ "description": "Ticket ID to comment on",
879
+ },
880
+ "content": {
881
+ "type": "string",
882
+ "description": "Comment content (required for 'add' operation)",
883
+ },
884
+ "author": {
885
+ "type": "string",
886
+ "description": "Comment author (optional for 'add' operation)",
887
+ },
888
+ "limit": {
889
+ "type": "integer",
890
+ "default": 10,
891
+ "description": "Maximum number of comments to return (for 'list' operation)",
892
+ },
893
+ "offset": {
894
+ "type": "integer",
895
+ "default": 0,
896
+ "description": "Number of comments to skip (for 'list' operation)",
897
+ },
898
+ },
899
+ "required": ["ticket_id"],
900
+ },
901
+ },
864
902
  ]
865
903
  }
866
904
 
@@ -913,6 +951,8 @@ class MCPTicketServer:
913
951
  result = await self._handle_transition(arguments)
914
952
  elif tool_name == "ticket_search":
915
953
  result = await self._handle_search(arguments)
954
+ elif tool_name == "ticket_comment":
955
+ result = await self._handle_comment(arguments)
916
956
  # PR integration
917
957
  elif tool_name == "ticket_create_pr":
918
958
  result = await self._handle_create_pr(arguments)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-ticketer
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: Universal ticket management interface for AI agents with MCP support
5
5
  Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
6
6
  Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
@@ -1,5 +1,5 @@
1
1
  mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
2
- mcp_ticketer/__version__.py,sha256=RfLkvzGZCRZI8388D91iayKn1UOAoAbVt1mUwpoGOR8,1117
2
+ mcp_ticketer/__version__.py,sha256=s7BXKaEC9wzrKYxOdqIb1UHlYxMSgLWk-8vzfr8WEIc,1117
3
3
  mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
5
5
  mcp_ticketer/adapters/aitrackdown.py,sha256=Ecw2SQAGVQs5yMH6m2pj61LxCJsuy-g2bvF8uwTpLUE,22588
@@ -8,10 +8,10 @@ mcp_ticketer/adapters/hybrid.py,sha256=UADYZLc_UNw0xHPSbgguBNzvUCnuYn12Qi9ea-zdl
8
8
  mcp_ticketer/adapters/jira.py,sha256=labZFqOy_mmMmizC-RD1EQbu9m4LLtJywwZ956-_x5E,35347
9
9
  mcp_ticketer/adapters/linear.py,sha256=trm6ZhmlUl80sj51WAPAox_R2HQZXZ-h1QXJsrFYDCQ,587
10
10
  mcp_ticketer/adapters/linear/__init__.py,sha256=6l0ZoR6ZHSRcytLfps2AZuk5R189Pq1GfR5-YDQt8-Q,731
11
- mcp_ticketer/adapters/linear/adapter.py,sha256=EvF9FrxMQBVTtO6H3wd9aC8Xv_IeMPt4jM7e2WrsGWU,29610
11
+ mcp_ticketer/adapters/linear/adapter.py,sha256=ZCj6ZM5RiWbdiGrhA3NsMTgFImvrzaB0jBIr_3ZCcO8,30959
12
12
  mcp_ticketer/adapters/linear/client.py,sha256=0UmWlSEcRiwnSMFYKL89KMrPPL8S8uZ5V6rIY_KFOQU,8803
13
13
  mcp_ticketer/adapters/linear/mappers.py,sha256=GN1X7bOcU-5dhDW3dAtSEGivinhFBc8hoKYot8c5tCo,9631
14
- mcp_ticketer/adapters/linear/queries.py,sha256=lT6z64eUjX50Mz00Mk7jkFCRKPM9GptHPelWMj2csXc,7200
14
+ mcp_ticketer/adapters/linear/queries.py,sha256=K8y7xc3iH-q9LEUmg-0YDBhh546LAwLZDvVLkzx3yY4,7223
15
15
  mcp_ticketer/adapters/linear/types.py,sha256=ugXtRGLljDw6yoCnEVgdFs0xLR9ErLdnv4ffh9EAUhk,7874
16
16
  mcp_ticketer/cache/__init__.py,sha256=Xcd-cKnt-Cx7jBzvfzUUUPaGkmyXFi5XUFWw3Z4b7d4,138
17
17
  mcp_ticketer/cache/memory.py,sha256=2yBqGi9i0SanlUhJoOC7nijWjoMa3_ntPe-V-AV-LfU,5042
@@ -45,7 +45,7 @@ mcp_ticketer/mcp/__init__.py,sha256=Y05eTzsPk0wH8yKNIM-ekpGjgSDO0bQr0EME-vOP4GE,
45
45
  mcp_ticketer/mcp/constants.py,sha256=EBGsJtBPaTCvAm5rOMknckrXActrNIls7lRklnh1L4s,2072
46
46
  mcp_ticketer/mcp/dto.py,sha256=fUNAdCnPNp80s6RYLFqSmgqQZX04BHYry4GArmFkdG0,7336
47
47
  mcp_ticketer/mcp/response_builder.py,sha256=sEYiwQddlfQmIOcbQ-yBsDvH1EJfbTDwCEUJNf7q5Vk,4946
48
- mcp_ticketer/mcp/server.py,sha256=l9DQ4e9pzdIyIS3Y-gKQwzOzBiVxDi0YJO0U6EeCz5M,46825
48
+ mcp_ticketer/mcp/server.py,sha256=SZ8lHdXJJAfV3jVRiZcR2uNYpHY-Lcbt2xXy_Tc728E,48848
49
49
  mcp_ticketer/queue/__init__.py,sha256=1YIaCpZpFqPcqvDEQXiEvDLiw94DXRdCJkBaVIFQrms,231
50
50
  mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
51
51
  mcp_ticketer/queue/health_monitor.py,sha256=KFOzksomUFnS94XKBiuHFPmGK6b4QXWzsrjwhHkR9vI,12245
@@ -54,9 +54,9 @@ mcp_ticketer/queue/queue.py,sha256=PIB_8gOE4rCb5_tBNKw9qD6YhSgH3Ei3IzVrUSY3F_o,1
54
54
  mcp_ticketer/queue/run_worker.py,sha256=WhoeamL8LKZ66TM8W1PkMPwjF2w_EDFMP-mevs6C1TM,1019
55
55
  mcp_ticketer/queue/ticket_registry.py,sha256=FE6W_D8NA-66cJQ6VqghChF3JasYW845JVfEZdiqLbA,15449
56
56
  mcp_ticketer/queue/worker.py,sha256=AF6W1bdxWnHiJd6-iBWqTHkZ4lFflsS65CAtgFPR0FA,20983
57
- mcp_ticketer-0.3.5.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
58
- mcp_ticketer-0.3.5.dist-info/METADATA,sha256=P_TStpQVYOyHeSxjHnvIQ7WUP3jTbwBDnlL0DmwrORw,13219
59
- mcp_ticketer-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
- mcp_ticketer-0.3.5.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
61
- mcp_ticketer-0.3.5.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
62
- mcp_ticketer-0.3.5.dist-info/RECORD,,
57
+ mcp_ticketer-0.3.7.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
58
+ mcp_ticketer-0.3.7.dist-info/METADATA,sha256=ivW854H301BPY_YzUtzYhhhQ15sQKF_u-owFjOq6e24,13219
59
+ mcp_ticketer-0.3.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
+ mcp_ticketer-0.3.7.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
61
+ mcp_ticketer-0.3.7.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
62
+ mcp_ticketer-0.3.7.dist-info/RECORD,,