mcp-ticketer 0.4.1__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.
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/cli/auggie_configure.py +66 -0
- mcp_ticketer/cli/codex_configure.py +68 -0
- mcp_ticketer/cli/gemini_configure.py +66 -0
- mcp_ticketer/cli/main.py +194 -32
- mcp_ticketer/cli/mcp_configure.py +60 -0
- mcp_ticketer/mcp/server_sdk.py +93 -0
- mcp_ticketer/mcp/tools/__init__.py +38 -0
- mcp_ticketer/mcp/tools/attachment_tools.py +180 -0
- mcp_ticketer/mcp/tools/bulk_tools.py +273 -0
- mcp_ticketer/mcp/tools/comment_tools.py +90 -0
- mcp_ticketer/mcp/tools/hierarchy_tools.py +383 -0
- mcp_ticketer/mcp/tools/pr_tools.py +154 -0
- mcp_ticketer/mcp/tools/search_tools.py +206 -0
- mcp_ticketer/mcp/tools/ticket_tools.py +277 -0
- {mcp_ticketer-0.4.1.dist-info → mcp_ticketer-0.4.2.dist-info}/METADATA +30 -16
- {mcp_ticketer-0.4.1.dist-info → mcp_ticketer-0.4.2.dist-info}/RECORD +21 -12
- {mcp_ticketer-0.4.1.dist-info → mcp_ticketer-0.4.2.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.4.1.dist-info → mcp_ticketer-0.4.2.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.4.1.dist-info → mcp_ticketer-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.4.1.dist-info → mcp_ticketer-0.4.2.dist-info}/top_level.txt +0 -0
mcp_ticketer/__version__.py
CHANGED
|
@@ -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
|
@@ -1941,50 +1941,89 @@ mcp_app = typer.Typer(
|
|
|
1941
1941
|
add_completion=False,
|
|
1942
1942
|
)
|
|
1943
1943
|
|
|
1944
|
-
# Create install command group (like kuzu-memory)
|
|
1945
|
-
install_app = typer.Typer(
|
|
1946
|
-
name="install",
|
|
1947
|
-
help="Manage AI system integrations",
|
|
1948
|
-
add_completion=False,
|
|
1949
|
-
)
|
|
1950
|
-
|
|
1951
1944
|
|
|
1952
|
-
@
|
|
1953
|
-
def
|
|
1954
|
-
platform: str = typer.Argument(
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
project_path: Optional[str] = typer.Option(
|
|
1958
|
-
None, "--project", help="Project directory"
|
|
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)",
|
|
1959
1950
|
),
|
|
1960
1951
|
dry_run: bool = typer.Option(
|
|
1961
1952
|
False, "--dry-run", help="Show what would be done without making changes"
|
|
1962
1953
|
),
|
|
1963
|
-
verbose: bool = typer.Option(False, "--verbose", help="Enable verbose output"),
|
|
1964
1954
|
) -> None:
|
|
1965
|
-
"""Install mcp-ticketer
|
|
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.
|
|
1966
1959
|
|
|
1967
1960
|
Each platform gets the right configuration automatically:
|
|
1968
1961
|
- claude-code: Project-level MCP server
|
|
1969
1962
|
- claude-desktop: Global MCP server
|
|
1963
|
+
- auggie: Project-level MCP server
|
|
1964
|
+
- gemini: Project-level MCP server
|
|
1965
|
+
- codex: Project-level MCP server
|
|
1970
1966
|
|
|
1971
1967
|
Examples:
|
|
1968
|
+
# Show status and available platforms
|
|
1969
|
+
mcp-ticketer install
|
|
1970
|
+
|
|
1972
1971
|
# Install for Claude Code (project-level)
|
|
1973
|
-
mcp-ticketer install
|
|
1972
|
+
mcp-ticketer install claude-code
|
|
1974
1973
|
|
|
1975
1974
|
# Install for Claude Desktop (global)
|
|
1976
|
-
mcp-ticketer install
|
|
1975
|
+
mcp-ticketer install claude-desktop
|
|
1976
|
+
|
|
1977
|
+
# Install for Auggie
|
|
1978
|
+
mcp-ticketer install auggie
|
|
1977
1979
|
|
|
1978
1980
|
# Dry run to preview changes
|
|
1979
|
-
mcp-ticketer install
|
|
1981
|
+
mcp-ticketer install claude-code --dry-run
|
|
1980
1982
|
|
|
1981
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
|
|
1982
2003
|
from .mcp_configure import configure_claude_mcp
|
|
1983
2004
|
|
|
1984
|
-
# Map platform names to configuration
|
|
2005
|
+
# Map platform names to configuration functions
|
|
1985
2006
|
platform_mapping = {
|
|
1986
|
-
"claude-code": {
|
|
1987
|
-
|
|
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
|
+
},
|
|
1988
2027
|
}
|
|
1989
2028
|
|
|
1990
2029
|
if platform not in platform_mapping:
|
|
@@ -2001,12 +2040,134 @@ def install_add(
|
|
|
2001
2040
|
return
|
|
2002
2041
|
|
|
2003
2042
|
try:
|
|
2004
|
-
|
|
2043
|
+
config["func"]()
|
|
2005
2044
|
except Exception as e:
|
|
2006
2045
|
console.print(f"[red]Installation failed: {e}[/red]")
|
|
2007
2046
|
raise typer.Exit(1)
|
|
2008
2047
|
|
|
2009
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
|
+
|
|
2010
2171
|
@app.command(deprecated=True, hidden=True)
|
|
2011
2172
|
def check(queue_id: str = typer.Argument(..., help="Queue ID to check")):
|
|
2012
2173
|
"""Check status of a queued operation.
|
|
@@ -2077,7 +2238,8 @@ def mcp_serve(
|
|
|
2077
2238
|
2. Global: ~/.mcp-ticketer/config.json
|
|
2078
2239
|
3. Default: aitrackdown adapter with .aitrackdown base path
|
|
2079
2240
|
"""
|
|
2080
|
-
from ..mcp.
|
|
2241
|
+
from ..mcp.server_sdk import configure_adapter
|
|
2242
|
+
from ..mcp.server_sdk import main as sdk_main
|
|
2081
2243
|
|
|
2082
2244
|
# Load configuration (respects project-specific config in cwd)
|
|
2083
2245
|
config = load_config()
|
|
@@ -2118,21 +2280,22 @@ def mcp_serve(
|
|
|
2118
2280
|
if sys.stderr.isatty():
|
|
2119
2281
|
# Only print if stderr is a terminal (not redirected)
|
|
2120
2282
|
console.file = sys.stderr
|
|
2121
|
-
console.print(
|
|
2283
|
+
console.print(
|
|
2284
|
+
f"[green]Starting MCP SDK server[/green] with {adapter_type} adapter"
|
|
2285
|
+
)
|
|
2122
2286
|
console.print(
|
|
2123
2287
|
"[dim]Server running on stdio. Send JSON-RPC requests via stdin.[/dim]"
|
|
2124
2288
|
)
|
|
2125
2289
|
|
|
2126
|
-
#
|
|
2290
|
+
# Configure adapter and run SDK server
|
|
2127
2291
|
try:
|
|
2128
|
-
|
|
2129
|
-
|
|
2292
|
+
configure_adapter(adapter_type, adapter_config)
|
|
2293
|
+
sdk_main()
|
|
2130
2294
|
except KeyboardInterrupt:
|
|
2131
|
-
#
|
|
2295
|
+
# Send this to stderr
|
|
2132
2296
|
if sys.stderr.isatty():
|
|
2133
2297
|
console.print("\n[yellow]Server stopped by user[/yellow]")
|
|
2134
|
-
|
|
2135
|
-
asyncio.run(server.stop())
|
|
2298
|
+
sys.exit(0)
|
|
2136
2299
|
except Exception as e:
|
|
2137
2300
|
# Log error to stderr
|
|
2138
2301
|
sys.stderr.write(f"MCP server error: {e}\n")
|
|
@@ -2292,7 +2455,6 @@ def mcp_auggie(
|
|
|
2292
2455
|
|
|
2293
2456
|
# Add command groups to main app (must be after all subcommands are defined)
|
|
2294
2457
|
app.add_typer(mcp_app, name="mcp")
|
|
2295
|
-
app.add_typer(install_app, name="install")
|
|
2296
2458
|
|
|
2297
2459
|
|
|
2298
2460
|
def main():
|
|
@@ -217,6 +217,66 @@ def create_mcp_server_config(
|
|
|
217
217
|
return config
|
|
218
218
|
|
|
219
219
|
|
|
220
|
+
def remove_claude_mcp(global_config: bool = False, dry_run: bool = False) -> None:
|
|
221
|
+
"""Remove mcp-ticketer from Claude Code/Desktop configuration.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
global_config: Remove from Claude Desktop instead of project-level
|
|
225
|
+
dry_run: Show what would be removed without making changes
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
# Step 1: Find Claude MCP config location
|
|
229
|
+
config_type = "Claude Desktop" if global_config else "project-level"
|
|
230
|
+
console.print(f"[cyan]🔍 Removing {config_type} MCP configuration...[/cyan]")
|
|
231
|
+
|
|
232
|
+
mcp_config_path = find_claude_mcp_config(global_config)
|
|
233
|
+
console.print(f"[dim]Config location: {mcp_config_path}[/dim]")
|
|
234
|
+
|
|
235
|
+
# Step 2: Check if config file exists
|
|
236
|
+
if not mcp_config_path.exists():
|
|
237
|
+
console.print(f"[yellow]⚠ No configuration found at {mcp_config_path}[/yellow]")
|
|
238
|
+
console.print("[dim]mcp-ticketer is not configured for this platform[/dim]")
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
# Step 3: Load existing MCP configuration
|
|
242
|
+
mcp_config = load_claude_mcp_config(mcp_config_path)
|
|
243
|
+
|
|
244
|
+
# Step 4: Check if mcp-ticketer is configured
|
|
245
|
+
if "mcp-ticketer" not in mcp_config.get("mcpServers", {}):
|
|
246
|
+
console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
|
|
247
|
+
console.print(f"[dim]No mcp-ticketer entry found in {mcp_config_path}[/dim]")
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
# Step 5: Show what would be removed (dry run or actual removal)
|
|
251
|
+
if dry_run:
|
|
252
|
+
console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
|
|
253
|
+
console.print(" Server name: mcp-ticketer")
|
|
254
|
+
console.print(f" From: {mcp_config_path}")
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
# Step 6: Remove mcp-ticketer from configuration
|
|
258
|
+
del mcp_config["mcpServers"]["mcp-ticketer"]
|
|
259
|
+
|
|
260
|
+
# Step 7: Save updated configuration
|
|
261
|
+
try:
|
|
262
|
+
save_claude_mcp_config(mcp_config_path, mcp_config)
|
|
263
|
+
console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
|
|
264
|
+
console.print(f"[dim]Configuration updated: {mcp_config_path}[/dim]")
|
|
265
|
+
|
|
266
|
+
# Next steps
|
|
267
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
268
|
+
if global_config:
|
|
269
|
+
console.print("1. Restart Claude Desktop")
|
|
270
|
+
console.print("2. mcp-ticketer will no longer be available in MCP menu")
|
|
271
|
+
else:
|
|
272
|
+
console.print("1. Restart Claude Code")
|
|
273
|
+
console.print("2. mcp-ticketer will no longer be available in this project")
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
|
|
277
|
+
raise
|
|
278
|
+
|
|
279
|
+
|
|
220
280
|
def configure_claude_mcp(global_config: bool = False, force: bool = False) -> None:
|
|
221
281
|
"""Configure Claude Code to use mcp-ticketer.
|
|
222
282
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""FastMCP-based MCP server implementation.
|
|
2
|
+
|
|
3
|
+
This module implements the MCP server using the official FastMCP SDK,
|
|
4
|
+
replacing the custom JSON-RPC implementation. It provides a cleaner,
|
|
5
|
+
more maintainable approach with automatic schema generation and
|
|
6
|
+
better error handling.
|
|
7
|
+
|
|
8
|
+
The server manages a global adapter instance that is configured at
|
|
9
|
+
startup and used by all tool implementations.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
|
|
15
|
+
from mcp.server.fastmcp import FastMCP
|
|
16
|
+
|
|
17
|
+
from ..core.adapter import BaseAdapter
|
|
18
|
+
from ..core.registry import AdapterRegistry
|
|
19
|
+
|
|
20
|
+
# Initialize FastMCP server
|
|
21
|
+
mcp = FastMCP("mcp-ticketer")
|
|
22
|
+
|
|
23
|
+
# Global adapter instance
|
|
24
|
+
_adapter: Optional[BaseAdapter] = None
|
|
25
|
+
|
|
26
|
+
# Configure logging
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def configure_adapter(adapter_type: str, config: dict[str, Any]) -> None:
|
|
31
|
+
"""Configure the global adapter instance.
|
|
32
|
+
|
|
33
|
+
This must be called before starting the server to initialize the
|
|
34
|
+
adapter that will handle all ticket operations.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
adapter_type: Type of adapter to create (e.g., "linear", "jira", "github")
|
|
38
|
+
config: Configuration dictionary for the adapter
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If adapter type is not registered
|
|
42
|
+
RuntimeError: If adapter configuration fails
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
global _adapter
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Get adapter from registry
|
|
49
|
+
_adapter = AdapterRegistry.get_adapter(adapter_type, config)
|
|
50
|
+
logger.info(f"Configured {adapter_type} adapter for MCP server")
|
|
51
|
+
except Exception as e:
|
|
52
|
+
logger.error(f"Failed to configure adapter: {e}")
|
|
53
|
+
raise RuntimeError(f"Adapter configuration failed: {e}") from e
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_adapter() -> BaseAdapter:
|
|
57
|
+
"""Get the configured adapter instance.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The global adapter instance
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
RuntimeError: If adapter has not been configured
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
if _adapter is None:
|
|
67
|
+
raise RuntimeError(
|
|
68
|
+
"Adapter not configured. Call configure_adapter() before starting server."
|
|
69
|
+
)
|
|
70
|
+
return _adapter
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Import all tool modules to register them with FastMCP
|
|
74
|
+
# These imports must come after mcp is initialized but before main()
|
|
75
|
+
from . import tools # noqa: E402, F401
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main() -> None:
|
|
79
|
+
"""Run the FastMCP server.
|
|
80
|
+
|
|
81
|
+
This function starts the server using stdio transport for
|
|
82
|
+
JSON-RPC communication with Claude Desktop/Code.
|
|
83
|
+
|
|
84
|
+
The adapter must be configured via configure_adapter() before
|
|
85
|
+
calling this function.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
# Run the server with stdio transport
|
|
89
|
+
mcp.run(transport="stdio")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
main()
|