mcp-ticketer 0.2.0__py3-none-any.whl → 0.3.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.
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/cli/adapter_diagnostics.py +384 -0
- mcp_ticketer/cli/main.py +364 -81
- mcp_ticketer/core/env_discovery.py +31 -3
- mcp_ticketer/mcp/server.py +172 -37
- {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-0.3.1.dist-info}/METADATA +1 -1
- {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-0.3.1.dist-info}/RECORD +11 -10
- {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-0.3.1.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-0.3.1.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-0.3.1.dist-info}/top_level.txt +0 -0
mcp_ticketer/__version__.py
CHANGED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""Adapter diagnostics and configuration validation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from ..core import AdapterRegistry
|
|
14
|
+
from ..core.env_discovery import discover_config
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def diagnose_adapter_configuration(console: Console) -> None:
|
|
18
|
+
"""Diagnose adapter configuration and provide recommendations.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
console: Rich console for output
|
|
22
|
+
"""
|
|
23
|
+
console.print("\n[bold blue]🔍 MCP Ticketer Adapter Configuration Diagnostics[/bold blue]\n")
|
|
24
|
+
|
|
25
|
+
# 1. Check .env files
|
|
26
|
+
_check_env_files(console)
|
|
27
|
+
|
|
28
|
+
# 2. Check configuration files
|
|
29
|
+
_check_configuration_files(console)
|
|
30
|
+
|
|
31
|
+
# 3. Check adapter discovery
|
|
32
|
+
_check_adapter_discovery(console)
|
|
33
|
+
|
|
34
|
+
# 4. Test adapter instantiation
|
|
35
|
+
_test_adapter_instantiation(console)
|
|
36
|
+
|
|
37
|
+
# 5. Provide recommendations
|
|
38
|
+
_provide_recommendations(console)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _check_env_files(console: Console) -> None:
|
|
42
|
+
"""Check .env files for configuration."""
|
|
43
|
+
console.print("[bold]1. .env File Configuration[/bold]")
|
|
44
|
+
|
|
45
|
+
# Load .env files
|
|
46
|
+
from ..mcp.server import _load_env_configuration
|
|
47
|
+
env_config = _load_env_configuration()
|
|
48
|
+
|
|
49
|
+
# Check for .env files
|
|
50
|
+
env_files = [".env.local", ".env"]
|
|
51
|
+
|
|
52
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
53
|
+
table.add_column("File", style="cyan")
|
|
54
|
+
table.add_column("Status", style="green")
|
|
55
|
+
table.add_column("Variables Found", style="yellow")
|
|
56
|
+
|
|
57
|
+
for env_file in env_files:
|
|
58
|
+
env_path = Path.cwd() / env_file
|
|
59
|
+
if env_path.exists():
|
|
60
|
+
try:
|
|
61
|
+
# Count variables in file
|
|
62
|
+
var_count = 0
|
|
63
|
+
with open(env_path, 'r') as f:
|
|
64
|
+
for line in f:
|
|
65
|
+
line = line.strip()
|
|
66
|
+
if line and not line.startswith('#') and '=' in line:
|
|
67
|
+
var_count += 1
|
|
68
|
+
|
|
69
|
+
status = "✅ Found"
|
|
70
|
+
variables = f"{var_count} variables"
|
|
71
|
+
except Exception:
|
|
72
|
+
status = "⚠️ Error reading"
|
|
73
|
+
variables = "Unknown"
|
|
74
|
+
else:
|
|
75
|
+
status = "❌ Missing"
|
|
76
|
+
variables = "N/A"
|
|
77
|
+
|
|
78
|
+
table.add_row(env_file, status, variables)
|
|
79
|
+
|
|
80
|
+
console.print(table)
|
|
81
|
+
|
|
82
|
+
# Show discovered configuration
|
|
83
|
+
if env_config:
|
|
84
|
+
console.print(f"\n[green]✅ Discovered adapter: {env_config['adapter_type']}[/green]")
|
|
85
|
+
config_keys = list(env_config['adapter_config'].keys())
|
|
86
|
+
console.print(f"[dim]Configuration keys: {config_keys}[/dim]")
|
|
87
|
+
else:
|
|
88
|
+
console.print("\n[yellow]⚠️ No adapter configuration found in .env files[/yellow]")
|
|
89
|
+
|
|
90
|
+
console.print()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _check_configuration_files(console: Console) -> None:
|
|
94
|
+
"""Check configuration files."""
|
|
95
|
+
console.print("[bold]2. Configuration Files[/bold]")
|
|
96
|
+
|
|
97
|
+
config_files = [
|
|
98
|
+
(".env.local", "Local environment file (highest priority)"),
|
|
99
|
+
(".env", "Environment file"),
|
|
100
|
+
(".mcp-ticketer/config.json", "Project configuration"),
|
|
101
|
+
(str(Path.home() / ".mcp-ticketer" / "config.json"), "Global configuration"),
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
105
|
+
table.add_column("File", style="cyan")
|
|
106
|
+
table.add_column("Description", style="white")
|
|
107
|
+
table.add_column("Status", style="green")
|
|
108
|
+
table.add_column("Size", style="yellow")
|
|
109
|
+
|
|
110
|
+
for file_path, description in config_files:
|
|
111
|
+
path = Path(file_path)
|
|
112
|
+
if path.exists():
|
|
113
|
+
try:
|
|
114
|
+
size = path.stat().st_size
|
|
115
|
+
status = "✅ Found"
|
|
116
|
+
size_str = f"{size} bytes"
|
|
117
|
+
except Exception:
|
|
118
|
+
status = "⚠️ Error"
|
|
119
|
+
size_str = "Unknown"
|
|
120
|
+
else:
|
|
121
|
+
status = "❌ Missing"
|
|
122
|
+
size_str = "N/A"
|
|
123
|
+
|
|
124
|
+
table.add_row(str(path), description, status, size_str)
|
|
125
|
+
|
|
126
|
+
console.print(table)
|
|
127
|
+
console.print()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _check_adapter_discovery(console: Console) -> None:
|
|
131
|
+
"""Check adapter discovery from configuration."""
|
|
132
|
+
console.print("[bold]3. Adapter Discovery[/bold]")
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
# Discover configuration
|
|
136
|
+
discovered = discover_config(Path.cwd())
|
|
137
|
+
|
|
138
|
+
if discovered and discovered.adapters:
|
|
139
|
+
primary = discovered.get_primary_adapter()
|
|
140
|
+
|
|
141
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
142
|
+
table.add_column("Adapter", style="cyan")
|
|
143
|
+
table.add_column("Confidence", style="white")
|
|
144
|
+
table.add_column("Source", style="green")
|
|
145
|
+
table.add_column("Status", style="yellow")
|
|
146
|
+
|
|
147
|
+
for adapter_info in discovered.adapters:
|
|
148
|
+
confidence = f"{adapter_info.confidence:.0%}"
|
|
149
|
+
status = "✅ Primary" if adapter_info == primary else "⚪ Available"
|
|
150
|
+
|
|
151
|
+
table.add_row(
|
|
152
|
+
adapter_info.adapter_type,
|
|
153
|
+
confidence,
|
|
154
|
+
adapter_info.found_in,
|
|
155
|
+
status
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
console.print(table)
|
|
159
|
+
|
|
160
|
+
if primary:
|
|
161
|
+
console.print(f"\n[green]✅ Primary adapter detected: {primary.adapter_type}[/green]")
|
|
162
|
+
console.print(f"[dim]Source: {primary.found_in}[/dim]")
|
|
163
|
+
console.print(f"[dim]Confidence: {primary.confidence:.0%}[/dim]")
|
|
164
|
+
else:
|
|
165
|
+
console.print("\n[yellow]⚠️ No primary adapter detected[/yellow]")
|
|
166
|
+
else:
|
|
167
|
+
console.print("[red]❌ No adapters discovered[/red]")
|
|
168
|
+
console.print("[dim]This usually means no credentials are configured[/dim]")
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
console.print(f"[red]❌ Error during discovery: {e}[/red]")
|
|
172
|
+
|
|
173
|
+
console.print()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _test_adapter_instantiation(console: Console) -> None:
|
|
177
|
+
"""Test adapter instantiation."""
|
|
178
|
+
console.print("[bold]4. Adapter Instantiation Test[/bold]")
|
|
179
|
+
|
|
180
|
+
# Determine which adapter to test from .env files
|
|
181
|
+
from ..mcp.server import _load_env_configuration
|
|
182
|
+
env_config = _load_env_configuration()
|
|
183
|
+
|
|
184
|
+
if env_config:
|
|
185
|
+
adapter_type = env_config["adapter_type"]
|
|
186
|
+
config = env_config["adapter_config"]
|
|
187
|
+
else:
|
|
188
|
+
# Try to discover from existing discovery system
|
|
189
|
+
try:
|
|
190
|
+
discovered = discover_config(Path.cwd())
|
|
191
|
+
if discovered and discovered.adapters:
|
|
192
|
+
primary = discovered.get_primary_adapter()
|
|
193
|
+
if primary:
|
|
194
|
+
adapter_type = primary.adapter_type
|
|
195
|
+
# Build config from discovery
|
|
196
|
+
from ..mcp.server import _build_adapter_config_from_env_vars
|
|
197
|
+
config = _build_adapter_config_from_env_vars(adapter_type, {})
|
|
198
|
+
else:
|
|
199
|
+
adapter_type = "aitrackdown"
|
|
200
|
+
config = {"base_path": ".aitrackdown"}
|
|
201
|
+
else:
|
|
202
|
+
adapter_type = "aitrackdown"
|
|
203
|
+
config = {"base_path": ".aitrackdown"}
|
|
204
|
+
except Exception:
|
|
205
|
+
adapter_type = "aitrackdown"
|
|
206
|
+
config = {"base_path": ".aitrackdown"}
|
|
207
|
+
|
|
208
|
+
console.print(f"Testing adapter: [cyan]{adapter_type}[/cyan]")
|
|
209
|
+
console.print(f"Configuration keys: [yellow]{list(config.keys())}[/yellow]")
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
# Try to instantiate adapter
|
|
213
|
+
adapter = AdapterRegistry.get_adapter(adapter_type, config)
|
|
214
|
+
|
|
215
|
+
console.print(f"[green]✅ Adapter instantiated successfully: {adapter.__class__.__name__}[/green]")
|
|
216
|
+
|
|
217
|
+
# Test basic functionality
|
|
218
|
+
if hasattr(adapter, 'validate_credentials'):
|
|
219
|
+
try:
|
|
220
|
+
is_valid, error_msg = adapter.validate_credentials()
|
|
221
|
+
if is_valid:
|
|
222
|
+
console.print("[green]✅ Credentials validation passed[/green]")
|
|
223
|
+
else:
|
|
224
|
+
console.print(f"[red]❌ Credentials validation failed: {error_msg}[/red]")
|
|
225
|
+
except Exception as e:
|
|
226
|
+
console.print(f"[yellow]⚠️ Credentials validation error: {e}[/yellow]")
|
|
227
|
+
|
|
228
|
+
except Exception as e:
|
|
229
|
+
console.print(f"[red]❌ Adapter instantiation failed: {e}[/red]")
|
|
230
|
+
|
|
231
|
+
# Provide specific guidance based on adapter type
|
|
232
|
+
if adapter_type == "linear":
|
|
233
|
+
console.print("\n[yellow]Linear adapter requires in .env/.env.local:[/yellow]")
|
|
234
|
+
console.print("• LINEAR_API_KEY=your_api_key")
|
|
235
|
+
console.print("• LINEAR_TEAM_ID=your_team_id (or LINEAR_TEAM_KEY=your_team_key)")
|
|
236
|
+
elif adapter_type == "github":
|
|
237
|
+
console.print("\n[yellow]GitHub adapter requires in .env/.env.local:[/yellow]")
|
|
238
|
+
console.print("• GITHUB_TOKEN=your_token")
|
|
239
|
+
console.print("• GITHUB_OWNER=your_username")
|
|
240
|
+
console.print("• GITHUB_REPO=your_repository")
|
|
241
|
+
elif adapter_type == "jira":
|
|
242
|
+
console.print("\n[yellow]JIRA adapter requires in .env/.env.local:[/yellow]")
|
|
243
|
+
console.print("• JIRA_SERVER=your_server_url")
|
|
244
|
+
console.print("• JIRA_EMAIL=your_email")
|
|
245
|
+
console.print("• JIRA_API_TOKEN=your_token")
|
|
246
|
+
|
|
247
|
+
console.print()
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _provide_recommendations(console: Console) -> None:
|
|
251
|
+
"""Provide configuration recommendations."""
|
|
252
|
+
console.print("[bold]5. Recommendations[/bold]")
|
|
253
|
+
|
|
254
|
+
# Check .env configuration
|
|
255
|
+
from ..mcp.server import _load_env_configuration
|
|
256
|
+
env_config = _load_env_configuration()
|
|
257
|
+
|
|
258
|
+
recommendations = []
|
|
259
|
+
|
|
260
|
+
if not env_config:
|
|
261
|
+
recommendations.append(
|
|
262
|
+
"Create .env.local or .env file with adapter configuration"
|
|
263
|
+
)
|
|
264
|
+
recommendations.append(
|
|
265
|
+
"Add MCP_TICKETER_ADAPTER=linear (or github, jira) to specify adapter type"
|
|
266
|
+
)
|
|
267
|
+
else:
|
|
268
|
+
adapter_type = env_config["adapter_type"]
|
|
269
|
+
config = env_config["adapter_config"]
|
|
270
|
+
|
|
271
|
+
# Check for incomplete configurations
|
|
272
|
+
if adapter_type == "linear":
|
|
273
|
+
if not config.get("api_key"):
|
|
274
|
+
recommendations.append("Add LINEAR_API_KEY to .env file")
|
|
275
|
+
if not config.get("team_id") and not config.get("team_key"):
|
|
276
|
+
recommendations.append("Add LINEAR_TEAM_ID or LINEAR_TEAM_KEY to .env file")
|
|
277
|
+
|
|
278
|
+
elif adapter_type == "github":
|
|
279
|
+
missing = []
|
|
280
|
+
if not config.get("token"):
|
|
281
|
+
missing.append("GITHUB_TOKEN")
|
|
282
|
+
if not config.get("owner"):
|
|
283
|
+
missing.append("GITHUB_OWNER")
|
|
284
|
+
if not config.get("repo"):
|
|
285
|
+
missing.append("GITHUB_REPO")
|
|
286
|
+
if missing:
|
|
287
|
+
recommendations.append(f"Add missing GitHub variables to .env: {', '.join(missing)}")
|
|
288
|
+
|
|
289
|
+
elif adapter_type == "jira":
|
|
290
|
+
missing = []
|
|
291
|
+
if not config.get("server"):
|
|
292
|
+
missing.append("JIRA_SERVER")
|
|
293
|
+
if not config.get("email"):
|
|
294
|
+
missing.append("JIRA_EMAIL")
|
|
295
|
+
if not config.get("api_token"):
|
|
296
|
+
missing.append("JIRA_API_TOKEN")
|
|
297
|
+
if missing:
|
|
298
|
+
recommendations.append(f"Add missing JIRA variables to .env: {', '.join(missing)}")
|
|
299
|
+
|
|
300
|
+
if recommendations:
|
|
301
|
+
for i, rec in enumerate(recommendations, 1):
|
|
302
|
+
console.print(f"{i}. [yellow]{rec}[/yellow]")
|
|
303
|
+
else:
|
|
304
|
+
console.print("[green]✅ Configuration looks good![/green]")
|
|
305
|
+
|
|
306
|
+
# Show .env file examples
|
|
307
|
+
console.print("\n[bold].env File Examples:[/bold]")
|
|
308
|
+
console.print("• Linear: [cyan]echo 'MCP_TICKETER_ADAPTER=linear\\nLINEAR_API_KEY=your_key\\nLINEAR_TEAM_ID=your_team' > .env.local[/cyan]")
|
|
309
|
+
console.print("• GitHub: [cyan]echo 'MCP_TICKETER_ADAPTER=github\\nGITHUB_TOKEN=your_token\\nGITHUB_OWNER=user\\nGITHUB_REPO=repo' > .env.local[/cyan]")
|
|
310
|
+
|
|
311
|
+
console.print("\n[bold]Quick Setup Commands:[/bold]")
|
|
312
|
+
console.print("• For Linear: [cyan]mcp-ticketer init linear[/cyan]")
|
|
313
|
+
console.print("• For GitHub: [cyan]mcp-ticketer init github[/cyan]")
|
|
314
|
+
console.print("• For JIRA: [cyan]mcp-ticketer init jira[/cyan]")
|
|
315
|
+
console.print("• For local files: [cyan]mcp-ticketer init aitrackdown[/cyan]")
|
|
316
|
+
|
|
317
|
+
console.print("\n[bold]Test Configuration:[/bold]")
|
|
318
|
+
console.print("• Run diagnostics: [cyan]mcp-ticketer diagnose[/cyan]")
|
|
319
|
+
console.print("• Test ticket creation: [cyan]mcp-ticketer create 'Test ticket'[/cyan]")
|
|
320
|
+
console.print("• List tickets: [cyan]mcp-ticketer list[/cyan]")
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def get_adapter_status() -> Dict[str, Any]:
|
|
324
|
+
"""Get current adapter status for programmatic use.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Dictionary with adapter status information
|
|
328
|
+
"""
|
|
329
|
+
status = {
|
|
330
|
+
"adapter_type": None,
|
|
331
|
+
"configuration_source": None,
|
|
332
|
+
"credentials_valid": False,
|
|
333
|
+
"error_message": None,
|
|
334
|
+
"recommendations": []
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
try:
|
|
338
|
+
# Check .env files first
|
|
339
|
+
from ..mcp.server import _load_env_configuration
|
|
340
|
+
env_config = _load_env_configuration()
|
|
341
|
+
|
|
342
|
+
if env_config:
|
|
343
|
+
adapter_type = env_config["adapter_type"]
|
|
344
|
+
config = env_config["adapter_config"]
|
|
345
|
+
status["configuration_source"] = ".env files"
|
|
346
|
+
else:
|
|
347
|
+
# Try discovery system
|
|
348
|
+
discovered = discover_config(Path.cwd())
|
|
349
|
+
if discovered and discovered.adapters:
|
|
350
|
+
primary = discovered.get_primary_adapter()
|
|
351
|
+
if primary:
|
|
352
|
+
adapter_type = primary.adapter_type
|
|
353
|
+
status["configuration_source"] = primary.found_in
|
|
354
|
+
# Build basic config
|
|
355
|
+
from ..mcp.server import _build_adapter_config_from_env_vars
|
|
356
|
+
config = _build_adapter_config_from_env_vars(adapter_type, {})
|
|
357
|
+
else:
|
|
358
|
+
adapter_type = "aitrackdown"
|
|
359
|
+
config = {"base_path": ".aitrackdown"}
|
|
360
|
+
status["configuration_source"] = "default"
|
|
361
|
+
else:
|
|
362
|
+
adapter_type = "aitrackdown"
|
|
363
|
+
config = {"base_path": ".aitrackdown"}
|
|
364
|
+
status["configuration_source"] = "default"
|
|
365
|
+
|
|
366
|
+
status["adapter_type"] = adapter_type
|
|
367
|
+
|
|
368
|
+
# Test adapter instantiation
|
|
369
|
+
adapter = AdapterRegistry.get_adapter(adapter_type, config)
|
|
370
|
+
|
|
371
|
+
# Test credentials if possible
|
|
372
|
+
if hasattr(adapter, 'validate_credentials'):
|
|
373
|
+
is_valid, error_msg = adapter.validate_credentials()
|
|
374
|
+
status["credentials_valid"] = is_valid
|
|
375
|
+
if not is_valid:
|
|
376
|
+
status["error_message"] = error_msg
|
|
377
|
+
else:
|
|
378
|
+
status["credentials_valid"] = True # Assume valid if no validation method
|
|
379
|
+
|
|
380
|
+
except Exception as e:
|
|
381
|
+
status["error_message"] = str(e)
|
|
382
|
+
status["recommendations"].append("Check .env file configuration and credentials")
|
|
383
|
+
|
|
384
|
+
return status
|
mcp_ticketer/cli/main.py
CHANGED
|
@@ -317,13 +317,161 @@ def get_adapter(
|
|
|
317
317
|
return AdapterRegistry.get_adapter(adapter_type, adapter_config)
|
|
318
318
|
|
|
319
319
|
|
|
320
|
+
def _prompt_for_adapter_selection(console: Console) -> str:
|
|
321
|
+
"""Interactive prompt for adapter selection.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
console: Rich console for output
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Selected adapter type
|
|
328
|
+
"""
|
|
329
|
+
console.print("\n[bold blue]🚀 MCP Ticketer Setup[/bold blue]")
|
|
330
|
+
console.print("Choose which ticket system you want to connect to:\n")
|
|
331
|
+
|
|
332
|
+
# Define adapter options with descriptions
|
|
333
|
+
adapters = [
|
|
334
|
+
{
|
|
335
|
+
"name": "linear",
|
|
336
|
+
"title": "Linear",
|
|
337
|
+
"description": "Modern project management (linear.app)",
|
|
338
|
+
"requirements": "API key and team ID"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"name": "github",
|
|
342
|
+
"title": "GitHub Issues",
|
|
343
|
+
"description": "GitHub repository issues",
|
|
344
|
+
"requirements": "Personal access token, owner, and repo"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"name": "jira",
|
|
348
|
+
"title": "JIRA",
|
|
349
|
+
"description": "Atlassian JIRA project management",
|
|
350
|
+
"requirements": "Server URL, email, and API token"
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"name": "aitrackdown",
|
|
354
|
+
"title": "Local Files (AITrackdown)",
|
|
355
|
+
"description": "Store tickets in local files (no external service)",
|
|
356
|
+
"requirements": "None - works offline"
|
|
357
|
+
}
|
|
358
|
+
]
|
|
359
|
+
|
|
360
|
+
# Display options
|
|
361
|
+
for i, adapter in enumerate(adapters, 1):
|
|
362
|
+
console.print(f"[cyan]{i}.[/cyan] [bold]{adapter['title']}[/bold]")
|
|
363
|
+
console.print(f" {adapter['description']}")
|
|
364
|
+
console.print(f" [dim]Requirements: {adapter['requirements']}[/dim]\n")
|
|
365
|
+
|
|
366
|
+
# Get user selection
|
|
367
|
+
while True:
|
|
368
|
+
try:
|
|
369
|
+
choice = typer.prompt(
|
|
370
|
+
"Select adapter (1-4)",
|
|
371
|
+
type=int,
|
|
372
|
+
default=1
|
|
373
|
+
)
|
|
374
|
+
if 1 <= choice <= len(adapters):
|
|
375
|
+
selected_adapter = adapters[choice - 1]
|
|
376
|
+
console.print(f"\n[green]✓ Selected: {selected_adapter['title']}[/green]")
|
|
377
|
+
return selected_adapter["name"]
|
|
378
|
+
else:
|
|
379
|
+
console.print(f"[red]Please enter a number between 1 and {len(adapters)}[/red]")
|
|
380
|
+
except (ValueError, typer.Abort):
|
|
381
|
+
console.print("[yellow]Setup cancelled.[/yellow]")
|
|
382
|
+
raise typer.Exit(0)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@app.command()
|
|
386
|
+
def setup(
|
|
387
|
+
adapter: Optional[str] = typer.Option(
|
|
388
|
+
None,
|
|
389
|
+
"--adapter",
|
|
390
|
+
"-a",
|
|
391
|
+
help="Adapter type to use (interactive prompt if not specified)",
|
|
392
|
+
),
|
|
393
|
+
project_path: Optional[str] = typer.Option(
|
|
394
|
+
None, "--path", help="Project path (default: current directory)"
|
|
395
|
+
),
|
|
396
|
+
global_config: bool = typer.Option(
|
|
397
|
+
False,
|
|
398
|
+
"--global",
|
|
399
|
+
"-g",
|
|
400
|
+
help="Save to global config instead of project-specific",
|
|
401
|
+
),
|
|
402
|
+
base_path: Optional[str] = typer.Option(
|
|
403
|
+
None,
|
|
404
|
+
"--base-path",
|
|
405
|
+
"-p",
|
|
406
|
+
help="Base path for ticket storage (AITrackdown only)",
|
|
407
|
+
),
|
|
408
|
+
api_key: Optional[str] = typer.Option(
|
|
409
|
+
None, "--api-key", help="API key for Linear or API token for JIRA"
|
|
410
|
+
),
|
|
411
|
+
team_id: Optional[str] = typer.Option(
|
|
412
|
+
None, "--team-id", help="Linear team ID (required for Linear adapter)"
|
|
413
|
+
),
|
|
414
|
+
jira_server: Optional[str] = typer.Option(
|
|
415
|
+
None,
|
|
416
|
+
"--jira-server",
|
|
417
|
+
help="JIRA server URL (e.g., https://company.atlassian.net)",
|
|
418
|
+
),
|
|
419
|
+
jira_email: Optional[str] = typer.Option(
|
|
420
|
+
None, "--jira-email", help="JIRA user email for authentication"
|
|
421
|
+
),
|
|
422
|
+
jira_project: Optional[str] = typer.Option(
|
|
423
|
+
None, "--jira-project", help="Default JIRA project key"
|
|
424
|
+
),
|
|
425
|
+
github_owner: Optional[str] = typer.Option(
|
|
426
|
+
None, "--github-owner", help="GitHub repository owner"
|
|
427
|
+
),
|
|
428
|
+
github_repo: Optional[str] = typer.Option(
|
|
429
|
+
None, "--github-repo", help="GitHub repository name"
|
|
430
|
+
),
|
|
431
|
+
github_token: Optional[str] = typer.Option(
|
|
432
|
+
None, "--github-token", help="GitHub Personal Access Token"
|
|
433
|
+
),
|
|
434
|
+
) -> None:
|
|
435
|
+
"""Interactive setup wizard for MCP Ticketer (alias for init).
|
|
436
|
+
|
|
437
|
+
This command provides a user-friendly setup experience with prompts
|
|
438
|
+
to guide you through configuring MCP Ticketer for your preferred
|
|
439
|
+
ticket management system. It's identical to 'init' and 'install'.
|
|
440
|
+
|
|
441
|
+
Examples:
|
|
442
|
+
# Run interactive setup
|
|
443
|
+
mcp-ticketer setup
|
|
444
|
+
|
|
445
|
+
# Setup with specific adapter
|
|
446
|
+
mcp-ticketer setup --adapter linear
|
|
447
|
+
|
|
448
|
+
# Setup for different project
|
|
449
|
+
mcp-ticketer setup --path /path/to/project
|
|
450
|
+
"""
|
|
451
|
+
# Call init with all parameters
|
|
452
|
+
init(
|
|
453
|
+
adapter=adapter,
|
|
454
|
+
project_path=project_path,
|
|
455
|
+
global_config=global_config,
|
|
456
|
+
base_path=base_path,
|
|
457
|
+
api_key=api_key,
|
|
458
|
+
team_id=team_id,
|
|
459
|
+
jira_server=jira_server,
|
|
460
|
+
jira_email=jira_email,
|
|
461
|
+
jira_project=jira_project,
|
|
462
|
+
github_owner=github_owner,
|
|
463
|
+
github_repo=github_repo,
|
|
464
|
+
github_token=github_token,
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
|
|
320
468
|
@app.command()
|
|
321
469
|
def init(
|
|
322
470
|
adapter: Optional[str] = typer.Option(
|
|
323
471
|
None,
|
|
324
472
|
"--adapter",
|
|
325
473
|
"-a",
|
|
326
|
-
help="Adapter type to use (
|
|
474
|
+
help="Adapter type to use (interactive prompt if not specified)",
|
|
327
475
|
),
|
|
328
476
|
project_path: Optional[str] = typer.Option(
|
|
329
477
|
None, "--path", help="Project path (default: current directory)"
|
|
@@ -369,11 +517,17 @@ def init(
|
|
|
369
517
|
) -> None:
|
|
370
518
|
"""Initialize mcp-ticketer for the current project.
|
|
371
519
|
|
|
520
|
+
This command sets up MCP Ticketer configuration with interactive prompts
|
|
521
|
+
to guide you through the process. It auto-detects adapter configuration
|
|
522
|
+
from .env files or prompts for interactive setup if no configuration is found.
|
|
523
|
+
|
|
372
524
|
Creates .mcp-ticketer/config.json in the current directory with
|
|
373
525
|
auto-detected or specified adapter configuration.
|
|
374
526
|
|
|
527
|
+
Note: 'setup' and 'install' are synonyms for this command.
|
|
528
|
+
|
|
375
529
|
Examples:
|
|
376
|
-
#
|
|
530
|
+
# Interactive setup (same as 'setup' and 'install')
|
|
377
531
|
mcp-ticketer init
|
|
378
532
|
|
|
379
533
|
# Force specific adapter
|
|
@@ -414,32 +568,60 @@ def init(
|
|
|
414
568
|
console.print(
|
|
415
569
|
"[cyan]🔍 Auto-discovering configuration from .env files...[/cyan]"
|
|
416
570
|
)
|
|
417
|
-
discovered = discover_config(proj_path)
|
|
418
|
-
|
|
419
|
-
if discovered and discovered.adapters:
|
|
420
|
-
primary = discovered.get_primary_adapter()
|
|
421
|
-
if primary:
|
|
422
|
-
adapter_type = primary.adapter_type
|
|
423
|
-
console.print(
|
|
424
|
-
f"[green]✓ Detected {adapter_type} adapter from environment files[/green]"
|
|
425
|
-
)
|
|
426
571
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
adapter_type = "aitrackdown" # Fallback
|
|
434
|
-
console.print(
|
|
435
|
-
"[yellow]⚠ No credentials found, defaulting to aitrackdown[/yellow]"
|
|
436
|
-
)
|
|
437
|
-
else:
|
|
438
|
-
adapter_type = "aitrackdown" # Fallback
|
|
572
|
+
# First try our improved .env configuration loader
|
|
573
|
+
from ..mcp.server import _load_env_configuration
|
|
574
|
+
env_config = _load_env_configuration()
|
|
575
|
+
|
|
576
|
+
if env_config:
|
|
577
|
+
adapter_type = env_config["adapter_type"]
|
|
439
578
|
console.print(
|
|
440
|
-
"[
|
|
579
|
+
f"[green]✓ Detected {adapter_type} adapter from environment files[/green]"
|
|
441
580
|
)
|
|
442
581
|
|
|
582
|
+
# Show what was discovered
|
|
583
|
+
console.print(f"\n[dim]Configuration found in: .env files[/dim]")
|
|
584
|
+
console.print(f"[dim]Confidence: 100%[/dim]")
|
|
585
|
+
|
|
586
|
+
# Ask user to confirm auto-detected adapter
|
|
587
|
+
if not typer.confirm(
|
|
588
|
+
f"Use detected {adapter_type} adapter?",
|
|
589
|
+
default=True,
|
|
590
|
+
):
|
|
591
|
+
adapter_type = None # Will trigger interactive selection
|
|
592
|
+
else:
|
|
593
|
+
# Fallback to old discovery system for backward compatibility
|
|
594
|
+
discovered = discover_config(proj_path)
|
|
595
|
+
|
|
596
|
+
if discovered and discovered.adapters:
|
|
597
|
+
primary = discovered.get_primary_adapter()
|
|
598
|
+
if primary:
|
|
599
|
+
adapter_type = primary.adapter_type
|
|
600
|
+
console.print(
|
|
601
|
+
f"[green]✓ Detected {adapter_type} adapter from environment files[/green]"
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
# Show what was discovered
|
|
605
|
+
console.print(
|
|
606
|
+
f"\n[dim]Configuration found in: {primary.found_in}[/dim]"
|
|
607
|
+
)
|
|
608
|
+
console.print(f"[dim]Confidence: {primary.confidence:.0%}[/dim]")
|
|
609
|
+
|
|
610
|
+
# Ask user to confirm auto-detected adapter
|
|
611
|
+
if not typer.confirm(
|
|
612
|
+
f"Use detected {adapter_type} adapter?",
|
|
613
|
+
default=True,
|
|
614
|
+
):
|
|
615
|
+
adapter_type = None # Will trigger interactive selection
|
|
616
|
+
else:
|
|
617
|
+
adapter_type = None # Will trigger interactive selection
|
|
618
|
+
else:
|
|
619
|
+
adapter_type = None # Will trigger interactive selection
|
|
620
|
+
|
|
621
|
+
# If no adapter determined, show interactive selection
|
|
622
|
+
if not adapter_type:
|
|
623
|
+
adapter_type = _prompt_for_adapter_selection(console)
|
|
624
|
+
|
|
443
625
|
# 2. Create configuration based on adapter type
|
|
444
626
|
config = {"default_adapter": adapter_type, "adapters": {}}
|
|
445
627
|
|
|
@@ -462,59 +644,99 @@ def init(
|
|
|
462
644
|
}
|
|
463
645
|
|
|
464
646
|
elif adapter_type == "linear":
|
|
465
|
-
# If not auto-discovered, build from CLI params
|
|
647
|
+
# If not auto-discovered, build from CLI params or prompt
|
|
466
648
|
if adapter_type not in config["adapters"]:
|
|
467
649
|
linear_config = {}
|
|
468
650
|
|
|
469
|
-
# Team ID
|
|
470
|
-
if team_id:
|
|
471
|
-
linear_config["team_id"] = team_id
|
|
472
|
-
|
|
473
651
|
# API Key
|
|
474
652
|
linear_api_key = api_key or os.getenv("LINEAR_API_KEY")
|
|
653
|
+
if not linear_api_key and not discovered:
|
|
654
|
+
console.print("\n[bold]Linear Configuration[/bold]")
|
|
655
|
+
console.print("You need a Linear API key to connect to Linear.")
|
|
656
|
+
console.print("[dim]Get your API key at: https://linear.app/settings/api[/dim]\n")
|
|
657
|
+
|
|
658
|
+
linear_api_key = typer.prompt(
|
|
659
|
+
"Enter your Linear API key",
|
|
660
|
+
hide_input=True
|
|
661
|
+
)
|
|
662
|
+
|
|
475
663
|
if linear_api_key:
|
|
476
664
|
linear_config["api_key"] = linear_api_key
|
|
477
|
-
elif not discovered:
|
|
478
|
-
console.print("[yellow]Warning:[/yellow] No Linear API key provided.")
|
|
479
|
-
console.print(
|
|
480
|
-
"Set LINEAR_API_KEY environment variable or use --api-key option"
|
|
481
|
-
)
|
|
482
665
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
666
|
+
# Team ID
|
|
667
|
+
linear_team_id = team_id or os.getenv("LINEAR_TEAM_ID")
|
|
668
|
+
if not linear_team_id and not discovered:
|
|
669
|
+
console.print("\nYou need your Linear team ID.")
|
|
670
|
+
console.print("[dim]Find it in Linear settings or team URL[/dim]\n")
|
|
671
|
+
|
|
672
|
+
linear_team_id = typer.prompt("Enter your Linear team ID")
|
|
673
|
+
|
|
674
|
+
if linear_team_id:
|
|
675
|
+
linear_config["team_id"] = linear_team_id
|
|
676
|
+
|
|
677
|
+
if not linear_config.get("api_key") or not linear_config.get("team_id"):
|
|
678
|
+
console.print("[red]Error:[/red] Linear requires both API key and team ID")
|
|
679
|
+
console.print("Run 'mcp-ticketer init --adapter linear' with proper credentials")
|
|
680
|
+
raise typer.Exit(1)
|
|
681
|
+
|
|
682
|
+
linear_config["type"] = "linear"
|
|
683
|
+
config["adapters"]["linear"] = linear_config
|
|
486
684
|
|
|
487
685
|
elif adapter_type == "jira":
|
|
488
|
-
# If not auto-discovered, build from CLI params
|
|
686
|
+
# If not auto-discovered, build from CLI params or prompt
|
|
489
687
|
if adapter_type not in config["adapters"]:
|
|
490
688
|
server = jira_server or os.getenv("JIRA_SERVER")
|
|
491
689
|
email = jira_email or os.getenv("JIRA_EMAIL")
|
|
492
690
|
token = api_key or os.getenv("JIRA_API_TOKEN")
|
|
493
691
|
project = jira_project or os.getenv("JIRA_PROJECT_KEY")
|
|
494
692
|
|
|
693
|
+
# Interactive prompts for missing values
|
|
694
|
+
if not server and not discovered:
|
|
695
|
+
console.print("\n[bold]JIRA Configuration[/bold]")
|
|
696
|
+
console.print("Enter your JIRA server details.\n")
|
|
697
|
+
|
|
698
|
+
server = typer.prompt(
|
|
699
|
+
"JIRA server URL (e.g., https://company.atlassian.net)"
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
if not email and not discovered:
|
|
703
|
+
email = typer.prompt("Your JIRA email address")
|
|
704
|
+
|
|
705
|
+
if not token and not discovered:
|
|
706
|
+
console.print("\nYou need a JIRA API token.")
|
|
707
|
+
console.print("[dim]Generate one at: https://id.atlassian.com/manage/api-tokens[/dim]\n")
|
|
708
|
+
|
|
709
|
+
token = typer.prompt(
|
|
710
|
+
"Enter your JIRA API token",
|
|
711
|
+
hide_input=True
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
if not project and not discovered:
|
|
715
|
+
project = typer.prompt(
|
|
716
|
+
"Default JIRA project key (optional, press Enter to skip)",
|
|
717
|
+
default="",
|
|
718
|
+
show_default=False
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# Validate required fields
|
|
495
722
|
if not server:
|
|
496
723
|
console.print("[red]Error:[/red] JIRA server URL is required")
|
|
497
|
-
console.print(
|
|
498
|
-
"Use --jira-server or set JIRA_SERVER environment variable"
|
|
499
|
-
)
|
|
500
724
|
raise typer.Exit(1)
|
|
501
725
|
|
|
502
726
|
if not email:
|
|
503
727
|
console.print("[red]Error:[/red] JIRA email is required")
|
|
504
|
-
console.print("Use --jira-email or set JIRA_EMAIL environment variable")
|
|
505
728
|
raise typer.Exit(1)
|
|
506
729
|
|
|
507
730
|
if not token:
|
|
508
731
|
console.print("[red]Error:[/red] JIRA API token is required")
|
|
509
|
-
console.print(
|
|
510
|
-
"Use --api-key or set JIRA_API_TOKEN environment variable"
|
|
511
|
-
)
|
|
512
|
-
console.print(
|
|
513
|
-
"[dim]Generate token at: https://id.atlassian.com/manage/api-tokens[/dim]"
|
|
514
|
-
)
|
|
515
732
|
raise typer.Exit(1)
|
|
516
733
|
|
|
517
|
-
jira_config = {
|
|
734
|
+
jira_config = {
|
|
735
|
+
"server": server,
|
|
736
|
+
"email": email,
|
|
737
|
+
"api_token": token,
|
|
738
|
+
"type": "jira"
|
|
739
|
+
}
|
|
518
740
|
|
|
519
741
|
if project:
|
|
520
742
|
jira_config["project_key"] = project
|
|
@@ -522,45 +744,50 @@ def init(
|
|
|
522
744
|
config["adapters"]["jira"] = jira_config
|
|
523
745
|
|
|
524
746
|
elif adapter_type == "github":
|
|
525
|
-
# If not auto-discovered, build from CLI params
|
|
747
|
+
# If not auto-discovered, build from CLI params or prompt
|
|
526
748
|
if adapter_type not in config["adapters"]:
|
|
527
749
|
owner = github_owner or os.getenv("GITHUB_OWNER")
|
|
528
750
|
repo = github_repo or os.getenv("GITHUB_REPO")
|
|
529
751
|
token = github_token or os.getenv("GITHUB_TOKEN")
|
|
530
752
|
|
|
753
|
+
# Interactive prompts for missing values
|
|
754
|
+
if not owner and not discovered:
|
|
755
|
+
console.print("\n[bold]GitHub Configuration[/bold]")
|
|
756
|
+
console.print("Enter your GitHub repository details.\n")
|
|
757
|
+
|
|
758
|
+
owner = typer.prompt("GitHub repository owner (username or organization)")
|
|
759
|
+
|
|
760
|
+
if not repo and not discovered:
|
|
761
|
+
repo = typer.prompt("GitHub repository name")
|
|
762
|
+
|
|
763
|
+
if not token and not discovered:
|
|
764
|
+
console.print("\nYou need a GitHub Personal Access Token.")
|
|
765
|
+
console.print("[dim]Create one at: https://github.com/settings/tokens/new[/dim]")
|
|
766
|
+
console.print("[dim]Required scopes: repo (for private repos) or public_repo (for public repos)[/dim]\n")
|
|
767
|
+
|
|
768
|
+
token = typer.prompt(
|
|
769
|
+
"Enter your GitHub Personal Access Token",
|
|
770
|
+
hide_input=True
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
# Validate required fields
|
|
531
774
|
if not owner:
|
|
532
775
|
console.print("[red]Error:[/red] GitHub repository owner is required")
|
|
533
|
-
console.print(
|
|
534
|
-
"Use --github-owner or set GITHUB_OWNER environment variable"
|
|
535
|
-
)
|
|
536
776
|
raise typer.Exit(1)
|
|
537
777
|
|
|
538
778
|
if not repo:
|
|
539
779
|
console.print("[red]Error:[/red] GitHub repository name is required")
|
|
540
|
-
console.print(
|
|
541
|
-
"Use --github-repo or set GITHUB_REPO environment variable"
|
|
542
|
-
)
|
|
543
780
|
raise typer.Exit(1)
|
|
544
781
|
|
|
545
782
|
if not token:
|
|
546
|
-
console.print(
|
|
547
|
-
"[red]Error:[/red] GitHub Personal Access Token is required"
|
|
548
|
-
)
|
|
549
|
-
console.print(
|
|
550
|
-
"Use --github-token or set GITHUB_TOKEN environment variable"
|
|
551
|
-
)
|
|
552
|
-
console.print(
|
|
553
|
-
"[dim]Create token at: https://github.com/settings/tokens/new[/dim]"
|
|
554
|
-
)
|
|
555
|
-
console.print(
|
|
556
|
-
"[dim]Required scopes: repo (for private repos) or public_repo (for public repos)[/dim]"
|
|
557
|
-
)
|
|
783
|
+
console.print("[red]Error:[/red] GitHub Personal Access Token is required")
|
|
558
784
|
raise typer.Exit(1)
|
|
559
785
|
|
|
560
786
|
config["adapters"]["github"] = {
|
|
561
787
|
"owner": owner,
|
|
562
788
|
"repo": repo,
|
|
563
789
|
"token": token,
|
|
790
|
+
"type": "github"
|
|
564
791
|
}
|
|
565
792
|
|
|
566
793
|
# 5. Save to appropriate location
|
|
@@ -600,6 +827,48 @@ def init(
|
|
|
600
827
|
f.write("# MCP Ticketer\n.mcp-ticketer/\n")
|
|
601
828
|
console.print("[dim]✓ Created .gitignore with .mcp-ticketer/[/dim]")
|
|
602
829
|
|
|
830
|
+
# Show next steps
|
|
831
|
+
_show_next_steps(console, adapter_type, config_file_path)
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def _show_next_steps(console: Console, adapter_type: str, config_file_path: Path) -> None:
|
|
835
|
+
"""Show helpful next steps after initialization.
|
|
836
|
+
|
|
837
|
+
Args:
|
|
838
|
+
console: Rich console for output
|
|
839
|
+
adapter_type: Type of adapter that was configured
|
|
840
|
+
config_file_path: Path to the configuration file
|
|
841
|
+
"""
|
|
842
|
+
console.print("\n[bold green]🎉 Setup Complete![/bold green]")
|
|
843
|
+
console.print(f"MCP Ticketer is now configured to use {adapter_type.title()}.\n")
|
|
844
|
+
|
|
845
|
+
console.print("[bold]Next Steps:[/bold]")
|
|
846
|
+
console.print("1. [cyan]Test your configuration:[/cyan]")
|
|
847
|
+
console.print(" mcp-ticketer diagnose")
|
|
848
|
+
console.print("\n2. [cyan]Create a test ticket:[/cyan]")
|
|
849
|
+
console.print(" mcp-ticketer create 'Test ticket from MCP Ticketer'")
|
|
850
|
+
|
|
851
|
+
if adapter_type != "aitrackdown":
|
|
852
|
+
console.print(f"\n3. [cyan]Verify the ticket appears in {adapter_type.title()}[/cyan]")
|
|
853
|
+
|
|
854
|
+
if adapter_type == "linear":
|
|
855
|
+
console.print(" Check your Linear workspace for the new ticket")
|
|
856
|
+
elif adapter_type == "github":
|
|
857
|
+
console.print(" Check your GitHub repository's Issues tab")
|
|
858
|
+
elif adapter_type == "jira":
|
|
859
|
+
console.print(" Check your JIRA project for the new ticket")
|
|
860
|
+
else:
|
|
861
|
+
console.print("\n3. [cyan]Check local ticket storage:[/cyan]")
|
|
862
|
+
console.print(" ls .aitrackdown/")
|
|
863
|
+
|
|
864
|
+
console.print("\n4. [cyan]Configure MCP clients (optional):[/cyan]")
|
|
865
|
+
console.print(" mcp-ticketer mcp claude # For Claude Code")
|
|
866
|
+
console.print(" mcp-ticketer mcp auggie # For Auggie")
|
|
867
|
+
console.print(" mcp-ticketer mcp gemini # For Gemini CLI")
|
|
868
|
+
|
|
869
|
+
console.print(f"\n[dim]Configuration saved to: {config_file_path}[/dim]")
|
|
870
|
+
console.print("[dim]Run 'mcp-ticketer --help' for more commands[/dim]")
|
|
871
|
+
|
|
603
872
|
|
|
604
873
|
@app.command()
|
|
605
874
|
def install(
|
|
@@ -653,12 +922,12 @@ def install(
|
|
|
653
922
|
) -> None:
|
|
654
923
|
"""Initialize mcp-ticketer for the current project (alias for init).
|
|
655
924
|
|
|
656
|
-
This command is synonymous with 'init' and
|
|
657
|
-
|
|
658
|
-
|
|
925
|
+
This command is synonymous with 'init' and 'setup' - all three provide
|
|
926
|
+
identical functionality with interactive prompts to guide you through
|
|
927
|
+
configuring MCP Ticketer for your preferred ticket management system.
|
|
659
928
|
|
|
660
929
|
Examples:
|
|
661
|
-
#
|
|
930
|
+
# Interactive setup (same as 'init' and 'setup')
|
|
662
931
|
mcp-ticketer install
|
|
663
932
|
|
|
664
933
|
# Force specific adapter
|
|
@@ -1548,6 +1817,9 @@ def check(queue_id: str = typer.Argument(..., help="Queue ID to check")):
|
|
|
1548
1817
|
console.print(f"\nRetry Count: {item.retry_count}")
|
|
1549
1818
|
|
|
1550
1819
|
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
|
|
1551
1823
|
@app.command()
|
|
1552
1824
|
def serve(
|
|
1553
1825
|
adapter: Optional[AdapterType] = typer.Option(
|
|
@@ -1575,16 +1847,27 @@ def serve(
|
|
|
1575
1847
|
# Load configuration (respects project-specific config in cwd)
|
|
1576
1848
|
config = load_config()
|
|
1577
1849
|
|
|
1578
|
-
# Determine adapter type
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1850
|
+
# Determine adapter type with priority: CLI arg > .env files > config > default
|
|
1851
|
+
if adapter:
|
|
1852
|
+
# Priority 1: Command line argument
|
|
1853
|
+
adapter_type = adapter.value
|
|
1854
|
+
# Get base config from config file
|
|
1855
|
+
adapters_config = config.get("adapters", {})
|
|
1856
|
+
adapter_config = adapters_config.get(adapter_type, {})
|
|
1857
|
+
else:
|
|
1858
|
+
# Priority 2: .env files
|
|
1859
|
+
from ..mcp.server import _load_env_configuration
|
|
1860
|
+
env_config = _load_env_configuration()
|
|
1861
|
+
if env_config:
|
|
1862
|
+
adapter_type = env_config["adapter_type"]
|
|
1863
|
+
adapter_config = env_config["adapter_config"]
|
|
1864
|
+
else:
|
|
1865
|
+
# Priority 3: Configuration file
|
|
1866
|
+
adapter_type = config.get("default_adapter", "aitrackdown")
|
|
1867
|
+
adapters_config = config.get("adapters", {})
|
|
1868
|
+
adapter_config = adapters_config.get(adapter_type, {})
|
|
1586
1869
|
|
|
1587
|
-
# Override with command line options if provided
|
|
1870
|
+
# Override with command line options if provided (highest priority)
|
|
1588
1871
|
if base_path and adapter_type == "aitrackdown":
|
|
1589
1872
|
adapter_config["base_path"] = base_path
|
|
1590
1873
|
|
|
@@ -454,11 +454,32 @@ class EnvDiscovery:
|
|
|
454
454
|
"""
|
|
455
455
|
base_path = self._find_key_value(env_vars, AITRACKDOWN_PATH_PATTERNS)
|
|
456
456
|
|
|
457
|
-
#
|
|
457
|
+
# Check for explicit MCP_TICKETER_ADAPTER setting
|
|
458
|
+
explicit_adapter = env_vars.get("MCP_TICKETER_ADAPTER")
|
|
459
|
+
if explicit_adapter and explicit_adapter != "aitrackdown":
|
|
460
|
+
# If another adapter is explicitly set, don't detect aitrackdown
|
|
461
|
+
return None
|
|
462
|
+
|
|
463
|
+
# Check if .aitrackdown directory exists
|
|
458
464
|
aitrackdown_dir = self.project_path / ".aitrackdown"
|
|
465
|
+
|
|
466
|
+
# Only detect aitrackdown if:
|
|
467
|
+
# 1. There's an explicit base_path setting, OR
|
|
468
|
+
# 2. There's a .aitrackdown directory AND no other adapter variables are present
|
|
469
|
+
has_other_adapter_vars = (
|
|
470
|
+
any(key.startswith("LINEAR_") for key in env_vars) or
|
|
471
|
+
any(key.startswith("GITHUB_") for key in env_vars) or
|
|
472
|
+
any(key.startswith("JIRA_") for key in env_vars)
|
|
473
|
+
)
|
|
474
|
+
|
|
459
475
|
if not base_path and not aitrackdown_dir.exists():
|
|
460
476
|
return None
|
|
461
477
|
|
|
478
|
+
if not base_path and has_other_adapter_vars:
|
|
479
|
+
# Don't detect aitrackdown if other adapter variables are present
|
|
480
|
+
# unless explicitly configured
|
|
481
|
+
return None
|
|
482
|
+
|
|
462
483
|
config: dict[str, Any] = {
|
|
463
484
|
"adapter": AdapterType.AITRACKDOWN.value,
|
|
464
485
|
}
|
|
@@ -468,8 +489,15 @@ class EnvDiscovery:
|
|
|
468
489
|
else:
|
|
469
490
|
config["base_path"] = ".aitrackdown"
|
|
470
491
|
|
|
471
|
-
#
|
|
472
|
-
|
|
492
|
+
# Lower confidence when other adapter variables are present
|
|
493
|
+
if has_other_adapter_vars:
|
|
494
|
+
confidence = 0.3 # Low confidence when other adapters are configured
|
|
495
|
+
elif base_path:
|
|
496
|
+
confidence = 1.0 # High confidence when explicitly configured
|
|
497
|
+
elif aitrackdown_dir.exists():
|
|
498
|
+
confidence = 0.8 # Medium confidence when directory exists
|
|
499
|
+
else:
|
|
500
|
+
confidence = 0.5 # Low confidence as fallback
|
|
473
501
|
|
|
474
502
|
return DiscoveredAdapter(
|
|
475
503
|
adapter_type=AdapterType.AITRACKDOWN.value,
|
mcp_ticketer/mcp/server.py
CHANGED
|
@@ -1652,56 +1652,191 @@ async def main():
|
|
|
1652
1652
|
# Load configuration
|
|
1653
1653
|
import json
|
|
1654
1654
|
import logging
|
|
1655
|
+
import os
|
|
1655
1656
|
from pathlib import Path
|
|
1656
1657
|
|
|
1657
1658
|
logger = logging.getLogger(__name__)
|
|
1658
1659
|
|
|
1659
|
-
#
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
#
|
|
1675
|
-
|
|
1660
|
+
# Initialize defaults
|
|
1661
|
+
adapter_type = "aitrackdown"
|
|
1662
|
+
adapter_config = {"base_path": ".aitrackdown"}
|
|
1663
|
+
|
|
1664
|
+
# Priority 1: Check .env files (highest priority for MCP)
|
|
1665
|
+
env_config = _load_env_configuration()
|
|
1666
|
+
if env_config and env_config.get("adapter_type"):
|
|
1667
|
+
adapter_type = env_config["adapter_type"]
|
|
1668
|
+
adapter_config = env_config["adapter_config"]
|
|
1669
|
+
logger.info(f"Using adapter from .env files: {adapter_type}")
|
|
1670
|
+
logger.info(f"Built adapter config from .env: {list(adapter_config.keys())}")
|
|
1671
|
+
else:
|
|
1672
|
+
# Priority 2: Check project-local config file
|
|
1673
|
+
config_file = Path.cwd() / ".mcp-ticketer" / "config.json"
|
|
1674
|
+
if config_file.exists():
|
|
1675
|
+
# Validate config is within project
|
|
1676
|
+
try:
|
|
1677
|
+
if not config_file.resolve().is_relative_to(Path.cwd().resolve()):
|
|
1678
|
+
logger.error(
|
|
1679
|
+
f"Security violation: Config file {config_file} "
|
|
1680
|
+
"is not within project directory"
|
|
1681
|
+
)
|
|
1682
|
+
raise ValueError(
|
|
1683
|
+
f"Security violation: Config file {config_file} "
|
|
1684
|
+
"is not within project directory"
|
|
1685
|
+
)
|
|
1686
|
+
except (ValueError, RuntimeError):
|
|
1687
|
+
# is_relative_to may raise ValueError in some cases
|
|
1688
|
+
pass
|
|
1676
1689
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1690
|
+
try:
|
|
1691
|
+
with open(config_file) as f:
|
|
1692
|
+
config = json.load(f)
|
|
1693
|
+
adapter_type = config.get("default_adapter", "aitrackdown")
|
|
1694
|
+
# Get adapter-specific config
|
|
1695
|
+
adapters_config = config.get("adapters", {})
|
|
1696
|
+
adapter_config = adapters_config.get(adapter_type, {})
|
|
1697
|
+
# Fallback to legacy config format
|
|
1698
|
+
if not adapter_config and "config" in config:
|
|
1699
|
+
adapter_config = config["config"]
|
|
1700
|
+
logger.info(
|
|
1701
|
+
f"Loaded MCP configuration from project-local: {config_file}"
|
|
1702
|
+
)
|
|
1703
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
1704
|
+
logger.warning(f"Could not load project config: {e}, using defaults")
|
|
1705
|
+
adapter_type = "aitrackdown"
|
|
1706
|
+
adapter_config = {"base_path": ".aitrackdown"}
|
|
1707
|
+
else:
|
|
1708
|
+
# Priority 3: Default to aitrackdown
|
|
1709
|
+
logger.info("No configuration found, defaulting to aitrackdown adapter")
|
|
1692
1710
|
adapter_type = "aitrackdown"
|
|
1693
1711
|
adapter_config = {"base_path": ".aitrackdown"}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
adapter_config = {"base_path": ".aitrackdown"}
|
|
1712
|
+
|
|
1713
|
+
# Log final configuration for debugging
|
|
1714
|
+
logger.info(f"Starting MCP server with adapter: {adapter_type}")
|
|
1715
|
+
logger.debug(f"Adapter config keys: {list(adapter_config.keys())}")
|
|
1699
1716
|
|
|
1700
1717
|
# Create and run server
|
|
1701
1718
|
server = MCPTicketServer(adapter_type, adapter_config)
|
|
1702
1719
|
await server.run()
|
|
1703
1720
|
|
|
1704
1721
|
|
|
1722
|
+
def _load_env_configuration() -> Optional[dict[str, Any]]:
|
|
1723
|
+
"""Load adapter configuration from .env files.
|
|
1724
|
+
|
|
1725
|
+
Checks .env.local first (highest priority), then .env.
|
|
1726
|
+
|
|
1727
|
+
Returns:
|
|
1728
|
+
Dictionary with 'adapter_type' and 'adapter_config' keys, or None if no config found
|
|
1729
|
+
"""
|
|
1730
|
+
from pathlib import Path
|
|
1731
|
+
|
|
1732
|
+
# Check for .env files in order of preference
|
|
1733
|
+
env_files = [".env.local", ".env"]
|
|
1734
|
+
env_vars = {}
|
|
1735
|
+
|
|
1736
|
+
for env_file in env_files:
|
|
1737
|
+
env_path = Path.cwd() / env_file
|
|
1738
|
+
if env_path.exists():
|
|
1739
|
+
try:
|
|
1740
|
+
# Parse .env file manually to avoid external dependencies
|
|
1741
|
+
with open(env_path, 'r') as f:
|
|
1742
|
+
for line in f:
|
|
1743
|
+
line = line.strip()
|
|
1744
|
+
if line and not line.startswith('#') and '=' in line:
|
|
1745
|
+
key, value = line.split('=', 1)
|
|
1746
|
+
key = key.strip()
|
|
1747
|
+
value = value.strip().strip('"').strip("'")
|
|
1748
|
+
if value: # Only add non-empty values
|
|
1749
|
+
env_vars[key] = value
|
|
1750
|
+
except Exception:
|
|
1751
|
+
continue
|
|
1752
|
+
|
|
1753
|
+
if not env_vars:
|
|
1754
|
+
return None
|
|
1755
|
+
|
|
1756
|
+
# Determine adapter type and build config
|
|
1757
|
+
adapter_type = env_vars.get("MCP_TICKETER_ADAPTER")
|
|
1758
|
+
if not adapter_type:
|
|
1759
|
+
# Auto-detect based on available keys
|
|
1760
|
+
if any(key.startswith("LINEAR_") for key in env_vars):
|
|
1761
|
+
adapter_type = "linear"
|
|
1762
|
+
elif any(key.startswith("GITHUB_") for key in env_vars):
|
|
1763
|
+
adapter_type = "github"
|
|
1764
|
+
elif any(key.startswith("JIRA_") for key in env_vars):
|
|
1765
|
+
adapter_type = "jira"
|
|
1766
|
+
else:
|
|
1767
|
+
return None
|
|
1768
|
+
|
|
1769
|
+
# Build adapter-specific configuration
|
|
1770
|
+
adapter_config = _build_adapter_config_from_env_vars(adapter_type, env_vars)
|
|
1771
|
+
|
|
1772
|
+
if not adapter_config:
|
|
1773
|
+
return None
|
|
1774
|
+
|
|
1775
|
+
return {
|
|
1776
|
+
"adapter_type": adapter_type,
|
|
1777
|
+
"adapter_config": adapter_config
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
|
|
1781
|
+
def _build_adapter_config_from_env_vars(adapter_type: str, env_vars: dict[str, str]) -> dict[str, Any]:
|
|
1782
|
+
"""Build adapter configuration from parsed environment variables.
|
|
1783
|
+
|
|
1784
|
+
Args:
|
|
1785
|
+
adapter_type: Type of adapter to configure
|
|
1786
|
+
env_vars: Dictionary of environment variables from .env files
|
|
1787
|
+
|
|
1788
|
+
Returns:
|
|
1789
|
+
Dictionary of adapter configuration
|
|
1790
|
+
"""
|
|
1791
|
+
config = {}
|
|
1792
|
+
|
|
1793
|
+
if adapter_type == "linear":
|
|
1794
|
+
# Linear adapter configuration
|
|
1795
|
+
if env_vars.get("LINEAR_API_KEY"):
|
|
1796
|
+
config["api_key"] = env_vars["LINEAR_API_KEY"]
|
|
1797
|
+
if env_vars.get("LINEAR_TEAM_ID"):
|
|
1798
|
+
config["team_id"] = env_vars["LINEAR_TEAM_ID"]
|
|
1799
|
+
if env_vars.get("LINEAR_TEAM_KEY"):
|
|
1800
|
+
config["team_key"] = env_vars["LINEAR_TEAM_KEY"]
|
|
1801
|
+
if env_vars.get("LINEAR_API_URL"):
|
|
1802
|
+
config["api_url"] = env_vars["LINEAR_API_URL"]
|
|
1803
|
+
|
|
1804
|
+
elif adapter_type == "github":
|
|
1805
|
+
# GitHub adapter configuration
|
|
1806
|
+
if env_vars.get("GITHUB_TOKEN"):
|
|
1807
|
+
config["token"] = env_vars["GITHUB_TOKEN"]
|
|
1808
|
+
if env_vars.get("GITHUB_OWNER"):
|
|
1809
|
+
config["owner"] = env_vars["GITHUB_OWNER"]
|
|
1810
|
+
if env_vars.get("GITHUB_REPO"):
|
|
1811
|
+
config["repo"] = env_vars["GITHUB_REPO"]
|
|
1812
|
+
|
|
1813
|
+
elif adapter_type == "jira":
|
|
1814
|
+
# JIRA adapter configuration
|
|
1815
|
+
if env_vars.get("JIRA_SERVER"):
|
|
1816
|
+
config["server"] = env_vars["JIRA_SERVER"]
|
|
1817
|
+
if env_vars.get("JIRA_EMAIL"):
|
|
1818
|
+
config["email"] = env_vars["JIRA_EMAIL"]
|
|
1819
|
+
if env_vars.get("JIRA_API_TOKEN"):
|
|
1820
|
+
config["api_token"] = env_vars["JIRA_API_TOKEN"]
|
|
1821
|
+
if env_vars.get("JIRA_PROJECT_KEY"):
|
|
1822
|
+
config["project_key"] = env_vars["JIRA_PROJECT_KEY"]
|
|
1823
|
+
|
|
1824
|
+
elif adapter_type == "aitrackdown":
|
|
1825
|
+
# AITrackdown adapter configuration
|
|
1826
|
+
base_path = env_vars.get("MCP_TICKETER_BASE_PATH", ".aitrackdown")
|
|
1827
|
+
config["base_path"] = base_path
|
|
1828
|
+
config["auto_create_dirs"] = True
|
|
1829
|
+
|
|
1830
|
+
# Add any generic overrides
|
|
1831
|
+
if env_vars.get("MCP_TICKETER_API_KEY"):
|
|
1832
|
+
config["api_key"] = env_vars["MCP_TICKETER_API_KEY"]
|
|
1833
|
+
|
|
1834
|
+
return config
|
|
1835
|
+
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
|
|
1839
|
+
|
|
1705
1840
|
# Add diagnostic handler methods to MCPTicketServer class
|
|
1706
1841
|
async def _handle_system_health(self, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
1707
1842
|
"""Handle system health check."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-ticketer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Universal ticket management interface for AI agents with MCP support
|
|
5
5
|
Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
6
6
|
Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
|
|
2
|
-
mcp_ticketer/__version__.py,sha256=
|
|
2
|
+
mcp_ticketer/__version__.py,sha256=hTsAyUEYaGHMTOcqUuCMjSc3y7Sywx_V8sgWryJLRsY,1117
|
|
3
3
|
mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
|
|
5
5
|
mcp_ticketer/adapters/aitrackdown.py,sha256=stlbge8K6w-EyQkw_vEQNSXQgCOWN5tOlQUgGWZQNMQ,17936
|
|
@@ -16,6 +16,7 @@ mcp_ticketer/adapters/linear/types.py,sha256=VuGPu1Z5jGHtbI2zkCyL5YFnwQNukGW-UV7
|
|
|
16
16
|
mcp_ticketer/cache/__init__.py,sha256=Xcd-cKnt-Cx7jBzvfzUUUPaGkmyXFi5XUFWw3Z4b7d4,138
|
|
17
17
|
mcp_ticketer/cache/memory.py,sha256=2yBqGi9i0SanlUhJoOC7nijWjoMa3_ntPe-V-AV-LfU,5042
|
|
18
18
|
mcp_ticketer/cli/__init__.py,sha256=l9Q8iKmfGkTu0cssHBVqNZTsL4eAtFzOB25AED_0G6g,89
|
|
19
|
+
mcp_ticketer/cli/adapter_diagnostics.py,sha256=t7RyesBRSyJBoVvft2scdNRcugJDIWvqYS9NszPV5ok,14820
|
|
19
20
|
mcp_ticketer/cli/auggie_configure.py,sha256=MXKzLtqe3K_UTQ2GacHAWbvf_B0779KL325smiAKE0Q,8212
|
|
20
21
|
mcp_ticketer/cli/codex_configure.py,sha256=xDppHouT6_-cYXswyAggoPX5bSlRXMvCoM_x9PQ-42A,9086
|
|
21
22
|
mcp_ticketer/cli/configure.py,sha256=BsA_pSHQMQS0t1bJO_wMM8LWsd5sWJDASjEPRHvwC18,16198
|
|
@@ -23,7 +24,7 @@ mcp_ticketer/cli/diagnostics.py,sha256=AC7cMQHVWdHfrYH2Y1tkhmRezM2-mID5E_Dhfil7F
|
|
|
23
24
|
mcp_ticketer/cli/discover.py,sha256=AF_qlQc1Oo0UkWayoF5pmRChS5J3fJjH6f2YZzd_k8w,13188
|
|
24
25
|
mcp_ticketer/cli/gemini_configure.py,sha256=ZNSA1lBW-itVToza-JxW95Po7daVXKiZAh7lp6pmXMU,9343
|
|
25
26
|
mcp_ticketer/cli/linear_commands.py,sha256=b5v4c9uBNPwj_vdy314JkLZbyC1fXU6IcY2VoG0z7gI,17193
|
|
26
|
-
mcp_ticketer/cli/main.py,sha256=
|
|
27
|
+
mcp_ticketer/cli/main.py,sha256=blViBOtv9HbRUkuAPlb7CstyH3nt90vxgEHIZcdGls8,73130
|
|
27
28
|
mcp_ticketer/cli/mcp_configure.py,sha256=RzV50UjXgOmvMp-9S0zS39psuvjffVByaMrqrUaAGAM,9594
|
|
28
29
|
mcp_ticketer/cli/migrate_config.py,sha256=MYsr_C5ZxsGg0P13etWTWNrJ_lc6ElRCkzfQADYr3DM,5956
|
|
29
30
|
mcp_ticketer/cli/queue_commands.py,sha256=mm-3H6jmkUGJDyU_E46o9iRpek8tvFCm77F19OtHiZI,7884
|
|
@@ -32,7 +33,7 @@ mcp_ticketer/cli/utils.py,sha256=bGoLcqsNsv7YMjVeWc9tlqKQC5zeOmWZp7wEfe0JfzM,229
|
|
|
32
33
|
mcp_ticketer/core/__init__.py,sha256=eXovsaJymQRP2AwOBuOy6mFtI3I68D7gGenZ5V-IMqo,349
|
|
33
34
|
mcp_ticketer/core/adapter.py,sha256=q64LxOInIno7EIbmuxItf8KEsd-g9grCs__Z4uwZHto,10273
|
|
34
35
|
mcp_ticketer/core/config.py,sha256=hvn8RoN2BSmYzESKqCvcpKleX0PrGiHVQ5v35nX12Mc,19241
|
|
35
|
-
mcp_ticketer/core/env_discovery.py,sha256=
|
|
36
|
+
mcp_ticketer/core/env_discovery.py,sha256=m2x3K8hrd0_nzmSGZn5EYUnED7MI2qxcWHTp6ns880o,19970
|
|
36
37
|
mcp_ticketer/core/env_loader.py,sha256=fX8EpHcAJxxhPJ-XY5BD8HlLs-Y9jdlQ24Qtt5ylBNo,12032
|
|
37
38
|
mcp_ticketer/core/exceptions.py,sha256=78tzV3Muc7E_UhEIByF0NtsPe1I0lFVrbpdDNX56kQg,3737
|
|
38
39
|
mcp_ticketer/core/http_client.py,sha256=s5ikMiwEJ8TJjNn73wu3gv3OdAtyBEpAqPnSroRMW2k,13971
|
|
@@ -41,7 +42,7 @@ mcp_ticketer/core/models.py,sha256=a_2AbL3NlN0pfdZad-hXus_zb4bSi9zISHcsNYl0sng,1
|
|
|
41
42
|
mcp_ticketer/core/project_config.py,sha256=yYxlgxjcEPeOwx-b-SXFpe0k9pW9xzBRAK72PsItG-o,23346
|
|
42
43
|
mcp_ticketer/core/registry.py,sha256=ShYLDPE62KFJpB0kj_zFyQzRxSH3LkQEEuo1jaakb1k,3483
|
|
43
44
|
mcp_ticketer/mcp/__init__.py,sha256=Y05eTzsPk0wH8yKNIM-ekpGjgSDO0bQr0EME-vOP4GE,123
|
|
44
|
-
mcp_ticketer/mcp/server.py,sha256=
|
|
45
|
+
mcp_ticketer/mcp/server.py,sha256=PCNerYqLMq5et3ZYAIdd_q_-4LGLBhDuvPAYTuWxDiQ,81351
|
|
45
46
|
mcp_ticketer/queue/__init__.py,sha256=1YIaCpZpFqPcqvDEQXiEvDLiw94DXRdCJkBaVIFQrms,231
|
|
46
47
|
mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
|
|
47
48
|
mcp_ticketer/queue/health_monitor.py,sha256=aQrlBzfbLWu8-fV2b5CuHs4oqyTqGGcntKIHM3r-dDI,11844
|
|
@@ -50,9 +51,9 @@ mcp_ticketer/queue/queue.py,sha256=jSAkYNEIbNH1cbYuF8s6eFuZmXqn8WHXx3mbfMU2Ud8,1
|
|
|
50
51
|
mcp_ticketer/queue/run_worker.py,sha256=F7anuhdkgZF9lXZntHuJ7rEzuEkAfAZO1qvGh3R57bw,1033
|
|
51
52
|
mcp_ticketer/queue/ticket_registry.py,sha256=k8FYg2cFYsI4POb94-o-fTrIVr-ttfi60r0O5YhJYck,15321
|
|
52
53
|
mcp_ticketer/queue/worker.py,sha256=zXJpyhRJ99be0VLaez3YPtC9OU17vVNu5qhr1dCGaLg,19992
|
|
53
|
-
mcp_ticketer-0.
|
|
54
|
-
mcp_ticketer-0.
|
|
55
|
-
mcp_ticketer-0.
|
|
56
|
-
mcp_ticketer-0.
|
|
57
|
-
mcp_ticketer-0.
|
|
58
|
-
mcp_ticketer-0.
|
|
54
|
+
mcp_ticketer-0.3.1.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
|
|
55
|
+
mcp_ticketer-0.3.1.dist-info/METADATA,sha256=7MkHCRw1QYtKKRfHVQ2oUMW3syEXGilHV5y-Ooud4xQ,13219
|
|
56
|
+
mcp_ticketer-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
mcp_ticketer-0.3.1.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
|
|
58
|
+
mcp_ticketer-0.3.1.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
|
|
59
|
+
mcp_ticketer-0.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|