mcp-ticketer 0.4.0__py3-none-any.whl → 0.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mcp-ticketer might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  """Version information for mcp-ticketer package."""
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "0.4.2"
4
4
  __version_info__ = tuple(int(part) for part in __version__.split("."))
5
5
 
6
6
  # Package metadata
@@ -138,6 +138,72 @@ def create_auggie_server_config(
138
138
  return config
139
139
 
140
140
 
141
+ def remove_auggie_mcp(dry_run: bool = False) -> None:
142
+ """Remove mcp-ticketer from Auggie CLI configuration.
143
+
144
+ IMPORTANT: Auggie CLI ONLY supports global configuration.
145
+ This will remove mcp-ticketer from ~/.augment/settings.json.
146
+
147
+ Args:
148
+ dry_run: Show what would be removed without making changes
149
+
150
+ """
151
+ # Step 1: Find Auggie config location
152
+ console.print("[cyan]🔍 Removing Auggie CLI global configuration...[/cyan]")
153
+ console.print(
154
+ "[yellow]⚠ NOTE: Auggie only supports global configuration (affects all projects)[/yellow]"
155
+ )
156
+
157
+ auggie_config_path = find_auggie_config()
158
+ console.print(f"[dim]Config location: {auggie_config_path}[/dim]")
159
+
160
+ # Step 2: Check if config file exists
161
+ if not auggie_config_path.exists():
162
+ console.print(
163
+ f"[yellow]⚠ No configuration found at {auggie_config_path}[/yellow]"
164
+ )
165
+ console.print("[dim]mcp-ticketer is not configured for Auggie[/dim]")
166
+ return
167
+
168
+ # Step 3: Load existing Auggie configuration
169
+ auggie_config = load_auggie_config(auggie_config_path)
170
+
171
+ # Step 4: Check if mcp-ticketer is configured
172
+ if "mcp-ticketer" not in auggie_config.get("mcpServers", {}):
173
+ console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
174
+ console.print(f"[dim]No mcp-ticketer entry found in {auggie_config_path}[/dim]")
175
+ return
176
+
177
+ # Step 5: Show what would be removed (dry run or actual removal)
178
+ if dry_run:
179
+ console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
180
+ console.print(" Server name: mcp-ticketer")
181
+ console.print(f" From: {auggie_config_path}")
182
+ console.print(" Scope: Global (all projects)")
183
+ return
184
+
185
+ # Step 6: Remove mcp-ticketer from configuration
186
+ del auggie_config["mcpServers"]["mcp-ticketer"]
187
+
188
+ # Step 7: Save updated configuration
189
+ try:
190
+ save_auggie_config(auggie_config_path, auggie_config)
191
+ console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
192
+ console.print(f"[dim]Configuration updated: {auggie_config_path}[/dim]")
193
+
194
+ # Next steps
195
+ console.print("\n[bold cyan]Next Steps:[/bold cyan]")
196
+ console.print("1. Restart Auggie CLI for changes to take effect")
197
+ console.print("2. mcp-ticketer will no longer be available via MCP")
198
+ console.print(
199
+ "\n[yellow]⚠ Note: This removes global configuration affecting all projects[/yellow]"
200
+ )
201
+
202
+ except Exception as e:
203
+ console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
204
+ raise
205
+
206
+
141
207
  def configure_auggie_mcp(force: bool = False) -> None:
142
208
  """Configure Auggie CLI to use mcp-ticketer.
143
209
 
@@ -151,6 +151,74 @@ def create_codex_server_config(
151
151
  return config
152
152
 
153
153
 
154
+ def remove_codex_mcp(dry_run: bool = False) -> None:
155
+ """Remove mcp-ticketer from Codex CLI configuration.
156
+
157
+ IMPORTANT: Codex CLI ONLY supports global configuration at ~/.codex/config.toml.
158
+ This will remove mcp-ticketer from the global configuration.
159
+
160
+ Args:
161
+ dry_run: Show what would be removed without making changes
162
+
163
+ """
164
+ # Step 1: Find Codex config location (always global)
165
+ console.print("[cyan]🔍 Removing Codex CLI global configuration...[/cyan]")
166
+ console.print(
167
+ "[yellow]⚠ Note: Codex CLI only supports global configuration[/yellow]"
168
+ )
169
+
170
+ codex_config_path = find_codex_config()
171
+ console.print(f"[dim]Config location: {codex_config_path}[/dim]")
172
+
173
+ # Step 2: Check if config file exists
174
+ if not codex_config_path.exists():
175
+ console.print(
176
+ f"[yellow]⚠ No configuration found at {codex_config_path}[/yellow]"
177
+ )
178
+ console.print("[dim]mcp-ticketer is not configured for Codex CLI[/dim]")
179
+ return
180
+
181
+ # Step 3: Load existing Codex configuration
182
+ codex_config = load_codex_config(codex_config_path)
183
+
184
+ # Step 4: Check if mcp-ticketer is configured
185
+ # NOTE: Use underscore mcp_servers, not camelCase
186
+ mcp_servers = codex_config.get("mcp_servers", {})
187
+ if "mcp-ticketer" not in mcp_servers:
188
+ console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
189
+ console.print(f"[dim]No mcp-ticketer entry found in {codex_config_path}[/dim]")
190
+ return
191
+
192
+ # Step 5: Show what would be removed (dry run or actual removal)
193
+ if dry_run:
194
+ console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
195
+ console.print(" Server name: mcp-ticketer")
196
+ console.print(f" From: {codex_config_path}")
197
+ console.print(" Scope: Global (all sessions)")
198
+ return
199
+
200
+ # Step 6: Remove mcp-ticketer from configuration
201
+ del codex_config["mcp_servers"]["mcp-ticketer"]
202
+
203
+ # Step 7: Save updated configuration
204
+ try:
205
+ save_codex_config(codex_config_path, codex_config)
206
+ console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
207
+ console.print(f"[dim]Configuration updated: {codex_config_path}[/dim]")
208
+
209
+ # Next steps
210
+ console.print("\n[bold cyan]Next Steps:[/bold cyan]")
211
+ console.print("1. [bold]Restart Codex CLI[/bold] (required for changes)")
212
+ console.print("2. mcp-ticketer will no longer be available via MCP")
213
+ console.print(
214
+ "\n[yellow]⚠ Note: This removes global configuration affecting all Codex sessions[/yellow]"
215
+ )
216
+
217
+ except Exception as e:
218
+ console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
219
+ raise
220
+
221
+
154
222
  def configure_codex_mcp(force: bool = False) -> None:
155
223
  """Configure Codex CLI to use mcp-ticketer.
156
224
 
@@ -147,6 +147,72 @@ def create_gemini_server_config(
147
147
  return config
148
148
 
149
149
 
150
+ def remove_gemini_mcp(
151
+ scope: Literal["project", "user"] = "project", dry_run: bool = False
152
+ ) -> None:
153
+ """Remove mcp-ticketer from Gemini CLI configuration.
154
+
155
+ Args:
156
+ scope: Configuration scope - "project" or "user"
157
+ dry_run: Show what would be removed without making changes
158
+
159
+ """
160
+ # Step 1: Find Gemini config location
161
+ config_type = "user-level" if scope == "user" else "project-level"
162
+ console.print(f"[cyan]🔍 Removing {config_type} Gemini CLI configuration...[/cyan]")
163
+
164
+ gemini_config_path = find_gemini_config(scope)
165
+ console.print(f"[dim]Config location: {gemini_config_path}[/dim]")
166
+
167
+ # Step 2: Check if config file exists
168
+ if not gemini_config_path.exists():
169
+ console.print(
170
+ f"[yellow]⚠ No configuration found at {gemini_config_path}[/yellow]"
171
+ )
172
+ console.print("[dim]mcp-ticketer is not configured for Gemini CLI[/dim]")
173
+ return
174
+
175
+ # Step 3: Load existing Gemini configuration
176
+ gemini_config = load_gemini_config(gemini_config_path)
177
+
178
+ # Step 4: Check if mcp-ticketer is configured
179
+ if "mcp-ticketer" not in gemini_config.get("mcpServers", {}):
180
+ console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
181
+ console.print(f"[dim]No mcp-ticketer entry found in {gemini_config_path}[/dim]")
182
+ return
183
+
184
+ # Step 5: Show what would be removed (dry run or actual removal)
185
+ if dry_run:
186
+ console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
187
+ console.print(" Server name: mcp-ticketer")
188
+ console.print(f" From: {gemini_config_path}")
189
+ console.print(f" Scope: {config_type}")
190
+ return
191
+
192
+ # Step 6: Remove mcp-ticketer from configuration
193
+ del gemini_config["mcpServers"]["mcp-ticketer"]
194
+
195
+ # Step 7: Save updated configuration
196
+ try:
197
+ save_gemini_config(gemini_config_path, gemini_config)
198
+ console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
199
+ console.print(f"[dim]Configuration updated: {gemini_config_path}[/dim]")
200
+
201
+ # Next steps
202
+ console.print("\n[bold cyan]Next Steps:[/bold cyan]")
203
+ if scope == "user":
204
+ console.print("1. Gemini CLI global configuration updated")
205
+ console.print("2. mcp-ticketer will no longer be available in any project")
206
+ else:
207
+ console.print("1. Gemini CLI project configuration updated")
208
+ console.print("2. mcp-ticketer will no longer be available in this project")
209
+ console.print("3. Restart Gemini CLI if currently running")
210
+
211
+ except Exception as e:
212
+ console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
213
+ raise
214
+
215
+
150
216
  def configure_gemini_mcp(
151
217
  scope: Literal["project", "user"] = "project", force: bool = False
152
218
  ) -> None:
mcp_ticketer/cli/main.py CHANGED
@@ -891,7 +891,8 @@ def _show_next_steps(
891
891
  console.print("[dim]Run 'mcp-ticketer --help' for more commands[/dim]")
892
892
 
893
893
 
894
- @app.command()
894
+ # Keep the old install command as deprecated alias to init
895
+ @app.command(deprecated=True, hidden=True)
895
896
  def install(
896
897
  adapter: Optional[str] = typer.Option(
897
898
  None,
@@ -941,26 +942,14 @@ def install(
941
942
  None, "--github-token", help="GitHub Personal Access Token"
942
943
  ),
943
944
  ) -> None:
944
- """Initialize mcp-ticketer for the current project (alias for init).
945
-
946
- This command is synonymous with 'init' and 'setup' - all three provide
947
- identical functionality with interactive prompts to guide you through
948
- configuring MCP Ticketer for your preferred ticket management system.
949
-
950
- Examples:
951
- # Interactive setup (same as 'init' and 'setup')
952
- mcp-ticketer install
945
+ """DEPRECATED: Use 'mcp-ticketer init' instead.
953
946
 
954
- # Force specific adapter
955
- mcp-ticketer install --adapter linear
956
-
957
- # Initialize for different project
958
- mcp-ticketer install --path /path/to/project
959
-
960
- # Save globally (not recommended)
961
- mcp-ticketer install --global
947
+ This command is deprecated. Use 'mcp-ticketer init' for project initialization.
962
948
 
963
949
  """
950
+ console.print(
951
+ "[yellow]⚠️ 'install' is deprecated. Use 'mcp-ticketer init' instead.[/yellow]\n"
952
+ )
964
953
  # Call init with all parameters
965
954
  init(
966
955
  adapter=adapter,
@@ -1149,7 +1138,9 @@ def old_queue_status_command():
1149
1138
 
1150
1139
  DEPRECATED: Use 'mcp-ticketer queue status' instead.
1151
1140
  """
1152
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer queue status' instead.[/yellow]\n")
1141
+ console.print(
1142
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer queue status' instead.[/yellow]\n"
1143
+ )
1153
1144
 
1154
1145
  queue = Queue()
1155
1146
  manager = WorkerManager()
@@ -1192,7 +1183,9 @@ def old_queue_health_command(
1192
1183
 
1193
1184
  DEPRECATED: Use 'mcp-ticketer queue health' instead.
1194
1185
  """
1195
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer queue health' instead.[/yellow]\n")
1186
+ console.print(
1187
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer queue health' instead.[/yellow]\n"
1188
+ )
1196
1189
  health_monitor = QueueHealthMonitor()
1197
1190
  health = health_monitor.check_health()
1198
1191
 
@@ -1294,7 +1287,9 @@ def create(
1294
1287
 
1295
1288
  DEPRECATED: Use 'mcp-ticketer ticket create' instead.
1296
1289
  """
1297
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket create' instead.[/yellow]\n")
1290
+ console.print(
1291
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket create' instead.[/yellow]\n"
1292
+ )
1298
1293
 
1299
1294
  # IMMEDIATE HEALTH CHECK - Critical for reliability
1300
1295
  health_monitor = QueueHealthMonitor()
@@ -1508,7 +1503,9 @@ def list_tickets(
1508
1503
 
1509
1504
  DEPRECATED: Use 'mcp-ticketer ticket list' instead.
1510
1505
  """
1511
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket list' instead.[/yellow]\n")
1506
+ console.print(
1507
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket list' instead.[/yellow]\n"
1508
+ )
1512
1509
 
1513
1510
  async def _list():
1514
1511
  adapter_instance = get_adapter(
@@ -1562,7 +1559,9 @@ def show(
1562
1559
 
1563
1560
  DEPRECATED: Use 'mcp-ticketer ticket show' instead.
1564
1561
  """
1565
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket show' instead.[/yellow]\n")
1562
+ console.print(
1563
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket show' instead.[/yellow]\n"
1564
+ )
1566
1565
 
1567
1566
  async def _show():
1568
1567
  adapter_instance = get_adapter(
@@ -1616,7 +1615,9 @@ def comment(
1616
1615
 
1617
1616
  DEPRECATED: Use 'mcp-ticketer ticket comment' instead.
1618
1617
  """
1619
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket comment' instead.[/yellow]\n")
1618
+ console.print(
1619
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket comment' instead.[/yellow]\n"
1620
+ )
1620
1621
 
1621
1622
  async def _comment():
1622
1623
  adapter_instance = get_adapter(
@@ -1665,7 +1666,9 @@ def update(
1665
1666
 
1666
1667
  DEPRECATED: Use 'mcp-ticketer ticket update' instead.
1667
1668
  """
1668
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket update' instead.[/yellow]\n")
1669
+ console.print(
1670
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket update' instead.[/yellow]\n"
1671
+ )
1669
1672
  updates = {}
1670
1673
  if title:
1671
1674
  updates["title"] = title
@@ -1738,7 +1741,9 @@ def transition(
1738
1741
  mcp-ticketer ticket transition BTA-215 done
1739
1742
 
1740
1743
  """
1741
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket transition' instead.[/yellow]\n")
1744
+ console.print(
1745
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket transition' instead.[/yellow]\n"
1746
+ )
1742
1747
 
1743
1748
  # Determine which state to use (prefer flag over positional)
1744
1749
  target_state = state if state is not None else state_positional
@@ -1797,7 +1802,9 @@ def search(
1797
1802
 
1798
1803
  DEPRECATED: Use 'mcp-ticketer ticket search' instead.
1799
1804
  """
1800
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket search' instead.[/yellow]\n")
1805
+ console.print(
1806
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket search' instead.[/yellow]\n"
1807
+ )
1801
1808
 
1802
1809
  async def _search():
1803
1810
  adapter_instance = get_adapter(
@@ -1935,13 +1942,241 @@ mcp_app = typer.Typer(
1935
1942
  )
1936
1943
 
1937
1944
 
1945
+ @app.command()
1946
+ def install(
1947
+ platform: Optional[str] = typer.Argument(
1948
+ None,
1949
+ help="Platform to install (claude-code, claude-desktop, auggie, gemini, codex)",
1950
+ ),
1951
+ dry_run: bool = typer.Option(
1952
+ False, "--dry-run", help="Show what would be done without making changes"
1953
+ ),
1954
+ ) -> None:
1955
+ """Install mcp-ticketer for AI platforms.
1956
+
1957
+ Without arguments, shows installation status and available platforms.
1958
+ With a platform argument, installs MCP configuration for that platform.
1959
+
1960
+ Each platform gets the right configuration automatically:
1961
+ - claude-code: Project-level MCP server
1962
+ - claude-desktop: Global MCP server
1963
+ - auggie: Project-level MCP server
1964
+ - gemini: Project-level MCP server
1965
+ - codex: Project-level MCP server
1966
+
1967
+ Examples:
1968
+ # Show status and available platforms
1969
+ mcp-ticketer install
1970
+
1971
+ # Install for Claude Code (project-level)
1972
+ mcp-ticketer install claude-code
1973
+
1974
+ # Install for Claude Desktop (global)
1975
+ mcp-ticketer install claude-desktop
1976
+
1977
+ # Install for Auggie
1978
+ mcp-ticketer install auggie
1979
+
1980
+ # Dry run to preview changes
1981
+ mcp-ticketer install claude-code --dry-run
1982
+
1983
+ """
1984
+ # If no platform specified, show help message
1985
+ if platform is None:
1986
+ console.print("[green]✓[/green] mcp-ticketer CLI is already installed.\n")
1987
+ console.print(
1988
+ "[bold]To configure MCP for a specific platform, use:[/bold]\n"
1989
+ " mcp-ticketer install <platform>\n"
1990
+ )
1991
+ console.print("[bold]Available platforms:[/bold]")
1992
+ console.print(" • claude-code - Claude Code (project-level)")
1993
+ console.print(" • claude-desktop - Claude Desktop (global)")
1994
+ console.print(" • auggie - Auggie (project-level)")
1995
+ console.print(" • gemini - Gemini CLI (project-level)")
1996
+ console.print(" • codex - Codex (project-level)")
1997
+ return
1998
+
1999
+ # Import configuration functions
2000
+ from .auggie_configure import configure_auggie_mcp
2001
+ from .codex_configure import configure_codex_mcp
2002
+ from .gemini_configure import configure_gemini_mcp
2003
+ from .mcp_configure import configure_claude_mcp
2004
+
2005
+ # Map platform names to configuration functions
2006
+ platform_mapping = {
2007
+ "claude-code": {
2008
+ "func": lambda: configure_claude_mcp(global_config=False, force=True),
2009
+ "name": "Claude Code",
2010
+ },
2011
+ "claude-desktop": {
2012
+ "func": lambda: configure_claude_mcp(global_config=True, force=True),
2013
+ "name": "Claude Desktop",
2014
+ },
2015
+ "auggie": {
2016
+ "func": lambda: configure_auggie_mcp(force=True),
2017
+ "name": "Auggie",
2018
+ },
2019
+ "gemini": {
2020
+ "func": lambda: configure_gemini_mcp(scope="project", force=True),
2021
+ "name": "Gemini CLI",
2022
+ },
2023
+ "codex": {
2024
+ "func": lambda: configure_codex_mcp(force=True),
2025
+ "name": "Codex",
2026
+ },
2027
+ }
2028
+
2029
+ if platform not in platform_mapping:
2030
+ console.print(f"[red]Unknown platform: {platform}[/red]")
2031
+ console.print("\n[bold]Available platforms:[/bold]")
2032
+ for p in platform_mapping.keys():
2033
+ console.print(f" • {p}")
2034
+ raise typer.Exit(1)
2035
+
2036
+ config = platform_mapping[platform]
2037
+
2038
+ if dry_run:
2039
+ console.print(f"[cyan]DRY RUN - Would install for {config['name']}[/cyan]")
2040
+ return
2041
+
2042
+ try:
2043
+ config["func"]()
2044
+ except Exception as e:
2045
+ console.print(f"[red]Installation failed: {e}[/red]")
2046
+ raise typer.Exit(1)
2047
+
2048
+
2049
+ @app.command()
2050
+ def remove(
2051
+ platform: Optional[str] = typer.Argument(
2052
+ None,
2053
+ help="Platform to remove (claude-code, claude-desktop, auggie, gemini, codex)",
2054
+ ),
2055
+ dry_run: bool = typer.Option(
2056
+ False, "--dry-run", help="Show what would be done without making changes"
2057
+ ),
2058
+ ) -> None:
2059
+ """Remove mcp-ticketer from AI platforms.
2060
+
2061
+ Without arguments, shows help and available platforms.
2062
+ With a platform argument, removes MCP configuration for that platform.
2063
+
2064
+ Examples:
2065
+ # Remove from Claude Code (project-level)
2066
+ mcp-ticketer remove claude-code
2067
+
2068
+ # Remove from Claude Desktop (global)
2069
+ mcp-ticketer remove claude-desktop
2070
+
2071
+ # Remove from Auggie
2072
+ mcp-ticketer remove auggie
2073
+
2074
+ # Dry run to preview changes
2075
+ mcp-ticketer remove claude-code --dry-run
2076
+
2077
+ """
2078
+ # If no platform specified, show help message
2079
+ if platform is None:
2080
+ console.print("[bold]Remove mcp-ticketer from AI platforms[/bold]\n")
2081
+ console.print("Usage: mcp-ticketer remove <platform>\n")
2082
+ console.print("[bold]Available platforms:[/bold]")
2083
+ console.print(" • claude-code - Claude Code (project-level)")
2084
+ console.print(" • claude-desktop - Claude Desktop (global)")
2085
+ console.print(" • auggie - Auggie (global)")
2086
+ console.print(" • gemini - Gemini CLI (project-level by default)")
2087
+ console.print(" • codex - Codex (global)")
2088
+ return
2089
+
2090
+ # Import removal functions
2091
+ from .auggie_configure import remove_auggie_mcp
2092
+ from .codex_configure import remove_codex_mcp
2093
+ from .gemini_configure import remove_gemini_mcp
2094
+ from .mcp_configure import remove_claude_mcp
2095
+
2096
+ # Map platform names to removal functions
2097
+ platform_mapping = {
2098
+ "claude-code": {
2099
+ "func": lambda: remove_claude_mcp(global_config=False, dry_run=dry_run),
2100
+ "name": "Claude Code",
2101
+ },
2102
+ "claude-desktop": {
2103
+ "func": lambda: remove_claude_mcp(global_config=True, dry_run=dry_run),
2104
+ "name": "Claude Desktop",
2105
+ },
2106
+ "auggie": {
2107
+ "func": lambda: remove_auggie_mcp(dry_run=dry_run),
2108
+ "name": "Auggie",
2109
+ },
2110
+ "gemini": {
2111
+ "func": lambda: remove_gemini_mcp(scope="project", dry_run=dry_run),
2112
+ "name": "Gemini CLI",
2113
+ },
2114
+ "codex": {
2115
+ "func": lambda: remove_codex_mcp(dry_run=dry_run),
2116
+ "name": "Codex",
2117
+ },
2118
+ }
2119
+
2120
+ if platform not in platform_mapping:
2121
+ console.print(f"[red]Unknown platform: {platform}[/red]")
2122
+ console.print("\n[bold]Available platforms:[/bold]")
2123
+ for p in platform_mapping.keys():
2124
+ console.print(f" • {p}")
2125
+ raise typer.Exit(1)
2126
+
2127
+ config = platform_mapping[platform]
2128
+
2129
+ try:
2130
+ config["func"]()
2131
+ except Exception as e:
2132
+ console.print(f"[red]Removal failed: {e}[/red]")
2133
+ raise typer.Exit(1)
2134
+
2135
+
2136
+ @app.command()
2137
+ def uninstall(
2138
+ platform: Optional[str] = typer.Argument(
2139
+ None,
2140
+ help="Platform to uninstall (claude-code, claude-desktop, auggie, gemini, codex)",
2141
+ ),
2142
+ dry_run: bool = typer.Option(
2143
+ False, "--dry-run", help="Show what would be done without making changes"
2144
+ ),
2145
+ ) -> None:
2146
+ """Uninstall mcp-ticketer from AI platforms (alias for remove).
2147
+
2148
+ This is an alias for the 'remove' command.
2149
+
2150
+ Without arguments, shows help and available platforms.
2151
+ With a platform argument, removes MCP configuration for that platform.
2152
+
2153
+ Examples:
2154
+ # Uninstall from Claude Code (project-level)
2155
+ mcp-ticketer uninstall claude-code
2156
+
2157
+ # Uninstall from Claude Desktop (global)
2158
+ mcp-ticketer uninstall claude-desktop
2159
+
2160
+ # Uninstall from Auggie
2161
+ mcp-ticketer uninstall auggie
2162
+
2163
+ # Dry run to preview changes
2164
+ mcp-ticketer uninstall claude-code --dry-run
2165
+
2166
+ """
2167
+ # Call the remove command with the same parameters
2168
+ remove(platform=platform, dry_run=dry_run)
2169
+
2170
+
1938
2171
  @app.command(deprecated=True, hidden=True)
1939
2172
  def check(queue_id: str = typer.Argument(..., help="Queue ID to check")):
1940
2173
  """Check status of a queued operation.
1941
2174
 
1942
2175
  DEPRECATED: Use 'mcp-ticketer ticket check' instead.
1943
2176
  """
1944
- console.print("[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket check' instead.[/yellow]\n")
2177
+ console.print(
2178
+ "[yellow]⚠️ This command is deprecated. Use 'mcp-ticketer ticket check' instead.[/yellow]\n"
2179
+ )
1945
2180
  queue = Queue()
1946
2181
  item = queue.get_item(queue_id)
1947
2182
 
@@ -1981,8 +2216,8 @@ def check(queue_id: str = typer.Argument(..., help="Queue ID to check")):
1981
2216
  console.print(f"\nRetry Count: {item.retry_count}")
1982
2217
 
1983
2218
 
1984
- @app.command()
1985
- def serve(
2219
+ @mcp_app.command(name="serve")
2220
+ def mcp_serve(
1986
2221
  adapter: Optional[AdapterType] = typer.Option(
1987
2222
  None, "--adapter", "-a", help="Override default adapter type"
1988
2223
  ),
@@ -1993,7 +2228,7 @@ def serve(
1993
2228
  """Start MCP server for JSON-RPC communication over stdio.
1994
2229
 
1995
2230
  This command is used by Claude Code/Desktop when connecting to the MCP server.
1996
- You typically don't need to run this manually - use 'mcp-ticketer mcp' to configure.
2231
+ You typically don't need to run this manually - use 'mcp-ticketer install add' to configure.
1997
2232
 
1998
2233
  Configuration Resolution:
1999
2234
  - When MCP server starts, it uses the current working directory (cwd)
@@ -2003,7 +2238,8 @@ def serve(
2003
2238
  2. Global: ~/.mcp-ticketer/config.json
2004
2239
  3. Default: aitrackdown adapter with .aitrackdown base path
2005
2240
  """
2006
- from ..mcp.server import MCPTicketServer
2241
+ from ..mcp.server_sdk import configure_adapter
2242
+ from ..mcp.server_sdk import main as sdk_main
2007
2243
 
2008
2244
  # Load configuration (respects project-specific config in cwd)
2009
2245
  config = load_config()
@@ -2044,21 +2280,22 @@ def serve(
2044
2280
  if sys.stderr.isatty():
2045
2281
  # Only print if stderr is a terminal (not redirected)
2046
2282
  console.file = sys.stderr
2047
- console.print(f"[green]Starting MCP server[/green] with {adapter_type} adapter")
2283
+ console.print(
2284
+ f"[green]Starting MCP SDK server[/green] with {adapter_type} adapter"
2285
+ )
2048
2286
  console.print(
2049
2287
  "[dim]Server running on stdio. Send JSON-RPC requests via stdin.[/dim]"
2050
2288
  )
2051
2289
 
2052
- # Create and run server
2290
+ # Configure adapter and run SDK server
2053
2291
  try:
2054
- server = MCPTicketServer(adapter_type, adapter_config)
2055
- asyncio.run(server.run())
2292
+ configure_adapter(adapter_type, adapter_config)
2293
+ sdk_main()
2056
2294
  except KeyboardInterrupt:
2057
- # Also send this to stderr
2295
+ # Send this to stderr
2058
2296
  if sys.stderr.isatty():
2059
2297
  console.print("\n[yellow]Server stopped by user[/yellow]")
2060
- if "server" in locals():
2061
- asyncio.run(server.stop())
2298
+ sys.exit(0)
2062
2299
  except Exception as e:
2063
2300
  # Log error to stderr
2064
2301
  sys.stderr.write(f"MCP server error: {e}\n")