mcp-ticketer 0.4.5__py3-none-any.whl → 0.4.9__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 (32) hide show
  1. mcp_ticketer/__version__.py +1 -1
  2. mcp_ticketer/adapters/aitrackdown.py +2 -2
  3. mcp_ticketer/adapters/linear/adapter.py +126 -4
  4. mcp_ticketer/cli/auggie_configure.py +9 -4
  5. mcp_ticketer/cli/codex_configure.py +9 -5
  6. mcp_ticketer/cli/gemini_configure.py +8 -4
  7. mcp_ticketer/cli/main.py +24 -4
  8. mcp_ticketer/cli/mcp_configure.py +7 -7
  9. mcp_ticketer/mcp/__init__.py +3 -3
  10. mcp_ticketer/mcp/server/__init__.py +21 -0
  11. mcp_ticketer/mcp/server/__main__.py +60 -0
  12. mcp_ticketer/mcp/{server.py → server/main.py} +2 -2
  13. mcp_ticketer/mcp/{server_sdk.py → server/server_sdk.py} +2 -2
  14. mcp_ticketer/mcp/{tools → server/tools}/attachment_tools.py +1 -1
  15. mcp_ticketer/mcp/{tools → server/tools}/bulk_tools.py +1 -1
  16. mcp_ticketer/mcp/{tools → server/tools}/comment_tools.py +1 -1
  17. mcp_ticketer/mcp/{tools → server/tools}/hierarchy_tools.py +1 -1
  18. mcp_ticketer/mcp/{tools → server/tools}/pr_tools.py +1 -1
  19. mcp_ticketer/mcp/{tools → server/tools}/search_tools.py +1 -1
  20. mcp_ticketer/mcp/{tools → server/tools}/ticket_tools.py +1 -1
  21. mcp_ticketer/queue/__init__.py +2 -3
  22. mcp_ticketer/queue/manager.py +1 -1
  23. {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/METADATA +1 -1
  24. {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/RECORD +32 -30
  25. /mcp_ticketer/mcp/{constants.py → server/constants.py} +0 -0
  26. /mcp_ticketer/mcp/{dto.py → server/dto.py} +0 -0
  27. /mcp_ticketer/mcp/{response_builder.py → server/response_builder.py} +0 -0
  28. /mcp_ticketer/mcp/{tools → server/tools}/__init__.py +0 -0
  29. {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/WHEEL +0 -0
  30. {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/entry_points.txt +0 -0
  31. {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/licenses/LICENSE +0 -0
  32. {mcp_ticketer-0.4.5.dist-info → mcp_ticketer-0.4.9.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  """Version information for mcp-ticketer package."""
2
2
 
3
- __version__ = "0.4.5"
3
+ __version__ = "0.4.9"
4
4
  __version_info__ = tuple(int(part) for part in __version__.split("."))
5
5
 
6
6
  # Package metadata
@@ -8,8 +8,6 @@ from pathlib import Path
8
8
  from typing import Any
9
9
 
10
10
  from ..core.adapter import BaseAdapter
11
-
12
- logger = logging.getLogger(__name__)
13
11
  from ..core.models import (
14
12
  Attachment,
15
13
  Comment,
@@ -21,6 +19,8 @@ from ..core.models import (
21
19
  )
22
20
  from ..core.registry import AdapterRegistry
23
21
 
22
+ logger = logging.getLogger(__name__)
23
+
24
24
  # Import ai-trackdown-pytools when available
25
25
  try:
26
26
  from ai_trackdown_pytools import AITrackdown
@@ -94,10 +94,28 @@ class LinearAdapter(BaseAdapter[Task]):
94
94
  "Linear API key is required (api_key or LINEAR_API_KEY env var)"
95
95
  )
96
96
 
97
- # Clean API key - remove Bearer prefix if accidentally included in config
98
- # (The client will add it back when making requests)
99
- if self.api_key.startswith("Bearer "):
100
- self.api_key = self.api_key.replace("Bearer ", "")
97
+ # Clean API key - remove common prefixes if accidentally included in config
98
+ # (The client will add Bearer back when making requests)
99
+ if isinstance(self.api_key, str):
100
+ # Remove Bearer prefix
101
+ if self.api_key.startswith("Bearer "):
102
+ self.api_key = self.api_key.replace("Bearer ", "")
103
+ # Remove environment variable name prefix (e.g., "LINEAR_API_KEY=")
104
+ if "=" in self.api_key:
105
+ parts = self.api_key.split("=", 1)
106
+ if len(parts) == 2 and parts[0].upper() in (
107
+ "LINEAR_API_KEY",
108
+ "API_KEY",
109
+ ):
110
+ self.api_key = parts[1]
111
+
112
+ # Validate API key format (Linear keys start with "lin_api_")
113
+ if not self.api_key.startswith("lin_api_"):
114
+ raise ValueError(
115
+ f"Invalid Linear API key format. Expected key starting with 'lin_api_', "
116
+ f"got: {self.api_key[:15]}... "
117
+ f"Please check your configuration and ensure the API key is correct."
118
+ )
101
119
 
102
120
  self.workspace = config.get("workspace", "")
103
121
  self.team_key = config.get("team_key")
@@ -194,6 +212,96 @@ class LinearAdapter(BaseAdapter[Task]):
194
212
  except Exception as e:
195
213
  raise ValueError(f"Failed to resolve team '{self.team_key}': {e}")
196
214
 
215
+ async def _resolve_project_id(self, project_identifier: str) -> str | None:
216
+ """Resolve project identifier (slug, name, short ID, or URL) to full UUID.
217
+
218
+ Args:
219
+ project_identifier: Project slug, name, short ID, or URL
220
+
221
+ Returns:
222
+ Full Linear project UUID, or None if not found
223
+
224
+ Raises:
225
+ ValueError: If project lookup fails
226
+
227
+ Examples:
228
+ - "crm-smart-monitoring-system" (slug)
229
+ - "CRM Smart Monitoring System" (name)
230
+ - "f59a41a96c52" (short ID from URL)
231
+ - "https://linear.app/travel-bta/project/crm-smart-monitoring-system-f59a41a96c52/overview" (full URL)
232
+
233
+ """
234
+ if not project_identifier:
235
+ return None
236
+
237
+ # Extract slug/ID from URL if full URL provided
238
+ if project_identifier.startswith("http"):
239
+ # Extract slug-shortid from URL like:
240
+ # https://linear.app/travel-bta/project/crm-smart-monitoring-system-f59a41a96c52/overview
241
+ parts = project_identifier.split("/project/")
242
+ if len(parts) > 1:
243
+ slug_with_id = parts[1].split("/")[
244
+ 0
245
+ ] # Get "crm-smart-monitoring-system-f59a41a96c52"
246
+ project_identifier = slug_with_id
247
+ else:
248
+ raise ValueError(f"Invalid Linear project URL: {project_identifier}")
249
+
250
+ # If it looks like a full UUID already (exactly 36 chars with exactly 4 dashes), return it
251
+ # UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
252
+ if len(project_identifier) == 36 and project_identifier.count("-") == 4:
253
+ return project_identifier
254
+
255
+ # Query all projects and search for matching slug, name, or slugId
256
+ query = """
257
+ query GetProjects {
258
+ projects(first: 100) {
259
+ nodes {
260
+ id
261
+ name
262
+ slugId
263
+ }
264
+ }
265
+ }
266
+ """
267
+
268
+ try:
269
+ result = await self.client.execute_query(query, {})
270
+ projects = result.get("projects", {}).get("nodes", [])
271
+
272
+ # Search for match by slug, slugId, name (case-insensitive)
273
+ project_lower = project_identifier.lower()
274
+ for project in projects:
275
+ # Check if identifier matches slug pattern (extracted from slugId)
276
+ slug_id = project.get("slugId", "")
277
+ if slug_id:
278
+ # slugId format: "crm-smart-monitoring-system-f59a41a96c52"
279
+ # Extract both the slug part and short ID
280
+ if "-" in slug_id:
281
+ parts = slug_id.rsplit(
282
+ "-", 1
283
+ ) # Split from right to get last part
284
+ slug_part = parts[0] # "crm-smart-monitoring-system"
285
+ short_id = parts[1] if len(parts) > 1 else "" # "f59a41a96c52"
286
+
287
+ # Match full slugId, slug part, or short ID
288
+ if (
289
+ slug_id.lower() == project_lower
290
+ or slug_part.lower() == project_lower
291
+ or short_id.lower() == project_lower
292
+ ):
293
+ return project["id"]
294
+
295
+ # Also check exact name match (case-insensitive)
296
+ if project["name"].lower() == project_lower:
297
+ return project["id"]
298
+
299
+ # No match found
300
+ return None
301
+
302
+ except Exception as e:
303
+ raise ValueError(f"Failed to resolve project '{project_identifier}': {e}")
304
+
197
305
  async def _load_workflow_states(self, team_id: str) -> None:
198
306
  """Load and cache workflow states for the team.
199
307
 
@@ -443,6 +551,20 @@ class LinearAdapter(BaseAdapter[Task]):
443
551
  # Remove labelIds if no labels resolved
444
552
  issue_input.pop("labelIds", None)
445
553
 
554
+ # Resolve project ID if parent_epic is provided (supports slug, name, short ID, or URL)
555
+ if task.parent_epic:
556
+ project_id = await self._resolve_project_id(task.parent_epic)
557
+ if project_id:
558
+ issue_input["projectId"] = project_id
559
+ else:
560
+ # Log warning but don't fail - user may have provided invalid project
561
+ logging.getLogger(__name__).warning(
562
+ f"Could not resolve project identifier '{task.parent_epic}' to UUID. "
563
+ "Issue will be created without project assignment."
564
+ )
565
+ # Remove projectId if we couldn't resolve it
566
+ issue_input.pop("projectId", None)
567
+
446
568
  try:
447
569
  result = await self.client.execute_mutation(
448
570
  CREATE_ISSUE_MUTATION, {"input": issue_input}
@@ -85,6 +85,11 @@ def create_auggie_server_config(
85
85
  Auggie MCP server configuration dict
86
86
 
87
87
  """
88
+ # Get mcp-ticketer CLI command from same directory as Python
89
+ from pathlib import Path
90
+
91
+ python_dir = Path(python_path).parent
92
+ mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
88
93
  # Get adapter configuration
89
94
  adapter = project_config.get("default_adapter", "aitrackdown")
90
95
  adapters_config = project_config.get("adapters", {})
@@ -134,14 +139,14 @@ def create_auggie_server_config(
134
139
  if "project_key" in adapter_config:
135
140
  env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
136
141
 
137
- # Use module invocation pattern: python -m mcp_ticketer.mcp.server
138
- args = ["-m", "mcp_ticketer.mcp.server"]
142
+ # Use CLI command: mcp-ticketer mcp
143
+ args = ["mcp"]
139
144
  if project_path:
140
145
  args.append(project_path)
141
146
 
142
147
  # Create server configuration (simpler than Gemini - no timeout/trust)
143
148
  config = {
144
- "command": python_path,
149
+ "command": mcp_ticketer_cmd,
145
150
  "args": args,
146
151
  "env": env_vars,
147
152
  }
@@ -298,7 +303,7 @@ def configure_auggie_mcp(force: bool = False) -> None:
298
303
  console.print(" Server name: mcp-ticketer")
299
304
  console.print(f" Adapter: {adapter}")
300
305
  console.print(f" Python: {python_path}")
301
- console.print(" Command: python -m mcp_ticketer.mcp.server")
306
+ console.print(" Command: mcp-ticketer mcp")
302
307
  console.print(" Scope: Global (affects all projects)")
303
308
  console.print(f" Project path: {project_path}")
304
309
  if "env" in server_config:
@@ -4,11 +4,11 @@ Codex CLI only supports global configuration at ~/.codex/config.toml.
4
4
  Unlike Claude Code and Gemini CLI, there is no project-level configuration support.
5
5
  """
6
6
 
7
- import tomllib
8
7
  from pathlib import Path
9
8
  from typing import Any
10
9
 
11
10
  import tomli_w
11
+ import tomllib
12
12
  from rich.console import Console
13
13
 
14
14
  from .mcp_configure import load_project_config
@@ -87,6 +87,10 @@ def create_codex_server_config(
87
87
  Codex MCP server configuration dict
88
88
 
89
89
  """
90
+ # Get mcp-ticketer CLI command from same directory as Python
91
+ python_dir = Path(python_path).parent
92
+ mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
93
+
90
94
  # Get adapter configuration
91
95
  adapter = project_config.get("default_adapter", "aitrackdown")
92
96
  adapters_config = project_config.get("adapters", {})
@@ -136,14 +140,14 @@ def create_codex_server_config(
136
140
  if "project_key" in adapter_config:
137
141
  env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
138
142
 
139
- # Use module invocation pattern: python -m mcp_ticketer.mcp.server
140
- args = ["-m", "mcp_ticketer.mcp.server"]
143
+ # Use CLI command: mcp-ticketer mcp
144
+ args = ["mcp"]
141
145
  if project_path:
142
146
  args.append(project_path)
143
147
 
144
148
  # Create server configuration with Codex-specific structure
145
149
  config: dict[str, Any] = {
146
- "command": python_path,
150
+ "command": mcp_ticketer_cmd,
147
151
  "args": args,
148
152
  "env": env_vars,
149
153
  }
@@ -307,7 +311,7 @@ def configure_codex_mcp(force: bool = False) -> None:
307
311
  console.print(" Server name: mcp-ticketer")
308
312
  console.print(f" Adapter: {adapter}")
309
313
  console.print(f" Python: {python_path}")
310
- console.print(" Command: python -m mcp_ticketer.mcp.server")
314
+ console.print(" Command: mcp-ticketer mcp")
311
315
  console.print(" Scope: global (Codex only supports global config)")
312
316
  console.print(f" Project path: {project_path}")
313
317
  if "env" in server_config:
@@ -87,6 +87,10 @@ def create_gemini_server_config(
87
87
  Gemini MCP server configuration dict
88
88
 
89
89
  """
90
+ # Get mcp-ticketer CLI command from same directory as Python
91
+ python_dir = Path(python_path).parent
92
+ mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
93
+
90
94
  # Get adapter configuration
91
95
  adapter = project_config.get("default_adapter", "aitrackdown")
92
96
  adapters_config = project_config.get("adapters", {})
@@ -136,14 +140,14 @@ def create_gemini_server_config(
136
140
  if "project_key" in adapter_config:
137
141
  env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
138
142
 
139
- # Use module invocation pattern: python -m mcp_ticketer.mcp.server
140
- args = ["-m", "mcp_ticketer.mcp.server"]
143
+ # Use CLI command: mcp-ticketer mcp
144
+ args = ["mcp"]
141
145
  if project_path:
142
146
  args.append(project_path)
143
147
 
144
148
  # Create server configuration with Gemini-specific options
145
149
  config = {
146
- "command": python_path,
150
+ "command": mcp_ticketer_cmd,
147
151
  "args": args,
148
152
  "env": env_vars,
149
153
  "timeout": 15000, # 15 seconds timeout
@@ -300,7 +304,7 @@ def configure_gemini_mcp(
300
304
  console.print(" Server name: mcp-ticketer")
301
305
  console.print(f" Adapter: {adapter}")
302
306
  console.print(f" Python: {python_path}")
303
- console.print(" Command: python -m mcp_ticketer.mcp.server")
307
+ console.print(" Command: mcp-ticketer mcp")
304
308
  console.print(f" Timeout: {server_config['timeout']}ms")
305
309
  console.print(f" Trust: {server_config['trust']}")
306
310
  if project_path:
mcp_ticketer/cli/main.py CHANGED
@@ -575,7 +575,7 @@ def init(
575
575
  )
576
576
 
577
577
  # First try our improved .env configuration loader
578
- from ..mcp.server import _load_env_configuration
578
+ from ..mcp.server.main import _load_env_configuration
579
579
 
580
580
  env_config = _load_env_configuration()
581
581
 
@@ -1868,9 +1868,29 @@ mcp_app = typer.Typer(
1868
1868
  name="mcp",
1869
1869
  help="Configure MCP integration for AI clients (Claude, Gemini, Codex, Auggie)",
1870
1870
  add_completion=False,
1871
+ invoke_without_command=True,
1871
1872
  )
1872
1873
 
1873
1874
 
1875
+ @mcp_app.callback()
1876
+ def mcp_callback(
1877
+ ctx: typer.Context,
1878
+ project_path: str | None = typer.Argument(
1879
+ None, help="Project directory path (optional - uses cwd if not provided)"
1880
+ ),
1881
+ ):
1882
+ """MCP command group - runs MCP server if no subcommand provided."""
1883
+ if ctx.invoked_subcommand is None:
1884
+ # No subcommand provided, run the serve command
1885
+ # Change to project directory if provided
1886
+ if project_path:
1887
+ import os
1888
+
1889
+ os.chdir(project_path)
1890
+ # Invoke the serve command through context
1891
+ ctx.invoke(mcp_serve, adapter=None, base_path=None)
1892
+
1893
+
1874
1894
  @app.command()
1875
1895
  def install(
1876
1896
  platform: str | None = typer.Argument(
@@ -2210,8 +2230,8 @@ def mcp_serve(
2210
2230
  2. Global: ~/.mcp-ticketer/config.json
2211
2231
  3. Default: aitrackdown adapter with .aitrackdown base path
2212
2232
  """
2213
- from ..mcp.server_sdk import configure_adapter
2214
- from ..mcp.server_sdk import main as sdk_main
2233
+ from ..mcp.server.server_sdk import configure_adapter
2234
+ from ..mcp.server.server_sdk import main as sdk_main
2215
2235
 
2216
2236
  # Load configuration (respects project-specific config in cwd)
2217
2237
  config = load_config()
@@ -2225,7 +2245,7 @@ def mcp_serve(
2225
2245
  adapter_config = adapters_config.get(adapter_type, {})
2226
2246
  else:
2227
2247
  # Priority 2: .env files
2228
- from ..mcp.server import _load_env_configuration
2248
+ from ..mcp.server.main import _load_env_configuration
2229
2249
 
2230
2250
  env_config = _load_env_configuration()
2231
2251
  if env_config:
@@ -161,12 +161,12 @@ def create_mcp_server_config(
161
161
  MCP server configuration dict matching Claude Code stdio pattern
162
162
 
163
163
  """
164
- # Ensure python3 is used (not python)
165
- if python_path.endswith("/python"):
166
- python_path = python_path.replace("/python", "/python3")
164
+ # Get mcp-ticketer CLI command from same directory as Python
165
+ python_dir = Path(python_path).parent
166
+ mcp_ticketer_cmd = str(python_dir / "mcp-ticketer")
167
167
 
168
- # Use module invocation pattern: python -m mcp_ticketer.mcp.server
169
- args = ["-m", "mcp_ticketer.mcp.server"]
168
+ # Use CLI command: mcp-ticketer mcp
169
+ args = ["mcp"]
170
170
 
171
171
  # Add project path if provided
172
172
  if project_path:
@@ -175,7 +175,7 @@ def create_mcp_server_config(
175
175
  # REQUIRED: Add "type": "stdio" for Claude Code compatibility
176
176
  config = {
177
177
  "type": "stdio",
178
- "command": python_path,
178
+ "command": mcp_ticketer_cmd,
179
179
  "args": args,
180
180
  }
181
181
 
@@ -385,7 +385,7 @@ def configure_claude_mcp(global_config: bool = False, force: bool = False) -> No
385
385
  console.print(" Server name: mcp-ticketer")
386
386
  console.print(f" Adapter: {adapter}")
387
387
  console.print(f" Python: {python_path}")
388
- console.print(" Command: python -m mcp_ticketer.mcp.server")
388
+ console.print(" Command: mcp-ticketer mcp")
389
389
  if project_path:
390
390
  console.print(f" Project path: {project_path}")
391
391
  if "env" in server_config:
@@ -3,7 +3,7 @@
3
3
  from typing import TYPE_CHECKING
4
4
 
5
5
  if TYPE_CHECKING:
6
- from .server import MCPTicketServer
6
+ from .server.main import MCPTicketServer
7
7
 
8
8
  __all__ = ["MCPTicketServer"]
9
9
 
@@ -23,11 +23,11 @@ def __getattr__(name: str):
23
23
  This prevents the RuntimeWarning when running:
24
24
  python -m mcp_ticketer.mcp.server
25
25
 
26
- The warning occurred because __init__.py imported server.py before
26
+ The warning occurred because __init__.py imported server before
27
27
  runpy could execute it as __main__.
28
28
  """
29
29
  if name == "MCPTicketServer":
30
- from .server import MCPTicketServer
30
+ from .server.main import MCPTicketServer
31
31
 
32
32
  return MCPTicketServer
33
33
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,21 @@
1
+ """MCP Server package for mcp-ticketer.
2
+
3
+ This package provides the FastMCP server implementation for ticket management
4
+ operations via the Model Context Protocol (MCP).
5
+ """
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .main import main
11
+
12
+ __all__ = ["main"]
13
+
14
+
15
+ def __getattr__(name: str):
16
+ """Lazy import to avoid premature module loading."""
17
+ if name == "main":
18
+ from .main import main
19
+
20
+ return main
21
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,60 @@
1
+ """Main entry point for MCP server module invocation.
2
+
3
+ This module enables running the MCP server via:
4
+ python -m mcp_ticketer.mcp.server [project_path]
5
+
6
+ This is the preferred invocation method for MCP configurations as it:
7
+ - Works reliably across installation methods (pipx, pip, uv)
8
+ - Doesn't depend on binary path detection
9
+ - Follows the proven mcp-vector-search pattern
10
+ """
11
+
12
+ import asyncio
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ from .main import main
17
+
18
+
19
+ def run_server() -> None:
20
+ """Run the MCP server with optional project path argument.
21
+
22
+ Usage:
23
+ python -m mcp_ticketer.mcp.server
24
+ python -m mcp_ticketer.mcp.server /path/to/project
25
+
26
+ Arguments:
27
+ project_path (optional): Path to project directory for context
28
+
29
+ """
30
+ # Check for project path argument
31
+ if len(sys.argv) > 1:
32
+ project_path = Path(sys.argv[1])
33
+
34
+ # Validate project path exists
35
+ if not project_path.exists():
36
+ sys.stderr.write(f"Error: Project path does not exist: {project_path}\n")
37
+ sys.exit(1)
38
+
39
+ # Change to project directory for context
40
+ try:
41
+ import os
42
+
43
+ os.chdir(project_path)
44
+ sys.stderr.write(f"[MCP Server] Working directory: {project_path}\n")
45
+ except OSError as e:
46
+ sys.stderr.write(f"Warning: Could not change to project directory: {e}\n")
47
+
48
+ # Run the async main function
49
+ try:
50
+ asyncio.run(main())
51
+ except KeyboardInterrupt:
52
+ sys.stderr.write("\n[MCP Server] Interrupted by user\n")
53
+ sys.exit(0)
54
+ except Exception as e:
55
+ sys.stderr.write(f"[MCP Server] Fatal error: {e}\n")
56
+ sys.exit(1)
57
+
58
+
59
+ if __name__ == "__main__":
60
+ run_server()
@@ -11,8 +11,8 @@ from dotenv import load_dotenv
11
11
  # Import adapters module to trigger registration
12
12
  import mcp_ticketer.adapters # noqa: F401
13
13
 
14
- from ..core import AdapterRegistry
15
- from ..core.models import Comment, Epic, Priority, SearchQuery, Task, TicketState
14
+ from ...core import AdapterRegistry
15
+ from ...core.models import Comment, Epic, Priority, SearchQuery, Task, TicketState
16
16
  from .constants import (
17
17
  DEFAULT_BASE_PATH,
18
18
  DEFAULT_LIMIT,
@@ -14,8 +14,8 @@ from typing import Any
14
14
 
15
15
  from mcp.server.fastmcp import FastMCP
16
16
 
17
- from ..core.adapter import BaseAdapter
18
- from ..core.registry import AdapterRegistry
17
+ from ...core.adapter import BaseAdapter
18
+ from ...core.registry import AdapterRegistry
19
19
 
20
20
  # Initialize FastMCP server
21
21
  mcp = FastMCP("mcp-ticketer")
@@ -7,7 +7,7 @@ available in all adapters.
7
7
 
8
8
  from typing import Any
9
9
 
10
- from ...core.models import Comment
10
+ from ....core.models import Comment
11
11
  from ..server_sdk import get_adapter, mcp
12
12
 
13
13
 
@@ -6,7 +6,7 @@ efficiency when working with multiple items.
6
6
 
7
7
  from typing import Any
8
8
 
9
- from ...core.models import Priority, Task, TicketState, TicketType
9
+ from ....core.models import Priority, Task, TicketState, TicketType
10
10
  from ..server_sdk import get_adapter, mcp
11
11
 
12
12
 
@@ -5,7 +5,7 @@ This module implements tools for adding and retrieving comments on tickets.
5
5
 
6
6
  from typing import Any
7
7
 
8
- from ...core.models import Comment
8
+ from ....core.models import Comment
9
9
  from ..server_sdk import get_adapter, mcp
10
10
 
11
11
 
@@ -9,7 +9,7 @@ This module implements tools for managing the three-level ticket hierarchy:
9
9
  from datetime import datetime
10
10
  from typing import Any
11
11
 
12
- from ...core.models import Epic, Priority, Task, TicketType
12
+ from ....core.models import Epic, Priority, Task, TicketType
13
13
  from ..server_sdk import get_adapter, mcp
14
14
 
15
15
 
@@ -129,7 +129,7 @@ async def ticket_link_pr(
129
129
  }
130
130
 
131
131
  # Fallback: Add PR link as comment
132
- from ...core.models import Comment
132
+ from ....core.models import Comment
133
133
 
134
134
  comment = Comment(
135
135
  ticket_id=ticket_id,
@@ -6,7 +6,7 @@ various filters and criteria.
6
6
 
7
7
  from typing import Any
8
8
 
9
- from ...core.models import Priority, SearchQuery, TicketState
9
+ from ....core.models import Priority, SearchQuery, TicketState
10
10
  from ..server_sdk import get_adapter, mcp
11
11
 
12
12
 
@@ -6,7 +6,7 @@ operations for tickets using the FastMCP SDK.
6
6
 
7
7
  from typing import Any
8
8
 
9
- from ...core.models import Priority, Task, TicketState
9
+ from ....core.models import Priority, Task, TicketState
10
10
  from ..server_sdk import get_adapter, mcp
11
11
 
12
12
 
@@ -1,9 +1,8 @@
1
1
  """Async queue system for mcp-ticketer."""
2
2
 
3
- from .queue import Queue, QueueItem, QueueStatus
4
- from .worker import Worker
5
-
6
3
  # Import manager last to avoid circular import
7
4
  from .manager import WorkerManager
5
+ from .queue import Queue, QueueItem, QueueStatus
6
+ from .worker import Worker
8
7
 
9
8
  __all__ = ["Queue", "QueueItem", "QueueStatus", "Worker", "WorkerManager"]
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Any
11
11
  import psutil
12
12
 
13
13
  if TYPE_CHECKING:
14
- from .queue import Queue
14
+ pass
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-ticketer
3
- Version: 0.4.5
3
+ Version: 0.4.9
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,14 +1,14 @@
1
1
  mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
2
- mcp_ticketer/__version__.py,sha256=vioHFC2eLXv3RlmwMaPO8QGtnhB8gou02LbH7LA6A-U,1117
2
+ mcp_ticketer/__version__.py,sha256=TgErXAgObQmg6Q5ACy63d_W9qLAaFynoPaauS16SAi0,1117
3
3
  mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
5
- mcp_ticketer/adapters/aitrackdown.py,sha256=qCR53JpOVlOHiR_pdjGF2RYOemgkKxIwDTOlpcYF18E,30320
5
+ mcp_ticketer/adapters/aitrackdown.py,sha256=cm8L7amB64ZCoGvWbnpH9zxZlXu6t-l8TWiYVSwDhlg,30320
6
6
  mcp_ticketer/adapters/github.py,sha256=QeZefKs204g2nXZ9yDb3j-HwrufbXBPoXB0zLp6bvW0,47338
7
7
  mcp_ticketer/adapters/hybrid.py,sha256=7ocRjK7N7FdXSUCeFc23jFevfVwcPvHPIsEPXV_4o1w,18997
8
8
  mcp_ticketer/adapters/jira.py,sha256=9OtYAQfUdUQqEYjs61jzYpVrHu23hyP22mm-Bfn5KqA,35204
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=ZCj6ZM5RiWbdiGrhA3NsMTgFImvrzaB0jBIr_3ZCcO8,30959
11
+ mcp_ticketer/adapters/linear/adapter.py,sha256=oQw_KgZWSdktfUhYEAonHUfK8z4CaiP18TC9pZqYv5Q,36251
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
14
  mcp_ticketer/adapters/linear/queries.py,sha256=K8y7xc3iH-q9LEUmg-0YDBhh546LAwLZDvVLkzx3yY4,7223
@@ -17,15 +17,15 @@ mcp_ticketer/cache/__init__.py,sha256=Xcd-cKnt-Cx7jBzvfzUUUPaGkmyXFi5XUFWw3Z4b7d
17
17
  mcp_ticketer/cache/memory.py,sha256=rWphWZy7XTbHezC7HMRQN9ISUhYo0Pc2OTgLG30vHnI,5047
18
18
  mcp_ticketer/cli/__init__.py,sha256=l9Q8iKmfGkTu0cssHBVqNZTsL4eAtFzOB25AED_0G6g,89
19
19
  mcp_ticketer/cli/adapter_diagnostics.py,sha256=pQDdtDgBwSW04wdFEPVzwbul3KgfB9g6ZMS85qpYulY,14988
20
- mcp_ticketer/cli/auggie_configure.py,sha256=BA31HvOXljPqi3QMKt5eI5jYUWCnnH00sDqwR3y2nSY,11701
21
- mcp_ticketer/cli/codex_configure.py,sha256=LLGzsFjCNO3irtabSazCpsZ5eUmG6eAjCNn6B5M4aHQ,12249
20
+ mcp_ticketer/cli/auggie_configure.py,sha256=47Xay__bMDGmNPWR6u9zxk4IVsSlEB02xVI40v8dhkQ,11825
21
+ mcp_ticketer/cli/codex_configure.py,sha256=EYgyEPoAWoc0-vLLiUSEEFkDMl0K-eOQx6p_R_iqhWI,12344
22
22
  mcp_ticketer/cli/configure.py,sha256=T4LczvZIc2FZM-joqICL8NpjCeThAUknEhJkHsmpdc8,16158
23
23
  mcp_ticketer/cli/diagnostics.py,sha256=s7P4vDzPpthgiBJfAOXpunlhZ62buHg_BA5W7ghQBqg,29952
24
24
  mcp_ticketer/cli/discover.py,sha256=paG8zElIFEK6lgVD-tHWoFDTuWQ185LirFp0fVlYgB0,13148
25
- mcp_ticketer/cli/gemini_configure.py,sha256=GXbQkfZGsmx_2cJKxH_5z3JdcwpMopF0duI_TzbKip8,12644
25
+ mcp_ticketer/cli/gemini_configure.py,sha256=6Czg-gJA_HrzMopKCvCLfRZeqkdR77OLV5W7W4ZG9hw,12739
26
26
  mcp_ticketer/cli/linear_commands.py,sha256=YnmqRacAfTdF0CLr4BXOxFs3uwm_hVifbGdTe6t2CfA,17273
27
- mcp_ticketer/cli/main.py,sha256=DV05n5-JGIjIX1t4-oR7j5tuE4c2Z0of6KVahUzGiJY,89254
28
- mcp_ticketer/cli/mcp_configure.py,sha256=dgApMLBApLxU0fvFOyDBkX69GOGVNTXhAKM-hZGw5v8,14142
27
+ mcp_ticketer/cli/main.py,sha256=W36hKxZZKoojjBUYl5U5MlXLaw-qX9i0nPrmvtsndtI,89926
28
+ mcp_ticketer/cli/mcp_configure.py,sha256=SfjA97OJAkYUfi9Mn-wqEt6p0xsgz_QZlATwxM_vjTo,14089
29
29
  mcp_ticketer/cli/migrate_config.py,sha256=MYsr_C5ZxsGg0P13etWTWNrJ_lc6ElRCkzfQADYr3DM,5956
30
30
  mcp_ticketer/cli/platform_commands.py,sha256=pTLRT2wot8dAmy1-roJWWOT0Cxu7j-06BlWDnZ9a4jY,3624
31
31
  mcp_ticketer/cli/python_detection.py,sha256=qmhi0CIDKH_AUVGkJ9jyY1zBpx1cwiQNv0vnEvMYDFQ,4272
@@ -44,32 +44,34 @@ mcp_ticketer/core/mappers.py,sha256=okte6EV_OuPvnM1KXHUcfrpPd7TWnKh44X3_W3HxwiI,
44
44
  mcp_ticketer/core/models.py,sha256=ABKdyAkEkKtMF_d6D8_qRL-2ujz1DshemHSyqTPUthA,14448
45
45
  mcp_ticketer/core/project_config.py,sha256=DmLekuMuOgNtzg-olOU4Utv00DdCH1-CXuoooA-adMs,23609
46
46
  mcp_ticketer/core/registry.py,sha256=gBeXcZ3grHl9gYFbyRp-C4IM7SD_KGTeXT_1jG8XrCc,3470
47
- mcp_ticketer/mcp/__init__.py,sha256=fWcCqL7MxKcsnq8vUqDV-rsxykOo3J5_98bba1oT1dw,857
47
+ mcp_ticketer/mcp/__init__.py,sha256=XscFBOFeIxBNgA_8yPLl6c75-YJCcIN2I9eoLEGsBdM,864
48
48
  mcp_ticketer/mcp/__main__.py,sha256=Fo_5KJOFako2gi1Z1kk5zEt2sGJW6BX6oXlYp7twYTs,1713
49
- mcp_ticketer/mcp/constants.py,sha256=EBGsJtBPaTCvAm5rOMknckrXActrNIls7lRklnh1L4s,2072
50
- mcp_ticketer/mcp/dto.py,sha256=FR_OBtaxrno8AsHynPwUUW715iAoaBkrr7Ud8HZTQW8,7233
51
- mcp_ticketer/mcp/response_builder.py,sha256=DUfe1e0CcXPlepLq-cGH6b_THqoZEynYfVKkZEeLe0M,4933
52
- mcp_ticketer/mcp/server.py,sha256=XjlajZs4F7g4rXuuDlxvKQ-kml4PqRBAFnpntxgw_Ds,48832
53
- mcp_ticketer/mcp/server_sdk.py,sha256=orgOTrvrtBn2BeMJt5HWeodvCU9sWH4o5pQIZ_geXao,2552
54
- mcp_ticketer/mcp/tools/__init__.py,sha256=6miiC2Cru8u2TCrm9RYF1jxd7vu9SI7BPLUjtzwOxT8,1056
55
- mcp_ticketer/mcp/tools/attachment_tools.py,sha256=hDeQV1rkSjZwT__FKLkR04FH_AsAF6QysyeP_CKxdiU,5673
56
- mcp_ticketer/mcp/tools/bulk_tools.py,sha256=UWY1T5DnWhpBJnqMdLMu7Feen515UAlcn_hOjTmDncc,9199
57
- mcp_ticketer/mcp/tools/comment_tools.py,sha256=fVJZeSg_rr6tVoVAKF8rv5nUooqg5d4S44s1mKFTYM0,2701
58
- mcp_ticketer/mcp/tools/hierarchy_tools.py,sha256=08KxOawUKGZK3kYPZ5w9mRk-lVaSFxQvaJJ0tmMuBLQ,10501
59
- mcp_ticketer/mcp/tools/pr_tools.py,sha256=SnKJRsvkmmr2eh1qaIqxC0PHD6rVXKg6mkk9Skdpc0Y,4539
60
- mcp_ticketer/mcp/tools/search_tools.py,sha256=6qwSBRkGx-zO0rxebeQqMcBj006ZovAgfyLGfw3kxig,6951
61
- mcp_ticketer/mcp/tools/ticket_tools.py,sha256=KYMl2h-nf7hZq-kOi1z4H0TEj7MzMklOsRIl4HaP8WQ,8121
62
- mcp_ticketer/queue/__init__.py,sha256=yQtdFNHhk1G_BHTuYk-Vlu6ZgEQ6Ik994zMH1IGHwbc,279
49
+ mcp_ticketer/mcp/server/__init__.py,sha256=2ll9kLEEiKP_NtvJghHVz6TG2p7xwIUBRqlxZKWRCvU,509
50
+ mcp_ticketer/mcp/server/__main__.py,sha256=xE1n94M5n2tKyT6qFIOXaqRXX7L--SxmCglKUPcljG0,1711
51
+ mcp_ticketer/mcp/server/constants.py,sha256=EBGsJtBPaTCvAm5rOMknckrXActrNIls7lRklnh1L4s,2072
52
+ mcp_ticketer/mcp/server/dto.py,sha256=FR_OBtaxrno8AsHynPwUUW715iAoaBkrr7Ud8HZTQW8,7233
53
+ mcp_ticketer/mcp/server/main.py,sha256=ddYl7u2vUwnuPRg-QP0sZS_Utils9nx-PEdxcIvqfy4,48834
54
+ mcp_ticketer/mcp/server/response_builder.py,sha256=DUfe1e0CcXPlepLq-cGH6b_THqoZEynYfVKkZEeLe0M,4933
55
+ mcp_ticketer/mcp/server/server_sdk.py,sha256=KGpMvvJAckKl5ReLsyYvNJCM44nZRgY-V7dkgENTFX0,2554
56
+ mcp_ticketer/mcp/server/tools/__init__.py,sha256=6miiC2Cru8u2TCrm9RYF1jxd7vu9SI7BPLUjtzwOxT8,1056
57
+ mcp_ticketer/mcp/server/tools/attachment_tools.py,sha256=c2ySBs0Zz1M_zbFszbsD0S8lJT8dnCzTy-mSsKEDPV0,5674
58
+ mcp_ticketer/mcp/server/tools/bulk_tools.py,sha256=H5RZylDsrYuTCDF1afUcVcm-Yv3cu052JwwBPW9-4YA,9200
59
+ mcp_ticketer/mcp/server/tools/comment_tools.py,sha256=XXPS4TCIDaxnoITdG4B38iOX9LOElqCZFus4FnULJeg,2702
60
+ mcp_ticketer/mcp/server/tools/hierarchy_tools.py,sha256=KcsZ4NAd00vOm-PZziAtAanOX8CB7baGkWNoIKDIako,10502
61
+ mcp_ticketer/mcp/server/tools/pr_tools.py,sha256=PoB5YABYIlrABw5-RPA8bTh8uHH3hituslV9ib9xUUU,4540
62
+ mcp_ticketer/mcp/server/tools/search_tools.py,sha256=60OwDXN9bLQKbe9apLmJDyM0TcTPCAv9Vp2X2gPBQX4,6952
63
+ mcp_ticketer/mcp/server/tools/ticket_tools.py,sha256=DrNxx8gKvmFOKpxR80hcmtXK1tZOmemBgAABlPAPl6E,8122
64
+ mcp_ticketer/queue/__init__.py,sha256=ut4EkrXng9RJlFPZRKUa3elhHo3MFGhshBXquZ16vcs,278
63
65
  mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
64
66
  mcp_ticketer/queue/health_monitor.py,sha256=TDmPnYuZJb3yHNJlGFvE9UU-LfsKTrC4Vapyvdb3fso,12226
65
- mcp_ticketer/queue/manager.py,sha256=UHkI0OSDyspY0EM2UVf5E1PjUTmZIcOcgGNovxtuVYs,10732
67
+ mcp_ticketer/queue/manager.py,sha256=dswFp3e0z-1hUIzoJ08raNxwJeX9_IYYu5WbHnlfZzQ,10712
66
68
  mcp_ticketer/queue/queue.py,sha256=q9HDXgnlwspamMJIeu9og7qONttXHmFZHPSaMtJDPlw,17923
67
69
  mcp_ticketer/queue/run_worker.py,sha256=WhoeamL8LKZ66TM8W1PkMPwjF2w_EDFMP-mevs6C1TM,1019
68
70
  mcp_ticketer/queue/ticket_registry.py,sha256=xVg3i7Eb5rtQY-4bbw3zYY1h-C6jF1t1NZEGhObzD8g,15491
69
71
  mcp_ticketer/queue/worker.py,sha256=AJHtpJZEhGoPuCDPXSMsn9DeODelo5f__0C__3zoN08,20970
70
- mcp_ticketer-0.4.5.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
71
- mcp_ticketer-0.4.5.dist-info/METADATA,sha256=l1eDYLelVRQ1aJldM9nXa0_vvelSgQ6o78smdDlCYjo,16020
72
- mcp_ticketer-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
73
- mcp_ticketer-0.4.5.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
74
- mcp_ticketer-0.4.5.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
75
- mcp_ticketer-0.4.5.dist-info/RECORD,,
72
+ mcp_ticketer-0.4.9.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
73
+ mcp_ticketer-0.4.9.dist-info/METADATA,sha256=_nIoXUxM3iodpZW31n9HYkeS_LTOd0GNKwZ36k88h3w,16020
74
+ mcp_ticketer-0.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
+ mcp_ticketer-0.4.9.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
76
+ mcp_ticketer-0.4.9.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
77
+ mcp_ticketer-0.4.9.dist-info/RECORD,,
File without changes
File without changes
File without changes