mcp-ticketer 0.12.0__py3-none-any.whl ā 2.2.13__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/__init__.py +10 -10
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/_version_scm.py +1 -0
- mcp_ticketer/adapters/aitrackdown.py +507 -6
- mcp_ticketer/adapters/asana/adapter.py +229 -0
- mcp_ticketer/adapters/asana/mappers.py +14 -0
- mcp_ticketer/adapters/github/__init__.py +26 -0
- mcp_ticketer/adapters/github/adapter.py +3229 -0
- mcp_ticketer/adapters/github/client.py +335 -0
- mcp_ticketer/adapters/github/mappers.py +797 -0
- mcp_ticketer/adapters/github/queries.py +692 -0
- mcp_ticketer/adapters/github/types.py +460 -0
- mcp_ticketer/adapters/hybrid.py +47 -5
- mcp_ticketer/adapters/jira/__init__.py +35 -0
- mcp_ticketer/adapters/jira/adapter.py +1351 -0
- mcp_ticketer/adapters/jira/client.py +271 -0
- mcp_ticketer/adapters/jira/mappers.py +246 -0
- mcp_ticketer/adapters/jira/queries.py +216 -0
- mcp_ticketer/adapters/jira/types.py +304 -0
- mcp_ticketer/adapters/linear/adapter.py +2730 -139
- mcp_ticketer/adapters/linear/client.py +175 -3
- mcp_ticketer/adapters/linear/mappers.py +203 -8
- mcp_ticketer/adapters/linear/queries.py +280 -3
- mcp_ticketer/adapters/linear/types.py +120 -4
- mcp_ticketer/analysis/__init__.py +56 -0
- mcp_ticketer/analysis/dependency_graph.py +255 -0
- mcp_ticketer/analysis/health_assessment.py +304 -0
- mcp_ticketer/analysis/orphaned.py +218 -0
- mcp_ticketer/analysis/project_status.py +594 -0
- mcp_ticketer/analysis/similarity.py +224 -0
- mcp_ticketer/analysis/staleness.py +266 -0
- mcp_ticketer/automation/__init__.py +11 -0
- mcp_ticketer/automation/project_updates.py +378 -0
- mcp_ticketer/cli/adapter_diagnostics.py +3 -1
- mcp_ticketer/cli/auggie_configure.py +17 -5
- mcp_ticketer/cli/codex_configure.py +97 -61
- mcp_ticketer/cli/configure.py +1288 -105
- mcp_ticketer/cli/cursor_configure.py +314 -0
- mcp_ticketer/cli/diagnostics.py +13 -12
- mcp_ticketer/cli/discover.py +5 -0
- mcp_ticketer/cli/gemini_configure.py +17 -5
- mcp_ticketer/cli/init_command.py +880 -0
- mcp_ticketer/cli/install_mcp_server.py +418 -0
- mcp_ticketer/cli/instruction_commands.py +6 -0
- mcp_ticketer/cli/main.py +267 -3175
- mcp_ticketer/cli/mcp_configure.py +821 -119
- mcp_ticketer/cli/mcp_server_commands.py +415 -0
- mcp_ticketer/cli/platform_detection.py +77 -12
- mcp_ticketer/cli/platform_installer.py +545 -0
- mcp_ticketer/cli/project_update_commands.py +350 -0
- mcp_ticketer/cli/setup_command.py +795 -0
- mcp_ticketer/cli/simple_health.py +12 -10
- mcp_ticketer/cli/ticket_commands.py +705 -103
- mcp_ticketer/cli/utils.py +113 -0
- mcp_ticketer/core/__init__.py +56 -6
- mcp_ticketer/core/adapter.py +533 -2
- mcp_ticketer/core/config.py +21 -21
- mcp_ticketer/core/exceptions.py +7 -1
- mcp_ticketer/core/label_manager.py +732 -0
- mcp_ticketer/core/mappers.py +31 -19
- mcp_ticketer/core/milestone_manager.py +252 -0
- mcp_ticketer/core/models.py +480 -0
- mcp_ticketer/core/onepassword_secrets.py +1 -1
- mcp_ticketer/core/priority_matcher.py +463 -0
- mcp_ticketer/core/project_config.py +132 -14
- mcp_ticketer/core/project_utils.py +281 -0
- mcp_ticketer/core/project_validator.py +376 -0
- mcp_ticketer/core/session_state.py +176 -0
- mcp_ticketer/core/state_matcher.py +625 -0
- mcp_ticketer/core/url_parser.py +425 -0
- mcp_ticketer/core/validators.py +69 -0
- mcp_ticketer/mcp/server/__main__.py +2 -1
- mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
- mcp_ticketer/mcp/server/main.py +106 -25
- mcp_ticketer/mcp/server/routing.py +723 -0
- mcp_ticketer/mcp/server/server_sdk.py +58 -0
- mcp_ticketer/mcp/server/tools/__init__.py +33 -11
- mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +5 -5
- mcp_ticketer/mcp/server/tools/bulk_tools.py +259 -202
- mcp_ticketer/mcp/server/tools/comment_tools.py +74 -12
- mcp_ticketer/mcp/server/tools/config_tools.py +1391 -145
- mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +870 -460
- mcp_ticketer/mcp/server/tools/instruction_tools.py +7 -5
- mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
- mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +3 -7
- mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
- mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +209 -97
- mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +1107 -124
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +218 -236
- mcp_ticketer/queue/queue.py +68 -0
- mcp_ticketer/queue/worker.py +1 -1
- mcp_ticketer/utils/__init__.py +5 -0
- mcp_ticketer/utils/token_utils.py +246 -0
- mcp_ticketer-2.2.13.dist-info/METADATA +1396 -0
- mcp_ticketer-2.2.13.dist-info/RECORD +158 -0
- mcp_ticketer-2.2.13.dist-info/top_level.txt +2 -0
- py_mcp_installer/examples/phase3_demo.py +178 -0
- py_mcp_installer/scripts/manage_version.py +54 -0
- py_mcp_installer/setup.py +6 -0
- py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
- py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
- py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
- py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
- py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
- py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
- py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
- py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
- py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
- py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
- py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
- py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
- py_mcp_installer/src/py_mcp_installer/types.py +222 -0
- py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
- py_mcp_installer/tests/__init__.py +0 -0
- py_mcp_installer/tests/platforms/__init__.py +0 -0
- py_mcp_installer/tests/test_platform_detector.py +17 -0
- mcp_ticketer/adapters/github.py +0 -1574
- mcp_ticketer/adapters/jira.py +0 -1258
- mcp_ticketer-0.12.0.dist-info/METADATA +0 -550
- mcp_ticketer-0.12.0.dist-info/RECORD +0 -91
- mcp_ticketer-0.12.0.dist-info/top_level.txt +0 -1
- {mcp_ticketer-0.12.0.dist-info ā mcp_ticketer-2.2.13.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.12.0.dist-info ā mcp_ticketer-2.2.13.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.12.0.dist-info ā mcp_ticketer-2.2.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""Cursor code editor configuration for mcp-ticketer integration.
|
|
2
|
+
|
|
3
|
+
Cursor uses project-level MCP configuration at ~/.cursor/mcp.json
|
|
4
|
+
with a flat mcpServers structure (similar to Claude Code's global config).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from .mcp_configure import load_project_config
|
|
14
|
+
from .python_detection import get_mcp_ticketer_python
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def find_cursor_config() -> Path:
|
|
20
|
+
"""Find or create Cursor MCP configuration file.
|
|
21
|
+
|
|
22
|
+
Cursor uses global MCP configuration with flat structure.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Path to Cursor MCP config file at ~/.cursor/mcp.json
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
config_path = Path.home() / ".cursor" / "mcp.json"
|
|
29
|
+
return config_path
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def load_cursor_config(config_path: Path) -> dict[str, Any]:
|
|
33
|
+
"""Load existing Cursor configuration or return empty structure.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
config_path: Path to Cursor MCP config file
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Cursor MCP configuration dict
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
if config_path.exists():
|
|
43
|
+
try:
|
|
44
|
+
with open(config_path) as f:
|
|
45
|
+
content = f.read().strip()
|
|
46
|
+
if not content:
|
|
47
|
+
return {"mcpServers": {}}
|
|
48
|
+
config: dict[str, Any] = json.load(f)
|
|
49
|
+
return config
|
|
50
|
+
except json.JSONDecodeError as e:
|
|
51
|
+
console.print(
|
|
52
|
+
f"[yellow]ā Warning: Invalid JSON in {config_path}, creating new config[/yellow]"
|
|
53
|
+
)
|
|
54
|
+
console.print(f"[dim]Error: {e}[/dim]")
|
|
55
|
+
|
|
56
|
+
# Return empty structure with mcpServers section
|
|
57
|
+
return {"mcpServers": {}}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def save_cursor_config(config_path: Path, config: dict[str, Any]) -> None:
|
|
61
|
+
"""Save Cursor MCP configuration to file.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
config_path: Path to Cursor MCP config file
|
|
65
|
+
config: Configuration to save
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
# Ensure directory exists
|
|
69
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
|
|
71
|
+
# Write with 2-space indentation (JSON standard)
|
|
72
|
+
with open(config_path, "w") as f:
|
|
73
|
+
json.dump(config, f, indent=2)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def create_cursor_server_config(
|
|
77
|
+
python_path: str,
|
|
78
|
+
project_config: dict[str, Any],
|
|
79
|
+
project_path: str | None = None,
|
|
80
|
+
) -> dict[str, Any]:
|
|
81
|
+
"""Create Cursor MCP server configuration for mcp-ticketer.
|
|
82
|
+
|
|
83
|
+
Uses the CLI command (mcp-ticketer mcp) which implements proper
|
|
84
|
+
Content-Length framing via FastMCP SDK, required for modern MCP clients.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
python_path: Path to Python executable in mcp-ticketer venv
|
|
88
|
+
project_config: Project configuration from .mcp-ticketer/config.json
|
|
89
|
+
project_path: Project directory path (optional, for project-specific config)
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Cursor MCP server configuration dict
|
|
93
|
+
|
|
94
|
+
"""
|
|
95
|
+
# IMPORTANT: Use CLI command, NOT Python module invocation
|
|
96
|
+
# The CLI uses FastMCP SDK which implements proper Content-Length framing
|
|
97
|
+
from pathlib import Path
|
|
98
|
+
|
|
99
|
+
# Get adapter configuration
|
|
100
|
+
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
101
|
+
adapters_config = project_config.get("adapters", {})
|
|
102
|
+
adapter_config = adapters_config.get(adapter, {})
|
|
103
|
+
|
|
104
|
+
# Get mcp-ticketer CLI path from Python path
|
|
105
|
+
python_dir = Path(python_path).parent
|
|
106
|
+
cli_path = str(python_dir / "mcp-ticketer")
|
|
107
|
+
|
|
108
|
+
# Build CLI arguments
|
|
109
|
+
args = ["mcp"]
|
|
110
|
+
if project_path:
|
|
111
|
+
args.extend(["--path", project_path])
|
|
112
|
+
|
|
113
|
+
# Build environment variables
|
|
114
|
+
env_vars = {}
|
|
115
|
+
|
|
116
|
+
# Add PYTHONPATH for project context
|
|
117
|
+
if project_path:
|
|
118
|
+
env_vars["PYTHONPATH"] = project_path
|
|
119
|
+
|
|
120
|
+
# Add adapter type
|
|
121
|
+
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
122
|
+
|
|
123
|
+
# Add adapter-specific environment variables
|
|
124
|
+
if adapter == "linear" and "api_key" in adapter_config:
|
|
125
|
+
env_vars["LINEAR_API_KEY"] = adapter_config["api_key"]
|
|
126
|
+
if "team_id" in adapter_config:
|
|
127
|
+
env_vars["LINEAR_TEAM_ID"] = adapter_config["team_id"]
|
|
128
|
+
elif adapter == "github" and "token" in adapter_config:
|
|
129
|
+
env_vars["GITHUB_TOKEN"] = adapter_config["token"]
|
|
130
|
+
if "owner" in adapter_config:
|
|
131
|
+
env_vars["GITHUB_OWNER"] = adapter_config["owner"]
|
|
132
|
+
if "repo" in adapter_config:
|
|
133
|
+
env_vars["GITHUB_REPO"] = adapter_config["repo"]
|
|
134
|
+
elif adapter == "jira":
|
|
135
|
+
if "api_token" in adapter_config:
|
|
136
|
+
env_vars["JIRA_API_TOKEN"] = adapter_config["api_token"]
|
|
137
|
+
if "email" in adapter_config:
|
|
138
|
+
env_vars["JIRA_EMAIL"] = adapter_config["email"]
|
|
139
|
+
|
|
140
|
+
# Create server configuration with Cursor-specific fields
|
|
141
|
+
config = {
|
|
142
|
+
"type": "stdio", # Cursor requires explicit type
|
|
143
|
+
"command": cli_path,
|
|
144
|
+
"args": args,
|
|
145
|
+
"env": env_vars,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Add working directory for project-specific configs
|
|
149
|
+
if project_path:
|
|
150
|
+
config["cwd"] = project_path
|
|
151
|
+
|
|
152
|
+
return config
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def remove_cursor_mcp(dry_run: bool = False) -> None:
|
|
156
|
+
"""Remove mcp-ticketer from Cursor configuration.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
dry_run: Show what would be removed without making changes
|
|
160
|
+
|
|
161
|
+
"""
|
|
162
|
+
# Step 1: Find Cursor config location
|
|
163
|
+
console.print("[cyan]š Removing Cursor MCP configuration...[/cyan]")
|
|
164
|
+
|
|
165
|
+
cursor_config_path = find_cursor_config()
|
|
166
|
+
console.print(f"[dim]Config location: {cursor_config_path}[/dim]")
|
|
167
|
+
|
|
168
|
+
# Step 2: Check if config file exists
|
|
169
|
+
if not cursor_config_path.exists():
|
|
170
|
+
console.print(
|
|
171
|
+
f"[yellow]ā No configuration found at {cursor_config_path}[/yellow]"
|
|
172
|
+
)
|
|
173
|
+
console.print("[dim]mcp-ticketer is not configured for Cursor[/dim]")
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
# Step 3: Load existing Cursor configuration
|
|
177
|
+
cursor_config = load_cursor_config(cursor_config_path)
|
|
178
|
+
|
|
179
|
+
# Step 4: Check if mcp-ticketer is configured
|
|
180
|
+
if "mcp-ticketer" not in cursor_config.get("mcpServers", {}):
|
|
181
|
+
console.print("[yellow]ā mcp-ticketer is not configured[/yellow]")
|
|
182
|
+
console.print(f"[dim]No mcp-ticketer entry found in {cursor_config_path}[/dim]")
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
# Show what would be removed (dry run)
|
|
186
|
+
if dry_run:
|
|
187
|
+
console.print(
|
|
188
|
+
f"\n[cyan]DRY RUN - Would remove from: {cursor_config_path}[/cyan]"
|
|
189
|
+
)
|
|
190
|
+
console.print(" Server name: mcp-ticketer")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
# Step 5: Remove mcp-ticketer from configuration
|
|
194
|
+
del cursor_config["mcpServers"]["mcp-ticketer"]
|
|
195
|
+
|
|
196
|
+
# Step 6: Save updated configuration
|
|
197
|
+
try:
|
|
198
|
+
save_cursor_config(cursor_config_path, cursor_config)
|
|
199
|
+
console.print("\n[green]ā Successfully removed mcp-ticketer[/green]")
|
|
200
|
+
console.print(f"[dim]Updated {cursor_config_path}[/dim]")
|
|
201
|
+
|
|
202
|
+
# Next steps
|
|
203
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
204
|
+
console.print("1. Restart Cursor editor")
|
|
205
|
+
console.print("2. mcp-ticketer will no longer be available in MCP menu")
|
|
206
|
+
except Exception as e:
|
|
207
|
+
console.print(f"\n[red]ā Failed to save configuration:[/red] {e}")
|
|
208
|
+
raise
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def configure_cursor_mcp(force: bool = False) -> None:
|
|
212
|
+
"""Configure Cursor to use mcp-ticketer.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
force: Overwrite existing configuration
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
FileNotFoundError: If Python executable or project config not found
|
|
219
|
+
ValueError: If configuration is invalid
|
|
220
|
+
|
|
221
|
+
"""
|
|
222
|
+
# Determine project path
|
|
223
|
+
project_path = Path.cwd()
|
|
224
|
+
|
|
225
|
+
# Step 1: Find Python executable
|
|
226
|
+
console.print("[cyan]š Finding mcp-ticketer Python executable...[/cyan]")
|
|
227
|
+
try:
|
|
228
|
+
python_path = get_mcp_ticketer_python(project_path=project_path)
|
|
229
|
+
console.print(f"[green]ā[/green] Found: {python_path}")
|
|
230
|
+
|
|
231
|
+
# Show if using project venv or fallback
|
|
232
|
+
if str(project_path / ".venv") in python_path:
|
|
233
|
+
console.print("[dim]Using project-specific venv[/dim]")
|
|
234
|
+
else:
|
|
235
|
+
console.print("[dim]Using pipx/system Python[/dim]")
|
|
236
|
+
except Exception as e:
|
|
237
|
+
console.print(f"[red]ā[/red] Could not find Python executable: {e}")
|
|
238
|
+
raise FileNotFoundError(
|
|
239
|
+
"Could not find mcp-ticketer Python executable. "
|
|
240
|
+
"Please ensure mcp-ticketer is installed.\n"
|
|
241
|
+
"Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
|
|
242
|
+
) from e
|
|
243
|
+
|
|
244
|
+
# Step 2: Load project configuration
|
|
245
|
+
console.print("\n[cyan]š Reading project configuration...[/cyan]")
|
|
246
|
+
try:
|
|
247
|
+
mcp_project_config = load_project_config()
|
|
248
|
+
adapter = mcp_project_config.get("default_adapter", "aitrackdown")
|
|
249
|
+
console.print(f"[green]ā[/green] Adapter: {adapter}")
|
|
250
|
+
except (FileNotFoundError, ValueError) as e:
|
|
251
|
+
console.print(f"[red]ā[/red] {e}")
|
|
252
|
+
raise
|
|
253
|
+
|
|
254
|
+
# Step 3: Find Cursor MCP config location
|
|
255
|
+
console.print("\n[cyan]š§ Configuring Cursor MCP...[/cyan]")
|
|
256
|
+
|
|
257
|
+
cursor_config_path = find_cursor_config()
|
|
258
|
+
console.print(f"[dim]Config path: {cursor_config_path}[/dim]")
|
|
259
|
+
|
|
260
|
+
# Step 4: Load existing MCP configuration
|
|
261
|
+
cursor_config = load_cursor_config(cursor_config_path)
|
|
262
|
+
|
|
263
|
+
# Step 5: Check if mcp-ticketer already configured
|
|
264
|
+
already_configured = "mcp-ticketer" in cursor_config.get("mcpServers", {})
|
|
265
|
+
|
|
266
|
+
if already_configured:
|
|
267
|
+
if not force:
|
|
268
|
+
console.print("[yellow]ā mcp-ticketer is already configured[/yellow]")
|
|
269
|
+
console.print("[dim]Use --force to overwrite existing configuration[/dim]")
|
|
270
|
+
return
|
|
271
|
+
else:
|
|
272
|
+
console.print("[yellow]ā Overwriting existing configuration[/yellow]")
|
|
273
|
+
|
|
274
|
+
# Step 6: Create mcp-ticketer server config
|
|
275
|
+
server_config = create_cursor_server_config(
|
|
276
|
+
python_path=python_path,
|
|
277
|
+
project_config=mcp_project_config,
|
|
278
|
+
project_path=str(project_path.resolve()),
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Step 7: Update MCP configuration
|
|
282
|
+
if "mcpServers" not in cursor_config:
|
|
283
|
+
cursor_config["mcpServers"] = {}
|
|
284
|
+
cursor_config["mcpServers"]["mcp-ticketer"] = server_config
|
|
285
|
+
|
|
286
|
+
# Step 8: Save configuration
|
|
287
|
+
try:
|
|
288
|
+
save_cursor_config(cursor_config_path, cursor_config)
|
|
289
|
+
console.print("\n[green]ā Successfully configured mcp-ticketer[/green]")
|
|
290
|
+
console.print(f"[dim]Configuration saved to: {cursor_config_path}[/dim]")
|
|
291
|
+
|
|
292
|
+
# Print configuration details
|
|
293
|
+
console.print("\n[bold]Configuration Details:[/bold]")
|
|
294
|
+
console.print(" Server name: mcp-ticketer")
|
|
295
|
+
console.print(f" Adapter: {adapter}")
|
|
296
|
+
console.print(f" Python: {python_path}")
|
|
297
|
+
console.print(f" Command: {server_config.get('command')}")
|
|
298
|
+
console.print(f" Args: {server_config.get('args')}")
|
|
299
|
+
console.print(" Protocol: Content-Length framing (FastMCP SDK)")
|
|
300
|
+
console.print(f" Project path: {project_path}")
|
|
301
|
+
if "env" in server_config:
|
|
302
|
+
console.print(
|
|
303
|
+
f" Environment variables: {list(server_config['env'].keys())}"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Next steps
|
|
307
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
308
|
+
console.print("1. Restart Cursor editor")
|
|
309
|
+
console.print("2. Open this project in Cursor")
|
|
310
|
+
console.print("3. mcp-ticketer tools will be available in the MCP menu")
|
|
311
|
+
|
|
312
|
+
except Exception as e:
|
|
313
|
+
console.print(f"\n[red]ā Failed to save configuration:[/red] {e}")
|
|
314
|
+
raise
|
mcp_ticketer/cli/diagnostics.py
CHANGED
|
@@ -154,7 +154,7 @@ class SystemDiagnostics:
|
|
|
154
154
|
"""Diagnose configuration issues."""
|
|
155
155
|
console.print("\nš [yellow]Configuration Analysis[/yellow]")
|
|
156
156
|
|
|
157
|
-
config_status = {
|
|
157
|
+
config_status: dict[str, Any] = {
|
|
158
158
|
"status": "healthy",
|
|
159
159
|
"adapters_configured": 0,
|
|
160
160
|
"default_adapter": None,
|
|
@@ -260,7 +260,7 @@ class SystemDiagnostics:
|
|
|
260
260
|
"""Diagnose adapter functionality."""
|
|
261
261
|
console.print("\nš [yellow]Adapter Diagnosis[/yellow]")
|
|
262
262
|
|
|
263
|
-
adapter_status = {
|
|
263
|
+
adapter_status: dict[str, Any] = {
|
|
264
264
|
"total_adapters": 0,
|
|
265
265
|
"healthy_adapters": 0,
|
|
266
266
|
"failed_adapters": 0,
|
|
@@ -278,7 +278,7 @@ class SystemDiagnostics:
|
|
|
278
278
|
for name, adapter_config in adapters_config.items():
|
|
279
279
|
adapter_type = adapter_config.get("type", name)
|
|
280
280
|
|
|
281
|
-
details = {
|
|
281
|
+
details: dict[str, Any] = {
|
|
282
282
|
"type": adapter_type,
|
|
283
283
|
"status": "unknown",
|
|
284
284
|
"last_test": None,
|
|
@@ -330,7 +330,7 @@ class SystemDiagnostics:
|
|
|
330
330
|
"""Diagnose queue system health with active testing."""
|
|
331
331
|
console.print("\nā” [yellow]Queue System Diagnosis[/yellow]")
|
|
332
332
|
|
|
333
|
-
queue_status = {
|
|
333
|
+
queue_status: dict[str, Any] = {
|
|
334
334
|
"worker_running": False,
|
|
335
335
|
"worker_pid": None,
|
|
336
336
|
"queue_stats": {},
|
|
@@ -455,7 +455,7 @@ class SystemDiagnostics:
|
|
|
455
455
|
|
|
456
456
|
async def _test_worker_startup(self) -> dict[str, Any]:
|
|
457
457
|
"""Test starting a queue worker."""
|
|
458
|
-
test_result = {
|
|
458
|
+
test_result: dict[str, Any] = {
|
|
459
459
|
"attempted": True,
|
|
460
460
|
"success": False,
|
|
461
461
|
"error": None,
|
|
@@ -497,7 +497,7 @@ class SystemDiagnostics:
|
|
|
497
497
|
|
|
498
498
|
async def _test_queue_operations(self) -> dict[str, Any]:
|
|
499
499
|
"""Test basic queue operations."""
|
|
500
|
-
test_result = {
|
|
500
|
+
test_result: dict[str, Any] = {
|
|
501
501
|
"attempted": True,
|
|
502
502
|
"success": False,
|
|
503
503
|
"error": None,
|
|
@@ -509,7 +509,7 @@ class SystemDiagnostics:
|
|
|
509
509
|
from ..core.models import Priority, Task
|
|
510
510
|
from ..queue.queue import Queue
|
|
511
511
|
|
|
512
|
-
test_task = Task(
|
|
512
|
+
test_task = Task( # type: ignore[call-arg]
|
|
513
513
|
title="[DIAGNOSTIC TEST] Queue functionality test",
|
|
514
514
|
description="This is a diagnostic test - safe to ignore",
|
|
515
515
|
priority=Priority.LOW,
|
|
@@ -532,7 +532,7 @@ class SystemDiagnostics:
|
|
|
532
532
|
|
|
533
533
|
async def _test_basic_queue_functionality(self) -> dict[str, Any]:
|
|
534
534
|
"""Test basic queue functionality in fallback mode."""
|
|
535
|
-
test_result = {
|
|
535
|
+
test_result: dict[str, Any] = {
|
|
536
536
|
"attempted": True,
|
|
537
537
|
"success": False,
|
|
538
538
|
"error": None,
|
|
@@ -544,7 +544,7 @@ class SystemDiagnostics:
|
|
|
544
544
|
from ..adapters.aitrackdown import AITrackdownAdapter
|
|
545
545
|
from ..core.models import Priority, Task
|
|
546
546
|
|
|
547
|
-
test_task = Task(
|
|
547
|
+
test_task = Task( # type: ignore[call-arg]
|
|
548
548
|
title="[DIAGNOSTIC TEST] Direct adapter test",
|
|
549
549
|
description="Testing direct adapter functionality",
|
|
550
550
|
priority=Priority.LOW,
|
|
@@ -564,7 +564,8 @@ class SystemDiagnostics:
|
|
|
564
564
|
test_result["details"] = f"Direct adapter test passed: {result.id}"
|
|
565
565
|
|
|
566
566
|
# Clean up test
|
|
567
|
-
|
|
567
|
+
if result.id:
|
|
568
|
+
await adapter.delete(result.id)
|
|
568
569
|
|
|
569
570
|
except Exception as e:
|
|
570
571
|
test_result["error"] = str(e)
|
|
@@ -575,7 +576,7 @@ class SystemDiagnostics:
|
|
|
575
576
|
"""Analyze recent log entries for issues."""
|
|
576
577
|
console.print("\nš [yellow]Recent Log Analysis[/yellow]")
|
|
577
578
|
|
|
578
|
-
log_analysis = {
|
|
579
|
+
log_analysis: dict[str, Any] = {
|
|
579
580
|
"log_files_found": [],
|
|
580
581
|
"recent_errors": [],
|
|
581
582
|
"recent_warnings": [],
|
|
@@ -644,7 +645,7 @@ class SystemDiagnostics:
|
|
|
644
645
|
"""Analyze system performance metrics."""
|
|
645
646
|
console.print("\nā” [yellow]Performance Analysis[/yellow]")
|
|
646
647
|
|
|
647
|
-
performance = {
|
|
648
|
+
performance: dict[str, Any] = {
|
|
648
649
|
"response_times": {},
|
|
649
650
|
"throughput": {},
|
|
650
651
|
"resource_usage": {},
|
mcp_ticketer/cli/discover.py
CHANGED
|
@@ -28,10 +28,12 @@ def _mask_sensitive(value: str, key: str) -> str:
|
|
|
28
28
|
"""Mask sensitive values for display.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
31
|
+
----
|
|
31
32
|
value: Value to potentially mask
|
|
32
33
|
key: Key name to determine if masking needed
|
|
33
34
|
|
|
34
35
|
Returns:
|
|
36
|
+
-------
|
|
35
37
|
Masked or original value
|
|
36
38
|
|
|
37
39
|
"""
|
|
@@ -61,6 +63,7 @@ def _display_discovered_adapter(
|
|
|
61
63
|
"""Display information about a discovered adapter.
|
|
62
64
|
|
|
63
65
|
Args:
|
|
66
|
+
----
|
|
64
67
|
adapter: Discovered adapter to display
|
|
65
68
|
discovery: EnvDiscovery instance for validation
|
|
66
69
|
|
|
@@ -504,6 +507,7 @@ def onepassword_template(
|
|
|
504
507
|
which can be used with: op run --env-file=.env.1password -- <command>
|
|
505
508
|
|
|
506
509
|
Examples:
|
|
510
|
+
--------
|
|
507
511
|
# Create Linear template
|
|
508
512
|
mcp-ticketer discover 1password-template linear
|
|
509
513
|
|
|
@@ -568,6 +572,7 @@ def onepassword_test(
|
|
|
568
572
|
displays the resolved values (with sensitive data masked).
|
|
569
573
|
|
|
570
574
|
Example:
|
|
575
|
+
-------
|
|
571
576
|
mcp-ticketer discover 1password-test --file=.env.1password.linear
|
|
572
577
|
|
|
573
578
|
"""
|
|
@@ -78,6 +78,9 @@ def create_gemini_server_config(
|
|
|
78
78
|
) -> dict:
|
|
79
79
|
"""Create Gemini MCP server configuration for mcp-ticketer.
|
|
80
80
|
|
|
81
|
+
Uses the CLI command (mcp-ticketer mcp) which implements proper
|
|
82
|
+
Content-Length framing via FastMCP SDK, required for modern MCP clients.
|
|
83
|
+
|
|
81
84
|
Args:
|
|
82
85
|
python_path: Path to Python executable in mcp-ticketer venv
|
|
83
86
|
project_config: Project configuration from .mcp-ticketer/config.json
|
|
@@ -87,7 +90,9 @@ def create_gemini_server_config(
|
|
|
87
90
|
Gemini MCP server configuration dict
|
|
88
91
|
|
|
89
92
|
"""
|
|
90
|
-
# Use Python module invocation
|
|
93
|
+
# IMPORTANT: Use CLI command, NOT Python module invocation
|
|
94
|
+
# The CLI uses FastMCP SDK which implements proper Content-Length framing
|
|
95
|
+
# Legacy python -m mcp_ticketer.mcp.server uses line-delimited JSON (incompatible)
|
|
91
96
|
|
|
92
97
|
# Get adapter configuration
|
|
93
98
|
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
@@ -138,14 +143,21 @@ def create_gemini_server_config(
|
|
|
138
143
|
if "project_key" in adapter_config:
|
|
139
144
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
140
145
|
|
|
141
|
-
#
|
|
142
|
-
|
|
146
|
+
# Get mcp-ticketer CLI path from Python path
|
|
147
|
+
# If python_path is /path/to/venv/bin/python, CLI is /path/to/venv/bin/mcp-ticketer
|
|
148
|
+
python_dir = Path(python_path).parent
|
|
149
|
+
cli_path = str(python_dir / "mcp-ticketer")
|
|
150
|
+
|
|
151
|
+
# Build CLI arguments
|
|
152
|
+
args = ["mcp"]
|
|
143
153
|
if project_path:
|
|
144
|
-
args.
|
|
154
|
+
args.extend(["--path", project_path])
|
|
145
155
|
|
|
146
156
|
# Create server configuration with Gemini-specific options
|
|
157
|
+
# NOTE: Environment variables below are optional fallbacks
|
|
158
|
+
# The CLI loads config from .mcp-ticketer/config.json
|
|
147
159
|
config = {
|
|
148
|
-
"command":
|
|
160
|
+
"command": cli_path,
|
|
149
161
|
"args": args,
|
|
150
162
|
"env": env_vars,
|
|
151
163
|
"timeout": 15000, # 15 seconds timeout
|