mcp-ticketer 0.4.11__py3-none-any.whl โ†’ 2.0.1__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 (111) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +3 -3
  3. mcp_ticketer/adapters/__init__.py +2 -0
  4. mcp_ticketer/adapters/aitrackdown.py +394 -9
  5. mcp_ticketer/adapters/asana/__init__.py +15 -0
  6. mcp_ticketer/adapters/asana/adapter.py +1416 -0
  7. mcp_ticketer/adapters/asana/client.py +292 -0
  8. mcp_ticketer/adapters/asana/mappers.py +348 -0
  9. mcp_ticketer/adapters/asana/types.py +146 -0
  10. mcp_ticketer/adapters/github.py +836 -105
  11. mcp_ticketer/adapters/hybrid.py +47 -5
  12. mcp_ticketer/adapters/jira.py +772 -1
  13. mcp_ticketer/adapters/linear/adapter.py +2293 -108
  14. mcp_ticketer/adapters/linear/client.py +146 -12
  15. mcp_ticketer/adapters/linear/mappers.py +105 -11
  16. mcp_ticketer/adapters/linear/queries.py +168 -1
  17. mcp_ticketer/adapters/linear/types.py +80 -4
  18. mcp_ticketer/analysis/__init__.py +56 -0
  19. mcp_ticketer/analysis/dependency_graph.py +255 -0
  20. mcp_ticketer/analysis/health_assessment.py +304 -0
  21. mcp_ticketer/analysis/orphaned.py +218 -0
  22. mcp_ticketer/analysis/project_status.py +594 -0
  23. mcp_ticketer/analysis/similarity.py +224 -0
  24. mcp_ticketer/analysis/staleness.py +266 -0
  25. mcp_ticketer/automation/__init__.py +11 -0
  26. mcp_ticketer/automation/project_updates.py +378 -0
  27. mcp_ticketer/cache/memory.py +3 -3
  28. mcp_ticketer/cli/adapter_diagnostics.py +4 -2
  29. mcp_ticketer/cli/auggie_configure.py +18 -6
  30. mcp_ticketer/cli/codex_configure.py +175 -60
  31. mcp_ticketer/cli/configure.py +884 -146
  32. mcp_ticketer/cli/cursor_configure.py +314 -0
  33. mcp_ticketer/cli/diagnostics.py +31 -28
  34. mcp_ticketer/cli/discover.py +293 -21
  35. mcp_ticketer/cli/gemini_configure.py +18 -6
  36. mcp_ticketer/cli/init_command.py +880 -0
  37. mcp_ticketer/cli/instruction_commands.py +435 -0
  38. mcp_ticketer/cli/linear_commands.py +99 -15
  39. mcp_ticketer/cli/main.py +109 -2055
  40. mcp_ticketer/cli/mcp_configure.py +673 -99
  41. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  42. mcp_ticketer/cli/migrate_config.py +12 -8
  43. mcp_ticketer/cli/platform_commands.py +6 -6
  44. mcp_ticketer/cli/platform_detection.py +477 -0
  45. mcp_ticketer/cli/platform_installer.py +536 -0
  46. mcp_ticketer/cli/project_update_commands.py +350 -0
  47. mcp_ticketer/cli/queue_commands.py +15 -15
  48. mcp_ticketer/cli/setup_command.py +639 -0
  49. mcp_ticketer/cli/simple_health.py +13 -11
  50. mcp_ticketer/cli/ticket_commands.py +277 -36
  51. mcp_ticketer/cli/update_checker.py +313 -0
  52. mcp_ticketer/cli/utils.py +45 -41
  53. mcp_ticketer/core/__init__.py +35 -1
  54. mcp_ticketer/core/adapter.py +170 -5
  55. mcp_ticketer/core/config.py +38 -31
  56. mcp_ticketer/core/env_discovery.py +33 -3
  57. mcp_ticketer/core/env_loader.py +7 -6
  58. mcp_ticketer/core/exceptions.py +10 -4
  59. mcp_ticketer/core/http_client.py +10 -10
  60. mcp_ticketer/core/instructions.py +405 -0
  61. mcp_ticketer/core/label_manager.py +732 -0
  62. mcp_ticketer/core/mappers.py +32 -20
  63. mcp_ticketer/core/models.py +136 -1
  64. mcp_ticketer/core/onepassword_secrets.py +379 -0
  65. mcp_ticketer/core/priority_matcher.py +463 -0
  66. mcp_ticketer/core/project_config.py +148 -14
  67. mcp_ticketer/core/registry.py +1 -1
  68. mcp_ticketer/core/session_state.py +171 -0
  69. mcp_ticketer/core/state_matcher.py +592 -0
  70. mcp_ticketer/core/url_parser.py +425 -0
  71. mcp_ticketer/core/validators.py +69 -0
  72. mcp_ticketer/defaults/ticket_instructions.md +644 -0
  73. mcp_ticketer/mcp/__init__.py +2 -2
  74. mcp_ticketer/mcp/server/__init__.py +2 -2
  75. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  76. mcp_ticketer/mcp/server/main.py +187 -93
  77. mcp_ticketer/mcp/server/routing.py +655 -0
  78. mcp_ticketer/mcp/server/server_sdk.py +58 -0
  79. mcp_ticketer/mcp/server/tools/__init__.py +37 -9
  80. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  81. mcp_ticketer/mcp/server/tools/attachment_tools.py +65 -20
  82. mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
  83. mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
  84. mcp_ticketer/mcp/server/tools/config_tools.py +1429 -0
  85. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  86. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +878 -319
  87. mcp_ticketer/mcp/server/tools/instruction_tools.py +295 -0
  88. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  89. mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
  90. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  91. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  92. mcp_ticketer/mcp/server/tools/search_tools.py +180 -97
  93. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  94. mcp_ticketer/mcp/server/tools/ticket_tools.py +1182 -82
  95. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +364 -0
  96. mcp_ticketer/queue/health_monitor.py +1 -0
  97. mcp_ticketer/queue/manager.py +4 -4
  98. mcp_ticketer/queue/queue.py +3 -3
  99. mcp_ticketer/queue/run_worker.py +1 -1
  100. mcp_ticketer/queue/ticket_registry.py +2 -2
  101. mcp_ticketer/queue/worker.py +15 -13
  102. mcp_ticketer/utils/__init__.py +5 -0
  103. mcp_ticketer/utils/token_utils.py +246 -0
  104. mcp_ticketer-2.0.1.dist-info/METADATA +1366 -0
  105. mcp_ticketer-2.0.1.dist-info/RECORD +122 -0
  106. mcp_ticketer-0.4.11.dist-info/METADATA +0 -496
  107. mcp_ticketer-0.4.11.dist-info/RECORD +0 -77
  108. {mcp_ticketer-0.4.11.dist-info โ†’ mcp_ticketer-2.0.1.dist-info}/WHEEL +0 -0
  109. {mcp_ticketer-0.4.11.dist-info โ†’ mcp_ticketer-2.0.1.dist-info}/entry_points.txt +0 -0
  110. {mcp_ticketer-0.4.11.dist-info โ†’ mcp_ticketer-2.0.1.dist-info}/licenses/LICENSE +0 -0
  111. {mcp_ticketer-0.4.11.dist-info โ†’ mcp_ticketer-2.0.1.dist-info}/top_level.txt +0 -0
@@ -24,6 +24,7 @@ def find_codex_config() -> Path:
24
24
  No project-level or user-scoped configuration is available.
25
25
 
26
26
  Returns:
27
+ -------
27
28
  Path to Codex global config file at ~/.codex/config.toml
28
29
 
29
30
  """
@@ -36,9 +37,11 @@ def load_codex_config(config_path: Path) -> dict[str, Any]:
36
37
  """Load existing Codex configuration or return empty structure.
37
38
 
38
39
  Args:
40
+ ----
39
41
  config_path: Path to Codex config.toml file
40
42
 
41
43
  Returns:
44
+ -------
42
45
  Codex configuration dict with mcp_servers section
43
46
 
44
47
  """
@@ -61,6 +64,7 @@ def save_codex_config(config_path: Path, config: dict[str, Any]) -> None:
61
64
  """Save Codex configuration to TOML file.
62
65
 
63
66
  Args:
67
+ ----
64
68
  config_path: Path to Codex config.toml file
65
69
  config: Configuration to save
66
70
 
@@ -78,81 +82,146 @@ def create_codex_server_config(
78
82
  ) -> dict[str, Any]:
79
83
  """Create Codex MCP server configuration for mcp-ticketer.
80
84
 
85
+ Uses the CLI command (mcp-ticketer mcp) which implements proper
86
+ Content-Length framing via FastMCP SDK, required for modern MCP clients.
87
+
81
88
  Args:
89
+ ----
82
90
  python_path: Path to Python executable in mcp-ticketer venv
83
91
  project_config: Project configuration from .mcp-ticketer/config.json
84
- project_path: Project directory path (optional, not used for global config)
92
+ project_path: Project directory path (optional)
85
93
 
86
94
  Returns:
95
+ -------
87
96
  Codex MCP server configuration dict
88
97
 
89
98
  """
90
- # Use Python module invocation pattern (works regardless of where package is installed)
91
-
92
- # Get adapter configuration
93
- adapter = project_config.get("default_adapter", "aitrackdown")
94
- adapters_config = project_config.get("adapters", {})
95
- adapter_config = adapters_config.get(adapter, {})
99
+ # IMPORTANT: Use CLI command, NOT Python module invocation
100
+ # The CLI uses FastMCP SDK which implements proper Content-Length framing
101
+ # Legacy python -m mcp_ticketer.mcp.server uses line-delimited JSON (incompatible)
96
102
 
97
- # Build environment variables
98
- env_vars: dict[str, str] = {}
103
+ # Get mcp-ticketer CLI path from Python path
104
+ # If python_path is /path/to/venv/bin/python, CLI is /path/to/venv/bin/mcp-ticketer
105
+ python_dir = Path(python_path).parent
106
+ cli_path = str(python_dir / "mcp-ticketer")
99
107
 
100
- # Add PYTHONPATH for project context
101
- if project_path:
102
- env_vars["PYTHONPATH"] = project_path
103
-
104
- # Add adapter type
105
- env_vars["MCP_TICKETER_ADAPTER"] = adapter
106
-
107
- # Add adapter-specific environment variables
108
- if adapter == "aitrackdown":
109
- # Set base path for local adapter
110
- base_path = adapter_config.get("base_path", ".aitrackdown")
111
- if project_path:
112
- # Use absolute path if project_path is provided
113
- env_vars["MCP_TICKETER_BASE_PATH"] = str(Path(project_path) / base_path)
114
- else:
115
- env_vars["MCP_TICKETER_BASE_PATH"] = base_path
116
-
117
- elif adapter == "linear":
118
- if "api_key" in adapter_config:
119
- env_vars["LINEAR_API_KEY"] = adapter_config["api_key"]
120
- if "team_id" in adapter_config:
121
- env_vars["LINEAR_TEAM_ID"] = adapter_config["team_id"]
122
-
123
- elif adapter == "github":
124
- if "token" in adapter_config:
125
- env_vars["GITHUB_TOKEN"] = adapter_config["token"]
126
- if "owner" in adapter_config:
127
- env_vars["GITHUB_OWNER"] = adapter_config["owner"]
128
- if "repo" in adapter_config:
129
- env_vars["GITHUB_REPO"] = adapter_config["repo"]
130
-
131
- elif adapter == "jira":
132
- if "api_token" in adapter_config:
133
- env_vars["JIRA_API_TOKEN"] = adapter_config["api_token"]
134
- if "email" in adapter_config:
135
- env_vars["JIRA_EMAIL"] = adapter_config["email"]
136
- if "server" in adapter_config:
137
- env_vars["JIRA_SERVER"] = adapter_config["server"]
138
- if "project_key" in adapter_config:
139
- env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
140
-
141
- # Use Python module invocation pattern
142
- args = ["-m", "mcp_ticketer.mcp.server"]
108
+ # Build CLI arguments
109
+ args = ["mcp"]
143
110
  if project_path:
144
- args.append(project_path)
111
+ args.extend(["--path", project_path])
145
112
 
146
113
  # Create server configuration with Codex-specific structure
114
+ # No environment variables needed - config loaded from .mcp-ticketer/config.json
147
115
  config: dict[str, Any] = {
148
- "command": python_path,
116
+ "command": cli_path,
149
117
  "args": args,
150
- "env": env_vars,
151
118
  }
152
119
 
153
120
  return config
154
121
 
155
122
 
123
+ def _test_configuration(adapter: str, project_config: dict) -> bool:
124
+ """Test the configuration by validating adapter credentials.
125
+
126
+ Args:
127
+ ----
128
+ adapter: Adapter type (linear, github, jira, aitrackdown)
129
+ project_config: Project configuration dict
130
+
131
+ Returns:
132
+ -------
133
+ True if validation passed, False otherwise
134
+
135
+ """
136
+ try:
137
+ from ..core import AdapterRegistry
138
+
139
+ # Get adapter configuration
140
+ adapters_config = project_config.get("adapters", {})
141
+ adapter_config = adapters_config.get(adapter, {})
142
+
143
+ # Test adapter instantiation
144
+ console.print(f" Testing {adapter} adapter...")
145
+
146
+ try:
147
+ adapter_instance = AdapterRegistry.get_adapter(adapter, adapter_config)
148
+ console.print(" [green]โœ“[/green] Adapter instantiated successfully")
149
+
150
+ # Test credentials if validation method exists
151
+ if hasattr(adapter_instance, "validate_credentials"):
152
+ console.print(f" Validating {adapter} credentials...")
153
+ is_valid, error_msg = adapter_instance.validate_credentials()
154
+
155
+ if is_valid:
156
+ console.print(" [green]โœ“[/green] Credentials are valid")
157
+ return True
158
+ else:
159
+ console.print(
160
+ f" [red]โœ—[/red] Credential validation failed: {error_msg}"
161
+ )
162
+ return False
163
+ else:
164
+ # No validation method, assume valid
165
+ console.print(
166
+ f" [yellow]โ—‹[/yellow] No credential validation available for {adapter}"
167
+ )
168
+ return True
169
+
170
+ except Exception as e:
171
+ console.print(f" [red]โœ—[/red] Adapter instantiation failed: {e}")
172
+
173
+ # Provide helpful error messages based on adapter type
174
+ if adapter == "linear":
175
+ console.print(
176
+ "\n [yellow]Linear requires:[/yellow] LINEAR_API_KEY and LINEAR_TEAM_ID"
177
+ )
178
+ elif adapter == "github":
179
+ console.print(
180
+ "\n [yellow]GitHub requires:[/yellow] GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO"
181
+ )
182
+ elif adapter == "jira":
183
+ console.print(
184
+ "\n [yellow]JIRA requires:[/yellow] JIRA_SERVER, JIRA_EMAIL, JIRA_API_TOKEN"
185
+ )
186
+
187
+ return False
188
+
189
+ except Exception as e:
190
+ console.print(f" [red]โœ—[/red] Configuration test error: {e}")
191
+ return False
192
+
193
+
194
+ def detect_legacy_config(config_path: Path) -> tuple[bool, dict[str, Any] | None]:
195
+ """Detect if existing config uses legacy Python module invocation.
196
+
197
+ Args:
198
+ ----
199
+ config_path: Path to Codex config.toml file
200
+
201
+ Returns:
202
+ -------
203
+ Tuple of (is_legacy, server_config):
204
+ - is_legacy: True if config uses 'python -m mcp_ticketer.mcp.server'
205
+ - server_config: The legacy server config dict, or None if not legacy
206
+
207
+ """
208
+ if not config_path.exists():
209
+ return False, None
210
+
211
+ codex_config = load_codex_config(config_path)
212
+ mcp_servers = codex_config.get("mcp_servers", {})
213
+
214
+ if "mcp-ticketer" in mcp_servers:
215
+ server_config = mcp_servers["mcp-ticketer"]
216
+ args = server_config.get("args", [])
217
+
218
+ # Check for legacy pattern: ["-m", "mcp_ticketer.mcp.server", ...]
219
+ if len(args) >= 2 and args[0] == "-m" and "mcp_ticketer.mcp.server" in args[1]:
220
+ return True, server_config
221
+
222
+ return False, None
223
+
224
+
156
225
  def remove_codex_mcp(dry_run: bool = False) -> None:
157
226
  """Remove mcp-ticketer from Codex CLI configuration.
158
227
 
@@ -160,6 +229,7 @@ def remove_codex_mcp(dry_run: bool = False) -> None:
160
229
  This will remove mcp-ticketer from the global configuration.
161
230
 
162
231
  Args:
232
+ ----
163
233
  dry_run: Show what would be removed without making changes
164
234
 
165
235
  """
@@ -230,9 +300,11 @@ def configure_codex_mcp(force: bool = False) -> None:
230
300
  After configuration, you must restart Codex CLI for changes to take effect.
231
301
 
232
302
  Args:
303
+ ----
233
304
  force: Overwrite existing configuration
234
305
 
235
306
  Raises:
307
+ ------
236
308
  FileNotFoundError: If Python executable or project config not found
237
309
  ValueError: If configuration is invalid
238
310
 
@@ -248,7 +320,7 @@ def configure_codex_mcp(force: bool = False) -> None:
248
320
  "Could not find mcp-ticketer Python executable. "
249
321
  "Please ensure mcp-ticketer is installed.\n"
250
322
  "Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
251
- )
323
+ ) from e
252
324
 
253
325
  # Step 2: Load project configuration
254
326
  console.print("\n[cyan]๐Ÿ“– Reading project configuration...[/cyan]")
@@ -269,6 +341,26 @@ def configure_codex_mcp(force: bool = False) -> None:
269
341
  codex_config_path = find_codex_config()
270
342
  console.print(f"[dim]Config location: {codex_config_path}[/dim]")
271
343
 
344
+ # Step 3.5: Check for legacy configuration (DETECTION & MIGRATION)
345
+ is_legacy, legacy_config = detect_legacy_config(codex_config_path)
346
+ if is_legacy:
347
+ console.print("\n[yellow]โš  LEGACY CONFIGURATION DETECTED[/yellow]")
348
+ console.print(
349
+ "[yellow]Your current configuration uses the legacy line-delimited JSON server:[/yellow]"
350
+ )
351
+ console.print(f"[dim] Command: {legacy_config.get('command')}[/dim]")
352
+ console.print(f"[dim] Args: {legacy_config.get('args')}[/dim]")
353
+ console.print(
354
+ "\n[red]This legacy server is incompatible with modern MCP clients (Codex, Claude Desktop/Code).[/red]"
355
+ )
356
+ console.print(
357
+ "[red]The legacy server uses line-delimited JSON instead of Content-Length framing.[/red]"
358
+ )
359
+ console.print(
360
+ "\n[cyan]โœจ Automatically migrating to modern FastMCP-based server...[/cyan]"
361
+ )
362
+ force = True # Auto-enable force mode for migration
363
+
272
364
  # Step 4: Load existing Codex configuration
273
365
  codex_config = load_codex_config(codex_config_path)
274
366
 
@@ -281,7 +373,9 @@ def configure_codex_mcp(force: bool = False) -> None:
281
373
  console.print("[dim]Use --force to overwrite existing configuration[/dim]")
282
374
  return
283
375
  else:
284
- console.print("[yellow]โš  Overwriting existing configuration[/yellow]")
376
+ if not is_legacy:
377
+ console.print("[yellow]โš  Overwriting existing configuration[/yellow]")
378
+ # If is_legacy, we already printed migration message above
285
379
 
286
380
  # Step 6: Create mcp-ticketer server config
287
381
  # For global config, include current working directory for context
@@ -309,12 +403,33 @@ def configure_codex_mcp(force: bool = False) -> None:
309
403
  console.print(" Server name: mcp-ticketer")
310
404
  console.print(f" Adapter: {adapter}")
311
405
  console.print(f" Python: {python_path}")
312
- console.print(" Command: python -m mcp_ticketer.mcp.server")
406
+ console.print(f" Command: {server_config.get('command')}")
407
+ console.print(f" Args: {server_config.get('args')}")
408
+ console.print(" Protocol: Content-Length framing (FastMCP SDK)")
313
409
  console.print(" Scope: global (Codex only supports global config)")
314
410
  console.print(f" Project path: {project_path}")
315
- if "env" in server_config:
411
+
412
+ # Step 9: Test configuration
413
+ console.print("\n[cyan]๐Ÿงช Testing configuration...[/cyan]")
414
+ test_success = _test_configuration(adapter, project_config)
415
+
416
+ if not test_success:
417
+ console.print(
418
+ "[yellow]โš  Configuration saved but validation failed. "
419
+ "Please check your credentials and settings.[/yellow]"
420
+ )
421
+
422
+ # Migration success message (if legacy config was detected)
423
+ if is_legacy:
424
+ console.print("\n[green]โœ… Migration Complete![/green]")
425
+ console.print(
426
+ "[green]Your configuration has been upgraded from legacy line-delimited JSON[/green]"
427
+ )
428
+ console.print(
429
+ "[green]to modern Content-Length framing (FastMCP SDK).[/green]"
430
+ )
316
431
  console.print(
317
- f" Environment variables: {list(server_config['env'].keys())}"
432
+ "\n[cyan]This fixes MCP connection issues with Codex and other modern clients.[/cyan]"
318
433
  )
319
434
 
320
435
  # Next steps