mcp-ticketer 0.3.5__py3-none-any.whl → 0.12.0__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 +3 -3
- mcp_ticketer/adapters/__init__.py +2 -0
- mcp_ticketer/adapters/aitrackdown.py +263 -14
- mcp_ticketer/adapters/asana/__init__.py +15 -0
- mcp_ticketer/adapters/asana/adapter.py +1308 -0
- mcp_ticketer/adapters/asana/client.py +292 -0
- mcp_ticketer/adapters/asana/mappers.py +334 -0
- mcp_ticketer/adapters/asana/types.py +146 -0
- mcp_ticketer/adapters/github.py +326 -109
- mcp_ticketer/adapters/hybrid.py +11 -11
- mcp_ticketer/adapters/jira.py +271 -25
- mcp_ticketer/adapters/linear/adapter.py +693 -39
- mcp_ticketer/adapters/linear/client.py +61 -9
- mcp_ticketer/adapters/linear/mappers.py +9 -3
- mcp_ticketer/adapters/linear/queries.py +9 -7
- mcp_ticketer/cache/memory.py +9 -8
- mcp_ticketer/cli/adapter_diagnostics.py +1 -1
- mcp_ticketer/cli/auggie_configure.py +104 -15
- mcp_ticketer/cli/codex_configure.py +188 -32
- mcp_ticketer/cli/configure.py +37 -48
- mcp_ticketer/cli/diagnostics.py +20 -18
- mcp_ticketer/cli/discover.py +292 -26
- mcp_ticketer/cli/gemini_configure.py +107 -26
- mcp_ticketer/cli/instruction_commands.py +429 -0
- mcp_ticketer/cli/linear_commands.py +105 -22
- mcp_ticketer/cli/main.py +1830 -435
- mcp_ticketer/cli/mcp_configure.py +296 -89
- mcp_ticketer/cli/migrate_config.py +12 -8
- mcp_ticketer/cli/platform_commands.py +123 -0
- mcp_ticketer/cli/platform_detection.py +412 -0
- mcp_ticketer/cli/python_detection.py +126 -0
- mcp_ticketer/cli/queue_commands.py +15 -15
- mcp_ticketer/cli/simple_health.py +1 -1
- mcp_ticketer/cli/ticket_commands.py +773 -0
- mcp_ticketer/cli/update_checker.py +313 -0
- mcp_ticketer/cli/utils.py +67 -62
- mcp_ticketer/core/__init__.py +14 -1
- mcp_ticketer/core/adapter.py +84 -15
- mcp_ticketer/core/config.py +44 -39
- mcp_ticketer/core/env_discovery.py +42 -12
- mcp_ticketer/core/env_loader.py +15 -14
- mcp_ticketer/core/exceptions.py +3 -3
- mcp_ticketer/core/http_client.py +26 -26
- mcp_ticketer/core/instructions.py +405 -0
- mcp_ticketer/core/mappers.py +11 -11
- mcp_ticketer/core/models.py +50 -20
- mcp_ticketer/core/onepassword_secrets.py +379 -0
- mcp_ticketer/core/project_config.py +57 -35
- mcp_ticketer/core/registry.py +3 -3
- mcp_ticketer/defaults/ticket_instructions.md +644 -0
- mcp_ticketer/mcp/__init__.py +29 -1
- mcp_ticketer/mcp/__main__.py +60 -0
- mcp_ticketer/mcp/server/__init__.py +25 -0
- mcp_ticketer/mcp/server/__main__.py +60 -0
- mcp_ticketer/mcp/{dto.py → server/dto.py} +32 -32
- mcp_ticketer/mcp/{server.py → server/main.py} +127 -74
- mcp_ticketer/mcp/{response_builder.py → server/response_builder.py} +2 -2
- mcp_ticketer/mcp/server/server_sdk.py +93 -0
- mcp_ticketer/mcp/server/tools/__init__.py +47 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +226 -0
- mcp_ticketer/mcp/server/tools/bulk_tools.py +273 -0
- mcp_ticketer/mcp/server/tools/comment_tools.py +90 -0
- mcp_ticketer/mcp/server/tools/config_tools.py +381 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +532 -0
- mcp_ticketer/mcp/server/tools/instruction_tools.py +293 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +154 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +206 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +430 -0
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +382 -0
- mcp_ticketer/queue/__init__.py +1 -0
- mcp_ticketer/queue/health_monitor.py +5 -4
- mcp_ticketer/queue/manager.py +15 -51
- mcp_ticketer/queue/queue.py +19 -19
- mcp_ticketer/queue/run_worker.py +1 -1
- mcp_ticketer/queue/ticket_registry.py +14 -14
- mcp_ticketer/queue/worker.py +16 -14
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/METADATA +168 -32
- mcp_ticketer-0.12.0.dist-info/RECORD +91 -0
- mcp_ticketer-0.3.5.dist-info/RECORD +0 -62
- /mcp_ticketer/mcp/{constants.py → server/constants.py} +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.3.5.dist-info → mcp_ticketer-0.12.0.dist-info}/top_level.txt +0 -0
|
@@ -69,7 +69,7 @@ class LinearGraphQLClient:
|
|
|
69
69
|
return client
|
|
70
70
|
|
|
71
71
|
except Exception as e:
|
|
72
|
-
raise AdapterError(f"Failed to create Linear client: {e}", "linear")
|
|
72
|
+
raise AdapterError(f"Failed to create Linear client: {e}", "linear") from e
|
|
73
73
|
|
|
74
74
|
async def execute_query(
|
|
75
75
|
self,
|
|
@@ -110,15 +110,19 @@ class LinearGraphQLClient:
|
|
|
110
110
|
status_code = e.response.status
|
|
111
111
|
|
|
112
112
|
if status_code == 401:
|
|
113
|
-
raise AuthenticationError(
|
|
113
|
+
raise AuthenticationError(
|
|
114
|
+
"Invalid Linear API key", "linear"
|
|
115
|
+
) from e
|
|
114
116
|
elif status_code == 403:
|
|
115
|
-
raise AuthenticationError(
|
|
117
|
+
raise AuthenticationError(
|
|
118
|
+
"Insufficient permissions", "linear"
|
|
119
|
+
) from e
|
|
116
120
|
elif status_code == 429:
|
|
117
121
|
# Rate limit exceeded
|
|
118
122
|
retry_after = e.response.headers.get("Retry-After", "60")
|
|
119
123
|
raise RateLimitError(
|
|
120
124
|
"Linear API rate limit exceeded", "linear", retry_after
|
|
121
|
-
)
|
|
125
|
+
) from e
|
|
122
126
|
elif status_code >= 500:
|
|
123
127
|
# Server error - retry
|
|
124
128
|
if attempt < retries:
|
|
@@ -126,13 +130,13 @@ class LinearGraphQLClient:
|
|
|
126
130
|
continue
|
|
127
131
|
raise AdapterError(
|
|
128
132
|
f"Linear API server error: {status_code}", "linear"
|
|
129
|
-
)
|
|
133
|
+
) from e
|
|
130
134
|
|
|
131
135
|
# Network or other transport error
|
|
132
136
|
if attempt < retries:
|
|
133
137
|
await asyncio.sleep(2**attempt)
|
|
134
138
|
continue
|
|
135
|
-
raise AdapterError(f"Linear API transport error: {e}", "linear")
|
|
139
|
+
raise AdapterError(f"Linear API transport error: {e}", "linear") from e
|
|
136
140
|
|
|
137
141
|
except Exception as e:
|
|
138
142
|
# GraphQL or other errors
|
|
@@ -145,15 +149,19 @@ class LinearGraphQLClient:
|
|
|
145
149
|
):
|
|
146
150
|
raise AuthenticationError(
|
|
147
151
|
f"Linear authentication failed: {error_msg}", "linear"
|
|
148
|
-
)
|
|
152
|
+
) from e
|
|
149
153
|
elif "rate limit" in error_msg.lower():
|
|
150
|
-
raise RateLimitError(
|
|
154
|
+
raise RateLimitError(
|
|
155
|
+
"Linear API rate limit exceeded", "linear"
|
|
156
|
+
) from e
|
|
151
157
|
|
|
152
158
|
# Generic error
|
|
153
159
|
if attempt < retries:
|
|
154
160
|
await asyncio.sleep(2**attempt)
|
|
155
161
|
continue
|
|
156
|
-
raise AdapterError(
|
|
162
|
+
raise AdapterError(
|
|
163
|
+
f"Linear GraphQL error: {error_msg}", "linear"
|
|
164
|
+
) from e
|
|
157
165
|
|
|
158
166
|
# Should never reach here
|
|
159
167
|
raise AdapterError("Maximum retries exceeded", "linear")
|
|
@@ -266,6 +274,50 @@ class LinearGraphQLClient:
|
|
|
266
274
|
except Exception:
|
|
267
275
|
return None
|
|
268
276
|
|
|
277
|
+
async def get_users_by_name(self, name: str) -> list[dict[str, Any]]:
|
|
278
|
+
"""Search users by display name or full name.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
name: Display name or full name to search for
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
List of matching users (may be empty)
|
|
285
|
+
|
|
286
|
+
"""
|
|
287
|
+
import logging
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
query = """
|
|
291
|
+
query SearchUsers($nameFilter: String!) {
|
|
292
|
+
users(
|
|
293
|
+
filter: {
|
|
294
|
+
or: [
|
|
295
|
+
{ displayName: { containsIgnoreCase: $nameFilter } }
|
|
296
|
+
{ name: { containsIgnoreCase: $nameFilter } }
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
first: 10
|
|
300
|
+
) {
|
|
301
|
+
nodes {
|
|
302
|
+
id
|
|
303
|
+
name
|
|
304
|
+
email
|
|
305
|
+
displayName
|
|
306
|
+
avatarUrl
|
|
307
|
+
active
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
result = await self.execute_query(query, {"nameFilter": name})
|
|
314
|
+
users = result.get("users", {}).get("nodes", [])
|
|
315
|
+
return [u for u in users if u.get("active", True)] # Filter active users
|
|
316
|
+
|
|
317
|
+
except Exception as e:
|
|
318
|
+
logging.getLogger(__name__).warning(f"Failed to search users by name: {e}")
|
|
319
|
+
return []
|
|
320
|
+
|
|
269
321
|
async def close(self) -> None:
|
|
270
322
|
"""Close the client connection.
|
|
271
323
|
|
|
@@ -10,7 +10,10 @@ from .types import extract_linear_metadata, get_universal_priority, get_universa
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def map_linear_issue_to_task(issue_data: dict[str, Any]) -> Task:
|
|
13
|
-
"""Convert Linear issue data to universal Task model.
|
|
13
|
+
"""Convert Linear issue or sub-issue data to universal Task model.
|
|
14
|
+
|
|
15
|
+
Handles both top-level issues (no parent) and sub-issues (child items
|
|
16
|
+
with a parent issue).
|
|
14
17
|
|
|
15
18
|
Args:
|
|
16
19
|
issue_data: Raw Linear issue data from GraphQL
|
|
@@ -203,7 +206,10 @@ def map_linear_comment_to_comment(
|
|
|
203
206
|
|
|
204
207
|
|
|
205
208
|
def build_linear_issue_input(task: Task, team_id: str) -> dict[str, Any]:
|
|
206
|
-
"""Build Linear issue input from universal Task model.
|
|
209
|
+
"""Build Linear issue or sub-issue input from universal Task model.
|
|
210
|
+
|
|
211
|
+
Creates input for a top-level issue when task.parent_issue is not set,
|
|
212
|
+
or for a sub-issue when task.parent_issue is provided.
|
|
207
213
|
|
|
208
214
|
Args:
|
|
209
215
|
task: Universal Task model
|
|
@@ -215,7 +221,7 @@ def build_linear_issue_input(task: Task, team_id: str) -> dict[str, Any]:
|
|
|
215
221
|
"""
|
|
216
222
|
from .types import get_linear_priority
|
|
217
223
|
|
|
218
|
-
issue_input = {
|
|
224
|
+
issue_input: dict[str, Any] = {
|
|
219
225
|
"title": task.title,
|
|
220
226
|
"teamId": team_id,
|
|
221
227
|
}
|
|
@@ -226,13 +226,15 @@ ISSUE_LIST_FRAGMENTS = (
|
|
|
226
226
|
|
|
227
227
|
WORKFLOW_STATES_QUERY = """
|
|
228
228
|
query WorkflowStates($teamId: String!) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
team(id: $teamId) {
|
|
230
|
+
states {
|
|
231
|
+
nodes {
|
|
232
|
+
id
|
|
233
|
+
name
|
|
234
|
+
type
|
|
235
|
+
position
|
|
236
|
+
color
|
|
237
|
+
}
|
|
236
238
|
}
|
|
237
239
|
}
|
|
238
240
|
}
|
mcp_ticketer/cache/memory.py
CHANGED
|
@@ -4,8 +4,9 @@ import asyncio
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import json
|
|
6
6
|
import time
|
|
7
|
+
from collections.abc import Callable
|
|
7
8
|
from functools import wraps
|
|
8
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class CacheEntry:
|
|
@@ -41,7 +42,7 @@ class MemoryCache:
|
|
|
41
42
|
self._default_ttl = default_ttl
|
|
42
43
|
self._lock = asyncio.Lock()
|
|
43
44
|
|
|
44
|
-
async def get(self, key: str) ->
|
|
45
|
+
async def get(self, key: str) -> Any | None:
|
|
45
46
|
"""Get value from cache.
|
|
46
47
|
|
|
47
48
|
Args:
|
|
@@ -60,7 +61,7 @@ class MemoryCache:
|
|
|
60
61
|
del self._cache[key]
|
|
61
62
|
return None
|
|
62
63
|
|
|
63
|
-
async def set(self, key: str, value: Any, ttl:
|
|
64
|
+
async def set(self, key: str, value: Any, ttl: float | None = None) -> None:
|
|
64
65
|
"""Set value in cache.
|
|
65
66
|
|
|
66
67
|
Args:
|
|
@@ -114,7 +115,7 @@ class MemoryCache:
|
|
|
114
115
|
return len(self._cache)
|
|
115
116
|
|
|
116
117
|
@staticmethod
|
|
117
|
-
def generate_key(*args, **kwargs) -> str:
|
|
118
|
+
def generate_key(*args: Any, **kwargs: Any) -> str:
|
|
118
119
|
"""Generate cache key from arguments.
|
|
119
120
|
|
|
120
121
|
Args:
|
|
@@ -134,11 +135,11 @@ class MemoryCache:
|
|
|
134
135
|
|
|
135
136
|
|
|
136
137
|
def cache_decorator(
|
|
137
|
-
ttl:
|
|
138
|
+
ttl: float | None = None,
|
|
138
139
|
key_prefix: str = "",
|
|
139
|
-
cache_instance:
|
|
140
|
+
cache_instance: MemoryCache | None = None,
|
|
140
141
|
) -> Callable:
|
|
141
|
-
"""
|
|
142
|
+
"""Decorate async function to cache its results.
|
|
142
143
|
|
|
143
144
|
Args:
|
|
144
145
|
ttl: TTL for cached results
|
|
@@ -154,7 +155,7 @@ def cache_decorator(
|
|
|
154
155
|
|
|
155
156
|
def decorator(func: Callable) -> Callable:
|
|
156
157
|
@wraps(func)
|
|
157
|
-
async def wrapper(*args, **kwargs):
|
|
158
|
+
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
158
159
|
# Generate cache key
|
|
159
160
|
base_key = MemoryCache.generate_key(*args, **kwargs)
|
|
160
161
|
cache_key = f"{key_prefix}:{func.__name__}:{base_key}"
|
|
@@ -343,7 +343,7 @@ def _provide_recommendations(console: Console) -> None:
|
|
|
343
343
|
console.print("• For local files: [cyan]mcp-ticketer init aitrackdown[/cyan]")
|
|
344
344
|
|
|
345
345
|
console.print("\n[bold]Test Configuration:[/bold]")
|
|
346
|
-
console.print("• Run diagnostics: [cyan]mcp-ticketer
|
|
346
|
+
console.print("• Run diagnostics: [cyan]mcp-ticketer doctor[/cyan]")
|
|
347
347
|
console.print(
|
|
348
348
|
"• Test ticket creation: [cyan]mcp-ticketer create 'Test ticket'[/cyan]"
|
|
349
349
|
)
|
|
@@ -10,7 +10,8 @@ from typing import Any
|
|
|
10
10
|
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
|
|
13
|
-
from .mcp_configure import
|
|
13
|
+
from .mcp_configure import load_project_config
|
|
14
|
+
from .python_detection import get_mcp_ticketer_python
|
|
14
15
|
|
|
15
16
|
console = Console()
|
|
16
17
|
|
|
@@ -71,18 +72,22 @@ def save_auggie_config(config_path: Path, config: dict[str, Any]) -> None:
|
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
def create_auggie_server_config(
|
|
74
|
-
|
|
75
|
+
python_path: str, project_config: dict[str, Any], project_path: str | None = None
|
|
75
76
|
) -> dict[str, Any]:
|
|
76
77
|
"""Create Auggie MCP server configuration for mcp-ticketer.
|
|
77
78
|
|
|
78
79
|
Args:
|
|
79
|
-
|
|
80
|
+
python_path: Path to Python executable in mcp-ticketer venv
|
|
80
81
|
project_config: Project configuration from .mcp-ticketer/config.json
|
|
82
|
+
project_path: Project directory path (optional)
|
|
81
83
|
|
|
82
84
|
Returns:
|
|
83
85
|
Auggie MCP server configuration dict
|
|
84
86
|
|
|
85
87
|
"""
|
|
88
|
+
# Use Python module invocation pattern (works regardless of where package is installed)
|
|
89
|
+
from pathlib import Path
|
|
90
|
+
|
|
86
91
|
# Get adapter configuration
|
|
87
92
|
adapter = project_config.get("default_adapter", "aitrackdown")
|
|
88
93
|
adapters_config = project_config.get("adapters", {})
|
|
@@ -91,6 +96,10 @@ def create_auggie_server_config(
|
|
|
91
96
|
# Build environment variables
|
|
92
97
|
env_vars = {}
|
|
93
98
|
|
|
99
|
+
# Add PYTHONPATH for project context
|
|
100
|
+
if project_path:
|
|
101
|
+
env_vars["PYTHONPATH"] = project_path
|
|
102
|
+
|
|
94
103
|
# Add adapter type
|
|
95
104
|
env_vars["MCP_TICKETER_ADAPTER"] = adapter
|
|
96
105
|
|
|
@@ -128,16 +137,87 @@ def create_auggie_server_config(
|
|
|
128
137
|
if "project_key" in adapter_config:
|
|
129
138
|
env_vars["JIRA_PROJECT_KEY"] = adapter_config["project_key"]
|
|
130
139
|
|
|
140
|
+
# Use Python module invocation pattern
|
|
141
|
+
args = ["-m", "mcp_ticketer.mcp.server"]
|
|
142
|
+
if project_path:
|
|
143
|
+
args.append(project_path)
|
|
144
|
+
|
|
131
145
|
# Create server configuration (simpler than Gemini - no timeout/trust)
|
|
132
146
|
config = {
|
|
133
|
-
"command":
|
|
134
|
-
"args":
|
|
147
|
+
"command": python_path,
|
|
148
|
+
"args": args,
|
|
135
149
|
"env": env_vars,
|
|
136
150
|
}
|
|
137
151
|
|
|
138
152
|
return config
|
|
139
153
|
|
|
140
154
|
|
|
155
|
+
def remove_auggie_mcp(dry_run: bool = False) -> None:
|
|
156
|
+
"""Remove mcp-ticketer from Auggie CLI configuration.
|
|
157
|
+
|
|
158
|
+
IMPORTANT: Auggie CLI ONLY supports global configuration.
|
|
159
|
+
This will remove mcp-ticketer from ~/.augment/settings.json.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
dry_run: Show what would be removed without making changes
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
# Step 1: Find Auggie config location
|
|
166
|
+
console.print("[cyan]🔍 Removing Auggie CLI global configuration...[/cyan]")
|
|
167
|
+
console.print(
|
|
168
|
+
"[yellow]⚠ NOTE: Auggie only supports global configuration (affects all projects)[/yellow]"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
auggie_config_path = find_auggie_config()
|
|
172
|
+
console.print(f"[dim]Config location: {auggie_config_path}[/dim]")
|
|
173
|
+
|
|
174
|
+
# Step 2: Check if config file exists
|
|
175
|
+
if not auggie_config_path.exists():
|
|
176
|
+
console.print(
|
|
177
|
+
f"[yellow]⚠ No configuration found at {auggie_config_path}[/yellow]"
|
|
178
|
+
)
|
|
179
|
+
console.print("[dim]mcp-ticketer is not configured for Auggie[/dim]")
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
# Step 3: Load existing Auggie configuration
|
|
183
|
+
auggie_config = load_auggie_config(auggie_config_path)
|
|
184
|
+
|
|
185
|
+
# Step 4: Check if mcp-ticketer is configured
|
|
186
|
+
if "mcp-ticketer" not in auggie_config.get("mcpServers", {}):
|
|
187
|
+
console.print("[yellow]⚠ mcp-ticketer is not configured[/yellow]")
|
|
188
|
+
console.print(f"[dim]No mcp-ticketer entry found in {auggie_config_path}[/dim]")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
# Step 5: Show what would be removed (dry run or actual removal)
|
|
192
|
+
if dry_run:
|
|
193
|
+
console.print("\n[cyan]DRY RUN - Would remove:[/cyan]")
|
|
194
|
+
console.print(" Server name: mcp-ticketer")
|
|
195
|
+
console.print(f" From: {auggie_config_path}")
|
|
196
|
+
console.print(" Scope: Global (all projects)")
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
# Step 6: Remove mcp-ticketer from configuration
|
|
200
|
+
del auggie_config["mcpServers"]["mcp-ticketer"]
|
|
201
|
+
|
|
202
|
+
# Step 7: Save updated configuration
|
|
203
|
+
try:
|
|
204
|
+
save_auggie_config(auggie_config_path, auggie_config)
|
|
205
|
+
console.print("\n[green]✓ Successfully removed mcp-ticketer[/green]")
|
|
206
|
+
console.print(f"[dim]Configuration updated: {auggie_config_path}[/dim]")
|
|
207
|
+
|
|
208
|
+
# Next steps
|
|
209
|
+
console.print("\n[bold cyan]Next Steps:[/bold cyan]")
|
|
210
|
+
console.print("1. Restart Auggie CLI for changes to take effect")
|
|
211
|
+
console.print("2. mcp-ticketer will no longer be available via MCP")
|
|
212
|
+
console.print(
|
|
213
|
+
"\n[yellow]⚠ Note: This removes global configuration affecting all projects[/yellow]"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
except Exception as e:
|
|
217
|
+
console.print(f"\n[red]✗ Failed to update configuration:[/red] {e}")
|
|
218
|
+
raise
|
|
219
|
+
|
|
220
|
+
|
|
141
221
|
def configure_auggie_mcp(force: bool = False) -> None:
|
|
142
222
|
"""Configure Auggie CLI to use mcp-ticketer.
|
|
143
223
|
|
|
@@ -148,18 +228,22 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
148
228
|
force: Overwrite existing configuration
|
|
149
229
|
|
|
150
230
|
Raises:
|
|
151
|
-
FileNotFoundError: If
|
|
231
|
+
FileNotFoundError: If Python executable or project config not found
|
|
152
232
|
ValueError: If configuration is invalid
|
|
153
233
|
|
|
154
234
|
"""
|
|
155
|
-
# Step 1: Find
|
|
156
|
-
console.print("[cyan]🔍 Finding mcp-ticketer
|
|
235
|
+
# Step 1: Find Python executable
|
|
236
|
+
console.print("[cyan]🔍 Finding mcp-ticketer Python executable...[/cyan]")
|
|
157
237
|
try:
|
|
158
|
-
|
|
159
|
-
console.print(f"[green]✓[/green] Found: {
|
|
160
|
-
except
|
|
161
|
-
console.print(f"[red]✗[/red] {e}")
|
|
162
|
-
raise
|
|
238
|
+
python_path = get_mcp_ticketer_python()
|
|
239
|
+
console.print(f"[green]✓[/green] Found: {python_path}")
|
|
240
|
+
except Exception as e:
|
|
241
|
+
console.print(f"[red]✗[/red] Could not find Python executable: {e}")
|
|
242
|
+
raise FileNotFoundError(
|
|
243
|
+
"Could not find mcp-ticketer Python executable. "
|
|
244
|
+
"Please ensure mcp-ticketer is installed.\n"
|
|
245
|
+
"Install with: pip install mcp-ticketer or pipx install mcp-ticketer"
|
|
246
|
+
) from e
|
|
163
247
|
|
|
164
248
|
# Step 2: Load project configuration
|
|
165
249
|
console.print("\n[cyan]📖 Reading project configuration...[/cyan]")
|
|
@@ -193,8 +277,11 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
193
277
|
console.print("[yellow]⚠ Overwriting existing configuration[/yellow]")
|
|
194
278
|
|
|
195
279
|
# Step 6: Create mcp-ticketer server config
|
|
280
|
+
project_path = str(Path.cwd())
|
|
196
281
|
server_config = create_auggie_server_config(
|
|
197
|
-
|
|
282
|
+
python_path=python_path,
|
|
283
|
+
project_config=project_config,
|
|
284
|
+
project_path=project_path,
|
|
198
285
|
)
|
|
199
286
|
|
|
200
287
|
# Step 7: Update Auggie configuration
|
|
@@ -213,8 +300,10 @@ def configure_auggie_mcp(force: bool = False) -> None:
|
|
|
213
300
|
console.print("\n[bold]Configuration Details:[/bold]")
|
|
214
301
|
console.print(" Server name: mcp-ticketer")
|
|
215
302
|
console.print(f" Adapter: {adapter}")
|
|
216
|
-
console.print(f"
|
|
303
|
+
console.print(f" Python: {python_path}")
|
|
304
|
+
console.print(" Command: python -m mcp_ticketer.mcp.server")
|
|
217
305
|
console.print(" Scope: Global (affects all projects)")
|
|
306
|
+
console.print(f" Project path: {project_path}")
|
|
218
307
|
if "env" in server_config:
|
|
219
308
|
console.print(
|
|
220
309
|
f" Environment variables: {list(server_config['env'].keys())}"
|