mcp-ticketer 0.12.0__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 (87) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +1 -1
  3. mcp_ticketer/adapters/aitrackdown.py +385 -6
  4. mcp_ticketer/adapters/asana/adapter.py +108 -0
  5. mcp_ticketer/adapters/asana/mappers.py +14 -0
  6. mcp_ticketer/adapters/github.py +525 -11
  7. mcp_ticketer/adapters/hybrid.py +47 -5
  8. mcp_ticketer/adapters/jira.py +521 -0
  9. mcp_ticketer/adapters/linear/adapter.py +1784 -101
  10. mcp_ticketer/adapters/linear/client.py +85 -3
  11. mcp_ticketer/adapters/linear/mappers.py +96 -8
  12. mcp_ticketer/adapters/linear/queries.py +168 -1
  13. mcp_ticketer/adapters/linear/types.py +80 -4
  14. mcp_ticketer/analysis/__init__.py +56 -0
  15. mcp_ticketer/analysis/dependency_graph.py +255 -0
  16. mcp_ticketer/analysis/health_assessment.py +304 -0
  17. mcp_ticketer/analysis/orphaned.py +218 -0
  18. mcp_ticketer/analysis/project_status.py +594 -0
  19. mcp_ticketer/analysis/similarity.py +224 -0
  20. mcp_ticketer/analysis/staleness.py +266 -0
  21. mcp_ticketer/automation/__init__.py +11 -0
  22. mcp_ticketer/automation/project_updates.py +378 -0
  23. mcp_ticketer/cli/adapter_diagnostics.py +3 -1
  24. mcp_ticketer/cli/auggie_configure.py +17 -5
  25. mcp_ticketer/cli/codex_configure.py +97 -61
  26. mcp_ticketer/cli/configure.py +851 -103
  27. mcp_ticketer/cli/cursor_configure.py +314 -0
  28. mcp_ticketer/cli/diagnostics.py +13 -12
  29. mcp_ticketer/cli/discover.py +5 -0
  30. mcp_ticketer/cli/gemini_configure.py +17 -5
  31. mcp_ticketer/cli/init_command.py +880 -0
  32. mcp_ticketer/cli/instruction_commands.py +6 -0
  33. mcp_ticketer/cli/main.py +233 -3151
  34. mcp_ticketer/cli/mcp_configure.py +672 -98
  35. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  36. mcp_ticketer/cli/platform_detection.py +77 -12
  37. mcp_ticketer/cli/platform_installer.py +536 -0
  38. mcp_ticketer/cli/project_update_commands.py +350 -0
  39. mcp_ticketer/cli/setup_command.py +639 -0
  40. mcp_ticketer/cli/simple_health.py +12 -10
  41. mcp_ticketer/cli/ticket_commands.py +264 -24
  42. mcp_ticketer/core/__init__.py +28 -6
  43. mcp_ticketer/core/adapter.py +166 -1
  44. mcp_ticketer/core/config.py +21 -21
  45. mcp_ticketer/core/exceptions.py +7 -1
  46. mcp_ticketer/core/label_manager.py +732 -0
  47. mcp_ticketer/core/mappers.py +31 -19
  48. mcp_ticketer/core/models.py +135 -0
  49. mcp_ticketer/core/onepassword_secrets.py +1 -1
  50. mcp_ticketer/core/priority_matcher.py +463 -0
  51. mcp_ticketer/core/project_config.py +132 -14
  52. mcp_ticketer/core/session_state.py +171 -0
  53. mcp_ticketer/core/state_matcher.py +592 -0
  54. mcp_ticketer/core/url_parser.py +425 -0
  55. mcp_ticketer/core/validators.py +69 -0
  56. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  57. mcp_ticketer/mcp/server/main.py +106 -25
  58. mcp_ticketer/mcp/server/routing.py +655 -0
  59. mcp_ticketer/mcp/server/server_sdk.py +58 -0
  60. mcp_ticketer/mcp/server/tools/__init__.py +31 -12
  61. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  62. mcp_ticketer/mcp/server/tools/attachment_tools.py +6 -8
  63. mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
  64. mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
  65. mcp_ticketer/mcp/server/tools/config_tools.py +1184 -136
  66. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  67. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
  68. mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
  69. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  70. mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
  71. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  72. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  73. mcp_ticketer/mcp/server/tools/search_tools.py +180 -97
  74. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  75. mcp_ticketer/mcp/server/tools/ticket_tools.py +1070 -123
  76. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
  77. mcp_ticketer/queue/worker.py +1 -1
  78. mcp_ticketer/utils/__init__.py +5 -0
  79. mcp_ticketer/utils/token_utils.py +246 -0
  80. mcp_ticketer-2.0.1.dist-info/METADATA +1366 -0
  81. mcp_ticketer-2.0.1.dist-info/RECORD +122 -0
  82. mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
  83. mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
  84. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/WHEEL +0 -0
  85. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/entry_points.txt +0 -0
  86. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/licenses/LICENSE +0 -0
  87. {mcp_ticketer-0.12.0.dist-info → mcp_ticketer-2.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,536 @@
1
+ """Platform installation commands for mcp-ticketer MCP server.
2
+
3
+ This module provides commands for installing and removing mcp-ticketer
4
+ MCP server configuration for various AI platforms (Claude, Gemini, Codex, Auggie).
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ # Import for typing compatibility
14
+ from .init_command import init
15
+ from .platform_detection import PlatformDetector, get_platform_by_name
16
+
17
+ console = Console()
18
+
19
+
20
+ def install(
21
+ platform: str | None = typer.Argument(
22
+ None,
23
+ help="Platform to install (claude-code, cursor, gemini, codex, auggie). Use claude-desktop for desktop AI assistant.",
24
+ ),
25
+ auto_detect: bool = typer.Option(
26
+ False,
27
+ "--auto-detect",
28
+ "-d",
29
+ help="Auto-detect and show all code editors (excludes desktop AI assistants by default)",
30
+ ),
31
+ install_all: bool = typer.Option(
32
+ False,
33
+ "--all",
34
+ help="Install for all detected code editors (excludes Claude Desktop unless --include-desktop specified)",
35
+ ),
36
+ include_desktop: bool = typer.Option(
37
+ False,
38
+ "--include-desktop",
39
+ help="Include Claude Desktop in auto-detection and --all installation",
40
+ ),
41
+ adapter: str | None = typer.Option(
42
+ None,
43
+ "--adapter",
44
+ "-a",
45
+ help="Adapter type to use (interactive prompt if not specified)",
46
+ ),
47
+ project_path: str | None = typer.Option(
48
+ None, "--path", help="Project path (default: current directory)"
49
+ ),
50
+ global_config: bool = typer.Option(
51
+ False,
52
+ "--global",
53
+ "-g",
54
+ help="Save to global config instead of project-specific",
55
+ ),
56
+ base_path: str | None = typer.Option(
57
+ None,
58
+ "--base-path",
59
+ "-p",
60
+ help="Base path for ticket storage (AITrackdown only)",
61
+ ),
62
+ api_key: str | None = typer.Option(
63
+ None, "--api-key", help="API key for Linear or API token for JIRA"
64
+ ),
65
+ team_id: str | None = typer.Option(
66
+ None, "--team-id", help="Linear team ID (required for Linear adapter)"
67
+ ),
68
+ jira_server: str | None = typer.Option(
69
+ None,
70
+ "--jira-server",
71
+ help="JIRA server URL (e.g., https://company.atlassian.net)",
72
+ ),
73
+ jira_email: str | None = typer.Option(
74
+ None, "--jira-email", help="JIRA user email for authentication"
75
+ ),
76
+ jira_project: str | None = typer.Option(
77
+ None, "--jira-project", help="Default JIRA project key"
78
+ ),
79
+ github_owner: str | None = typer.Option(
80
+ None, "--github-owner", help="GitHub repository owner"
81
+ ),
82
+ github_repo: str | None = typer.Option(
83
+ None, "--github-repo", help="GitHub repository name"
84
+ ),
85
+ github_token: str | None = typer.Option(
86
+ None, "--github-token", help="GitHub Personal Access Token"
87
+ ),
88
+ dry_run: bool = typer.Option(
89
+ False,
90
+ "--dry-run",
91
+ help="Show what would be done without making changes (for platform installation)",
92
+ ),
93
+ ) -> None:
94
+ """Install MCP server configuration for AI platforms.
95
+
96
+ This command configures mcp-ticketer as an MCP server for various AI
97
+ platforms. It updates platform-specific configuration files to enable
98
+ mcp-ticketer integration.
99
+
100
+ RECOMMENDED: Use 'mcp-ticketer setup' for first-time setup, which
101
+ handles both adapter configuration and platform installation together.
102
+
103
+ Platform Installation:
104
+ # Auto-detect and prompt for platform selection
105
+ mcp-ticketer install
106
+
107
+ # Show all detected platforms
108
+ mcp-ticketer install --auto-detect
109
+
110
+ # Install for all detected platforms
111
+ mcp-ticketer install --all
112
+
113
+ # Install for specific platform
114
+ mcp-ticketer install claude-code # Claude Code (project-level)
115
+ mcp-ticketer install claude-desktop # Claude Desktop (global)
116
+ mcp-ticketer install gemini # Gemini CLI
117
+ mcp-ticketer install codex # Codex
118
+ mcp-ticketer install auggie # Auggie
119
+
120
+ Legacy Usage (adapter setup, deprecated - use 'init' or 'setup' instead):
121
+ mcp-ticketer install --adapter linear # Use 'init' or 'setup' instead
122
+
123
+ """
124
+ detector = PlatformDetector()
125
+
126
+ # Handle auto-detect flag (just show detected platforms and exit)
127
+ if auto_detect:
128
+ detected = detector.detect_all(
129
+ project_path=Path(project_path) if project_path else Path.cwd(),
130
+ exclude_desktop=not include_desktop,
131
+ )
132
+
133
+ if not detected:
134
+ console.print("[yellow]No code editors detected.[/yellow]")
135
+ console.print("\n[bold]Supported code editors:[/bold]")
136
+ console.print(" • Claude Code - Project-level AI code assistant")
137
+ console.print(" • Cursor - AI-powered code editor")
138
+ console.print(" • Auggie - CLI code assistant")
139
+ console.print(" • Codex - CLI code assistant")
140
+ console.print(" • Gemini - CLI code assistant")
141
+ if not include_desktop:
142
+ console.print(
143
+ "\n[dim]Use --include-desktop to also detect Claude Desktop (desktop AI assistant)[/dim]"
144
+ )
145
+ console.print(
146
+ "\n[dim]Install these platforms to use them with mcp-ticketer.[/dim]"
147
+ )
148
+ return
149
+
150
+ console.print("[bold]Detected AI platforms:[/bold]\n")
151
+ table = Table(show_header=True, header_style="bold cyan")
152
+ table.add_column("Platform", style="green")
153
+ table.add_column("Status", style="yellow")
154
+ table.add_column("Scope", style="blue")
155
+ table.add_column("Config Path", style="dim")
156
+
157
+ for plat in detected:
158
+ status = "✓ Installed" if plat.is_installed else "⚠ Config Issue"
159
+ table.add_row(plat.display_name, status, plat.scope, str(plat.config_path))
160
+
161
+ console.print(table)
162
+ console.print(
163
+ "\n[dim]Run 'mcp-ticketer install <platform>' to configure a specific platform[/dim]"
164
+ )
165
+ console.print(
166
+ "[dim]Run 'mcp-ticketer install --all' to configure all detected platforms[/dim]"
167
+ )
168
+ return
169
+
170
+ # Handle --all flag (install for all detected platforms)
171
+ if install_all:
172
+ detected = detector.detect_all(
173
+ project_path=Path(project_path) if project_path else Path.cwd(),
174
+ exclude_desktop=not include_desktop,
175
+ )
176
+
177
+ if not detected:
178
+ console.print("[yellow]No AI platforms detected.[/yellow]")
179
+ console.print(
180
+ "Run 'mcp-ticketer install --auto-detect' to see supported platforms."
181
+ )
182
+ return
183
+
184
+ # Handle dry-run mode - show what would be installed without actually installing
185
+ if dry_run:
186
+ console.print(
187
+ "\n[yellow]DRY RUN - The following platforms would be configured:[/yellow]\n"
188
+ )
189
+
190
+ installable_count = 0
191
+ for plat in detected:
192
+ if plat.is_installed:
193
+ console.print(f" ✓ {plat.display_name} ({plat.scope})")
194
+ installable_count += 1
195
+ else:
196
+ console.print(
197
+ f" ⚠ {plat.display_name} ({plat.scope}) - would be skipped (configuration issue)"
198
+ )
199
+
200
+ console.print(
201
+ f"\n[dim]Would configure {installable_count} platform(s)[/dim]"
202
+ )
203
+ return
204
+
205
+ console.print(
206
+ f"[bold]Installing for {len(detected)} detected platform(s)...[/bold]\n"
207
+ )
208
+
209
+ # Import configuration functions
210
+ from .auggie_configure import configure_auggie_mcp
211
+ from .codex_configure import configure_codex_mcp
212
+ from .cursor_configure import configure_cursor_mcp
213
+ from .gemini_configure import configure_gemini_mcp
214
+ from .mcp_configure import configure_claude_mcp
215
+
216
+ # Map platform names to configuration functions
217
+ platform_mapping = {
218
+ "claude-code": lambda: configure_claude_mcp(
219
+ global_config=False, force=True
220
+ ),
221
+ "claude-desktop": lambda: configure_claude_mcp(
222
+ global_config=True, force=True
223
+ ),
224
+ "cursor": lambda: configure_cursor_mcp(force=True),
225
+ "auggie": lambda: configure_auggie_mcp(force=True),
226
+ "gemini": lambda: configure_gemini_mcp(scope="project", force=True),
227
+ "codex": lambda: configure_codex_mcp(force=True),
228
+ }
229
+
230
+ success_count = 0
231
+ failed = []
232
+
233
+ for plat in detected:
234
+ if not plat.is_installed:
235
+ console.print(
236
+ f"[yellow]⚠[/yellow] Skipping {plat.display_name} (configuration issue)"
237
+ )
238
+ continue
239
+
240
+ config_func = platform_mapping.get(plat.name)
241
+ if not config_func:
242
+ console.print(
243
+ f"[yellow]⚠[/yellow] No installer for {plat.display_name}"
244
+ )
245
+ continue
246
+
247
+ try:
248
+ console.print(f"[cyan]Installing for {plat.display_name}...[/cyan]")
249
+ config_func()
250
+ success_count += 1
251
+ except Exception as e:
252
+ console.print(
253
+ f"[red]✗[/red] Failed to install for {plat.display_name}: {e}"
254
+ )
255
+ failed.append(plat.display_name)
256
+
257
+ console.print(
258
+ f"\n[bold]Installation complete:[/bold] {success_count} succeeded"
259
+ )
260
+ if failed:
261
+ console.print(f"[red]Failed:[/red] {', '.join(failed)}")
262
+ return
263
+
264
+ # If no platform argument and no adapter flag, auto-detect and prompt
265
+ if platform is None and adapter is None:
266
+ detected = detector.detect_all(
267
+ project_path=Path(project_path) if project_path else Path.cwd()
268
+ )
269
+
270
+ # Filter to only installed platforms
271
+ installed = [p for p in detected if p.is_installed]
272
+
273
+ if not installed:
274
+ console.print("[yellow]No AI platforms detected.[/yellow]")
275
+ console.print("\n[bold]To see supported platforms:[/bold]")
276
+ console.print(" mcp-ticketer install --auto-detect")
277
+ console.print("\n[bold]Or run legacy adapter setup:[/bold]")
278
+ console.print(" mcp-ticketer install --adapter <adapter-type>")
279
+ return
280
+
281
+ # Show detected platforms and prompt for selection
282
+ console.print("[bold]Detected AI platforms:[/bold]\n")
283
+ for idx, plat in enumerate(installed, 1):
284
+ console.print(f" {idx}. {plat.display_name} ({plat.scope})")
285
+
286
+ console.print(
287
+ "\n[dim]Enter the number of the platform to configure, or 'q' to quit:[/dim]"
288
+ )
289
+ choice = typer.prompt("Select platform")
290
+
291
+ if choice.lower() == "q":
292
+ console.print("Installation cancelled.")
293
+ return
294
+
295
+ try:
296
+ idx = int(choice) - 1
297
+ if idx < 0 or idx >= len(installed):
298
+ console.print("[red]Invalid selection.[/red]")
299
+ raise typer.Exit(1) from None
300
+ platform = installed[idx].name
301
+ except ValueError as e:
302
+ console.print("[red]Invalid input. Please enter a number.[/red]")
303
+ raise typer.Exit(1) from e
304
+
305
+ # If platform argument is provided, handle MCP platform installation (NEW SYNTAX)
306
+ if platform is not None:
307
+ # Validate that the platform is actually installed
308
+ platform_info = get_platform_by_name(
309
+ platform, project_path=Path(project_path) if project_path else Path.cwd()
310
+ )
311
+
312
+ if platform_info and not platform_info.is_installed:
313
+ console.print(
314
+ f"[yellow]⚠[/yellow] {platform_info.display_name} was detected but has a configuration issue."
315
+ )
316
+ console.print(f"[dim]Config path: {platform_info.config_path}[/dim]\n")
317
+
318
+ proceed = typer.confirm(
319
+ "Do you want to proceed with installation anyway?", default=False
320
+ )
321
+ if not proceed:
322
+ console.print("Installation cancelled.")
323
+ return
324
+
325
+ elif not platform_info:
326
+ # Platform not detected at all - warn but allow proceeding
327
+ console.print(
328
+ f"[yellow]⚠[/yellow] Platform '{platform}' not detected on this system."
329
+ )
330
+ console.print(
331
+ "[dim]Run 'mcp-ticketer install --auto-detect' to see detected platforms.[/dim]\n"
332
+ )
333
+
334
+ proceed = typer.confirm(
335
+ "Do you want to proceed with installation anyway?", default=False
336
+ )
337
+ if not proceed:
338
+ console.print("Installation cancelled.")
339
+ return
340
+
341
+ # Import configuration functions
342
+ from .auggie_configure import configure_auggie_mcp
343
+ from .codex_configure import configure_codex_mcp
344
+ from .cursor_configure import configure_cursor_mcp
345
+ from .gemini_configure import configure_gemini_mcp
346
+ from .mcp_configure import configure_claude_mcp
347
+
348
+ # Map platform names to configuration functions
349
+ platform_mapping = {
350
+ "claude-code": {
351
+ "func": lambda: configure_claude_mcp(global_config=False, force=True),
352
+ "name": "Claude Code",
353
+ },
354
+ "claude-desktop": {
355
+ "func": lambda: configure_claude_mcp(global_config=True, force=True),
356
+ "name": "Claude Desktop",
357
+ },
358
+ "cursor": {
359
+ "func": lambda: configure_cursor_mcp(force=True),
360
+ "name": "Cursor",
361
+ },
362
+ "auggie": {
363
+ "func": lambda: configure_auggie_mcp(force=True),
364
+ "name": "Auggie",
365
+ },
366
+ "gemini": {
367
+ "func": lambda: configure_gemini_mcp(scope="project", force=True),
368
+ "name": "Gemini CLI",
369
+ },
370
+ "codex": {
371
+ "func": lambda: configure_codex_mcp(force=True),
372
+ "name": "Codex",
373
+ },
374
+ }
375
+
376
+ if platform not in platform_mapping:
377
+ console.print(f"[red]Unknown platform: {platform}[/red]")
378
+ console.print("\n[bold]Available platforms:[/bold]")
379
+ for p in platform_mapping.keys():
380
+ console.print(f" • {p}")
381
+ raise typer.Exit(1) from None
382
+
383
+ config = platform_mapping[platform]
384
+
385
+ if dry_run:
386
+ console.print(f"[cyan]DRY RUN - Would install for {config['name']}[/cyan]")
387
+ return
388
+
389
+ try:
390
+ config["func"]()
391
+ except Exception as e:
392
+ console.print(f"[red]Installation failed: {e}[/red]")
393
+ raise typer.Exit(1) from e
394
+ return
395
+
396
+ # Otherwise, delegate to init for adapter initialization (LEGACY BEHAVIOR)
397
+ # This makes 'install' and 'init' synonymous when called without platform argument
398
+ init(
399
+ adapter=adapter,
400
+ project_path=project_path,
401
+ global_config=global_config,
402
+ base_path=base_path,
403
+ api_key=api_key,
404
+ team_id=team_id,
405
+ jira_server=jira_server,
406
+ jira_email=jira_email,
407
+ jira_project=jira_project,
408
+ github_owner=github_owner,
409
+ github_repo=github_repo,
410
+ github_token=github_token,
411
+ )
412
+
413
+
414
+ def remove(
415
+ platform: str | None = typer.Argument(
416
+ None,
417
+ help="Platform to remove (claude-code, claude-desktop, cursor, auggie, gemini, codex)",
418
+ ),
419
+ dry_run: bool = typer.Option(
420
+ False, "--dry-run", help="Show what would be done without making changes"
421
+ ),
422
+ ) -> None:
423
+ """Remove mcp-ticketer from AI platforms.
424
+
425
+ Without arguments, shows help and available platforms.
426
+ With a platform argument, removes MCP configuration for that platform.
427
+
428
+ Examples:
429
+ # Remove from Claude Code (project-level)
430
+ mcp-ticketer remove claude-code
431
+
432
+ # Remove from Claude Desktop (global)
433
+ mcp-ticketer remove claude-desktop
434
+
435
+ # Remove from Auggie
436
+ mcp-ticketer remove auggie
437
+
438
+ # Dry run to preview changes
439
+ mcp-ticketer remove claude-code --dry-run
440
+
441
+ """
442
+ # If no platform specified, show help message
443
+ if platform is None:
444
+ console.print("[bold]Remove mcp-ticketer from AI platforms[/bold]\n")
445
+ console.print("Usage: mcp-ticketer remove <platform>\n")
446
+ console.print("[bold]Available platforms:[/bold]")
447
+ console.print(" • claude-code - Claude Code (project-level)")
448
+ console.print(" • claude-desktop - Claude Desktop (global)")
449
+ console.print(" • auggie - Auggie (global)")
450
+ console.print(" • gemini - Gemini CLI (project-level by default)")
451
+ console.print(" • codex - Codex (global)")
452
+ return
453
+
454
+ # Import removal functions
455
+ from .auggie_configure import remove_auggie_mcp
456
+ from .codex_configure import remove_codex_mcp
457
+ from .cursor_configure import remove_cursor_mcp
458
+ from .gemini_configure import remove_gemini_mcp
459
+ from .mcp_configure import remove_claude_mcp
460
+
461
+ # Map platform names to removal functions
462
+ platform_mapping = {
463
+ "claude-code": {
464
+ "func": lambda: remove_claude_mcp(global_config=False, dry_run=dry_run),
465
+ "name": "Claude Code",
466
+ },
467
+ "claude-desktop": {
468
+ "func": lambda: remove_claude_mcp(global_config=True, dry_run=dry_run),
469
+ "name": "Claude Desktop",
470
+ },
471
+ "cursor": {
472
+ "func": lambda: remove_cursor_mcp(dry_run=dry_run),
473
+ "name": "Cursor",
474
+ },
475
+ "auggie": {
476
+ "func": lambda: remove_auggie_mcp(dry_run=dry_run),
477
+ "name": "Auggie",
478
+ },
479
+ "gemini": {
480
+ "func": lambda: remove_gemini_mcp(scope="project", dry_run=dry_run),
481
+ "name": "Gemini CLI",
482
+ },
483
+ "codex": {
484
+ "func": lambda: remove_codex_mcp(dry_run=dry_run),
485
+ "name": "Codex",
486
+ },
487
+ }
488
+
489
+ if platform not in platform_mapping:
490
+ console.print(f"[red]Unknown platform: {platform}[/red]")
491
+ console.print("\n[bold]Available platforms:[/bold]")
492
+ for p in platform_mapping.keys():
493
+ console.print(f" • {p}")
494
+ raise typer.Exit(1) from None
495
+
496
+ config = platform_mapping[platform]
497
+
498
+ try:
499
+ config["func"]()
500
+ except Exception as e:
501
+ console.print(f"[red]Removal failed: {e}[/red]")
502
+ raise typer.Exit(1) from e
503
+
504
+
505
+ def uninstall(
506
+ platform: str | None = typer.Argument(
507
+ None,
508
+ help="Platform to uninstall (claude-code, claude-desktop, auggie, gemini, codex)",
509
+ ),
510
+ dry_run: bool = typer.Option(
511
+ False, "--dry-run", help="Show what would be done without making changes"
512
+ ),
513
+ ) -> None:
514
+ """Uninstall mcp-ticketer from AI platforms (alias for remove).
515
+
516
+ This is an alias for the 'remove' command.
517
+
518
+ Without arguments, shows help and available platforms.
519
+ With a platform argument, removes MCP configuration for that platform.
520
+
521
+ Examples:
522
+ # Uninstall from Claude Code (project-level)
523
+ mcp-ticketer uninstall claude-code
524
+
525
+ # Uninstall from Claude Desktop (global)
526
+ mcp-ticketer uninstall claude-desktop
527
+
528
+ # Uninstall from Auggie
529
+ mcp-ticketer uninstall auggie
530
+
531
+ # Dry run to preview changes
532
+ mcp-ticketer uninstall claude-code --dry-run
533
+
534
+ """
535
+ # Call the remove command with the same parameters
536
+ remove(platform=platform, dry_run=dry_run)