mcp-ticketer 0.1.17__py3-none-any.whl → 0.1.20__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.1.17"
3
+ __version__ = "0.1.20"
4
4
  __version_info__ = tuple(int(part) for part in __version__.split("."))
5
5
 
6
6
  # Package metadata
@@ -277,8 +277,11 @@ class LinearAdapter(BaseAdapter[Task]):
277
277
  config: Configuration with:
278
278
  - api_key: Linear API key (or LINEAR_API_KEY env var)
279
279
  - workspace: Linear workspace name (optional, for documentation)
280
- - team_key: Linear team key (required, e.g., 'BTA')
280
+ - team_key: Linear team key (e.g., 'BTA') OR
281
+ - team_id: Linear team UUID (e.g., '02d15669-7351-4451-9719-807576c16049')
281
282
  - api_url: Optional Linear API URL
283
+
284
+ Note: Either team_key or team_id is required. If both are provided, team_id takes precedence.
282
285
  """
283
286
  super().__init__(config)
284
287
 
@@ -288,18 +291,16 @@ class LinearAdapter(BaseAdapter[Task]):
288
291
  raise ValueError("Linear API key required (config.api_key or LINEAR_API_KEY env var)")
289
292
 
290
293
  self.workspace = config.get("workspace") # Optional, for documentation
291
- self.team_key = config.get("team_key")
292
- if not self.team_key:
293
- raise ValueError("Linear team_key is required in configuration")
294
- self.api_url = config.get("api_url", "https://api.linear.app/graphql")
295
294
 
296
- # Setup GraphQL client with authentication
297
- transport = HTTPXAsyncTransport(
298
- url=self.api_url,
299
- headers={"Authorization": self.api_key},
300
- timeout=30.0,
301
- )
302
- self.client = Client(transport=transport, fetch_schema_from_transport=False)
295
+ # Support both team_key (short key) and team_id (UUID)
296
+ self.team_key = config.get("team_key") # Short key like "BTA"
297
+ self.team_id_config = config.get("team_id") # UUID like "02d15669-..."
298
+
299
+ # Require at least one team identifier
300
+ if not self.team_key and not self.team_id_config:
301
+ raise ValueError("Either team_key or team_id is required in configuration")
302
+
303
+ self.api_url = config.get("api_url", "https://api.linear.app/graphql")
303
304
 
304
305
  # Caches for frequently used data
305
306
  self._team_id: Optional[str] = None
@@ -314,6 +315,22 @@ class LinearAdapter(BaseAdapter[Task]):
314
315
  self._init_lock = asyncio.Lock()
315
316
  self._initialized = False
316
317
 
318
+ def _create_client(self) -> Client:
319
+ """Create a fresh GraphQL client for each operation.
320
+
321
+ This prevents 'Transport is already connected' errors by ensuring
322
+ each operation gets its own client and transport instance.
323
+
324
+ Returns:
325
+ Client: Fresh GraphQL client instance
326
+ """
327
+ transport = HTTPXAsyncTransport(
328
+ url=self.api_url,
329
+ headers={"Authorization": self.api_key},
330
+ timeout=30.0,
331
+ )
332
+ return Client(transport=transport, fetch_schema_from_transport=False)
333
+
317
334
  async def initialize(self) -> None:
318
335
  """Initialize adapter by preloading team, states, and labels data concurrently."""
319
336
  if self._initialized:
@@ -347,9 +364,36 @@ class LinearAdapter(BaseAdapter[Task]):
347
364
  raise e
348
365
 
349
366
  async def _fetch_team_data(self) -> str:
350
- """Fetch team ID."""
367
+ """Fetch team ID.
368
+
369
+ If team_id is configured, validate it exists and return it.
370
+ If team_key is configured, fetch the team_id by key.
371
+ """
372
+ # If team_id (UUID) is provided, use it directly (preferred)
373
+ if self.team_id_config:
374
+ # Validate that this team ID exists
375
+ query = gql("""
376
+ query GetTeamById($id: String!) {
377
+ team(id: $id) {
378
+ id
379
+ name
380
+ key
381
+ }
382
+ }
383
+ """)
384
+
385
+ client = self._create_client()
386
+ async with client as session:
387
+ result = await session.execute(query, variable_values={"id": self.team_id_config})
388
+
389
+ if not result.get("team"):
390
+ raise ValueError(f"Team with ID '{self.team_id_config}' not found")
391
+
392
+ return result["team"]["id"]
393
+
394
+ # Otherwise, fetch team ID by key
351
395
  query = gql("""
352
- query GetTeam($key: String!) {
396
+ query GetTeamByKey($key: String!) {
353
397
  teams(filter: { key: { eq: $key } }) {
354
398
  nodes {
355
399
  id
@@ -360,7 +404,8 @@ class LinearAdapter(BaseAdapter[Task]):
360
404
  }
361
405
  """)
362
406
 
363
- async with self.client as session:
407
+ client = self._create_client()
408
+ async with client as session:
364
409
  result = await session.execute(query, variable_values={"key": self.team_key})
365
410
 
366
411
  if not result["teams"]["nodes"]:
@@ -384,7 +429,8 @@ class LinearAdapter(BaseAdapter[Task]):
384
429
  }
385
430
  """)
386
431
 
387
- async with self.client as session:
432
+ client = self._create_client()
433
+ async with client as session:
388
434
  result = await session.execute(query, variable_values={"teamId": team_id})
389
435
 
390
436
  workflow_states = {}
@@ -410,7 +456,8 @@ class LinearAdapter(BaseAdapter[Task]):
410
456
  }
411
457
  """)
412
458
 
413
- async with self.client as session:
459
+ client = self._create_client()
460
+ async with client as session:
414
461
  result = await session.execute(query, variable_values={"teamId": team_id})
415
462
 
416
463
  return {label["name"]: label["id"] for label in result["issueLabels"]["nodes"]}
@@ -451,7 +498,8 @@ class LinearAdapter(BaseAdapter[Task]):
451
498
  }
452
499
  """)
453
500
 
454
- async with self.client as session:
501
+ client = self._create_client()
502
+ async with client as session:
455
503
  result = await session.execute(
456
504
  search_query,
457
505
  variable_values={"name": name, "teamId": team_id}
@@ -481,7 +529,8 @@ class LinearAdapter(BaseAdapter[Task]):
481
529
  if color:
482
530
  label_input["color"] = color
483
531
 
484
- async with self.client as session:
532
+ client = self._create_client()
533
+ async with client as session:
485
534
  result = await session.execute(
486
535
  create_query,
487
536
  variable_values={"input": label_input}
@@ -510,7 +559,8 @@ class LinearAdapter(BaseAdapter[Task]):
510
559
  }
511
560
  """)
512
561
 
513
- async with self.client as session:
562
+ client = self._create_client()
563
+ async with client as session:
514
564
  result = await session.execute(query, variable_values={"email": email})
515
565
 
516
566
  if result["users"]["nodes"]:
@@ -528,8 +578,8 @@ class LinearAdapter(BaseAdapter[Task]):
528
578
  """
529
579
  if not self.api_key:
530
580
  return False, "LINEAR_API_KEY is required but not found. Set it in .env.local or environment."
531
- if not self.team_key:
532
- return False, "Linear team_key is required in configuration. Set it in .mcp-ticketer/config.json"
581
+ if not self.team_key and not self.team_id_config:
582
+ return False, "Either Linear team_key or team_id is required in configuration. Set it in .mcp-ticketer/config.json"
533
583
  return True, ""
534
584
 
535
585
  def _get_state_mapping(self) -> Dict[TicketState, str]:
@@ -792,7 +842,8 @@ class LinearAdapter(BaseAdapter[Task]):
792
842
  }
793
843
  }
794
844
  """)
795
- async with self.client as session:
845
+ client = self._create_client()
846
+ async with client as session:
796
847
  parent_result = await session.execute(
797
848
  parent_query,
798
849
  variable_values={"identifier": ticket.parent_issue}
@@ -824,7 +875,8 @@ class LinearAdapter(BaseAdapter[Task]):
824
875
  }
825
876
  """)
826
877
 
827
- async with self.client as session:
878
+ client = self._create_client()
879
+ async with client as session:
828
880
  result = await session.execute(
829
881
  create_query,
830
882
  variable_values={"input": issue_input}
@@ -852,7 +904,8 @@ class LinearAdapter(BaseAdapter[Task]):
852
904
  """)
853
905
 
854
906
  try:
855
- async with self.client as session:
907
+ client = self._create_client()
908
+ async with client as session:
856
909
  result = await session.execute(
857
910
  query,
858
911
  variable_values={"identifier": ticket_id}
@@ -882,7 +935,8 @@ class LinearAdapter(BaseAdapter[Task]):
882
935
  }
883
936
  """)
884
937
 
885
- async with self.client as session:
938
+ client = self._create_client()
939
+ async with client as session:
886
940
  result = await session.execute(
887
941
  query,
888
942
  variable_values={"identifier": ticket_id}
@@ -958,7 +1012,8 @@ class LinearAdapter(BaseAdapter[Task]):
958
1012
  }
959
1013
  """)
960
1014
 
961
- async with self.client as session:
1015
+ client = self._create_client()
1016
+ async with client as session:
962
1017
  result = await session.execute(
963
1018
  update_query,
964
1019
  variable_values={"id": linear_id, "input": update_input}
@@ -985,7 +1040,8 @@ class LinearAdapter(BaseAdapter[Task]):
985
1040
  }
986
1041
  """)
987
1042
 
988
- async with self.client as session:
1043
+ client = self._create_client()
1044
+ async with client as session:
989
1045
  result = await session.execute(
990
1046
  query,
991
1047
  variable_values={"identifier": ticket_id}
@@ -1005,7 +1061,8 @@ class LinearAdapter(BaseAdapter[Task]):
1005
1061
  }
1006
1062
  """)
1007
1063
 
1008
- async with self.client as session:
1064
+ client = self._create_client()
1065
+ async with client as session:
1009
1066
  result = await session.execute(
1010
1067
  archive_query,
1011
1068
  variable_values={"id": linear_id}
@@ -1101,7 +1158,8 @@ class LinearAdapter(BaseAdapter[Task]):
1101
1158
  }
1102
1159
  """)
1103
1160
 
1104
- async with self.client as session:
1161
+ client = self._create_client()
1162
+ async with client as session:
1105
1163
  result = await session.execute(
1106
1164
  query,
1107
1165
  variable_values={
@@ -1175,7 +1233,8 @@ class LinearAdapter(BaseAdapter[Task]):
1175
1233
  }
1176
1234
  """)
1177
1235
 
1178
- async with self.client as session:
1236
+ client = self._create_client()
1237
+ async with client as session:
1179
1238
  result = await session.execute(
1180
1239
  search_query,
1181
1240
  variable_values={
@@ -1215,7 +1274,8 @@ class LinearAdapter(BaseAdapter[Task]):
1215
1274
  }
1216
1275
  """)
1217
1276
 
1218
- async with self.client as session:
1277
+ client = self._create_client()
1278
+ async with client as session:
1219
1279
  result = await session.execute(
1220
1280
  query,
1221
1281
  variable_values={"identifier": comment.ticket_id}
@@ -1247,7 +1307,8 @@ class LinearAdapter(BaseAdapter[Task]):
1247
1307
  if comment.metadata and "parent_comment_id" in comment.metadata:
1248
1308
  comment_input["parentId"] = comment.metadata["parent_comment_id"]
1249
1309
 
1250
- async with self.client as session:
1310
+ client = self._create_client()
1311
+ async with client as session:
1251
1312
  result = await session.execute(
1252
1313
  create_comment_query,
1253
1314
  variable_values={"input": comment_input}
@@ -1292,7 +1353,8 @@ class LinearAdapter(BaseAdapter[Task]):
1292
1353
  """)
1293
1354
 
1294
1355
  try:
1295
- async with self.client as session:
1356
+ client = self._create_client()
1357
+ async with client as session:
1296
1358
  result = await session.execute(
1297
1359
  query,
1298
1360
  variable_values={
@@ -1348,7 +1410,8 @@ class LinearAdapter(BaseAdapter[Task]):
1348
1410
  if description:
1349
1411
  project_input["description"] = description
1350
1412
 
1351
- async with self.client as session:
1413
+ client = self._create_client()
1414
+ async with client as session:
1352
1415
  result = await session.execute(
1353
1416
  create_query,
1354
1417
  variable_values={"input": project_input}
@@ -1389,7 +1452,8 @@ class LinearAdapter(BaseAdapter[Task]):
1389
1452
  }
1390
1453
  """)
1391
1454
 
1392
- async with self.client as session:
1455
+ client = self._create_client()
1456
+ async with client as session:
1393
1457
  result = await session.execute(
1394
1458
  query,
1395
1459
  variable_values={"filter": cycle_filter}
@@ -1424,7 +1488,8 @@ class LinearAdapter(BaseAdapter[Task]):
1424
1488
  }
1425
1489
  """)
1426
1490
 
1427
- async with self.client as session:
1491
+ client = self._create_client()
1492
+ async with client as session:
1428
1493
  result = await session.execute(
1429
1494
  create_query,
1430
1495
  variable_values={
@@ -1502,7 +1567,8 @@ class LinearAdapter(BaseAdapter[Task]):
1502
1567
  },
1503
1568
  }
1504
1569
 
1505
- async with self.client as session:
1570
+ client = self._create_client()
1571
+ async with client as session:
1506
1572
  result = await session.execute(
1507
1573
  create_query,
1508
1574
  variable_values={"input": attachment_input}
@@ -1601,7 +1667,8 @@ class LinearAdapter(BaseAdapter[Task]):
1601
1667
  raise ValueError(f"Could not find Linear ID for issue {ticket_id}")
1602
1668
  linear_id = search_result["id"]
1603
1669
 
1604
- async with self.client as session:
1670
+ client = self._create_client()
1671
+ async with client as session:
1605
1672
  result = await session.execute(
1606
1673
  update_query,
1607
1674
  variable_values={
@@ -1648,7 +1715,8 @@ class LinearAdapter(BaseAdapter[Task]):
1648
1715
  """)
1649
1716
 
1650
1717
  try:
1651
- async with self.client as session:
1718
+ client = self._create_client()
1719
+ async with client as session:
1652
1720
  result = await session.execute(
1653
1721
  search_query,
1654
1722
  variable_values={"identifier": identifier}
@@ -1701,7 +1769,8 @@ class LinearAdapter(BaseAdapter[Task]):
1701
1769
  if "lead_id" in kwargs:
1702
1770
  project_input["leadId"] = kwargs["lead_id"]
1703
1771
 
1704
- async with self.client as session:
1772
+ client = self._create_client()
1773
+ async with client as session:
1705
1774
  result = await session.execute(
1706
1775
  create_query,
1707
1776
  variable_values={"input": project_input}
@@ -1731,7 +1800,8 @@ class LinearAdapter(BaseAdapter[Task]):
1731
1800
  """)
1732
1801
 
1733
1802
  try:
1734
- async with self.client as session:
1803
+ client = self._create_client()
1804
+ async with client as session:
1735
1805
  result = await session.execute(
1736
1806
  query,
1737
1807
  variable_values={"id": epic_id}
@@ -1780,7 +1850,8 @@ class LinearAdapter(BaseAdapter[Task]):
1780
1850
  }
1781
1851
  """)
1782
1852
 
1783
- async with self.client as session:
1853
+ client = self._create_client()
1854
+ async with client as session:
1784
1855
  result = await session.execute(
1785
1856
  query,
1786
1857
  variable_values={
@@ -1847,7 +1918,8 @@ class LinearAdapter(BaseAdapter[Task]):
1847
1918
  """)
1848
1919
 
1849
1920
  try:
1850
- async with self.client as session:
1921
+ client = self._create_client()
1922
+ async with client as session:
1851
1923
  result = await session.execute(
1852
1924
  query,
1853
1925
  variable_values={"projectId": epic_id, "first": 100}
@@ -1900,7 +1972,8 @@ class LinearAdapter(BaseAdapter[Task]):
1900
1972
  }
1901
1973
  """)
1902
1974
 
1903
- async with self.client as session:
1975
+ client = self._create_client()
1976
+ async with client as session:
1904
1977
  parent_result = await session.execute(
1905
1978
  parent_query,
1906
1979
  variable_values={"identifier": parent_id}
@@ -1965,7 +2038,8 @@ class LinearAdapter(BaseAdapter[Task]):
1965
2038
  }
1966
2039
  """)
1967
2040
 
1968
- async with self.client as session:
2041
+ client = self._create_client()
2042
+ async with client as session:
1969
2043
  result = await session.execute(
1970
2044
  create_query,
1971
2045
  variable_values={"input": issue_input}
@@ -1999,7 +2073,8 @@ class LinearAdapter(BaseAdapter[Task]):
1999
2073
  """)
2000
2074
 
2001
2075
  try:
2002
- async with self.client as session:
2076
+ client = self._create_client()
2077
+ async with client as session:
2003
2078
  result = await session.execute(
2004
2079
  query,
2005
2080
  variable_values={"identifier": issue_id}
@@ -2020,11 +2095,13 @@ class LinearAdapter(BaseAdapter[Task]):
2020
2095
  return []
2021
2096
 
2022
2097
  async def close(self) -> None:
2023
- """Close the GraphQL client connection."""
2024
- if hasattr(self.client, 'close_async'):
2025
- await self.client.close_async()
2026
- elif hasattr(self.client.transport, 'close'):
2027
- await self.client.transport.close()
2098
+ """Close the GraphQL client connection.
2099
+
2100
+ Since we create fresh clients for each operation, there's no persistent
2101
+ connection to close. Each client's transport is automatically closed when
2102
+ the async context manager exits.
2103
+ """
2104
+ pass
2028
2105
 
2029
2106
 
2030
2107
  # Register the adapter
mcp_ticketer/cli/main.py CHANGED
@@ -23,9 +23,18 @@ from .configure import configure_wizard, show_current_config, set_adapter_config
23
23
  from .migrate_config import migrate_config_command
24
24
  from .discover import app as discover_app
25
25
 
26
- # Load environment variables
26
+ # Load environment variables from .env files
27
+ # Priority: .env.local (highest) > .env (base)
28
+ # This matches the pattern used in worker.py and server.py
29
+
30
+ # Load .env first (base configuration)
27
31
  load_dotenv()
28
32
 
33
+ # Load .env.local with override=True (project-specific overrides)
34
+ env_local = Path.cwd() / ".env.local"
35
+ if env_local.exists():
36
+ load_dotenv(env_local, override=True)
37
+
29
38
  app = typer.Typer(
30
39
  name="mcp-ticketer",
31
40
  help="Universal ticket management interface",
@@ -70,18 +79,24 @@ class AdapterType(str, Enum):
70
79
  GITHUB = "github"
71
80
 
72
81
 
73
- def load_config() -> dict:
82
+ def load_config(project_dir: Optional[Path] = None) -> dict:
74
83
  """Load configuration from file.
75
84
 
85
+ Args:
86
+ project_dir: Optional project directory to load config from
87
+
76
88
  Resolution order:
77
- 1. Project-specific config (.mcp-ticketer/config.json in cwd)
89
+ 1. Project-specific config (.mcp-ticketer/config.json in project_dir or cwd)
78
90
  2. Global config (~/.mcp-ticketer/config.json)
79
91
 
80
92
  Returns:
81
93
  Configuration dictionary
82
94
  """
95
+ # Use provided project_dir or current working directory
96
+ base_dir = project_dir or Path.cwd()
97
+
83
98
  # Check project-specific config first
84
- project_config = Path.cwd() / ".mcp-ticketer" / "config.json"
99
+ project_config = base_dir / ".mcp-ticketer" / "config.json"
85
100
  if project_config.exists():
86
101
  try:
87
102
  with open(project_config, "r") as f:
@@ -211,9 +211,9 @@ class ConfigValidator:
211
211
  if field not in config or not config[field]:
212
212
  return False, f"Linear config missing required field: {field}"
213
213
 
214
- # Warn if team_id is missing but don't fail
215
- if not config.get("team_id"):
216
- logger.warning("Linear config missing team_id - may be required for some operations")
214
+ # Require either team_key or team_id (team_id is preferred)
215
+ if not config.get("team_key") and not config.get("team_id"):
216
+ return False, "Linear config requires either team_key (short key like 'BTA') or team_id (UUID)"
217
217
 
218
218
  return True, None
219
219
 
@@ -32,6 +32,7 @@ class QueueItem:
32
32
  error_message: Optional[str] = None
33
33
  retry_count: int = 0
34
34
  result: Optional[Dict[str, Any]] = None
35
+ project_dir: Optional[str] = None
35
36
 
36
37
  def to_dict(self) -> dict:
37
38
  """Convert to dictionary for storage."""
@@ -54,7 +55,8 @@ class QueueItem:
54
55
  processed_at=datetime.fromisoformat(row[6]) if row[6] else None,
55
56
  error_message=row[7],
56
57
  retry_count=row[8],
57
- result=json.loads(row[9]) if row[9] else None
58
+ result=json.loads(row[9]) if row[9] else None,
59
+ project_dir=row[10] if len(row) > 10 else None
58
60
  )
59
61
 
60
62
 
@@ -109,31 +111,43 @@ class Queue:
109
111
  ON queue(adapter)
110
112
  ''')
111
113
 
114
+ # Migration: Add project_dir column if it doesn't exist
115
+ cursor = conn.execute("PRAGMA table_info(queue)")
116
+ columns = [row[1] for row in cursor.fetchall()]
117
+ if 'project_dir' not in columns:
118
+ conn.execute('ALTER TABLE queue ADD COLUMN project_dir TEXT')
119
+
112
120
  conn.commit()
113
121
 
114
122
  def add(self,
115
123
  ticket_data: Dict[str, Any],
116
124
  adapter: str,
117
- operation: str) -> str:
125
+ operation: str,
126
+ project_dir: Optional[str] = None) -> str:
118
127
  """Add item to queue.
119
128
 
120
129
  Args:
121
130
  ticket_data: The ticket data for the operation
122
131
  adapter: Name of the adapter to use
123
132
  operation: Operation to perform (create, update, delete, etc.)
133
+ project_dir: Project directory for config resolution (defaults to current directory)
124
134
 
125
135
  Returns:
126
136
  Queue ID for tracking
127
137
  """
128
138
  queue_id = f"Q-{uuid.uuid4().hex[:8].upper()}"
129
139
 
140
+ # Default to current working directory if not provided
141
+ if project_dir is None:
142
+ project_dir = str(Path.cwd())
143
+
130
144
  with self._lock:
131
145
  with sqlite3.connect(self.db_path) as conn:
132
146
  conn.execute('''
133
147
  INSERT INTO queue (
134
148
  id, ticket_data, adapter, operation,
135
- status, created_at, retry_count
136
- ) VALUES (?, ?, ?, ?, ?, ?, ?)
149
+ status, created_at, retry_count, project_dir
150
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
137
151
  ''', (
138
152
  queue_id,
139
153
  json.dumps(ticket_data),
@@ -141,7 +155,8 @@ class Queue:
141
155
  operation,
142
156
  QueueStatus.PENDING.value,
143
157
  datetime.now().isoformat(),
144
- 0
158
+ 0,
159
+ project_dir
145
160
  ))
146
161
  conn.commit()
147
162
 
@@ -303,15 +303,26 @@ class Worker:
303
303
  Returns:
304
304
  Adapter instance
305
305
  """
306
- # Load configuration
306
+ # Load configuration from the project directory where the item was created
307
307
  from ..cli.main import load_config
308
+ from pathlib import Path
309
+ import os
310
+
311
+ # Use item's project_dir if available, otherwise use current directory
312
+ project_path = Path(item.project_dir) if item.project_dir else None
308
313
 
309
- config = load_config()
314
+ # Load environment variables from project directory's .env.local if it exists
315
+ if project_path:
316
+ env_file = project_path / ".env.local"
317
+ if env_file.exists():
318
+ logger.debug(f"Loading environment from {env_file}")
319
+ load_dotenv(env_file)
320
+
321
+ config = load_config(project_dir=project_path)
310
322
  adapters_config = config.get("adapters", {})
311
323
  adapter_config = adapters_config.get(item.adapter, {})
312
324
 
313
325
  # Add environment variables for authentication
314
- import os
315
326
  if item.adapter == "linear":
316
327
  if not adapter_config.get("api_key"):
317
328
  adapter_config["api_key"] = os.getenv("LINEAR_API_KEY")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-ticketer
3
- Version: 0.1.17
3
+ Version: 0.1.20
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,18 +1,18 @@
1
1
  mcp_ticketer/__init__.py,sha256=ayPQdFr6msypD06_G96a1H0bdFCT1m1wDtv8MZBpY4I,496
2
- mcp_ticketer/__version__.py,sha256=WgT2pB0LHF_awU8L3zAq7pQyDkXi1wLI6eY0gHjmjUw,1115
2
+ mcp_ticketer/__version__.py,sha256=xOyZKZS-YkKJVpGRqRAWelt12iv3Z2x09GFOZ4CXK0c,1115
3
3
  mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcp_ticketer/adapters/__init__.py,sha256=K_1egvhHb5F_7yFceUx2YzPGEoc7vX-q8dMVaS4K6gw,356
5
5
  mcp_ticketer/adapters/aitrackdown.py,sha256=916SpzQcG6gSdQit5Ptm9AdGOsZFXpt9nNWlRjCReMY,15924
6
6
  mcp_ticketer/adapters/github.py,sha256=NdUPaSlOEi4zZN_VBvAjSJANJhp1IBwdOkkF6fGbaKs,45410
7
7
  mcp_ticketer/adapters/hybrid.py,sha256=H9B-pfWmDKXO3GgzxB8undEcZTMzLz_1a6zWhj7xfR0,18556
8
8
  mcp_ticketer/adapters/jira.py,sha256=jxoQS22wjOl1FhsYiGK-r1pLXOenUmbe5wa0ehD6xDg,30373
9
- mcp_ticketer/adapters/linear.py,sha256=VmZ9UI5cDvbsiFgWEopnUTwWGcHZDeGtsTSnR_Z5_BA,66506
9
+ mcp_ticketer/adapters/linear.py,sha256=ewGTpvJmyTBJd73PPQBM6ijwVsacXZ4oRAigh_RAdAA,69315
10
10
  mcp_ticketer/cache/__init__.py,sha256=MSi3GLXancfP2-edPC9TFAJk7r0j6H5-XmpMHnkGPbI,137
11
11
  mcp_ticketer/cache/memory.py,sha256=gTzv-xF7qGfiYVUjG7lnzo0ZcqgXQajMl4NAYUcaytg,5133
12
12
  mcp_ticketer/cli/__init__.py,sha256=YeljyLtv906TqkvRuEPhmKO-Uk0CberQ9I6kx1tx2UA,88
13
13
  mcp_ticketer/cli/configure.py,sha256=etFutvc0QpaVDMOsZiiN7wKuaT98Od1Tj9W6lsEWw5A,16351
14
14
  mcp_ticketer/cli/discover.py,sha256=putWrGcctUH8K0fOMtr9MZA9VnWoXzbtoe7mXSkDxhg,13156
15
- mcp_ticketer/cli/main.py,sha256=WGYXZ0r0Iv296qrCMZLPGHUAWagRhkAgqAm-lJSnyUY,40428
15
+ mcp_ticketer/cli/main.py,sha256=98lIVFsSPIig5Oz7g9CxyA-u6EIiHT6Ernj7su7NMc8,40988
16
16
  mcp_ticketer/cli/mcp_configure.py,sha256=1WbBdF25OvxfAGcjxtYa9xmBOGEPQu-wh_nkefmWjMQ,9352
17
17
  mcp_ticketer/cli/migrate_config.py,sha256=iZIstnlr9vkhiW_MlnSyJOkMi4KHQqrZ6Hz1ECD_VUk,6045
18
18
  mcp_ticketer/cli/queue_commands.py,sha256=f3pEHKZ43dBHEIoCBvdfvjfMB9_WJltps9ATwTzorY0,8160
@@ -24,19 +24,19 @@ mcp_ticketer/core/env_discovery.py,sha256=SPoyq_y5j-3gJG5gYNVjCIIrbdzimOdDbTYySm
24
24
  mcp_ticketer/core/http_client.py,sha256=RM9CEMNcuRb-FxhAijmM_FeBMgxgh1OII9HIPBdJue0,13855
25
25
  mcp_ticketer/core/mappers.py,sha256=8I4jcqDqoQEdWlteDMpVeVF3Wo0fDCkmFPRr8oNv8gA,16933
26
26
  mcp_ticketer/core/models.py,sha256=GhuTitY6t_QlqfEUvWT6Q2zvY7qAtx_SQyCMMn8iYkk,6473
27
- mcp_ticketer/core/project_config.py,sha256=VVSeCwuESuemL-iC4fqbPrJxR4i5k5fhUpujnY7MCZA,22389
27
+ mcp_ticketer/core/project_config.py,sha256=dc_sGE6ds_WYBdwY2s6onWP07umTQ_TZBRmIL_ZrMpI,22446
28
28
  mcp_ticketer/core/registry.py,sha256=fwje0fnjp0YKPZ0SrVWk82SMNLs7CD0JlHQmx7SigNo,3537
29
29
  mcp_ticketer/mcp/__init__.py,sha256=Bvzof9vBu6VwcXcIZK8RgKv6ycRV9tDlO-9TUmd8zqQ,122
30
30
  mcp_ticketer/mcp/server.py,sha256=CC1iaeugUbiVrNvNgOgm2mRb4AW-5e0X2ygLjH8I6mM,34835
31
31
  mcp_ticketer/queue/__init__.py,sha256=xHBoUwor8ZdO8bIHc7nP25EsAp5Si5Co4g_8ybb7fes,230
32
32
  mcp_ticketer/queue/__main__.py,sha256=kQd6iOCKbbFqpRdbIRavuI4_G7-oE898JE4a0yLEYPE,108
33
33
  mcp_ticketer/queue/manager.py,sha256=79AH9oUxdBXH3lmJ3kIlFf2GQkWHL6XB6u5JqVWPq60,7571
34
- mcp_ticketer/queue/queue.py,sha256=z4aivQCtsH5_OUr2OfXSfnFKzugTahNnwHw0LS3ZhZc,11549
34
+ mcp_ticketer/queue/queue.py,sha256=mXCUwlayqEHDB6IN8RvxSQpVdKcrlSww40VS_4i9lj8,12292
35
35
  mcp_ticketer/queue/run_worker.py,sha256=HFoykfDpOoz8OUxWbQ2Fka_UlGrYwjPVZ-DEimGFH9o,802
36
- mcp_ticketer/queue/worker.py,sha256=cVjHR_kfnGKAkiUg0HuXCnbKeKNBBEuj0XZHgIuIn4k,14017
37
- mcp_ticketer-0.1.17.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
38
- mcp_ticketer-0.1.17.dist-info/METADATA,sha256=Mcy0c4cYLlswlEJbYolMAdS4shFf27maFmydb2LtnaA,11211
39
- mcp_ticketer-0.1.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- mcp_ticketer-0.1.17.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
41
- mcp_ticketer-0.1.17.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
42
- mcp_ticketer-0.1.17.dist-info/RECORD,,
36
+ mcp_ticketer/queue/worker.py,sha256=h0Y3l51Ld5zpWd-HoQ3sPlgcGRJM1kiId3aKSqJSars,14588
37
+ mcp_ticketer-0.1.20.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
38
+ mcp_ticketer-0.1.20.dist-info/METADATA,sha256=i1en-2GscVd8PrEFgrhBRAWwUhoGFi1dFd5end_chWU,11211
39
+ mcp_ticketer-0.1.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ mcp_ticketer-0.1.20.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
41
+ mcp_ticketer-0.1.20.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
42
+ mcp_ticketer-0.1.20.dist-info/RECORD,,