mcp-ticketer 0.1.30__py3-none-any.whl → 1.2.11__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 (109) 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 +796 -46
  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 +879 -129
  11. mcp_ticketer/adapters/hybrid.py +11 -11
  12. mcp_ticketer/adapters/jira.py +973 -73
  13. mcp_ticketer/adapters/linear/__init__.py +24 -0
  14. mcp_ticketer/adapters/linear/adapter.py +2732 -0
  15. mcp_ticketer/adapters/linear/client.py +344 -0
  16. mcp_ticketer/adapters/linear/mappers.py +420 -0
  17. mcp_ticketer/adapters/linear/queries.py +479 -0
  18. mcp_ticketer/adapters/linear/types.py +360 -0
  19. mcp_ticketer/adapters/linear.py +10 -2315
  20. mcp_ticketer/analysis/__init__.py +23 -0
  21. mcp_ticketer/analysis/orphaned.py +218 -0
  22. mcp_ticketer/analysis/similarity.py +224 -0
  23. mcp_ticketer/analysis/staleness.py +266 -0
  24. mcp_ticketer/cache/memory.py +9 -8
  25. mcp_ticketer/cli/adapter_diagnostics.py +421 -0
  26. mcp_ticketer/cli/auggie_configure.py +116 -15
  27. mcp_ticketer/cli/codex_configure.py +274 -82
  28. mcp_ticketer/cli/configure.py +888 -151
  29. mcp_ticketer/cli/diagnostics.py +400 -157
  30. mcp_ticketer/cli/discover.py +297 -26
  31. mcp_ticketer/cli/gemini_configure.py +119 -26
  32. mcp_ticketer/cli/init_command.py +880 -0
  33. mcp_ticketer/cli/instruction_commands.py +435 -0
  34. mcp_ticketer/cli/linear_commands.py +616 -0
  35. mcp_ticketer/cli/main.py +203 -1165
  36. mcp_ticketer/cli/mcp_configure.py +474 -90
  37. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  38. mcp_ticketer/cli/migrate_config.py +12 -8
  39. mcp_ticketer/cli/platform_commands.py +123 -0
  40. mcp_ticketer/cli/platform_detection.py +418 -0
  41. mcp_ticketer/cli/platform_installer.py +513 -0
  42. mcp_ticketer/cli/python_detection.py +126 -0
  43. mcp_ticketer/cli/queue_commands.py +15 -15
  44. mcp_ticketer/cli/setup_command.py +639 -0
  45. mcp_ticketer/cli/simple_health.py +90 -65
  46. mcp_ticketer/cli/ticket_commands.py +1013 -0
  47. mcp_ticketer/cli/update_checker.py +313 -0
  48. mcp_ticketer/cli/utils.py +114 -66
  49. mcp_ticketer/core/__init__.py +24 -1
  50. mcp_ticketer/core/adapter.py +250 -16
  51. mcp_ticketer/core/config.py +145 -37
  52. mcp_ticketer/core/env_discovery.py +101 -22
  53. mcp_ticketer/core/env_loader.py +349 -0
  54. mcp_ticketer/core/exceptions.py +160 -0
  55. mcp_ticketer/core/http_client.py +26 -26
  56. mcp_ticketer/core/instructions.py +405 -0
  57. mcp_ticketer/core/label_manager.py +732 -0
  58. mcp_ticketer/core/mappers.py +42 -30
  59. mcp_ticketer/core/models.py +280 -28
  60. mcp_ticketer/core/onepassword_secrets.py +379 -0
  61. mcp_ticketer/core/project_config.py +183 -49
  62. mcp_ticketer/core/registry.py +3 -3
  63. mcp_ticketer/core/session_state.py +171 -0
  64. mcp_ticketer/core/state_matcher.py +592 -0
  65. mcp_ticketer/core/url_parser.py +425 -0
  66. mcp_ticketer/core/validators.py +69 -0
  67. mcp_ticketer/defaults/ticket_instructions.md +644 -0
  68. mcp_ticketer/mcp/__init__.py +29 -1
  69. mcp_ticketer/mcp/__main__.py +60 -0
  70. mcp_ticketer/mcp/server/__init__.py +25 -0
  71. mcp_ticketer/mcp/server/__main__.py +60 -0
  72. mcp_ticketer/mcp/server/constants.py +58 -0
  73. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  74. mcp_ticketer/mcp/server/dto.py +195 -0
  75. mcp_ticketer/mcp/server/main.py +1343 -0
  76. mcp_ticketer/mcp/server/response_builder.py +206 -0
  77. mcp_ticketer/mcp/server/routing.py +655 -0
  78. mcp_ticketer/mcp/server/server_sdk.py +151 -0
  79. mcp_ticketer/mcp/server/tools/__init__.py +56 -0
  80. mcp_ticketer/mcp/server/tools/analysis_tools.py +495 -0
  81. mcp_ticketer/mcp/server/tools/attachment_tools.py +226 -0
  82. mcp_ticketer/mcp/server/tools/bulk_tools.py +273 -0
  83. mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
  84. mcp_ticketer/mcp/server/tools/config_tools.py +1439 -0
  85. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  86. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +921 -0
  87. mcp_ticketer/mcp/server/tools/instruction_tools.py +300 -0
  88. mcp_ticketer/mcp/server/tools/label_tools.py +948 -0
  89. mcp_ticketer/mcp/server/tools/pr_tools.py +152 -0
  90. mcp_ticketer/mcp/server/tools/search_tools.py +215 -0
  91. mcp_ticketer/mcp/server/tools/session_tools.py +170 -0
  92. mcp_ticketer/mcp/server/tools/ticket_tools.py +1268 -0
  93. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +547 -0
  94. mcp_ticketer/queue/__init__.py +1 -0
  95. mcp_ticketer/queue/health_monitor.py +168 -136
  96. mcp_ticketer/queue/manager.py +95 -25
  97. mcp_ticketer/queue/queue.py +40 -21
  98. mcp_ticketer/queue/run_worker.py +6 -1
  99. mcp_ticketer/queue/ticket_registry.py +213 -155
  100. mcp_ticketer/queue/worker.py +109 -49
  101. mcp_ticketer-1.2.11.dist-info/METADATA +792 -0
  102. mcp_ticketer-1.2.11.dist-info/RECORD +110 -0
  103. mcp_ticketer/mcp/server.py +0 -1895
  104. mcp_ticketer-0.1.30.dist-info/METADATA +0 -413
  105. mcp_ticketer-0.1.30.dist-info/RECORD +0 -49
  106. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/WHEEL +0 -0
  107. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/entry_points.txt +0 -0
  108. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/licenses/LICENSE +0 -0
  109. {mcp_ticketer-0.1.30.dist-info → mcp_ticketer-1.2.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,421 @@
1
+ """Adapter diagnostics and configuration validation."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from rich.console import Console
7
+ from rich.table import Table
8
+
9
+ from ..core import AdapterRegistry
10
+ from ..core.env_discovery import discover_config
11
+
12
+
13
+ def diagnose_adapter_configuration(console: Console) -> None:
14
+ """Diagnose adapter configuration and provide recommendations.
15
+
16
+ Args:
17
+ ----
18
+ console: Rich console for output
19
+
20
+ """
21
+ console.print(
22
+ "\n[bold blue]🔍 MCP Ticketer Adapter Configuration Diagnostics[/bold blue]\n"
23
+ )
24
+
25
+ # 1. Check .env files
26
+ _check_env_files(console)
27
+
28
+ # 2. Check configuration files
29
+ _check_configuration_files(console)
30
+
31
+ # 3. Check adapter discovery
32
+ _check_adapter_discovery(console)
33
+
34
+ # 4. Test adapter instantiation
35
+ _test_adapter_instantiation(console)
36
+
37
+ # 5. Provide recommendations
38
+ _provide_recommendations(console)
39
+
40
+
41
+ def _check_env_files(console: Console) -> None:
42
+ """Check .env files for configuration."""
43
+ console.print("[bold]1. .env File Configuration[/bold]")
44
+
45
+ # Load .env files
46
+ from ..mcp.server import _load_env_configuration
47
+
48
+ env_config = _load_env_configuration()
49
+
50
+ # Check for .env files
51
+ env_files = [".env.local", ".env"]
52
+
53
+ table = Table(show_header=True, header_style="bold magenta")
54
+ table.add_column("File", style="cyan")
55
+ table.add_column("Status", style="green")
56
+ table.add_column("Variables Found", style="yellow")
57
+
58
+ for env_file in env_files:
59
+ env_path = Path.cwd() / env_file
60
+ if env_path.exists():
61
+ try:
62
+ # Count variables in file
63
+ var_count = 0
64
+ with open(env_path) as f:
65
+ for line in f:
66
+ line = line.strip()
67
+ if line and not line.startswith("#") and "=" in line:
68
+ var_count += 1
69
+
70
+ status = "✅ Found"
71
+ variables = f"{var_count} variables"
72
+ except Exception:
73
+ status = "⚠️ Error reading"
74
+ variables = "Unknown"
75
+ else:
76
+ status = "❌ Missing"
77
+ variables = "N/A"
78
+
79
+ table.add_row(env_file, status, variables)
80
+
81
+ console.print(table)
82
+
83
+ # Show discovered configuration
84
+ if env_config:
85
+ console.print(
86
+ f"\n[green]✅ Discovered adapter: {env_config['adapter_type']}[/green]"
87
+ )
88
+ config_keys = list(env_config["adapter_config"].keys())
89
+ console.print(f"[dim]Configuration keys: {config_keys}[/dim]")
90
+ else:
91
+ console.print(
92
+ "\n[yellow]⚠️ No adapter configuration found in .env files[/yellow]"
93
+ )
94
+
95
+ console.print()
96
+
97
+
98
+ def _check_configuration_files(console: Console) -> None:
99
+ """Check configuration files."""
100
+ console.print("[bold]2. Configuration Files[/bold]")
101
+
102
+ config_files = [
103
+ (".env.local", "Local environment file (highest priority)"),
104
+ (".env", "Environment file"),
105
+ (".mcp-ticketer/config.json", "Project configuration"),
106
+ (str(Path.home() / ".mcp-ticketer" / "config.json"), "Global configuration"),
107
+ ]
108
+
109
+ table = Table(show_header=True, header_style="bold magenta")
110
+ table.add_column("File", style="cyan")
111
+ table.add_column("Description", style="white")
112
+ table.add_column("Status", style="green")
113
+ table.add_column("Size", style="yellow")
114
+
115
+ for file_path, description in config_files:
116
+ path = Path(file_path)
117
+ if path.exists():
118
+ try:
119
+ size = path.stat().st_size
120
+ status = "✅ Found"
121
+ size_str = f"{size} bytes"
122
+ except Exception:
123
+ status = "⚠️ Error"
124
+ size_str = "Unknown"
125
+ else:
126
+ status = "❌ Missing"
127
+ size_str = "N/A"
128
+
129
+ table.add_row(str(path), description, status, size_str)
130
+
131
+ console.print(table)
132
+ console.print()
133
+
134
+
135
+ def _check_adapter_discovery(console: Console) -> None:
136
+ """Check adapter discovery from configuration."""
137
+ console.print("[bold]3. Adapter Discovery[/bold]")
138
+
139
+ try:
140
+ # Discover configuration
141
+ discovered = discover_config(Path.cwd())
142
+
143
+ if discovered and discovered.adapters:
144
+ primary = discovered.get_primary_adapter()
145
+
146
+ table = Table(show_header=True, header_style="bold magenta")
147
+ table.add_column("Adapter", style="cyan")
148
+ table.add_column("Confidence", style="white")
149
+ table.add_column("Source", style="green")
150
+ table.add_column("Status", style="yellow")
151
+
152
+ for adapter_info in discovered.adapters:
153
+ confidence = f"{adapter_info.confidence:.0%}"
154
+ status = "✅ Primary" if adapter_info == primary else "⚪ Available"
155
+
156
+ table.add_row(
157
+ adapter_info.adapter_type, confidence, adapter_info.found_in, status
158
+ )
159
+
160
+ console.print(table)
161
+
162
+ if primary:
163
+ console.print(
164
+ f"\n[green]✅ Primary adapter detected: {primary.adapter_type}[/green]"
165
+ )
166
+ console.print(f"[dim]Source: {primary.found_in}[/dim]")
167
+ console.print(f"[dim]Confidence: {primary.confidence:.0%}[/dim]")
168
+ else:
169
+ console.print("\n[yellow]⚠️ No primary adapter detected[/yellow]")
170
+ else:
171
+ console.print("[red]❌ No adapters discovered[/red]")
172
+ console.print("[dim]This usually means no credentials are configured[/dim]")
173
+
174
+ except Exception as e:
175
+ console.print(f"[red]❌ Error during discovery: {e}[/red]")
176
+
177
+ console.print()
178
+
179
+
180
+ def _test_adapter_instantiation(console: Console) -> None:
181
+ """Test adapter instantiation."""
182
+ console.print("[bold]4. Adapter Instantiation Test[/bold]")
183
+
184
+ # Determine which adapter to test from .env files
185
+ from ..mcp.server import _load_env_configuration
186
+
187
+ env_config = _load_env_configuration()
188
+
189
+ if env_config:
190
+ adapter_type = env_config["adapter_type"]
191
+ config = env_config["adapter_config"]
192
+ else:
193
+ # Try to discover from existing discovery system
194
+ try:
195
+ discovered = discover_config(Path.cwd())
196
+ if discovered and discovered.adapters:
197
+ primary = discovered.get_primary_adapter()
198
+ if primary:
199
+ adapter_type = primary.adapter_type
200
+ # Build config from discovery
201
+ from ..mcp.server import _build_adapter_config_from_env_vars
202
+
203
+ config = _build_adapter_config_from_env_vars(adapter_type, {})
204
+ else:
205
+ adapter_type = "aitrackdown"
206
+ config = {"base_path": ".aitrackdown"}
207
+ else:
208
+ adapter_type = "aitrackdown"
209
+ config = {"base_path": ".aitrackdown"}
210
+ except Exception:
211
+ adapter_type = "aitrackdown"
212
+ config = {"base_path": ".aitrackdown"}
213
+
214
+ console.print(f"Testing adapter: [cyan]{adapter_type}[/cyan]")
215
+ console.print(f"Configuration keys: [yellow]{list(config.keys())}[/yellow]")
216
+
217
+ try:
218
+ # Try to instantiate adapter
219
+ adapter = AdapterRegistry.get_adapter(adapter_type, config)
220
+
221
+ console.print(
222
+ f"[green]✅ Adapter instantiated successfully: {adapter.__class__.__name__}[/green]"
223
+ )
224
+
225
+ # Test basic functionality
226
+ if hasattr(adapter, "validate_credentials"):
227
+ try:
228
+ is_valid, error_msg = adapter.validate_credentials()
229
+ if is_valid:
230
+ console.print("[green]✅ Credentials validation passed[/green]")
231
+ else:
232
+ console.print(
233
+ f"[red]❌ Credentials validation failed: {error_msg}[/red]"
234
+ )
235
+ except Exception as e:
236
+ console.print(f"[yellow]⚠️ Credentials validation error: {e}[/yellow]")
237
+
238
+ except Exception as e:
239
+ console.print(f"[red]❌ Adapter instantiation failed: {e}[/red]")
240
+
241
+ # Provide specific guidance based on adapter type
242
+ if adapter_type == "linear":
243
+ console.print(
244
+ "\n[yellow]Linear adapter requires in .env/.env.local:[/yellow]"
245
+ )
246
+ console.print("• LINEAR_API_KEY=your_api_key")
247
+ console.print(
248
+ "• LINEAR_TEAM_ID=your_team_id (or LINEAR_TEAM_KEY=your_team_key)"
249
+ )
250
+ elif adapter_type == "github":
251
+ console.print(
252
+ "\n[yellow]GitHub adapter requires in .env/.env.local:[/yellow]"
253
+ )
254
+ console.print("• GITHUB_TOKEN=your_token")
255
+ console.print("• GITHUB_OWNER=your_username")
256
+ console.print("• GITHUB_REPO=your_repository")
257
+ elif adapter_type == "jira":
258
+ console.print(
259
+ "\n[yellow]JIRA adapter requires in .env/.env.local:[/yellow]"
260
+ )
261
+ console.print("• JIRA_SERVER=your_server_url")
262
+ console.print("• JIRA_EMAIL=your_email")
263
+ console.print("• JIRA_API_TOKEN=your_token")
264
+
265
+ console.print()
266
+
267
+
268
+ def _provide_recommendations(console: Console) -> None:
269
+ """Provide configuration recommendations."""
270
+ console.print("[bold]5. Recommendations[/bold]")
271
+
272
+ # Check .env configuration
273
+ from ..mcp.server import _load_env_configuration
274
+
275
+ env_config = _load_env_configuration()
276
+
277
+ recommendations = []
278
+
279
+ if not env_config:
280
+ recommendations.append(
281
+ "Create .env.local or .env file with adapter configuration"
282
+ )
283
+ recommendations.append(
284
+ "Add MCP_TICKETER_ADAPTER=linear (or github, jira) to specify adapter type"
285
+ )
286
+ else:
287
+ adapter_type = env_config["adapter_type"]
288
+ config = env_config["adapter_config"]
289
+
290
+ # Check for incomplete configurations
291
+ if adapter_type == "linear":
292
+ if not config.get("api_key"):
293
+ recommendations.append("Add LINEAR_API_KEY to .env file")
294
+ if not config.get("team_id") and not config.get("team_key"):
295
+ recommendations.append(
296
+ "Add LINEAR_TEAM_ID or LINEAR_TEAM_KEY to .env file"
297
+ )
298
+
299
+ elif adapter_type == "github":
300
+ missing = []
301
+ if not config.get("token"):
302
+ missing.append("GITHUB_TOKEN")
303
+ if not config.get("owner"):
304
+ missing.append("GITHUB_OWNER")
305
+ if not config.get("repo"):
306
+ missing.append("GITHUB_REPO")
307
+ if missing:
308
+ recommendations.append(
309
+ f"Add missing GitHub variables to .env: {', '.join(missing)}"
310
+ )
311
+
312
+ elif adapter_type == "jira":
313
+ missing = []
314
+ if not config.get("server"):
315
+ missing.append("JIRA_SERVER")
316
+ if not config.get("email"):
317
+ missing.append("JIRA_EMAIL")
318
+ if not config.get("api_token"):
319
+ missing.append("JIRA_API_TOKEN")
320
+ if missing:
321
+ recommendations.append(
322
+ f"Add missing JIRA variables to .env: {', '.join(missing)}"
323
+ )
324
+
325
+ if recommendations:
326
+ for i, rec in enumerate(recommendations, 1):
327
+ console.print(f"{i}. [yellow]{rec}[/yellow]")
328
+ else:
329
+ console.print("[green]✅ Configuration looks good![/green]")
330
+
331
+ # Show .env file examples
332
+ console.print("\n[bold].env File Examples:[/bold]")
333
+ console.print(
334
+ "• Linear: [cyan]echo 'MCP_TICKETER_ADAPTER=linear\\nLINEAR_API_KEY=your_key\\nLINEAR_TEAM_ID=your_team' > .env.local[/cyan]"
335
+ )
336
+ console.print(
337
+ "• GitHub: [cyan]echo 'MCP_TICKETER_ADAPTER=github\\nGITHUB_TOKEN=your_token\\nGITHUB_OWNER=user\\nGITHUB_REPO=repo' > .env.local[/cyan]"
338
+ )
339
+
340
+ console.print("\n[bold]Quick Setup Commands:[/bold]")
341
+ console.print("• For Linear: [cyan]mcp-ticketer init linear[/cyan]")
342
+ console.print("• For GitHub: [cyan]mcp-ticketer init github[/cyan]")
343
+ console.print("• For JIRA: [cyan]mcp-ticketer init jira[/cyan]")
344
+ console.print("• For local files: [cyan]mcp-ticketer init aitrackdown[/cyan]")
345
+
346
+ console.print("\n[bold]Test Configuration:[/bold]")
347
+ console.print("• Run diagnostics: [cyan]mcp-ticketer doctor[/cyan]")
348
+ console.print(
349
+ "• Test ticket creation: [cyan]mcp-ticketer create 'Test ticket'[/cyan]"
350
+ )
351
+ console.print("• List tickets: [cyan]mcp-ticketer list[/cyan]")
352
+
353
+
354
+ def get_adapter_status() -> dict[str, Any]:
355
+ """Get current adapter status for programmatic use.
356
+
357
+ Returns:
358
+ -------
359
+ Dictionary with adapter status information
360
+
361
+ """
362
+ status: dict[str, Any] = {
363
+ "adapter_type": None,
364
+ "configuration_source": None,
365
+ "credentials_valid": False,
366
+ "error_message": None,
367
+ "recommendations": [],
368
+ }
369
+
370
+ try:
371
+ # Check .env files first
372
+ from ..mcp.server import _load_env_configuration
373
+
374
+ env_config = _load_env_configuration()
375
+
376
+ if env_config:
377
+ adapter_type = env_config["adapter_type"]
378
+ config = env_config["adapter_config"]
379
+ status["configuration_source"] = ".env files"
380
+ else:
381
+ # Try discovery system
382
+ discovered = discover_config(Path.cwd())
383
+ if discovered and discovered.adapters:
384
+ primary = discovered.get_primary_adapter()
385
+ if primary:
386
+ adapter_type = primary.adapter_type
387
+ status["configuration_source"] = primary.found_in
388
+ # Build basic config
389
+ from ..mcp.server import _build_adapter_config_from_env_vars
390
+
391
+ config = _build_adapter_config_from_env_vars(adapter_type, {})
392
+ else:
393
+ adapter_type = "aitrackdown"
394
+ config = {"base_path": ".aitrackdown"}
395
+ status["configuration_source"] = "default"
396
+ else:
397
+ adapter_type = "aitrackdown"
398
+ config = {"base_path": ".aitrackdown"}
399
+ status["configuration_source"] = "default"
400
+
401
+ status["adapter_type"] = adapter_type
402
+
403
+ # Test adapter instantiation
404
+ adapter = AdapterRegistry.get_adapter(adapter_type, config)
405
+
406
+ # Test credentials if possible
407
+ if hasattr(adapter, "validate_credentials"):
408
+ is_valid, error_msg = adapter.validate_credentials()
409
+ status["credentials_valid"] = is_valid
410
+ if not is_valid:
411
+ status["error_message"] = error_msg
412
+ else:
413
+ status["credentials_valid"] = True # Assume valid if no validation method
414
+
415
+ except Exception as e:
416
+ status["error_message"] = str(e)
417
+ status["recommendations"].append(
418
+ "Check .env file configuration and credentials"
419
+ )
420
+
421
+ return status
@@ -10,7 +10,8 @@ from typing import Any
10
10
 
11
11
  from rich.console import Console
12
12
 
13
- from .mcp_configure import find_mcp_ticketer_binary, load_project_config
13
+ from .mcp_configure import load_project_config
14
+ from .python_detection import get_mcp_ticketer_python
14
15
 
15
16
  console = Console()
16
17
 
@@ -71,18 +72,27 @@ def save_auggie_config(config_path: Path, config: dict[str, Any]) -> None:
71
72
 
72
73
 
73
74
  def create_auggie_server_config(
74
- binary_path: str, project_config: dict[str, Any]
75
+ python_path: str, project_config: dict[str, Any], project_path: str | None = None
75
76
  ) -> dict[str, Any]:
76
77
  """Create Auggie MCP server configuration for mcp-ticketer.
77
78
 
79
+ Uses the CLI command (mcp-ticketer mcp) which implements proper
80
+ Content-Length framing via FastMCP SDK, required for modern MCP clients.
81
+
78
82
  Args:
79
- binary_path: Path to mcp-ticketer binary
83
+ python_path: Path to Python executable in mcp-ticketer venv
80
84
  project_config: Project configuration from .mcp-ticketer/config.json
85
+ project_path: Project directory path (optional)
81
86
 
82
87
  Returns:
83
88
  Auggie MCP server configuration dict
84
89
 
85
90
  """
91
+ # IMPORTANT: Use CLI command, NOT Python module invocation
92
+ # The CLI uses FastMCP SDK which implements proper Content-Length framing
93
+ # Legacy python -m mcp_ticketer.mcp.server uses line-delimited JSON (incompatible)
94
+ from pathlib import Path
95
+
86
96
  # Get adapter configuration
87
97
  adapter = project_config.get("default_adapter", "aitrackdown")
88
98
  adapters_config = project_config.get("adapters", {})
@@ -91,6 +101,10 @@ def create_auggie_server_config(
91
101
  # Build environment variables
92
102
  env_vars = {}
93
103
 
104
+ # Add PYTHONPATH for project context
105
+ if project_path:
106
+ env_vars["PYTHONPATH"] = project_path
107
+
94
108
  # Add adapter type
95
109
  env_vars["MCP_TICKETER_ADAPTER"] = adapter
96
110
 
@@ -128,16 +142,94 @@ def create_auggie_server_config(
128
142
  if "project_key" in adapter_config:
129
143
  env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
130
144
 
145
+ # Get mcp-ticketer CLI path from Python path
146
+ # If python_path is /path/to/venv/bin/python, CLI is /path/to/venv/bin/mcp-ticketer
147
+ python_dir = Path(python_path).parent
148
+ cli_path = str(python_dir / "mcp-ticketer")
149
+
150
+ # Build CLI arguments
151
+ args = ["mcp"]
152
+ if project_path:
153
+ args.extend(["--path", project_path])
154
+
131
155
  # Create server configuration (simpler than Gemini - no timeout/trust)
156
+ # NOTE: Environment variables below are optional fallbacks
157
+ # The CLI loads config from .mcp-ticketer/config.json
132
158
  config = {
133
- "command": binary_path,
134
- "args": ["serve"],
159
+ "command": cli_path,
160
+ "args": args,
135
161
  "env": env_vars,
136
162
  }
137
163
 
138
164
  return config
139
165
 
140
166
 
167
+ def remove_auggie_mcp(dry_run: bool = False) -> None:
168
+ """Remove mcp-ticketer from Auggie CLI configuration.
169
+
170
+ IMPORTANT: Auggie CLI ONLY supports global configuration.
171
+ This will remove mcp-ticketer from ~/.augment/settings.json.
172
+
173
+ Args:
174
+ dry_run: Show what would be removed without making changes
175
+
176
+ """
177
+ # Step 1: Find Auggie config location
178
+ console.print("[cyan]🔍 Removing Auggie CLI global configuration...[/cyan]")
179
+ console.print(
180
+ "[yellow]⚠ NOTE: Auggie only supports global configuration (affects all projects)[/yellow]"
181
+ )
182
+
183
+ auggie_config_path = find_auggie_config()
184
+ console.print(f"[dim]Config location: {auggie_config_path}[/dim]")
185
+
186
+ # Step 2: Check if config file exists
187
+ if not auggie_config_path.exists():
188
+ console.print(
189
+ f"[yellow]⚠ No configuration found at {auggie_config_path}[/yellow]"
190
+ )
191
+ console.print("[dim]mcp-ticketer is not configured for Auggie[/dim]")
192
+ return
193
+
194
+ # Step 3: Load existing Auggie configuration
195
+ auggie_config = load_auggie_config(auggie_config_path)
196
+
197
+ # Step 4: Check if mcp-ticketer is configured
198
+ if "mcp-ticketer" not in auggie_config.get("mcpServers", {}):
199
+ console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
200
+ console.print(f"[dim]No mcp-ticketer entry found in {auggie_config_path}[/dim]")
201
+ return
202
+
203
+ # Step 5: Show what would be removed (dry run or actual removal)
204
+ if dry_run:
205
+ console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
206
+ console.print(" Server name: mcp-ticketer")
207
+ console.print(f" From: {auggie_config_path}")
208
+ console.print(" Scope: Global (all projects)")
209
+ return
210
+
211
+ # Step 6: Remove mcp-ticketer from configuration
212
+ del auggie_config["mcpServers"]["mcp-ticketer"]
213
+
214
+ # Step 7: Save updated configuration
215
+ try:
216
+ save_auggie_config(auggie_config_path, auggie_config)
217
+ console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
218
+ console.print(f"[dim]Configuration updated: {auggie_config_path}[/dim]")
219
+
220
+ # Next steps
221
+ console.print("\n[bold cyan]Next Steps:[/bold cyan]")
222
+ console.print("1. Restart Auggie CLI for changes to take effect")
223
+ console.print("2. mcp-ticketer will no longer be available via MCP")
224
+ console.print(
225
+ "\n[yellow]⚠ Note: This removes global configuration affecting all projects[/yellow]"
226
+ )
227
+
228
+ except Exception as e:
229
+ console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
230
+ raise
231
+
232
+
141
233
  def configure_auggie_mcp(force: bool = False) -> None:
142
234
  """Configure Auggie CLI to use mcp-ticketer.
143
235
 
@@ -148,18 +240,22 @@ def configure_auggie_mcp(force: bool = False) -> None:
148
240
  force: Overwrite existing configuration
149
241
 
150
242
  Raises:
151
- FileNotFoundError: If binary or project config not found
243
+ FileNotFoundError: If Python executable or project config not found
152
244
  ValueError: If configuration is invalid
153
245
 
154
246
  """
155
- # Step 1: Find mcp-ticketer binary
156
- console.print("[cyan]🔍 Finding mcp-ticketer binary...[/cyan]")
247
+ # Step 1: Find Python executable
248
+ console.print("[cyan]🔍 Finding mcp-ticketer Python executable...[/cyan]")
157
249
  try:
158
- binary_path = find_mcp_ticketer_binary()
159
- console.print(f"[green]✓[/green] Found: {binary_path}")
160
- except FileNotFoundError as e:
161
- console.print(f"[red]✗[/red] {e}")
162
- raise
250
+ python_path = get_mcp_ticketer_python()
251
+ console.print(f"[green]✓[/green] Found: {python_path}")
252
+ except Exception as e:
253
+ console.print(f"[red]✗[/red] Could not find Python executable: {e}")
254
+ raise FileNotFoundError(
255
+ "Could not find mcp-ticketer Python executable. "
256
+ "Please ensure mcp-ticketer is installed.\n"
257
+ "Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
258
+ ) from e
163
259
 
164
260
  # Step 2: Load project configuration
165
261
  console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
@@ -193,8 +289,11 @@ def configure_auggie_mcp(force: bool = False) -> None:
193
289
  console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
194
290
 
195
291
  # Step 6: Create mcp-ticketer server config
292
+ project_path = str(Path.cwd())
196
293
  server_config = create_auggie_server_config(
197
- binary_path=binary_path, project_config=project_config
294
+ python_path=python_path,
295
+ project_config=project_config,
296
+ project_path=project_path,
198
297
  )
199
298
 
200
299
  # Step 7: Update Auggie configuration
@@ -213,8 +312,10 @@ def configure_auggie_mcp(force: bool = False) -> None:
213
312
  console.print("\n[bold]Configuration Details:[/bold]")
214
313
  console.print(" Server name: mcp-ticketer")
215
314
  console.print(f" Adapter: {adapter}")
216
- console.print(f" Binary: {binary_path}")
315
+ console.print(f" Python: {python_path}")
316
+ console.print(" Command: python -m mcp_ticketer.mcp.server")
217
317
  console.print(" Scope: Global (affects all projects)")
318
+ console.print(f" Project path: {project_path}")
218
319
  if "env" in server_config:
219
320
  console.print(
220
321
  f" Environment variables: {list(server_config['env'].keys())}"